diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 1998-02-22 08:56:11 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 1998-02-22 08:56:11 +0000 |
commit | e8befda30be9497912a351c5f2bb0b3d0a8257f9 (patch) | |
tree | fc964c412f584dff1e3c8b45784a9d25e301b686 /gnu | |
parent | 26a78b7b0ee68653084708a1dddba75fd9918291 (diff) |
Integrate local changes
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/usr.bin/cvs/Makefile.in | 9 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/configure | 900 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/configure.in | 148 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/lib/getdate.c | 116 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/lib/getdate.y | 32 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/lib/getwd.c | 35 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/lib/strdup.c | 43 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/Makefile.in | 6 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/checkout.c | 540 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/commit.c | 196 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 122 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/ignore.c | 51 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/lock.c | 8 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 278 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/patch.c | 105 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcs.c | 3345 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcscmds.c | 617 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 1244 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/update.c | 342 |
19 files changed, 6167 insertions, 1970 deletions
diff --git a/gnu/usr.bin/cvs/Makefile.in b/gnu/usr.bin/cvs/Makefile.in index bdf22f7b77f..54d625cdf7b 100644 --- a/gnu/usr.bin/cvs/Makefile.in +++ b/gnu/usr.bin/cvs/Makefile.in @@ -89,12 +89,12 @@ DISTFILES = \ ChangeLog NEWS ChangeLog.zoo \ configure configure.in stamp-h.in config.h.in Makefile.in acconfig.h \ cvs-format.el mkinstalldirs install-sh \ - cvsnt.mak \ + cvsnt.mak cvsnt.dsp \ .cvsignore cvs.spec ### Subdirectories to run make in for the primary targets. # Unix source subdirs, where we'll want to run lint and etags: -USOURCE_SUBDIRS = lib zlib src +USOURCE_SUBDIRS = lib zlib diff src # Documentation directories; special handling INSTALL_MAN=man # All other subdirs: @@ -190,18 +190,21 @@ saber: check: cd lib ; $(MAKE) $(FLAGS_TO_PASS) cd zlib ; $(MAKE) $(FLAGS_TO_PASS) + cd diff ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) check .PHONY: remotecheck remotecheck: cd lib ; $(MAKE) $(FLAGS_TO_PASS) cd zlib ; $(MAKE) $(FLAGS_TO_PASS) + cd diff ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) remotecheck .PHONY: installcheck installcheck: cd lib ; $(MAKE) $(FLAGS_TO_PASS) cd zlib ; $(MAKE) $(FLAGS_TO_PASS) + cd diff ; $(MAKE) $(FLAGS_TO_PASS) cd src ; $(MAKE) $(FLAGS_TO_PASS) installcheck .PHONY: lint @@ -241,7 +244,7 @@ spec: echo > .fname cvs-`cat .version` rm -f `cat .fname`.spec sed < $(top_srcdir)/cvs.spec \ - -e 's/@VERSION@/'`cat .version`'/' \ + -e 's/@VERSION@/'`cat .version`'/g' \ > `cat .fname`.spec diff --git a/gnu/usr.bin/cvs/configure b/gnu/usr.bin/cvs/configure index 570fcb47fa4..f2c3eabbcc8 100644 --- a/gnu/usr.bin/cvs/configure +++ b/gnu/usr.bin/cvs/configure @@ -14,6 +14,8 @@ ac_default_prefix=/usr/local ac_help="$ac_help --with-krb4=value set default \$(KRB4) from value" ac_help="$ac_help + --with-gssapi=value GSSAPI directory" +ac_help="$ac_help --enable-encryption enable encryption support" ac_help="$ac_help --enable-client include code for running as a remote client (default) @@ -533,7 +535,7 @@ fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:537: checking for $ac_word" >&5 +echo "configure:539: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -562,7 +564,7 @@ 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 $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:566: checking for $ac_word" >&5 +echo "configure:568: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -610,7 +612,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:614: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:616: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -620,11 +622,11 @@ ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <<EOF -#line 624 "configure" +#line 626 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:628: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:630: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -644,12 +646,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:648: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:650: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:653: checking whether we are using GNU C" >&5 +echo "configure:655: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -658,7 +660,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:662: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:664: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -673,7 +675,7 @@ if test $ac_cv_prog_gcc = yes; then ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:677: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:679: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -702,7 +704,7 @@ fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:706: checking how to run the C preprocessor" >&5 +echo "configure:708: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -717,13 +719,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext <<EOF -#line 721 "configure" +#line 723 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:727: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:729: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -734,13 +736,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext <<EOF -#line 738 "configure" +#line 740 "configure" #include "confdefs.h" #include <assert.h> Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:744: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:746: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -763,9 +765,9 @@ fi echo "$ac_t""$CPP" 1>&6 echo $ac_n "checking for AIX""... $ac_c" 1>&6 -echo "configure:767: checking for AIX" >&5 +echo "configure:769: checking for AIX" >&5 cat > conftest.$ac_ext <<EOF -#line 769 "configure" +#line 771 "configure" #include "confdefs.h" #ifdef _AIX yes @@ -788,17 +790,17 @@ rm -f conftest* ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 -echo "configure:792: checking for minix/config.h" >&5 +echo "configure:794: checking for minix/config.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 797 "configure" +#line 799 "configure" #include "confdefs.h" #include <minix/config.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:802: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:804: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -836,7 +838,7 @@ EOF fi echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 -echo "configure:840: checking for POSIXized ISC" >&5 +echo "configure:842: checking for POSIXized ISC" >&5 if test -d /etc/conf/kconfig.d && grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 then @@ -866,7 +868,7 @@ echo $ac_n "checking for prefix by $ac_c" 1>&6 # Extract the first word of "cvs", so it can be a program name with args. set dummy cvs; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:870: checking for $ac_word" >&5 +echo "configure:872: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_CVS'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -903,12 +905,12 @@ fi echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:907: checking for working const" >&5 +echo "configure:909: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 912 "configure" +#line 914 "configure" #include "confdefs.h" int main() { @@ -957,7 +959,7 @@ ccp = (char const *const *) p; ; return 0; } EOF -if { (eval echo configure:961: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:963: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -1008,7 +1010,7 @@ ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:1012: checking for a BSD compatible install" >&5 +echo "configure:1014: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1060,7 +1062,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1064: checking for $ac_word" >&5 +echo "configure:1066: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1091,7 +1093,7 @@ 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 $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1095: checking for $ac_word" >&5 +echo "configure:1097: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_YACC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1121,7 +1123,7 @@ done test -n "$YACC" || YACC="yacc" echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -echo "configure:1125: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo "configure:1127: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1151,7 +1153,7 @@ fi # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1155: checking for $ac_word" >&5 +echo "configure:1157: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_perl_path'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1183,7 +1185,7 @@ fi # Extract the first word of "csh", so it can be a program name with args. set dummy csh; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1187: checking for $ac_word" >&5 +echo "configure:1189: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_csh_path'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1216,7 +1218,7 @@ fi # Pull the hash mark out of the macro call to avoid m4 problems. ac_msg="whether #! works in shell scripts" echo $ac_n "checking $ac_msg""... $ac_c" 1>&6 -echo "configure:1220: checking $ac_msg" >&5 +echo "configure:1222: checking $ac_msg" >&5 if eval "test \"`echo '$''{'ac_cv_sys_interpreter'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1242,12 +1244,12 @@ if test X"$ac_cv_sys_interpreter" != X"yes" ; then fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:1246: checking for ANSI C header files" >&5 +echo "configure:1248: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1251 "configure" +#line 1253 "configure" #include "confdefs.h" #include <stdlib.h> #include <stdarg.h> @@ -1255,7 +1257,7 @@ else #include <float.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1259: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1261: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1272,7 +1274,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1276 "configure" +#line 1278 "configure" #include "confdefs.h" #include <string.h> EOF @@ -1290,7 +1292,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext <<EOF -#line 1294 "configure" +#line 1296 "configure" #include "confdefs.h" #include <stdlib.h> EOF @@ -1311,7 +1313,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext <<EOF -#line 1315 "configure" +#line 1317 "configure" #include "confdefs.h" #include <ctype.h> #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -1322,7 +1324,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:1326: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1328: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then : else @@ -1346,22 +1348,23 @@ EOF fi for ac_hdr in errno.h unistd.h string.h memory.h utime.h fcntl.h ndbm.h \ + limits.h sys/file.h \ sys/param.h sys/select.h sys/time.h sys/timeb.h \ io.h direct.h sys/bsdtypes.h sys/resource.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1355: checking for $ac_hdr" >&5 +echo "configure:1358: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1360 "configure" +#line 1363 "configure" #include "confdefs.h" #include <$ac_hdr> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1365: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1368: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1388,12 +1391,12 @@ fi done echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6 -echo "configure:1392: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo "configure:1395: checking for sys/wait.h that is POSIX.1 compatible" >&5 if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1397 "configure" +#line 1400 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/wait.h> @@ -1409,7 +1412,7 @@ wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } EOF -if { (eval echo configure:1413: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1416: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_sys_wait_h=yes else @@ -1430,12 +1433,12 @@ EOF fi echo $ac_n "checking whether stat file-mode macros are broken""... $ac_c" 1>&6 -echo "configure:1434: checking whether stat file-mode macros are broken" >&5 +echo "configure:1437: checking whether stat file-mode macros are broken" >&5 if eval "test \"`echo '$''{'ac_cv_header_stat_broken'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1439 "configure" +#line 1442 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/stat.h> @@ -1486,12 +1489,12 @@ EOF fi echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 -echo "configure:1490: checking whether time.h and sys/time.h may both be included" >&5 +echo "configure:1493: checking whether time.h and sys/time.h may both be included" >&5 if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1495 "configure" +#line 1498 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/time.h> @@ -1500,7 +1503,7 @@ int main() { struct tm *tp; ; return 0; } EOF -if { (eval echo configure:1504: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1507: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_time=yes else @@ -1525,12 +1528,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 -echo "configure:1529: checking for $ac_hdr that defines DIR" >&5 +echo "configure:1532: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1534 "configure" +#line 1537 "configure" #include "confdefs.h" #include <sys/types.h> #include <$ac_hdr> @@ -1538,7 +1541,7 @@ int main() { DIR *dirp = 0; ; return 0; } EOF -if { (eval echo configure:1542: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1545: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else @@ -1563,7 +1566,7 @@ done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 -echo "configure:1567: checking for opendir in -ldir" >&5 +echo "configure:1570: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1571,7 +1574,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <<EOF -#line 1575 "configure" +#line 1578 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1582,7 +1585,7 @@ int main() { opendir() ; return 0; } EOF -if { (eval echo configure:1586: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1589: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1604,7 +1607,7 @@ fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 -echo "configure:1608: checking for opendir in -lx" >&5 +echo "configure:1611: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1612,7 +1615,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <<EOF -#line 1616 "configure" +#line 1619 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1623,7 +1626,7 @@ int main() { opendir() ; return 0; } EOF -if { (eval echo configure:1627: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1630: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1646,12 +1649,12 @@ fi fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 -echo "configure:1650: checking return type of signal handlers" >&5 +echo "configure:1653: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1655 "configure" +#line 1658 "configure" #include "confdefs.h" #include <sys/types.h> #include <signal.h> @@ -1668,7 +1671,7 @@ int main() { int i; ; return 0; } EOF -if { (eval echo configure:1672: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1675: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else @@ -1687,12 +1690,12 @@ EOF echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 -echo "configure:1691: checking for uid_t in sys/types.h" >&5 +echo "configure:1694: checking for uid_t in sys/types.h" >&5 if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1696 "configure" +#line 1699 "configure" #include "confdefs.h" #include <sys/types.h> EOF @@ -1721,12 +1724,12 @@ EOF fi echo $ac_n "checking for mode_t""... $ac_c" 1>&6 -echo "configure:1725: checking for mode_t" >&5 +echo "configure:1728: checking for mode_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1730 "configure" +#line 1733 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1754,12 +1757,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:1758: checking for size_t" >&5 +echo "configure:1761: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1763 "configure" +#line 1766 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1787,12 +1790,12 @@ EOF fi echo $ac_n "checking for pid_t""... $ac_c" 1>&6 -echo "configure:1791: checking for pid_t" >&5 +echo "configure:1794: checking for pid_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1796 "configure" +#line 1799 "configure" #include "confdefs.h" #include <sys/types.h> #if STDC_HEADERS @@ -1819,15 +1822,49 @@ EOF fi -for ac_func in getwd mkdir rename strdup strstr dup2 strerror valloc waitpid vasprintf strtoul +echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6 +echo "configure:1827: checking for st_blksize in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1832 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_blksize; +; return 0; } +EOF +if { (eval echo configure:1840: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blksize=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blksize=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6 +if test $ac_cv_struct_st_blksize = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLKSIZE 1 +EOF + +fi + +for ac_func in mkdir rename strstr dup2 strerror valloc waitpid vasprintf strtoul do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1826: checking for $ac_func" >&5 +echo "configure:1863: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1831 "configure" +#line 1868 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -1850,7 +1887,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:1854: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1891: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1876,15 +1913,15 @@ fi done -for ac_func in fchmod fsync ftime mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3 +for ac_func in fchmod fsync ftime mktemp putenv vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3 do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1883: checking for $ac_func" >&5 +echo "configure:1920: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1888 "configure" +#line 1925 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -1907,7 +1944,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:1911: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1932,13 +1969,269 @@ fi done +cat >> confdefs.h <<\EOF +#define HAVE_STRCHR 1 +EOF + +cat >> confdefs.h <<\EOF +#define HAVE_MEMCHR 1 +EOF + + +ac_safe=`echo "vfork.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for vfork.h""... $ac_c" 1>&6 +echo "configure:1984: checking for vfork.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1989 "configure" +#include "confdefs.h" +#include <vfork.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1994: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VFORK_H 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for working vfork""... $ac_c" 1>&6 +echo "configure:2019: checking for working vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + echo $ac_n "checking for vfork""... $ac_c" 1>&6 +echo "configure:2025: checking for vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2030 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char vfork(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vfork(); + +int main() { + +/* 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_vfork) || defined (__stub___vfork) +choke me +#else +vfork(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2053: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_vfork=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vfork=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vfork`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +else + cat > conftest.$ac_ext <<EOF +#line 2074 "configure" +#include "confdefs.h" +/* Thanks to Paul Eggert for this test. */ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_VFORK_H +#include <vfork.h> +#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 <vfork.h>, + but some compilers (e.g. gcc -O) don't grok <vfork.h>. + Test for this by using a static variable whose address + is put into a register that is clobbered by the vfork. */ +static +#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); + } + } +} +main() { + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (); + + 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 + ); + } +} +EOF +if { (eval echo configure:2169: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_func_vfork_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_vfork_works=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_vfork_works" 1>&6 +if test $ac_cv_func_vfork_works = no; then + cat >> confdefs.h <<\EOF +#define vfork fork +EOF + +fi + +echo $ac_n "checking whether closedir returns void""... $ac_c" 1>&6 +echo "configure:2192: checking whether closedir returns void" >&5 +if eval "test \"`echo '$''{'ac_cv_func_closedir_void'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_closedir_void=yes +else + cat > conftest.$ac_ext <<EOF +#line 2200 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_header_dirent> +int closedir(); main() { exit(closedir(opendir(".")) != 0); } +EOF +if { (eval echo configure:2206: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_func_closedir_void=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_closedir_void=yes +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_closedir_void" 1>&6 +if test $ac_cv_func_closedir_void = yes; then + cat >> confdefs.h <<\EOF +#define CLOSEDIR_VOID 1 +EOF + +fi + + echo $ac_n "checking for evidence of shadow passwords""... $ac_c" 1>&6 -echo "configure:1937: checking for evidence of shadow passwords" >&5 +echo "configure:2230: checking for evidence of shadow passwords" >&5 if test -f /etc/shadow \ || test -f /etc/security/passwd.adjunct ; then found="yes" echo $ac_n "checking for getspnam in -lsec""... $ac_c" 1>&6 -echo "configure:1942: checking for getspnam in -lsec" >&5 +echo "configure:2235: checking for getspnam in -lsec" >&5 ac_lib_var=`echo sec'_'getspnam | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1946,7 +2239,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lsec $LIBS" cat > conftest.$ac_ext <<EOF -#line 1950 "configure" +#line 2243 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -1957,7 +2250,7 @@ int main() { getspnam() ; return 0; } EOF -if { (eval echo configure:1961: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2254: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1987,12 +2280,12 @@ fi for ac_func in getspnam do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1991: checking for $ac_func" >&5 +echo "configure:2284: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1996 "configure" +#line 2289 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -2015,7 +2308,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2019: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2312: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2044,57 +2337,8 @@ else fi echo "$ac_t""$found" 1>&6 -echo $ac_n "checking for re_exec""... $ac_c" 1>&6 -echo "configure:2049: checking for re_exec" >&5 -if eval "test \"`echo '$''{'ac_cv_func_re_exec'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext <<EOF -#line 2054 "configure" -#include "confdefs.h" -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char re_exec(); below. */ -#include <assert.h> -/* Override any gcc2 internal prototype to avoid an error. */ -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char re_exec(); - -int main() { - -/* 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_re_exec) || defined (__stub___re_exec) -choke me -#else -re_exec(); -#endif - -; return 0; } -EOF -if { (eval echo configure:2077: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then - rm -rf conftest* - eval "ac_cv_func_re_exec=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_func_re_exec=no" -fi -rm -f conftest* -fi - -if eval "test \"`echo '$ac_cv_func_'re_exec`\" = yes"; then - echo "$ac_t""yes" 1>&6 - : -else - echo "$ac_t""no" 1>&6 -LIBOBJS="$LIBOBJS regex.o" -fi - echo $ac_n "checking whether utime accepts a null argument""... $ac_c" 1>&6 -echo "configure:2098: checking whether utime accepts a null argument" >&5 +echo "configure:2342: checking whether utime accepts a null argument" >&5 if eval "test \"`echo '$''{'ac_cv_func_utime_null'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2104,7 +2348,7 @@ if test "$cross_compiling" = yes; then ac_cv_func_utime_null=no else cat > conftest.$ac_ext <<EOF -#line 2108 "configure" +#line 2352 "configure" #include "confdefs.h" #include <sys/types.h> #include <sys/stat.h> @@ -2115,7 +2359,7 @@ exit(!(stat ("conftestdata", &s) == 0 && utime("conftestdata", (long *)0) == 0 && t.st_mtime - s.st_mtime < 120)); } EOF -if { (eval echo configure:2119: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2363: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_func_utime_null=yes else @@ -2139,7 +2383,7 @@ EOF fi echo $ac_n "checking for long file names""... $ac_c" 1>&6 -echo "configure:2143: checking for long file names" >&5 +echo "configure:2387: checking for long file names" >&5 if eval "test \"`echo '$''{'ac_cv_sys_long_file_names'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2184,7 +2428,7 @@ fi echo $ac_n "checking for working fnmatch function""... $ac_c" 1>&6 -echo "configure:2188: checking for working fnmatch function" >&5 +echo "configure:2432: checking for working fnmatch function" >&5 if eval "test \"`echo '$''{'ccvs_cv_sys_working_fnmatch'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2192,7 +2436,7 @@ else ccvs_cv_sys_working_fnmatch=no else cat > conftest.$ac_ext <<EOF -#line 2196 "configure" +#line 2440 "configure" #include "confdefs.h" #include <fnmatch.h> @@ -2204,7 +2448,7 @@ main () ? 0 : 1); } EOF -if { (eval echo configure:2208: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2452: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ccvs_cv_sys_working_fnmatch=yes else @@ -2229,12 +2473,12 @@ echo "$ac_t""$ccvs_cv_sys_working_fnmatch" 1>&6 # only looks in /etc/hosts), so we only look for -lsocket if we need # it. echo $ac_n "checking for connect""... $ac_c" 1>&6 -echo "configure:2233: checking for connect" >&5 +echo "configure:2477: checking for connect" >&5 if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2238 "configure" +#line 2482 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char connect(); below. */ @@ -2257,7 +2501,7 @@ connect(); ; return 0; } EOF -if { (eval echo configure:2261: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2505: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_connect=yes" else @@ -2277,7 +2521,7 @@ else case "$LIBS" in *-lnsl*) ;; *) echo $ac_n "checking for printf in -lnsl_s""... $ac_c" 1>&6 -echo "configure:2281: checking for printf in -lnsl_s" >&5 +echo "configure:2525: checking for printf in -lnsl_s" >&5 ac_lib_var=`echo nsl_s'_'printf | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2285,7 +2529,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lnsl_s $LIBS" cat > conftest.$ac_ext <<EOF -#line 2289 "configure" +#line 2533 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2296,7 +2540,7 @@ int main() { printf() ; return 0; } EOF -if { (eval echo configure:2300: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2544: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2327,7 +2571,7 @@ esac case "$LIBS" in *-lnsl*) ;; *) echo $ac_n "checking for printf in -lnsl""... $ac_c" 1>&6 -echo "configure:2331: checking for printf in -lnsl" >&5 +echo "configure:2575: checking for printf in -lnsl" >&5 ac_lib_var=`echo nsl'_'printf | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2335,7 +2579,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lnsl $LIBS" cat > conftest.$ac_ext <<EOF -#line 2339 "configure" +#line 2583 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2346,7 +2590,7 @@ int main() { printf() ; return 0; } EOF -if { (eval echo configure:2350: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2594: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2377,7 +2621,7 @@ esac case "$LIBS" in *-lsocket*) ;; *) echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6 -echo "configure:2381: checking for connect in -lsocket" >&5 +echo "configure:2625: checking for connect in -lsocket" >&5 ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2385,7 +2629,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <<EOF -#line 2389 "configure" +#line 2633 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2396,7 +2640,7 @@ int main() { connect() ; return 0; } EOF -if { (eval echo configure:2400: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2644: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2427,7 +2671,7 @@ esac case "$LIBS" in *-linet*) ;; *) echo $ac_n "checking for connect in -linet""... $ac_c" 1>&6 -echo "configure:2431: checking for connect in -linet" >&5 +echo "configure:2675: checking for connect in -linet" >&5 ac_lib_var=`echo inet'_'connect | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2435,7 +2679,7 @@ else ac_save_LIBS="$LIBS" LIBS="-linet $LIBS" cat > conftest.$ac_ext <<EOF -#line 2439 "configure" +#line 2683 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2446,7 +2690,7 @@ int main() { connect() ; return 0; } EOF -if { (eval echo configure:2450: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2694: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2496,55 +2740,73 @@ echo "default place for krb4 is $KRB4" krb_h= echo $ac_n "checking for krb.h""... $ac_c" 1>&6 -echo "configure:2500: checking for krb.h" >&5 -cat > conftest.$ac_ext <<EOF -#line 2502 "configure" +echo "configure:2744: checking for krb.h" >&5 +if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB4/include" + cat > conftest.$ac_ext <<EOF +#line 2749 "configure" #include "confdefs.h" #include <krb.h> int main() { int i; ; return 0; } EOF -if { (eval echo configure:2509: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2756: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* - krb_h=yes krb_incdir= + krb_h=yes krb_incdir=$KRB4/include else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* - if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then - hold_cflags=$CFLAGS - CFLAGS="$CFLAGS -I$KRB4/include" - cat > conftest.$ac_ext <<EOF -#line 2520 "configure" + CFLAGS=$hold_cflags + cat > conftest.$ac_ext <<EOF +#line 2765 "configure" #include "confdefs.h" #include <krb.h> int main() { int i; ; return 0; } EOF -if { (eval echo configure:2527: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2772: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* - krb_h=yes krb_incdir=$KRB4/include + krb_h=yes krb_incdir= else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -f conftest* - CFLAGS=$hold_cflags - fi fi rm -f conftest* + CFLAGS=$hold_cflags +else + cat > conftest.$ac_ext <<EOF +#line 2785 "configure" +#include "confdefs.h" +#include <krb.h> +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:2792: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + krb_h=yes krb_incdir= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +fi if test -z "$krb_h"; then cat > conftest.$ac_ext <<EOF -#line 2541 "configure" +#line 2803 "configure" #include "confdefs.h" #include <krb.h> int main() { int i; ; return 0; } EOF -if { (eval echo configure:2548: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2810: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* krb_h=yes krb_incdir= else @@ -2555,14 +2817,14 @@ else hold_cflags=$CFLAGS CFLAGS="$CFLAGS -I$KRB4/include/kerberosIV" cat > conftest.$ac_ext <<EOF -#line 2559 "configure" +#line 2821 "configure" #include "confdefs.h" #include <krb.h> int main() { int i; ; return 0; } EOF -if { (eval echo configure:2566: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2828: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* krb_h=yes krb_incdir=$KRB4/include/kerberosIV else @@ -2577,10 +2839,100 @@ rm -f conftest* fi echo "$ac_t""$krb_h" 1>&6 +includeopt= + if test -n "$krb_h"; then krb_lib= - echo $ac_n "checking for printf in -lkrb""... $ac_c" 1>&6 -echo "configure:2584: checking for printf in -lkrb" >&5 + if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then + hold_ldflags=$LDFLAGS + LDFLAGS="-L${KRB4}/lib $LDFLAGS" + echo $ac_n "checking for printf in -lkrb""... $ac_c" 1>&6 +echo "configure:2851: checking for printf in -lkrb" >&5 +ac_lib_var=`echo krb'_'printf | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lkrb $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2859 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char printf(); + +int main() { +printf() +; return 0; } +EOF +if { (eval echo configure:2870: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + krb_lib=yes krb_libdir=${KRB4}/lib +else + echo "$ac_t""no" 1>&6 +LDFLAGS=$hold_ldflags + # Using open here instead of printf so we don't + # get confused by the cached value for printf from above. + echo $ac_n "checking for open in -lkrb""... $ac_c" 1>&6 +echo "configure:2892: checking for open in -lkrb" >&5 +ac_lib_var=`echo krb'_'open | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lkrb $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2900 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char open(); + +int main() { +open() +; return 0; } +EOF +if { (eval echo configure:2911: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + krb_lib=yes krb_libdir= +else + echo "$ac_t""no" 1>&6 +fi + +fi + + LDFLAGS=$hold_ldflags + else + echo $ac_n "checking for printf in -lkrb""... $ac_c" 1>&6 +echo "configure:2936: checking for printf in -lkrb" >&5 ac_lib_var=`echo krb'_'printf | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2588,7 +2940,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lkrb $LIBS" cat > conftest.$ac_ext <<EOF -#line 2592 "configure" +#line 2944 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2599,7 +2951,7 @@ int main() { printf() ; return 0; } EOF -if { (eval echo configure:2603: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2955: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2617,11 +2969,9 @@ if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then krb_lib=yes krb_libdir= else echo "$ac_t""no" 1>&6 -if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then - krb_lib=yes krb_libdir=$KRB4/lib - fi fi + fi if test -n "$krb_lib"; then cat >> confdefs.h <<\EOF #define HAVE_KERBEROS 1 @@ -2635,7 +2985,7 @@ EOF hold_ldflags=$LDFLAGS test -n "${krb_libdir}" && LDFLAGS="$LDFLAGS -L${krb_libdir}" echo $ac_n "checking for printf in -ldes""... $ac_c" 1>&6 -echo "configure:2639: checking for printf in -ldes" >&5 +echo "configure:2989: checking for printf in -ldes" >&5 ac_lib_var=`echo des'_'printf | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2643,7 +2993,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldes $LIBS" cat > conftest.$ac_ext <<EOF -#line 2647 "configure" +#line 2997 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2654,7 +3004,7 @@ int main() { printf() ; return 0; } EOF -if { (eval echo configure:2658: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3008: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2677,19 +3027,18 @@ fi LDFLAGS=$hold_ldflags if test -n "$krb_incdir"; then includeopt="${includeopt} -I$krb_incdir" - fi fi fi for ac_func in krb_get_err_text do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:2688: checking for $ac_func" >&5 +echo "configure:3037: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2693 "configure" +#line 3042 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -2712,7 +3061,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2716: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3065: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2737,6 +3086,109 @@ fi done +GSSAPI=/usr/cygnus/kerbnet + +# Check whether --with-gssapi or --without-gssapi was given. +if test "${with_gssapi+set}" = set; then + withval="$with_gssapi" + GSSAPI=$withval +fi +echo "default place for GSSAPI is $GSSAPI" + + +echo $ac_n "checking for gssapi.h""... $ac_c" 1>&6 +echo "configure:3101: checking for gssapi.h" >&5 +hold_cppflags=$CPPFLAGS +CPPFLAGS="$CPPFLAGS -I$GSSAPI/include " +ac_safe=`echo "gssapi/gssapi.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for gssapi/gssapi.h""... $ac_c" 1>&6 +echo "configure:3106: checking for gssapi/gssapi.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3111 "configure" +#include "confdefs.h" +#include <gssapi/gssapi.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3116: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_GSSAPI 1 +EOF + + LIBS="$LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err" + includeopt="${includeopt} -I$GSSAPI/include" + # 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 $ac_n "checking for compile in -lgen""... $ac_c" 1>&6 +echo "configure:3141: checking for compile in -lgen" >&5 +ac_lib_var=`echo gen'_'compile | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lgen $LIBS" +cat > conftest.$ac_ext <<EOF +#line 3149 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char compile(); + +int main() { +compile() +; return 0; } +EOF +if { (eval echo configure:3160: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo gen | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lgen $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + +else + echo "$ac_t""no" 1>&6 +fi + +CPPFLAGS=$hold_cppflags + # Check whether --enable-encryption or --disable-encryption was given. if test "${enable_encryption+set}" = set; then enableval="$enable_encryption" @@ -2757,12 +3209,12 @@ EOF fi echo $ac_n "checking for gethostname""... $ac_c" 1>&6 -echo "configure:2761: checking for gethostname" >&5 +echo "configure:3213: checking for gethostname" >&5 if eval "test \"`echo '$''{'ac_cv_func_gethostname'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2766 "configure" +#line 3218 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char gethostname(); below. */ @@ -2785,7 +3237,7 @@ gethostname(); ; return 0; } EOF -if { (eval echo configure:2789: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3241: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_gethostname=yes" else @@ -2864,12 +3316,12 @@ if test "$enable_server" = yes; then for ac_func in crypt do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:2868: checking for $ac_func" >&5 +echo "configure:3320: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2873 "configure" +#line 3325 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -2892,7 +3344,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2896: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3348: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2918,7 +3370,7 @@ done if test "$ac_cv_func_crypt" = no; then echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 -echo "configure:2922: checking for crypt in -lcrypt" >&5 +echo "configure:3374: checking for crypt in -lcrypt" >&5 ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2926,7 +3378,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lcrypt $LIBS" cat > conftest.$ac_ext <<EOF -#line 2930 "configure" +#line 3382 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2937,7 +3389,7 @@ int main() { crypt() ; return 0; } EOF -if { (eval echo configure:2941: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3393: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2967,12 +3419,12 @@ fi for ac_func in crypt do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:2971: checking for $ac_func" >&5 +echo "configure:3423: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 2976 "configure" +#line 3428 "configure" #include "confdefs.h" /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func(); below. */ @@ -2995,7 +3447,7 @@ $ac_func(); ; return 0; } EOF -if { (eval echo configure:2999: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3451: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3029,6 +3481,51 @@ EOF fi fi # enable_server +echo $ac_n "checking for cygwin32""... $ac_c" 1>&6 +echo "configure:3486: checking for cygwin32" >&5 +if eval "test \"`echo '$''{'ccvs_cv_sys_cygwin32'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3491 "configure" +#include "confdefs.h" + +int main() { +return __CYGWIN32__; +; return 0; } +EOF +if { (eval echo configure:3498: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ccvs_cv_sys_cygwin32=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ccvs_cv_sys_cygwin32=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ccvs_cv_sys_cygwin32" 1>&6 +if test $ccvs_cv_sys_cygwin32 = yes; then + LIBOBJS="$LIBOBJS fncase.o" + LIBS="$LIBS -ladvapi32" + + cat >> confdefs.h <<\EOF +#define UTIME_EXPECTS_WRITABLE 1 +EOF + + + cat >> confdefs.h <<\EOF +#define USE_SETMODE_STDOUT 1 +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_SETMODE 1 +EOF + +fi + test -f src/options.h && ( echo "configure: warning: saving ./src/options.h in ./src/options.h-SAVED" 1>&2 echo "configure: warning: You may wish to check that local options have not been lost." 1>&2 @@ -3137,7 +3634,7 @@ done ac_given_srcdir=$srcdir ac_given_INSTALL="$INSTALL" -trap 'rm -fr `echo "Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ +trap 'rm -fr `echo "Makefile lib/Makefile src/Makefile zlib/Makefile diff/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ @@ -3185,6 +3682,7 @@ s%@csh_path@%$csh_path%g s%@LIBOBJS@%$LIBOBJS%g s%@KRB4@%$KRB4%g s%@includeopt@%$includeopt%g +s%@GSSAPI@%$GSSAPI%g CEOF EOF @@ -3226,7 +3724,7 @@ EOF cat >> $CONFIG_STATUS <<EOF -CONFIG_FILES=\${CONFIG_FILES-"Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ +CONFIG_FILES=\${CONFIG_FILES-"Makefile lib/Makefile src/Makefile zlib/Makefile diff/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ diff --git a/gnu/usr.bin/cvs/configure.in b/gnu/usr.bin/cvs/configure.in index 4f6334cc049..2296f71af59 100644 --- a/gnu/usr.bin/cvs/configure.in +++ b/gnu/usr.bin/cvs/configure.in @@ -10,8 +10,8 @@ dnl It is possible that we should just change the above required version dnl to 2.10; it seems like everyone is using 2.10 anyway, and there is dnl at least some sentiment that we should be using a version which has dnl --bindir (and correspondingly, using @bindir@ and friends in our -dnl Makefile.in files. I'm not sure exactly what version of autoconf -dnl introduced --bindir but I know 2.10 has it. +dnl Makefile.in files. Rumor has it that autoconf 2.7 +dnl introduced --bindir but the point is that 2.10 has it. AC_CONFIG_HEADER(config.h src/options.h) AC_PROG_CC @@ -50,6 +50,7 @@ fi AC_HEADER_STDC AC_CHECK_HEADERS(errno.h unistd.h string.h memory.h utime.h fcntl.h ndbm.h \ + limits.h sys/file.h \ sys/param.h sys/select.h sys/time.h sys/timeb.h \ io.h direct.h sys/bsdtypes.h sys/resource.h) AC_HEADER_SYS_WAIT @@ -61,8 +62,33 @@ AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_TYPE_PID_T -AC_REPLACE_FUNCS(getwd mkdir rename strdup strstr dup2 strerror valloc waitpid vasprintf strtoul) -AC_CHECK_FUNCS(fchmod fsync ftime mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3) +AC_STRUCT_ST_BLKSIZE +AC_REPLACE_FUNCS(mkdir rename strstr dup2 strerror valloc waitpid vasprintf strtoul) +AC_CHECK_FUNCS(fchmod fsync ftime mktemp putenv vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3) + +dnl +dnl The CVS coding standard (as specified in HACKING) is that if it exists +dnl in SunOS4 and ANSI, we use it. CVS itself, of course, therefore doesn't +dnl need HAVE_* defines for such functions, but diff wants them. +dnl +AC_DEFINE(HAVE_STRCHR) +AC_DEFINE(HAVE_MEMCHR) + +dnl +dnl AC_FUNC_VFORK is rather baroque. It seems to be rather more picky +dnl than, say, the Single Unix Specification (version 2), which simplifies +dnl a lot of cases by saying that the child process can't set any variables +dnl (thus avoiding problems with register allocation) or call any functions +dnl (thus avoiding problems with whether file descriptors are shared). +dnl It would be nice if we could just write to the Single Unix Specification. +dnl I think the only way to do redirection this way is by doing it in the +dnl parent, and then undoing it afterwards (analogous to windows-NT/run.c). +dnl That would appear to have a race condition if the user hits ^C (or +dnl some other signal) at the wrong time, as main_cleanup will try to use +dnl stdout/stderr. So maybe we are stuck with AC_FUNC_VFORK. +dnl +AC_FUNC_VFORK +AC_FUNC_CLOSEDIR_VOID dnl dnl Look for shadow password files before we go ahead and set getspnam. @@ -84,7 +110,24 @@ else fi AC_MSG_RESULT([$found]) -AC_CHECK_FUNC(re_exec, :, LIBOBJS="$LIBOBJS regex.o") +dnl We always use CVS's regular expression matcher. +dnl This is because: +dnl (1) If memory serves, the syntax of the regular expressions +dnl handled by re_exec is not consistent from system to system, which +dnl is a Bad Thing because CVS passes this syntax out to the user. +dnl We might have better luck with the POSIX interface, if we really +dnl want to look for a system-supplied matcher. +dnl (2) It is necessary to make _sure_ that we get a regex.h and regex.c +dnl that match each other. In particular, rx and the CVS/emacs +dnl regex.c have a different "struct re_pattern_buffer" and so using +dnl the system regex.h and our regex.c, or vice versa, will tend to +dnl cause a core dump. +dnl (3) Just as a random data point, CVS uses re_exec (a BSD interface); +dnl libdiff uses re_compile_pattern (a GNU interface, I think). Diff +dnl should probably be fixed to have the caller (CVS) supply the regexp +dnl matching. +dnl +dnl AC_CHECK_FUNC(re_exec, :, LIBOBJS="$LIBOBJS regex.o") AC_FUNC_UTIME_NULL AC_SYS_LONG_FILE_NAMES @@ -139,6 +182,10 @@ fi]) dnl dnl set $(KRB4) from --with-krb4=value -- WITH_KRB4 dnl +dnl If you change this, keep in mind that some systems have a bogus +dnl libkrb in the system libraries, so --with-krb4=value needs to +dnl override the system -lkrb. +dnl KRB4=/usr/kerberos define(WITH_KRB4,[ AC_ARG_WITH([krb4], @@ -151,15 +198,19 @@ WITH_KRB4 krb_h= AC_MSG_CHECKING([for krb.h]) -AC_TRY_LINK([#include <krb.h>],[int i;], - [krb_h=yes krb_incdir=], - [if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then - hold_cflags=$CFLAGS - CFLAGS="$CFLAGS -I$KRB4/include" - AC_TRY_LINK([#include <krb.h>],[int i;], - [krb_h=yes krb_incdir=$KRB4/include]) - CFLAGS=$hold_cflags - fi]) +if test "$cross_compiling" != yes && test -r $KRB4/include/krb.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB4/include" + AC_TRY_LINK([#include <krb.h>],[int i;], + [krb_h=yes krb_incdir=$KRB4/include], + [CFLAGS=$hold_cflags + AC_TRY_LINK([#include <krb.h>],[int i;], + [krb_h=yes krb_incdir=])]) + CFLAGS=$hold_cflags +else + AC_TRY_LINK([#include <krb.h>],[int i;], + [krb_h=yes krb_incdir=]) +fi if test -z "$krb_h"; then AC_TRY_LINK([#include <krb.h>],[int i;], [krb_h=yes krb_incdir=], @@ -173,12 +224,22 @@ if test -z "$krb_h"; then fi AC_MSG_RESULT($krb_h) +includeopt= +AC_SUBST(includeopt) if test -n "$krb_h"; then krb_lib= - AC_CHECK_LIB(krb,printf,[krb_lib=yes krb_libdir=], - [if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then - krb_lib=yes krb_libdir=$KRB4/lib - fi]) + if test "$cross_compiling" != yes && test -r $KRB4/lib/libkrb.a; then + hold_ldflags=$LDFLAGS + LDFLAGS="-L${KRB4}/lib $LDFLAGS" + AC_CHECK_LIB(krb,printf,[krb_lib=yes krb_libdir=${KRB4}/lib], + [LDFLAGS=$hold_ldflags + # Using open here instead of printf so we don't + # get confused by the cached value for printf from above. + AC_CHECK_LIB(krb,open,[krb_lib=yes krb_libdir=])]) + LDFLAGS=$hold_ldflags + else + AC_CHECK_LIB(krb,printf,[krb_lib=yes krb_libdir=]) + fi if test -n "$krb_lib"; then AC_DEFINE(HAVE_KERBEROS) test -n "${krb_libdir}" && LIBS="${LIBS} -L${krb_libdir}" @@ -192,13 +253,37 @@ if test -n "$krb_h"; then LDFLAGS=$hold_ldflags if test -n "$krb_incdir"; then includeopt="${includeopt} -I$krb_incdir" - AC_SUBST(includeopt) fi fi fi AC_CHECK_FUNCS(krb_get_err_text) dnl +dnl Use --with-gssapi=DIR to enable GSSAPI support. +dnl +GSSAPI=/usr/cygnus/kerbnet +define(WITH_GSSAPI,[ +AC_ARG_WITH([gssapi], + [ --with-gssapi=value GSSAPI directory], + [GSSAPI=$withval], +)dnl +echo "default place for GSSAPI is $GSSAPI" +AC_SUBST(GSSAPI)])dnl +WITH_GSSAPI + +AC_MSG_CHECKING([for gssapi.h]) +hold_cppflags=$CPPFLAGS +CPPFLAGS="$CPPFLAGS -I$GSSAPI/include " +AC_CHECK_HEADER(gssapi/gssapi.h, + [AC_DEFINE(HAVE_GSSAPI) + LIBS="$LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err" + includeopt="${includeopt} -I$GSSAPI/include" + # 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. + AC_CHECK_LIB(gen, compile)]) +CPPFLAGS=$hold_cppflags + +dnl dnl Use --with-encryption to turn on encryption support dnl AC_ARG_ENABLE(encryption, @@ -263,6 +348,29 @@ if test "$ac_cv_func_crypt" = yes; then fi fi # enable_server +dnl On cygwin32, we configure like a Unix system, but we use the +dnl Windows support code in lib/fncase.c to handle the case +dnl insensitive file system. We also need some support libraries. We +dnl do this at the end so that the new libraries are added at the end +dnl of LIBS. +AC_CACHE_CHECK(for cygwin32, ccvs_cv_sys_cygwin32, +[AC_TRY_COMPILE([], [return __CYGWIN32__;], +ccvs_cv_sys_cygwin32=yes, ccvs_cv_sys_cygwin32=no)]) +if test $ccvs_cv_sys_cygwin32 = yes; then + LIBOBJS="$LIBOBJS fncase.o" + LIBS="$LIBS -ladvapi32" + + dnl On Windows you can only change file times if you can write to + dnl the file. cygwin32 should really handle this for us, but as of + dnl January 1998 it doesn't. + AC_DEFINE(UTIME_EXPECTS_WRITABLE) + + dnl On Windows we must use setmode to change between binary and text + dnl mode. + AC_DEFINE(USE_SETMODE_STDOUT) + AC_DEFINE(HAVE_SETMODE) +fi + test -f src/options.h && ( AC_MSG_WARN(saving ./src/options.h in ./src/options.h-SAVED) AC_MSG_WARN(You may wish to check that local options have not been lost.) @@ -270,7 +378,7 @@ test -f src/options.h && ( cp ./src/options.h ./src/options.h-SAVED ) -AC_OUTPUT(Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ +AC_OUTPUT(Makefile lib/Makefile src/Makefile zlib/Makefile diff/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ diff --git a/gnu/usr.bin/cvs/lib/getdate.c b/gnu/usr.bin/cvs/lib/getdate.c index d4d93506e90..c97fd30af03 100644 --- a/gnu/usr.bin/cvs/lib/getdate.c +++ b/gnu/usr.bin/cvs/lib/getdate.c @@ -14,7 +14,6 @@ static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; -** send any email to Rich. ** ** This grammar has 10 shift/reduce conflicts. ** @@ -185,12 +184,12 @@ static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; -#line 180 "../../work/ccvs/lib/getdate.y" +#line 179 "../../work/ccvs/lib/getdate.y" typedef union { time_t Number; enum _MERIDIAN Meridian; } YYSTYPE; -#line 194 "y.tab.c" +#line 193 "y.tab.c" #define tAGO 257 #define tDAY 258 #define tDAYZONE 259 @@ -403,7 +402,7 @@ YYSTYPE yylval; short yyss[YYSTACKSIZE]; YYSTYPE yyvs[YYSTACKSIZE]; #define yystacksize YYSTACKSIZE -#line 393 "../../work/ccvs/lib/getdate.y" +#line 392 "../../work/ccvs/lib/getdate.y" /* Month and day table. */ static TABLE const MonthDayTable[] = { @@ -618,10 +617,14 @@ ToSeconds(Hours, Minutes, Seconds, Meridian) 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 (); @@ -927,17 +930,34 @@ get_date(p, now) yyInput = p; if (now == NULL) { + struct tm *gmt_ptr; + now = &ftz; (void)time (&nowtime); - if (! (tm = gmtime (&nowtime))) - return -1; - gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + 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; - - ftz.timezone = difftm (&gmt, tm) / 60; + + 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; } @@ -1020,7 +1040,7 @@ main(ac, av) /* NOTREACHED */ } #endif /* defined(TEST) */ -#line 1024 "y.tab.c" +#line 1044 "y.tab.c" #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept @@ -1162,37 +1182,37 @@ yyreduce: switch (yyn) { case 3: -#line 198 "../../work/ccvs/lib/getdate.y" +#line 197 "../../work/ccvs/lib/getdate.y" { yyHaveTime++; } break; case 4: -#line 201 "../../work/ccvs/lib/getdate.y" +#line 200 "../../work/ccvs/lib/getdate.y" { yyHaveZone++; } break; case 5: -#line 204 "../../work/ccvs/lib/getdate.y" +#line 203 "../../work/ccvs/lib/getdate.y" { yyHaveDate++; } break; case 6: -#line 207 "../../work/ccvs/lib/getdate.y" +#line 206 "../../work/ccvs/lib/getdate.y" { yyHaveDay++; } break; case 7: -#line 210 "../../work/ccvs/lib/getdate.y" +#line 209 "../../work/ccvs/lib/getdate.y" { yyHaveRel++; } break; case 9: -#line 216 "../../work/ccvs/lib/getdate.y" +#line 215 "../../work/ccvs/lib/getdate.y" { yyHour = yyvsp[-1].Number; yyMinutes = 0; @@ -1201,7 +1221,7 @@ case 9: } break; case 10: -#line 222 "../../work/ccvs/lib/getdate.y" +#line 221 "../../work/ccvs/lib/getdate.y" { yyHour = yyvsp[-3].Number; yyMinutes = yyvsp[-1].Number; @@ -1210,7 +1230,7 @@ case 10: } break; case 11: -#line 228 "../../work/ccvs/lib/getdate.y" +#line 227 "../../work/ccvs/lib/getdate.y" { yyHour = yyvsp[-3].Number; yyMinutes = yyvsp[-1].Number; @@ -1220,7 +1240,7 @@ case 11: } break; case 12: -#line 235 "../../work/ccvs/lib/getdate.y" +#line 234 "../../work/ccvs/lib/getdate.y" { yyHour = yyvsp[-5].Number; yyMinutes = yyvsp[-3].Number; @@ -1229,7 +1249,7 @@ case 12: } break; case 13: -#line 241 "../../work/ccvs/lib/getdate.y" +#line 240 "../../work/ccvs/lib/getdate.y" { yyHour = yyvsp[-5].Number; yyMinutes = yyvsp[-3].Number; @@ -1240,56 +1260,56 @@ case 13: } break; case 14: -#line 251 "../../work/ccvs/lib/getdate.y" +#line 250 "../../work/ccvs/lib/getdate.y" { yyTimezone = yyvsp[0].Number; yyDSTmode = DSToff; } break; case 15: -#line 255 "../../work/ccvs/lib/getdate.y" +#line 254 "../../work/ccvs/lib/getdate.y" { yyTimezone = yyvsp[0].Number; yyDSTmode = DSTon; } break; case 16: -#line 260 "../../work/ccvs/lib/getdate.y" +#line 259 "../../work/ccvs/lib/getdate.y" { yyTimezone = yyvsp[-1].Number; yyDSTmode = DSTon; } break; case 17: -#line 266 "../../work/ccvs/lib/getdate.y" +#line 265 "../../work/ccvs/lib/getdate.y" { yyDayOrdinal = 1; yyDayNumber = yyvsp[0].Number; } break; case 18: -#line 270 "../../work/ccvs/lib/getdate.y" +#line 269 "../../work/ccvs/lib/getdate.y" { yyDayOrdinal = 1; yyDayNumber = yyvsp[-1].Number; } break; case 19: -#line 274 "../../work/ccvs/lib/getdate.y" +#line 273 "../../work/ccvs/lib/getdate.y" { yyDayOrdinal = yyvsp[-1].Number; yyDayNumber = yyvsp[0].Number; } break; case 20: -#line 280 "../../work/ccvs/lib/getdate.y" +#line 279 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[-2].Number; yyDay = yyvsp[0].Number; } break; case 21: -#line 284 "../../work/ccvs/lib/getdate.y" +#line 283 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[-4].Number; yyDay = yyvsp[-2].Number; @@ -1297,7 +1317,7 @@ case 21: } break; case 22: -#line 289 "../../work/ccvs/lib/getdate.y" +#line 288 "../../work/ccvs/lib/getdate.y" { /* ISO 8601 format. yyyy-mm-dd. */ yyYear = yyvsp[-2].Number; @@ -1306,7 +1326,7 @@ case 22: } break; case 23: -#line 295 "../../work/ccvs/lib/getdate.y" +#line 294 "../../work/ccvs/lib/getdate.y" { /* e.g. 17-JUN-1992. */ yyDay = yyvsp[-2].Number; @@ -1315,14 +1335,14 @@ case 23: } break; case 24: -#line 301 "../../work/ccvs/lib/getdate.y" +#line 300 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[-1].Number; yyDay = yyvsp[0].Number; } break; case 25: -#line 305 "../../work/ccvs/lib/getdate.y" +#line 304 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[-3].Number; yyDay = yyvsp[-2].Number; @@ -1330,14 +1350,14 @@ case 25: } break; case 26: -#line 310 "../../work/ccvs/lib/getdate.y" +#line 309 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[0].Number; yyDay = yyvsp[-1].Number; } break; case 27: -#line 314 "../../work/ccvs/lib/getdate.y" +#line 313 "../../work/ccvs/lib/getdate.y" { yyMonth = yyvsp[-1].Number; yyDay = yyvsp[-2].Number; @@ -1345,68 +1365,68 @@ case 27: } break; case 28: -#line 321 "../../work/ccvs/lib/getdate.y" +#line 320 "../../work/ccvs/lib/getdate.y" { yyRelSeconds = -yyRelSeconds; yyRelMonth = -yyRelMonth; } break; case 30: -#line 328 "../../work/ccvs/lib/getdate.y" +#line 327 "../../work/ccvs/lib/getdate.y" { yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L; } break; case 31: -#line 331 "../../work/ccvs/lib/getdate.y" +#line 330 "../../work/ccvs/lib/getdate.y" { yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L; } break; case 32: -#line 334 "../../work/ccvs/lib/getdate.y" +#line 333 "../../work/ccvs/lib/getdate.y" { yyRelSeconds += yyvsp[0].Number * 60L; } break; case 33: -#line 337 "../../work/ccvs/lib/getdate.y" +#line 336 "../../work/ccvs/lib/getdate.y" { yyRelSeconds += yyvsp[-1].Number; } break; case 34: -#line 340 "../../work/ccvs/lib/getdate.y" +#line 339 "../../work/ccvs/lib/getdate.y" { yyRelSeconds += yyvsp[-1].Number; } break; case 35: -#line 343 "../../work/ccvs/lib/getdate.y" +#line 342 "../../work/ccvs/lib/getdate.y" { yyRelSeconds++; } break; case 36: -#line 346 "../../work/ccvs/lib/getdate.y" +#line 345 "../../work/ccvs/lib/getdate.y" { yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; } break; case 37: -#line 349 "../../work/ccvs/lib/getdate.y" +#line 348 "../../work/ccvs/lib/getdate.y" { yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number; } break; case 38: -#line 352 "../../work/ccvs/lib/getdate.y" +#line 351 "../../work/ccvs/lib/getdate.y" { yyRelMonth += yyvsp[0].Number; } break; case 39: -#line 357 "../../work/ccvs/lib/getdate.y" +#line 356 "../../work/ccvs/lib/getdate.y" { if (yyHaveTime && yyHaveDate && !yyHaveRel) yyYear = yyvsp[0].Number; @@ -1434,18 +1454,18 @@ case 39: } break; case 40: -#line 384 "../../work/ccvs/lib/getdate.y" +#line 383 "../../work/ccvs/lib/getdate.y" { yyval.Meridian = MER24; } break; case 41: -#line 387 "../../work/ccvs/lib/getdate.y" +#line 386 "../../work/ccvs/lib/getdate.y" { yyval.Meridian = yyvsp[0].Meridian; } break; -#line 1449 "y.tab.c" +#line 1469 "y.tab.c" } yyssp -= yym; yystate = *yyssp; diff --git a/gnu/usr.bin/cvs/lib/getdate.y b/gnu/usr.bin/cvs/lib/getdate.y index 8ed565caacc..fdb177d04c1 100644 --- a/gnu/usr.bin/cvs/lib/getdate.y +++ b/gnu/usr.bin/cvs/lib/getdate.y @@ -4,7 +4,6 @@ ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; -** send any email to Rich. ** ** This grammar has 10 shift/reduce conflicts. ** @@ -604,10 +603,14 @@ ToSeconds(Hours, Minutes, Seconds, Meridian) 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 (); @@ -913,17 +916,34 @@ get_date(p, now) yyInput = p; if (now == NULL) { + struct tm *gmt_ptr; + now = &ftz; (void)time (&nowtime); - if (! (tm = gmtime (&nowtime))) - return -1; - gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + 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; - - ftz.timezone = difftm (&gmt, tm) / 60; + + 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; } diff --git a/gnu/usr.bin/cvs/lib/getwd.c b/gnu/usr.bin/cvs/lib/getwd.c deleted file mode 100644 index 573a7889244..00000000000 --- a/gnu/usr.bin/cvs/lib/getwd.c +++ /dev/null @@ -1,35 +0,0 @@ -/* getwd.c -- get current working directory pathname - Copyright (C) 1992 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Some systems which include both getwd() and getcwd() have an implementation - of getwd() which is much faster than getcwd(). As a result, we use the - system's getwd() if it is available */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "system.h" - -/* Get the current working directory into PATHNAME */ - -char * -getwd (pathname) - char *pathname; -{ - return (getcwd(pathname, PATH_MAX)); -} diff --git a/gnu/usr.bin/cvs/lib/strdup.c b/gnu/usr.bin/cvs/lib/strdup.c deleted file mode 100644 index 46fc8a0d6db..00000000000 --- a/gnu/usr.bin/cvs/lib/strdup.c +++ /dev/null @@ -1,43 +0,0 @@ -/* strdup.c -- return a newly allocated copy of a string - Copyright (C) 1990 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef STDC_HEADERS -#include <string.h> -#include <stdlib.h> -#else -char *malloc (); -char *strcpy (); -#endif - -/* Return a newly allocated copy of STR, - or 0 if out of memory. */ - -char * -strdup (str) - char *str; -{ - char *newstr; - - newstr = (char *) malloc (strlen (str) + 1); - if (newstr) - strcpy (newstr, str); - return newstr; -} diff --git a/gnu/usr.bin/cvs/src/Makefile.in b/gnu/usr.bin/cvs/src/Makefile.in index 3a573fa9f14..dfcd4d6e8d0 100644 --- a/gnu/usr.bin/cvs/src/Makefile.in +++ b/gnu/usr.bin/cvs/src/Makefile.in @@ -60,7 +60,7 @@ HEADERS = buffer.h cvs.h rcs.h hash.h myndbm.h \ TAGFILES = $(HEADERS) options.h.in $(SOURCES) DISTFILES = .cvsignore Makefile.in \ - ChangeLog ChangeLog-96 ChangeLog-9395 ChangeLog-9194 \ + ChangeLog ChangeLog-97 ChangeLog-96 ChangeLog-9395 ChangeLog-9194 \ sanity.sh cvsbug.sh $(TAGFILES) build_src.com PROGS = cvs cvsbug @@ -144,10 +144,10 @@ dist-dir: # Linking rules. -$(PROGS): ../lib/libcvs.a ../zlib/libz.a +$(PROGS): ../lib/libcvs.a ../zlib/libz.a ../diff/libdiff.a cvs: $(OBJECTS) - $(CC) $(OBJECTS) ../lib/libcvs.a ../zlib/libz.a $(LIBS) $(LDFLAGS) -o $@ + $(CC) $(OBJECTS) ../lib/libcvs.a ../zlib/libz.a ../diff/libdiff.a $(LIBS) $(LDFLAGS) -o $@ xlint: $(SOURCES) files= ; \ diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c index e36e729b819..3c1eef7fe11 100644 --- a/gnu/usr.bin/cvs/src/checkout.c +++ b/gnu/usr.bin/cvs/src/checkout.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * specified in the README file that comes with the CVS source distribution. * * Create Version * @@ -61,6 +61,7 @@ static const char *const checkout_usage[] = "\t-d dir\tCheck out into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", + "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -76,6 +77,7 @@ static const char *const export_usage[] = "\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 }; @@ -257,7 +259,7 @@ checkout (argc, argv) server send the check-in and update programs for the various modules/dirs requested. If we turn this off and simply request the names of the modules and directories (as - below in !expand_modules), those files (CVS/Checking.prog + below in !expand_modules), those files (CVS/Checkin.prog or CVS/Update.prog) don't get created. Grrr. */ expand_modules = (!cat && !status && !pipeout @@ -332,54 +334,16 @@ checkout (argc, argv) } db = open_module (); - /* - * if we have more than one argument and where was specified, we make the - * where, cd into it, and try to shorten names as much as possible. - * Otherwise, we pass the where as a single argument to do_module. - */ - if (argc > 1 && where != NULL) - { - (void) CVS_MKDIR (where, 0777); - if ( CVS_CHDIR (where) < 0) - error (1, errno, "cannot chdir to %s", where); - preload_update_dir = xstrdup (where); - where = (char *) NULL; - if (!isfile (CVSADM)) - { - char *repository; - repository = xmalloc (strlen (CVSroot_directory) + 80); - (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, - CVSROOTADM, CVSNULLREPOS); - if (!isfile (repository)) - { - mode_t omask; - omask = umask (cvsumask); - (void) CVS_MKDIR (repository, 0777); - (void) umask (omask); - } + /* 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. */ - /* I'm not sure whether this check is redundant. */ - if (!isdir (repository)) - error (1, 0, "there is no repository %s", repository); + if (argc > 1) + shorten = 0; - Create_Admin (".", preload_update_dir, repository, - (char *) NULL, (char *) NULL, 0); - 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 (preload_update_dir, repository); -#endif - } - free (repository); - } - } /* If we will be calling history_write, work out the name to pass it. */ @@ -396,29 +360,6 @@ checkout (argc, argv) history_name = date; } - /* - * if where was specified (-d) and we have not taken care of it already - * with the multiple arg stuff, and it was not a simple directory name - * but rather a path, we strip off everything but the last component and - * attempt to cd to the indicated place. where then becomes simply the - * last component - */ - if (where != NULL && strchr (where, '/') != NULL) - { - char *slash; - - slash = strrchr (where, '/'); - *slash = '\0'; - - if ( CVS_CHDIR (where) < 0) - error (1, errno, "cannot chdir to %s", where); - - preload_update_dir = xstrdup (where); - - where = slash + 1; - if (*where == '\0') - where = NULL; - } for (i = 0; i < argc; i++) err += do_module (db, argv[i], m_type, "Updating", checkout_proc, @@ -456,6 +397,8 @@ safe_location () hardpath[x] = '\0'; } current = xgetwd (); + if (current == NULL) + error (1, errno, "could not get working directory"); hardpath_len = strlen (hardpath); if (strlen (current) >= hardpath_len && strncmp (current, hardpath, hardpath_len) == 0) @@ -506,17 +449,18 @@ build_one_dir (repository, dirpath, sticky) if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", dirpath, repository, - sticky ? (char *) NULL : tag, - sticky ? (char *) NULL : date, + if (Create_Admin (".", dirpath, repository, + sticky ? (char *) NULL : tag, + sticky ? (char *) NULL : date, - /* FIXME? This is a guess. If it is important - for nonbranch to be set correctly here I - think we need to write it one way now and - then rewrite it later via WriteTag, once - we've had a chance to call RCS_nodeisbranch - on each file. */ - 0); + /* 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)) + return; if (!noexec) { @@ -536,11 +480,11 @@ build_one_dir (repository, dirpath, sticky) */ /* ARGSUSED */ static int -checkout_proc (pargc, argv, where, mwhere, mfile, shorten, +checkout_proc (pargc, argv, where_orig, mwhere, mfile, shorten, local_specified, omodule, msg) int *pargc; char **argv; - char *where; + char *where_orig; char *mwhere; char *mfile; int shorten; @@ -551,11 +495,9 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, int err = 0; int which; char *cp; - char *cp2; char *repository; - char *xwhere = NULL; char *oldupdate = NULL; - char *prepath; + char *where; /* * OK, so we're doing the checkout! Our args are as follows: @@ -563,152 +505,172 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, * where contains where to put it (if supplied by checkout) * mwhere contains the module name or -d from module file * mfile says do only that part of the module - * shorten = TRUE says shorten as much as possible + * 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 */ - repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + 5); + /* 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 (CVSroot_directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + + 10); (void) sprintf (repository, "%s/%s", CVSroot_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); - /* fix up argv[] for the case of partial modules */ - if (mfile != NULL) - { - char *file; - /* if mfile is really a path, straighten it out first */ - if ((cp = strrchr (mfile, '/')) != NULL) + /* 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) { - *cp = 0; - repository = xrealloc (repository, - strlen (repository) + strlen (mfile) + 10); - (void) strcat (repository, "/"); - (void) strcat (repository, mfile); - - /* - * Now we need to fill in the where correctly. if !shorten, tack - * the rest of the path onto where if where is filled in - * otherwise tack the rest of the path onto mwhere and make that - * the where - * - * If shorten is enabled, we might use mwhere to set where if - * nobody set it yet, so we'll need to setup mwhere as the last - * component of the path we are tacking onto repository - */ - if (!shorten) - { - if (where != NULL) - { - xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", where, mfile); - } - else - { - xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", mwhere, mfile); - } - where = xwhere; - } - else - { - char *slash; - - if ((slash = strrchr (mfile, '/')) != NULL) - mwhere = slash + 1; - else - mwhere = mfile; - } - mfile = cp + 1; + /* 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); } - - file = xmalloc (strlen (repository) + strlen (mfile) + 5); - (void) sprintf (file, "%s/%s", repository, mfile); - if (isdir (file)) + else if (mwhere != NULL) { + /* Second preference is the value of mwhere, which is from + the `-d' flag in the modules file. */ - /* - * The portion of a module was a directory, so kludge up where to - * be the subdir, and fix up repository - */ - free (repository); - repository = xstrdup (file); - - /* - * At this point, if shorten is not enabled, we make where either - * where with mfile concatenated, or if where hadn't been set we - * set it to mwhere with mfile concatenated. - * - * If shorten is enabled and where hasn't been set yet, then where - * becomes mfile - */ - if (!shorten) - { - if (where != NULL) - { - xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", where, mfile); - } - else - { - xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", mwhere, mfile); - } - where = xwhere; - } - else if (where == NULL) - where = mfile; + (void) strcpy (where, mwhere); } else { - int i; - - /* - * The portion of a module was a file, so kludge up argv to be - * correct - */ - for (i = 1; i < *pargc; i++)/* free the old ones */ - free (argv[i]); - /* FIXME: Normally one has to realloc argv to make sure - argv[1] exists. But this argv does not points to the - beginning of an allocated block. For now we allocate - at least 4 entries for argv (in line2argv). */ - argv[1] = xstrdup (mfile); /* set up the new one */ - *pargc = 2; - - /* where gets mwhere if where isn't set */ - if (where == NULL) - where = mwhere; + /* Third preference is the directory specified in argv[0] + which is this module'e directory in the repository. */ + + (void) strcpy (where, argv[0]); } - free (file); } - - /* - * if shorten is enabled and where isn't specified yet, we pluck the last - * directory component of argv[0] and make it the where - */ - if (shorten && where == NULL) + else { - if ((cp = strrchr (argv[0], '/')) != NULL) + /* Use the same preferences here, bug don't shorten -- that + is, tack on where_orig if it exists. */ + + *where = '\0'; + + if (where_orig != NULL) { - xwhere = xstrdup (cp + 1); - where = xwhere; + (void) strcat (where, where_orig); + (void) strcat (where, "/"); } + + if (mwhere != NULL) + (void) strcat (where, mwhere); + else + (void) strcat (where, argv[0]); } + strip_trailing_slashes (where); /* necessary? */ + - /* if where is still NULL, use mwhere if set or the argv[0] dir */ - if (where == NULL) + /* 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) { - if (mwhere) - where = mwhere; + /* 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 { - xwhere = xstrdup (argv[0]); - where = xwhere; + /* It's a file, which means we have to screw around with + argv. */ + + int i; + + + /* Paranoia check. */ + + if (*pargc > 1) + { + error (0, 0, "checkout_proc: trashing argv elements!"); + for (i = 1; i < *pargc; i++) + { + error (0, 0, "checkout_proc: argv[%d] `%s'", + i, argv[i]); + } + } + + for (i = 1; i < *pargc; i++) + free (argv[i]); + argv[1] = xstrdup (mfile); + (*pargc) = 2; } + free (path); } if (preload_update_dir != NULL) @@ -725,6 +687,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* * 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. */ /* @@ -733,20 +701,11 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ if (!pipeout) { - size_t root_len; struct dir_to_build *head; + char *reposcopy; - /* We need to tell build_dirs not only the path we want it to - build, but also the repositories we want it to populate the - path with. To accomplish this, we walk the path backwards, - one pathname component at a time, constucting a linked - list of struct dir_to_build. */ - prepath = xstrdup (repository); - - /* We don't want to start stripping elements off prepath after we - get to CVSROOT. */ - root_len = strlen (CVSroot_directory); - if (strncmp (repository, CVSroot_directory, root_len) != 0) + if (strncmp (repository, CVSroot_directory, + strlen (CVSroot_directory)) != 0) error (1, 0, "\ internal error: %s doesn't start with %s in checkout_proc", repository, CVSroot_directory); @@ -760,9 +719,14 @@ internal error: %s doesn't start with %s in checkout_proc", head->dirpath = xstrdup (where); head->next = NULL; - cp = strrchr (where, '/'); - cp2 = strrchr (prepath, '/'); + /* 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 = strrchr (where, '/'); while (cp != NULL) { struct dir_to_build *new; @@ -771,42 +735,97 @@ internal error: %s doesn't start with %s in checkout_proc", new->dirpath = xmalloc (strlen (where)); strncpy (new->dirpath, where, cp - where); new->dirpath[cp - where] = '\0'; - if (cp2 == NULL || cp2 < prepath + root_len) + + /* 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: + . -> . (this is where we executed the cmd) + what -> Emptydir (generated dir -- not in repos) + ever -> . (same as "cd what/ever; cvs co -N foo") + bar -> Emptydir (generated dir -- not in repos) + baz -> quux (finally!) */ + + if (strcmp (reposcopy, CVSroot_directory) == 0) { - /* Don't walk up past CVSROOT; instead put in CVSNULLREPOS. */ - new->repository = - xmalloc (strlen (CVSroot_directory) + 80); - (void) sprintf (new->repository, "%s/%s/%s", - CVSroot_directory, - CVSROOTADM, CVSNULLREPOS); - if (!isfile (new->repository)) - { - mode_t omask; - omask = umask (cvsumask); - if (CVS_MKDIR (new->repository, 0777) < 0) - error (0, errno, "cannot create %s", - new->repository); - (void) umask (omask); - } + /* We can't walk up past CVSROOT. Instead, the + repository should be Emptydir. */ + new->repository = emptydir_name (); } else { - new->repository = xmalloc (strlen (prepath)); - strncpy (new->repository, prepath, cp2 - prepath); - new->repository[cp2 - prepath] = '\0'; + if ((where_orig != NULL) + && (strcmp (new->dirpath, where_orig) == 0)) + { + /* It's the case that the user specified a + * destination directory with the "-d" flag. The + * repository in this directory should be "." + * since the user's command is equivalent to: + * + * cd <dir>; cvs co blah */ + + strcpy (reposcopy, CVSroot_directory); + goto allocate_repos; + } + else if (mwhere != NULL) + { + /* This is a generated directory, so point to + CVSNULLREPOS. */ + + new->repository = emptydir_name (); + } + else + { + /* It's a directory in the repository! */ + + char *rp = strrchr (reposcopy, '/'); + + /* We'll always be below CVSROOT, but check for + paranoia's sake. */ + if (rp == NULL) + error (1, 0, + "internal error: %s doesn't contain a slash", + reposcopy); + + *rp = '\0'; + + allocate_repos: + new->repository = xmalloc (strlen (reposcopy) + 5); + (void) strcpy (new->repository, reposcopy); + + if (strcmp (reposcopy, CVSroot_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, "/."); + } + } } + new->next = head; head = new; cp = findslash (where, cp - 1); - cp2 = findslash (prepath, cp2 - 1); } - /* First build the top-level CVSADM directory. The value we - pass in here for repository is probably wrong; see modules3-7f - in the testsuite. */ - build_one_dir (head->repository != NULL ? head->repository : prepath, - ".", *pargc <= 1); + /* clean up */ + free (reposcopy); + + /* The top-level CVSADM directory should always be + CVSroot_directory. Create it. + + It may be argued that we shouldn't set any sticky bits for + the top-level repository. FIXME? */ + build_one_dir (CVSroot_directory, ".", *pargc <= 1); /* * build dirs on the path if necessary and leave us in the bottom @@ -817,14 +836,10 @@ internal error: %s doesn't start with %s in checkout_proc", if (build_dirs_and_chdir (head, *pargc <= 1) != 0) { error (0, 0, "ignoring module %s", omodule); - free (prepath); err = 1; goto out; } - /* clean up */ - free (prepath); - /* set up the repository (or make sure the old one matches) */ if (!isfile (CVSADM)) { @@ -837,7 +852,7 @@ internal error: %s doesn't start with %s in checkout_proc", error (1, 0, "there is no repository %s", repository); Create_Admin (".", preload_update_dir, repository, - (char *) NULL, (char *) NULL, 0); + (char *) NULL, (char *) NULL, 0, 0); fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); @@ -860,7 +875,7 @@ internal error: %s doesn't start with %s in checkout_proc", then rewrite it later via WriteTag, once we've had a chance to call RCS_nodeisbranch on each file. */ - 0); + 0, 0); } } else @@ -1004,8 +1019,7 @@ internal error: %s doesn't start with %s in checkout_proc", out: free (preload_update_dir); preload_update_dir = oldupdate; - if (xwhere != NULL) - free (xwhere); + free (where); free (repository); return (err); } @@ -1026,6 +1040,32 @@ findslash (start, p) return (p); } +/* 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 () +{ + char *repository; + + repository = xmalloc (strlen (CVSroot_directory) + + sizeof (CVSROOTADM) + + sizeof (CVSNULLREPOS) + + 10); + (void) sprintf (repository, "%s/%s/%s", CVSroot_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 ->repository is NULL, do not create a CVSADM directory for that subdirectory; just CVS_CHDIR into it. */ diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c index c43e35f3538..a7fec5f9bb8 100644 --- a/gnu/usr.bin/cvs/src/commit.c +++ b/gnu/usr.bin/cvs/src/commit.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * specified in the README file that comes with the CVS source distribution. * * Commit Files * @@ -92,6 +92,7 @@ static const char *const commit_usage[] = "\t-F file\tRead the log message from file.\n", "\t-m msg\tLog message.\n", "\t-r rev\tCommit to this branch or trunk revision.\n", + "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -144,8 +145,22 @@ find_dirent_proc (callerdat, dir, repository, update_dir, 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; } @@ -232,7 +247,7 @@ find_fileproc (callerdat, finfo) xfinfo.repository = NULL; xfinfo.rcs = NULL; - vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); + vers = Version_TS (&xfinfo, NULL, tag, NULL, 0, 0); if (vers->ts_user == NULL && vers->vn_user != NULL && vers->vn_user[0] == '-') @@ -246,8 +261,8 @@ find_fileproc (callerdat, finfo) if (vers->ts_user == NULL) error (0, 0, "nothing known about `%s'", finfo->fullname); else - error (0, 0, "use `cvs add' to create an entry for %s", - finfo->fullname); + error (0, 0, "use `%s add' to create an entry for %s", + program_name, finfo->fullname); return 1; } else if (vers->ts_user != NULL @@ -321,6 +336,9 @@ commit (argc, argv) * 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) { struct passwd *pw; @@ -342,9 +360,9 @@ commit (argc, argv) break; case 'm': #ifdef FORCE_USE_EDITOR - use_editor = TRUE; + use_editor = 1; #else - use_editor = FALSE; + use_editor = 0; #endif if (message) { @@ -371,9 +389,9 @@ commit (argc, argv) break; case 'F': #ifdef FORCE_USE_EDITOR - use_editor = TRUE; + use_editor = 1; #else - use_editor = FALSE; + use_editor = 0; #endif logfile = optarg; break; @@ -425,6 +443,7 @@ commit (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) { + int err; struct find_data find_args; ign_setup (); @@ -489,6 +508,8 @@ commit (argc, argv) do_verify (message, (char *)NULL); /* We always send some sort of message, even if empty. */ + /* FIXME: is that true? There seems to be some code in do_editor + which can leave the message NULL. */ option_with_arg ("-m", message); /* OK, now process all the questionable files we have been saving @@ -553,14 +574,39 @@ commit (argc, argv) "cvs commit -r 2" across a whole bunch of files a very slow operation (and it isn't documented in cvsclient.texi). I haven't looked at the server code carefully enough to be - _sure_ why this is needed, but if it is because RCS_CI - wants the file to exist, then it would be relatively simple - (but not trivial) to fix in the server. */ + _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); send_to_server ("ci\012", 0); - return get_responses_and_close (); + err = get_responses_and_close (); + if (err != 0 && use_editor && 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; + + fname = cvs_temp_name (); + fp = CVS_FOPEN (fname, "w+"); + if (fp == NULL) + error (1, 0, "cannot create temporary file %s", fname); + if (fwrite (message, 1, strlen (message), fp) != strlen (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); + } + return err; } #endif @@ -633,7 +679,10 @@ classify_file_internal (finfo, 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; @@ -817,16 +866,30 @@ check_fileproc (callerdat, finfo) 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, - "file `%s' still contains conflict indicators", + "\ +warning: file `%s' seems to still contain conflict indicators", finfo->fullname); - freevers_ts (&vers); - return (1); } } if (status == T_REMOVED && vers->tag && isdigit (*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); @@ -946,7 +1009,8 @@ check_fileproc (callerdat, finfo) } /* - * Print warm fuzzies while examining the dirs + * By default, return the code that tells do_recursion to examine all + * directories */ /* ARGSUSED */ static Dtype @@ -957,6 +1021,9 @@ check_direntproc (callerdat, dir, repos, update_dir, entries) char *update_dir; List *entries; { + if (!isdir (dir)) + return (R_SKIP_ALL); + if (!quiet) error (0, 0, "Examining %s", update_dir); @@ -1013,7 +1080,8 @@ precommit_proc (repository, filter) free (s); } - run_setup ("%s %s", filter, repository); + run_setup (filter); + run_arg (repository); (void) walklist (ulist, precommit_list_proc, NULL); return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); } @@ -1333,7 +1401,8 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) if (line[line_length - 1] == '\n') line[--line_length] = '\0'; repository = Name_Repository ((char *) NULL, update_dir); - run_setup ("%s %s", line, repository); + run_setup (line); + run_arg (repository); cvs_output (program_name, 0); cvs_output (" ", 1); cvs_output (command_name, 0); @@ -1365,7 +1434,7 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) } /* - * Get the log message for a dir and print a warm fuzzy + * Get the log message for a dir */ /* ARGSUSED */ static Dtype @@ -1380,6 +1449,9 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries) 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) @@ -1391,10 +1463,6 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries) if (ulist == NULL || ulist->list->next == ulist->list) return (R_SKIP_FILES); - /* print the warm fuzzy */ - if (!quiet) - error (0, 0, "Committing %s", update_dir); - /* get commit message */ real_repos = Name_Repository (dir, update_dir); got_message = 1; @@ -1489,10 +1557,10 @@ remove_file (finfo, tag, message) error (1, 0, "internal error: no parsed RCS file"); branch = 0; - if (tag && !(branch = RCS_isbranch (finfo->rcs, tag))) + 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, 1)) != 0) + if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -1500,6 +1568,7 @@ remove_file (finfo, tag, message) finfo->fullname); return (1); } + RCS_rewrite (finfo->rcs, NULL, NULL); Scratch_Entry (finfo->entries, finfo->file); return (0); } @@ -1556,6 +1625,7 @@ remove_file (finfo, tag, message) finfo->fullname); return (1); } + RCS_rewrite (finfo->rcs, NULL, NULL); } #ifdef SERVER_SUPPORT @@ -1584,12 +1654,15 @@ remove_file (finfo, tag, message) /* Except when we are creating a branch, lock the revision so that we can check in the new revision. */ if (lockflag) - RCS_lock (finfo->rcs, rev ? corev : NULL, 0); + { + if (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->path, finfo->file, message, rev, + retcode = RCS_checkin (finfo->rcs, finfo->file, message, rev, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); if (retcode != 0) { @@ -1690,6 +1763,8 @@ unlockrcs (rcs) if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not unlock %s", rcs->path); + else + RCS_rewrite (rcs, NULL, NULL); } /* @@ -1730,6 +1805,7 @@ fixbranch (rcs, branch) 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); } } @@ -1841,29 +1917,63 @@ internal error: `%s' didn't move out of the attic", { /* this is the first time we have ever seen this file; create an rcs file. */ - run_setup ("%s%s -x,v/ -i", Rcsbin, RCS); + char *desc; + size_t descalloc; + size_t desclen; + + char *opt; + + desc = NULL; + descalloc = 0; + desclen = 0; fname = xmalloc (strlen (file) + sizeof (CVSADM) + sizeof (CVSEXT_LOG) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); /* If the file does not exist, no big deal. In particular, the server does not (yet at least) create CVSEXT_LOG files. */ if (isfile (fname)) - run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); + /* 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 && options[0] == '-' && options[1] == 'k') - run_arg (options); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + opt = options + 2; + else + opt = NULL; + + /* This message is an artifact of the time when this + was implemented via "rcs -i". It should be revised at + some point (does the "initial revision" in the message from + RCS_checkin indicate that this is a new file? Or does the + "RCS file" message serve some function?). */ + cvs_output ("RCS file: ", 0); + cvs_output (rcs, 0); + cvs_output ("\ndone\n", 0); + + if (add_rcs_file (NULL, rcs, file, NULL, opt, + NULL, NULL, 0, NULL, + desc, desclen, NULL) != 0) { - error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, - "could not create %s", rcs); retval = 1; goto out; } + rcsfile = RCS_parsercsfile (rcs); newfile = 1; + if (desc != NULL) + free (desc); } /* when adding a file for the first time, and using a tag, we need @@ -1883,7 +1993,7 @@ internal error: `%s' didn't move out of the attic", /* commit a dead revision. */ (void) sprintf (tmp, "file %s was initially added on branch %s.", file, tag); - retcode = RCS_checkin (rcs, NULL, tmp, NULL, + retcode = RCS_checkin (rcsfile, NULL, tmp, NULL, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); free (tmp); if (retcode != 0) @@ -1898,7 +2008,8 @@ internal error: `%s' didn't move out of the attic", rename_file (fname, file); free (fname); - assert (rcsfile == NULL); + /* double-check that the file was written correctly */ + freercsnode (&rcsfile); rcsfile = RCS_parse (file, repository); if (rcsfile == NULL) { @@ -1952,6 +2063,7 @@ internal error: `%s' didn't move out of the attic", magicrev = RCS_magicrev (rcsfile, head); retcode = RCS_settag (rcsfile, tag, magicrev); + RCS_rewrite (rcsfile, NULL, NULL); free (head); free (magicrev); @@ -1984,7 +2096,14 @@ internal error: `%s' didn't move out of the attic", fileattr_newfile (file); + /* I don't think fix_rcs_modes is needed any more. In the + add_rcs_file case, the algorithms used by add_rcs_file and + fix_rcs_modes are the same, so there is no need to go through + it all twice. In the other cases, I think we want to just + preserve the mode that the file had before we started. That is + a behavior change, but I would think a desirable one. */ fix_rcs_modes (rcs, file); + retval = 0; out: @@ -2031,12 +2150,13 @@ lock_RCS (user, rcs, rev, repository) return (1); } } - err = RCS_lock(rcs, NULL, 0); + err = RCS_lock(rcs, NULL, 1); } else { (void) RCS_lock(rcs, rev, 1); } + RCS_rewrite (rcs, NULL, NULL); if (err == 0) { diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index a141ccf7580..342fed2d4ae 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -127,6 +127,8 @@ extern int errno; #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" @@ -144,6 +146,8 @@ extern int errno; /* 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 */ @@ -182,7 +186,7 @@ extern int errno; #define CVSROOTADM_READERS "readers" #define CVSROOTADM_WRITERS "writers" #define CVSROOTADM_PASSWD "passwd" -#define CVSROOTADM_OPTIONS "options" +#define CVSROOTADM_CONFIG "config" #define CVSNULLREPOS "Emptydir" /* an empty directory */ @@ -207,11 +211,10 @@ extern int errno; /* Command attributes -- see function lookup_command_attribute(). */ #define CVS_CMD_IGNORE_ADMROOT 1 -/* Set if CVS does _not_ need to create a CVS/Root file upon - completion of this command. The name is confusing, both because - the meaning is closer to "does not use working directory" than - "uses working directory" and because the flag isn't really as - general purpose as it seems (cvs release sets it). */ +/* 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 @@ -241,9 +244,6 @@ extern int errno; #endif #endif /* USE_VMS_FILENAMES */ -#define FALSE 0 -#define TRUE 1 - /* * Special tags. -rHEAD refers to the head of an RCS file, regardless of any * sticky tags. -rBASE refers to the current revision the user has checked @@ -254,13 +254,10 @@ extern int errno; /* Environment variable used by CVS */ #define CVSREAD_ENV "CVSREAD" /* make files read-only */ -#define CVSREAD_DFLT FALSE /* writable files by default */ +#define CVSREAD_DFLT 0 /* writable files by default */ #define CVSREADONLYFS_ENV "CVSREADONLYFS" /* repository is read-only */ -#define RCSBIN_ENV "RCSBIN" /* RCS binary directory */ -/* #define RCSBIN_DFLT Set by options.h */ - #define TMPDIR_ENV "TMPDIR" /* Temporary directory */ /* #define TMPDIR_DFLT Set by options.h */ @@ -303,8 +300,13 @@ 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; @@ -356,7 +358,7 @@ typedef enum direnter_type Dtype; #endif extern char *program_name, *program_path, *command_name; -extern char *Rcsbin, *Tmpdir, *Editor; +extern char *Tmpdir, *Editor; extern int cvsadmin_root; extern char *CurDir; extern int really_quiet, quiet; @@ -367,7 +369,8 @@ extern char *RCS_citag; /* Access method specified in CVSroot. */ typedef enum { - local_method, server_method, pserver_method, kserver_method, ext_method + local_method, server_method, pserver_method, kserver_method, gserver_method, + ext_method } CVSmethod; extern char *method_names[]; /* change this in root.c if you change the enum above */ @@ -379,6 +382,8 @@ extern char *CVSroot_username; /* the username or NULL if method == local */ extern char *CVSroot_hostname; /* the hostname or NULL if method == local */ extern char *CVSroot_directory; /* the directory name */ +extern char *emptydir_name PROTO ((void)); + 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 */ @@ -394,20 +399,24 @@ extern char hostname[]; /* Externs that are included directly in the CVS sources */ -int RCS_exec_settag PROTO((const char *, const char *, const char *)); -int RCS_exec_deltag PROTO((const char *, const char *, int)); -int RCS_exec_setbranch PROTO((const char *, const char *)); -int RCS_exec_lock PROTO((const char *, const char *, int)); -int RCS_exec_unlock PROTO((const char *, const char *, int)); -int RCS_merge PROTO((const char *, const char *, const char *, const char *)); +int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, 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 -int RCS_checkin PROTO ((char *rcsfile, char *workfile, char *message, - char *rev, int flags)); + +extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile, + char *opts, char *options, + char *rev1, char *rev2, + char *label1, char *label2, + char *workfile)); +extern int diff_exec PROTO ((char *file1, char *file2, char *options, + char *out)); +extern int diff_execv PROTO ((char *file1, char *file2, + char *label1, char *label2, + char *options, char *out)); @@ -421,9 +430,11 @@ List *Entries_Open PROTO((int aflag)); void Subdirs_Known PROTO((List *entries)); void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); + char *Make_Date PROTO((char *rawdate)); char *Name_Repository PROTO((char *dir, char *update_dir)); - +char *Short_Repository PROTO((char *repository)); +void Sanitize_Repository_Name PROTO((char *repository)); char *Name_Root PROTO((char *dir, char *update_dir)); int parse_cvsroot PROTO((char *CVSroot)); @@ -433,9 +444,8 @@ void root_allow_add PROTO ((char *)); void root_allow_free PROTO ((void)); int root_allow_ok PROTO ((char *)); -int same_directories PROTO((char *dir1, char *dir2)); -char *Short_Repository PROTO((char *repository)); -char *gca PROTO((char *rev1, char *rev2)); +char *gca PROTO((const char *rev1, const char *rev2)); +extern void check_numeric PROTO ((const char *, int, char **)); char *getcaller PROTO((void)); char *time_stamp PROTO((char *file)); @@ -448,6 +458,8 @@ int pathname_levels PROTO ((char *path)); typedef int (*CALLPROC) PROTO((char *repository, char *value)); int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); +extern int parse_config PROTO ((char *)); + typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(()); int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); int isdir PROTO((const char *file)); @@ -463,6 +475,8 @@ char *cvs_temp_name PROTO ((void)); void parseopts PROTO ((const char *root)); int numdots PROTO((const char *s)); +char *increment_revnum PROTO ((const char *)); +int compare_revnums PROTO ((const char *, const char *)); int unlink_file PROTO((const char *f)); int link_file PROTO ((const char *from, const char *to)); int unlink_file_dir PROTO((const char *f)); @@ -471,9 +485,10 @@ int xcmp PROTO((const char *file1, const char *file2)); int yesno PROTO((void)); void *valloc PROTO((size_t bytes)); time_t get_date PROTO((char *date, struct timeb *now)); -void Create_Admin PROTO((char *dir, char *update_dir, - char *repository, char *tag, char *date, - int nonbranch)); +extern int Create_Admin PROTO ((char *dir, char *update_dir, + char *repository, char *tag, char *date, + int nonbranch, int warn)); +extern int expand_at_signs PROTO ((char *, off_t, FILE *)); /* Locking subsystem (implemented in lock.c). */ @@ -511,7 +526,7 @@ extern int ign_case; #include "update.h" -void line2argv PROTO ((int *pargc, char ***argv, char *line)); +void line2argv PROTO ((int *pargc, char ***argv, char *line, char *sepchars)); void make_directories PROTO((const char *name)); void make_directory PROTO((const char *name)); extern int mkdir_if_needed PROTO ((char *name)); @@ -548,7 +563,7 @@ void do_editor PROTO((char *dir, char **messagep, void do_verify PROTO((char *message, char *repository)); typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where, - char *mwhere, char *mfile, int horten, int local_specified, + char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, char *msg)); /* This is the structure that the recursion processor passes to the @@ -609,7 +624,9 @@ void SIG_endCrSect PROTO((void)); void read_cvsrc PROTO((int *argc, char ***argv, char *cmdname)); char *make_message_rcslegal PROTO((char *message)); -extern int file_has_markers PROTO ((struct file_info *)); +extern int file_has_markers PROTO ((const struct file_info *)); +extern void get_file PROTO ((const char *, const char *, const char *, + char **, size_t *, size_t *)); /* flags for run_exec(), the fast system() for CVS */ #define RUN_NORMAL 0x0000 /* no special behaviour */ @@ -622,14 +639,9 @@ extern int file_has_markers PROTO ((struct file_info *)); void run_arg PROTO((const char *s)); void run_print PROTO((FILE * fp)); -#ifdef HAVE_VPRINTF -void run_setup PROTO((const char *fmt,...)); -void run_args PROTO((const char *fmt,...)); -#else -void run_setup (); -void run_args (); -#endif -int run_exec PROTO((char *stin, char *stout, char *sterr, int flags)); +void run_setup PROTO ((const char *prog)); +int run_exec PROTO((const char *stin, const char *stout, const char *sterr, + int flags)); /* other similar-minded stuff from run.c. */ FILE *run_popen PROTO((const char *, const char *)); @@ -649,10 +661,16 @@ pid_t waitpid PROTO((pid_t, int *, int)); struct vers_ts { /* rcs version user file derives from, from CVS/Entries. - * it can have the following special values: - * empty = no user file - * 0 = user file is new - * -vers = user file to be removed. */ + It can have the following special values: + + NULL = file is not mentioned in Entries (this is also used for a + directory). + "" = ILLEGAL! 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 legal 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 @@ -675,7 +693,8 @@ struct vers_ts and if they differ it is modified. */ char *ts_rcs; - /* Options from CVS/Entries (keyword expansion). */ + /* 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) @@ -714,6 +733,11 @@ int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev, char *tag, char *options, char *message)); int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers)); +/* CVSADM_BASEREV stuff, from entries.c. */ +extern char *base_get PROTO ((struct file_info *)); +extern void base_register PROTO ((struct file_info *, char *)); +extern void base_deregister PROTO ((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 @@ -775,6 +799,9 @@ void wrap_fromcvs_process_file PROTO ((const char *fileName)); void wrap_add_file PROTO((const char *file,int temp)); void wrap_add PROTO((char *line,int temp)); void wrap_send PROTO ((void)); +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +void wrap_unparse_rcs_options PROTO ((char **, int)); +#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ /* Pathname expansion */ char *expand_path PROTO((char *name, char *file, int line)); @@ -824,10 +851,13 @@ extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *)); extern void tag_check_valid_join PROTO ((char *, int, char **, int, int, char *)); +/* From server.c and documented there. */ extern void cvs_output PROTO ((const char *, size_t)); +extern void cvs_output_binary PROTO ((char *, size_t)); extern void cvs_outerr PROTO ((const char *, size_t)); extern void cvs_flusherr PROTO ((void)); extern void cvs_flushout PROTO ((void)); +extern void cvs_output_tagged PROTO ((char *, char *)); #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) #include "server.h" diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c index 82fd84a6730..2f842cd84a3 100644 --- a/gnu/usr.bin/cvs/src/ignore.c +++ b/gnu/usr.bin/cvs/src/ignore.c @@ -304,49 +304,54 @@ ign_name (name) } } -/* FIXME: This list of dirs to ignore stuff seems not to be used. */ +/* 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 (name) - char *name; +/* Add a directory to list of dirs to ignore. */ +void +ign_dir_add (name) + char *name; { - /* make sure we've got the space for the entry */ - if (dir_ign_current <= dir_ign_max) + /* 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 ((char *) dir_ign_list, (dir_ign_max+1) * sizeof(char*)); + 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] = name; + dir_ign_list[dir_ign_current] = name; - dir_ign_current += 1 ; + dir_ign_current += 1 ; } -/* this function returns 1 (true) if the given directory name is part of - * the list of directories to ignore - */ +/* Return nonzero if NAME is part of the list of directories to ignore. */ -int ignore_directory (name) - char *name; +int +ignore_directory (name) + char *name; { - int i; + int i; - if (!dir_ign_list) - return 0; + if (!dir_ign_list) + return 0; - i = dir_ign_current; - while (i--) + i = dir_ign_current; + while (i--) { - if (strncmp(name, dir_ign_list[i], strlen(dir_ign_list[i])) == 0) - return 1; + if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0) + return 1; } - return 0; + return 0; } /* diff --git a/gnu/usr.bin/cvs/src/lock.c b/gnu/usr.bin/cvs/src/lock.c index c47943c1e88..72ae48a5a7a 100644 --- a/gnu/usr.bin/cvs/src/lock.c +++ b/gnu/usr.bin/cvs/src/lock.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * specified in the README file that comes with the CVS source distribution. * * Set Lock * @@ -39,9 +39,9 @@ be. * Readlocks ensure that we won't find the file in the state in - which it is in between the "rcs -i" and the RCS_checkin in commit.c - (when a file is being added). This state is a state in which the - RCS file parsing routines in rcs.c cannot parse the file. + 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). diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c index 046bf9c3f8b..ba4d5c89b8d 100644 --- a/gnu/usr.bin/cvs/src/main.c +++ b/gnu/usr.bin/cvs/src/main.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License - * as specified in the README file that comes with the CVS 1.4 kit. + * as specified in the README file that comes with the CVS source distribution. * * This is the main C driver for the CVS system. * @@ -33,15 +33,15 @@ char *command_name; char hostname[MAXHOSTNAMELEN]; -int use_editor = TRUE; -int use_cvsrc = TRUE; +int use_editor = 1; +int use_cvsrc = 1; int cvswrite = !CVSREAD_DFLT; -int really_quiet = FALSE; -int quiet = FALSE; -int trace = FALSE; -int noexec = FALSE; -int readonlyfs = FALSE; -int logoff = FALSE; +int really_quiet = 0; +int quiet = 0; +int trace = 0; +int noexec = 0; +int readonlyfs = 0; +int logoff = 0; mode_t cvsumask = UMASK_DFLT; char *RCS_citag = NULL; @@ -50,7 +50,6 @@ char *CurDir; /* * Defaults, for the environment variables that are not set */ -char *Rcsbin = RCSBIN_DFLT; char *Tmpdir = TMPDIR_DFLT; char *Editor = EDITOR_DFLT; @@ -126,32 +125,47 @@ static const struct cmd static const char *const usg[] = { - "Usage: %s [cvs-options] command [command-options] [files...]\n", - " Where 'cvs-options' are:\n", - " -H Displays Usage information for command\n", - " -Q Cause CVS to be really quiet.\n", - " -q Cause CVS to be somewhat quiet.\n", - " -r Make checked-out files read-only\n", - " -w Make checked-out files read-write (default)\n", - " -l Turn History logging off\n", - " -n Do not execute anything that will change the disk\n", - " -t Show trace of program execution -- Try with -n\n", - " -v CVS version and copyright\n", - " -b bindir Find RCS programs in 'bindir'\n", - " -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 -#endif - " -s VAR=VAL Set CVS user variable.\n", + /* 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. Puncutation 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", - " and where 'command' is: add, admin, etc. (use the --help-commands\n", - " option for a list of commands)\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", + " Cyclic Software at http://www.cyclic.com/ or\n", + " Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n", NULL, }; @@ -185,17 +199,47 @@ static const char *const cmd_usage[] = " update Bring work tree in sync with repository\n", " watch Set watches\n", " watchers See who is watching a file\n", - "(Use the --help-synonyms option for a list of alternate command names)\n", + "(Specify the --help option for a list of other help options)\n", NULL, }; +static const char *const opt_usage[] = +{ + "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", + " -l Turn history logging off.\n", + " -n Do not execute anything that will change the disk.\n", + " -t Show trace of program execution -- try with -n.\n", + " -v CVS version and copyright.\n", + " -b bindir Find RCS programs in 'bindir'.\n", + " -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 const char * const* cmd_synonyms () { char ** synonyms; char ** line; const struct cmd *c = &cmds[0]; - int numcmds = 2; /* two more for title and end */ + /* Three more for title, "specify --help" line, and NULL. */ + int numcmds = 3; while (c->fullname != NULL) { @@ -220,6 +264,7 @@ cmd_synonyms () 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 */ @@ -330,11 +375,10 @@ main (argc, argv) char *cp, *end; const struct cmd *cm; int c, err = 0; - int rcsbin_update_env, tmpdir_update_env, cvs_update_env; + int tmpdir_update_env, cvs_update_env; int free_CVSroot = 0; int free_Editor = 0; int free_Tmpdir = 0; - int free_Rcsbin = 0; int help = 0; /* Has the user asked for help? This lets us support the `cvs -H cmd' @@ -345,6 +389,7 @@ main (argc, argv) {"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} }; @@ -382,12 +427,6 @@ main (argc, argv) * they can be overridden by command line arguments */ cvs_update_env = 0; - rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */ - if ((cp = getenv (RCSBIN_ENV)) != NULL) - { - Rcsbin = cp; - rcsbin_update_env = 0; /* it's already there */ - } tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */ if ((cp = getenv (TMPDIR_ENV)) != NULL) { @@ -406,19 +445,11 @@ main (argc, argv) cvs_update_env = 0; /* it's already there */ } if (getenv (CVSREAD_ENV) != NULL) - cvswrite = FALSE; + cvswrite = 0; if (getenv (CVSREADONLYFS_ENV)) { readonlyfs = TRUE; logoff = TRUE; } - 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); - } /* Set this to 0 to force getopt initialization. getopt() sets this to 1 internally. */ @@ -435,7 +466,7 @@ main (argc, argv) != EOF) { if (c == 'f') - use_cvsrc = FALSE; + use_cvsrc = 0; } /* @@ -448,7 +479,7 @@ main (argc, argv) opterr = 1; while ((c = getopt_long - (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index)) + (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:xa", long_options, &option_index)) != EOF) { switch (c) @@ -461,47 +492,59 @@ main (argc, argv) /* --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 = TRUE; + really_quiet = 1; /* FALL THROUGH */ case 'q': - quiet = TRUE; + quiet = 1; break; case 'r': - cvswrite = FALSE; + cvswrite = 0; break; case 'w': - cvswrite = TRUE; + cvswrite = 1; break; case 't': - trace = TRUE; + trace = 1; break; case 'n': - noexec = TRUE; + noexec = 1; case 'l': /* Fall through */ - logoff = TRUE; + logoff = 1; break; case 'v': + /* 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 (version_string, stdout); (void) fputs (config_string, stdout); (void) fputs ("\n", stdout); - (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout); - (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout); - (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout); - (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout); + (void) fputs ("\ +Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\ + Jeff Polk, and other authors\n", stdout); (void) fputs ("\n", stdout); (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout); + (void) fputs ("\n", stdout); + + (void) fputs ("Specify the --help option for further information about CVS\n", stdout); + exit (0); break; case 'b': - Rcsbin = xstrdup (optarg); - free_Rcsbin = 1; - rcsbin_update_env = 1; /* need to update environment */ + /* 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': Tmpdir = xstrdup (optarg); @@ -521,7 +564,7 @@ main (argc, argv) help = 1; break; case 'f': - use_cvsrc = FALSE; /* unnecessary, since we've done it above */ + use_cvsrc = 0; /* unnecessary, since we've done it above */ break; case 'z': #ifdef CLIENT_SUPPORT @@ -546,6 +589,15 @@ main (argc, argv) 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); @@ -576,6 +628,9 @@ main (argc, argv) else command_name = cm->fullname; /* Global pointer for later use */ + /* This should probably remain a warning, rather than an error, + for quite a while. For one thing the version of VC distributed + with GNU emacs 19.34 invokes 'cvs rlog' instead of 'cvs log'. */ if (strcmp (argv[0], "rlog") == 0) { error (0, 0, "warning: the rlog command is deprecated"); @@ -619,7 +674,7 @@ main (argc, argv) #endif /* HAVE_KERBEROS */ -#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT) +#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) if (strcmp (command_name, "pserver") == 0) { /* The reason that --allow-root is not a command option @@ -636,15 +691,16 @@ main (argc, argv) /* Pretend we were invoked as a plain server. */ command_name = "server"; } -#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */ +#endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */ +#ifdef SERVER_SUPPORT + server_active = strcmp (command_name, "server") == 0; /* 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. */ -#ifdef SERVER_SUPPORT - if (strcmp (command_name, "server") != 0) + if (!server_active) #endif { char *CVSADM_Root; @@ -792,7 +848,7 @@ main (argc, argv) it is worth the trouble. */ #ifdef SERVER_SUPPORT - if (strcmp (command_name, "server") == 0) + if (server_active) CurDir = xstrdup ("<remote>"); else #endif @@ -806,16 +862,6 @@ main (argc, argv) Tmpdir = "/tmp"; #ifdef HAVE_PUTENV - /* Now, see if we should update the environment with the - Rcsbin value */ - if (rcsbin_update_env) - { - char *env; - env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1); - (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin); - (void) putenv (env); - /* do not free env, as putenv has control of it */ - } if (tmpdir_update_env) { char *env; @@ -826,24 +872,6 @@ main (argc, argv) } #endif - /* - * If Rcsbin is set to something, make sure it is terminated with - * a slash character. If not, add one. - */ - if (*Rcsbin) - { - int len = strlen (Rcsbin); - char *rcsbin; - - if (Rcsbin[len - 1] != '/') - { - rcsbin = Rcsbin; - Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */ - (void) strcpy (Rcsbin, rcsbin); - (void) strcat (Rcsbin, "/"); - } - } - #ifndef DONT_USE_SIGNALS /* make sure we clean up on error */ #ifdef SIGHUP @@ -881,6 +909,28 @@ main (argc, argv) if (use_cvsrc) read_cvsrc (&argc, &argv, command_name); + /* 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 + && !client_active +#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 (CVSroot_directory); + } } /* end of stuff that gets done if the user DOESN'T ask for help */ err = (*(cm->func)) (argc, argv); @@ -889,7 +939,10 @@ main (argc, argv) { /* Update the CVS/Root file. We might want to do this in all directories that we recurse into, but currently we - don't. */ + don't. Note that if there is an error writing the file, + we give an error/warning. This is so if users try to rewrite + CVS/Root with the -d option (a documented feature), they will + either succeed, or be told why it didn't work. */ Create_Root (NULL, CVSroot); } @@ -902,8 +955,6 @@ main (argc, argv) free (Editor); if (free_Tmpdir) free (Tmpdir); - if (free_Rcsbin) - free (Rcsbin); root_allow_free (); #ifdef SYSTEM_CLEANUP @@ -915,6 +966,8 @@ main (argc, argv) /* 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 * @@ -929,11 +982,14 @@ Make_Date (rawdate) unixtime = get_date (rawdate, (struct timeb *) NULL); if (unixtime == (time_t) - 1) error (1, 0, "Can't parse date/time: %s", rawdate); -#ifdef HAVE_RCS5 + ftm = gmtime (&unixtime); -#else - ftm = localtime (&unixtime); -#endif + 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. */ + 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, diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c index 194999be651..9d091c34b6f 100644 --- a/gnu/usr.bin/cvs/src/patch.c +++ b/gnu/usr.bin/cvs/src/patch.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * specified in the README file that comes with the CVS source distribution. * * Patch * @@ -54,6 +54,7 @@ static const char *const patch_usage[] = "\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 }; @@ -385,6 +386,7 @@ patch_fileproc (callerdat, finfo) size_t line2_chars_allocated; char *cp1, *cp2; FILE *fp; + int line_length; line1 = NULL; line1_chars_allocated = 0; @@ -461,23 +463,39 @@ patch_fileproc (callerdat, finfo) if (patch_short) { - (void) printf ("File %s ", finfo->fullname); + cvs_output ("File ", 0); + cvs_output (finfo->fullname, 0); if (vers_tag == NULL) - (void) printf ("is new; current revision %s\n", vers_head); + { + cvs_output (" is new; current revision ", 0); + cvs_output (vers_head, 0); + cvs_output ("\n", 1); + } else if (vers_head == NULL) { - (void) printf ("is removed; not included in "); + cvs_output (" is removed; not included in ", 0); if (rev2 != NULL) - (void) printf ("release tag %s", rev2); + { + cvs_output ("release tag ", 0); + cvs_output (rev2, 0); + } else if (date2 != NULL) - (void) printf ("release date %s", date2); + { + cvs_output ("release date ", 0); + cvs_output (date2, 0); + } else - (void) printf ("current release"); - (void) printf ("\n"); + cvs_output ("current release", 0); + cvs_output ("\n", 1); } else - (void) printf ("changed from revision %s to %s\n", - vers_tag, vers_head); + { + 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 out2; } @@ -542,11 +560,8 @@ patch_fileproc (callerdat, finfo) and therefore should be on the server, not the client. */ (void) utime (tmpfile2, &t); } - run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c'); - run_arg (tmpfile1); - run_arg (tmpfile2); - switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_REALLY)) + switch (diff_exec (tmpfile1, tmpfile2, unidiff ? "-u" : "-c", tmpfile3)) { case -1: /* fork/wait failure */ error (1, errno, "fork for diff failed on %s", rcs); @@ -561,9 +576,9 @@ patch_fileproc (callerdat, finfo) */ /* Output an "Index:" line for patch to use */ - (void) fflush (stdout); - (void) printf ("Index: %s\n", finfo->fullname); - (void) fflush (stdout); + cvs_output ("Index: ", 0); + cvs_output (finfo->fullname, 0); + cvs_output ("\n", 1); fp = open_file (tmpfile3, "r"); if (getline (&line1, &line1_chars_allocated, fp) < 0 || @@ -628,25 +643,49 @@ patch_fileproc (callerdat, finfo) (void) sprintf (file2, "%s:%s", finfo->fullname, vers_head ? vers_head : "removed"); - /* Note that this prints "diff" not DIFF. The format of a diff - does not depend on the name of the program which happens to - have produced it. */ + /* 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) { - (void) printf ("diff -u %s %s\n", file1, file2); - (void) printf ("--- %s%s+++ ", file1, cp1); + 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 { - (void) printf ("diff -c %s %s\n", file1, file2); - (void) printf ("*** %s%s--- ", file1, cp1); + 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); } - (void) printf ("%s%s", finfo->fullname, cp2); + cvs_output (finfo->fullname, 0); + cvs_output (cp2, 0); + /* spew the rest of the diff out */ - while (getline (&line1, &line1_chars_allocated, fp) >= 0) - (void) fputs (line1, stdout); - (void) fclose (fp); + 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; @@ -658,10 +697,12 @@ patch_fileproc (callerdat, finfo) free (line1); if (line2) free (line2); - /* FIXME: should be checking for errors. */ - (void) CVS_UNLINK (tmpfile1); - (void) CVS_UNLINK (tmpfile2); - (void) CVS_UNLINK (tmpfile3); + 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); diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c index bc8ed0818ad..23d7e97b8e1 100644 --- a/gnu/usr.bin/cvs/src/rcs.c +++ b/gnu/usr.bin/cvs/src/rcs.c @@ -2,7 +2,7 @@ * Copyright (c) 1992, Brian Berliner and Jeff Polk * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * 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 @@ -10,6 +10,7 @@ #include <assert.h> #include "cvs.h" +#include "edit.h" /* 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 @@ -19,7 +20,6 @@ static const char *const kflags[] = enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B }; static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile)); -static void RCS_reparsercsfile PROTO((RCSNode *, int, FILE **)); static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch)); static int getrcskey PROTO((FILE * fp, char **keyp, char **valp, size_t *lenp)); @@ -27,7 +27,9 @@ static void getrcsrev PROTO ((FILE *fp, char **revp)); static int checkmagic_proc PROTO((Node *p, void *closure)); static void do_branches PROTO((List * list, char *val)); static void do_symbols PROTO((List * list, char *val)); +static void do_locks PROTO((List * list, char *val)); static void free_rcsnode_contents PROTO((RCSNode *)); +static void free_rcsvers_contents PROTO((RCSVers *)); static void rcsvers_delproc PROTO((Node * p)); static char *translate_symtag PROTO((RCSNode *, const char *)); static char *printable_date PROTO((const char *)); @@ -41,6 +43,24 @@ enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; static void RCS_deltas PROTO ((RCSNode *, FILE *, char *, enum rcs_delta_op, char **, size_t *, char **, size_t *)); +/* Routines for reading, parsing and writing RCS files. */ +static RCSVers *getdelta PROTO ((FILE *, char *)); +static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *)); +static void freedeltatext PROTO ((Deltatext *)); + +static void RCS_putadmin PROTO ((RCSNode *, FILE *)); +static void RCS_putdtree PROTO ((RCSNode *, char *, FILE *)); +static void RCS_putdesc PROTO ((RCSNode *, FILE *)); +static void putdelta PROTO ((RCSVers *, FILE *)); +static int putrcsfield_proc PROTO ((Node *, void *)); +static int putsymbol_proc PROTO ((Node *, void *)); +static void RCS_copydeltas PROTO ((RCSNode *, FILE *, FILE *, Deltatext *, char *)); +static void putdeltatext PROTO ((FILE *, Deltatext *)); + +static FILE *rcs_internal_lockfile PROTO ((char *)); +static void rcs_internal_unlockfile PROTO ((FILE *, char *)); +static char *rcs_lockfilename PROTO ((char *)); + /* * We don't want to use isspace() from the C library because: * @@ -232,10 +252,20 @@ RCS_parsercsfile_i (fp, rcsfile) rdata->path = xstrdup (rcsfile); /* Process HEAD and BRANCH keywords from the RCS header. - * - * Most cvs operatations on the main branch don't need any more - * information. Those that do call XXX to completely parse the - * RCS file. */ + + 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. + + People often wonder whether this is inefficient, to open the + file once here and once in RCS_reparsercsfile. Well, it might + help a little bit if we kept the file open (I haven't tried + timing this myself), but basically the common case, which we + want to optimize, is the one in which we call + RCS_parsercsfile_i and not RCS_reparsercsfile (for example, + "cvs update" on a lot of files most of which are unmodified). + So making the case in which we call RCS_reparsercsfile fast is + not as important. */ if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) goto l_error; @@ -288,23 +318,19 @@ l_error: On error, die with a fatal error; if it returns at all it was successful. - If ALL is nonzero, remember all keywords and values. Otherwise - only keep the ones we will need. - If PFP is NULL, close the file when done. Otherwise, leave it open and store the FILE * in *PFP. */ -static void -RCS_reparsercsfile (rdata, all, pfp) +void +RCS_reparsercsfile (rdata, pfp) RCSNode *rdata; - int all; FILE **pfp; { FILE *fp; char *rcsfile; - Node *q; + Node *q, *kv; RCSVers *vnode; - int n; + long fpos; char *cp; char *key, *value; @@ -316,6 +342,9 @@ RCS_reparsercsfile (rdata, all, pfp) error (1, 0, "unable to reopen `%s'", rcsfile); /* 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 (); /* @@ -324,12 +353,13 @@ RCS_reparsercsfile (rdata, all, pfp) */ for (;;) { + fpos = ftell (fp); + /* get the next key/value pair */ /* if key is NULL here, then the file is missing some headers or we had trouble reading the file. */ - if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL - || strcmp (key, RCSDESC) == 0) + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) { if (ferror(fp)) { @@ -342,6 +372,35 @@ RCS_reparsercsfile (rdata, all, pfp) } } + /* Skip head and branch tags; we already have them. */ + if (strcmp (key, RCSHEAD) == 0 || strcmp (key, RCSBRANCH) == 0) + continue; + + if (strcmp (key, "access") == 0) + { + if (value != NULL) + rdata->access = xstrdup (value); + continue; + } + + /* We always save lock information, so that we can handle + -kkvl correctly when checking out a file. */ + if (strcmp (key, "locks") == 0) + { + if (value != NULL) + rdata->locks_data = xstrdup (value); + fpos = ftell (fp); + if (getrcskey (fp, &key, &value, NULL) >= 0 && + strcmp (key, "strict") == 0 && + value == NULL) + { + rdata->strict_locks = 1; + } + else + (void) fseek (fp, fpos, SEEK_SET); + continue; + } + if (strcmp (RCSSYMBOLS, key) == 0) { if (value != NULL) @@ -357,7 +416,7 @@ RCS_reparsercsfile (rdata, all, pfp) /* * check key for '.''s and digits (probably a rev) if it is a - * revision, we are done with the headers and are down to the + * 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 (*cp) || *cp == '.') && *cp != '\0'; cp++) @@ -365,26 +424,25 @@ RCS_reparsercsfile (rdata, all, pfp) if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; - /* We always save lock information, so that we can handle - -kkvl correctly when checking out a file. We don't use a - special field for this information, since it will normally - not be set for a CVS file. */ - if (all || strcmp (key, "locks") == 0) - { - Node *kv; - - if (rdata->other == NULL) - rdata->other = getlist (); - kv = getnode (); - kv->type = RCSFIELD; - kv->key = xstrdup (key); - kv->data = xstrdup (value); - if (addnode (rdata->other, kv) != 0) - { - error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", - key, rcsfile); - freenode (kv); - } + if (strcmp (key, RCSDESC) == 0) + break; + + if (strcmp (key, "comment") == 0) + { + rdata->comment = xstrdup (value); + continue; + } + if (rdata->other == NULL) + rdata->other = getlist (); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + 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 */ @@ -395,116 +453,11 @@ RCS_reparsercsfile (rdata, all, pfp) * revision delta in our hand key=the revision and value=the date key and * its value */ - for (;;) - { - char *valp; - - vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); - memset (vnode, 0, sizeof (RCSVers)); - - /* fill in the version before we forget it */ - vnode->version = xstrdup (key); - - /* grab the value of the date from value */ - valp = value + strlen (RCSDATE);/* skip the "date" keyword */ - while (whitespace (*valp)) /* take space off front of value */ - valp++; - - vnode->date = xstrdup (valp); - - /* Get author field. */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be using errno in case of ferror. */ - if (key == NULL || strcmp (key, "author") != 0) - error (1, 0, "\ -unable to parse rcs file; `author' not in the expected place"); - vnode->author = xstrdup (value); - - /* Get state field. */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be using errno in case of ferror. */ - if (key == NULL || strcmp (key, "state") != 0) - error (1, 0, "\ -unable to parse rcs file; `state' not in the expected place"); - vnode->state = xstrdup (value); - if (strcmp (value, "dead") == 0) - { - vnode->dead = 1; - } - - /* fill in the branch list (if any branches exist) */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be handling various error conditions better. */ - if (key != NULL && strcmp (key, RCSDESC) == 0) - value = NULL; - if (value != (char *) NULL) - { - vnode->branches = getlist (); - do_branches (vnode->branches, value); - } - - /* fill in the next field if there is a next revision */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be handling various error conditions better. */ - if (key != NULL && strcmp (key, RCSDESC) == 0) - value = NULL; - if (value != (char *) NULL) - vnode->next = xstrdup (value); - - /* - * at this point, we skip any user defined fields XXX - this is where - * we put the symbolic link stuff??? - */ - /* FIXME: Does not correctly handle errors, e.g. from stdio. */ - while ((n = getrcskey (fp, &key, &value, NULL)) >= 0) - { - assert (key != NULL); - - if (strcmp (key, RCSDESC) == 0) - { - n = -1; - break; - } - - /* 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 (strcmp(key, RCSDEAD) == 0) - { - vnode->dead = 1; - if (vnode->state != NULL) - free (vnode->state); - vnode->state = xstrdup ("dead"); - continue; - } - /* if we have a revision, break and do it */ - for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) - /* do nothing */ ; - if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) - break; - - if (all) - { - Node *kv; - - if (vnode->other == NULL) - vnode->other = getlist (); - kv = getnode (); - kv->type = RCSFIELD; - kv->key = xstrdup (key); - kv->data = xstrdup (value); - if (addnode (vnode->other, kv) != 0) - { - error (0, 0, - "\ -warning: duplicate key `%s' in version `%s' of RCS file `%s'", - key, vnode->version, rcsfile); - freenode (kv); - } - } - } + /* First, seek back to the start of the delta block. */ + (void) fseek (fp, fpos, SEEK_SET); + while ((vnode = getdelta (fp, rcsfile)) != NULL) + { /* get the node */ q = getnode (); q->type = RCSVERS; @@ -521,36 +474,22 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", freenode (q); #endif } - - /* - * if we left the loop because there were no more keys, we break out - * of the revision processing loop - */ - if (n < 0) - break; } - if (all && key != NULL && strcmp (key, RCSDESC) == 0) + (void) getrcskey (fp, &key, &value, NULL); + if (key != NULL && strcmp (key, RCSDESC) == 0) { - Node *kv; - - if (rdata->other == NULL) - rdata->other = getlist (); - kv = getnode (); - kv->type = RCSFIELD; - kv->key = xstrdup (key); - kv->data = xstrdup (value); - if (addnode (rdata->other, kv) != 0) + if (rdata->desc != NULL) { error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", key, rcsfile); - freenode (kv); + free (rdata->desc); } + rdata->desc = xstrdup (value); } rdata->delta_pos = ftell (fp); - rdata->flags &= ~NODELTA; if (pfp == NULL) { @@ -582,7 +521,7 @@ RCS_fully_parse (rcs) { FILE *fp; - RCS_reparsercsfile (rcs, 1, &fp); + RCS_reparsercsfile (rcs, &fp); while (1) { @@ -780,19 +719,25 @@ free_rcsnode_contents (rnode) 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); } -/* - * rcsvers_delproc - free up an RCSVers type node - */ +/* free_rcsvers_contents -- free up the contents of an RCSVers node, + but also free the pointer to the node itself. */ + static void -rcsvers_delproc (p) - Node *p; -{ +free_rcsvers_contents (rnode) RCSVers *rnode; - - rnode = (RCSVers *) p->data; - +{ if (rnode->branches != (List *) NULL) dellist (&rnode->branches); if (rnode->date != (char *) NULL) @@ -805,14 +750,29 @@ rcsvers_delproc (p) 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 (p) + Node *p; +{ + free_rcsvers_contents ((RCSVers *) p->data); +} + +/* * getrcskey - fill in the key and value from the rcs file the algorithm is * as follows * - * o skip whitespace o fill in key with everything up to next white + * o skip whitespace + * o fill in key with everything up to next white * space or semicolon * o if key == "desc" then key and data are NULL and return -1 * o if key wasn't terminated by a semicolon, skip white space and fill @@ -978,14 +938,18 @@ getrcskey (fp, keyp, valp, lenp) } } while (whitespace (c)); - if (cur >= max) + /* Do not include any trailing whitespace in the value. */ + if (c != ';') { - size_t curoff = cur - value; - expand_string (&value, &valsize, valsize + 1); - cur = value + curoff; - max = value + valsize; + if (cur >= max) + { + size_t curoff = cur - value; + expand_string (&value, &valsize, valsize + 1); + cur = value + curoff; + max = value + valsize; + } + *cur++ = ' '; } - *cur++ = ' '; } /* if we got a semi-colon we are done with the entire value */ @@ -1096,6 +1060,59 @@ getrcsrev (fp, revp) *revp = key; } +/* Like getrcsrev, but don't die on error. Return the last character + read (last call to getc, which may be EOF). TODO: implement getrcsrev + in terms of this function. */ +static int +getrevnum (fp, revp) + FILE *fp; + char **revp; +{ + char *cur; + char *max; + int c; + + *revp = NULL; + do { + c = getc (fp); + if (c == EOF) + return c; + } while (whitespace (c)); + + if (!(isdigit (c) || c == '.')) + return c; + + cur = key; + max = key + keysize; + while (isdigit (c) || c == '.') + { + if (cur >= max) + { + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; + } + *cur = c; + + c = getc (fp); + if (c == EOF) + break; + cur++; + } + + if (cur >= max) + { + size_t curoff = cur - key; + expand_string (&key, &keysize, keysize + 1); + cur = key + curoff; + max = key + keysize; + } + *cur = '\0'; + *revp = key; + return c; +} + /* * process the symbols list of the rcs file */ @@ -1137,6 +1154,48 @@ do_symbols (list, val) } /* + * 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, val) + 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 @@ -1198,7 +1257,7 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag) { char *branch, *rev; - if (! RCS_isbranch (rcs, tag)) + if (! RCS_nodeisbranch (rcs, tag)) { /* We can't get a particular date if the tag is not a branch. */ @@ -1232,6 +1291,8 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag) * 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 (rcs, symtag, force_tag_match, simple_tag) @@ -1251,7 +1312,7 @@ RCS_gettag (rcs, symtag, force_tag_match, simple_tag) /* XXX this is probably not necessary, --jtc */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); /* If tag is "HEAD", special case to get head RCS revision */ if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0')) @@ -1483,6 +1544,8 @@ RCS_nodeisbranch (rcs, rev) int dots; char *version; + assert (rcs != NULL); + /* numeric revisions are easy -- even number of dots is a branch */ if (isdigit (*rev)) return ((numdots (rev) & 1) == 0); @@ -1573,7 +1636,8 @@ RCS_whatbranch (rcs, rev) /* * Get the head of the specified branch. If the branch does not exist, - * return NULL or RCS_head depending on force_tag_match + * return NULL or RCS_head depending on force_tag_match. + * Returns NULL or a newly malloc'd string. */ char * RCS_getbranch (rcs, tag, force_tag_match) @@ -1591,7 +1655,7 @@ RCS_getbranch (rcs, tag, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); /* find out if the tag contains a dot, or is on the trunk */ cp = strrchr (tag, '.'); @@ -1690,9 +1754,88 @@ RCS_getbranch (rcs, tag, force_tag_match) return (xstrdup (vn->version)); } +/* 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 (rcs, target) + 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 = (RCSVers *) 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 + * branch, otherwise the real head. + * Returns NULL or a newly malloc'd string. */ char * RCS_head (rcs) @@ -1730,7 +1873,7 @@ RCS_getdate (rcs, date, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); /* if the head is on a branch, try the branch first */ if (rcs->branch != NULL) @@ -1817,7 +1960,7 @@ RCS_getdatebranch (rcs, date, branch) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); p = findnode (rcs->versions, xrev); free (xrev); @@ -1829,7 +1972,8 @@ RCS_getdatebranch (rcs, date, branch) if (RCS_datecmp (vers->date, date) <= 0) cur_rev = vers->version; - /* if no branches list, return now */ + /* 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); @@ -1843,9 +1987,11 @@ RCS_getdatebranch (rcs, date, branch) free (xbranch); if (p == vers->branches->list) { - /* FIXME: This case would seem to imply that the RCS file is - somehow invalid. Should we give an error message? */ - return (NULL); + /* 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); @@ -1911,7 +2057,7 @@ RCS_getrevtime (rcs, rev, date, fudge) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); /* look up the revision */ p = findnode (rcs->versions, rev); @@ -1934,15 +2080,9 @@ RCS_getrevtime (rcs, rev, date, fudge) ftm->tm_year -= 1900; /* put the date in a form getdate can grok */ -#ifdef HAVE_RCS5 (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon, ftm->tm_mday, ftm->tm_year + 1900, ftm->tm_hour, ftm->tm_min, ftm->tm_sec); -#else - (void) sprintf (tdate, "%d/%d/%d %d:%d:%d", ftm->tm_mon, - ftm->tm_mday, ftm->tm_year + 1900, ftm->tm_hour, - ftm->tm_min, ftm->tm_sec); -#endif /* turn it into seconds since the epoch */ revdate = get_date (tdate, (struct timeb *) NULL); @@ -1952,11 +2092,7 @@ RCS_getrevtime (rcs, rev, date, fudge) if (date) { /* put an appropriate string into ``date'' if we were given one */ -#ifdef HAVE_RCS5 ftm = gmtime (&revdate); -#else - ftm = localtime (&revdate); -#endif (void) sprintf (date, DATEFORM, ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, @@ -1967,13 +2103,32 @@ RCS_getrevtime (rcs, rev, date, fudge) } List * +RCS_getlocks (rcs) + RCSNode *rcs; +{ + assert(rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, 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(rcs) RCSNode *rcs; { assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->symbols_data) { rcs->symbols = getlist (); @@ -1987,6 +2142,7 @@ RCS_symbols(rcs) /* * Return the version associated with a particular symbolic tag. + * Returns NULL or a newly malloc'd string. */ static char * translate_symtag (rcs, tag) @@ -1994,7 +2150,7 @@ translate_symtag (rcs, tag) const char *tag; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); if (rcs->symbols != NULL) { @@ -2067,17 +2223,13 @@ RCS_check_kflag (arg) " -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; -#ifndef HAVE_RCS5 - error (1, 0, "%s %s: your version of RCS does not support the -k option", - program_name, command_name); -#endif - if (arg) { for (cpp = kflags; *cpp != NULL; cpp++) @@ -2140,7 +2292,7 @@ RCS_isdead (rcs, tag) RCSVers *version; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); p = findnode (rcs->versions, tag); if (p == NULL) @@ -2161,7 +2313,7 @@ RCS_getexpand (rcs) { assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, NULL); + RCS_reparsercsfile (rcs, NULL); return rcs->expand; } @@ -2345,45 +2497,12 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) /* If we are using -kkvl, dig out the locker information if any. */ locker = NULL; - if (expand == KFLAG_KVL && rcs->other != NULL) + if (expand == KFLAG_KVL) { - Node *p; - - p = findnode (rcs->other, "locks"); - if (p != NULL) - { - char *cp; - size_t verlen; - - /* The format of the locking information is - USER:VERSION USER:VERSION ... - If we find our version on the list, we set LOCKER to - the corresponding user name. */ - - verlen = strlen (ver->version); - cp = p->data; - while ((cp = strstr (cp, ver->version)) != NULL) - { - if (cp > p->data - && cp[-1] == ':' - && (cp[verlen] == '\0' - || whitespace (cp[verlen]))) - { - char *cpend; - - --cp; - cpend = cp; - while (cp > p->data && ! whitespace (*cp)) - --cp; - locker = xmalloc (cpend - cp + 1); - memcpy (locker, cp, cpend - cp); - locker[cpend - cp] = '\0'; - break; - } - - ++cp; - } - } + Node *lock; + lock = findnode (RCS_getlocks(rcs), ver->version); + if (lock != NULL) + locker = xstrdup (lock->data); } /* RCS keywords look like $STRING$ or $STRING: VALUE$. */ @@ -2857,14 +2976,8 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) /* We want the head revision. Try to read it directly. */ - if (rcs->flags & NODELTA) - { - free_rcsnode_contents (rcs); - rcs->flags |= PARTIAL; - } - if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, &fp); + RCS_reparsercsfile (rcs, &fp); else { fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); @@ -2912,7 +3025,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) fp = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, 0, &fp); + RCS_reparsercsfile (rcs, &fp); if (fp == NULL) { @@ -3021,16 +3134,42 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (workfile == NULL && sout == RUN_TTY) { - if (len > 0) - cvs_output (value, len); + 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 { - if (fwrite (value, 1, len, ofp) != len) - error (1, errno, "cannot write %s", - (workfile != NULL - ? workfile - : (sout != RUN_TTY ? sout : "stdout"))); + /* 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 (1, errno, "cannot write %s", + (workfile != NULL + ? workfile + : (sout != RUN_TTY ? sout : "stdout"))); + p += nstep; + nleft -= nstep; + if (nleft < nstep) + nstep = nleft; + } } if (workfile != NULL) @@ -3057,6 +3196,794 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) return 0; } +static RCSVers *RCS_findlock_or_tip PROTO ((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 (rcs) + 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 (strcmp (p->data, user) == 0) + { + 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 (RCSVers *) 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 (RCSVers *) 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 (r) + 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 (r) + 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 (r, s) + 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 *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 (rcs, branch) + 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); + return NULL; + } + branchnode = (RCSVers *) 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 legality). + If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If FLAGS & + RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & RCS_FLAGS_MODTIME, + use the working file's modification time for the checkin time. + WORKFILE is the working file to check in from, or NULL to use the usual + RCS rules for deriving it from the RCSFILE. + + Return value is -1 for error (and errno is set to indicate the + error), positive for error (and an error message has been printed), + or zero for success. */ + +/* TODO: RCS_checkin always unlinks the working file after checkin -- + then RCS_checkout checks it out again. The logic should probably + be reversed here. */ + +int +RCS_checkin (rcs, workfile, message, rev, flags) + RCSNode *rcs; + char *workfile; + char *message; + char *rev; + int flags; +{ + RCSVers *delta, *commitpt; + Deltatext *dtext; + Node *nodep; + char *tmpfile, *changefile, *chtext; + char *diffopts; + size_t bufsize; + int buflen, chtextlen; + int status, checkin_quiet, allocated_workfile; + struct tm *ftm; + time_t modtime; + + commitpt = NULL; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); + + /* Get basename of working file. Is there a library function to + do this? I couldn't find one. -twp */ + allocated_workfile = 0; + 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'; + allocated_workfile = 1; + } + + checkin_quiet = flags & RCS_FLAGS_QUIET; + if (!checkin_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 (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"); + + /* Create a new deltatext node. */ + dtext = (Deltatext *) xmalloc (sizeof (Deltatext)); + memset (dtext, 0, sizeof (Deltatext)); + + dtext->log = make_message_rcslegal (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->key = xstrdup (newrev); + nodep->data = (char *) delta; + (void) addnode (rcs->versions, nodep); + + dtext->version = xstrdup (newrev); + bufsize = 0; + get_file(workfile, workfile, "r", &dtext->text, &bufsize, &dtext->len); + + if (!checkin_quiet) + { + cvs_output ("initial revision: ", 0); + cvs_output (rcs->head, 0); + cvs_output ("\n", 1); + } + + fout = rcs_internal_lockfile (rcs->path); + RCS_putadmin (rcs, fout); + RCS_putdtree (rcs, rcs->head, fout); + RCS_putdesc (rcs, fout); + rcs->delta_pos = ftell (fout); + if (rcs->delta_pos == -1) + error (1, errno, "cannot ftell for %s", rcs->path); + putdeltatext (fout, dtext); + rcs_internal_unlockfile (fout, rcs->path); + freedeltatext (dtext); + + /* Removing the file here is an RCS user-visible behavior which + we almost surely do not need in the CVS case. In fact, getting + rid of it should clean up link_file and friends in import.c. */ + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (0, errno, "cannot remove %s", workfile); + + if (!checkin_quiet) + cvs_output ("done\n", 5); + + return 0; + } + + /* 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 + || strcmp (commitpt->version, rcs->head) == 0) + 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(*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); + 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 = (RCSVers *) 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 (strcmp (nodep->data, delta->author) != 0) + { + error (0, 0, "%s: revision %s locked by %s", + rcs->path, + nodep->key, nodep->data); + status = 1; + goto checkin_done; + } + 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 + && strcmp (rcs->expand, "b") == 0) + ? "-kb" + : "-ko"), + tmpfile, + (RCSCHECKOUTPROC)0, NULL); + if (status != 0) + error (1, status < 0 ? errno : 0, + "could not check out revision %s of `%s'", + commitpt->version, rcs->path); + + bufsize = buflen = 0; + chtext = NULL; + chtextlen = 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 && strcmp (rcs->expand, "b") == 0 + ? "-a -n --binary" + : "-a -n"); + + if (strcmp (commitpt->version, rcs->head) == 0 && + 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; + get_file (workfile, workfile, + rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "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, 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 && strcmp (rcs->expand, "b") == 0 ? "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, 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 && strcmp (rcs->expand, "b") == 0 ? "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 (strcmp (commitpt->version, rcs->head) == 0) + { + 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->key = xstrdup (delta->version); + nodep->data = (char *) delta; + (void) addnode (rcs->versions, nodep); + + /* Write the new RCS file, inserting the new delta at COMMITPT. */ + if (!checkin_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); + + /* Removing the file here is an RCS user-visible behavior which + we almost surely do not need in the CVS case. In fact, getting + rid of it should clean up link_file and friends in import.c. */ + 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); + if (unlink_file (changefile) < 0) + error (0, errno, "cannot remove %s", changefile); + + if (!checkin_quiet) + cvs_output ("done\n", 5); + + checkin_done: + if (allocated_workfile) + 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 @@ -3183,7 +4110,11 @@ RCS_settag (rcs, tag, rev) const char *tag; const char *rev; { - int ret; + List *symbols; + Node *node; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); /* FIXME: This check should be moved to RCS_check_tag. There is no reason for it to be here. */ @@ -3198,89 +4129,61 @@ RCS_settag (rcs, tag, rev) return 1; } - ret = RCS_exec_settag (rcs->path, tag, rev); - if (ret != 0) - return ret; - - /* If we have already parsed the RCS file, update the tag - information. If we have not yet parsed it (i.e., the PARTIAL - flag is set), the new tag information will be read when and if - we do parse it. */ - if ((rcs->flags & PARTIAL) == 0) + /* 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) { - List *symbols; - Node *node; - - /* 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 (symbols, node); - } + 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); } - - /* Setting the tag will most likely have invalidated delta_pos. */ - rcs->flags |= NODELTA; return 0; } -/* Delete the symbolic tag TAG from the RCS file RCS. NOERR is 1 to - suppress errors--FIXME it would be better to avoid the errors or - some cleaner solution. */ +/* 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 (rcs, tag, noerr) +RCS_deltag (rcs, tag) RCSNode *rcs; const char *tag; - int noerr; { - int ret; - - ret = RCS_exec_deltag (rcs->path, tag, noerr); - if (ret != 0) - return ret; - - /* If we have already parsed the RCS file, update the tag - information. If we have not yet parsed it (i.e., the PARTIAL - flag is set), the new tag information will be read when and if - we do parse it. */ - if ((rcs->flags & PARTIAL) == 0) - { - List *symbols; + List *symbols; + Node *node; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); - /* 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) - { - Node *node; + symbols = RCS_symbols (rcs); + if (symbols == NULL) + return 1; - node = findnode (symbols, tag); - if (node != NULL) - delnode (node); - } - } + node = findnode (symbols, tag); + if (node == NULL) + return 1; - /* Deleting the tag will most likely have invalidated delta_pos. */ - rcs->flags |= NODELTA; + delnode (node); return 0; } @@ -3292,124 +4195,836 @@ RCS_setbranch (rcs, rev) RCSNode *rcs; const char *rev; { - int ret; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); if (rev == NULL && rcs->branch == NULL) return 0; if (rev != NULL && rcs->branch != NULL && strcmp (rev, rcs->branch) == 0) return 0; - ret = RCS_exec_setbranch (rcs->path, rev); - if (ret != 0) - return ret; - if (rcs->branch != NULL) free (rcs->branch); rcs->branch = xstrdup (rev); - /* Changing the branch will have changed the data in the file, so - delta_pos will no longer be correct. */ - rcs->flags |= NODELTA; - return 0; } -/* Lock revision REV. NOERR is 1 to suppress errors--FIXME it would - be better to avoid the errors or some cleaner solution. FIXME: +/* Lock revision REV. LOCK_QUIET is 1 to suppress output. FIXME: This is only required because the RCS ci program requires a lock. If we eventually do the checkin ourselves, this can become a no-op. */ +/* 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 (rcs, rev, noerr) +RCS_lock (rcs, rev, lock_quiet) RCSNode *rcs; const char *rev; - int noerr; + int lock_quiet; { - int ret; + List *locks; + Node *p; + char *user; + char *xrev = NULL; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, 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); + + /* If rev is a branch number, lock the latest revision on that + branch. I think that if the branch doesn't exist, it's + okay to return 0 -- that just means that the branch is new, + so we don't need to lock it anyway. -twp */ + else if (RCS_nodeisbranch (rcs, rev)) + { + xrev = RCS_getbranch (rcs, (char *) rev, 1); + if (xrev == NULL) + { + if (!lock_quiet) + error (0, 0, "%s: branch %s absent", rcs->path, rev); + return 1; + } + } + + if (xrev == NULL) + xrev = xstrdup (rev); + + /* 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 (findnode (rcs->versions, xrev) == NULL) + { + if (!lock_quiet) + error (0, 0, "%s: revision %s absent", rcs->path, xrev); + free (xrev); + return 1; + } - ret = RCS_exec_lock (rcs->path, rev, noerr); - if (ret != 0) - return ret; + /* Is this rev already locked? */ + p = findnode (locks, xrev); + if (p != NULL) + { + if (strcmp (p->data, user) == 0) + { + /* We already own the lock on this revision, so do nothing. */ + free (xrev); + return 0; + } - /* Setting a lock will have changed the data in the file, so - delta_pos will no longer be correct. */ - rcs->flags |= NODELTA; + /* Break the lock. */ + if (!lock_quiet) + { + cvs_output (rev, 0); + cvs_output (" unlocked\n", 0); + } + delnode (p); + } + + /* 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. NOERR is 1 to suppress errors--FIXME it would - be better to avoid the errors or some cleaner solution. FIXME: +/* 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. */ + 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 (rcs, rev, noerr) +RCS_unlock (rcs, rev, unlock_quiet) RCSNode *rcs; const char *rev; - int noerr; + int unlock_quiet; { - int ret; + Node *lock; + List *locks; + char *user; + char *xrev = NULL; + + user = getcaller(); + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); + + /* If rev is NULL, unlock 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 (strcmp (p->data, user) == 0) + { + 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) + return 0; /* no lock found, ergo nothing to do */ + xrev = xstrdup (lock->key); + } + else if (RCS_nodeisbranch (rcs, rev)) + { + /* If rev is a branch number, unlock the latest revision on that + branch. */ + xrev = RCS_getbranch (rcs, (char *) rev, 1); + if (xrev == NULL) + { + error (0, 0, "%s: branch %s absent", rcs->path, rev); + return 1; + } + } + else + /* REV is an exact revision number. */ + xrev = xstrdup (rev); - ret = RCS_exec_unlock (rcs->path, rev, noerr); - if (ret != 0) - return ret; + lock = findnode (RCS_getlocks (rcs), xrev); + if (lock == NULL) + { + /* This revision isn't locked. */ + free (xrev); + return 0; + } - /* Setting a lock will have changed the data in the file, so - delta_pos will no longer be correct. */ - rcs->flags |= NODELTA; + if (strcmp (lock->data, user) != 0) + { + /* 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; + 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; } - -/* RCS_deltas and friends. Processing of the deltas in RCS files. */ -/* Linked list of allocated blocks. Seems kind of silly to - reinvent the obstack wheel, and this isn't as nice as obstacks - in some ways, but obstacks are pretty baroque. */ -struct allocblock +/* 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 (rcs, user) + RCSNode *rcs; + char *user; { - char *text; - struct allocblock *next; -}; -struct allocblock *blocks; + char *access, *a; -static void *block_alloc PROTO ((size_t)); + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); -static void * -block_alloc (n) - size_t n; -{ - struct allocblock *blk; - blk = (struct allocblock *) xmalloc (sizeof (struct allocblock)); - blk->text = xmalloc (n); - blk->next = blocks; - blocks = blk; - return blk->text; + if (rcs->access == NULL) + rcs->access = xstrdup (user); + else + { + access = xstrdup (rcs->access); + for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " ")) + { + if (strcmp (a, user) == 0) + { + free (access); + return; + } + } + rcs->access = (char *) xrealloc + (rcs->access, strlen (rcs->access) + strlen (user) + 2); + strcat (rcs->access, " "); + strcat (rcs->access, user); + } } -static void block_free PROTO ((void)); +/* Remove USER from the access list of RCS. */ -static void -block_free () +void +RCS_delaccess (rcs, user) + RCSNode *rcs; + char *user; { - struct allocblock *p; - struct allocblock *q; + char *p, *s; + int ulen; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); + + if (rcs->access == NULL) + return; - p = blocks; + p = rcs->access; + ulen = strlen (user); while (p != NULL) { - free (p->text); - q = p->next; - free (p); - p = q; + if (p[ulen] == '\0' || p[ulen] == ' ') + if (strncmp (p, user, ulen) == 0) + break; + p = strchr (p, ' '); + if (p != NULL) + ++p; } - blocks = NULL; + + if (p == NULL) + return; + + s = p + ulen; + while (*s != '\0') + *p++ = *s++; + *p = '\0'; +} + +char * +RCS_getaccess (rcs) + RCSNode *rcs; +{ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, NULL); + + return rcs->access; } +static int findtag PROTO ((Node *, void *)); + +/* Return a nonzero value if the revision specified by ARG is found. */ + +static int +findtag (node, arg) + Node *node; + void *arg; +{ + char *rev = (char *)arg; + + if (strcmp (node->data, rev) == 0) + 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 (rcs, tag1, tag2, inclusive) + 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) && strcmp (rev1, rev2) == 0) + 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 (strcmp (rev1, branchpoint) != 0) + { + /* Walk deltas from BRANCHPOINT on, looking for REV1. */ + nodep = findnode (rcs->versions, branchpoint); + revp = (RCSVers *) nodep->data; + while (revp->next != NULL && strcmp (revp->next, rev1) != 0) + { + revp = (RCSVers *) 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 = (RCSVers *) nodep->data; + + if (rev2 != NULL) + found = (strcmp (revp->version, rev2) == 0); + 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; + } + + /* 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. */ + + assert (before != NULL || after != NULL); + + if (after != NULL) + { + char *diffbuf; + size_t bufsize, len; + + afterfile = cvs_temp_name(); + status = RCS_checkout (rcs, NULL, after, NULL, NULL, afterfile, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + goto delrev_done; + + else if (status < 0) + { + error (0, errno, + "cannot check out revision %s of %s", after, rcs->path); + 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, NULL, beforefile, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + goto delrev_done; + else if (status < 0) + { + error (0, errno, "cannot check out revision %s of %s", + before, rcs->path); + goto delrev_done; + } + + outfile = cvs_temp_name(); + status = diff_exec (beforefile, afterfile, "-n", 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 = (RCSVers *) 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 || strcmp (next, after) != 0); + next = revp->next) + { + nodep = findnode (rcs->versions, next); + revp = (RCSVers *) 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 (strcmp (rev1, branchpoint) == 0) + { + nodep = findnode (rcs->versions, before); + revp = (RCSVers *) nodep->data; + nodep = revp->branches->list->next; + while (nodep != revp->branches->list && + strcmp (nodep->key, rev1) != 0) + 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 = (RCSVers *) 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; +} + +/* RCS_deltas and friends. Processing of the deltas in RCS files. */ + struct line { - /* Text of this 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; @@ -3418,6 +5033,8 @@ struct line /* 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 @@ -3442,30 +5059,36 @@ linevector_init (vec) vec->vector = NULL; } -static int linevector_add PROTO ((struct linevector *vec, char *text, +static int linevector_add PROTO ((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. All \n in TEXT are changed to \0 (FIXME: I - don't think this is needed, or used, now that we have the ->len - field). Set the version for each of the new lines to VERS. This + 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. */ + 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 (vec, text, len, vers, pos) struct linevector *vec; - char *text; + const char *text; size_t len; RCSVers *vers; unsigned int pos; { - char *textend; + const char *textend; unsigned int i; unsigned int nnew; - char *p; - struct line *lines; + const char *p; + const char *nextline_text; + size_t nextline_len; + int nextline_newline; + struct line *q; if (len == 0) return 1; @@ -3477,8 +5100,6 @@ linevector_add (vec, text, len, vers, pos) for (p = text; p < textend; ++p) if (*p == '\n' && p + 1 < textend) ++nnew; - /* Allocate the struct line's. */ - lines = block_alloc (nnew * sizeof (struct line)); /* Expand VEC->VECTOR if needed. */ if (vec->nlines + nnew >= vec->lines_alloced) @@ -3498,29 +5119,41 @@ linevector_add (vec, text, len, vers, pos) if (pos > vec->nlines) return 0; - /* Actually add the lines, to LINES and VEC->VECTOR. */ + /* Actually add the lines, to VEC->VECTOR. */ i = pos; - lines[0].text = text; - lines[0].vers = vers; - lines[0].has_newline = 0; - vec->vector[i++] = &lines[0]; + nextline_text = text; + nextline_newline = 0; for (p = text; p < textend; ++p) if (*p == '\n') { - *p = '\0'; - lines[i - pos - 1].has_newline = 1; + nextline_newline = 1; if (p + 1 == textend) /* If there are no characters beyond the last newline, we don't consider it another line. */ break; - lines[i - pos - 1].len = p - lines[i - pos - 1].text; - lines[i - pos].text = p + 1; - lines[i - pos].vers = vers; - lines[i - pos].has_newline = 0; - vec->vector[i] = &lines[i - pos]; - ++i; - } - lines[i - pos - 1].len = p - lines[i - pos - 1].text; + 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; @@ -3541,6 +5174,11 @@ linevector_delete (vec, pos, nlines) 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; @@ -3554,6 +5192,13 @@ linevector_copy (to, from) 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) @@ -3566,18 +5211,27 @@ linevector_copy (to, from) 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 PROTO ((struct linevector *)); -/* Free storage associated with linevector (that is, the vector but - not the lines pointed to). */ +/* Free storage associated with linevector. */ static void linevector_free (vec) 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 PROTO ((char *)); @@ -3638,7 +5292,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers) enum {ADD, DELETE} type; unsigned long pos; unsigned long nlines; - char *new_lines; + const char *new_lines; size_t len; struct deltafrag *next; }; @@ -3691,9 +5345,8 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers) break; } - /* Copy the text we are adding into allocated space. */ - df->new_lines = block_alloc (q - p); - memcpy (df->new_lines, p, q - p); + /* Stash away a pointer to the text we are adding. */ + df->new_lines = p; df->len = q - p; p = q; @@ -3743,9 +5396,9 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers) 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. The function - changes the contents of TEXTBUF. This function returns 1 for - success. On failure, it calls error and returns 0. */ + and *RETLEN. The new buffer is allocated by xmalloc. + + Return 1 for success. On failure, call error and return 0. */ int rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) @@ -3803,11 +5456,6 @@ rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) linevector_free (&lines); - /* Note that this assumes that we have not called from anything - else which uses the block vectors. FIXME: We could fix this by - saving and restoring the state of the block allocation code. */ - block_free (); - return ret; } @@ -3859,19 +5507,11 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) if (fp == NULL) { - if (rcs->flags & NODELTA) - { - free_rcsnode_contents (rcs); - RCS_reparsercsfile (rcs, 0, &fp); - } - else - { - fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); - if (fp == NULL) - error (1, 0, "unable to reopen `%s'", rcs->path); - if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) - error (1, 0, "cannot fseek RCS file"); - } + fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file"); } ishead = 1; @@ -3948,12 +5588,7 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) { if (ishead) { - char *p; - - p = block_alloc (vallen); - memcpy (p, value, vallen); - - if (! linevector_add (&curlines, p, vallen, NULL, 0)) + if (! linevector_add (&curlines, value, vallen, NULL, 0)) error (1, 0, "invalid rcs file %s", rcs->path); ishead = 0; @@ -4092,7 +5727,18 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) /* Now output the date. */ ym = strchr (prvers->date, '.'); if (ym == NULL) - cvs_output ("?\?-??\?-??", 0); + { + /* ??- is an ANSI trigraph. The ANSI way to + avoid it is \? but some pre ANSI compilers + complain about the unrecognized escape + sequence. Of course string concatenation + ("??" "-???") is also an ANSI-ism. Testing + __STDC__ seems to be a can of worms, since + compilers do all kinds of things with it. */ + cvs_output ("??", 0); + cvs_output ("-???", 0); + cvs_output ("-??", 0); + } else { md = strchr (ym + 1, '.'); @@ -4110,8 +5756,9 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) cvs_output (ym - 2, 2); } cvs_output ("): ", 0); - cvs_output (headlines.vector[ln]->text, - headlines.vector[ln]->len); + if (headlines.vector[ln]->len != 0) + cvs_output (headlines.vector[ln]->text, + headlines.vector[ln]->len); cvs_output ("\n", 1); } } @@ -4149,7 +5796,6 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) linevector_free (&headlines); linevector_free (&trunklines); - block_free (); return; l_error: @@ -4159,6 +5805,720 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) error (1, 0, "%s does not appear to be a valid rcs file", rcs->path); } + +static RCSVers * +getdelta (fp, rcsfile) + FILE *fp; + char *rcsfile; +{ + RCSVers *vnode; + char *key, *value, *cp; + long fpos; + Node *kv; + + vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); + memset (vnode, 0, sizeof (RCSVers)); + + /* Get revision number. This uses getrcskey because it doesn't + croak when encountering unexpected input. As a result, we have + to play unholy games with `key' and `value'. */ + fpos = ftell (fp); + getrcskey (fp, &key, &value, NULL); + + /* Make sure that it is a revision number and not a cabbage + or something. */ + for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) + /* do nothing */ ; + if (*cp != '\0' || strncmp (RCSDATE, value, strlen (RCSDATE)) != 0) + { + (void) fseek (fp, fpos, SEEK_SET); + free (vnode); + return NULL; + } + vnode->version = xstrdup (key); + + /* grab the value of the date from value */ + cp = value + strlen (RCSDATE);/* skip the "date" keyword */ + while (whitespace (*cp)) /* take space off front of value */ + cp++; + + vnode->date = xstrdup (cp); + + /* Get author field. */ + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be using errno in case of ferror. */ + if (key == NULL || strcmp (key, "author") != 0) + error (1, 0, "\ +unable to parse rcs file; `author' not in the expected place"); + vnode->author = xstrdup (value); + + /* Get state field. */ + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be using errno in case of ferror. */ + if (key == NULL || strcmp (key, "state") != 0) + error (1, 0, "\ +unable to parse rcs file; `state' not in the expected place"); + vnode->state = xstrdup (value); + if (strcmp (value, "dead") == 0) + { + vnode->dead = 1; + } + + /* Note that "branches" and "next" are in fact mandatory, according + to doc/RCSFILES. We perhaps should be giving an error if they + are not there. */ + + /* fill in the branch list (if any branches exist) */ + fpos = ftell (fp); + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be handling various error conditions better. */ + if (key != NULL && strcmp (key, RCSDESC) == 0) + { + (void) fseek (fp, fpos, SEEK_SET); + return vnode; + } + if (value != (char *) NULL) + { + vnode->branches = getlist (); + do_branches (vnode->branches, value); + } + + /* fill in the next field if there is a next revision */ + fpos = ftell (fp); + (void) getrcskey (fp, &key, &value, NULL); + /* FIXME: should be handling various error conditions better. */ + if (key != NULL && strcmp (key, RCSDESC) == 0) + { + (void) fseek (fp, fpos, SEEK_SET); + return vnode; + } + if (value != (char *) NULL) + vnode->next = xstrdup (value); + + /* + * XXX - this is where we put the symbolic link stuff??? + * (into newphrases in the deltas). + */ + /* FIXME: Does not correctly handle errors, e.g. from stdio. */ + while (1) + { + fpos = ftell (fp); + if (getrcskey (fp, &key, &value, NULL) < 0) + break; + + assert (key != NULL); + + if (strcmp (key, RCSDESC) == 0) + break; + + /* 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 (strcmp(key, RCSDEAD) == 0) + { + vnode->dead = 1; + if (vnode->state != NULL) + free (vnode->state); + vnode->state = xstrdup ("dead"); + continue; + } + /* if we have a new revision number, we're done with this delta */ + for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) + /* do nothing */ ; + 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 = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + 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); + } + } + + /* We got here because we read beyond the end of a delta. Seek back + to the beginning of the erroneous read. */ + (void) fseek (fp, fpos, SEEK_SET); + + return vnode; +} + +static void +freedeltatext (d) + 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 (rcs, fp) + RCSNode *rcs; + FILE *fp; +{ + char *num; + char *key, *value; + int n; + Node *p; + Deltatext *d; + size_t textlen; + + /* Get the revision number. */ + n = getrevnum (fp, &num); + if (ferror (fp)) + error (1, errno, "%s: cannot read", rcs->path); + if (n == EOF) + { + /* If n == EOF and 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", + rcs->path); + + d = (Deltatext *) xmalloc (sizeof (Deltatext)); + d->version = xstrdup (num); + + /* Get the log message. */ + if (getrcskey (fp, &key, &value, NULL) < 0) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); + if (strcmp (key, "log") != 0) + error (1, 0, "%s, delta %s: expected `log', got `%s'", + rcs->path, num, key); + d->log = xstrdup (value); + + /* Get random newphrases. */ + d->other = getlist(); + for (n = getrcskey (fp, &key, &value, &textlen); + n >= 0 && strcmp (key, "text") != 0; + n = getrcskey (fp, &key, &value, &textlen)) + { + p = getnode(); + p->type = RCSFIELD; + p->key = xstrdup (key); + p->data = xstrdup (value); + if (addnode (d->other, p) < 0) + { + error (0, 0, "warning: %s, delta %s: duplicate field `%s'", + rcs->path, num, key); + } + } + if (n < 0) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); + + /* Get the change text. We already know that this key is `text'. */ + d->text = (char *) malloc (textlen + 1); + d->len = textlen; + memcpy (d->text, value, textlen); + + 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 (symnode, fp) + Node *symnode; + void *fp; +{ + return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->key, symnode->data); +} + +static int putlock_proc PROTO ((Node *, void *)); + +/* putlock_proc is like putsymbol_proc, but key and data are reversed. */ + +static int +putlock_proc (symnode, fp) + Node *symnode; + void *fp; +{ + return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->data, symnode->key); +} + +static int +putrcsfield_proc (node, vfp) + 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 legal + 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. */ + int n = strcspn (node->data, "$,.:;@"); + if (node->data[n] == 0) + 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 (strcmp (node->key, "desc") != 0 && + strcmp (node->key, "log") != 0 && + strcmp (node->key, "text") != 0) + { + putc (';', fp); + } + return 0; +} + +/* Output the admin node for RCS into stream FP. */ + +static void +RCS_putadmin (rcs, fp) + 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); + 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 && strcmp (rcs->expand, "kv") != 0) + fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand); + + walklist (rcs->other, putrcsfield_proc, (void *) fp); + + putc ('\n', fp); +} + +static void +putdelta (vers, fp) + 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); + + putc ('\n', fp); +} + +static void +RCS_putdtree (rcs, rev, fp) + 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); + assert (p != NULL); + versp = (RCSVers *) 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 (rcs, fp) + 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 (fp, d) + 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 (rcs, fin, fout, newdtext, insertpt) + RCSNode *rcs; + FILE *fin; + FILE *fout; + Deltatext *newdtext; + char *insertpt; +{ + Deltatext *dtext; + RCSVers *dadmin; + Node *np; + int insertbefore, found; + + /* Make a note of whether NEWDTEXT should be inserted + before or after its INSERTPT. */ + insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1); + + found = 0; + while ((dtext = RCS_getdeltatext (rcs, fin)) != NULL) + { + found = (insertpt != NULL && strcmp (dtext->version, insertpt) == 0); + if (found && insertbefore) + putdeltatext (fout, newdtext); + + np = findnode (rcs->versions, dtext->version); + dadmin = (RCSVers *) np->data; + + /* If this revision has been outdated, just skip it. */ + if (dadmin->outdated) + 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) + { + 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); + } + putc ('\n', fout); +} + +/* 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. + + Note that we don't clean up the ,foo, file on ^C. We probably should. + I'm not completely sure whether RCS does or not (I looked at the code + a little, and didn't find it). + + If there is an error, give a fatal error; if we return we always + return a non-NULL value. */ + +static FILE * +rcs_internal_lockfile (rcsfile) + char *rcsfile; +{ + char *lockfile; + int fd; + struct stat rstat; + FILE *fp; + + /* Get the lock file name: `,file,' for RCS file `file,v'. */ + 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 (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. */ + fd = open (lockfile, OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, + S_IRUSR | S_IRGRP | S_IROTH); + + if (fd < 0) + { + error (1, errno, "could not open lock file `%s'", lockfile); + } + + free (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 (fd, rstat.st_mode) < 0) + error (1, errno, "cannot change mode for %s", lockfile); +#endif + fp = fdopen (fd, FOPEN_BINARY_WRITE); + if (fp == NULL) + error (1, errno, "cannot fdopen %s", lockfile); + return fp; +} + +static void +rcs_internal_unlockfile (fp, rcsfile) + FILE *fp; + char *rcsfile; +{ + char *lockfile; + + /* Get the lock file name: `,file,' for RCS file `file,v'. */ + lockfile = rcs_lockfilename (rcsfile); + + /* 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)) + error (1, 0, "error writing to lock file %s", lockfile); + if (fclose (fp) == EOF) + error (1, errno, "error closing lock file %s", lockfile); + + rename_file (lockfile, rcsfile); + free (lockfile); +} + +static char * +rcs_lockfilename (rcsfile) + char *rcsfile; +{ + char *lockfile, *lockp; + 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 (rcs, newdtext, insertpt) + RCSNode *rcs; + Deltatext *newdtext; + char *insertpt; +{ + FILE *fin, *fout; + + if (noexec) + return; + + 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. */ + if ((fin = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ)) == NULL) + error (1, errno, "cannot open RCS file `%s' for reading", rcs->path); + if (fseek (fin, rcs->delta_pos, SEEK_SET) < 0) + error (1, errno, "cannot fseek in RCS file %s", rcs->path); + + /* 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 = ftell (fout); + if (rcs->delta_pos == -1) + error (1, errno, "cannot ftell in RCS file %s", rcs->path); + + RCS_copydeltas (rcs, fin, fout, newdtext, insertpt); + + if (ferror (fin)) + error (0, errno, "warning: when closing 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); +} /* Annotate command. In rcs.c for historical reasons (from back when @@ -4184,7 +6544,7 @@ annotate_fileproc (callerdat, finfo) return (1); if (finfo->rcs->flags & PARTIAL) - RCS_reparsercsfile (finfo->rcs, 0, &fp); + RCS_reparsercsfile (finfo->rcs, &fp); version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, (int *) NULL); @@ -4211,6 +6571,7 @@ static const char *const annotate_usage[] = "\t-f\tUse head revision if tag/date not found.\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 }; @@ -4282,3 +6643,57 @@ annotate (argc, argv) argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, 1); } + +/* + * 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 <tab> YYYY/MM/DD <sp> HH:MM:SS <tab> 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. + */ +char * +make_file_label (path, rev, rcs) + char *path; + char *rev; + RCSNode *rcs; +{ + char datebuf[MAXDATELEN]; + char *label; + char *file; + + file = last_component (path); + label = (char *) xmalloc (strlen (file) + + (rev == NULL ? 0 : strlen (rev)) + + 50); + + if (rev) + { + char *date; + RCS_getrevtime (rcs, rev, datebuf, 0); + date = printable_date (datebuf); + (void) sprintf (label, "-L%s\t%s\t%s", file, date, rev); + free (date); + } + else + { + struct stat sb; + struct tm *wm; + + if (CVS_STAT (file, &sb) < 0) + error (0, 1, "could not get info for `%s'", path); + else + { + wm = gmtime (&sb.st_mtime); + (void) sprintf (datebuf, "%04d/%02d/%02d %02d:%02d:%02d", + wm->tm_year + 1900, wm->tm_mon + 1, + wm->tm_mday, wm->tm_hour, + wm->tm_min, wm->tm_sec); + (void) sprintf (label, "-L%s\t%s", file, datebuf); + } + } + return label; +} + diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c index 1f356cfd8f8..9a237a9be96 100644 --- a/gnu/usr.bin/cvs/src/rcscmds.c +++ b/gnu/usr.bin/cvs/src/rcscmds.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * 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. @@ -12,16 +12,8 @@ #include "cvs.h" #include <assert.h> -/* This file, rcs.h, and rcs.c, are intended to define our interface - to RCS files. As of July, 1996, there are still a few places that - still exec RCS commands directly. The intended long-term direction - is to have CVS access RCS files via an RCS library (rcs.c can be - considered a start at one), for performance, cleanliness (CVS has - some awful hacks to work around RCS behaviors which don't make - sense for CVS), installation hassles, ease of implementing the CVS - server (I don't think that the output-out-of-order bug can be - completely fixed as long as CVS calls RCS), and perhaps other - reasons. +/* 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 @@ -32,7 +24,7 @@ existing CVS code which accesses RCS files. In particular, simple approaches will often be slow. - 2. An RCS library should not use the code from the current RCS + 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 @@ -54,169 +46,514 @@ See doc/RCSFILES in the CVS distribution for documentation of this file format. - On somewhat related notes: + On a related note, see the comments at diff_exec, later in this file, + for more on the diff library. */ - 1. A library for diff is an obvious idea. The one thing which I'm - not so sure about is that I think CVS probably wants the ability to - allow arbitrarily-bizarre (and possibly customized for particular - file formats) external diff programs. +static void RCS_output_diff_options PROTO ((char *, char *, char *, char *)); - 2. A library for patch is another such idea. CVS's needs are - smaller than the functionality of the standalone patch program (it - only calls patch in the client, and only needs to be able to patch - unmodified versions, which is something that RCS_deltas already - does in a different context). But it is silly for CVS to be making - people install patch as well as CVS for such a simple purpose. */ -/* For RCS file PATH, make symbolic tag TAG point to revision REV. - This validates that TAG is OK for a user to use. Return value is - -1 for error (and errno is set to indicate the error), positive for - error (and an error message has been printed), or zero for success. */ +/* 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. */ -int -RCS_exec_settag(path, tag, rev) - const char *path; - const char *tag; - const char *rev; +/* 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 PROTO ((const char *)); +static void call_diff_setup PROTO ((const char *prog)); +static int call_diff PROTO ((char *out)); +static int call_diff3 PROTO ((char *out)); + +/* VARARGS */ +static void +call_diff_setup (prog) + const char *prog; { - run_setup ("%s%s -x,v/ -q -N%s:%s", Rcsbin, RCS, tag, rev); - run_arg (path); - return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + 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); } -/* NOERR is 1 to suppress errors--FIXME it would - be better to avoid the errors or some cleaner solution. */ -int -RCS_exec_deltag(path, tag, noerr) - const char *path; - const char *tag; - int noerr; +static void +call_diff_arg (s) + const char *s; { - run_setup ("%s%s -x,v/ -q -N%s", Rcsbin, RCS, tag); - run_arg (path); - return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL); + call_diff_add_arg (s); } -/* set RCS branch to REV */ -int -RCS_exec_setbranch(path, rev) - const char *path; - const char *rev; +static void +call_diff_add_arg (s) + const char *s; { - run_setup ("%s%s -x,v/ -q -b%s", Rcsbin, RCS, rev ? rev : ""); - run_arg (path); - return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + /* 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; } -/* Lock revision REV. NOERR is 1 to suppress errors--FIXME it would - be better to avoid the errors or some cleaner solution. */ -int -RCS_exec_lock(path, rev, noerr) - const char *path; - const char *rev; - int noerr; +/* diff_run is imported from libdiff.a. */ +extern int diff_run PROTO ((int argc, char **argv, char *out)); + +static int +call_diff (out) + char *out; { - run_setup ("%s%s -x,v/ -q -l%s", Rcsbin, RCS, rev ? rev : ""); - run_arg (path); - return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL); + /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output + with has "Index: foo" and such; stdout and/or stderr for diff's + output). I think the only reason that this used to not be such + a problem is that the time spent on the fork() and exec() of diff + slowed us down enough to let the "Index:" make it through first. + + The real fix, of course, will be to have the diff library do all + its output through callbacks (which CVS will supply as cvs_output + and cvs_outerr). */ + sleep (1); + + if (out == RUN_TTY) + return diff_run (call_diff_argc, call_diff_argv, NULL); + else + return diff_run (call_diff_argc, call_diff_argv, out); } -/* Unlock revision REV. NOERR is 1 to suppress errors--FIXME it would - be better to avoid the errors or some cleaner solution. */ -int -RCS_exec_unlock(path, rev, noerr) - const char *path; - const char *rev; - int noerr; +extern int diff3_run PROTO ((int argc, char **argv, char *out)); + +static int +call_diff3 (out) + char *out; { - run_setup ("%s%s -x,v/ -q -u%s", Rcsbin, RCS, rev ? rev : ""); - run_arg (path); - return run_exec (RUN_TTY, RUN_TTY, noerr ? DEVNULL : RUN_TTY, RUN_NORMAL); + /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output + with has "Index: foo" and such; stdout and/or stderr for diff's + output). I think the only reason that this used to not be such + a problem is that the time spent on the fork() and exec() of diff + slowed us down enough to let the "Index:" make it through first. + + The real fix, of course, will be to have the diff library do all + its output through callbacks (which CVS will supply as cvs_output + and cvs_outerr). */ + sleep (1); + + if (out == RUN_TTY) + return diff3_run (call_diff_argc, call_diff_argv, NULL); + else + return diff3_run (call_diff_argc, call_diff_argv, out); } + + /* Merge revisions REV1 and REV2. */ + int -RCS_merge(path, options, rev1, rev2) - const char *path; - const char *options; - const char *rev1; - const char *rev2; +RCS_merge(rcs, path, workfile, options, rev1, rev2) + RCSNode *rcs; + char *path; + char *workfile; + char *options; + char *rev1; + char *rev2; { - int status; + 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). */ + xrev1 = RCS_gettag (rcs, rev1, 0, NULL); + xrev2 = RCS_gettag (rcs, rev2, 0, NULL); - /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */ + /* 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); - run_setup ("%s%s -x,v/ %s -r%s -r%s %s", Rcsbin, RCS_RCSMERGE, - options, rev1, rev2, path); - status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); -#ifndef HAVE_RCS5 - if (status == 0) + tmp1 = cvs_temp_name(); + if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, + (RCSCHECKOUTPROC)0, NULL)) { - error (1, 0, "CVS no longer supports RCS versions older than RCS5"); - /* This case needs to call file_has_markers to see if the file - contains conflict indicators. But is anyone using the !HAVE_RCS5 - code any more? */ + cvs_outerr ("rcsmerge: co failed\n", 0); + error_exit(); } -#endif - return status; + + 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); + error_exit(); + } + + /* 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 (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) + error_exit(); + + 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; } -/* Check in to RCSFILE with revision REV (which must be greater than the - largest revision) and message MESSAGE (which is checked for legality). - If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If FLAGS & - RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & RCS_FLAGS_MODTIME, - use the working file's modification time for the checkin time. - WORKFILE is the working file to check in from, or NULL to use the usual - RCS rules for deriving it from the RCSFILE. - - Return value is -1 for error (and errno is set to indicate the - error), positive for error (and an error message has been printed), - or zero for success. */ +/* 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_checkin (rcsfile, workfile, message, rev, flags) - char *rcsfile; +RCS_exec_rcsdiff (rcsfile, opts, options, rev1, rev2, label1, label2, workfile) + RCSNode *rcsfile; + char *opts; + char *options; + char *rev1; + char *rev2; + char *label1; + char *label2; char *workfile; - char *message; - char *rev; - int flags; { - /* The desired behavior regarding permissions is to preserve the - permissions on RCSFILE if it already exists. Based on looking - at the RCS 5.7 source, it would appear that RCS_CI does this - except when it is creating RCSFILE (reasonable), or when - RCSFILE was created with rcs -i (this is strange, and quite - possibly unintentional). In those two cases it copies the - permissions from the workfile. - - Anyway, the fix is simple enough: we preserve the mode ourself. */ - struct stat sb; - int fix_mode = 1; - int retval; + char *tmpfile1; + char *tmpfile2; + char *use_file2; + int status, retval; + + tmpfile1 = cvs_temp_name (); + tmpfile2 = NULL; + + cvs_output ("\ +===================================================================\n\ +RCS file: ", 0); + cvs_output (rcsfile->path, 0); + cvs_output ("\n", 1); - if (CVS_STAT (rcsfile, &sb) < 0) + /* 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); + status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, + (RCSCHECKOUTPROC)0, NULL); + if (status > 0) + { + retval = status; + goto error_return; + } + else if (status < 0) { - fix_mode = 0; - if (!existence_error (errno)) - error (0, errno, "warning: cannot stat %s", rcsfile); + error (0, errno, + "cannot check out revision %s of %s", rev1, rcsfile->path); + retval = 1; + goto error_return; } - run_setup ("%s%s -x,v/ -w%s -f %s%s", Rcsbin, RCS_CI, getcaller (), - rev ? "-r" : "", rev ? rev : ""); - if (flags & RCS_FLAGS_DEAD) - run_arg ("-sdead"); - if (flags & RCS_FLAGS_QUIET) - run_arg ("-q"); - if (flags & RCS_FLAGS_MODTIME) - run_arg ("-d"); - run_args ("-m%s", make_message_rcslegal (message)); - if (workfile != NULL) - run_arg (workfile); - run_arg (rcsfile); - retval = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); - if (retval == 0 && fix_mode) + + 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_execv (tmpfile1, use_file2, label1, label2, opts, RUN_TTY); + if (status >= 0) { - if (chmod (rcsfile, sb.st_mode) < 0) - error (0, errno, "warning: cannot change permissions on %s", - rcsfile); + retval = status; + goto error_return; } + else if (status < 0) + { + error (0, errno, + "cannot diff %s and %s", tmpfile1, use_file2); + retval = 1; + goto error_return; + } + + error_return: + { + int save_noexec = noexec; + noexec = 0; + if (unlink_file (tmpfile1) < 0) + { + if (!existence_error (errno)) + error (0, errno, "cannot remove temp file %s", tmpfile1); + } + noexec = save_noexec; + } + free (tmpfile1); + if (tmpfile2 != NULL) + { + int save_noexec = noexec; + noexec = 0; + if (unlink_file (tmpfile2) < 0) + { + if (!existence_error (errno)) + error (0, errno, "cannot remove temp file %s", tmpfile2); + } + noexec = save_noexec; + 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 (file1, file2, options, out) + char *file1; + char *file2; + char *options; + char *out; +{ + char *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); + call_diff_arg (file1); + call_diff_arg (file2); + free (args); + + return call_diff (out); +} + +int +diff_execv (file1, file2, label1, label2, options, out) + char *file1; + char *file2; + char *label1; + char *label2; + char *options; + char *out; +{ + char *args = xmalloc (strlen (options) + 10); + /* The first word in this string is used only for error reporting. */ + /* I guess we are pretty confident that options starts with a space. */ + sprintf (args, "diff%s", options); + call_diff_setup (args); + if (label1) + call_diff_arg (label1); + if (label2) + call_diff_arg (label2); + 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 (opts, rev1, rev2, workfile) + char *opts; + char *rev1; + char *rev2; + 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/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 98c7a76e69d..5cecbdfc066 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -22,16 +22,16 @@ #include <winsock.h> #endif -#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) +#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) #include <sys/socket.h> #endif #ifdef HAVE_KERBEROS -#include <netinet/in.h> -#include <krb.h> -#ifndef HAVE_KRB_GET_ERR_TEXT -#define krb_get_err_text(status) krb_err_txt[status] -#endif +# include <netinet/in.h> +# include <krb.h> +# ifndef HAVE_KRB_GET_ERR_TEXT +# define krb_get_err_text(status) krb_err_txt[status] +# endif /* Information we need if we are going to use Kerberos encryption. */ static C_Block kblock; @@ -39,6 +39,34 @@ static Key_schedule sched; #endif +#ifdef HAVE_GSSAPI + +#include <netdb.h> +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_generic.h> + +/* We use Kerberos 5 routines to map the GSSAPI credential to a user + name. */ +#include <krb5.h> + +/* We need this to wrap data. */ +static gss_ctx_id_t gcontext; + +static void gserver_authenticate_connection PROTO((void)); + +/* Whether we are already wrapping GSSAPI communication. */ +static int cvs_gssapi_wrapping; + +# ifdef ENCRYPTION +/* Whether to encrypt GSSAPI communication. We use a global variable + like this because we use the same buffer type (gssapi_wrap) to + handle both authentication and encryption, and we don't want + multiple instances of that buffer in the communication stream. */ +int cvs_gssapi_encrypt; +# endif + +#endif + /* for select */ #include <sys/types.h> #ifdef HAVE_SYS_BSDTYPES_H @@ -76,12 +104,12 @@ static Key_schedule sched; #ifdef HAVE_GETSPNAM #include <shadow.h> #endif +#endif /* AUTH_SERVER_SUPPORT */ + /* For initgroups(). */ #if HAVE_INITGROUPS #include <grp.h> #endif /* HAVE_INITGROUPS */ -#endif /* AUTH_SERVER_SUPPORT */ - #ifdef AUTH_SERVER_SUPPORT @@ -95,6 +123,10 @@ char *CVS_Username = NULL; later CVS protocol. Exported because root.c also uses. */ 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 */ @@ -329,7 +361,7 @@ mkdir_p (dir) { strncpy (q, dir, p - dir); q[p - dir] = '\0'; - if (CVS_MKDIR (q, 0777) < 0) + if (q[p - dir - 1] != '/' && CVS_MKDIR (q, 0777) < 0) { int saved_errno = errno; @@ -437,6 +469,8 @@ alloc_pending (size) return 1; } +static void serve_is_modified PROTO ((char *)); + static int supported_response PROTO ((char *)); static int @@ -526,8 +560,30 @@ serve_root (arg) "E Root %s must be an absolute pathname", arg); return; } + + /* Sending "Root" twice is illegal. It would also be nice to + check for the other case, in which there is no Root request + prior to a request which requires one. + + 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 (CVSroot_directory != NULL) + { + if (alloc_pending (80 + strlen (arg))) + sprintf (pending_error_text, + "E Protocol error: Duplicate Root request, for %s", arg); + return; + } + set_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 (CVSroot_directory); + path = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + sizeof (CVSROOTADM_HISTORY) @@ -630,6 +686,7 @@ dirswitch (dir, repos) int status; FILE *f; char *b; + size_t dir_len; server_write_entries (); @@ -638,20 +695,22 @@ dirswitch (dir, repos) if (dir_name != NULL) free (dir_name); + dir_len = strlen (dir); + /* Check for a trailing '/'. This is not ISDIRSEP because \ in the protocol is an ordinary character, not a directory separator (of course, it is perhaps unwise to use it in directory names, but that is another issue). */ - if (strlen (dir) > 0 - && dir[strlen (dir) - 1] == '/') + if (dir_len > 0 + && dir[dir_len - 1] == '/') { - if (alloc_pending (80 + strlen (dir))) + if (alloc_pending (80 + dir_len)) sprintf (pending_error_text, - "E protocol error: illegal directory syntax in %s", dir); + "E protocol error: invalid directory syntax in %s", dir); return; } - dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40); + dir_name = malloc (strlen (server_temp_dir) + dir_len + 40); if (dir_name == NULL) { pending_error = ENOMEM; @@ -672,6 +731,12 @@ dirswitch (dir, repos) return; } + /* Note that this call to Subdir_Register will be a noop if the parent + directory does not yet exist (for example, if the client sends + "Directory foo" followed by "Directory .", then the subdirectory does + not get registered, but if the client sends "Directory ." followed + by "Directory foo", then the subdirectory does get registered. + This seems pretty fishy, but maybe it is the way it needs to work. */ b = strrchr (dir_name, '/'); *b = '\0'; Subdir_Register ((List *) NULL, dir_name, b + 1); @@ -775,6 +840,24 @@ serve_directory (arg) status = buf_read_line (buf_from_net, &repos, (int *) NULL); if (status == 0) { + /* I think isabsolute (repos) should always be true, and that + any RELATIVE_REPOS stuff should only be in CVS/Repository + files, not the protocol (for compatibility), but I'm putting + in the in isabsolute check just in case. */ + if (isabsolute (repos) + && strncmp (CVSroot_directory, + repos, + strlen (CVSroot_directory)) != 0) + { + if (alloc_pending (strlen (CVSroot_directory) + + strlen (repos) + + 80)) + sprintf (pending_error_text, "\ +E protocol error: directory '%s' not within root '%s'", + repos, CVSroot_directory); + return; + } + dirswitch (arg, repos); free (repos); } @@ -1005,6 +1088,11 @@ receive_file (size, file, gzipped) } } +/* Kopt for the next file sent in Modified or Is-modified. */ +static char *kopt; + +static void serve_modified PROTO ((char *)); + static void serve_modified (arg) char *arg; @@ -1119,6 +1207,13 @@ serve_modified (arg) 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); } @@ -1175,8 +1270,6 @@ serve_unchanged (arg) } } -static void serve_is_modified PROTO ((char *)); - static void serve_is_modified (arg) char *arg; @@ -1213,6 +1306,16 @@ serve_is_modified (arg) } *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; } @@ -1221,22 +1324,36 @@ serve_is_modified (arg) { /* We got Is-modified but no Entry. Add a dummy entry. The "D" timestamp is what makes it a dummy. */ - struct an_entry *p; p = (struct an_entry *) malloc (sizeof (struct an_entry)); if (p == NULL) { pending_error = ENOMEM; return; } - p->entry = xmalloc (strlen (arg) + 80); + p->entry = malloc (strlen (arg) + 80); + if (p->entry == NULL) + { + pending_error = ENOMEM; + free (p); + return; + } strcpy (p->entry, "/"); strcat (p->entry, arg); - strcat (p->entry, "//D//"); + 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 PROTO ((char *)); + static void serve_entry (arg) char *arg; @@ -1263,6 +1380,45 @@ serve_entry (arg) entries = p; } +static void serve_kopt PROTO ((char *)); + +static void +serve_kopt (arg) + 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 = malloc (strlen (arg) + 1); + if (kopt == NULL) + { + pending_error = ENOMEM; + return; + } + strcpy (kopt, arg); +} + static void server_write_entries () { @@ -1610,6 +1766,7 @@ serve_set (arg) } #ifdef ENCRYPTION + #ifdef HAVE_KERBEROS static void @@ -1627,7 +1784,68 @@ serve_kerberos_encrypt (arg) } #endif /* HAVE_KERBEROS */ + +#ifdef HAVE_GSSAPI + +static void +serve_gssapi_encrypt (arg) + 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 (arg) + 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. */ @@ -1815,10 +2033,18 @@ check_command_legal_p (cmd_name) CVSROOTADM, CVSROOTADM_READERS); fp = fopen (fname, "r"); - free (fname); if (fp == NULL) - goto do_writers; + { + 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) @@ -1836,16 +2062,19 @@ check_command_legal_p (cmd_name) if (strcmp (linebuf, CVS_Username) == 0) goto handle_illegal; } + 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 . */ - fclose (fp); - goto do_writers; + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); } + free (fname); + + /* Now check the writers file. */ - do_writers: - flen = strlen (CVSroot_directory) + strlen (CVSROOTADM) + strlen (CVSROOTADM_WRITERS) @@ -1856,19 +2085,28 @@ check_command_legal_p (cmd_name) CVSROOTADM, CVSROOTADM_WRITERS); fp = fopen (fname, "r"); - free (fname); if (fp == NULL) { - /* writers file does not exist, so everyone is a writer, - by default */ if (linebuf) free (linebuf); - return 1; + 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; + } } - /* else */ - found_it = 0; while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) { @@ -1882,20 +2120,26 @@ check_command_legal_p (cmd_name) break; } } + if (num_red < 0 && !feof (fp)) + error (0, errno, "cannot read %s", fname); if (found_it) { - fclose (fp); + 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_illegal: - fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); if (linebuf) free (linebuf); + free (fname); return 0; } } @@ -2814,7 +3058,7 @@ static void serve_log (arg) char *arg; { - do_cvs_command ("cvslog", cvslog); + do_cvs_command ("log", cvslog); } static void @@ -3086,6 +3330,8 @@ server_modtime (finfo, vers_ts) {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + assert (vers_ts->vn_rcs != NULL); + if (!supported_response ("Mod-time")) return; @@ -3118,7 +3364,18 @@ server_updated (finfo, vers, updated, file_info, checksum) unsigned char *checksum; { 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) { @@ -3524,6 +3781,38 @@ serve_gzip_stream (arg) buf_from_net->memory_error); } +/* Tell the client about RCS options set in CVSROOT/cvswrappers. */ +static void +serve_wrapper_sendme_rcs_options (arg) + 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 (arg) char *arg; @@ -3560,6 +3849,11 @@ expand_proc (pargc, argv, where, mwhere, mfile, shorten, 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) { @@ -3575,6 +3869,11 @@ expand_proc (pargc, argv, where, mwhere, mfile, shorten, if (*pargc == 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'); } @@ -3583,6 +3882,11 @@ expand_proc (pargc, argv, where, mwhere, mfile, shorten, for (i = 1; i < *pargc; ++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]); @@ -3761,6 +4065,7 @@ struct request requests[] = REQ_LINE("Checkin-prog", serve_checkin_prog, rq_optional), REQ_LINE("Update-prog", serve_update_prog, rq_optional), REQ_LINE("Entry", serve_entry, rq_essential), + REQ_LINE("Kopt", serve_kopt, rq_optional), REQ_LINE("Modified", serve_modified, rq_essential), REQ_LINE("Is-modified", serve_is_modified, rq_optional), @@ -3777,11 +4082,20 @@ struct request requests[] = REQ_LINE("Argumentx", serve_argumentx, rq_essential), REQ_LINE("Global_option", serve_global_option, rq_optional), REQ_LINE("Gzip-stream", serve_gzip_stream, rq_optional), + REQ_LINE("wrapper-sendme-rcsOptions", + serve_wrapper_sendme_rcs_options, + rq_optional), REQ_LINE("Set", serve_set, rq_optional), #ifdef ENCRYPTION -#ifdef HAVE_KERBEROS +# ifdef HAVE_KERBEROS REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, rq_optional), +# endif +# ifdef HAVE_GSSAPI + REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, rq_optional), +# endif #endif +#ifdef HAVE_GSSAPI + REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, rq_optional), #endif REQ_LINE("expand-modules", serve_expand_modules, rq_optional), REQ_LINE("ci", serve_ci, rq_essential), @@ -4028,50 +4342,6 @@ server (argc, argv) protocol to report error messages. */ error_use_protocol = 1; - /* - * Put Rcsbin at the start of PATH, so that rcs programs can find - * themselves. - */ -#ifdef HAVE_PUTENV - if (Rcsbin != NULL && *Rcsbin) - { - char *p; - char *env; - - p = getenv ("PATH"); - if (p != NULL) - { - env = malloc (strlen (Rcsbin) + strlen (p) + sizeof "PATH=:"); - if (env != NULL) - sprintf (env, "PATH=%s:%s", Rcsbin, p); - } - else - { - env = malloc (strlen (Rcsbin) + sizeof "PATH="); - if (env != NULL) - sprintf (env, "PATH=%s", Rcsbin); - } - if (env == NULL) - { - printf ("E Fatal server error, aborting.\n\ -error ENOMEM Virtual memory exhausted.\n"); - - /* I'm doing this manually rather than via error_exit () - because I'm not sure whether we want to call server_cleanup. - Needs more investigation.... */ - -#ifdef SYSTEM_CLEANUP - /* Hook for OS-specific behavior, for example socket subsystems on - NT and OS2 or dealing with windows and arguments on Mac. */ - SYSTEM_CLEANUP (); -#endif - - exit (EXIT_FAILURE); - } - putenv (env); - } -#endif - /* OK, now figure out where we stash our temporary files. */ { char *p; @@ -4224,8 +4494,6 @@ error ENOMEM Virtual memory exhausted.\n"); by this server" or something like that instead of usage message. */ argument_vector[0] = "cvs server"; - server_active = 1; - while (1) { char *cmd, *orig_cmd; @@ -4278,7 +4546,7 @@ error ENOMEM Virtual memory exhausted.\n"); } -#if defined (HAVE_KERBEROS) || defined (AUTH_SERVER_SUPPORT) +#if defined (HAVE_KERBEROS) || defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI) static void switch_to_user PROTO((const char *)); static void @@ -4305,6 +4573,15 @@ error 0 %s: no such user\n", username); exit (EXIT_FAILURE); } + /* FIXME? We don't check for errors from initgroups, setuid, &c. + I think this mainly would come up if someone is trying to run + the server as a non-root user. I think we should be checking for + errors and aborting (as with the error above from getpwnam) if + there is an error (presumably EPERM). That means that pserver + should continue to work right if all of the "system usernames" + in CVSROOT/passwd match the user which the server is being run + as (in inetd.conf), but fail otherwise. */ + #if HAVE_INITGROUPS initgroups (pw->pw_name, pw->pw_gid); #endif /* HAVE_INITGROUPS */ @@ -4475,7 +4752,7 @@ check_password (username, password, repository) /* host_user already set by reference, so just return. */ goto handle_return; } - else if (rc == 0) + else if (rc == 0 && system_auth) { /* No cvs password found, so try /etc/passwd. */ @@ -4534,6 +4811,27 @@ error 0 %s: no such user\n", username); goto handle_return; } } + else if (rc == 0) + { + /* 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); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); + } else { /* Something strange happened. We don't know what it was, but @@ -4554,6 +4852,10 @@ handle_return: 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. */ @@ -4562,6 +4864,7 @@ pserver_authenticate_connection () { char *tmp = NULL; size_t tmp_allocated = 0; +#ifdef AUTH_SERVER_SUPPORT char *repository = NULL; size_t repository_allocated = 0; char *username = NULL; @@ -4571,6 +4874,7 @@ pserver_authenticate_connection () char *host_user; char *descrambled_password; +#endif /* AUTH_SERVER_SUPPORT */ int verify_and_exit = 0; /* The Authentication Protocol. Client sends: @@ -4635,9 +4939,27 @@ pserver_authenticate_connection () 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 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. */ getline (&repository, &repository_allocated, stdin); @@ -4660,14 +4982,21 @@ pserver_authenticate_connection () error (1, 0, "bad auth protocol end: %s", tmp); } if (!root_allow_ok (repository)) - /* At least for the moment I'm going to do the paranoid - security thing and not tell them how it failed. I'm not - sure that is a good idea; it is a real pain when one needs - to track down what is going on for legitimate reasons. - The other issue is that the protocol doesn't really have - a good way for anything other than I HATE YOU. */ + /* Just give a generic I HATE YOU. This is because CVS 1.9.10 + and older clients do not support "error". Once more recent + clients are more widespread, probably want to fix this (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). */ 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); @@ -4719,9 +5048,11 @@ pserver_authenticate_connection () free (repository); free (username); free (password); -} #endif /* AUTH_SERVER_SUPPORT */ +} + +#endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */ #ifdef HAVE_KERBEROS @@ -4801,6 +5132,122 @@ error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); } #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 () +{ + char hostname[MAXHOSTNAMELEN]; + struct hostent *hp; + gss_buffer_desc tok_in, tok_out; + char buf[1024]; + 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_nt_service_name, + &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); + assert (nbytes <= sizeof buf); + + if (fread (buf, 1, nbytes, stdin) != nbytes) + error (1, errno, "read of data failed"); + + gcontext = GSS_C_NO_CONTEXT; + tok_in.length = nbytes; + tok_in.value = buf; + + 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 (buf); + + printf ("I LOVE YOU\n"); + fflush (stdout); +} + +#endif /* HAVE_GSSAPI */ + #endif /* SERVER_SUPPORT */ #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) @@ -4809,320 +5256,232 @@ error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); the command line. */ int cvsencrypt; -#ifdef ENCRYPTION +/* This global variable is non-zero if the users requests stream + authentication on the command line. */ +int cvsauthenticate; -#ifdef HAVE_KERBEROS +#ifdef HAVE_GSSAPI -/* An encryption interface using Kerberos. This is built on top of - the buffer structure. We encrypt using a big endian two byte count - field followed by a block of encrypted data. */ +/* An buffer interface using GSSAPI. This is built on top of a + packetizing buffer. */ -/* This structure is the closure field of a Kerberos encryption - buffer. */ +/* This structure is the closure field of the GSSAPI translation + routines. */ -struct krb_encrypt_buffer +struct cvs_gssapi_wrap_data { - /* The underlying buffer. */ - struct buffer *buf; - /* The Kerberos key schedule. */ - Key_schedule sched; - /* The Kerberos DES block. */ - C_Block block; - /* For an input buffer, we may have to buffer up data here. */ - /* This is non-zero if the buffered data is decrypted. Otherwise, - the buffered data is encrypted, and starts with the two byte - count. */ - int clear; - /* The amount of buffered data. */ - int holdsize; - /* The buffer allocated to hold the data. */ - char *holdbuf; - /* The size of holdbuf. */ - int holdbufsize; - /* If clear is set, we need another data pointer to track where we - are in holdbuf. If clear is zero, then this pointer is not - used. */ - char *holddata; + /* The GSSAPI context. */ + gss_ctx_id_t gcontext; }; -static int krb_encrypt_buffer_input PROTO((void *, char *, int, int, int *)); -static int krb_encrypt_buffer_output PROTO((void *, const char *, int, int *)); -static int krb_encrypt_buffer_flush PROTO((void *)); -static int krb_encrypt_buffer_block PROTO((void *, int)); -static int krb_encrypt_buffer_shutdown PROTO((void *)); +static int cvs_gssapi_wrap_input PROTO((void *, const char *, char *, int)); +static int cvs_gssapi_wrap_output PROTO((void *, const char *, char *, int, + int *)); -/* Create an encryption buffer. */ +/* Create a GSSAPI wrapping buffer. We use a packetizing buffer with + GSSAPI wrapping routines. */ struct buffer * -krb_encrypt_buffer_initialize (buf, input, sched, block, memory) +cvs_gssapi_wrap_buffer_initialize (buf, input, gcontext, memory) struct buffer *buf; int input; - Key_schedule sched; - C_Block block; + gss_ctx_id_t gcontext; void (*memory) PROTO((struct buffer *)); { - struct krb_encrypt_buffer *kb; + struct cvs_gssapi_wrap_data *gd; - kb = (struct krb_encrypt_buffer *) xmalloc (sizeof *kb); - memset (kb, 0, sizeof *kb); - - kb->buf = buf; - memcpy (kb->sched, sched, sizeof (Key_schedule)); - memcpy (kb->block, block, sizeof (C_Block)); - if (input) - { - /* We add some space to the buffer to hold the length. */ - kb->holdbufsize = BUFFER_DATA_SIZE + 16; - kb->holdbuf = xmalloc (kb->holdbufsize); - } + gd = (struct cvs_gssapi_wrap_data *) xmalloc (sizeof *gd); + gd->gcontext = gcontext; - return buf_initialize (input ? krb_encrypt_buffer_input : NULL, - input ? NULL : krb_encrypt_buffer_output, - input ? NULL : krb_encrypt_buffer_flush, - krb_encrypt_buffer_block, - krb_encrypt_buffer_shutdown, - memory, - kb); + return (packetizing_buffer_initialize + (buf, + input ? cvs_gssapi_wrap_input : NULL, + input ? NULL : cvs_gssapi_wrap_output, + gd, + memory)); } -/* Input data from a Kerberos encryption buffer. */ +/* Unwrap data using GSSAPI. */ static int -krb_encrypt_buffer_input (closure, data, need, size, got) - void *closure; - char *data; - int need; +cvs_gssapi_wrap_input (fnclosure, input, output, size) + void *fnclosure; + const char *input; + char *output; int size; - int *got; { - struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; + struct cvs_gssapi_wrap_data *gd = + (struct cvs_gssapi_wrap_data *) fnclosure; + gss_buffer_desc inbuf, outbuf; + OM_uint32 stat_min; + int conf; - *got = 0; + inbuf.value = (void *) input; + inbuf.length = size; - if (kb->holdsize > 0 && kb->clear) + if (gss_unwrap (&stat_min, gd->gcontext, &inbuf, &outbuf, &conf, NULL) + != GSS_S_COMPLETE) { - int copy; - - copy = kb->holdsize; - - if (copy > size) - { - memcpy (data, kb->holddata, size); - kb->holdsize -= size; - kb->holddata += size; - *got = size; - return 0; - } - - memcpy (data, kb->holddata, copy); - kb->holdsize = 0; - kb->clear = 0; - - data += copy; - need -= copy; - size -= copy; - *got = copy; + error (1, 0, "gss_unwrap failed"); } - while (need > 0 || *got == 0) - { - int get, status, nread, count, dcount; - char *bytes; - char stackoutbuf[BUFFER_DATA_SIZE + 16]; - char *outbuf; + if (outbuf.length > size) + abort (); - /* If we don't already have the two byte count, get it. */ - if (kb->holdsize < 2) - { - get = 2 - kb->holdsize; - status = buf_read_data (kb->buf, get, &bytes, &nread); - if (status != 0) - { - /* buf_read_data can return -2, but a buffer input - function is only supposed to return -1, 0, or an - error code. */ - if (status == -2) - status = ENOMEM; - return status; - } + memcpy (output, outbuf.value, outbuf.length); - if (nread == 0) - { - /* The buffer is in nonblocking mode, and we didn't - manage to read anything. */ - return 0; - } + /* The real packet size is stored in the data, so we don't need to + remember outbuf.length. */ - if (get == 1) - kb->holdbuf[1] = bytes[0]; - else - { - kb->holdbuf[0] = bytes[0]; - if (nread < 2) - { - /* We only got one byte, but we needed two. Stash - the byte we got, and try again. */ - kb->holdsize = 1; - continue; - } - kb->holdbuf[1] = bytes[1]; - } - kb->holdsize = 2; - } + gss_release_buffer (&stat_min, &outbuf); - /* Read the encrypted block of data. */ + return 0; +} - count = (((kb->holdbuf[0] & 0xff) << 8) - + (kb->holdbuf[1] & 0xff)); +/* Wrap data using GSSAPI. */ - if (count + 2 > kb->holdbufsize) - { - char *n; +static int +cvs_gssapi_wrap_output (fnclosure, input, output, size, translated) + void *fnclosure; + const char *input; + char *output; + int size; + int *translated; +{ + struct cvs_gssapi_wrap_data *gd = + (struct cvs_gssapi_wrap_data *) fnclosure; + gss_buffer_desc inbuf, outbuf; + OM_uint32 stat_min; + int conf_req, conf; - /* This should be impossible, since we should have - allocated space for the largest possible block in the - initialize function. However, we handle it just in - case something changes in the future, so that a current - server can handle a later client. */ + inbuf.value = (void *) input; + inbuf.length = size; - n = realloc (kb->holdbuf, count + 2); - if (n == NULL) - { - (*kb->buf->memory_error) (kb->buf); - return ENOMEM; - } - kb->holdbuf = n; - kb->holdbufsize = count + 2; - } +#ifdef ENCRYPTION + conf_req = cvs_gssapi_encrypt; +#else + conf_req = 0; +#endif - get = count - (kb->holdsize - 2); + if (gss_wrap (&stat_min, gd->gcontext, conf_req, GSS_C_QOP_DEFAULT, + &inbuf, &conf, &outbuf) != GSS_S_COMPLETE) + error (1, 0, "gss_wrap failed"); - status = buf_read_data (kb->buf, get, &bytes, &nread); - if (status != 0) - { - /* buf_read_data can return -2, but a buffer input - function is only supposed to return -1, 0, or an error - code. */ - if (status == -2) - status = ENOMEM; - return status; - } + /* The packetizing buffer only permits us to add 100 bytes. + FIXME: I don't know what, if anything, is guaranteed by GSSAPI. + This may need to be increased for a different GSSAPI + implementation, or we may need a different algorithm. */ + if (outbuf.length > size + 100) + abort (); - if (nread == 0) - { - /* We did not get any data. Presumably the buffer is in - nonblocking mode. */ - return 0; - } + memcpy (output, outbuf.value, outbuf.length); - /* FIXME: We could complicate the code here to avoid this - memcpy in the common case of kb->holdsize == 2 && nread == - get. */ - memcpy (kb->holdbuf + kb->holdsize, bytes, nread); - kb->holdsize += nread; + *translated = outbuf.length; - if (nread < get) - { - /* We did not get all the data we need. buf_read_data - does not promise to return all the bytes requested, so - we must try again. */ - continue; - } + gss_release_buffer (&stat_min, &outbuf); - /* We have a complete encrypted block of COUNT bytes at - KB->HOLDBUF + 2. Decrypt it. */ + return 0; +} - if (count <= sizeof stackoutbuf) - outbuf = stackoutbuf; - else - { - /* I believe this is currently impossible, but we handle - it for the benefit of future client changes. */ - outbuf = malloc (count); - if (outbuf == NULL) - { - (*kb->buf->memory_error) (kb->buf); - return ENOMEM; - } - } +#endif /* HAVE_GSSAPI */ - des_cbc_encrypt ((C_Block *) (kb->holdbuf + 2), (C_Block *) outbuf, - count, kb->sched, &kb->block, 0); +#ifdef ENCRYPTION - /* The first two bytes in the decrypted buffer are the real - (unaligned) length. */ - dcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff); +#ifdef HAVE_KERBEROS - if (((dcount + 2 + 7) & ~7) != count) - error (1, 0, "Decryption failure"); +/* An encryption interface using Kerberos. This is built on top of a + packetizing buffer. */ - if (dcount > size) - { - /* We have too much data for the buffer. We need to save - some of it for the next call. */ +/* This structure is the closure field of the Kerberos translation + routines. */ - memcpy (data, outbuf + 2, size); - *got += size; +struct krb_encrypt_data +{ + /* The Kerberos key schedule. */ + Key_schedule sched; + /* The Kerberos DES block. */ + C_Block block; +}; - kb->holdsize = dcount - size; - memcpy (kb->holdbuf, outbuf + 2 + size, dcount - size); - kb->holddata = kb->holdbuf; - kb->clear = 1; +static int krb_encrypt_input PROTO((void *, const char *, char *, int)); +static int krb_encrypt_output PROTO((void *, const char *, char *, int, + int *)); - if (outbuf != stackoutbuf) - free (outbuf); +/* Create a Kerberos encryption buffer. We use a packetizing buffer + with Kerberos encryption translation routines. */ - return 0; - } +struct buffer * +krb_encrypt_buffer_initialize (buf, input, sched, block, memory) + struct buffer *buf; + int input; + Key_schedule sched; + C_Block block; + void (*memory) PROTO((struct buffer *)); +{ + struct krb_encrypt_data *kd; - memcpy (data, outbuf + 2, dcount); + kd = (struct krb_encrypt_data *) xmalloc (sizeof *kd); + memcpy (kd->sched, sched, sizeof (Key_schedule)); + memcpy (kd->block, block, sizeof (C_Block)); - if (outbuf != stackoutbuf) - free (outbuf); + return packetizing_buffer_initialize (buf, + input ? krb_encrypt_input : NULL, + input ? NULL : krb_encrypt_output, + kd, + memory); +} - kb->holdsize = 0; +/* Decrypt Kerberos data. */ - data += dcount; - need -= dcount; - size -= dcount; - *got += dcount; - } +static int +krb_encrypt_input (fnclosure, input, output, size) + 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; } -/* Output data to a Kerberos encryption buffer. */ +/* Encrypt Kerberos data. */ static int -krb_encrypt_buffer_output (closure, data, have, wrote) - void *closure; - const char *data; - int have; - int *wrote; +krb_encrypt_output (fnclosure, input, output, size, translated) + void *fnclosure; + const char *input; + char *output; + int size; + int *translated; { - struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; - char inbuf[BUFFER_DATA_SIZE + 16]; - char outbuf[BUFFER_DATA_SIZE + 16]; + struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure; int aligned; - if (have > BUFFER_DATA_SIZE) - { - /* It would be easy to malloc a buffer, but I don't think this - case can ever arise. */ - abort (); - } - - inbuf[0] = (have >> 8) & 0xff; - inbuf[1] = have & 0xff; - memcpy (inbuf + 2, data, have); - /* For security against a known plaintext attack, we should initialize any padding bytes to random values. Instead, we just pick up whatever is on the stack, which is at least better than using zero. */ - /* Align (have + 2) (plus 2 for the count) to an 8 byte boundary. */ - aligned = (have + 2 + 7) & ~7; + /* 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 @@ -5131,65 +5490,12 @@ krb_encrypt_buffer_output (closure, data, have, wrote) fail over a long network connection. We trust krb_recvauth to guard against a replay attack. */ - des_cbc_encrypt ((C_Block *) inbuf, (C_Block *) (outbuf + 2), aligned, - kb->sched, &kb->block, 1); - - outbuf[0] = (aligned >> 8) & 0xff; - outbuf[1] = aligned & 0xff; - - /* FIXME: It would be more efficient to get des_cbc_encrypt to put - its output directly into a buffer_data structure, which we - could then append to kb->buf. That would save a memcpy. */ - - buf_output (kb->buf, outbuf, aligned + 2); - - *wrote = have; - - /* We will only be here because buf_send_output was called on the - encryption buffer. That means that we should now call - buf_send_output on the underlying buffer. */ - return buf_send_output (kb->buf); -} + des_cbc_encrypt ((C_Block *) input, (C_Block *) output, aligned, + kd->sched, &kd->block, 1); -/* Flush data to a Kerberos encryption buffer. */ - -static int -krb_encrypt_buffer_flush (closure) - void *closure; -{ - struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; - - /* Flush the underlying buffer. Note that if the original call to - buf_flush passed 1 for the BLOCK argument, then the buffer will - already have been set into blocking mode, so we should always - pass 0 here. */ - return buf_flush (kb->buf, 0); -} + *translated = aligned; -/* The block routine for a Kerberos encryption buffer. */ - -static int -krb_encrypt_buffer_block (closure, block) - void *closure; - int block; -{ - struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; - - if (block) - return set_block (kb->buf); - else - return set_nonblock (kb->buf); -} - -/* Shut down a Kerberos encryption buffer. */ - -static int -krb_encrypt_buffer_shutdown (closure) - void *closure; -{ - struct krb_encrypt_buffer *kb = (struct krb_encrypt_buffer *) closure; - - return buf_shutdown (kb->buf); + return 0; } #endif /* HAVE_KERBEROS */ @@ -5225,6 +5531,92 @@ cvs_output (str, len) 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 (str, len) + 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 if (server_active) + buf = protocol; + + 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), _O_BINARY); + if (oldmode < 0) + error (0, errno, "failed to setmode on stdout"); +#endif + while (to_write > 0) { written = fwrite (p, 1, to_write, stdout); @@ -5233,6 +5625,11 @@ cvs_output (str, len) p += written; to_write -= written; } +#ifdef USE_SETMODE_STDOUT + fflush (stdout); + if (_setmode (_fileno (stdout), oldmode) != _O_BINARY) + error (0, errno, "failed to setmode on stdout"); +#endif } } @@ -5329,3 +5726,62 @@ cvs_flushout () #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 (tag, text) + char *tag; + 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 + { + if (strcmp (tag, "newline") == 0) + cvs_output ("\n", 1); + else if (text != NULL) + cvs_output (text, 0); + } +} diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c index 82886b7a3e9..0563eb346ad 100644 --- a/gnu/usr.bin/cvs/src/update.c +++ b/gnu/usr.bin/cvs/src/update.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * 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 @@ -64,12 +64,8 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *)); static int update_filesdone_proc PROTO ((void *callerdat, int err, char *repository, char *update_dir, List *entries)); -static int write_letter PROTO((char *file, int letter, char *update_dir)); -#ifdef SERVER_SUPPORT -static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); -#else +static void write_letter PROTO ((struct file_info *finfo, int letter)); static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); -#endif static char *options = NULL; static char *tag = NULL; @@ -113,6 +109,7 @@ static const char *const update_usage[] = "\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 }; @@ -259,16 +256,18 @@ update (argc, argv) /* 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. */ + 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 (failed_patches == NULL) { -#ifndef DONT_USE_PATCH - /* Systems which don't have the patch program ported to them - will want to define DONT_USE_PATCH; then CVS won't try to - invoke patch. */ if (supported_request ("update-patches")) send_arg ("-u"); -#endif } if (failed_patches == NULL) @@ -548,44 +547,17 @@ update_fileproc (callerdat, finfo) break; case T_CONFLICT: /* old punt-type errors */ retval = 1; - (void) write_letter (finfo->file, 'C', finfo->update_dir); + write_letter (finfo, 'C'); break; case T_NEEDS_MERGE: /* needs merging */ if (noexec) { retval = 1; - (void) write_letter (finfo->file, 'C', finfo->update_dir); + write_letter (finfo, 'C'); } else { - if (wrap_merge_is_copy (finfo->file)) -#if 0 - /* Look, we can't clobber the user's file. We - know it is modified and we're going to - overwrite their mod? Puh-leeze. The - correct behavior is probably something like - what merge_file does for -kb, which is to - give the users both files and tell them - what the two filenames are. Of course, -m - in wrappers needs to be documented *much* - better. Anyway, until then, make this a - fatal error. */ - - /* Should we be warning the user that we are - * overwriting the user's copy of the file? */ - retval = - checkout_file (finfo, vers, 0); -#else - { - error (0, 0, "A -m 'COPY' wrapper is specified"); - error (0, 0, "but file %s needs merge", - finfo->fullname); - error (1, 0, "\ -You probably want to avoid -m 'COPY' wrappers"); -#endif - } - else - retval = merge_file (finfo, vers); + retval = merge_file (finfo, vers); } break; case T_MODIFIED: /* locally modified */ @@ -622,7 +594,7 @@ You probably want to avoid -m 'COPY' wrappers"); if (!retcode) { - (void) write_letter (finfo->file, 'C', finfo->update_dir); + write_letter (finfo, 'C'); retval = 1; } else @@ -634,7 +606,10 @@ You probably want to avoid -m 'COPY' wrappers"); } } if (!retval) - retval = write_letter (finfo->file, 'M', finfo->update_dir); + { + write_letter (finfo, 'M'); + retval = 0; + } break; #ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ @@ -658,10 +633,9 @@ You probably want to avoid -m 'COPY' wrappers"); break; } } - /* Fall through. */ /* If we're not running as a server, just check the - file out. It's simpler and faster than starting up - two new processes (diff and patch). */ + file out. It's simpler and faster than producing + and applying patches. */ /* Fall through. */ #endif case T_CHECKOUT: /* needs checkout */ @@ -674,10 +648,12 @@ You probably want to avoid -m 'COPY' wrappers"); #endif break; case T_ADDED: /* added but not committed */ - retval = write_letter (finfo->file, 'A', finfo->update_dir); + write_letter (finfo, 'A'); + retval = 0; break; case T_REMOVED: /* removed but not committed */ - retval = write_letter (finfo->file, 'R', finfo->update_dir); + write_letter (finfo, 'R'); + retval = 0; break; case T_REMOVE_ENTRY: /* needs to be un-registered */ retval = scratch_file (finfo); @@ -727,7 +703,23 @@ update_ignproc (file, dir) char *file; char *dir; { - (void) write_letter (file, '?', dir); + struct file_info finfo; + + memset (&finfo, 0, sizeof (finfo)); + finfo.file = file; + finfo.update_dir = dir; + if (dir[0] == '\0') + finfo.fullname = xstrdup (file); + else + { + finfo.fullname = xmalloc (strlen (file) + strlen (dir) + 10); + strcpy (finfo.fullname, dir); + strcat (finfo.fullname, "/"); + strcat (finfo.fullname, file); + } + + write_letter (&finfo, '?'); + free (finfo.fullname); } /* ARGSUSED */ @@ -808,7 +800,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) if (noexec) { /* ignore the missing dir if -n is specified */ - error (0, 0, "New directory `%s' -- ignored", dir); + error (0, 0, "New directory `%s' -- ignored", update_dir); return (R_SKIP_ALL); } else @@ -818,6 +810,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) Create_Admin (dir, update_dir, repository, tag, date, /* This is a guess. We will rewrite it later via WriteTag. */ + 0, 0); rewrite_tag = 1; nonbranch = 0; @@ -920,7 +913,8 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) { if ((cp = strrchr (line, '\n')) != NULL) *cp = '\0'; - run_setup ("%s %s", line, repository); + run_setup (line); + run_arg (repository); cvs_output (program_name, 0); cvs_output (" ", 1); cvs_output (command_name, 0); @@ -1080,6 +1074,9 @@ checkout_file (finfo, vers_ts, adding) int status; int file_is_dead; + /* Solely to suppress a warning from gcc -Wall. */ + backup = NULL; + /* don't screw with backup files if we're going to stdout */ if (!pipeout) { @@ -1093,7 +1090,13 @@ checkout_file (finfo, vers_ts, adding) else /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ - (void) unlink_file_dir (backup); + 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); + } } file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); @@ -1133,7 +1136,7 @@ VERS: ", 0); { Vers_TS *xvers_ts; - if (cvswrite == TRUE + if (cvswrite && !file_is_dead && !fileattr_get (finfo->file, "_watched")) xchmod (finfo->file, 1); @@ -1155,13 +1158,11 @@ VERS: ", 0); } /* set the time from the RCS file iff it was unknown before */ - if (vers_ts->vn_user == NULL || - strncmp (vers_ts->ts_rcs, "Initial", 7) == 0) - { - set_time = 1; - } - else - set_time = 0; + 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); @@ -1222,7 +1223,7 @@ VERS: ", 0); if (!really_quiet && !file_is_dead) { - write_letter (finfo->file, 'U', finfo->update_dir); + write_letter (finfo, 'U'); } } } @@ -1243,7 +1244,13 @@ VERS: ", 0); { /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ - (void) unlink_file_dir (backup); + 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); } @@ -1307,6 +1314,27 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) 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); + } + backup = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + sizeof (CVSPREFIX) @@ -1383,7 +1411,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) retcode = 0; if (! fail) { - const char *diff_options; + char *diff_options; /* FIXME: It might be better to come up with a diff library which can be shared with the diffutils. */ @@ -1401,21 +1429,22 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) } else { - /* FIXME: We should use -a if diff supports it. We should - probably just copy over most or all of the diff - handling in the RCS configure script. */ - /* IMHO, we shouldn't copy over anything which even - vaguely resembles the RCS configure script. That kind of - thing tends to be ugly, slow, and fragile. It also is a - a support headache for CVS to behave differently in subtle - ways based on whether it was installed correctly. Instead we - should come up with a diff library. -kingdon, Apr 1997. */ + /* 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"; } - run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2); + retcode = diff_exec (file1, file2, diff_options, finfo->file); /* A retcode of 0 means no differences. 1 means some differences. */ - if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0 + if (retcode != 0 && retcode != 1) { fail = 1; @@ -1435,7 +1464,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) error (0, errno, "cannot change mode of file %s", finfo->file); - if (cvswrite == TRUE + if (cvswrite && !fileattr_get (finfo->file, "_watched")) xchmod (finfo->file, 1); @@ -1493,7 +1522,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) if (!really_quiet) { - write_letter (finfo->file, 'P', finfo->update_dir); + write_letter (finfo, 'P'); } } else @@ -1539,7 +1568,7 @@ patch_file_write (callerdat, buffer, len) data->final_nl = (buffer[len - 1] == '\n'); if (data->compute_checksum) - MD5Update (&data->context, buffer, len); + MD5Update (&data->context, (unsigned char *) buffer, len); } #endif /* SERVER_SUPPORT */ @@ -1548,27 +1577,45 @@ patch_file_write (callerdat, buffer, len) * Several of the types we process only print a bit of information consisting * of a single letter and the name. */ -static int -write_letter (file, letter, update_dir) - char *file; +static void +write_letter (finfo, letter) + struct file_info *finfo; int letter; - char *update_dir; { if (!really_quiet) { - char buf[2]; + 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] = ' '; - cvs_output (buf, 2); - if (update_dir[0]) + buf[2] = '\0'; + cvs_output_tagged ("text", buf); + cvs_output_tagged ("fname", finfo->fullname); + cvs_output_tagged ("newline", NULL); + if (tag != NULL) { - cvs_output (update_dir, 0); - cvs_output ("/", 1); + sprintf (buf, "-%s", tag); + cvs_output_tagged (buf, NULL); } - cvs_output (file, 0); - cvs_output ("\n", 1); } - return (0); + return; } /* @@ -1601,7 +1648,8 @@ merge_file (finfo, vers) copy_file (finfo->file, backup); xchmod (finfo->file, 1); - if (strcmp (vers->options, "-kb") == 0) + if (strcmp (vers->options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file)) { /* For binary files, a merge is always a conflict. We give the user the two files, and let them resolve it. It is possible @@ -1620,11 +1668,15 @@ merge_file (finfo, vers) (struct stat *) NULL, (unsigned char *) NULL); } #endif - error (0, 0, "binary file needs merge"); + /* 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->file, 'C', finfo->update_dir); + write_letter (finfo, 'C'); history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository); @@ -1632,7 +1684,7 @@ merge_file (finfo, vers) goto out; } - status = RCS_merge(vers->srcfile->path, + status = RCS_merge(finfo->rcs, vers->srcfile->path, finfo->file, vers->options, vers->vn_user, vers->vn_rcs); if (status != 0 && status != 1) { @@ -1695,7 +1747,7 @@ merge_file (finfo, vers) if (!noexec) error (0, 0, "conflicts found in %s", finfo->fullname); - write_letter (finfo->file, 'C', finfo->update_dir); + write_letter (finfo, 'C'); history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository); @@ -1707,7 +1759,7 @@ merge_file (finfo, vers) } else { - write_letter (finfo->file, 'M', finfo->update_dir); + write_letter (finfo, 'M'); history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository); } @@ -1742,14 +1794,6 @@ join_file (finfo, vers) jdate1 = date_rev1; jdate2 = date_rev2; - if (wrap_merge_is_copy (finfo->file)) - { - error (0, 0, - "Cannot merge %s because it is a merge-by-copy file.", - finfo->fullname); - return; - } - /* Determine if we need to do anything at all. */ if (vers->srcfile == NULL || vers->srcfile->path == NULL) @@ -2045,7 +2089,7 @@ join_file (finfo, vers) "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 @@ -2064,14 +2108,96 @@ join_file (finfo, vers) xchmod (finfo->file, 1); options = vers->options; -#ifdef HAVE_RCS5 #if 0 if (*options == '\0') options = "-kk"; /* to ignore keyword expansions */ #endif -#endif - status = RCS_merge (vers->srcfile->path, options, rev1, rev2); + /* 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 + + /* This is because of the worry below about $Name. If that + isn't a problem, I suspect this code probably works for + text files too. */ + && (strcmp (options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file))) + { + /* FIXME: what about nametag? What does RCS_merge do with + $Name? */ + if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, 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 (options, "-kb") == 0 + || wrap_merge_is_copy (finfo->file)) + { + /* We are dealing with binary files, but 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, NULL, 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, + options, rev1, rev2); + if (status != 0 && status != 1) { error (0, status == -1 ? errno : 0, |