diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 2001-02-10 18:58:00 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 2001-02-10 18:58:00 +0000 |
commit | 7669735f21a4da98b45ab261e6ab994716409388 (patch) | |
tree | 414fc50df1e6ea1c162ecde77690b88a14a9bd60 /gnu | |
parent | 9fe669e78968736544dc01ad2f8124de021887ae (diff) |
Latest from Cyclic Software
Diffstat (limited to 'gnu')
47 files changed, 6904 insertions, 3191 deletions
diff --git a/gnu/usr.bin/cvs/ChangeLog b/gnu/usr.bin/cvs/ChangeLog index 8a97c2f60bd..eaa0aea155c 100644 --- a/gnu/usr.bin/cvs/ChangeLog +++ b/gnu/usr.bin/cvs/ChangeLog @@ -1,3 +1,103 @@ +2000-09-07 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@ + from autoconf. + + * acconfig.h: Copy HAVE_CRYPT, HAVE_GETSPNAM, REGEX_MALLOC, and + _REGEX_RE_COMP from config.h.in to here, where they should have + been in the first place. + * config.h.in: Regenerated. + +2000-08-30 Larry Jones <larry.jones@sdrc.com> + + * NEWS: Note additional history enhancements. + +2000-08-01 Larry Jones <larry.jones@sdrc.com> + + * configure.in, config.h.in: Add check for getpassphrase (Solaris). + * configure: Regenerated. + +2000-07-11 Larry Jones <larry.jones@sdrc.com> + + * configure.in, config.h.in: Add checks for mknod() and st_rdev + since some systems (notably Plan 9) don't have them. + * configure: Regenerated. + +2000-07-10 Larry Jones <larry.jones@sdrc.com> + + * NEWS: Note the new "version" command. + +2000-07-06 Larry Jones <larry.jones@sdrc.com> + + * NEWS: Note that admin -t works in client/server. + +2000-06-19 Larry Jones <larry.jones@sdrc.com> + + * configure.in (AC_DEFINE): Define REGEX_MALLOC and _REGEX_RE_COMP + to configure lib/regex.c the way we want without messing with the + code. + * config.h.in: Ditto. + * configure: Regenerated. + +2000-05-16 Jim Kingdon <kingdon@redhat.com> + + * TODO (186): Remove paragraph about Eric Raymond's interest. + This is still on the future projects on his site, it just seems to + brief (and too long ago updated) that I don't really see the need + to mention it. + +2000-05-05 Larry Jones <larry.jones@sdrc.com> + + * TESTS: Add notes about required tools and where to get them. + +2000-05-02 Donald Sharp <sharpd@cisco.com> + and Larry Jones <larry.jones@sdrc.com> + + * NEWS: Note history output format change. + +2000-02-17 Larry Jones <larry.jones@sdrc.com> + + * NEWS: Note that PreservePermissions is disabled. + * configure.in: Don't define PRESERVE_PERMISSIONS_SUPPORT. + * configure: Regenerated. + +2000-02-01 Larry Jones <larry.jones@sdrc.com> + + * configure.in: Try again to handle systems that need both libsocket + and libnsl. + * configure: Regenerated. + +1999-12-09 Larry Jones <larry.jones@sdrc.com> + + * configure.in: Correctly handle systems that need both libsocket + and libnsl. + * configure: Regenerated. + +1999-12-06 Larry Jones <larry.jones@sdrc.com> + + * configure.in: Update to autoconf 2.13; use new AC_SEARCH_LIBS + to handle getspnam, connect, gethostbyname, and crypt correctly; + use new AC_FUNC_FNMATCH instead of doing it by hand. + * configure: Regenerated with autoconf 2.13. + +1999-12-06 Larry Jones <larry.jones@sdrc.com> + + * INSTALL (Tested platforms): Update info. + +1999-11-04 Jim Kingdon <http://developer.redhat.com/> + + * README (Installation): Yet another prep.ai.mit.edu -> gnu.org + change (can't believe we still haven't gotten them all). + +1999-11-04 Karl Fogel <kfogel@red-bean.com> + + * NEWS: added item about anon cvs no longer needing password. + +1999-10-28 Larry Jones <larry.jones@sdrc.com> + + * TESTS: Add note about not running as root. Remove note about + Solaris sort since sanity.sh was changed to avoid the problem. + 1999-07-12 Larry Jones <larry.jones@sdrc.com> * TESTS: Remove suspicion that setting LC_COLLATE has fixed the diff --git a/gnu/usr.bin/cvs/DEVEL-CVS b/gnu/usr.bin/cvs/DEVEL-CVS index b91b9ba126c..803d78cc127 100644 --- a/gnu/usr.bin/cvs/DEVEL-CVS +++ b/gnu/usr.bin/cvs/DEVEL-CVS @@ -6,7 +6,7 @@ development group operates. Also see the HACKING file. ---------------------------------------------------------------------- Charter for the devel-cvs mailing list: -The CVS Developers' List <devel-cvs@cyclic.com> exists to help people +The CVS Developers' List <devel-cvs@cvshome.org> exists to help people with access to the CVS source repository co-ordinate changes, make releases, and administer the repository. diff --git a/gnu/usr.bin/cvs/FAQ b/gnu/usr.bin/cvs/FAQ index 06fe8180da6..7a1b4e8c128 100644 --- a/gnu/usr.bin/cvs/FAQ +++ b/gnu/usr.bin/cvs/FAQ @@ -18,6 +18,9 @@ some time, you are encouraged to double-check them against other sources like the Cederqvist manual and update the FAQ. If you don't have such time, take them with a grain of salt or a few. +Since Feb. 2000 CVS is being maintained by OpenAvenue, Inc. and many of +the existing resources have been centeralized on http://www.cvshome.org. + Category: /, all questions Category: / @@ -8554,3 +8557,4 @@ done The FAQ-O-Matic lives at http://gille.loria.fr:7000/cgi-bin/faqomatic. The code was written by Jon Howell, and the content by folks from all over the web. + _________________________________________________________________ diff --git a/gnu/usr.bin/cvs/HACKING b/gnu/usr.bin/cvs/HACKING index aa6029dd5bd..a2840273ebd 100644 --- a/gnu/usr.bin/cvs/HACKING +++ b/gnu/usr.bin/cvs/HACKING @@ -191,7 +191,7 @@ Anyone can add themselves to the following mailing lists: automatically, daily, by a script which runs "make check" and "make remotecheck" on the master CVS sources. To subscribe to devel-cvs, commit-cvs, or test-results, send -a message to "majordomo@cyclic.com" whose body consists of +a message to "majordomo@cvshome.org" whose body consists of "subscribe <list>", where <list> is devel-cvs, commit-cvs or test-results. diff --git a/gnu/usr.bin/cvs/INSTALL b/gnu/usr.bin/cvs/INSTALL index 2400f07640c..1a1da8c99e6 100644 --- a/gnu/usr.bin/cvs/INSTALL +++ b/gnu/usr.bin/cvs/INSTALL @@ -122,7 +122,7 @@ HPPA: HPPA 1.1 running HP-UX A.09.04 (1.7.1) HPPA running HP-UX 9.05 (1.9) HPPA running HP-UX 10.01 (1.7) - HPPA running HP-UX 10.20 (1.9, 1.9.14) + HPPA running HP-UX 10.20 (1.10.7) NextSTEP 3.3 (1.7) i386 family: Solaris 2.4 using gcc (about 1.4A2) @@ -134,7 +134,7 @@ i386 family: Linux (kernel 1.2.x) (1.8.86) Linux (kernel 2.0.x, RedHat 4.2) (1.10) Linux (kernel 2.0.x, RedHat 5.x) (1.10) - BSDI 2.0 (1.4.93) (footnote 5) + BSDI 4.0 (1.10.7) FreeBSD 2.1.5-stable (1.8.87) NextSTEP 3.3 (1.7) SCO Unix 3.2.4.2, gcc 2.7.2 (1.8.87) (footnote 4) @@ -168,11 +168,13 @@ MIPS: SGI running Irix 6.2 using SGI MIPSpro 6.2 and beta 7.2 compilers (1.9) SGI running Irix-6.2 (1.9.8) SGI running IRIX 6.4 (1.10) + SGI running IRIX 6.5 (1.10.7) Siemens-Nixdorf RM600 running SINIX-Y (1.6) PowerPC or RS/6000: IBM RS/6000 running AIX 3.1 using gcc and cc (1.6.86) IBM RS/6000 running AIX 3.2.5 (1.8) IBM RS/6000 running AIX 4.1 (1.9) + IBM RS/6000 running AIX 4.3 (1.10.7) Lynx 2.3.1 120495 (1.6.86) (footnote 9) Lynx 2.5 (1.9) (footnote 10) MkLinux DR3 GENERIC #6 (1.10.5.1) (presumably LinuxPPC too) @@ -182,6 +184,7 @@ SPARC: Sun SPARCstation running Solaris 2.4 using gcc and cc (about 1.5.91) Sun SPARC running Solaris 2.5 (1.8.87) Sun SPARC running Solaris 2.5.1 using gcc 2.7.2.2 (1.9.14) + Sun SPARC running Solaris 2.6 (1.10.7) Sun UltraSPARC running Solaris 2.6 using gcc 2.8.1 (1.10) NextSTEP 3.3 (1.7) Sun SPARC running Linux 2.0.17, gcc 2.7.2 (1.8.87) @@ -198,11 +201,6 @@ VAX: (footnote 4) Comment out the include of sys/time.h in src/server.c. (1.4.93) You also may have to make sure TIME_WITH_SYS_TIME is undef'ed. -(footnote 5) Change /usr/tmp to /var/tmp in src/server.c (2 places) (1.4.93). - (This should no longer be needed; CVS doesn't have /usr/tmp in - src/server.c any more. Has anyone tried a more recent version - on BSDI? If so, please report it so we can update this file). - (footnote 6) Build in ucb universe with COFF compiler tools. Put /usr/local/bin first in PATH while doing a configure, make and install of GNU diffutils-2.7, rcs-5.7, then cvs-1.5. diff --git a/gnu/usr.bin/cvs/NEWS b/gnu/usr.bin/cvs/NEWS index 05506ef7843..cd5ad8717ad 100644 --- a/gnu/usr.bin/cvs/NEWS +++ b/gnu/usr.bin/cvs/NEWS @@ -1,5 +1,24 @@ Changes since 1.10: +* The new "cvs version" command gives a short version message. If +the repository is remote, both the client and server versions are +reported. + +* "cvs admin -t" now works correctly in client/server mode. + +* The "cvs history" command output format has changed -- the date +now includes the year and is given is ISO 8601 format (yyyy-mm-dd). +Also, the new LogHistory option in CVSROOT/config can be used to +control what information gets recorded in the log file and code has +been added to record file removals. + +* The buggy PreservePermissions code has been disabled. + +* Anonymous read-only access can now be done without requiring a +password. On the server side, simply give that user (presumably +`anonymous') an empty password in the CVSROOT/passwd file, and then +any received password will authenticate successfully. + * There is a new access method :fork: which is similar to :local: except that it is implemented via the CVS remote protocol, and thus has a somewhat different set of quirks and bugs. diff --git a/gnu/usr.bin/cvs/README b/gnu/usr.bin/cvs/README index e6c436ba16d..cde5be6e2e7 100644 --- a/gnu/usr.bin/cvs/README +++ b/gnu/usr.bin/cvs/README @@ -97,9 +97,12 @@ versions of CVS? On the web, http://www.loria.fr/~molli/cvs-index.html. -The mailing list for CVS is info-cvs@prep.ai.mit.edu. Send +See also + http://www.cvshome.org + +The mailing list for CVS is info-cvs@gnu.org. Send subscription and removal requests for that list to -info-cvs-request@prep.ai.mit.edu. +info-cvs-request@gnu.org. The newsgroup for CVS (and other configuration management systems) is comp.software.config-mgmt. There is not yet a CVS-specific newsgroup, diff --git a/gnu/usr.bin/cvs/TESTS b/gnu/usr.bin/cvs/TESTS index c27f9701909..bde8d1fd50f 100644 --- a/gnu/usr.bin/cvs/TESTS +++ b/gnu/usr.bin/cvs/TESTS @@ -8,6 +8,8 @@ pathname of a shell which handles normal shell functions: $ make SHELL=/bin/sh5 check +Also note that you must be logged in as a regular user, not root. + WARNING: This test can take quite a while to run, esp. if your disks are slow or over-loaded. @@ -16,13 +18,22 @@ If for some reason you want them to work in a different directory, you can set the TESTDIR environment variable to the desired location before running them. -You will probably need GNU expr, which is part of the GNU sh-utils -package. This is just for running the tests; CVS itself doesn't -require expr. - -With CVS 1.10 people also had trouble with the Solaris sort program -not behaving the way that the testsuite expects (with Solaris, lines -starting with tabs sort before blank lines). +The tests use a number of tools (awk, expr, id, tr, etc.) that are not +required for running CVS itself. In most cases, the standard vendor- +supplied versions of these tools work just fine, but there are some +exceptions -- expr in particular is heavily used and many vendor +versions are deficient in one way or another. Note that some vendors +provide multiple versions of tools (typically an ancient, traditional +version and a new, standards-conforming version), so you may already +have a usable version even if the default version isn't. If you don't +have a suitable tool, you can probably get one from the GNU Project (see +http://www.gnu.org). expr and id are both part of the GNU shellutils +package, tr is part of the GNU textutils package, and awk is part of the +GNU gawk package. The test script tries to verify that the tools exist +and are usable; if not, it tries to find the GNU versions and use them +instead. If it can't find the GNU versions either, it will print an +error message and, depending on the severity of the deficiency, it may +exit. If there is some unexpected output, that is a failure which can be somewhat hard to track down. Finding out which test is producing the diff --git a/gnu/usr.bin/cvs/TODO b/gnu/usr.bin/cvs/TODO index 91313a309c0..06d1ebc1d10 100644 --- a/gnu/usr.bin/cvs/TODO +++ b/gnu/usr.bin/cvs/TODO @@ -515,11 +515,6 @@ merges from one branch to the other happen automatically (for example, the case in which the branches have not diverged). This might be a legitimate question to ask even quite aside from multisite features. -One additional random tidbit is to note that Eric Raymond has some -interest in this sort of thing. The item "Cooperative distributed -freeware development" on http://www.ccil.org/~esr/ has a very brief -introduction to what he is thinking about. - 187. Might want to separate out usage error messages and help messages. The problem now is that if you specify an invalid option, for example, the error message is lost among all the help text. In diff --git a/gnu/usr.bin/cvs/acconfig.h b/gnu/usr.bin/cvs/acconfig.h index 7748e28f19a..15589e8f7a3 100644 --- a/gnu/usr.bin/cvs/acconfig.h +++ b/gnu/usr.bin/cvs/acconfig.h @@ -43,3 +43,15 @@ /* Define if the diff library should use setmode for binary files. FIXME: Why two different macros for setmode? */ #undef HAVE_SETMODE + +/* Define if you have the crypt function. */ +#undef HAVE_CRYPT + +/* Define if you have the getspnam function. */ +#undef HAVE_GETSPNAM + +/* Define to force lib/regex.c to use malloc instead of alloca. */ +#undef REGEX_MALLOC + +/* Define to force lib/regex.c to define re_comp et al. */ +#undef _REGEX_RE_COMP diff --git a/gnu/usr.bin/cvs/config.h.in b/gnu/usr.bin/cvs/config.h.in index 1280d2e54bd..438b798d3dc 100644 --- a/gnu/usr.bin/cvs/config.h.in +++ b/gnu/usr.bin/cvs/config.h.in @@ -16,12 +16,18 @@ /* Define to `int' if <sys/types.h> doesn't define. */ #undef gid_t +/* Define if your system has a working fnmatch function. */ +#undef HAVE_FNMATCH + /* Define if you support file names longer than 14 characters. */ #undef HAVE_LONG_FILE_NAMES /* Define if your struct stat has st_blksize. */ #undef HAVE_ST_BLKSIZE +/* Define if your struct stat has st_rdev. */ +#undef HAVE_ST_RDEV + /* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H @@ -94,9 +100,6 @@ /* Define if you have the connect function. */ #undef HAVE_CONNECT -/* Define if this system supports chown(), link(), and friends. */ -#undef PRESERVE_PERMISSIONS_SUPPORT - /* Define if you have memchr (always for CVS). */ #undef HAVE_MEMCHR @@ -117,6 +120,18 @@ /* Define if you have the crypt function. */ #undef HAVE_CRYPT +/* Define if you have the getspnam function. */ +#undef HAVE_GETSPNAM + +/* Define to force lib/regex.c to use malloc instead of alloca. */ +#undef REGEX_MALLOC + +/* Define to force lib/regex.c to define re_comp et al. */ +#undef _REGEX_RE_COMP + +/* Define if you have the dup2 function. */ +#undef HAVE_DUP2 + /* Define if you have the fchdir function. */ #undef HAVE_FCHDIR @@ -135,8 +150,8 @@ /* Define if you have the getpagesize function. */ #undef HAVE_GETPAGESIZE -/* Define if you have the getspnam function. */ -#undef HAVE_GETSPNAM +/* Define if you have the getpassphrase function. */ +#undef HAVE_GETPASSPHRASE /* Define if you have the initgroups function. */ #undef HAVE_INITGROUPS @@ -144,6 +159,15 @@ /* Define if you have the krb_get_err_text function. */ #undef HAVE_KRB_GET_ERR_TEXT +/* Define if you have the memmove function. */ +#undef HAVE_MEMMOVE + +/* Define if you have the mkdir function. */ +#undef HAVE_MKDIR + +/* Define if you have the mknod function. */ +#undef HAVE_MKNOD + /* Define if you have the mktemp function. */ #undef HAVE_MKTEMP @@ -153,6 +177,9 @@ /* Define if you have the readlink function. */ #undef HAVE_READLINK +/* Define if you have the rename function. */ +#undef HAVE_RENAME + /* Define if you have the sigaction function. */ #undef HAVE_SIGACTION @@ -168,6 +195,15 @@ /* Define if you have the sigvec function. */ #undef HAVE_SIGVEC +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the strtoul function. */ +#undef HAVE_STRTOUL + /* Define if you have the tempnam function. */ #undef HAVE_TEMPNAM @@ -177,12 +213,18 @@ /* Define if you have the tzset function. */ #undef HAVE_TZSET +/* Define if you have the valloc function. */ +#undef HAVE_VALLOC + /* Define if you have the vprintf function. */ #undef HAVE_VPRINTF /* Define if you have the wait3 function. */ #undef HAVE_WAIT3 +/* Define if you have the waitpid function. */ +#undef HAVE_WAITPID + /* Define if you have the <direct.h> header file. */ #undef HAVE_DIRECT_H @@ -207,6 +249,9 @@ /* Define if you have the <io.h> header file. */ #undef HAVE_IO_H +/* Define if you have the <krb5.h> header file. */ +#undef HAVE_KRB5_H + /* Define if you have the <limits.h> header file. */ #undef HAVE_LIMITS_H @@ -255,23 +300,5 @@ /* Define if you have the <utime.h> header file. */ #undef HAVE_UTIME_H -/* Define if you have the crypt library (-lcrypt). */ -#undef HAVE_LIBCRYPT - /* Define if you have the gen library (-lgen). */ #undef HAVE_LIBGEN - -/* Define if you have the inet library (-linet). */ -#undef HAVE_LIBINET - -/* Define if you have the nsl library (-lnsl). */ -#undef HAVE_LIBNSL - -/* Define if you have the nsl_s library (-lnsl_s). */ -#undef HAVE_LIBNSL_S - -/* Define if you have the sec library (-lsec). */ -#undef HAVE_LIBSEC - -/* Define if you have the socket library (-lsocket). */ -#undef HAVE_LIBSOCKET diff --git a/gnu/usr.bin/cvs/contrib/ChangeLog b/gnu/usr.bin/cvs/contrib/ChangeLog index 46cf007051b..6a2ee13d066 100644 --- a/gnu/usr.bin/cvs/contrib/ChangeLog +++ b/gnu/usr.bin/cvs/contrib/ChangeLog @@ -1,3 +1,20 @@ +2000-09-07 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@ + from autoconf. + +2000-02-25 Larry Jones <larry.jones@sdr.com> + + * log.pl: Get committer from command line instead of getlogin + so that client/server works correctly. + * loc_accum.pl: Ditto. + +2000-01-24 K.J. Paradise <kj@sourcegear.com> + + * sccs2rcs.csh: fixed a y2k bug. This was submitted + by Ceri Davies <ceri_davies@isdcorp.com>, and looks + okay to me. + 1999-01-19 Graham Stoney <greyham@research.canon.com.au> * log.pl: The author commited the canonical perl "localtime" Y2K diff --git a/gnu/usr.bin/cvs/cvs.spec b/gnu/usr.bin/cvs/cvs.spec index bae2f9cdd60..80e8e7babf0 100644 --- a/gnu/usr.bin/cvs/cvs.spec +++ b/gnu/usr.bin/cvs/cvs.spec @@ -4,7 +4,7 @@ Version: @VERSION@ Release: 1 Copyright: GPL Group: Development/Version Control -Source: ftp://download.cyclic.com/pub/cvs-@VERSION@/cvs-@VERSION@.tar.gz +Source: ftp://ftp.cvshome.org/pub/cvs-@VERSION@/cvs-@VERSION@.tar.gz Prefix: /usr %description diff --git a/gnu/usr.bin/cvs/diff/ChangeLog b/gnu/usr.bin/cvs/diff/ChangeLog index a43518258d0..e3272e84c2d 100644 --- a/gnu/usr.bin/cvs/diff/ChangeLog +++ b/gnu/usr.bin/cvs/diff/ChangeLog @@ -1,3 +1,33 @@ +2000-08-03 Larry Jones <larry.jones@sdrc.com> + + * diff3.c (read_diff): Use cvs_temp_name () instead of tmpnam () so + there's at least a chance of getting the file in the correct tmp dir. + +2000-07-10 Larry Jones <larry.jones@sdrc.com> + + * util.c (printf_output): Fix type clashes. + +2000-06-15 Larry Jones <larry.jones@sdrc.com> + + * diff3.c (diff3_run, make_3way_diff): Plug memory leaks. + +1999-12-29 Jim Kingdon <http://developer.redhat.com/> + + * diff.c (compare_files): Use explicit braces with if-if-else, per + GNU coding standards and gcc -Wall. + +1999-11-23 Larry Jones <larry.jones@sdrc.com> + + * diff3.c: Explicitly initialize zero_diff3 to placate neurotic + compilers that gripe about implicitly initialized const variables. + Reported by Eric Veum <sysv@yahoo.com>. + +1999-09-15 Larry Jones <larry.jones@sdrc.com> + + * diff.c (diff_run): Move the setjmp call before the options + processing since option errors can call fatal which in turn + calls longjmp. + 1999-05-06 Jim Kingdon <http://www.cyclic.com> * Makefile.in (DISTFILES): Remove libdiff.mak. diff --git a/gnu/usr.bin/cvs/diff/diff.c b/gnu/usr.bin/cvs/diff/diff.c index e5f7e42bfcd..aa91913fe61 100644 --- a/gnu/usr.bin/cvs/diff/diff.c +++ b/gnu/usr.bin/cvs/diff/diff.c @@ -247,11 +247,21 @@ diff_run (argc, argv, out, callbacks_arg) /* Do our initializations. */ initialize_main (&argc, &argv); - - /* Decode the options. */ - optind_old = optind; optind = 0; + + /* Set the jump buffer, so that diff may abort execution without + terminating the process. */ + val = setjmp (diff_abort_buf); + if (val != 0) + { + optind = optind_old; + if (opened_file) + fclose (outfile); + return val; + } + + /* Decode the options. */ while ((c = getopt_long (argc, argv, "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y", longopts, 0)) != EOF) @@ -686,17 +696,6 @@ diff_run (argc, argv, out, callbacks_arg) } } - /* Set the jump buffer, so that diff may abort execution without - terminating the process. */ - val = setjmp (diff_abort_buf); - if (val != 0) - { - optind = optind_old; - if (opened_file) - fclose (outfile); - return val; - } - val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); /* Print any messages that were saved up for last. */ @@ -1147,13 +1146,15 @@ compare_files (dir0, name0, dir1, name1, depth) failed = 1; } if (inf[1].desc == -2) - if (same_files) - inf[1].desc = inf[0].desc; - else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) - { - perror_with_name (inf[1].name); - failed = 1; - } + { + if (same_files) + inf[1].desc = inf[0].desc; + else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) + { + perror_with_name (inf[1].name); + failed = 1; + } + } #if HAVE_SETMODE if (binary_I_O) diff --git a/gnu/usr.bin/cvs/diff/diff3.c b/gnu/usr.bin/cvs/diff/diff3.c index e3be1503e86..2f05d165fc9 100644 --- a/gnu/usr.bin/cvs/diff/diff3.c +++ b/gnu/usr.bin/cvs/diff/diff3.c @@ -41,6 +41,8 @@ void printf_output PARAMS((char const *, ...)) ; void flush_output PARAMS((void)); +char * cvs_temp_name PARAMS((void)); + /* * Internal data structures and macros for the diff3 program; includes * data structures for both diff3 diffs and normal diffs. @@ -475,8 +477,6 @@ diff3_run (argc, argv, out, callbacks_arg) free(content0); free(content1); - free_diff_blocks(thread0); - free_diff_blocks(thread1); free_diff3_blocks(diff3); if (! callbacks || ! callbacks->write_output) @@ -676,7 +676,7 @@ make_3way_diff (thread0, thread1) struct diff3_block const *last_diff3; - static struct diff3_block const zero_diff3; + static struct diff3_block const zero_diff3 = { 0 }; /* Initialization */ result = 0; @@ -765,6 +765,8 @@ make_3way_diff (thread0, thread1) tmpblock = using_to_diff3_block (using, last_using, base_water_thread, high_water_thread, last_diff3); + free_diff_blocks(using[0]); + free_diff_blocks(using[1]); if (!tmpblock) diff3_fatal ("internal error: screwup in format of diff blocks"); @@ -1274,7 +1276,7 @@ read_diff (filea, fileb, output_placement) *ap++ = fileb; *ap = 0; - diffout = tmpnam(NULL); + diffout = cvs_temp_name (); outfile_hold = outfile; callbacks_hold = callbacks; diff --git a/gnu/usr.bin/cvs/doc/ChangeLog b/gnu/usr.bin/cvs/doc/ChangeLog index 33b7f91d518..6a9206d5853 100644 --- a/gnu/usr.bin/cvs/doc/ChangeLog +++ b/gnu/usr.bin/cvs/doc/ChangeLog @@ -1,3 +1,193 @@ +2000-09-07 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@ + from autoconf. + +2000-08-21 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Removing directories, export): Note that export always + prunes directories and remove references to the non-existent -P flag. + +2000-07-28 Larry Jones <larry.jones@sdrc.com> + + * cvsclient.texi (Requests): Ensure that all rootless requests say + that they're rootless. + +2000-07-12 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Module program options): Remove note that commit and + update programs only working locally; they've worked client/server + for quite some time. + +2000-07-10 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Invoking CVS): Document new version command. + * cvsclient.texi (Requests): Document new version request. + +2000-07-06 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (admin options): Remove note about -t not working + in client/server. + +2000-04-03 Pavel Roskin <pavel_roskin@geocities.com> + + * cvs.texinfo (Telling CVS to notify you): Remove backslashes + before quotes. + +2000-05-24 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (From files): Clean up @var{wdir}/@var{rdir} vs. + @var{dir} usage. + +2000-05-19 Larry Jones <larry.jones@sdrc.com> + + * cvsclient.texi (Requests): Note that Global_option is now + valid without Root. + +2000-04-17 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Variables): Clarify what USER means in pserver. + +2000-03-08 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Connection): Add note about inetd rate limit. + (ErrorMessages): Add root home directory permission messages. + +2000-02-12 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Clean up text/formatting of previous change. + +2000-02-21 K.J. Paradise <kj@sourcegear.com> + + * cvs.texinfo : Adding John Cavanaugh's patch to allow + the history file to log actions based on the CVSROOT/config + file. (To limit which cvs actions actually make it into the + history file) + +2000-02-17 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Remove references to PreservePermissions. + + * cvs.texinfo (history options): Note default report type. + +2000-01-18 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Global options): Document compression levels. + +2000-01-18 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Minor editorial changes from Ken Foskey + <waratah@zip.com.au>. + +2000-01-11 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Add index entries for "Compression" and "Gzip". + Correct typography in many index entries (English phrases should + have initial caps, subcommands/files/etc. should be as-is). + +2000-01-10 Karl Fogel <kfogel@red-bean.com> + + * cvs.texinfo (loginfo): correctly describe CVSROOT/loginfo's + %-expansion behavior. Thanks to Karl Heinz Marbaise + <kama@hippo.fido.de> for noticing the error. + +2000-01-07 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo (Password authentication server): Use -f in example + inetd.conf line. + (Connection): Add advice about using shell script or env to avoid + problems with inetd setting HOME in the server's environment. + (various): Use @file for inetd.conf. + +2000-01-02 John P Cavanaugh <cavanaug@sr.hp.com> + + * cvs.texinfo: document new -C option to update, now that it works + both remotely and locally. + (Re-applied by Karl Fogel <kfogel@red-bean.com>.) + +1999-12-11 Karl Fogel <kfogel@red-bean.com> + + * Revert previous change -- it doesn't work remotely yet. + +1999-12-10 John P Cavanaugh <cavanaug@sr.hp.com> + + * cvs.texinfo: document new -C option to update. + (Applied by Karl Fogel <kfogel@red-bean.com>.) + +1999-11-20 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo(history options): Document -f, -n, and -z. + +1999-11-09 Jim Kingdon <http://developer.redhat.com/> + + * cvsclient.texi (Requests): Document the arguments to "log", now + that I've changed log.c to be more specific in terms of what it + will send. + +1999-11-05 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Revert Karl's change once again since the code is now + fixed. Add "Variables" and "User variables" to index. + +1999-11-04 Karl Fogel <kfogel@red-bean.com> + + * log.c (log_usage): Revert Jim Kingdon's reversion of my change + of 1999-11-03. Allowing a space between option and argument + results in lossage; here is a reproduction recipe: run this from + the top of a remote copy of the cvs source tree + + cvs log -d '>1999-03-01' > log-out.with-space + + and then run this (note there's no space after -d now): + + cvs log -d'>1999-03-01' > log-out.no-space + + The resulting files differ; furthermore, a glance at the output of + cvs shows that the first command failed to recurse into + subdirectories. Until this misbehavior can be fixed in the source + code, the documentation should reflect the true state of affairs: + if one simply omits the space, everything works fine. + +1999-11-04 Jim Kingdon <http://developer.redhat.com/> + + * cvs.texinfo (log options): Revert Karl's change regarding -d and + -s. A space is allowed (see sanity.sh for example). + + * cvs.texinfo (Password authentication server): The name of the + file is "passwd" not "password". + + * cvsclient.texi (Top): Add @dircategory and @direntry. + +1999-11-04 Karl Fogel <kfogel@red-bean.com> + + * cvs.texinfo (Password authentication server, Password + authentication client): Rewritten to accommodate the [new] + possibility of empty passwords. + +1999-11-03 Karl Fogel <kfogel@red-bean.com> + + * cvs.texinfo (Invoking CVS): correct documentation for -d and -s + options (as did elsewhere, earlier today). + +1999-11-03 Karl Fogel <kfogel@red-bean.com> + + * cvs.texinfo (Setting a watch): describe `watch off' behavior + more accurately. + +1999-11-03 Karl Fogel <kfogel@red-bean.com> + + * cvs.texinfo (log options): correct documentation for -d and -s + options. There can be no space between these options and their + arguments. + + Also, make sure all @sc{cvs} codes refer to "cvs" in lower case; + this avoids makeinfo warnings. And use @code for the CVSEDITOR + environment variable, not @sc. + +1999-09-24 Larry Jones <larry.jones@sdrc.com> + + * cvs.texinfo: Misc. formatting cleanups. + 1999-07-16 Tom Tromey <tromey@cygnus.com> * cvs.texinfo (admin): Mention admin -k exception. Add cvsadmin diff --git a/gnu/usr.bin/cvs/doc/cvsclient.texi b/gnu/usr.bin/cvs/doc/cvsclient.texi index 394c0cafb09..5ba7ed6b4eb 100644 --- a/gnu/usr.bin/cvs/doc/cvsclient.texi +++ b/gnu/usr.bin/cvs/doc/cvsclient.texi @@ -3,6 +3,11 @@ @setfilename cvsclient.info @include CVSvn.texi +@dircategory Programming +@direntry +* cvsclient: (cvsclient). The CVS client/server protocol. +@end direntry + @node Top @top CVS Client/Server @@ -492,7 +497,7 @@ command line client (versions 1.5 through at least 1.9). For the @samp{-d} option to the @code{log} request, servers should at least support RFC 822/1123 format. Clients are encouraged to use this -format too (traditionally the command line CVS client has just passed +format too (the command line CVS client, version 1.10 and older, just passed along the date format specified by the user, however). The @code{Mod-time} response and @code{Checkin-time} request use RFC @@ -537,16 +542,19 @@ in use, connection, authentication, etc., are already taken care of. The @code{Root} request must be sent only once, and it must be sent before any requests other than @code{Valid-responses}, -@code{valid-requests}, @code{UseUnchanged}, @code{Set} or @code{init}. +@code{valid-requests}, @code{UseUnchanged}, @code{Set}, +@code{Global_option}, @code{init}, @code{noop}, or @code{version}. @item Valid-responses @var{request-list} \n Response expected: no. Tell the server what responses the client will accept. request-list is a space separated list of tokens. +The @code{Root} request need not have been previously sent. @item valid-requests \n Response expected: yes. Ask the server to send back a @code{Valid-requests} response. +The @code{Root} request need not have been previously sent. @item Directory @var{local-directory} \n Additional data: @var{repository} \n. Response expected: no. @@ -859,6 +867,7 @@ must not contain @samp{/}. Response expected: no. To specify the version of the protocol described in this document, servers must support this request (although it need not do anything) and clients must issue it. +The @code{Root} request need not have been previously sent. @item Notify @var{filename} \n Response expected: no. @@ -950,6 +959,7 @@ strings, no variations (such as combining of options) are allowed. For graceful handling of @code{valid-requests}, it is probably better to make new global options separate requests, rather than trying to add them to this request. +The @code{Root} request need not have been previously sent. @item Gzip-stream @var{level} \n Response expected: no. @@ -1007,6 +1017,7 @@ connection between the initial authentication and the @item Set @var{variable}=@var{value} \n Response expected: no. Set a user variable @var{variable} to @var{value}. +The @code{Root} request need not have been previously sent. @item expand-modules \n Response expected: yes. Expand the modules which are specified in the @@ -1064,7 +1075,6 @@ directory. @itemx diff \n @itemx tag \n @itemx status \n -@itemx log \n @itemx admin \n @itemx history \n @itemx watchers \n @@ -1078,6 +1088,56 @@ of the operation. No provision is made for any input from the user. This means that @code{ci} must use a @code{-m} argument if it wants to specify a log message. +@item log \n +Response expected: yes. Show information for past revisions. This uses +any previous @code{Directory}, @code{Entry}, or @code{Modified} +requests, if they have been sent. The last @code{Directory} sent +specifies the working directory at the time of the operation. Also uses +previous @code{Argument}'s of which the canonical forms are the +following (@sc{cvs} 1.10 and older clients sent what the user specified, +but clients are encouraged to use the canonical forms and other forms +are deprecated): + +@table @code +@item -b, -h, -l, -N, -R, -t +These options go by themselves, one option per @code{Argument} request. + +@item -d @var{date1}<@var{date2} +Select revisions between @var{date1} and @var{date2}. Either date +may be omitted in which case there is no date limit at that end of the +range (clients may specify dates such as 1 Jan 1970 or 1 Jan 2038 for +similar purposes but this is problematic as it makes assumptions about +what dates the server supports). Dates are in RFC822/1123 format. The +@samp{-d} is one @code{Argument} request and the date range is a second +one. + +@item -d @var{date1}<=@var{date2} +Likewise but compare dates for equality. + +@item -d @var{singledate} +Select the single, latest revision dated @var{singledate} or earlier. + +To include several date ranges and/or singledates, repeat the @samp{-d} +option as many times as necessary. + +@item -r@var{rev1}:@var{rev2} +@itemx -r@var{branch} +@itemx -r@var{branch}. +@itemx -r +Specify revisions (note that @var{rev1} or @var{rev2} can be omitted, or +can refer to branches). Send both the @samp{-r} and the revision +information in a single @code{Argument} request. To include several +revision selections, repeat the @samp{-r} option. + +@item -s @var{state} +@itemx -w +@itemx -w@var{login} +Select on states or users. To include more than one state or user, +repeat the option. Send the @samp{-s} option as a separate argument +from the state being selected. Send the @samp{-w} option as part of the +same argument as the user being selected. +@end table + @item co \n Response expected: yes. Get files from the repository. This uses any previous @code{Argument}, @code{Directory}, @code{Entry}, or @@ -1111,8 +1171,8 @@ commands are module names, as described for @code{co}. @item init @var{root-name} \n Response expected: yes. If it doesn't already exist, create a @sc{cvs} repository @var{root-name}. Note that @var{root-name} is a local -directory and @emph{not} a fully qualified @code{CVSROOT} variable. The -@code{Root} request need not have been previously sent. +directory and @emph{not} a fully qualified @code{CVSROOT} variable. +The @code{Root} request need not have been previously sent. @item update \n Response expected: yes. Actually do a @code{cvs update} command. This @@ -1245,6 +1305,7 @@ Response expected: yes. This request is a null command in the sense that it doesn't do anything, but merely (as with any other requests expecting a response) sends back any responses pertaining to pending errors, pending @code{Notified} responses, etc. +The @code{Root} request need not have been previously sent. @item update-patches \n Response expected: yes. @@ -1277,6 +1338,11 @@ Response expected: yes. Request that the server transmit mappings from filenames to keyword expansion modes in @code{Wrapper-rcsOption} responses. +@item version \n +Response expected: yes. +Request that the server transmit its version message. +The @code{Root} request need not have been previously sent. + @item @var{other-request} @var{text} \n Response expected: yes. Any unrecognized request expects a response, and does not @@ -1934,8 +2000,8 @@ working directory, and the meaning of sending @code{Entries} without A number of enhancements are possible. Also see the file @sc{todo} in the @sc{cvs} source distribution, which has further ideas concerning various aspects of @sc{cvs}, some of which impact the protocol. -Similarly, the @code{http://www.cyclic.com} site, in particular the -@cite{Development of CVS} page. +Similarly, the @code{http://www.cvshome.org} site, in particular the +@cite{Development} pages. @itemize @bullet @item diff --git a/gnu/usr.bin/cvs/install-sh b/gnu/usr.bin/cvs/install-sh index ab74c882e92..402d456bd15 100644 --- a/gnu/usr.bin/cvs/install-sh +++ b/gnu/usr.bin/cvs/install-sh @@ -10,6 +10,7 @@ # This script is compatible with the BSD install script, but was written # from scratch. # +# # set DOITPROG to echo to test this script diff --git a/gnu/usr.bin/cvs/lib/ChangeLog b/gnu/usr.bin/cvs/lib/ChangeLog index 37cc6ab9f5d..102ca4cf4ef 100644 --- a/gnu/usr.bin/cvs/lib/ChangeLog +++ b/gnu/usr.bin/cvs/lib/ChangeLog @@ -1,3 +1,46 @@ +2000-07-10 Larry Jones <larry.jones@sdrc.com> + + * savecwd.c: #include <sys/types.h> before <fcntl.h>. + +2000-07-04 Karl Fogel <kfogel@red-bean.com> + + * getline.h, getline.c (getstr): take new limit arg. + (GETLINE_NO_LIMIT): new #define. + (getline_safe): new function, takes limit arg and passes it on. + (getline): pass GETLINE_NO_LIMIT to getstr(). + + See related change of same date in ../src/ChangeLog. + +2000-06-19 Larry Jones <larry.jones@sdrc.com> + + * regex.c, regex.h: Version from emacs 20.7 to plug memory leaks + and avoid potential portability problems. + +2000-03-22 Larry Jones <larry.jones@sdrc.com> + + * getdate.y: Add logic to allow yyyy/mm/dd in addition to mm/dd/yy + (since that is the format CVS frequently uses). + * getdate.c: Regenerated. + +2000-02-16 Jim Meyering <meyering@lucent.com> + + * sighandle.c (SIG_inCrSect): New function. + +2000-01-03 Larry Jones <larry.jones@sdrc.com> + + * getdate.y (Convert): Add window to determine whether 2-digit dates + are 19xx (69-99) or 20xx (00-68). + (get_date): Fix y2k bug: initialize yyYear to tm_year + 1900, + not just tm_year. + * getdate.c: Regenerated. + +1999-12-29 Jim Kingdon <http://developer.redhat.com/> + + * Makefile.in: There was a comment here which referred to a long + comment in configure.in about regex.o (the configure.in comment + isn't there any more). Replace our comment with a conciser + version of the former configure.in comment. + 1999-03-26 Jim Kingdon <http://www.cyclic.com> * getopt.h: Don't declare the arguments to getopt. diff --git a/gnu/usr.bin/cvs/lib/regex.c b/gnu/usr.bin/cvs/lib/regex.c index 38b31f43afd..1c3ab8d0e17 100644 --- a/gnu/usr.bin/cvs/lib/regex.c +++ b/gnu/usr.bin/cvs/lib/regex.c @@ -2,7 +2,7 @@ 0.12. (Implements POSIX draft P10003.2/D11.2, except for internationalization features.) - Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,25 +11,50 @@ 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. */ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -/* Trying to define this in the makefile would get hairy, unless we can - more gracefully do it for NT, OS/2, unix, etc. */ -#define REGEX_MALLOC 1 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ /* AIX requires this to be the first thing in the file. */ #if defined (_AIX) && !defined (REGEX_MALLOC) #pragma alloca #endif +#undef _GNU_SOURCE #define _GNU_SOURCE -/* We need this for `regex.h', and perhaps for the Emacs include files. */ -#include <sys/types.h> +#ifdef emacs +/* Converts the pointer to the char to BEG-based offset from the start. */ +#define PTR_TO_OFFSET(d) \ + POS_AS_IN_BUFFER (MATCHING_IN_FIRST_STRING \ + ? (d) - string1 : (d) - (string2 - size1)) +#define POS_AS_IN_BUFFER(p) ((p) + (NILP (re_match_object) || BUFFERP (re_match_object))) +#else +#define PTR_TO_OFFSET(d) 0 +#endif #ifdef HAVE_CONFIG_H -#include "config.h" +#include <config.h> +#endif + +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +#include <sys/types.h> + +/* This is for other GNU distributions with internationalized messages. */ +#if HAVE_LIBINTL_H || defined (_LIBC) +# include <libintl.h> +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +#define gettext_noop(String) String #endif /* The `emacs' switch turns on certain matching commands @@ -47,13 +72,38 @@ #include "category.h" #define malloc xmalloc +#define realloc xrealloc #define free xfree #else /* not emacs */ -/* We used to test for `BSTRING' here, but only GCC and Emacs define - `BSTRING', as far as I know, and neither of them use this code. */ -#if HAVE_STRING_H || STDC_HEADERS +/* If we are not linking with Emacs proper, + we can't use the relocating allocator + even if config.h says that we can. */ +#undef REL_ALLOC + +#if defined (STDC_HEADERS) || defined (_LIBC) +#include <stdlib.h> +#else +char *malloc (); +char *realloc (); +#endif + +/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow. + If nothing else has been done, use the method below. */ +#ifdef INHIBIT_STRING_HEADER +#if !(defined (HAVE_BZERO) && defined (HAVE_BCOPY)) +#if !defined (bzero) && !defined (bcopy) +#undef INHIBIT_STRING_HEADER +#endif +#endif +#endif + +/* This is the normal way of making sure we have a bcopy and a bzero. + This is used in most programs--a few other programs avoid this + by defining INHIBIT_STRING_HEADER. */ +#ifndef INHIBIT_STRING_HEADER +#if defined (HAVE_STRING_H) || defined (STDC_HEADERS) || defined (_LIBC) #include <string.h> #ifndef bcmp #define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) @@ -67,15 +117,8 @@ #else #include <strings.h> #endif - -#ifdef STDC_HEADERS -#include <stdlib.h> -#else -char *malloc (); -char *realloc (); #endif - /* Define the syntax stuff for \<, \>, etc. */ /* This must be nonzero for the wordchar and notwordchar pattern @@ -84,6 +127,12 @@ char *realloc (); #define Sword 1 #endif +#ifdef SWITCH_ENUM_BUG +#define SWITCH_ENUM_CAST(x) ((int)(x)) +#else +#define SWITCH_ENUM_CAST(x) (x) +#endif + #ifdef SYNTAX_TABLE extern char *re_syntax_table; @@ -124,6 +173,19 @@ init_syntax_once () #define SYNTAX(c) re_syntax_table[c] +/* Dummy macros for non-Emacs environments. */ +#define BASE_LEADING_CODE_P(c) (0) +#define WORD_BOUNDARY_P(c1, c2) (0) +#define CHAR_HEAD_P(p) (1) +#define SINGLE_BYTE_CHAR_P(c) (1) +#define SAME_CHARSET_P(c1, c2) (1) +#define MULTIBYTE_FORM_LENGTH(p, s) (1) +#define STRING_CHAR(p, s) (*(p)) +#define STRING_CHAR_AND_LENGTH(p, s, actual_len) ((actual_len) = 1, *(p)) +#define GET_CHAR_AFTER_2(c, p, str1, end1, str2, end2) \ + (c = ((p) == (end1) ? *(str2) : *(p))) +#define GET_CHAR_BEFORE_2(c, p, str1, end1, str2, end2) \ + (c = ((p) == (str2) ? *((end1) - 1) : *((p) - 1))) #endif /* not emacs */ /* Get the interface, including the syntax bits. */ @@ -132,34 +194,47 @@ init_syntax_once () /* isalpha etc. are used for the character classes. */ #include <ctype.h> -#ifndef isascii -#define isascii(c) 1 +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." */ + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +#define ISASCII(c) 1 +#else +#define ISASCII(c) isascii(c) #endif #ifdef isblank -#define ISBLANK(c) (isascii (c) && isblank (c)) +#define ISBLANK(c) (ISASCII (c) && isblank (c)) #else #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef isgraph -#define ISGRAPH(c) (isascii (c) && isgraph (c)) +#define ISGRAPH(c) (ISASCII (c) && isgraph (c)) #else -#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c)) +#define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) #endif -#define ISPRINT(c) (isascii (c) && isprint (c)) -#define ISDIGIT(c) (isascii (c) && isdigit (c)) -#define ISALNUM(c) (isascii (c) && isalnum (c)) -#define ISALPHA(c) (isascii (c) && isalpha (c)) -#define ISCNTRL(c) (isascii (c) && iscntrl (c)) -#define ISLOWER(c) (isascii (c) && islower (c)) -#define ISPUNCT(c) (isascii (c) && ispunct (c)) -#define ISSPACE(c) (isascii (c) && isspace (c)) -#define ISUPPER(c) (isascii (c) && isupper (c)) -#define ISXDIGIT(c) (isascii (c) && isxdigit (c)) +#define ISPRINT(c) (ISASCII (c) && isprint (c)) +#define ISDIGIT(c) (ISASCII (c) && isdigit (c)) +#define ISALNUM(c) (ISASCII (c) && isalnum (c)) +#define ISALPHA(c) (ISASCII (c) && isalpha (c)) +#define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) +#define ISLOWER(c) (ISASCII (c) && islower (c)) +#define ISPUNCT(c) (ISASCII (c) && ispunct (c)) +#define ISSPACE(c) (ISASCII (c) && isspace (c)) +#define ISUPPER(c) (ISASCII (c) && isupper (c)) +#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) #ifndef NULL -#define NULL 0 +#define NULL (void *)0 #endif /* We remove any previous definition of `SIGN_EXTEND_CHAR', @@ -188,6 +263,7 @@ init_syntax_once () #define REGEX_ALLOCATE malloc #define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) +#define REGEX_FREE free #else /* not REGEX_MALLOC */ @@ -201,10 +277,12 @@ init_syntax_once () #if HAVE_ALLOCA_H #include <alloca.h> #else /* not __GNUC__ or HAVE_ALLOCA_H */ -#ifndef _AIX /* Already did AIX, up at the top. */ +#if 0 /* It is a bad idea to declare alloca. We always cast the result. */ +#ifndef _AIX /* Already did AIX, up at the top. */ char *alloca (); #endif /* not _AIX */ -#endif /* not HAVE_ALLOCA_H */ +#endif +#endif /* not HAVE_ALLOCA_H */ #endif /* not __GNUC__ */ #endif /* not alloca */ @@ -217,21 +295,57 @@ char *alloca (); bcopy (source, destination, osize), \ destination) +/* No need to do anything to free, after alloca. */ +#define REGEX_FREE(arg) ((void)0) /* Do nothing! But inhibit gcc warning. */ + +#endif /* not REGEX_MALLOC */ + +/* Define how to allocate the failure stack. */ + +#if defined (REL_ALLOC) && defined (REGEX_MALLOC) + +#define REGEX_ALLOCATE_STACK(size) \ + r_alloc (&failure_stack_ptr, (size)) +#define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + r_re_alloc (&failure_stack_ptr, (nsize)) +#define REGEX_FREE_STACK(ptr) \ + r_alloc_free (&failure_stack_ptr) + +#else /* not using relocating allocator */ + +#ifdef REGEX_MALLOC + +#define REGEX_ALLOCATE_STACK malloc +#define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize) +#define REGEX_FREE_STACK free + +#else /* not REGEX_MALLOC */ + +#define REGEX_ALLOCATE_STACK alloca + +#define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + REGEX_REALLOCATE (source, osize, nsize) +/* No need to explicitly free anything. */ +#define REGEX_FREE_STACK(arg) + #endif /* not REGEX_MALLOC */ +#endif /* not using relocating allocator */ /* True if `size1' is non-NULL and PTR is pointing anywhere inside `string1' or just past its end. This works if PTR is NULL, which is a good thing. */ -#define FIRST_STRING_P(ptr) \ +#define FIRST_STRING_P(ptr) \ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) /* (Re)Allocate N items of type T using malloc, or fail. */ #define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) #define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define RETALLOC_IF(addr, n, t) \ + if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t) #define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) -#define BYTEWIDTH 8 /* In bits. */ +#define BYTEWIDTH 8 /* In bits. */ #define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) @@ -243,122 +357,123 @@ char *alloca (); typedef char boolean; #define false 0 #define true 1 + +static int re_match_2_internal (); /* These are the command codes that appear in compiled regular - expressions. Some opcodes are followed by argument bytes. A + expressions. Some opcodes are followed by argument bytes. A command code can specify any interpretation whatsoever for its - arguments. Zero bytes may appear in the compiled regular expression. - - The value of `exactn' is needed in search.c (search_buffer) in Emacs. - So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of - `exactn' we use here must also be 1. */ + arguments. Zero bytes may appear in the compiled regular expression. */ typedef enum { no_op = 0, - /* Followed by one byte giving n, then by n literal bytes. */ - exactn = 1, + /* Succeed right away--no more backtracking. */ + succeed, - /* Matches any (more or less) character. */ + /* Followed by one byte giving n, then by n literal bytes. */ + exactn, + + /* Matches any (more or less) character. */ anychar, - /* Matches any one char belonging to specified set. First - following byte is number of bitmap bytes. Then come bytes - for a bitmap saying which chars are in. Bits in each byte - are ordered low-bit-first. A character is in the set if its - bit is 1. A character too large to have a bit in the map is - automatically not in the set. */ + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ charset, - /* Same parameters as charset, but match any character that is - not one of those specified. */ + /* Same parameters as charset, but match any character that is + not one of those specified. */ charset_not, - /* Start remembering the text that is matched, for storing in a - register. Followed by one byte with the register number, in - the range 0 to one less than the pattern buffer's re_nsub - field. Then followed by one byte with the number of groups - inner to this one. (This last has to be part of the - start_memory only because we need it in the on_failure_jump - of re_match_2.) */ + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ start_memory, - /* Stop remembering the text that is matched and store it in a - memory register. Followed by one byte with the register - number, in the range 0 to one less than `re_nsub' in the - pattern buffer, and one byte with the number of inner groups, - just like `start_memory'. (We need the number of inner - groups here because we don't have any easy way of finding the - corresponding start_memory when we're at a stop_memory.) */ + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ stop_memory, - /* Match a duplicate of something remembered. Followed by one - byte containing the register number. */ + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ duplicate, - /* Fail unless at beginning of line. */ + /* Fail unless at beginning of line. */ begline, - /* Fail unless at end of line. */ + /* Fail unless at end of line. */ endline, - /* Succeeds if at beginning of buffer (if emacs) or at beginning - of string to be matched (if not). */ + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ begbuf, - /* Analogously, for end of buffer/string. */ + /* Analogously, for end of buffer/string. */ endbuf, - /* Followed by two byte relative address to which to jump. */ + /* Followed by two byte relative address to which to jump. */ jump, /* Same as jump, but marks the end of an alternative. */ jump_past_alt, - /* Followed by two-byte relative address of place to resume at - in case of failure. */ + /* Followed by two-byte relative address of place to resume at + in case of failure. */ on_failure_jump, - /* Like on_failure_jump, but pushes a placeholder instead of the - current string position when executed. */ + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ on_failure_keep_string_jump, - /* Throw away latest failure point and then jump to following - two-byte relative address. */ + /* Throw away latest failure point and then jump to following + two-byte relative address. */ pop_failure_jump, - /* Change to pop_failure_jump if know won't have to backtrack to - match; otherwise change to jump. This is used to jump - back to the beginning of a repeat. If what follows this jump - clearly won't match what the repeat does, such that we can be - sure that there is no use backtracking out of repetitions - already matched, then we change it to a pop_failure_jump. - Followed by two-byte address. */ + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ maybe_pop_jump, - /* Jump to following two-byte address, and push a dummy failure - point. This failure point will be thrown away if an attempt - is made to use it for a failure. A `+' construct makes this - before the first repeat. Also used as an intermediary kind - of jump when compiling an alternative. */ + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ dummy_failure_jump, /* Push a dummy failure point and continue. Used at the end of alternatives. */ push_dummy_failure, - /* Followed by two-byte relative address and two-byte number n. - After matching N times, jump to the address upon failure. */ + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ succeed_n, - /* Followed by two-byte relative address, and two-byte number n. - Jump to the address N times, then fail. */ + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ jump_n, - /* Set the following two-byte relative address to the - subsequent two-byte number. The address *includes* the two - bytes of number. */ + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ set_number_at, wordchar, /* Matches any word-constituent character. */ @@ -368,7 +483,7 @@ typedef enum wordend, /* Succeeds if at word end. */ wordbound, /* Succeeds if at a word boundary. */ - notwordbound /* Succeeds if not at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ #ifdef emacs ,before_dot, /* Succeeds if before point. */ @@ -376,11 +491,21 @@ typedef enum after_dot, /* Succeeds if after point. */ /* Matches any character whose syntax is specified. Followed by - a byte which contains a syntax code, e.g., Sword. */ + a byte which contains a syntax code, e.g., Sword. */ syntaxspec, /* Matches any character whose syntax is not that specified. */ - notsyntaxspec + notsyntaxspec, + + /* Matches any character whose category-set contains the specified + category. The operator is followed by a byte which contains a + category code (mnemonic ASCII character). */ + categoryspec, + + /* Matches any character whose category-set does not contain the + specified category. The operator is followed by a byte which + contains the category code (mnemonic ASCII character). */ + notcategoryspec #endif /* emacs */ } re_opcode_t; @@ -424,7 +549,7 @@ extract_number (dest, source) *dest += temp << 8; } -#ifndef EXTRACT_MACROS /* To debug the macros. */ +#ifndef EXTRACT_MACROS /* To debug the macros. */ #undef EXTRACT_NUMBER #define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) #endif /* not EXTRACT_MACROS */ @@ -437,7 +562,7 @@ extract_number (dest, source) #define EXTRACT_NUMBER_AND_INCR(destination, source) \ do { \ EXTRACT_NUMBER (destination, source); \ - (source) += 2; \ + (source) += 2; \ } while (0) #ifdef DEBUG @@ -458,11 +583,98 @@ extract_number_and_incr (destination, source) #endif /* DEBUG */ +/* Store a multibyte character in three contiguous bytes starting + DESTINATION, and increment DESTINATION to the byte after where the + character is stored. Therefore, DESTINATION must be an lvalue. */ + +#define STORE_CHARACTER_AND_INCR(destination, character) \ + do { \ + (destination)[0] = (character) & 0377; \ + (destination)[1] = ((character) >> 8) & 0377; \ + (destination)[2] = (character) >> 16; \ + (destination) += 3; \ + } while (0) + +/* Put into DESTINATION a character stored in three contiguous bytes + starting at SOURCE. */ + +#define EXTRACT_CHARACTER(destination, source) \ + do { \ + (destination) = ((source)[0] \ + | ((source)[1] << 8) \ + | ((source)[2] << 16)); \ + } while (0) + + +/* Macros for charset. */ + +/* Size of bitmap of charset P in bytes. P is a start of charset, + i.e. *P is (re_opcode_t) charset or (re_opcode_t) charset_not. */ +#define CHARSET_BITMAP_SIZE(p) ((p)[1] & 0x7F) + +/* Nonzero if charset P has range table. */ +#define CHARSET_RANGE_TABLE_EXISTS_P(p) ((p)[1] & 0x80) + +/* Return the address of range table of charset P. But not the start + of table itself, but the before where the number of ranges is + stored. `2 +' means to skip re_opcode_t and size of bitmap. */ +#define CHARSET_RANGE_TABLE(p) (&(p)[2 + CHARSET_BITMAP_SIZE (p)]) + +/* Test if C is listed in the bitmap of charset P. */ +#define CHARSET_LOOKUP_BITMAP(p, c) \ + ((c) < CHARSET_BITMAP_SIZE (p) * BYTEWIDTH \ + && (p)[2 + (c) / BYTEWIDTH] & (1 << ((c) % BYTEWIDTH))) + +/* Return the address of end of RANGE_TABLE. COUNT is number of + ranges (which is a pair of (start, end)) in the RANGE_TABLE. `* 2' + is start of range and end of range. `* 3' is size of each start + and end. */ +#define CHARSET_RANGE_TABLE_END(range_table, count) \ + ((range_table) + (count) * 2 * 3) + +/* Test if C is in RANGE_TABLE. A flag NOT is negated if C is in. + COUNT is number of ranges in RANGE_TABLE. */ +#define CHARSET_LOOKUP_RANGE_TABLE_RAW(not, c, range_table, count) \ + do \ + { \ + int range_start, range_end; \ + unsigned char *p; \ + unsigned char *range_table_end \ + = CHARSET_RANGE_TABLE_END ((range_table), (count)); \ + \ + for (p = (range_table); p < range_table_end; p += 2 * 3) \ + { \ + EXTRACT_CHARACTER (range_start, p); \ + EXTRACT_CHARACTER (range_end, p + 3); \ + \ + if (range_start <= (c) && (c) <= range_end) \ + { \ + (not) = !(not); \ + break; \ + } \ + } \ + } \ + while (0) + +/* Test if C is in range table of CHARSET. The flag NOT is negated if + C is listed in it. */ +#define CHARSET_LOOKUP_RANGE_TABLE(not, c, charset) \ + do \ + { \ + /* Number of ranges in range table. */ \ + int count; \ + unsigned char *range_table = CHARSET_RANGE_TABLE (charset); \ + \ + EXTRACT_NUMBER_AND_INCR (count, range_table); \ + CHARSET_LOOKUP_RANGE_TABLE_RAW ((not), (c), range_table, count); \ + } \ + while (0) + /* If DEBUG is defined, Regex prints many voluminous messages about what it is doing (if the variable `debug' is nonzero). If linked with the main program in `iregex.c', you can enter patterns and strings interactively. And if linked with the main program in `main.c' and - the other test files, you can run the already-written tests. */ + the other test files, you can run the already-written tests. */ #ifdef DEBUG @@ -479,14 +691,12 @@ static int debug = 0; #define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) #define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) #define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) -#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ if (debug) print_partial_compiled_pattern (s, e) #define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ if (debug) print_double_string (w, s1, sz1, s2, sz2) -extern void printchar (); - /* Print the fastmap in human-readable form. */ void @@ -501,18 +711,18 @@ print_fastmap (fastmap) if (fastmap[i++]) { was_a_range = 0; - printchar (i - 1); - while (i < (1 << BYTEWIDTH) && fastmap[i]) - { - was_a_range = 1; - i++; - } + putchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } if (was_a_range) - { - printf ("-"); - printchar (i - 1); - } - } + { + printf ("-"); + putchar (i - 1); + } + } } putchar ('\n'); } @@ -539,32 +749,34 @@ print_partial_compiled_pattern (start, end) /* Loop over pattern commands. */ while (p < pend) { + printf ("%d:\t", p - start); + switch ((re_opcode_t) *p++) { - case no_op: - printf ("/no_op"); - break; + case no_op: + printf ("/no_op"); + break; case exactn: mcnt = *p++; - printf ("/exactn/%d", mcnt); - do + printf ("/exactn/%d", mcnt); + do { - putchar ('/'); - printchar (*p++); - } - while (--mcnt); - break; + putchar ('/'); + putchar (*p++); + } + while (--mcnt); + break; case start_memory: - mcnt = *p++; - printf ("/start_memory/%d/%d", mcnt, *p++); - break; + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; case stop_memory: - mcnt = *p++; + mcnt = *p++; printf ("/stop_memory/%d/%d", mcnt, *p++); - break; + break; case duplicate: printf ("/duplicate/%d", *p++); @@ -575,102 +787,120 @@ print_partial_compiled_pattern (start, end) break; case charset: - case charset_not: - { - register int c; - - printf ("/charset%s", - (re_opcode_t) *(p - 1) == charset_not ? "_not" : ""); - - assert (p + *p < pend); - - for (c = 0; c < *p; c++) - { - unsigned bit; - unsigned char map_byte = p[1 + c]; - - putchar ('/'); - - for (bit = 0; bit < BYTEWIDTH; bit++) - if (map_byte & (1 << bit)) - printchar (c * BYTEWIDTH + bit); - } + case charset_not: + { + register int c, last = -100; + register int in_range = 0; + + printf ("/charset [%s", + (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); + + assert (p + *p < pend); + + for (c = 0; c < 256; c++) + if (c / 8 < *p + && (p[1 + (c/8)] & (1 << (c % 8)))) + { + /* Are we starting a range? */ + if (last + 1 == c && ! in_range) + { + putchar ('-'); + in_range = 1; + } + /* Have we broken a range? */ + else if (last + 1 != c && in_range) + { + putchar (last); + in_range = 0; + } + + if (! in_range) + putchar (c); + + last = c; + } + + if (in_range) + putchar (last); + + putchar (']'); + p += 1 + *p; - break; } + break; case begline: printf ("/begline"); - break; + break; case endline: - printf ("/endline"); - break; + printf ("/endline"); + break; case on_failure_jump: - extract_number_and_incr (&mcnt, &p); - printf ("/on_failure_jump/0/%d", mcnt); - break; + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump to %d", p + mcnt - start); + break; case on_failure_keep_string_jump: - extract_number_and_incr (&mcnt, &p); - printf ("/on_failure_keep_string_jump/0/%d", mcnt); - break; + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); + break; case dummy_failure_jump: - extract_number_and_incr (&mcnt, &p); - printf ("/dummy_failure_jump/0/%d", mcnt); - break; + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump to %d", p + mcnt - start); + break; case push_dummy_failure: - printf ("/push_dummy_failure"); - break; - - case maybe_pop_jump: - extract_number_and_incr (&mcnt, &p); - printf ("/maybe_pop_jump/0/%d", mcnt); + printf ("/push_dummy_failure"); break; - case pop_failure_jump: + case maybe_pop_jump: extract_number_and_incr (&mcnt, &p); - printf ("/pop_failure_jump/0/%d", mcnt); - break; - - case jump_past_alt: + printf ("/maybe_pop_jump to %d", p + mcnt - start); + break; + + case pop_failure_jump: extract_number_and_incr (&mcnt, &p); - printf ("/jump_past_alt/0/%d", mcnt); - break; - - case jump: + printf ("/pop_failure_jump to %d", p + mcnt - start); + break; + + case jump_past_alt: extract_number_and_incr (&mcnt, &p); - printf ("/jump/0/%d", mcnt); + printf ("/jump_past_alt to %d", p + mcnt - start); break; - case succeed_n: - extract_number_and_incr (&mcnt, &p); - extract_number_and_incr (&mcnt2, &p); - printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2); - break; + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump to %d", p + mcnt - start); + break; - case jump_n: - extract_number_and_incr (&mcnt, &p); - extract_number_and_incr (&mcnt2, &p); - printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2); - break; + case succeed_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2); + break; - case set_number_at: - extract_number_and_incr (&mcnt, &p); - extract_number_and_incr (&mcnt2, &p); - printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2); - break; + case jump_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2); + break; - case wordbound: + case set_number_at: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2); + break; + + case wordbound: printf ("/wordbound"); break; case notwordbound: printf ("/notwordbound"); - break; + break; case wordbeg: printf ("/wordbeg"); @@ -682,24 +912,24 @@ print_partial_compiled_pattern (start, end) #ifdef emacs case before_dot: printf ("/before_dot"); - break; + break; case at_dot: printf ("/at_dot"); - break; + break; case after_dot: printf ("/after_dot"); - break; + break; case syntaxspec: - printf ("/syntaxspec"); + printf ("/syntaxspec"); mcnt = *p++; printf ("/%d", mcnt); - break; + break; case notsyntaxspec: - printf ("/notsyntaxspec"); + printf ("/notsyntaxspec"); mcnt = *p++; printf ("/%d", mcnt); break; @@ -707,25 +937,28 @@ print_partial_compiled_pattern (start, end) case wordchar: printf ("/wordchar"); - break; + break; case notwordchar: printf ("/notwordchar"); - break; + break; case begbuf: printf ("/begbuf"); - break; + break; case endbuf: printf ("/endbuf"); - break; + break; - default: - printf ("?%d", *(p-1)); + default: + printf ("?%d", *(p-1)); } + + putchar ('\n'); } - printf ("/\n"); + + printf ("%d:\tend of pattern.\n", p - start); } @@ -771,15 +1004,15 @@ print_double_string (where, string1, size1, string2, size2) else { if (FIRST_STRING_P (where)) - { - for (this_char = where - string1; this_char < size1; this_char++) - printchar (string1[this_char]); + { + for (this_char = where - string1; this_char < size1; this_char++) + putchar (string1[this_char]); - where = string2; - } + where = string2; + } for (this_char = where - string2; this_char < size2; this_char++) - printchar (string2[this_char]); + putchar (string2[this_char]); } } @@ -801,7 +1034,9 @@ print_double_string (where, string1, size1, string2, size2) /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can also be assigned to arbitrarily: each pattern buffer stores its own syntax, so it can be changed between regex compilations. */ -reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; /* Specify the precise syntax of regexps for compilation. This provides @@ -809,7 +1044,7 @@ reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; different, incompatible syntaxes. The argument SYNTAX is a bit mask comprised of the various bits - defined in regex.h. We return the old syntax. */ + defined in regex.h. We return the old syntax. */ reg_syntax_t re_set_syntax (syntax) @@ -822,28 +1057,477 @@ re_set_syntax (syntax) } /* This table gives an error message for each of the error codes listed - in regex.h. Obviously the order here has to be same as there. */ - -static const char *re_error_msg[] = - { NULL, /* REG_NOERROR */ - "No match", /* REG_NOMATCH */ - "Invalid regular expression", /* REG_BADPAT */ - "Invalid collation character", /* REG_ECOLLATE */ - "Invalid character class name", /* REG_ECTYPE */ - "Trailing backslash", /* REG_EESCAPE */ - "Invalid back reference", /* REG_ESUBREG */ - "Unmatched [ or [^", /* REG_EBRACK */ - "Unmatched ( or \\(", /* REG_EPAREN */ - "Unmatched \\{", /* REG_EBRACE */ - "Invalid content of \\{\\}", /* REG_BADBR */ - "Invalid range end", /* REG_ERANGE */ - "Memory exhausted", /* REG_ESPACE */ - "Invalid preceding regular expression", /* REG_BADRPT */ - "Premature end of regular expression", /* REG_EEND */ - "Regular expression too big", /* REG_ESIZE */ - "Unmatched ) or \\)", /* REG_ERPAREN */ + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +static const char *re_error_msgid[] = + { + gettext_noop ("Success"), /* REG_NOERROR */ + gettext_noop ("No match"), /* REG_NOMATCH */ + gettext_noop ("Invalid regular expression"), /* REG_BADPAT */ + gettext_noop ("Invalid collation character"), /* REG_ECOLLATE */ + gettext_noop ("Invalid character class name"), /* REG_ECTYPE */ + gettext_noop ("Trailing backslash"), /* REG_EESCAPE */ + gettext_noop ("Invalid back reference"), /* REG_ESUBREG */ + gettext_noop ("Unmatched [ or [^"), /* REG_EBRACK */ + gettext_noop ("Unmatched ( or \\("), /* REG_EPAREN */ + gettext_noop ("Unmatched \\{"), /* REG_EBRACE */ + gettext_noop ("Invalid content of \\{\\}"), /* REG_BADBR */ + gettext_noop ("Invalid range end"), /* REG_ERANGE */ + gettext_noop ("Memory exhausted"), /* REG_ESPACE */ + gettext_noop ("Invalid preceding regular expression"), /* REG_BADRPT */ + gettext_noop ("Premature end of regular expression"), /* REG_EEND */ + gettext_noop ("Regular expression too big"), /* REG_ESIZE */ + gettext_noop ("Unmatched ) or \\)"), /* REG_ERPAREN */ }; +/* Avoiding alloca during matching, to placate r_alloc. */ + +/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the + searching and matching functions should not call alloca. On some + systems, alloca is implemented in terms of malloc, and if we're + using the relocating allocator routines, then malloc could cause a + relocation, which might (if the strings being searched are in the + ralloc heap) shift the data out from underneath the regexp + routines. + + Here's another reason to avoid allocation: Emacs + processes input from X in a signal handler; processing X input may + call malloc; if input arrives while a matching routine is calling + malloc, then we're scrod. But Emacs can't just block input while + calling matching routines; then we don't notice interrupts when + they come in. So, Emacs blocks input around all regexp calls + except the matching calls, which it leaves unprotected, in the + faith that they will not malloc. */ + +/* Normally, this is fine. */ +#define MATCH_MAY_ALLOCATE + +/* When using GNU C, we are not REALLY using the C alloca, no matter + what config.h may say. So don't take precautions for it. */ +#ifdef __GNUC__ +#undef C_ALLOCA +#endif + +/* The match routines may not allocate if (1) they would do it with malloc + and (2) it's not safe for them to use malloc. + Note that if REL_ALLOC is defined, matching would not use malloc for the + failure stack, but we would still use it for the register vectors; + so REL_ALLOC should not affect this. */ +#if (defined (C_ALLOCA) || defined (REGEX_MALLOC)) && defined (emacs) +#undef MATCH_MAY_ALLOCATE +#endif + + +/* Failure stack declarations and macros; both re_compile_fastmap and + re_match_2 use a failure stack. These have to be macros because of + REGEX_ALLOCATE_STACK. */ + + +/* Approximate number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +#define INIT_FAILURE_ALLOC 20 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used TYPICAL_FAILURE_SIZE items each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ +#if defined (MATCH_MAY_ALLOCATE) +/* Note that 4400 is enough to cause a crash on Alpha OSF/1, + whose default stack limit is 2mb. In order for a larger + value to work reliably, you have to try to make it accord + with the process stack limit. */ +int re_max_failures = 40000; +#else +int re_max_failures = 4000; +#endif + +union fail_stack_elt +{ + unsigned char *pointer; + int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) + + +/* Define macros to initialize and free the failure stack. + Do `return -2' if the alloc fails. */ + +#ifdef MATCH_MAY_ALLOCATE +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * TYPICAL_FAILURE_SIZE \ + * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) + +#define RESET_FAIL_STACK() REGEX_FREE_STACK (fail_stack.stack) +#else +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.avail = 0; \ + } while (0) + +#define RESET_FAIL_STACK() +#endif + + +/* Double the size of FAIL_STACK, up to a limit + which allows approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE_STACK requires `destination' be declared. */ + +/* Factor to increase the failure stack size by + when we increase it. + This used to be 2, but 2 was too wasteful + because the old discarded stacks added up to as much space + were as ultimate, maximum-size stack. */ +#define FAIL_STACK_GROWTH_FACTOR 4 + +#define GROW_FAIL_STACK(fail_stack) \ + (((fail_stack).size * sizeof (fail_stack_elt_t) \ + >= re_max_failures * TYPICAL_FAILURE_SIZE) \ + ? 0 \ + : ((fail_stack).stack \ + = (fail_stack_elt_t *) \ + REGEX_REALLOCATE_STACK ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + MIN (re_max_failures * TYPICAL_FAILURE_SIZE, \ + ((fail_stack).size * sizeof (fail_stack_elt_t) \ + * FAIL_STACK_GROWTH_FACTOR))), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size \ + = (MIN (re_max_failures * TYPICAL_FAILURE_SIZE, \ + ((fail_stack).size * sizeof (fail_stack_elt_t) \ + * FAIL_STACK_GROWTH_FACTOR)) \ + / sizeof (fail_stack_elt_t)), \ + 1))) + + +/* Push pointer POINTER on FAIL_STACK. + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ + ((FAIL_STACK_FULL () \ + && !GROW_FAIL_STACK (FAIL_STACK)) \ + ? 0 \ + : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ + 1)) + +/* Push a pointer value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_POINTER(item) \ + fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) + +/* This pushes an integer-valued item onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_INT(item) \ + fail_stack.stack[fail_stack.avail++].integer = (item) + +/* Push a fail_stack_elt_t value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ELT(item) \ + fail_stack.stack[fail_stack.avail++] = (item) + +/* These three POP... operations complement the three PUSH... operations. + All assume that `fail_stack' is nonempty. */ +#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer +#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer +#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +#define DEBUG_PUSH PUSH_FAILURE_INT +#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () +#else +#define DEBUG_PUSH(item) +#define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs be declared. GROW_FAIL_STACK requires `destination' be + declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + int this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!GROW_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + if (1) \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + PUSH_FAILURE_POINTER (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + PUSH_FAILURE_POINTER (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ELT (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ + PUSH_FAILURE_INT (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ + PUSH_FAILURE_INT (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_POINTER (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_POINTER (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +#define NUM_NONREG_ITEMS 4 +#endif + +/* Estimate the size of data pushed by a typical failure stack entry. + An estimate is all we need, because all we use this for + is to choose a limit for how big to make the failure stack. */ + +#define TYPICAL_FAILURE_SIZE 20 + +/* This is how many items we actually use for a failure point. + It depends on the regexp. */ +#define NUM_FAILURE_ITEMS \ + (((0 \ + ? 0 : highest_active_reg - lowest_active_reg + 1) \ + * NUM_REG_ITEMS) \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ + int this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_POINTER (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (unsigned) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ + \ + low_reg = (unsigned) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ + \ + if (1) \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ELT (); \ + DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + } \ + else \ + { \ + for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ + { \ + reg_info[this_reg].word.integer = 0; \ + regend[this_reg] = 0; \ + regstart[this_reg] = 0; \ + } \ + highest_active_reg = high_reg; \ + } \ + \ + set_regs_matched_done = 0; \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* POP_FAILURE_POINT */ + + + +/* Structure for per-register (a.k.a. per-group) information. + Other register information, such as the + starting and ending positions (which are addresses), and the list of + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ + +typedef union +{ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + if (!set_regs_matched_done) \ + { \ + unsigned r; \ + set_regs_matched_done = 1; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + } \ + while (0) + +/* Registers are set to a sentinel when they haven't yet matched. */ +static char reg_unset_dummy; +#define REG_UNSET_VALUE (®_unset_dummy) +#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) + /* Subroutine declarations and macros for regex_compile. */ static void store_op1 (), store_op2 (); @@ -852,21 +1536,23 @@ static boolean at_begline_loc_p (), at_endline_loc_p (); static boolean group_in_compile_stack (); static reg_errcode_t compile_range (); -/* Fetch the next character in the uncompiled pattern---translating it +/* Fetch the next character in the uncompiled pattern---translating it if necessary. Also cast from a signed character in the constant string passed to us by the user to an unsigned char that we can use as an array index (in, e.g., `translate'). */ +#ifndef PATFETCH #define PATFETCH(c) \ do {if (p == pend) return REG_EEND; \ c = (unsigned char) *p++; \ - if (translate) c = translate[c]; \ + if (RE_TRANSLATE_P (translate)) c = RE_TRANSLATE (translate, c); \ } while (0) +#endif /* Fetch the next character in the uncompiled pattern, with no - translation. */ + translation. */ #define PATFETCH_RAW(c) \ do {if (p == pend) return REG_EEND; \ - c = (unsigned char) *p++; \ + c = (unsigned char) *p++; \ } while (0) /* Go backwards one character in the pattern. */ @@ -877,7 +1563,11 @@ static reg_errcode_t compile_range (); cast the subscript to translate because some data is declared as `char *', to avoid warnings when a string constant is passed. But when we use a character as a subscript we must make it unsigned. */ -#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d)) +#ifndef TRANSLATE +#define TRANSLATE(d) \ + (RE_TRANSLATE_P (translate) \ + ? (unsigned) RE_TRANSLATE (translate, (unsigned) (d)) : (d)) +#endif /* Macros for outputting the compiled pattern into `buffer'. */ @@ -885,7 +1575,7 @@ static reg_errcode_t compile_range (); /* If the buffer isn't allocated when it comes in, use this. */ #define INIT_BUF_SIZE 32 -/* Make sure we have at least N more bytes of space in buffer. */ +/* Make sure we have at least N more bytes of space in buffer. */ #define GET_BUFFER_SPACE(n) \ while (b - bufp->buffer + (n) > bufp->allocated) \ EXTEND_BUFFER () @@ -907,7 +1597,7 @@ static reg_errcode_t compile_range (); } while (0) -/* As with BUF_PUSH_2, except for three bytes. */ +/* As with BUF_PUSH_2, except for three bytes. */ #define BUF_PUSH_3(c1, c2, c3) \ do { \ GET_BUFFER_SPACE (3); \ @@ -918,7 +1608,7 @@ static reg_errcode_t compile_range (); /* Store a jump with opcode OP at LOC to location TO. We store a - relative address offset by the three bytes the jump itself occupies. */ + relative address offset by the three bytes the jump itself occupies. */ #define STORE_JUMP(op, loc, to) \ store_op1 (op, loc, (to) - (loc) - 3) @@ -926,7 +1616,7 @@ static reg_errcode_t compile_range (); #define STORE_JUMP2(op, loc, to, arg) \ store_op2 (op, loc, (to) - (loc) - 3, arg) -/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ #define INSERT_JUMP(op, loc, to) \ insert_op1 (op, loc, (to) - (loc) - 3, b) @@ -936,7 +1626,7 @@ static reg_errcode_t compile_range (); /* This is not an arbitrary limit: the arguments which represent offsets - into the pattern are two bytes long. So if 2^16 bytes turns out to + into the pattern are two bytes long. So if 2^16 bytes turns out to be too small, many things would have to change. */ #define MAX_BUF_SIZE (1L << 16) @@ -944,29 +1634,29 @@ static reg_errcode_t compile_range (); /* Extend the buffer by twice its current size via realloc and reset the pointers that pointed into the old block to point to the correct places in the new one. If extending the buffer results in it - being larger than MAX_BUF_SIZE, then flag memory exhausted. */ + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ #define EXTEND_BUFFER() \ - do { \ + do { \ unsigned char *old_buffer = bufp->buffer; \ - if (bufp->allocated == MAX_BUF_SIZE) \ + if (bufp->allocated == MAX_BUF_SIZE) \ return REG_ESIZE; \ bufp->allocated <<= 1; \ if (bufp->allocated > MAX_BUF_SIZE) \ - bufp->allocated = MAX_BUF_SIZE; \ + bufp->allocated = MAX_BUF_SIZE; \ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ if (bufp->buffer == NULL) \ return REG_ESPACE; \ /* If the buffer moved, move all the pointers into it. */ \ if (old_buffer != bufp->buffer) \ { \ - b = (b - old_buffer) + bufp->buffer; \ - begalt = (begalt - old_buffer) + bufp->buffer; \ - if (fixup_alt_jump) \ - fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ - if (laststart) \ - laststart = (laststart - old_buffer) + bufp->buffer; \ - if (pending_exact) \ - pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ } \ } while (0) @@ -984,7 +1674,7 @@ typedef unsigned regnum_t; /* Macros for the compile stack. */ /* Since offsets can go either forwards or backwards, this type needs to - be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ typedef int pattern_offset_t; typedef struct @@ -1010,31 +1700,76 @@ typedef struct #define COMPILE_STACK_EMPTY (compile_stack.avail == 0) #define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) -/* The next available element. */ +/* The next available element. */ #define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) +/* Structure to manage work area for range table. */ +struct range_table_work_area +{ + int *table; /* actual work area. */ + int allocated; /* allocated size for work area in bytes. */ + int used; /* actually used size in words. */ +}; + +/* Make sure that WORK_AREA can hold more N multibyte characters. */ +#define EXTEND_RANGE_TABLE_WORK_AREA(work_area, n) \ + do { \ + if (((work_area).used + (n)) * sizeof (int) > (work_area).allocated) \ + { \ + (work_area).allocated += 16 * sizeof (int); \ + if ((work_area).table) \ + (work_area).table \ + = (int *) realloc ((work_area).table, (work_area).allocated); \ + else \ + (work_area).table \ + = (int *) malloc ((work_area).allocated); \ + if ((work_area).table == 0) \ + FREE_STACK_RETURN (REG_ESPACE); \ + } \ + } while (0) + +/* Set a range (RANGE_START, RANGE_END) to WORK_AREA. */ +#define SET_RANGE_TABLE_WORK_AREA(work_area, range_start, range_end) \ + do { \ + EXTEND_RANGE_TABLE_WORK_AREA ((work_area), 2); \ + (work_area).table[(work_area).used++] = (range_start); \ + (work_area).table[(work_area).used++] = (range_end); \ + } while (0) + +/* Free allocated memory for WORK_AREA. */ +#define FREE_RANGE_TABLE_WORK_AREA(work_area) \ + do { \ + if ((work_area).table) \ + free ((work_area).table); \ + } while (0) + +#define CLEAR_RANGE_TABLE_WORK_USED(work_area) ((work_area).used = 0) +#define RANGE_TABLE_WORK_USED(work_area) ((work_area).used) +#define RANGE_TABLE_WORK_ELT(work_area, i) ((work_area).table[i]) + + /* Set the bit for character C in a list. */ -#define SET_LIST_BIT(c) \ - (b[((unsigned char) (c)) / BYTEWIDTH] \ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ |= 1 << (((unsigned char) c) % BYTEWIDTH)) /* Get the next unsigned number in the uncompiled pattern. */ -#define GET_UNSIGNED_NUMBER(num) \ +#define GET_UNSIGNED_NUMBER(num) \ { if (p != pend) \ { \ - PATFETCH (c); \ - while (ISDIGIT (c)) \ - { \ - if (num < 0) \ - num = 0; \ - num = num * 10 + c - '0'; \ - if (p == pend) \ - break; \ - PATFETCH (c); \ - } \ - } \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ } #define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ @@ -1047,6 +1782,54 @@ typedef struct || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) +#ifndef MATCH_MAY_ALLOCATE + +/* If we cannot allocate large objects within re_match_2_internal, + we make the fail stack and register vectors global. + The fail stack, we grow to the maximum size when a regexp + is compiled. + The register vectors, we adjust in size each time we + compile a regexp, according to the number of registers it needs. */ + +static fail_stack_type fail_stack; + +/* Size with which the following vectors are currently allocated. + That is so we can make them bigger as needed, + but never make them smaller. */ +static int regs_allocated_size; + +static const char ** regstart, ** regend; +static const char ** old_regstart, ** old_regend; +static const char **best_regstart, **best_regend; +static register_info_type *reg_info; +static const char **reg_dummy; +static register_info_type *reg_info_dummy; + +/* Make the register vectors big enough for NUM_REGS registers, + but don't make them smaller. */ + +static +regex_grow_registers (num_regs) + int num_regs; +{ + if (num_regs > regs_allocated_size) + { + RETALLOC_IF (regstart, num_regs, const char *); + RETALLOC_IF (regend, num_regs, const char *); + RETALLOC_IF (old_regstart, num_regs, const char *); + RETALLOC_IF (old_regend, num_regs, const char *); + RETALLOC_IF (best_regstart, num_regs, const char *); + RETALLOC_IF (best_regend, num_regs, const char *); + RETALLOC_IF (reg_info, num_regs, register_info_type); + RETALLOC_IF (reg_dummy, num_regs, const char *); + RETALLOC_IF (reg_info_dummy, num_regs, register_info_type); + + regs_allocated_size = num_regs; + } +} + +#endif /* not MATCH_MAY_ALLOCATE */ + /* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. Returns one of error codes defined in `regex.h', or zero for success. @@ -1065,6 +1848,14 @@ typedef struct The `fastmap' and `newline_anchor' fields are neither examined nor set. */ +/* Return, freeing storage we allocated. */ +#define FREE_STACK_RETURN(value) \ + do { \ + FREE_RANGE_TABLE_WORK_AREA (range_table_work); \ + free (compile_stack.stack); \ + return value; \ + } while (0) + static reg_errcode_t regex_compile (pattern, size, syntax, bufp) const char *pattern; @@ -1075,7 +1866,7 @@ regex_compile (pattern, size, syntax, bufp) /* We fetch characters from PATTERN here. Even though PATTERN is `char *' (i.e., signed), we declare these variables as unsigned, so they can be reliably used as array indices. */ - register unsigned char c, c1; + register unsigned int c, c1; /* A random temporary spot in PATTERN. */ const char *p1; @@ -1087,11 +1878,16 @@ regex_compile (pattern, size, syntax, bufp) compile_stack_type compile_stack; /* Points to the current (ending) position in the pattern. */ +#ifdef AIX + /* `const' makes AIX compiler fail. */ + char *p = pattern; +#else const char *p = pattern; +#endif const char *pend = pattern + size; /* How to translate the characters in the pattern. */ - char *translate = bufp->translate; + RE_TRANSLATE_TYPE translate = bufp->translate; /* Address of the count-byte of the most recently inserted `exactn' command. This makes it possible to tell if a new exact-match @@ -1112,7 +1908,7 @@ regex_compile (pattern, size, syntax, bufp) const char *beg_interval; /* Address of the place where a forward jump should go to the end of - the containing expression. Each alternative of an `or' -- except the + the containing expression. Each alternative of an `or' -- except the last -- ends with a forward jump of this sort. */ unsigned char *fixup_alt_jump = 0; @@ -1121,6 +1917,9 @@ regex_compile (pattern, size, syntax, bufp) number is put in the stop_memory as the start_memory. */ regnum_t regnum = 0; + /* Work area for range table of charset. */ + struct range_table_work_area range_table_work; + #ifdef DEBUG DEBUG_PRINT1 ("\nCompiling pattern: "); if (debug) @@ -1128,7 +1927,7 @@ regex_compile (pattern, size, syntax, bufp) unsigned debug_count; for (debug_count = 0; debug_count < size; debug_count++) - printchar (pattern[debug_count]); + putchar (pattern[debug_count]); putchar ('\n'); } #endif /* DEBUG */ @@ -1141,6 +1940,9 @@ regex_compile (pattern, size, syntax, bufp) compile_stack.size = INIT_COMPILE_STACK_SIZE; compile_stack.avail = 0; + range_table_work.table = 0; + range_table_work.allocated = 0; + /* Initialize the pattern buffer. */ bufp->syntax = syntax; bufp->fastmap_accurate = 0; @@ -1154,6 +1956,14 @@ regex_compile (pattern, size, syntax, bufp) /* Always count groups, whether or not bufp->no_sub is set. */ bufp->re_nsub = 0; +#ifdef emacs + /* bufp->multibyte is set before regex_compile is called, so don't alter + it. */ +#else /* not emacs */ + /* Nothing is recognized as a multibyte character. */ + bufp->multibyte = 0; +#endif + #if !defined (emacs) && !defined (SYNTAX_TABLE) /* Initialize the syntax table. */ init_syntax_once (); @@ -1163,15 +1973,15 @@ regex_compile (pattern, size, syntax, bufp) { if (bufp->buffer) { /* If zero allocated, but buffer is non-null, try to realloc - enough space. This loses if buffer's address is bogus, but - that is the user's responsibility. */ - RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); - } + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } else - { /* Caller did not allocate a buffer. Do it for them. */ - bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); - } - if (!bufp->buffer) return REG_ESPACE; + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE); bufp->allocated = INIT_BUF_SIZE; } @@ -1184,857 +1994,969 @@ regex_compile (pattern, size, syntax, bufp) PATFETCH (c); switch (c) - { - case '^': - { - if ( /* If at start of pattern, it's an operator. */ - p == pattern + 1 - /* If context independent, it's an operator. */ - || syntax & RE_CONTEXT_INDEP_ANCHORS - /* Otherwise, depends on what's come before. */ - || at_begline_loc_p (pattern, p, syntax)) - BUF_PUSH (begline); - else - goto normal_char; - } - break; + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; - case '$': - { - if ( /* If at end of pattern, it's an operator. */ - p == pend - /* If context independent, it's an operator. */ - || syntax & RE_CONTEXT_INDEP_ANCHORS - /* Otherwise, depends on what's next. */ - || at_endline_loc_p (p, pend, syntax)) - BUF_PUSH (endline); - else - goto normal_char; - } - break; + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; case '+': - case '?': - if ((syntax & RE_BK_PLUS_QM) - || (syntax & RE_LIMITED_OPS)) - goto normal_char; - handle_plus: - case '*': - /* If there is no previous pattern... */ - if (!laststart) - { - if (syntax & RE_CONTEXT_INVALID_OPS) - return REG_BADRPT; - else if (!(syntax & RE_CONTEXT_INDEP_OPS)) - goto normal_char; - } + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; - { - /* Are we optimizing this jump? */ - boolean keep_string_p = false; + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ - /* 1 means zero (many) matches is allowed. */ - char zero_times_ok = 0, many_times_ok = 0; + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; - /* If there is a sequence of repetition chars, collapse it - down to just one (the right one). We can't combine - interval operators with these because of, e.g., `a{2}*', - which should only match an even number of `a's. */ + if (p == pend) + break; - for (;;) - { - zero_times_ok |= c != '+'; - many_times_ok |= c != '?'; + PATFETCH (c); - if (p == pend) - break; + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; - PATFETCH (c); - - if (c == '*' - || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) - ; - - else if (syntax & RE_BK_PLUS_QM && c == '\\') - { - if (p == pend) return REG_EESCAPE; - - PATFETCH (c1); - if (!(c1 == '+' || c1 == '?')) - { - PATUNFETCH; - PATUNFETCH; - break; - } - - c = c1; - } - else - { - PATUNFETCH; - break; - } - - /* If we get here, we found another repeat character. */ - } - - /* Star, etc. applied to an empty pattern is equivalent - to an empty pattern. */ - if (!laststart) - break; - - /* Now we know whether or not zero matches is allowed - and also whether or not two or more matches is allowed. */ - if (many_times_ok) - { /* More than one repetition is allowed, so put in at the - end a backward relative jump from `b' to before the next - jump we're going to put in below (which jumps from - laststart to after this jump). - - But if we are at the `*' in the exact sequence `.*\n', - insert an unconditional jump backwards to the ., - instead of the beginning of the loop. This way we only - push a failure point once, instead of every time - through the loop. */ - assert (p - 1 > pattern); - - /* Allocate the space for the jump. */ - GET_BUFFER_SPACE (3); - - /* We know we are not at the first character of the pattern, - because laststart was nonzero. And we've already - incremented `p', by the way, to be the character after - the `*'. Do we have to do something analogous here - for null bytes, because of RE_DOT_NOT_NULL? */ - if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE ((unsigned char)*(p - 2)) == TRANSLATE ('.') && zero_times_ok - && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') - && !(syntax & RE_DOT_NEWLINE)) - { /* We have .*\n. */ - STORE_JUMP (jump, b, laststart); - keep_string_p = true; - } - else - /* Anything else. */ - STORE_JUMP (maybe_pop_jump, b, laststart - 3); - - /* We've added more stuff to the buffer. */ - b += 3; - } - - /* On failure, jump from laststart to b + 3, which will be the - end of the buffer after this jump is inserted. */ - GET_BUFFER_SPACE (3); - INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump - : on_failure_jump, - laststart, b + 3); - pending_exact = 0; - b += 3; - - if (!zero_times_ok) - { - /* At least one repetition is required, so insert a - `dummy_failure_jump' before the initial - `on_failure_jump' instruction of the loop. This - effects a skip over that instruction the first time - we hit that loop. */ - GET_BUFFER_SPACE (3); - INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); - b += 3; - } - } + && p < pend + && TRANSLATE ((unsigned char)*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } break; case '.': - laststart = b; - BUF_PUSH (anychar); - break; + laststart = b; + BUF_PUSH (anychar); + break; - case '[': - { - boolean had_char_class = false; + case '[': + { + CLEAR_RANGE_TABLE_WORK_USED (range_table_work); - if (p == pend) return REG_EBRACK; + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); - /* Ensure that we have enough space to push a charset: the - opcode, the length count, and the bitset; 34 bytes in all. */ + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ GET_BUFFER_SPACE (34); - laststart = b; + laststart = b; - /* We test `*p == '^' twice, instead of using an if - statement, so we only need one BUF_PUSH. */ - BUF_PUSH (*p == '^' ? charset_not : charset); - if (*p == '^') - p++; + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; - /* Remember the first position in the bracket expression. */ - p1 = p; + /* Remember the first position in the bracket expression. */ + p1 = p; - /* Push the number of bytes in the bitmap. */ - BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); - /* Clear the whole map. */ - bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + /* Clear the whole map. */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); - /* charset_not matches newline according to a syntax bit. */ - if ((re_opcode_t) b[-2] == charset_not - && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) - SET_LIST_BIT ('\n'); + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); - /* Read in characters and ranges, setting map bits. */ - for (;;) - { - if (p == pend) return REG_EBRACK; + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + int len; + boolean escaped_char = false; - PATFETCH (c); + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); - /* \ might escape characters inside [...] and [^...]. */ - if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') - { - if (p == pend) return REG_EESCAPE; + PATFETCH (c); - PATFETCH (c1); - SET_LIST_BIT (c1); - continue; - } + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); - /* Could be the end of the bracket expression. If it's - not (i.e., when the bracket expression is `[]' so - far), the ']' character bit gets set way below. */ - if (c == ']' && p != p1 + 1) - break; + PATFETCH (c); + escaped_char = true; + } + else + { + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + } - /* Look ahead to see if it's a range when the last thing - was a character class. */ - if (had_char_class && c == '-' && *p != ']') - return REG_ERANGE; - - /* Look ahead to see if it's a range when the last thing - was a character: if this is a hyphen not at the - beginning or the end of a list, then it's the range - operator. */ - if (c == '-' - && !(p - 2 >= pattern && p[-2] == '[') - && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') - && *p != ']') - { - reg_errcode_t ret - = compile_range (&p, pend, translate, syntax, b); - if (ret != REG_NOERROR) return ret; - } - - else if (p[0] == '-' && p[1] != ']') - { /* This handles ranges made up of characters only. */ - reg_errcode_t ret; - - /* Move past the `-'. */ - PATFETCH (c1); - - ret = compile_range (&p, pend, translate, syntax, b); - if (ret != REG_NOERROR) return ret; - } - - /* See if we're at the beginning of a possible character - class. */ - - else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') - { /* Leave room for the null. */ - char str[CHAR_CLASS_MAX_LENGTH + 1]; - - PATFETCH (c); - c1 = 0; - - /* If pattern is `[[:'. */ - if (p == pend) return REG_EBRACK; - - for (;;) - { - PATFETCH (c); - if (c == ':' || c == ']' || p == pend - || c1 == CHAR_CLASS_MAX_LENGTH) - break; - str[c1++] = c; - } - str[c1] = '\0'; - - /* If isn't a word bracketed by `[:' and:`]': - undo the ending character, the letters, and leave - the leading `:' and `[' (but set bits for them). */ - if (c == ':' && *p == ']') - { - int ch; - boolean is_alnum = STREQ (str, "alnum"); - boolean is_alpha = STREQ (str, "alpha"); - boolean is_blank = STREQ (str, "blank"); - boolean is_cntrl = STREQ (str, "cntrl"); - boolean is_digit = STREQ (str, "digit"); - boolean is_graph = STREQ (str, "graph"); - boolean is_lower = STREQ (str, "lower"); - boolean is_print = STREQ (str, "print"); - boolean is_punct = STREQ (str, "punct"); - boolean is_space = STREQ (str, "space"); - boolean is_upper = STREQ (str, "upper"); - boolean is_xdigit = STREQ (str, "xdigit"); - - if (!IS_CHAR_CLASS (str)) return REG_ECTYPE; - - /* Throw away the ] at the end of the character - class. */ - PATFETCH (c); - - if (p == pend) return REG_EBRACK; - - for (ch = 0; ch < 1 << BYTEWIDTH; ch++) - { - if ( (is_alnum && ISALNUM (ch)) - || (is_alpha && ISALPHA (ch)) - || (is_blank && ISBLANK (ch)) - || (is_cntrl && ISCNTRL (ch)) - || (is_digit && ISDIGIT (ch)) - || (is_graph && ISGRAPH (ch)) - || (is_lower && ISLOWER (ch)) - || (is_print && ISPRINT (ch)) - || (is_punct && ISPUNCT (ch)) - || (is_space && ISSPACE (ch)) - || (is_upper && ISUPPER (ch)) - || (is_xdigit && ISXDIGIT (ch))) - SET_LIST_BIT (ch); - } - had_char_class = true; - } - else - { - c1++; - while (c1--) - PATUNFETCH; - SET_LIST_BIT ('['); - SET_LIST_BIT (':'); - had_char_class = false; - } - } - else - { - had_char_class = false; - SET_LIST_BIT (c); - } - } - - /* Discard any (non)matching list bytes that are all 0 at the - end of the map. Decrease the map-length byte too. */ - while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) - b[-1]--; - b += b[-1]; - } - break; + /* If C indicates start of multibyte char, get the + actual character code in C, and set the pattern + pointer P to the next character boundary. */ + if (bufp->multibyte && BASE_LEADING_CODE_P (c)) + { + PATUNFETCH; + c = STRING_CHAR_AND_LENGTH (p, pend - p, len); + p += len; + } + /* What should we do for the character which is + greater than 0x7F, but not BASE_LEADING_CODE_P? + XXX */ + + /* See if we're at the beginning of a possible character + class. */ + + else if (!escaped_char && + syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { + /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (;;) + { + PATFETCH (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and `:]': + undo the ending character, the letters, and + leave the leading `:' and `[' (but set bits for + them). */ + if (c == ':' && *p == ']') + { + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + int translated = TRANSLATE (ch); + /* This was split into 3 if's to + avoid an arbitrary limit in some compiler. */ + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch))) + SET_LIST_BIT (translated); + if ( (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch))) + SET_LIST_BIT (translated); + if ( (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (translated); + } + + /* Repeat the loop. */ + continue; + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + + /* Because the `:' may starts the range, we + can't simply set bit and repeat the loop. + Instead, just set it to C and handle below. */ + c = ':'; + } + } + + if (p < pend && p[0] == '-' && p[1] != ']') + { + + /* Discard the `-'. */ + PATFETCH (c1); + + /* Fetch the character which ends the range. */ + PATFETCH (c1); + if (bufp->multibyte && BASE_LEADING_CODE_P (c1)) + { + PATUNFETCH; + c1 = STRING_CHAR_AND_LENGTH (p, pend - p, len); + p += len; + } + + if (SINGLE_BYTE_CHAR_P (c) + && ! SINGLE_BYTE_CHAR_P (c1)) + { + /* Handle a range such as \177-\377 in multibyte mode. + Split that into two ranges,, + the low one ending at 0237, and the high one + starting at ...040. */ + int c1_base = (c1 & ~0177) | 040; + SET_RANGE_TABLE_WORK_AREA (range_table_work, c, c1); + c1 = 0237; + } + else if (!SAME_CHARSET_P (c, c1)) + FREE_STACK_RETURN (REG_ERANGE); + } + else + /* Range from C to C. */ + c1 = c; + + /* Set the range ... */ + if (SINGLE_BYTE_CHAR_P (c)) + /* ... into bitmap. */ + { + unsigned this_char; + int range_start = c, range_end = c1; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + { + if (syntax & RE_NO_EMPTY_RANGES) + FREE_STACK_RETURN (REG_ERANGE); + /* Else, repeat the loop. */ + } + else + { + for (this_char = range_start; this_char <= range_end; + this_char++) + SET_LIST_BIT (TRANSLATE (this_char)); + } + } + else + /* ... into range table. */ + SET_RANGE_TABLE_WORK_AREA (range_table_work, c, c1); + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + + /* Build real range table from work area. */ + if (RANGE_TABLE_WORK_USED (range_table_work)) + { + int i; + int used = RANGE_TABLE_WORK_USED (range_table_work); + + /* Allocate space for COUNT + RANGE_TABLE. Needs two + bytes for COUNT and three bytes for each character. */ + GET_BUFFER_SPACE (2 + used * 3); + + /* Indicate the existence of range table. */ + laststart[1] |= 0x80; + + STORE_NUMBER_AND_INCR (b, used / 2); + for (i = 0; i < used; i++) + STORE_CHARACTER_AND_INCR + (b, RANGE_TABLE_WORK_ELT (range_table_work, i)); + } + } + break; case '(': - if (syntax & RE_NO_BK_PARENS) - goto handle_open; - else - goto normal_char; + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; - case ')': - if (syntax & RE_NO_BK_PARENS) - goto handle_close; - else - goto normal_char; + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; - case '\n': - if (syntax & RE_NEWLINE_ALT) - goto handle_alt; - else - goto normal_char; + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; case '|': - if (syntax & RE_NO_BK_VBAR) - goto handle_alt; - else - goto normal_char; + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; - case '{': - if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) - goto handle_interval; - else - goto normal_char; + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; - case '\\': - if (p == pend) return REG_EESCAPE; + case '\\': + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); - /* Do not translate the character after the \, so that we can - distinguish, e.g., \B from \b, even if we normally would - translate, e.g., B to b. */ - PATFETCH_RAW (c); + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); - switch (c) - { - case '(': - if (syntax & RE_NO_BK_PARENS) - goto normal_backslash; + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; - handle_open: - bufp->re_nsub++; - regnum++; + handle_open: + bufp->re_nsub++; + regnum++; - if (COMPILE_STACK_FULL) - { - RETALLOC (compile_stack.stack, compile_stack.size << 1, - compile_stack_elt_t); - if (compile_stack.stack == NULL) return REG_ESPACE; + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; - compile_stack.size <<= 1; - } + compile_stack.size <<= 1; + } - /* These are the values to restore when we hit end of this - group. They are all relative offsets, so that if the - whole pattern moves because of realloc, they will still - be valid. */ - COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; - COMPILE_STACK_TOP.fixup_alt_jump - = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; - COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; - COMPILE_STACK_TOP.regnum = regnum; - - /* We will eventually replace the 0 with the number of - groups inner to this one. But do not push a - start_memory for groups beyond the last one we can - represent in the compiled pattern. */ - if (regnum <= MAX_REGNUM) - { - COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; - BUF_PUSH_3 (start_memory, regnum, 0); - } + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } - compile_stack.avail++; + compile_stack.avail++; - fixup_alt_jump = 0; - laststart = 0; - begalt = b; + fixup_alt_jump = 0; + laststart = 0; + begalt = b; /* If we've reached MAX_REGNUM groups, then this open won't actually generate any code, so we'll have to clear pending_exact explicitly. */ pending_exact = 0; - break; + break; - case ')': - if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; - if (COMPILE_STACK_EMPTY) - if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) - goto normal_backslash; - else - return REG_ERPAREN; + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + FREE_STACK_RETURN (REG_ERPAREN); - handle_close: - if (fixup_alt_jump) - { /* Push a dummy failure point at the end of the - alternative for a possible future - `pop_failure_jump' to pop. See comments at - `push_dummy_failure' in `re_match_2'. */ - BUF_PUSH (push_dummy_failure); + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); - /* We allocated space for this jump when we assigned - to `fixup_alt_jump', in the `handle_alt' case below. */ - STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); - } + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + FREE_STACK_RETURN (REG_ERPAREN); - /* See similar code for backslashed left paren above. */ - if (COMPILE_STACK_EMPTY) - if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) - goto normal_char; - else - return REG_ERPAREN; - - /* Since we just checked for an empty stack above, this - ``can't happen''. */ - assert (compile_stack.avail != 0); - { - /* We don't just want to restore into `regnum', because - later groups should continue to be numbered higher, - as in `(ab)c(de)' -- the second group is #2. */ - regnum_t this_group_regnum; - - compile_stack.avail--; - begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; - fixup_alt_jump - = COMPILE_STACK_TOP.fixup_alt_jump - ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 - : 0; - laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; - this_group_regnum = COMPILE_STACK_TOP.regnum; + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; /* If we've reached MAX_REGNUM groups, then this open won't actually generate any code, so we'll have to clear pending_exact explicitly. */ pending_exact = 0; - /* We're at the end of the group, so now we know how many - groups were inside this one. */ - if (this_group_regnum <= MAX_REGNUM) - { - unsigned char *inner_group_loc - = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; - - *inner_group_loc = regnum - this_group_regnum; - BUF_PUSH_3 (stop_memory, this_group_regnum, - regnum - this_group_regnum); - } - } - break; - - - case '|': /* `\|'. */ - if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) - goto normal_backslash; - handle_alt: - if (syntax & RE_LIMITED_OPS) - goto normal_char; - - /* Insert before the previous alternative a jump which - jumps to this alternative if the former fails. */ - GET_BUFFER_SPACE (3); - INSERT_JUMP (on_failure_jump, begalt, b + 6); - pending_exact = 0; - b += 3; - - /* The alternative before this one has a jump after it - which gets executed if it gets matched. Adjust that - jump so it will jump to this alternative's analogous - jump (put in below, which in turn will jump to the next - (if any) alternative's such jump, etc.). The last such - jump jumps to the correct final destination. A picture: - _____ _____ - | | | | - | v | v - a | b | c - - If we are at `b', then fixup_alt_jump right now points to a - three-byte space after `a'. We'll put in the jump, set - fixup_alt_jump to right after `b', and leave behind three - bytes which we'll fill in when we get to after `c'. */ - - if (fixup_alt_jump) - STORE_JUMP (jump_past_alt, fixup_alt_jump, b); - - /* Mark and leave space for a jump after this alternative, - to be filled in later either by next alternative or - when know we're at the end of a series of alternatives. */ - fixup_alt_jump = b; - GET_BUFFER_SPACE (3); - b += 3; - - laststart = 0; - begalt = b; - break; - - - case '{': - /* If \{ is a literal. */ - if (!(syntax & RE_INTERVALS) - /* If we're at `\{' and it's not the open-interval - operator. */ - || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) - || (p - 2 == pattern && p == pend)) - goto normal_backslash; - - handle_interval: - { - /* If got here, then the syntax allows intervals. */ - - /* At least (most) this many matches must be made. */ - int lower_bound = -1, upper_bound = -1; - - beg_interval = p - 1; - - if (p == pend) - { - if (syntax & RE_NO_BK_BRACES) - goto unfetch_interval; - else - return REG_EBRACE; - } - - GET_UNSIGNED_NUMBER (lower_bound); - - if (c == ',') - { - GET_UNSIGNED_NUMBER (upper_bound); - if (upper_bound < 0) upper_bound = RE_DUP_MAX; - } - else - /* Interval such as `{1}' => match exactly once. */ - upper_bound = lower_bound; - - if (lower_bound < 0 || upper_bound > RE_DUP_MAX - || lower_bound > upper_bound) - { - if (syntax & RE_NO_BK_BRACES) - goto unfetch_interval; - else - return REG_BADBR; - } - - if (!(syntax & RE_NO_BK_BRACES)) - { - if (c != '\\') return REG_EBRACE; - - PATFETCH (c); - } - - if (c != '}') - { - if (syntax & RE_NO_BK_BRACES) - goto unfetch_interval; - else - return REG_BADBR; - } - - /* We just parsed a valid interval. */ - - /* If it's invalid to have no preceding re. */ - if (!laststart) - { - if (syntax & RE_CONTEXT_INVALID_OPS) - return REG_BADRPT; - else if (syntax & RE_CONTEXT_INDEP_OPS) - laststart = b; - else - goto unfetch_interval; - } - - /* If the upper bound is zero, don't want to succeed at - all; jump from `laststart' to `b + 3', which will be - the end of the buffer after we insert the jump. */ - if (upper_bound == 0) - { - GET_BUFFER_SPACE (3); - INSERT_JUMP (jump, laststart, b + 3); - b += 3; - } - - /* Otherwise, we have a nontrivial interval. When - we're all done, the pattern will look like: - set_number_at <jump count> <upper bound> - set_number_at <succeed_n count> <lower bound> - succeed_n <after jump addr> <succeed_n count> - <body of loop> - jump_n <succeed_n addr> <jump count> - (The upper bound and `jump_n' are omitted if - `upper_bound' is 1, though.) */ - else - { /* If the upper bound is > 1, we need to insert - more at the end of the loop. */ - unsigned nbytes = 10 + (upper_bound > 1) * 10; - - GET_BUFFER_SPACE (nbytes); - - /* Initialize lower bound of the `succeed_n', even - though it will be set during matching by its - attendant `set_number_at' (inserted next), - because `re_compile_fastmap' needs to know. - Jump to the `jump_n' we might insert below. */ - INSERT_JUMP2 (succeed_n, laststart, - b + 5 + (upper_bound > 1) * 5, - lower_bound); - b += 5; - - /* Code to initialize the lower bound. Insert - before the `succeed_n'. The `5' is the last two - bytes of this `set_number_at', plus 3 bytes of - the following `succeed_n'. */ - insert_op2 (set_number_at, laststart, 5, lower_bound, b); - b += 5; - - if (upper_bound > 1) - { /* More than one repetition is allowed, so - append a backward jump to the `succeed_n' - that starts this interval. - - When we've reached this during matching, - we'll have matched the interval once, so - jump back only `upper_bound - 1' times. */ - STORE_JUMP2 (jump_n, b, laststart + 5, - upper_bound - 1); - b += 5; - - /* The location we want to set is the second - parameter of the `jump_n'; that is `b-2' as - an absolute address. `laststart' will be - the `set_number_at' we're about to insert; - `laststart+3' the number to set, the source - for the relative address. But we are - inserting into the middle of the pattern -- - so everything is getting moved up by 5. - Conclusion: (b - 2) - (laststart + 3) + 5, - i.e., b - laststart. - - We insert this at the beginning of the loop - so that if we fail during matching, we'll - reinitialize the bounds. */ - insert_op2 (set_number_at, laststart, b - laststart, - upper_bound - 1, b); - b += 5; - } - } - pending_exact = 0; - beg_interval = NULL; - } - break; - - unfetch_interval: - /* If an invalid interval, match the characters as literals. */ - assert (beg_interval); - p = beg_interval; - beg_interval = NULL; - - /* normal_char and normal_backslash need `c'. */ - PATFETCH (c); - - if (!(syntax & RE_NO_BK_BRACES)) - { - if (p > pattern && p[-1] == '\\') - goto normal_backslash; - } - goto normal_char; + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_EBRACE); + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') FREE_STACK_RETURN (REG_EBRACE); + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at <jump count> <upper bound> + set_number_at <succeed_n count> <lower bound> + succeed_n <after jump addr> <succeed_n count> + <body of loop> + jump_n <succeed_n addr> <jump count> + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; #ifdef emacs - /* There is no way to specify the before_dot and after_dot - operators. rms says this is ok. --karl */ - case '=': - BUF_PUSH (at_dot); - break; - - case 's': - laststart = b; - PATFETCH (c); - BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); - break; - - case 'S': - laststart = b; - PATFETCH (c); - BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); - break; + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; + + case 'c': + laststart = b; + PATFETCH_RAW (c); + BUF_PUSH_2 (categoryspec, c); + break; + + case 'C': + laststart = b; + PATFETCH_RAW (c); + BUF_PUSH_2 (notcategoryspec, c); + break; #endif /* emacs */ - case 'w': - laststart = b; - BUF_PUSH (wordchar); - break; - + case 'w': + laststart = b; + BUF_PUSH (wordchar); + break; - case 'W': - laststart = b; - BUF_PUSH (notwordchar); - break; + case 'W': + laststart = b; + BUF_PUSH (notwordchar); + break; - case '<': - BUF_PUSH (wordbeg); - break; - case '>': - BUF_PUSH (wordend); - break; + case '<': + BUF_PUSH (wordbeg); + break; - case 'b': - BUF_PUSH (wordbound); - break; + case '>': + BUF_PUSH (wordend); + break; - case 'B': - BUF_PUSH (notwordbound); - break; + case 'b': + BUF_PUSH (wordbound); + break; - case '`': - BUF_PUSH (begbuf); - break; + case 'B': + BUF_PUSH (notwordbound); + break; - case '\'': - BUF_PUSH (endbuf); - break; + case '`': + BUF_PUSH (begbuf); + break; - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - if (syntax & RE_NO_BK_REFS) - goto normal_char; + case '\'': + BUF_PUSH (endbuf); + break; - c1 = c - '0'; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; - if (c1 > regnum) - return REG_ESUBREG; + c1 = c - '0'; - /* Can't back reference to a subexpression if inside of it. */ - if (group_in_compile_stack (compile_stack, c1)) - goto normal_char; + if (c1 > regnum) + FREE_STACK_RETURN (REG_ESUBREG); - laststart = b; - BUF_PUSH_2 (duplicate, c1); - break; + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, c1)) + goto normal_char; + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; - case '+': - case '?': - if (syntax & RE_BK_PLUS_QM) - goto handle_plus; - else - goto normal_backslash; - default: - normal_backslash: - /* You might think it would be useful for \ to mean - not to translate; but if we don't translate it - it will never match anything. */ - c = TRANSLATE (c); - goto normal_char; - } - break; + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; default: - /* Expects the character in `c'. */ + /* Expects the character in `c'. */ normal_char: + p1 = p - 1; /* P1 points the head of C. */ +#ifdef emacs + if (bufp->multibyte) + { + c = STRING_CHAR (p1, pend - p1); + c = TRANSLATE (c); + /* Set P to the next character boundary. */ + p += MULTIBYTE_FORM_LENGTH (p1, pend - p1) - 1; + } +#endif /* If no exactn currently being built. */ - if (!pending_exact + if (!pending_exact - /* If last exactn not at current position. */ - || pending_exact + *pending_exact + 1 != b + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b - /* We have only one byte following the exactn for the count. */ - || *pending_exact == (1 << BYTEWIDTH) - 1 + /* We have only one byte following the exactn for the count. */ + || *pending_exact >= (1 << BYTEWIDTH) - (p - p1) - /* If followed by a repetition operator. */ - || *p == '*' || *p == '^' + /* If followed by a repetition operator. */ + || (p != pend && (*p == '*' || *p == '^')) || ((syntax & RE_BK_PLUS_QM) - ? *p == '\\' && (p[1] == '+' || p[1] == '?') - : (*p == '+' || *p == '?')) + ? p + 1 < pend && *p == '\\' && (p[1] == '+' || p[1] == '?') + : p != pend && (*p == '+' || *p == '?')) || ((syntax & RE_INTERVALS) - && ((syntax & RE_NO_BK_BRACES) - ? *p == '{' - : (p[0] == '\\' && p[1] == '{')))) + && ((syntax & RE_NO_BK_BRACES) + ? p != pend && *p == '{' + : p + 1 < pend && p[0] == '\\' && p[1] == '{'))) { /* Start building a new exactn. */ - laststart = b; + laststart = b; BUF_PUSH_2 (exactn, 0); pending_exact = b - 1; - } + } - BUF_PUSH (c); - (*pending_exact)++; +#ifdef emacs + if (! SINGLE_BYTE_CHAR_P (c)) + { + unsigned char work[4], *str; + int i = CHAR_STRING (c, work, str); + int j; + for (j = 0; j < i; j++) + { + BUF_PUSH (str[j]); + (*pending_exact)++; + } + } + else +#endif + { + BUF_PUSH (c); + (*pending_exact)++; + } break; - } /* switch (c) */ + } /* switch (c) */ } /* while p != pend */ @@ -2044,7 +2966,12 @@ regex_compile (pattern, size, syntax, bufp) STORE_JUMP (jump_past_alt, fixup_alt_jump, b); if (!COMPILE_STACK_EMPTY) - return REG_EPAREN; + FREE_STACK_RETURN (REG_EPAREN); + + /* If we don't want backtracking, force success + the first time we reach the end of the compiled pattern. */ + if (syntax & RE_NO_POSIX_BACKTRACKING) + BUF_PUSH (succeed); free (compile_stack.stack); @@ -2054,17 +2981,55 @@ regex_compile (pattern, size, syntax, bufp) #ifdef DEBUG if (debug) { - DEBUG_PRINT1 ("\nCompiled pattern: "); + DEBUG_PRINT1 ("\nCompiled pattern: \n"); print_compiled_pattern (bufp); } #endif /* DEBUG */ +#ifndef MATCH_MAY_ALLOCATE + /* Initialize the failure stack to the largest possible stack. This + isn't necessary unless we're trying to avoid calling alloca in + the search and match routines. */ + { + int num_regs = bufp->re_nsub + 1; + + if (fail_stack.size < re_max_failures * TYPICAL_FAILURE_SIZE) + { + fail_stack.size = re_max_failures * TYPICAL_FAILURE_SIZE; + +#ifdef emacs + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) xmalloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) xrealloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +#else /* not emacs */ + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) malloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) realloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +#endif /* not emacs */ + } + + regex_grow_registers (num_regs); + } +#endif /* not MATCH_MAY_ALLOCATE */ + return REG_NOERROR; } /* regex_compile */ /* Subroutines for `regex_compile'. */ -/* Store OP at LOC followed by two-byte integer parameter ARG. */ +/* Store OP at LOC followed by two-byte integer parameter ARG. */ static void store_op1 (op, loc, arg) @@ -2145,7 +3110,7 @@ at_begline_loc_p (pattern, p, syntax) return /* After a subexpression? */ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) - /* After an alternative? */ + /* After an alternative? */ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); } @@ -2160,19 +3125,19 @@ at_endline_loc_p (p, pend, syntax) { const char *next = p; boolean next_backslash = *next == '\\'; - const char *next_next = p + 1 < pend ? p + 1 : NULL; - + const char *next_next = p + 1 < pend ? p + 1 : 0; + return /* Before a subexpression? */ (syntax & RE_NO_BK_PARENS ? *next == ')' - : next_backslash && next_next && *next_next == ')') + : next_backslash && next_next && *next_next == ')') /* Before an alternative? */ || (syntax & RE_NO_BK_VBAR ? *next == '|' - : next_backslash && next_next && *next_next == '|'); + : next_backslash && next_next && *next_next == '|'); } -/* Returns true if REGNUM is in one of COMPILE_STACK's elements and +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and false if it's not. */ static boolean @@ -2182,378 +3147,14 @@ group_in_compile_stack (compile_stack, regnum) { int this_element; - for (this_element = compile_stack.avail - 1; - this_element >= 0; + for (this_element = compile_stack.avail - 1; + this_element >= 0; this_element--) if (compile_stack.stack[this_element].regnum == regnum) return true; return false; } - - -/* Read the ending character of a range (in a bracket expression) from the - uncompiled pattern *P_PTR (which ends at PEND). We assume the - starting character is in `P[-2]'. (`P[-1]' is the character `-'.) - Then we set the translation of all bits between the starting and - ending characters (inclusive) in the compiled pattern B. - - Return an error code. - - We use these short variable names so we can use the same macros as - `regex_compile' itself. */ - -static reg_errcode_t -compile_range (p_ptr, pend, translate, syntax, b) - const char **p_ptr, *pend; - char *translate; - reg_syntax_t syntax; - unsigned char *b; -{ - unsigned this_char; - - const char *p = *p_ptr; - int range_start, range_end; - - if (p == pend) - return REG_ERANGE; - - /* Even though the pattern is a signed `char *', we need to fetch - with unsigned char *'s; if the high bit of the pattern character - is set, the range endpoints will be negative if we fetch using a - signed char *. - - We also want to fetch the endpoints without translating them; the - appropriate translation is done in the bit-setting loop below. */ - range_start = ((unsigned char *) p)[-2]; - range_end = ((unsigned char *) p)[0]; - - /* Have to increment the pointer into the pattern string, so the - caller isn't still at the ending character. */ - (*p_ptr)++; - - /* If the start is after the end, the range is empty. */ - if (range_start > range_end) - return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; - - /* Here we see why `this_char' has to be larger than an `unsigned - char' -- the range is inclusive, so if `range_end' == 0xff - (assuming 8-bit characters), we would otherwise go into an infinite - loop, since all characters <= 0xff. */ - for (this_char = range_start; this_char <= range_end; this_char++) - { - SET_LIST_BIT (TRANSLATE (this_char)); - } - - return REG_NOERROR; -} - -/* Failure stack declarations and macros; both re_compile_fastmap and - re_match_2 use a failure stack. These have to be macros because of - REGEX_ALLOCATE. */ - - -/* Number of failure points for which to initially allocate space - when matching. If this number is exceeded, we allocate more - space, so it is not a hard limit. */ -#ifndef INIT_FAILURE_ALLOC -#define INIT_FAILURE_ALLOC 5 -#endif - -/* Roughly the maximum number of failure points on the stack. Would be - exactly that if always used MAX_FAILURE_SPACE each time we failed. - This is a variable only so users of regex can assign to it; we never - change it ourselves. */ -int re_max_failures = 2000; - -union fail_stack_elt -{ - unsigned char *pointer; - int integer; -}; - -typedef union fail_stack_elt fail_stack_elt_t; - -typedef struct -{ - fail_stack_elt_t *stack; - unsigned size; - unsigned avail; /* Offset of next open position. */ -} fail_stack_type; - -#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) -#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) -#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) -#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail]) - - -/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */ - -#define INIT_FAIL_STACK() \ - do { \ - fail_stack.stack = (fail_stack_elt_t *) \ - REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ - \ - if (fail_stack.stack == NULL) \ - return -2; \ - \ - fail_stack.size = INIT_FAILURE_ALLOC; \ - fail_stack.avail = 0; \ - } while (0) - - -/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. - - Return 1 if succeeds, and 0 if either ran out of memory - allocating space for it or it was already too large. - - REGEX_REALLOCATE requires `destination' be declared. */ - -#define DOUBLE_FAIL_STACK(fail_stack) \ - ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \ - ? 0 \ - : ((fail_stack).stack = (fail_stack_elt_t *) \ - REGEX_REALLOCATE ((fail_stack).stack, \ - (fail_stack).size * sizeof (fail_stack_elt_t), \ - ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ - \ - (fail_stack).stack == NULL \ - ? 0 \ - : ((fail_stack).size <<= 1, \ - 1))) - - -/* Push PATTERN_OP on FAIL_STACK. - - Return 1 if was able to do so and 0 if ran out of memory allocating - space to do so. */ -#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ - ((FAIL_STACK_FULL () \ - && !DOUBLE_FAIL_STACK (FAIL_STACK)) \ - ? 0 \ - : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ - 1)) - -/* Push a pointer value onto the failure stack. - Assumes the variable `fail_stack'. Probably should only - be called from within `PUSH_FAILURE_POINT'. */ -#define PUSH_FAILURE_POINTER(item) \ - fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) - -/* This pushes an integer-valued item onto the failure stack. - Assumes the variable `fail_stack'. Probably should only - be called from within `PUSH_FAILURE_POINT'. */ -#define PUSH_FAILURE_INT(item) \ - fail_stack.stack[fail_stack.avail++].integer = (item) - -/* Push a fail_stack_elt_t value onto the failure stack. - Assumes the variable `fail_stack'. Probably should only - be called from within `PUSH_FAILURE_POINT'. */ -#define PUSH_FAILURE_ELT(item) \ - fail_stack.stack[fail_stack.avail++] = (item) - -/* These three POP... operations complement the three PUSH... operations. - All assume that `fail_stack' is nonempty. */ -#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer -#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer -#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] - -/* Used to omit pushing failure point id's when we're not debugging. */ -#ifdef DEBUG -#define DEBUG_PUSH PUSH_FAILURE_INT -#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () -#else -#define DEBUG_PUSH(item) -#define DEBUG_POP(item_addr) -#endif - - -/* Push the information about the state we will need - if we ever fail back to it. - - Requires variables fail_stack, regstart, regend, reg_info, and - num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be - declared. - - Does `return FAILURE_CODE' if runs out of memory. */ - -#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ - do { \ - char *destination; \ - /* Must be int, so when we don't save any registers, the arithmetic \ - of 0 + -1 isn't done as unsigned. */ \ - int this_reg; \ - \ - DEBUG_STATEMENT (failure_id++); \ - DEBUG_STATEMENT (nfailure_points_pushed++); \ - DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ - DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ - DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ - \ - DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ - DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ - \ - /* Ensure we have enough space allocated for what we will push. */ \ - while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ - { \ - if (!DOUBLE_FAIL_STACK (fail_stack)) \ - return failure_code; \ - \ - DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ - (fail_stack).size); \ - DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ - } \ - \ - /* Push the info, starting with the registers. */ \ - DEBUG_PRINT1 ("\n"); \ - \ - if (1) \ - for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ - this_reg++) \ - { \ - DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ - DEBUG_STATEMENT (num_regs_pushed++); \ - \ - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ - PUSH_FAILURE_POINTER (regstart[this_reg]); \ - \ - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ - PUSH_FAILURE_POINTER (regend[this_reg]); \ - \ - DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ - DEBUG_PRINT2 (" match_null=%d", \ - REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ - DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ - DEBUG_PRINT2 (" matched_something=%d", \ - MATCHED_SOMETHING (reg_info[this_reg])); \ - DEBUG_PRINT2 (" ever_matched=%d", \ - EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ - DEBUG_PRINT1 ("\n"); \ - PUSH_FAILURE_ELT (reg_info[this_reg].word); \ - } \ - \ - DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ - PUSH_FAILURE_INT (lowest_active_reg); \ - \ - DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ - PUSH_FAILURE_INT (highest_active_reg); \ - \ - DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ - DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ - PUSH_FAILURE_POINTER (pattern_place); \ - \ - DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ - DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ - size2); \ - DEBUG_PRINT1 ("'\n"); \ - PUSH_FAILURE_POINTER (string_place); \ - \ - DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ - DEBUG_PUSH (failure_id); \ - } while (0) - -/* This is the number of items that are pushed and popped on the stack - for each register. */ -#define NUM_REG_ITEMS 3 - -/* Individual items aside from the registers. */ -#ifdef DEBUG -#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ -#else -#define NUM_NONREG_ITEMS 4 -#endif - -/* We push at most this many items on the stack. */ -#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS) - -/* We actually push this many items. */ -#define NUM_FAILURE_ITEMS \ - ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \ - + NUM_NONREG_ITEMS) - -/* How many items can still be added to the stack without overflowing it. */ -#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) - - -/* Pops what PUSH_FAIL_STACK pushes. - - We restore into the parameters, all of which should be lvalues: - STR -- the saved data position. - PAT -- the saved pattern position. - LOW_REG, HIGH_REG -- the highest and lowest active registers. - REGSTART, REGEND -- arrays of string positions. - REG_INFO -- array of information about each subexpression. - - Also assumes the variables `fail_stack' and (if debugging), `bufp', - `pend', `string1', `size1', `string2', and `size2'. */ - -#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ -{ \ - DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ - int this_reg; \ - const unsigned char *string_temp; \ - \ - assert (!FAIL_STACK_EMPTY ()); \ - \ - /* Remove failure points and point to how many regs pushed. */ \ - DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ - DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ - DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ - \ - assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ - \ - DEBUG_POP (&failure_id); \ - DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ - \ - /* If the saved string location is NULL, it came from an \ - on_failure_keep_string_jump opcode, and we want to throw away the \ - saved NULL, thus retaining our current position in the string. */ \ - string_temp = POP_FAILURE_POINTER (); \ - if (string_temp != NULL) \ - str = (const char *) string_temp; \ - \ - DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ - DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ - DEBUG_PRINT1 ("'\n"); \ - \ - pat = (unsigned char *) POP_FAILURE_POINTER (); \ - DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ - DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ - \ - /* Restore register info. */ \ - high_reg = (unsigned) POP_FAILURE_INT (); \ - DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ - \ - low_reg = (unsigned) POP_FAILURE_INT (); \ - DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ - \ - if (1) \ - for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ - { \ - DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ - \ - reg_info[this_reg].word = POP_FAILURE_ELT (); \ - DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ - \ - regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ - DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ - \ - regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ - DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ - } \ - else \ - { \ - for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ - { \ - reg_info[this_reg].word.integer = 0; \ - regend[this_reg] = 0; \ - regstart[this_reg] = 0; \ - } \ - highest_active_reg = high_reg; \ - } \ - \ - DEBUG_STATEMENT (nfailure_points_popped++); \ -} /* POP_FAILURE_POINT */ /* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible @@ -2572,8 +3173,10 @@ int re_compile_fastmap (bufp) struct re_pattern_buffer *bufp; { - int j, k; + int i, j, k; +#ifdef MATCH_MAY_ALLOCATE fail_stack_type fail_stack; +#endif #ifndef REGEX_MALLOC char *destination; #endif @@ -2586,8 +3189,12 @@ re_compile_fastmap (bufp) unsigned char *p = pattern; register unsigned char *pend = pattern + size; + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; + /* Assume that each path through the pattern can be null until - proven otherwise. We set this false at the bottom of switch + proven otherwise. We set this false at the bottom of switch statement, to which we get only if a particular path doesn't match the empty string. */ boolean path_can_be_null = true; @@ -2595,69 +3202,81 @@ re_compile_fastmap (bufp) /* We aren't doing a `succeed_n' to begin with. */ boolean succeed_n_p = false; + /* If all elements for base leading-codes in fastmap is set, this + flag is set true. */ + boolean match_any_multibyte_characters = false; + + /* Maximum code of simple (single byte) character. */ + int simple_char_max; + assert (fastmap != NULL && p != NULL); INIT_FAIL_STACK (); - bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ bufp->fastmap_accurate = 1; /* It will be when we're done. */ bufp->can_be_null = 0; - while (p != pend || !FAIL_STACK_EMPTY ()) + while (1) { - if (p == pend) - { - bufp->can_be_null |= path_can_be_null; - - /* Reset for next path. */ - path_can_be_null = true; - - p = fail_stack.stack[--fail_stack.avail].pointer; + if (p == pend || *p == succeed) + { + /* We have reached the (effective) end of pattern. */ + if (!FAIL_STACK_EMPTY ()) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail].pointer; + + continue; + } + else + break; } - /* We should never be about to go beyond the end of the pattern. */ + /* We should never be about to go beyond the end of the pattern. */ assert (p < pend); - -#ifdef SWITCH_ENUM_BUG - switch ((int) ((re_opcode_t) *p++)) -#else - switch ((re_opcode_t) *p++) -#endif + + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) { - /* I guess the idea here is to simply not bother with a fastmap - if a backreference is used, since it's too hard to figure out - the fastmap for the corresponding group. Setting - `can_be_null' stops `re_search_2' from using the fastmap, so - that is all we do. */ + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ case duplicate: bufp->can_be_null = 1; - return 0; + goto done; /* Following are the cases which match a character. These end - with `break'. */ + with `break'. */ case exactn: - fastmap[p[1]] = 1; + fastmap[p[1]] = 1; break; - case charset: - for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) +#ifndef emacs + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) - fastmap[j] = 1; + fastmap[j] = 1; break; case charset_not: /* Chars beyond end of map must be allowed. */ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) - fastmap[j] = 1; + fastmap[j] = 1; for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) - fastmap[j] = 1; - break; + fastmap[j] = 1; + break; case wordchar: @@ -2672,171 +3291,327 @@ re_compile_fastmap (bufp) if (SYNTAX (j) != Sword) fastmap[j] = 1; break; +#else /* emacs */ + case charset: + for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH - 1, p++; + j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; + if (CHARSET_RANGE_TABLE_EXISTS_P (&p[-2]) + && match_any_multibyte_characters == false) + { + /* Set fastmap[I] 1 where I is a base leading code of each + multibyte character in the range table. */ + int c, count; - case anychar: - /* `.' matches anything ... */ - for (j = 0; j < (1 << BYTEWIDTH); j++) - fastmap[j] = 1; + /* Make P points the range table. */ + p += CHARSET_BITMAP_SIZE (&p[-2]); + + /* Extract the number of ranges in range table into + COUNT. */ + EXTRACT_NUMBER_AND_INCR (count, p); + for (; count > 0; count--, p += 2 * 3) /* XXX */ + { + /* Extract the start of each range. */ + EXTRACT_CHARACTER (c, p); + j = CHAR_CHARSET (c); + fastmap[CHARSET_LEADING_CODE_BASE (j)] = 1; + } + } + break; - /* ... except perhaps newline. */ - if (!(bufp->syntax & RE_DOT_NEWLINE)) - fastmap['\n'] = 0; - /* Return if we have already set `can_be_null'; if we have, - then the fastmap is irrelevant. Something's wrong here. */ - else if (bufp->can_be_null) - return 0; + case charset_not: + /* Chars beyond end of bitmap are possible matches. + All the single-byte codes can occur in multibyte buffers. + So any that are not listed in the charset + are possible matches, even in multibyte buffers. */ + simple_char_max = (1 << BYTEWIDTH); + for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH; + j < simple_char_max; j++) + fastmap[j] = 1; + + for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH - 1, p++; + j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; - /* Otherwise, have to check alternative paths. */ + if (bufp->multibyte) + /* Any character set can possibly contain a character + which doesn't match the specified set of characters. */ + { + set_fastmap_for_multibyte_characters: + if (match_any_multibyte_characters == false) + { + for (j = 0x80; j < 0xA0; j++) /* XXX */ + if (BASE_LEADING_CODE_P (j)) + fastmap[j] = 1; + match_any_multibyte_characters = true; + } + } break; + case wordchar: + /* All the single-byte codes can occur in multibyte buffers, + and they may have word syntax. So do consider them. */ + simple_char_max = (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose syntax is `Sword'. */ + goto set_fastmap_for_multibyte_characters; + break; + + + case notwordchar: + /* All the single-byte codes can occur in multibyte buffers, + and they may not have word syntax. So do consider them. */ + simple_char_max = (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose syntax is not `Sword'. */ + goto set_fastmap_for_multibyte_characters; + break; +#endif + + case anychar: + { + int fastmap_newline = fastmap['\n']; + + /* `.' matches anything, except perhaps newline. + Even in a multibyte buffer, it should match any + conceivable byte value for the fastmap. */ + if (bufp->multibyte) + match_any_multibyte_characters = true; + + simple_char_max = (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = fastmap_newline; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + goto done; + + /* Otherwise, have to check alternative paths. */ + break; + } + #ifdef emacs - case syntaxspec: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case notsyntaxspec: + case syntaxspec: + /* This match depends on text properties. These end with + aborting optimizations. */ + bufp->can_be_null = 1; + goto done; +#if 0 k = *p++; - for (j = 0; j < (1 << BYTEWIDTH); j++) + simple_char_max = bufp->multibyte ? 0x80 : (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) == (enum syntaxcode) k) fastmap[j] = 1; - break; + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose syntax is K. */ + goto set_fastmap_for_multibyte_characters; + break; case notsyntaxspec: k = *p++; - for (j = 0; j < (1 << BYTEWIDTH); j++) + simple_char_max = bufp->multibyte ? 0x80 : (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) != (enum syntaxcode) k) fastmap[j] = 1; + + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose syntax is not K. */ + goto set_fastmap_for_multibyte_characters; break; +#endif + + case categoryspec: + k = *p++; + simple_char_max = (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) + if (CHAR_HAS_CATEGORY (j, k)) + fastmap[j] = 1; + + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose category is K. */ + goto set_fastmap_for_multibyte_characters; + break; + + + case notcategoryspec: + k = *p++; + simple_char_max = (1 << BYTEWIDTH); + for (j = 0; j < simple_char_max; j++) + if (!CHAR_HAS_CATEGORY (j, k)) + fastmap[j] = 1; + + if (bufp->multibyte) + /* Any character set can possibly contain a character + whose category is not K. */ + goto set_fastmap_for_multibyte_characters; + break; /* All cases after this match the empty string. These end with - `continue'. */ + `continue'. */ case before_dot: case at_dot: case after_dot: - continue; -#endif /* not emacs */ + continue; +#endif /* emacs */ - case no_op: - case begline: - case endline: + case no_op: + case begline: + case endline: case begbuf: case endbuf: +#ifndef emacs case wordbound: case notwordbound: case wordbeg: case wordend: - case push_dummy_failure: - continue; +#endif + case push_dummy_failure: + continue; case jump_n: - case pop_failure_jump: + case pop_failure_jump: case maybe_pop_jump: case jump: - case jump_past_alt: + case jump_past_alt: case dummy_failure_jump: - EXTRACT_NUMBER_AND_INCR (j, p); + EXTRACT_NUMBER_AND_INCR (j, p); p += j; if (j > 0) continue; - /* Jump backward implies we just went through the body of a - loop and matched nothing. Opcode jumped to should be - `on_failure_jump' or `succeed_n'. Just treat it like an - ordinary jump. For a * loop, it has pushed its failure - point already; if so, discard that as redundant. */ - if ((re_opcode_t) *p != on_failure_jump + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump && (re_opcode_t) *p != succeed_n) continue; - p++; - EXTRACT_NUMBER_AND_INCR (j, p); - p += j; + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; - /* If what's on the stack is where we are now, pop it. */ - if (!FAIL_STACK_EMPTY () + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () && fail_stack.stack[fail_stack.avail - 1].pointer == p) - fail_stack.avail--; + fail_stack.avail--; - continue; + continue; - case on_failure_jump: - case on_failure_keep_string_jump: + case on_failure_jump: + case on_failure_keep_string_jump: handle_on_failure_jump: - EXTRACT_NUMBER_AND_INCR (j, p); - - /* For some patterns, e.g., `(a?)?', `p+j' here points to the - end of the pattern. We don't want to push such a point, - since when we restore it above, entering the switch will - increment `p' past the end of the pattern. We don't need - to push such a point since we obviously won't find any more - fastmap entries beyond `pend'. Such a pattern can match - the null string, though. */ - if (p + j < pend) - { - if (!PUSH_PATTERN_OP (p + j, fail_stack)) - return -2; - } - else - bufp->can_be_null = 1; + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + { + RESET_FAIL_STACK (); + return -2; + } + } + else + bufp->can_be_null = 1; - if (succeed_n_p) - { - EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ - succeed_n_p = false; + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; } - continue; + continue; case succeed_n: - /* Get to the number of times to succeed. */ - p += 2; + /* Get to the number of times to succeed. */ + p += 2; - /* Increment p past the n for when k != 0. */ - EXTRACT_NUMBER_AND_INCR (k, p); - if (k == 0) + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) { - p -= 4; - succeed_n_p = true; /* Spaghetti code alert. */ - goto handle_on_failure_jump; - } - continue; + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; case set_number_at: - p += 4; - continue; + p += 4; + continue; case start_memory: - case stop_memory: + case stop_memory: p += 2; continue; default: - abort (); /* We have listed all the cases. */ - } /* switch *p++ */ + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ /* Getting here means we have found the possible starting - characters for one path of the pattern -- and that the empty - string does not match. We need not follow this path further. - Instead, look at the next alternative (remembered on the - stack), or quit if no more. The test at the top of the loop - does these things. */ + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ path_can_be_null = false; p = pend; } /* while p */ /* Set `can_be_null' for the last path (also the first path, if the - pattern is empty). */ + pattern is empty). */ bufp->can_be_null |= path_can_be_null; + + done: + RESET_FAIL_STACK (); return 0; } /* re_compile_fastmap */ @@ -2875,7 +3650,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends) } } -/* Searching routines. */ +/* Searching routines. */ /* Like re_search_2, below, but only one string is specified, and doesn't let you say where to stop matching. */ @@ -2891,6 +3666,13 @@ re_search (bufp, string, size, startpos, range, regs) regs, size); } +/* End address of virtual concatenation of string. */ +#define STOP_ADDR_VSTRING(P) \ + (((P) >= size1 ? string2 + size2 : string1 + size1)) + +/* Address of POS in the concatenation of virtual string. */ +#define POS_ADDR_VSTRING(POS) \ + (((POS) >= size1 ? string2 - size1 : string1) + (POS)) /* Using the compiled pattern in BUFP->buffer, first tries to match the virtual concatenation of STRING1 and STRING2, starting first at index @@ -2925,29 +3707,34 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) { int val; register char *fastmap = bufp->fastmap; - register char *translate = bufp->translate; + register RE_TRANSLATE_TYPE translate = bufp->translate; int total_size = size1 + size2; int endpos = startpos + range; + int anchored_start = 0; + + /* Nonzero if we have to concern multibyte character. */ + int multibyte = bufp->multibyte; /* Check for out-of-range STARTPOS. */ if (startpos < 0 || startpos > total_size) return -1; /* Fix up RANGE if it might eventually take us outside - the virtual concatenation of STRING1 and STRING2. */ - if (endpos < -1) - range = -1 - startpos; + the virtual concatenation of STRING1 and STRING2. + Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE. */ + if (endpos < 0) + range = 0 - startpos; else if (endpos > total_size) range = total_size - startpos; /* If the search isn't to be a backwards one, don't waste time in a - search for a pattern that must be anchored. */ + search for a pattern anchored at beginning of buffer. */ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) { if (startpos > 0) return -1; else - range = 1; + range = 0; } #ifdef emacs @@ -2955,8 +3742,8 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) don't keep searching past point. */ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0) { - range = PT - startpos; - if (range <= 0) + range = PT_BYTE - BEGV_BYTE - startpos; + if (range < 0) return -1; } #endif /* emacs */ @@ -2966,57 +3753,122 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) if (re_compile_fastmap (bufp) == -2) return -2; + /* See whether the pattern is anchored. */ + if (bufp->buffer[0] == begline) + anchored_start = 1; + +#ifdef emacs + gl_state.object = re_match_object; + { + int adjpos = NILP (re_match_object) || BUFFERP (re_match_object); + int charpos = SYNTAX_TABLE_BYTE_TO_CHAR (startpos + adjpos); + + SETUP_SYNTAX_TABLE_FOR_OBJECT (re_match_object, charpos, 1); + } +#endif + /* Loop through the string, looking for a place to start matching. */ for (;;) { + /* If the pattern is anchored, + skip quickly past places we cannot match. + We don't bother to treat startpos == 0 specially + because that case doesn't repeat. */ + if (anchored_start && startpos > 0) + { + if (! (bufp->newline_anchor + && ((startpos <= size1 ? string1[startpos - 1] + : string2[startpos - size1 - 1]) + == '\n'))) + goto advance; + } + /* If a fastmap is supplied, skip quickly over characters that - cannot be the start of a match. If the pattern can match the - null string, however, we don't need to skip characters; we want - the first null string. */ + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ if (fastmap && startpos < total_size && !bufp->can_be_null) { - if (range > 0) /* Searching forwards. */ + register const char *d; + register unsigned int buf_ch; + + d = POS_ADDR_VSTRING (startpos); + + if (range > 0) /* Searching forwards. */ { - register const char *d; register int lim = 0; int irange = range; - if (startpos < size1 && startpos + range >= size1) - lim = range - (size1 - startpos); - - d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); - /* Written out as an if-else to avoid testing `translate' - inside the loop. */ - if (translate) - while (range > lim - && !fastmap[(unsigned char) - translate[(unsigned char) *d++]]) - range--; + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ + if (RE_TRANSLATE_P (translate)) + { + if (multibyte) + while (range > lim) + { + int buf_charlen; + + buf_ch = STRING_CHAR_AND_LENGTH (d, range - lim, + buf_charlen); + + buf_ch = RE_TRANSLATE (translate, buf_ch); + if (buf_ch >= 0400 + || fastmap[buf_ch]) + break; + + range -= buf_charlen; + d += buf_charlen; + } + else + while (range > lim + && !fastmap[(unsigned char) + RE_TRANSLATE (translate, (unsigned char) *d)]) + { + d++; + range--; + } + } else - while (range > lim && !fastmap[(unsigned char) *d++]) - range--; + while (range > lim && !fastmap[(unsigned char) *d]) + { + d++; + range--; + } startpos += irange - range; } - else /* Searching backwards. */ + else /* Searching backwards. */ { - register char c = (size1 == 0 || startpos >= size1 - ? string2[startpos - size1] - : string1[startpos]); + int room = (size1 == 0 || startpos >= size1 + ? size2 + size1 - startpos + : size1 - startpos); - if (!fastmap[(unsigned char) TRANSLATE (c)]) + buf_ch = STRING_CHAR (d, room); + if (RE_TRANSLATE_P (translate)) + buf_ch = RE_TRANSLATE (translate, buf_ch); + + if (! (buf_ch >= 0400 + || fastmap[buf_ch])) goto advance; } } /* If can't match the null string, and that's all we have left, fail. */ if (range >= 0 && startpos == total_size && fastmap - && !bufp->can_be_null) + && !bufp->can_be_null) return -1; - val = re_match_2 (bufp, string1, size1, string2, size2, - startpos, regs, stop); + val = re_match_2_internal (bufp, string1, size1, string2, size2, + startpos, regs, stop); +#ifndef REGEX_MALLOC +#ifdef C_ALLOCA + alloca (0); +#endif +#endif + if (val >= 0) return startpos; @@ -3025,17 +3877,60 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) advance: if (!range) - break; + break; else if (range > 0) - { - range--; - startpos++; - } + { + /* Update STARTPOS to the next character boundary. */ + if (multibyte) + { + const unsigned char *p + = (const unsigned char *) POS_ADDR_VSTRING (startpos); + const unsigned char *pend + = (const unsigned char *) STOP_ADDR_VSTRING (startpos); + int len = MULTIBYTE_FORM_LENGTH (p, pend - p); + + range -= len; + if (range < 0) + break; + startpos += len; + } + else + { + range--; + startpos++; + } + } else - { - range++; - startpos--; - } + { + range++; + startpos--; + + /* Update STARTPOS to the previous character boundary. */ + if (multibyte) + { + const unsigned char *p + = (const unsigned char *) POS_ADDR_VSTRING (startpos); + int len = 0; + + /* Find the head of multibyte form. */ + while (!CHAR_HEAD_P (*p)) + p--, len++; + + /* Adjust it. */ +#if 0 /* XXX */ + if (MULTIBYTE_FORM_LENGTH (p, len + 1) != (len + 1)) + ; + else +#endif + { + range += len; + if (range > 0) + break; + + startpos -= len; + } + } + } } return -1; } /* re_search_2 */ @@ -3044,67 +3939,15 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) static int bcmp_translate (); static boolean alt_match_null_string_p (), - common_op_match_null_string_p (), - group_match_null_string_p (); - -/* Structure for per-register (a.k.a. per-group) information. - Other register information, such as the - starting and ending positions (which are addresses), and the list of - inner groups (which is a bits list) are maintained in separate - variables. - - We are making a (strictly speaking) nonportable assumption here: that - the compiler will pack our bit fields into something that fits into - the type of `word', i.e., is something that fits into one item on the - failure stack. */ - -typedef union -{ - fail_stack_elt_t word; - struct - { - /* This field is one if this group can match the empty string, - zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ -#define MATCH_NULL_UNSET_VALUE 3 - unsigned match_null_string_p : 2; - unsigned is_active : 1; - unsigned matched_something : 1; - unsigned ever_matched_something : 1; - } bits; -} register_info_type; - -#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) -#define IS_ACTIVE(R) ((R).bits.is_active) -#define MATCHED_SOMETHING(R) ((R).bits.matched_something) -#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) - - -/* Call this when have matched a real character; it sets `matched' flags - for the subexpressions which we are currently inside. Also records - that those subexprs have matched. */ -#define SET_REGS_MATCHED() \ - do \ - { \ - unsigned r; \ - for (r = lowest_active_reg; r <= highest_active_reg; r++) \ - { \ - MATCHED_SOMETHING (reg_info[r]) \ - = EVER_MATCHED_SOMETHING (reg_info[r]) \ - = 1; \ - } \ - } \ - while (0) - + common_op_match_null_string_p (), + group_match_null_string_p (); /* This converts PTR, a pointer into one of the search strings `string1' and `string2' into an offset from the beginning of that string. */ -#define POINTER_TO_OFFSET(ptr) \ - (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1) - -/* Registers are set to a sentinel when they haven't yet matched. */ -#define REG_UNSET_VALUE ((char *) -1) -#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) - +#define POINTER_TO_OFFSET(ptr) \ + (FIRST_STRING_P (ptr) \ + ? ((regoff_t) ((ptr) - string1)) \ + : ((regoff_t) ((ptr) - string2 + size1))) /* Macros for dealing with the split strings in re_match_2. */ @@ -3113,19 +3956,19 @@ typedef union /* Call before fetching a character with *d. This switches over to string2 if necessary. */ #define PREFETCH() \ - while (d == dend) \ + while (d == dend) \ { \ /* End of string2 => fail. */ \ - if (dend == end_match_2) \ - goto fail; \ - /* End of string1 => advance to string2. */ \ - d = string2; \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ dend = end_match_2; \ } /* Test if at very beginning or at very end of the virtual concatenation - of `string1' and `string2'. If only one string, it's `string2'. */ + of `string1' and `string2'. If only one string, it's `string2'. */ #define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) #define AT_STRINGS_END(d) ((d) == end2) @@ -3136,22 +3979,33 @@ typedef union string2, look at the last character in string1. */ #define WORDCHAR_P(d) \ (SYNTAX ((d) == end1 ? *string2 \ - : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ == Sword) +/* Disabled due to a compiler bug -- see comment at case wordbound */ + +/* The comment at case wordbound is following one, but we don't use + AT_WORD_BOUNDARY anymore to support multibyte form. + + The DEC Alpha C compiler 3.x generates incorrect code for the + test WORDCHAR_P (d - 1) != WORDCHAR_P (d) in the expansion of + AT_WORD_BOUNDARY, so this code is disabled. Expanding the + macro and introducing temporary variables works around the bug. */ + +#if 0 /* Test if the character before D and the one at D differ with respect to being word-constituent. */ #define AT_WORD_BOUNDARY(d) \ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) - +#endif /* Free everything we malloc. */ -#ifdef REGEX_MALLOC -#define FREE_VAR(var) if (var) free (var); var = NULL +#ifdef MATCH_MAY_ALLOCATE +#define FREE_VAR(var) if (var) { REGEX_FREE (var); var = NULL; } else #define FREE_VARIABLES() \ do { \ - FREE_VAR (fail_stack.stack); \ + REGEX_FREE_STACK (fail_stack.stack); \ FREE_VAR (regstart); \ FREE_VAR (regend); \ FREE_VAR (old_regstart); \ @@ -3162,25 +4016,23 @@ typedef union FREE_VAR (reg_dummy); \ FREE_VAR (reg_info_dummy); \ } while (0) -#else /* not REGEX_MALLOC */ -/* Some MIPS systems (at least) want this to free alloca'd storage. */ -#define FREE_VARIABLES() alloca (0) -#endif /* not REGEX_MALLOC */ - +#else +#define FREE_VARIABLES() ((void)0) /* Do nothing! But inhibit gcc warning. */ +#endif /* not MATCH_MAY_ALLOCATE */ -/* These values must meet several constraints. They must not be valid +/* These values must meet several constraints. They must not be valid register values; since we have a limit of 255 registers (because we use only one byte in the pattern for the register number), we can - use numbers larger than 255. They must differ by 1, because of + use numbers larger than 255. They must differ by 1, because of NUM_FAILURE_ITEMS above. And the value for the lowest register must be larger than the value for the highest register, so we do not try - to actually save any registers when none are active. */ + to actually save any registers when none are active. */ #define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) #define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) /* Matching routines. */ -#ifndef emacs /* Emacs never uses this. */ +#ifndef emacs /* Emacs never uses this. */ /* re_match is like re_match_2 except it takes only a single string. */ int @@ -3189,11 +4041,23 @@ re_match (bufp, string, size, pos, regs) const char *string; int size, pos; struct re_registers *regs; - { - return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); +{ + int result = re_match_2_internal (bufp, NULL, 0, string, size, + pos, regs, size); +#ifndef REGEX_MALLOC /* CVS */ +#ifdef C_ALLOCA /* CVS */ + alloca (0); +#endif /* CVS */ +#endif /* CVS */ + return result; } #endif /* not emacs */ +#ifdef emacs +/* In Emacs, this is the string or buffer in which we + are matching. It is used for looking up syntax properties. */ +Lisp_Object re_match_object; +#endif /* re_match_2 matches the compiled pattern in BUFP against the the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 @@ -3201,11 +4065,11 @@ re_match (bufp, string, size, pos, regs) matching at STOP. If REGS is non-null and the `no_sub' field of BUFP is nonzero, we - store offsets for the substring each group matched in REGS. See the + store offsets for the substring each group matched in REGS. See the documentation for exactly how many groups we fill. We return -1 if no match, -2 if an internal error (such as the - failure stack overflowing). Otherwise, we return the length of the + failure stack overflowing). Otherwise, we return the length of the matched substring. */ int @@ -3217,6 +4081,37 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) struct re_registers *regs; int stop; { + int result; + +#ifdef emacs + int charpos; + int adjpos = NILP (re_match_object) || BUFFERP (re_match_object); + gl_state.object = re_match_object; + charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos + adjpos); + SETUP_SYNTAX_TABLE_FOR_OBJECT (re_match_object, charpos, 1); +#endif + + result = re_match_2_internal (bufp, string1, size1, string2, size2, + pos, regs, stop); +#ifndef REGEX_MALLOC /* CVS */ +#ifdef C_ALLOCA /* CVS */ + alloca (0); +#endif /* CVS */ +#endif /* CVS */ + return result; +} + +/* This is a separate function so that we can force an alloca cleanup + afterwards. */ +static int +re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ /* General temporaries. */ int mcnt; unsigned char *p1; @@ -3225,7 +4120,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) const char *end1, *end2; /* Pointers into string1 and string2, just past the last characters in - each to consider matching. */ + each to consider matching. */ const char *end_match_1, *end_match_2; /* Where we are in the data, and the end of the current string. */ @@ -3235,8 +4130,15 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) unsigned char *p = bufp->buffer; register unsigned char *pend = p + bufp->used; - /* We use this to map every character in the string. */ - char *translate = bufp->translate; + /* Mark the opcode just after a start_memory, so we can test for an + empty subpattern when we get to the stop_memory. */ + unsigned char *just_past_start_mem = 0; + + /* We use this to map every character in the string. */ + RE_TRANSLATE_TYPE translate = bufp->translate; + + /* Nonzero if we have to concern multibyte character. */ + int multibyte = bufp->multibyte; /* Failure point stack. Each place that can handle a failure further down the line pushes a failure point on this stack. It consists of @@ -3246,15 +4148,21 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) to resume scanning the pattern; the second one is where to resume scanning the strings. If the latter is zero, the failure point is a ``dummy''; if a failure happens and the failure point is a dummy, - it gets discarded and the next next one is tried. */ + it gets discarded and the next next one is tried. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ fail_stack_type fail_stack; +#endif #ifdef DEBUG static unsigned failure_id = 0; unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; #endif + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; + /* We fill all the registers internally, independent of what we - return, for use in backreferences. The number here includes + return, for use in backreferences. The number here includes an element for register zero. */ unsigned num_regs = bufp->re_nsub + 1; @@ -3269,33 +4177,41 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) matching and the regnum-th regend points to right after where we stopped matching the regnum-th subexpression. (The zeroth register keeps track of what the whole pattern matches.) */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **regstart, **regend; +#endif /* If a group that's operated upon by a repetition operator fails to match anything, then the register for its start will need to be restored because it will have been set to wherever in the string we are when we last see its open-group operator. Similarly for a register's end. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **old_regstart, **old_regend; +#endif /* The is_active field of reg_info helps us keep track of which (possibly nested) subexpressions we are currently in. The matched_something field of reg_info[reg_num] helps us tell whether or not we have matched any of the pattern so far this time through the reg_num-th subexpression. These two fields get reset each time through any - loop their register is in. */ + loop their register is in. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ register_info_type *reg_info; +#endif /* The following record the register info as found in the above variables when we find a match better than any we've seen before. This happens as we backtrack through the failure points, which in turn happens only if we have not yet matched the entire string. */ unsigned best_regs_set = false; +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **best_regstart, **best_regend; - +#endif + /* Logically, this is `best_regend[0]'. But we don't want to have to allocate space for that if we're not allocating space for anything - else (see below). Also, we never need info about register 0 for + else (see below). Also, we never need info about register 0 for any of the other register vectors, and it seems rather a kludge to treat `best_regend' differently than the rest. So we keep track of the end of the best match so far in a separate variable. We @@ -3303,9 +4219,14 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) and need to test it, it's not garbage. */ const char *match_end = NULL; + /* This helps SET_REGS_MATCHED avoid doing redundant work. */ + int set_regs_matched_done = 0; + /* Used when we pop values we don't care about. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **reg_dummy; register_info_type *reg_info_dummy; +#endif #ifdef DEBUG /* Counts the total number of registers pushed. */ @@ -3316,6 +4237,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) INIT_FAIL_STACK (); +#ifdef MATCH_MAY_ALLOCATE /* Do not bother to initialize all the register variables if there are no groups in the pattern, as it takes a fair amount of time. If there are groups, we include space for register 0 (the whole @@ -3334,22 +4256,21 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); if (!(regstart && regend && old_regstart && old_regend && reg_info - && best_regstart && best_regend && reg_dummy && reg_info_dummy)) - { - FREE_VARIABLES (); - return -2; - } + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } } -#ifdef REGEX_MALLOC else { /* We must initialize all our variables to NULL, so that - `FREE_VARIABLES' doesn't try to free them. */ + `FREE_VARIABLES' doesn't try to free them. */ regstart = regend = old_regstart = old_regend = best_regstart - = best_regend = reg_dummy = NULL; + = best_regend = reg_dummy = NULL; reg_info = reg_info_dummy = (register_info_type *) NULL; } -#endif /* REGEX_MALLOC */ +#endif /* MATCH_MAY_ALLOCATE */ /* The starting position is bogus. */ if (pos < 0 || pos > size1 + size2) @@ -3364,7 +4285,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = regend[mcnt] - = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; IS_ACTIVE (reg_info[mcnt]) = 0; @@ -3373,7 +4294,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) } /* We move `string1' into `string2' if the latter's empty -- but not if - `string1' is null. */ + `string1' is null. */ if (size2 == 0 && string1 != NULL) { string2 = string1; @@ -3419,7 +4340,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); DEBUG_PRINT1 ("'\n"); - /* This loops over pattern commands. It exits by returning from the + /* This loops over pattern commands. It exits by returning from the function if the match is complete, or it drops through if the match fails at this starting point in the input data. */ for (;;) @@ -3428,177 +4349,222 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) if (p == pend) { /* End of pattern means we might have succeeded. */ - DEBUG_PRINT1 ("end of pattern ... "); + DEBUG_PRINT1 ("end of pattern ... "); /* If we haven't matched the entire string, and we want the - longest match, try backtracking. */ - if (d != end_match_2) + longest match, try backtracking. */ + if (d != end_match_2) { - DEBUG_PRINT1 ("backtracking.\n"); + /* 1 if this match ends in the same string (string1 or string2) + as the best previous match. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + /* 1 if this match is the best seen so far. */ + boolean best_match_p; + + /* AIX compiler got confused when this was combined + with the previous declaration. */ + if (same_str_p) + best_match_p = d > match_end; + else + best_match_p = !MATCHING_IN_FIRST_STRING; - if (!FAIL_STACK_EMPTY ()) - { /* More failure points to try. */ - boolean same_str_p = (FIRST_STRING_P (match_end) - == MATCHING_IN_FIRST_STRING); + DEBUG_PRINT1 ("backtracking.\n"); - /* If exceeds best match so far, save it. */ - if (!best_regs_set - || (same_str_p && d > match_end) - || (!same_str_p && !MATCHING_IN_FIRST_STRING)) - { - best_regs_set = true; - match_end = d; + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ - DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + /* If exceeds best match so far, save it. */ + if (!best_regs_set || best_match_p) + { + best_regs_set = true; + match_end = d; - for (mcnt = 1; mcnt < num_regs; mcnt++) - { - best_regstart[mcnt] = regstart[mcnt]; - best_regend[mcnt] = regend[mcnt]; - } - } - goto fail; - } + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); - /* If no failure points, don't restore garbage. */ - else if (best_regs_set) - { - restore_best_regs: - /* Restore best match. It may happen that `dend == - end_match_1' while the restored d is in string2. - For example, the pattern `x.*y.*z' against the - strings `x-' and `y-z-', if the two strings are - not consecutive in memory. */ - DEBUG_PRINT1 ("Restoring best registers.\n"); - - d = match_end; - dend = ((d >= string1 && d <= end1) - ? end_match_1 : end_match_2); + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. And if + last match is real best match, don't restore second + best one. */ + else if (best_regs_set && !best_match_p) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = best_regstart[mcnt]; regend[mcnt] = best_regend[mcnt]; } - } - } /* d != end_match_2 */ + } + } /* d != end_match_2 */ - DEBUG_PRINT1 ("Accepting match.\n"); + succeed_label: + DEBUG_PRINT1 ("Accepting match.\n"); - /* If caller wants register contents data back, do it. */ - if (regs && !bufp->no_sub) + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) { - /* Have the register data arrays been allocated? */ - if (bufp->regs_allocated == REGS_UNALLOCATED) - { /* No. So allocate them with malloc. We need one - extra element beyond `num_regs' for the `-1' marker - GNU code uses. */ - regs->num_regs = MAX (RE_NREGS, num_regs + 1); - regs->start = TALLOC (regs->num_regs, regoff_t); - regs->end = TALLOC (regs->num_regs, regoff_t); - if (regs->start == NULL || regs->end == NULL) - return -2; - bufp->regs_allocated = REGS_REALLOCATE; - } - else if (bufp->regs_allocated == REGS_REALLOCATE) - { /* Yes. If we need more elements than were already - allocated, reallocate them. If we need fewer, just - leave it alone. */ - if (regs->num_regs < num_regs + 1) - { - regs->num_regs = num_regs + 1; - RETALLOC (regs->start, regs->num_regs, regoff_t); - RETALLOC (regs->end, regs->num_regs, regoff_t); - if (regs->start == NULL || regs->end == NULL) - return -2; - } - } - else + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + } + } + else { /* These braces fend off a "empty body in an else-statement" - warning under GCC when assert expands to nothing. */ + warning under GCC when assert expands to nothing. */ assert (bufp->regs_allocated == REGS_FIXED); } - /* Convert the pointer data in `regstart' and `regend' to - indices. Register zero has to be set differently, - since we haven't kept track of any info for it. */ - if (regs->num_regs > 0) - { - regs->start[0] = pos; - regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1 - : d - string2 + size1); - } + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING + ? ((regoff_t) (d - string1)) + : ((regoff_t) (d - string2 + size1))); + } - /* Go through the first `min (num_regs, regs->num_regs)' - registers, since that is all we initialized. */ + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) { - if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) - regs->start[mcnt] = regs->end[mcnt] = -1; - else - { - regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]); - regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]); - } + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]); + } } - /* If the regs structure we return has more elements than - were in the pattern, set the extra elements to -1. If - we (re)allocated the registers, this is the case, - because we always allocate enough to have at least one - -1 at the end. */ - for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) - regs->start[mcnt] = regs->end[mcnt] = -1; + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; } /* regs && !bufp->no_sub */ - FREE_VARIABLES (); - DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", - nfailure_points_pushed, nfailure_points_popped, - nfailure_points_pushed - nfailure_points_popped); - DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); - mcnt = d - pos - (MATCHING_IN_FIRST_STRING + mcnt = d - pos - (MATCHING_IN_FIRST_STRING ? string1 : string2 - size1); - DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); - return mcnt; - } + FREE_VARIABLES (); + return mcnt; + } - /* Otherwise match next pattern command. */ -#ifdef SWITCH_ENUM_BUG - switch ((int) ((re_opcode_t) *p++)) -#else - switch ((re_opcode_t) *p++) -#endif + /* Otherwise match next pattern command. */ + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) { - /* Ignore these. Used to ignore the n of succeed_n's which - currently have n == 0. */ - case no_op: - DEBUG_PRINT1 ("EXECUTING no_op.\n"); - break; + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; + case succeed: + DEBUG_PRINT1 ("EXECUTING succeed.\n"); + goto succeed_label; - /* Match the next n pattern characters exactly. The following - byte in the pattern defines n, and the n bytes after that - are the characters to match. */ + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ case exactn: mcnt = *p++; - DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); - /* This is written out as an if-else so we don't waste time - testing `translate' inside the loop. */ - if (translate) + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (RE_TRANSLATE_P (translate)) { - do - { - PREFETCH (); - if (translate[(unsigned char) *d++] != (char) *p++) - goto fail; - } - while (--mcnt); +#ifdef emacs + if (multibyte) + do + { + int pat_charlen, buf_charlen; + unsigned int pat_ch, buf_ch; + + PREFETCH (); + pat_ch = STRING_CHAR_AND_LENGTH (p, pend - p, pat_charlen); + buf_ch = STRING_CHAR_AND_LENGTH (d, dend - d, buf_charlen); + + if (RE_TRANSLATE (translate, buf_ch) + != pat_ch) + goto fail; + + p += pat_charlen; + d += buf_charlen; + mcnt -= pat_charlen; + } + while (mcnt > 0); + else +#endif /* not emacs */ + do + { + PREFETCH (); + if ((unsigned char) RE_TRANSLATE (translate, (unsigned char) *d) + != (unsigned char) *p++) + goto fail; + d++; + } + while (--mcnt); } else { @@ -3610,206 +4576,262 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) while (--mcnt); } SET_REGS_MATCHED (); - break; + break; - /* Match any character except possibly a newline or a null. */ + /* Match any character except possibly a newline or a null. */ case anychar: - DEBUG_PRINT1 ("EXECUTING anychar.\n"); + { + int buf_charlen; + unsigned int buf_ch; - PREFETCH (); + DEBUG_PRINT1 ("EXECUTING anychar.\n"); - if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') - || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) - goto fail; + PREFETCH (); - SET_REGS_MATCHED (); - DEBUG_PRINT2 (" Matched `%d'.\n", *d); - d++; +#ifdef emacs + if (multibyte) + buf_ch = STRING_CHAR_AND_LENGTH (d, dend - d, buf_charlen); + else +#endif /* not emacs */ + { + buf_ch = (unsigned char) *d; + buf_charlen = 1; + } + + buf_ch = TRANSLATE (buf_ch); + + if ((!(bufp->syntax & RE_DOT_NEWLINE) + && buf_ch == '\n') + || ((bufp->syntax & RE_DOT_NOT_NULL) + && buf_ch == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d += buf_charlen; + } break; case charset: case charset_not: { - register unsigned char c; + register unsigned int c; boolean not = (re_opcode_t) *(p - 1) == charset_not; + int len; - DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + /* Start of actual range_table, or end of bitmap if there is no + range table. */ + unsigned char *range_table; - PREFETCH (); - c = TRANSLATE (*d); /* The character to match. */ + /* Nonzero if there is range table. */ + int range_table_exists; - /* Cast to `unsigned' instead of `unsigned char' in case the - bit list is a full 32 bytes long. */ - if (c < (unsigned) (*p * BYTEWIDTH) + /* Number of ranges of range table. Not in bytes. */ + int count; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = (unsigned char) *d; + + range_table = CHARSET_RANGE_TABLE (&p[-1]); /* Past the bitmap. */ + range_table_exists = CHARSET_RANGE_TABLE_EXISTS_P (&p[-1]); + if (range_table_exists) + EXTRACT_NUMBER_AND_INCR (count, range_table); + else + count = 0; + + if (multibyte && BASE_LEADING_CODE_P (c)) + c = STRING_CHAR_AND_LENGTH (d, dend - d, len); + + if (SINGLE_BYTE_CHAR_P (c)) + { /* Lookup bitmap. */ + c = TRANSLATE (c); /* The character to match. */ + len = 1; + + /* Cast to `unsigned' instead of `unsigned char' in + case the bit list is a full 32 bytes long. */ + if (c < (unsigned) (CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH) && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; + } + else if (range_table_exists) + CHARSET_LOOKUP_RANGE_TABLE_RAW (not, c, range_table, count); - p += 1 + *p; + p = CHARSET_RANGE_TABLE_END (range_table, count); if (!not) goto fail; SET_REGS_MATCHED (); - d++; + d += len; break; } - /* The beginning of a group is represented by start_memory. - The arguments are the register number in the next byte, and the - number of groups inner to this one in the next. The text - matched within the group is recorded (in the internal - registers data structure) under the register number. */ - case start_memory: + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); - /* Find out if this group can match the empty string. */ + /* Find out if this group can match the empty string. */ p1 = p; /* To send to group_match_null_string_p. */ - if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) - REG_MATCH_NULL_STRING_P (reg_info[*p]) - = group_match_null_string_p (&p1, pend, reg_info); - - /* Save the position in the string where we were the last time - we were at this open-group operator in case the group is - operated upon by a repetition operator, e.g., with `(a*)*b' - against `ab'; then we want to ignore where we are now in - the string in case this attempt to match fails. */ - old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) - ? REG_UNSET (regstart[*p]) ? d : regstart[*p] - : regstart[*p]; + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; DEBUG_PRINT2 (" old_regstart: %d\n", POINTER_TO_OFFSET (old_regstart[*p])); - regstart[*p] = d; + regstart[*p] = d; DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); - IS_ACTIVE (reg_info[*p]) = 1; - MATCHED_SOMETHING (reg_info[*p]) = 0; + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; - /* This is the new highest active register. */ - highest_active_reg = *p; + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; - /* If nothing was active before, this is the new lowest active - register. */ - if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) - lowest_active_reg = *p; + /* This is the new highest active register. */ + highest_active_reg = *p; - /* Move past the register number and inner group count. */ - p += 2; - break; + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + just_past_start_mem = p; + + break; - /* The stop_memory opcode represents the end of a group. Its - arguments are the same as start_memory's: the register - number, and the number of inner groups. */ + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ case stop_memory: DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); - /* We need to save the string position the last time we were at - this close-group operator in case the group is operated - upon by a repetition operator, e.g., with `((a*)*(b*)*)*' - against `aba'; then we want to ignore where we are now in - the string in case this attempt to match fails. */ - old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) - ? REG_UNSET (regend[*p]) ? d : regend[*p] + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] : regend[*p]; DEBUG_PRINT2 (" old_regend: %d\n", POINTER_TO_OFFSET (old_regend[*p])); - regend[*p] = d; + regend[*p] = d; DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); - /* This register isn't active anymore. */ - IS_ACTIVE (reg_info[*p]) = 0; + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; - /* If this was the only register active, nothing is active - anymore. */ - if (lowest_active_reg == highest_active_reg) - { - lowest_active_reg = NO_LOWEST_ACTIVE_REG; - highest_active_reg = NO_HIGHEST_ACTIVE_REG; - } - else - { /* We must scan for the new highest active register, since - it isn't necessarily one less than now: consider - (a(b)c(d(e)f)g). When group 3 ends, after the f), the - new highest active register is 1. */ - unsigned char r = *p - 1; - while (r > 0 && !IS_ACTIVE (reg_info[r])) - r--; - - /* If we end up at register zero, that means that we saved - the registers as the result of an `on_failure_jump', not - a `start_memory', and we jumped to past the innermost - `stop_memory'. For example, in ((.)*) we save - registers 1 and 2 as a result of the *, but when we pop - back to the second ), we are at the stop_memory 1. - Thus, nothing is active. */ + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ if (r == 0) - { - lowest_active_reg = NO_LOWEST_ACTIVE_REG; - highest_active_reg = NO_HIGHEST_ACTIVE_REG; - } - else - highest_active_reg = r; - } + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } - /* If just failed to match something this time around with a - group that's operated on by a repetition operator, try to - force exit from the ``loop'', and restore the register - information for this group that we had before trying this - last match. */ - if ((!MATCHED_SOMETHING (reg_info[*p]) - || (re_opcode_t) p[-3] == start_memory) - && (p + 2) < pend) - { - boolean is_a_jump_n = false; + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || just_past_start_mem == p - 1) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; - p1 = p + 2; - mcnt = 0; - switch ((re_opcode_t) *p1++) - { - case jump_n: + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: is_a_jump_n = true; - case pop_failure_jump: + case pop_failure_jump: case maybe_pop_jump: case jump: case dummy_failure_jump: - EXTRACT_NUMBER_AND_INCR (mcnt, p1); + EXTRACT_NUMBER_AND_INCR (mcnt, p1); if (is_a_jump_n) p1 += 2; - break; + break; - default: - /* do nothing */ ; - } + default: + /* do nothing */ ; + } p1 += mcnt; - /* If the next operation is a jump backwards in the pattern - to an on_failure_jump right before the start_memory - corresponding to this stop_memory, exit from the loop - by forcing a failure after pushing on the stack the - on_failure_jump's jump in the pattern, and d. */ - if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump - && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) { - /* If this group ever matched anything, then restore - what its registers were before trying this last - failed match, e.g., with `(a*)*b' against `ab' for - regstart[1], and, e.g., with `((a*)*(b*)*)*' - against `aba' for regend[3]. + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. - Also restore the registers for inner groups for, - e.g., `((a*)(b*))*' against `aba' (register 3 would - otherwise get trashed). */ + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ - if (EVER_MATCHED_SOMETHING (reg_info[*p])) + if (EVER_MATCHED_SOMETHING (reg_info[*p])) { unsigned r; - EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; /* Restore this and inner groups' (if any) registers. */ for (r = *p; r < *p + *(p + 1); r++) @@ -3822,53 +4844,53 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) } } p1++; - EXTRACT_NUMBER_AND_INCR (mcnt, p1); - PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); - goto fail; - } - } + goto fail; + } + } - /* Move past the register number and the inner group count. */ - p += 2; - break; + /* Move past the register number and the inner group count. */ + p += 2; + break; /* \<digit> has been turned into a `duplicate' command which is - followed by the numeric value of <digit> as the register number. */ - case duplicate: + followed by the numeric value of <digit> as the register number. */ + case duplicate: { register const char *d2, *dend2; - int regno = *p++; /* Get which register to match against. */ + int regno = *p++; /* Get which register to match against. */ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); - /* Can't back reference a group which we've never matched. */ - if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) - goto fail; + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; - /* Where in input to try to start matching. */ - d2 = regstart[regno]; + /* Where in input to try to start matching. */ + d2 = regstart[regno]; - /* Where to stop matching; if both the place to start and - the place to stop matching are in the same string, then - set to the place to stop, otherwise, for now have to use - the end of the first string. */ + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ - dend2 = ((FIRST_STRING_P (regstart[regno]) + dend2 = ((FIRST_STRING_P (regstart[regno]) == FIRST_STRING_P (regend[regno])) ? regend[regno] : end_match_1); for (;;) { /* If necessary, advance to next segment in register - contents. */ + contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; if (dend2 == regend[regno]) break; - /* End of string1 => advance to string2. */ - d2 = string2; - dend2 = regend[regno]; + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; } /* At end of register contents => success */ if (d2 == dend2) break; @@ -3880,433 +4902,761 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) mcnt = dend - d; /* Want how many consecutive characters we can match in - one shot, so, if necessary, adjust the count. */ - if (mcnt > dend2 - d2) + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) mcnt = dend2 - d2; /* Compare that many; failure if mismatch, else move - past them. */ - if (translate - ? bcmp_translate (d, d2, mcnt, translate) - : bcmp (d, d2, mcnt)) + past them. */ + if (RE_TRANSLATE_P (translate) + ? bcmp_translate (d, d2, mcnt, translate) + : bcmp (d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; + + /* Do this because we've match some characters. */ + SET_REGS_MATCHED (); } } break; - /* begline matches the empty string at the beginning of the string - (unless `not_bol' is set in `bufp'), and, if - `newline_anchor' is set, after newlines. */ + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ case begline: - DEBUG_PRINT1 ("EXECUTING begline.\n"); + DEBUG_PRINT1 ("EXECUTING begline.\n"); - if (AT_STRINGS_BEG (d)) - { - if (!bufp->not_bol) break; - } - else if (d[-1] == '\n' && bufp->newline_anchor) - { - break; - } - /* In all other cases, we fail. */ - goto fail; + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; - /* endline is the dual of begline. */ + /* endline is the dual of begline. */ case endline: - DEBUG_PRINT1 ("EXECUTING endline.\n"); + DEBUG_PRINT1 ("EXECUTING endline.\n"); - if (AT_STRINGS_END (d)) - { - if (!bufp->not_eol) break; - } + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } - /* We have to ``prefetch'' the next character. */ - else if ((d == end1 ? *string2 : *d) == '\n' - && bufp->newline_anchor) - { - break; - } - goto fail; + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; /* Match at the very beginning of the data. */ - case begbuf: - DEBUG_PRINT1 ("EXECUTING begbuf.\n"); - if (AT_STRINGS_BEG (d)) - break; - goto fail; + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; /* Match at the very end of the data. */ - case endbuf: - DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); if (AT_STRINGS_END (d)) break; - goto fail; - - - /* on_failure_keep_string_jump is used to optimize `.*\n'. It - pushes NULL as the value for the string on the stack. Then - `pop_failure_point' will keep the current value for the - string, instead of restoring it. To see why, consider - matching `foo\nbar' against `.*\n'. The .* matches the foo; - then the . fails against the \n. But the next thing we want - to do is match the \n against the \n; if we restored the - string value, we would be back at the foo. - - Because this is used only in specific cases, we don't need to - check all the things that `on_failure_jump' does, to make - sure the right things get saved on the stack. Hence we don't - share its code. The only reason to push anything on the - stack at all is that otherwise we would have to change - `anychar's code to do something besides goto fail in this - case; that seems worse than this. */ - case on_failure_keep_string_jump: - DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); - - EXTRACT_NUMBER_AND_INCR (mcnt, p); - DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); - - PUSH_FAILURE_POINT (p + mcnt, NULL, -2); - break; + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; /* Uses of on_failure_jump: - Each alternative starts with an on_failure_jump that points - to the beginning of the next alternative. Each alternative - except the last ends with a jump that in effect jumps past - the rest of the alternatives. (They really jump to the - ending jump of the following alternative, because tensioning - these jumps is a hassle.) + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) - Repeats start with an on_failure_jump that points past both - the repetition text and either the following jump or - pop_failure_jump back to this on_failure_jump. */ + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ case on_failure_jump: - on_failure: - DEBUG_PRINT1 ("EXECUTING on_failure_jump"); - - EXTRACT_NUMBER_AND_INCR (mcnt, p); - DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); - - /* If this on_failure_jump comes right before a group (i.e., - the original * applied to a group), save the information - for that group and all inner ones, so that if we fail back - to this point, the group's information will be correct. - For example, in \(a*\)*\1, we need the preceding group, - and in \(\(a*\)b*\)\2, we need the inner group. */ - - /* We can't use `p' to check ahead because we push - a failure point to `p + mcnt' after we do this. */ - p1 = p; - - /* We need to skip no_op's before we look for the - start_memory in case this on_failure_jump is happening as - the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 - against aba. */ - while (p1 < pend && (re_opcode_t) *p1 == no_op) - p1++; - - if (p1 < pend && (re_opcode_t) *p1 == start_memory) - { - /* We have a new highest active register now. This will - get reset at the start_memory we are about to get to, - but we will have saved all the registers relevant to - this repetition op, as described above. */ - highest_active_reg = *(p1 + 1) + *(p1 + 2); - if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) - lowest_active_reg = *(p1 + 1); - } + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); - DEBUG_PRINT1 (":\n"); - PUSH_FAILURE_POINT (p + mcnt, d, -2); - break; +#if defined (WINDOWSNT) && defined (emacs) + QUIT; +#endif + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(zz\(a*\)b*\)\2, we need the inner group. */ + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; - /* A smart repeat ends with `maybe_pop_jump'. - We change it to either `pop_failure_jump' or `jump'. */ - case maybe_pop_jump: - EXTRACT_NUMBER_AND_INCR (mcnt, p); - DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); - { + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: +#if defined (WINDOWSNT) && defined (emacs) + QUIT; +#endif + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { register unsigned char *p2 = p; - /* Compare the beginning of the repeat with what in the - pattern follows its end. If we can establish that there - is nothing that they would both match, i.e., that we - would have to backtrack because of (as in, e.g., `a*a') - then we can change to pop_failure_jump, because we'll - never have to backtrack. - - This is not true in the case of alternatives: in - `(a|ab)*' we do need to backtrack to the `ab' alternative - (e.g., if the string was `ab'). But instead of trying to - detect that here, the alternative has put on a dummy - failure point which is what we will end up popping. */ - - /* Skip over open/close-group commands. */ - while (p2 + 2 < pend - && ((re_opcode_t) *p2 == stop_memory - || (re_opcode_t) *p2 == start_memory)) - p2 += 3; /* Skip over args, too. */ - - /* If we're at the end of the pattern, we can change. */ - if (p2 == pend) + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. + If what follows this loop is a ...+ construct, + look at what begins its body, since we will have to + match at least one of that. */ + while (1) + { + if (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; + else if (p2 + 6 < pend + && (re_opcode_t) *p2 == dummy_failure_jump) + p2 += 6; + else + break; + } + + p1 = p + mcnt; + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) { /* Consider what happens when matching ":\(.*\)" against ":/". I don't really understand this code - yet. */ - p[-3] = (unsigned char) pop_failure_jump; - DEBUG_PRINT1 - (" End of pattern: change to `pop_failure_jump'.\n"); - } + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } - else if ((re_opcode_t) *p2 == exactn + else if ((re_opcode_t) *p2 == exactn || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) { - register unsigned char c - = *p2 == (unsigned char) endline ? '\n' : p2[2]; - p1 = p + mcnt; - - /* p1[0] ... p1[2] are the `on_failure_jump' corresponding - to the `maybe_finalize_jump' of this case. Examine what - follows. */ - if ((re_opcode_t) p1[3] == exactn && p1[5] != c) - { - p[-3] = (unsigned char) pop_failure_jump; - DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", - c, p1[5]); - } + register unsigned int c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + + if ((re_opcode_t) p1[3] == exactn) + { + if (!(multibyte /* && (c != '\n') */ + && BASE_LEADING_CODE_P (c)) + ? c != p1[5] + : (STRING_CHAR (&p2[2], pend - &p2[2]) + != STRING_CHAR (&p1[5], pend - &p1[5]))) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + } else if ((re_opcode_t) p1[3] == charset || (re_opcode_t) p1[3] == charset_not) { int not = (re_opcode_t) p1[3] == charset_not; - if (c < (unsigned char) (p1[4] * BYTEWIDTH) + if (multibyte /* && (c != '\n') */ + && BASE_LEADING_CODE_P (c)) + c = STRING_CHAR (&p2[2], pend - &p2[2]); + + /* Test if C is listed in charset (or charset_not) + at `&p1[3]'. */ + if (SINGLE_BYTE_CHAR_P (c)) + { + if (c < CHARSET_BITMAP_SIZE (&p1[3]) * BYTEWIDTH && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; + } + else if (CHARSET_RANGE_TABLE_EXISTS_P (&p1[3])) + CHARSET_LOOKUP_RANGE_TABLE (not, c, &p1[3]); - /* `not' is equal to 1 if c would match, which means - that we can't change to pop_failure_jump. */ + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ if (!not) - { - p[-3] = (unsigned char) pop_failure_jump; - DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); - } + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } } } + else if ((re_opcode_t) *p2 == charset) + { + if ((re_opcode_t) p1[3] == exactn) + { + register unsigned int c = p1[5]; + int not = 0; + + if (multibyte && BASE_LEADING_CODE_P (c)) + c = STRING_CHAR (&p1[5], pend - &p1[5]); + + /* Test if C is listed in charset at `p2'. */ + if (SINGLE_BYTE_CHAR_P (c)) + { + if (c < CHARSET_BITMAP_SIZE (p2) * BYTEWIDTH + && (p2[2 + c / BYTEWIDTH] + & (1 << (c % BYTEWIDTH)))) + not = !not; + } + else if (CHARSET_RANGE_TABLE_EXISTS_P (p2)) + CHARSET_LOOKUP_RANGE_TABLE (not, c, p2); + + if (!not) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + + /* It is hard to list up all the character in charset + P2 if it includes multibyte character. Give up in + such case. */ + else if (!multibyte || !CHARSET_RANGE_TABLE_EXISTS_P (p2)) + { + /* Now, we are sure that P2 has no range table. + So, for the size of bitmap in P2, `p2[1]' is + enough. But P1 may have range table, so the + size of bitmap table of P1 is extracted by + using macro `CHARSET_BITMAP_SIZE'. + + Since we know that all the character listed in + P2 is ASCII, it is enough to test only bitmap + table of P1. */ + + if ((re_opcode_t) p1[3] == charset_not) + { + int idx; + /* We win if the charset_not inside the loop lists + every character listed in the charset after. */ + for (idx = 0; idx < (int) p2[1]; idx++) + if (! (p2[2 + idx] == 0 + || (idx < CHARSET_BITMAP_SIZE (&p1[3]) + && ((p2[2 + idx] & ~ p1[5 + idx]) == 0)))) + break; + + if (idx == p2[1]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + else if ((re_opcode_t) p1[3] == charset) + { + int idx; + /* We win if the charset inside the loop + has no overlap with the one after the loop. */ + for (idx = 0; + (idx < (int) p2[1] + && idx < CHARSET_BITMAP_SIZE (&p1[3])); + idx++) + if ((p2[2 + idx] & p1[5 + idx]) != 0) + break; + + if (idx == p2[1] + || idx == CHARSET_BITMAP_SIZE (&p1[3])) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + } } p -= 2; /* Point at relative address again. */ if ((re_opcode_t) p[-1] != pop_failure_jump) { p[-1] = (unsigned char) jump; - DEBUG_PRINT1 (" Match => jump.\n"); + DEBUG_PRINT1 (" Match => jump.\n"); goto unconditional_jump; } - /* Note fall through. */ + /* Note fall through. */ /* The end of a simple repeat has a pop_failure_jump back to - its matching on_failure_jump, where the latter will push a - failure point. The pop_failure_jump takes off failure - points put on by this pop_failure_jump's matching - on_failure_jump; we got through the pattern to here from the - matching on_failure_jump, so didn't fail. */ - case pop_failure_jump: - { - /* We need to pass separate storage for the lowest and - highest registers, even though we don't care about the - actual values. Otherwise, we will restore only one - register from the stack, since lowest will == highest in - `pop_failure_point'. */ - unsigned dummy_low_reg, dummy_high_reg; - unsigned char *pdummy; - const char *sdummy; - - DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); - POP_FAILURE_POINT (sdummy, pdummy, - dummy_low_reg, dummy_high_reg, - reg_dummy, reg_dummy, reg_info_dummy); - } - /* Note fall through. */ - - - /* Unconditionally jump (without popping any failure points). */ - case jump: + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + unsigned dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + + /* Unconditionally jump (without popping any failure points). */ + case jump: unconditional_jump: +#if defined (WINDOWSNT) && defined (emacs) + QUIT; +#endif EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ - DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); - p += mcnt; /* Do the jump. */ - DEBUG_PRINT2 ("(to 0x%x).\n", p); + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ + DEBUG_PRINT2 ("(to 0x%x).\n", p); break; - /* We need this opcode so we can detect where alternatives end - in `group_match_null_string_p' et al. */ - case jump_past_alt: - DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); - goto unconditional_jump; - - - /* Normally, the on_failure_jump pushes a failure point, which - then gets popped at pop_failure_jump. We will end up at - pop_failure_jump, also, and with a pattern of, say, `a+', we - are skipping over the on_failure_jump, so we have to push - something meaningless for pop_failure_jump to pop. */ - case dummy_failure_jump: - DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); - /* It doesn't matter what we push for the string here. What - the code at `fail' tests is the value for the pattern. */ - PUSH_FAILURE_POINT (0, 0, -2); - goto unconditional_jump; - - - /* At the end of an alternative, we need to push a dummy failure - point in case we are followed by a `pop_failure_jump', because - we don't want the failure point for the alternative to be - popped. For example, matching `(a|ab)*' against `aab' - requires that we match the `ab' alternative. */ - case push_dummy_failure: - DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); - /* See comments just above at `dummy_failure_jump' about the - two zeroes. */ - PUSH_FAILURE_POINT (0, 0, -2); - break; + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; - /* Have to succeed matching what follows at least n times. - After that, handle like `on_failure_jump'. */ - case succeed_n: - EXTRACT_NUMBER (mcnt, p + 2); - DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); - assert (mcnt >= 0); - /* Originally, this is how many times we HAVE to succeed. */ - if (mcnt > 0) - { - mcnt--; + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (0, 0, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (0, 0, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; p += 2; - STORE_NUMBER_AND_INCR (p, mcnt); - DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); - } + STORE_NUMBER_AND_INCR (p, mcnt); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); + } else if (mcnt == 0) - { - DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); + { + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); p[2] = (unsigned char) no_op; - p[3] = (unsigned char) no_op; - goto on_failure; - } - break; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; - case jump_n: - EXTRACT_NUMBER (mcnt, p + 2); - DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); - /* Originally, this is how many times we CAN jump. */ - if (mcnt) - { - mcnt--; - STORE_NUMBER (p + 2, mcnt); + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); goto unconditional_jump; - } - /* If don't have to jump any more, skip over the rest of command. */ + } + /* If don't have to jump any more, skip over the rest of command. */ else p += 4; - break; + break; case set_number_at: { - DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); - EXTRACT_NUMBER_AND_INCR (mcnt, p); - p1 = p + mcnt; - EXTRACT_NUMBER_AND_INCR (mcnt, p); - DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); STORE_NUMBER (p1, mcnt); - break; - } + break; + } + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); - case wordbound: - DEBUG_PRINT1 ("EXECUTING wordbound.\n"); - if (AT_WORD_BOUNDARY (d)) + /* We SUCCEED in one of the following cases: */ + + /* Case 1: D is at the beginning or the end of string. */ + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) + break; + else + { + /* C1 is the character before D, S1 is the syntax of C1, C2 + is the character at D, and S2 is the syntax of C2. */ + int c1, c2, s1, s2; + int pos1 = PTR_TO_OFFSET (d - 1); + int charpos; + + GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); + GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); +#ifdef emacs + charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); + UPDATE_SYNTAX_TABLE (charpos); +#endif + s1 = SYNTAX (c1); +#ifdef emacs + UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1); +#endif + s2 = SYNTAX (c2); + + if (/* Case 2: Only one of S1 and S2 is Sword. */ + ((s1 == Sword) != (s2 == Sword)) + /* Case 3: Both of S1 and S2 are Sword, and macro + WORD_BOUNDARY_P (C1, C2) returns nonzero. */ + || ((s1 == Sword) && WORD_BOUNDARY_P (c1, c2))) break; - goto fail; + } + goto fail; - case notwordbound: - DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); - if (AT_WORD_BOUNDARY (d)) + case notwordbound: + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + + /* We FAIL in one of the following cases: */ + + /* Case 1: D is at the beginning or the end of string. */ + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) goto fail; - break; + else + { + /* C1 is the character before D, S1 is the syntax of C1, C2 + is the character at D, and S2 is the syntax of C2. */ + int c1, c2, s1, s2; + int pos1 = PTR_TO_OFFSET (d - 1); + int charpos; + + GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); + GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); +#ifdef emacs + charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); + UPDATE_SYNTAX_TABLE (charpos); +#endif + s1 = SYNTAX (c1); +#ifdef emacs + UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1); +#endif + s2 = SYNTAX (c2); + + if (/* Case 2: Only one of S1 and S2 is Sword. */ + ((s1 == Sword) != (s2 == Sword)) + /* Case 3: Both of S1 and S2 are Sword, and macro + WORD_BOUNDARY_P (C1, C2) returns nonzero. */ + || ((s1 == Sword) && WORD_BOUNDARY_P (c1, c2))) + goto fail; + } + break; case wordbeg: - DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); - if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) - break; - goto fail; + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + + /* We FAIL in one of the following cases: */ + + /* Case 1: D is at the end of string. */ + if (AT_STRINGS_END (d)) + goto fail; + else + { + /* C1 is the character before D, S1 is the syntax of C1, C2 + is the character at D, and S2 is the syntax of C2. */ + int c1, c2, s1, s2; + int pos1 = PTR_TO_OFFSET (d); + int charpos; + + GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); +#ifdef emacs + charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); + UPDATE_SYNTAX_TABLE (charpos); +#endif + s2 = SYNTAX (c2); + + /* Case 2: S2 is not Sword. */ + if (s2 != Sword) + goto fail; + + /* Case 3: D is not at the beginning of string ... */ + if (!AT_STRINGS_BEG (d)) + { + GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); +#ifdef emacs + UPDATE_SYNTAX_TABLE_BACKWARD (charpos - 1); +#endif + s1 = SYNTAX (c1); + + /* ... and S1 is Sword, and WORD_BOUNDARY_P (C1, C2) + returns 0. */ + if ((s1 == Sword) && !WORD_BOUNDARY_P (c1, c2)) + goto fail; + } + } + break; case wordend: - DEBUG_PRINT1 ("EXECUTING wordend.\n"); - if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) - && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) - break; - goto fail; + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + + /* We FAIL in one of the following cases: */ + + /* Case 1: D is at the beginning of string. */ + if (AT_STRINGS_BEG (d)) + goto fail; + else + { + /* C1 is the character before D, S1 is the syntax of C1, C2 + is the character at D, and S2 is the syntax of C2. */ + int c1, c2, s1, s2; + int pos1 = PTR_TO_OFFSET (d); + int charpos; + GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); #ifdef emacs - case before_dot: - DEBUG_PRINT1 ("EXECUTING before_dot.\n"); - if (PTR_CHAR_POS ((unsigned char *) d) >= point) - goto fail; - break; - - case at_dot: - DEBUG_PRINT1 ("EXECUTING at_dot.\n"); - if (PTR_CHAR_POS ((unsigned char *) d) != point) - goto fail; - break; - - case after_dot: - DEBUG_PRINT1 ("EXECUTING after_dot.\n"); - if (PTR_CHAR_POS ((unsigned char *) d) <= point) - goto fail; - break; + charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1 - 1); + UPDATE_SYNTAX_TABLE (charpos); +#endif + s1 = SYNTAX (c1); + + /* Case 2: S1 is not Sword. */ + if (s1 != Sword) + goto fail; + + /* Case 3: D is not at the end of string ... */ + if (!AT_STRINGS_END (d)) + { + GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); +#ifdef emacs + UPDATE_SYNTAX_TABLE_FORWARD (charpos); +#endif + s2 = SYNTAX (c2); + + /* ... and S2 is Sword, and WORD_BOUNDARY_P (C1, C2) + returns 0. */ + if ((s2 == Sword) && !WORD_BOUNDARY_P (c1, c2)) + goto fail; + } + } + break; + +#ifdef emacs + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_BYTE_POS ((unsigned char *) d) >= PT_BYTE) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_BYTE_POS ((unsigned char *) d) != PT_BYTE) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_BYTE_POS ((unsigned char *) d) <= PT_BYTE) + goto fail; + break; case syntaxspec: - DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); mcnt = *p++; goto matchsyntax; - case wordchar: - DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); mcnt = (int) Sword; - matchsyntax: + matchsyntax: PREFETCH (); - if (SYNTAX (*d++) != (enum syntaxcode) mcnt) - goto fail; - /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ - d++; - if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt) +#ifdef emacs + { + int pos1 = SYNTAX_TABLE_BYTE_TO_CHAR (PTR_TO_OFFSET (d)); + UPDATE_SYNTAX_TABLE (pos1); + } +#endif + { + int c, len; + + if (multibyte) + /* we must concern about multibyte form, ... */ + c = STRING_CHAR_AND_LENGTH (d, dend - d, len); + else + /* everything should be handled as ASCII, even though it + looks like multibyte form. */ + c = *d, len = 1; + + if (SYNTAX (c) != (enum syntaxcode) mcnt) goto fail; - SET_REGS_MATCHED (); + d += len; + } + SET_REGS_MATCHED (); break; case notsyntaxspec: - DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); mcnt = *p++; goto matchnotsyntax; - case notwordchar: - DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); mcnt = (int) Sword; - matchnotsyntax: + matchnotsyntax: PREFETCH (); - /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ - d++; - if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt) +#ifdef emacs + { + int pos1 = SYNTAX_TABLE_BYTE_TO_CHAR (PTR_TO_OFFSET (d)); + UPDATE_SYNTAX_TABLE (pos1); + } +#endif + { + int c, len; + + if (multibyte) + c = STRING_CHAR_AND_LENGTH (d, dend - d, len); + else + c = *d, len = 1; + + if (SYNTAX (c) == (enum syntaxcode) mcnt) goto fail; + d += len; + } + SET_REGS_MATCHED (); + break; + + case categoryspec: + DEBUG_PRINT2 ("EXECUTING categoryspec %d.\n", *p); + mcnt = *p++; + PREFETCH (); + { + int c, len; + + if (multibyte) + c = STRING_CHAR_AND_LENGTH (d, dend - d, len); + else + c = *d, len = 1; + + if (!CHAR_HAS_CATEGORY (c, mcnt)) + goto fail; + d += len; + } + SET_REGS_MATCHED (); + break; + + case notcategoryspec: + DEBUG_PRINT2 ("EXECUTING notcategoryspec %d.\n", *p); + mcnt = *p++; + PREFETCH (); + { + int c, len; + + if (multibyte) + c = STRING_CHAR_AND_LENGTH (d, dend - d, len); + else + c = *d, len = 1; + + if (CHAR_HAS_CATEGORY (c, mcnt)) + goto fail; + d += len; + } SET_REGS_MATCHED (); break; @@ -4338,6 +5688,9 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) /* We goto here if a matching operation fails. */ fail: +#if defined (WINDOWSNT) && defined (emacs) + QUIT; +#endif if (!FAIL_STACK_EMPTY ()) { /* A restart point is known. Restore to that state. */ DEBUG_PRINT1 ("\nFAIL:\n"); @@ -4644,14 +5997,30 @@ static int bcmp_translate (s1, s2, len, translate) unsigned char *s1, *s2; register int len; - char *translate; + RE_TRANSLATE_TYPE translate; { register unsigned char *p1 = s1, *p2 = s2; - while (len) + unsigned char *p1_end = s1 + len; + unsigned char *p2_end = s2 + len; + + while (p1 != p1_end && p2 != p2_end) { - if (translate[*p1++] != translate[*p2++]) return 1; - len--; + int p1_charlen, p2_charlen; + int p1_ch, p2_ch; + + p1_ch = STRING_CHAR_AND_LENGTH (p1, p1_end - p1, p1_charlen); + p2_ch = STRING_CHAR_AND_LENGTH (p2, p2_end - p2, p2_charlen); + + if (RE_TRANSLATE (translate, p1_ch) + != RE_TRANSLATE (translate, p2_ch)) + return 1; + + p1 += p1_charlen, p2 += p2_charlen; } + + if (p1 != p1_end || p2 != p2_end) + return 1; + return 0; } @@ -4688,18 +6057,26 @@ re_compile_pattern (pattern, length, bufp) ret = regex_compile (pattern, length, re_syntax_options, bufp); - return re_error_msg[(int) ret]; -} + if (!ret) + return NULL; + return gettext (re_error_msgid[(int) ret]); +} /* Entry points compatible with 4.2 BSD regex library. We don't define - them if this is an Emacs or POSIX compilation. */ + them unless specifically requested. */ -#if !defined (emacs) +#if defined (_REGEX_RE_COMP) || defined (_LIBC) /* BSD has one and only one pattern buffer. */ static struct re_pattern_buffer re_comp_buf; char * +#ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec below without link errors. */ +weak_function +#endif re_comp (s) const char *s; { @@ -4708,7 +6085,7 @@ re_comp (s) if (!s) { if (!re_comp_buf.buffer) - return "No previous regular expression"; + return gettext ("No previous regular expression"); return 0; } @@ -4716,12 +6093,14 @@ re_comp (s) { re_comp_buf.buffer = (unsigned char *) malloc (200); if (re_comp_buf.buffer == NULL) - return "Memory exhausted"; + /* CVS: Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (re_error_msgid[(int) REG_ESPACE]); re_comp_buf.allocated = 200; re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); if (re_comp_buf.fastmap == NULL) - return "Memory exhausted"; + /* CVS: Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (re_error_msgid[(int) REG_ESPACE]); } /* Since `re_exec' always passes NULL for the `regs' argument, we @@ -4731,13 +6110,19 @@ re_comp (s) re_comp_buf.newline_anchor = 1; ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); - - /* Yes, we're discarding `const' here. */ - return (char *) re_error_msg[(int) ret]; + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (re_error_msgid[(int) ret]); } int +#ifdef _LIBC +weak_function +#endif re_exec (s) const char *s; { @@ -4745,7 +6130,7 @@ re_exec (s) return 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); } -#endif /* not emacs and not _POSIX_SOURCE */ +#endif /* _REGEX_RE_COMP */ /* POSIX.2 functions. Don't define these for Emacs. */ @@ -4799,6 +6184,7 @@ regcomp (preg, pattern, cflags) /* regex_compile will allocate the space for the compiled pattern. */ preg->buffer = 0; preg->allocated = 0; + preg->used = 0; /* Don't bother to use a fastmap when searching. This simplifies the REG_NEWLINE case: if we used a fastmap, we'd have to put all the @@ -4810,7 +6196,9 @@ regcomp (preg, pattern, cflags) { unsigned i; - preg->translate = (char *) malloc (CHAR_SET_SIZE); + preg->translate + = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE + * sizeof (*(RE_TRANSLATE_TYPE)0)); if (preg->translate == NULL) return (int) REG_ESPACE; @@ -4936,19 +6324,14 @@ regerror (errcode, preg, errbuf, errbuf_size) size_t msg_size; if (errcode < 0 - || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0]))) + || errcode >= (sizeof (re_error_msgid) / sizeof (re_error_msgid[0]))) /* Only error codes returned by the rest of the code should be passed to this routine. If we are given anything else, or if other regex code generates an invalid error code, then the program has a bug. Dump core so we can fix it. */ abort (); - msg = re_error_msg[errcode]; - - /* POSIX doesn't require that we do anything in this case, but why - not be nice. */ - if (! msg) - msg = "Success"; + msg = gettext (re_error_msgid[errcode]); msg_size = strlen (msg) + 1; /* Includes the null. */ diff --git a/gnu/usr.bin/cvs/lib/regex.h b/gnu/usr.bin/cvs/lib/regex.h index 6ab52c38a5d..411e7d26edc 100644 --- a/gnu/usr.bin/cvs/lib/regex.h +++ b/gnu/usr.bin/cvs/lib/regex.h @@ -1,7 +1,7 @@ /* Definitions for data structures and routines for the regular expression library, version 0.12. - Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + Copyright (C) 1985, 89, 90, 91, 92, 93, 95 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 @@ -11,7 +11,12 @@ 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. */ + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ #ifndef __REGEXP_LIBRARY_H__ #define __REGEXP_LIBRARY_H__ @@ -19,7 +24,7 @@ /* POSIX says that <sys/types.h> must be included (by the caller) before <regex.h>. */ -#ifdef VMS +#if !defined (_POSIX_C_SOURCE) && !defined (_POSIX_SOURCE) && defined (VMS) /* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it should be there. */ #include <stddef.h> @@ -126,11 +131,22 @@ typedef unsigned reg_syntax_t; If not set, then an unmatched ) is invalid. */ #define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + /* This global variable defines the particular regexp syntax to use (for some interfaces). When a regexp is compiled, the syntax used is stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; + +#ifdef emacs +/* In Emacs, this is the string or buffer in which we + are matching. It is used for looking up syntax properties. */ +extern Lisp_Object re_match_object; +#endif + /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so @@ -271,6 +287,12 @@ typedef enum compiled, the `re_nsub' field is available. All other fields are private to the regex routines. */ +#ifndef RE_TRANSLATE_TYPE +#define RE_TRANSLATE_TYPE char * +#define RE_TRANSLATE(TBL, C) ((TBL)[C]) +#define RE_TRANSLATE_P(TBL) (TBL) +#endif + struct re_pattern_buffer { /* [[[begin pattern_buffer]]] */ @@ -297,7 +319,7 @@ struct re_pattern_buffer comparing them, or zero for no translation. The translation is applied to a pattern when it is compiled and to a string when it is matched. */ - char *translate; + RE_TRANSLATE_TYPE translate; /* Number of subexpressions found by the compiler. */ size_t re_nsub; @@ -336,15 +358,14 @@ struct re_pattern_buffer /* If true, an anchor at a newline matches. */ unsigned newline_anchor : 1; + /* If true, multi-byte form in the `buffer' should be recognized as a + multibyte character. */ + unsigned multibyte : 1; + /* [[[end pattern_buffer]]] */ }; typedef struct re_pattern_buffer regex_t; - - -/* search.c (search_buffer) in Emacs needs this one opcode value. It is - defined both in `regex.c' and here. */ -#define RE_EXACTN_VALUE 1 /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; @@ -461,11 +482,12 @@ extern void re_set_registers _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends)); -/* 4.2 bsd compatibility. System headers may declare the argument as - either "char *" (e.g. Cray unistd.h) or "const char *" (e.g. linux - regex.h), so don't prototype them here. */ -extern char *re_comp (); -extern int re_exec (); +#ifdef _REGEX_RE_COMP +/* 4.2 bsd compatibility. */ +/* CVS: don't use prototypes: they may conflict with system headers. */ +extern char *re_comp _RE_ARGS (()); +extern int re_exec _RE_ARGS (()); +#endif /* POSIX compatibility. */ extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); diff --git a/gnu/usr.bin/cvs/lib/savecwd.c b/gnu/usr.bin/cvs/lib/savecwd.c index b5b7cb86a46..2d687c32ce6 100644 --- a/gnu/usr.bin/cvs/lib/savecwd.c +++ b/gnu/usr.bin/cvs/lib/savecwd.c @@ -13,6 +13,7 @@ #endif #ifdef HAVE_FCNTL_H +# include <sys/types.h> # include <fcntl.h> #else # include <sys/file.h> diff --git a/gnu/usr.bin/cvs/man/ChangeLog b/gnu/usr.bin/cvs/man/ChangeLog index 38cc91b0bf5..adb8e2dfec0 100644 --- a/gnu/usr.bin/cvs/man/ChangeLog +++ b/gnu/usr.bin/cvs/man/ChangeLog @@ -1,3 +1,37 @@ +2000-09-07 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@ + from autoconf. + +2000-05-03 Larry Jones <larry.jones@sdrc.com> + + * cvs.1: Correct CVSEDITOR/VISUAL/EDITOR documentation. + Correct KJ's botched patch application. + +2000-01-02 John P Cavanaugh <cavanaug@sr.hp.com> + + * cvs.texinfo: document new -C option to update, now that it works + both remotely and locally. + (Re-applied by Karl Fogel <kfogel@red-bean.com>.) + +1999-12-11 Karl Fogel <kfogel@red-bean.com> + + * Revert previous change -- it doesn't work remotely yet. + +1999-12-10 John P Cavanaugh <cavanaug@sr.hp.com> + + * cvs.1: document new -C option to update. + (Applied by Karl Fogel <kfogel@red-bean.com>.) + +1999-11-29 Larry Jones <larry.jones@sdrc.com> + + * cvsbug.8: Change .TH from section 1 to section 8. + +1999-11-10 Jim Kingdon <http://developer.redhat.com/> + + * cvs.1: Don't document -H as a command option; it has been a + while since that worked. + 1999-01-19 Vitaly V Fedrushkov <willy@snowyowl.csu.ac.ru> * Makefile.in (INSTALL_DATA): Wrong manpage permissions fixed. diff --git a/gnu/usr.bin/cvs/man/Makefile.in b/gnu/usr.bin/cvs/man/Makefile.in index d92d494e77e..f9b566585eb 100644 --- a/gnu/usr.bin/cvs/man/Makefile.in +++ b/gnu/usr.bin/cvs/man/Makefile.in @@ -27,7 +27,7 @@ DISTFILES = .cvsignore ChangeLog Makefile.in $(MANFILES) INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ prefix = @prefix@ -mandir = $(prefix)/man +mandir = @mandir@ man1dir = $(mandir)/man1 man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 diff --git a/gnu/usr.bin/cvs/man/cvsbug.8 b/gnu/usr.bin/cvs/man/cvsbug.8 index 63574d42875..ecf2eb8a7ee 100644 --- a/gnu/usr.bin/cvs/man/cvsbug.8 +++ b/gnu/usr.bin/cvs/man/cvsbug.8 @@ -18,7 +18,7 @@ .\" .\" --------------------------------------------------------------------------- .nh -.TH CVSBUG 1 xVERSIONx "February 1993" +.TH CVSBUG 8 xVERSIONx "February 1993" .SH NAME cvsbug \- send problem report (PR) about CVS to a central support site .SH SYNOPSIS diff --git a/gnu/usr.bin/cvs/mkinstalldirs b/gnu/usr.bin/cvs/mkinstalldirs index 91f6d04e17c..9259785ed4f 100644 --- a/gnu/usr.bin/cvs/mkinstalldirs +++ b/gnu/usr.bin/cvs/mkinstalldirs @@ -4,6 +4,7 @@ # Created: 1993-05-16 # Last modified: 1994-03-25 # Public domain +# errstatus=0 diff --git a/gnu/usr.bin/cvs/src/ChangeLog b/gnu/usr.bin/cvs/src/ChangeLog index 0d1889185a3..a33464d08b4 100644 --- a/gnu/usr.bin/cvs/src/ChangeLog +++ b/gnu/usr.bin/cvs/src/ChangeLog @@ -1,3 +1,1104 @@ +2000-09-19 Larry Jones <larry.jones@sdrc.com> + + * version.c: Version 1.11. + +2000-09-07 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@ + from autoconf. + +2000-08-23 Larry Jones <larry.jones@sdrc.com> + + * mkmodules.c (init): Create an empty val-tags file if it doesn't + already exist to avoid problems with users not having sufficient + permissions to create it later. + +2000-09-06 Jim Kingdon <jkingdon@dhcp-net200-89.su.valinux.com> + + * main.c (lookup_command_attribute): Add "release" to commands + which can be done by a read-only user. + +2000-08-23 Larry Jones <larry.jones@sdrc.com> + + * repos.c (Name_Repository): Use pathname_levels to detect attempts + to get above the repository instead of checking for leading .. + which isn't reliable. + * sanity.sh (multiroot3-12 to multiroot3-15): New tests for above. + +2000-08-21 Larry Jones <larry.jones@sdrc.com> + + * rcs.c (expand_keywords): Handle the unusual case of log == NULL. + (Reported by Craig Metz <cmetz@inner.net>.) + +2000-08-01 Larry Jones <larry.jones@sdrc.com> + + * subr.c (pathname_levels): Fix bug that miscounts adjacent + slashes. + (Patch submitted by Tanaka Akira <akr@m17n.org>.) + + * loginc.c (login): If available, use getpassphrase instead of + getpass to support long passwords on Solaris. + +2000-07-28 Larry Jones <larry.jones@sdrc.com> + + * server.c (server_noop): Avoid do_cvs_command() overhead. + (requests): Make noop RQ_ROOTLESS. + +2000-07-27 Noel Cragg <noel@red-bean.com> + + * root.c (parse_cvsroot): change fork method to behave like other + remote methods -- let the server check that the repository + directory is an absolute pathname. + +2000-07-27 Larry Jones <larry.jones@sdrc.com> + + * lock.c (set_lock): Include actual lock directory in error message. + * sanity.sh (multiroot3-10): Change to match. + + * sanity.sh (client-3): Allow for a potential "broken pipe". + +2000-07-26 Larry Jones <larry.jones@sdrc.com> + + * commit.c (commit_filesdoneproc): Flush stdout before running script. + * modules.c (do_module): Ditto. + * update.c (update_dirleave_proc): Ditto. + * server.c (do_cvs_command): Give input from the protocol pipe + precedence over input from stdout/stderr. There's no particularly + good justification for this other than helping to avoid out-of-order + messages in sanity.sh. + + * admin.c (admin_usage): Add the supported options. + + * sanity.sh (info): Try to avoid out-of-order messages. + + * sanity.sh (info): Fix problems when running twice in a row. + +2000-07-17 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (modules5-7, cvsadm-1e, emptydir-2): Allow for a nil + commit (can happen if the test is run twice in a row). + +2000-07-19 Pavel Roskin <proski@gnu.org> + and Larry Jones <larry.jones@sdrc.com> + + * mkmodules.c (config_contents): Add a commented out example for + LockDir. Don't suggest PreservePermissions unless it's enabled. + +2000-07-17 Larry Jones <larry.jones@sdrc.com> + + * login.c (get_cvs_password): Handle malformed ~/.cvspass more + gracefully. + +2000-07-12 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (modules5): New tests for module programs. + +2000-07-11 Larry Jones <larry.jones@sdrc.com> + + * filesubr.c (copy_file, xcmp): Handle systems (like Plan 9) that + don't support mknod() and/or st_rdev. + * import.c (add_rcs_file): Ditto. + * rcs.c (RCS_checkout, RCS_checkin): Ditto. + * update.c (special_file_mismatch): Ditto. + +2000-07-10 Larry Jones <larry.jones@sdrc.com> + + * zlib.c (gunzip_and_write): Fix type clashes. + + * main.c (main): Remove unused variables. + +2000-07-10 Jim Meyering <meyering@lucent.com> + + When a command like `cvs update -kk -jT1 -jT2' creates a new file + (because it had the T2 tag, but not T1), the subsequent commit of + that just-added file would effectively set the admin `-kk' option + for that file in the repository. + + * update.c (join_file): Rename global-shadowing local `options' + to `t_options'. + Set file-scoped global `options' to NULL just before + check-out. + * sanity.sh (join-admin): New test for this. + +2000-07-08 Larry Jones <larry.jones@sdrc.com> + + * version.c, cvs.h (version): New function. + * main.c (cmds[]): Add version command to invoke it. + (main): Also use it in -v. + * server.c (serve_version): New function. + (requests[]): Add version command to invoke it. + +2000-07-06 Karl Fogel <kfogel@red-bean.com> + + * sanity.sh (pserver-14): remove this test for portability + reasons (it was only recently added for the 2000-07-04 change). + +2000-07-06 Larry Jones <larry.jones@sdrc.com> + + sanity.sh (modules-148): Don't test for specific revisions. + + * main.c (main): Catch SIGABRT to try to clean up after assertion + failures. Don't bother SIG_register'ing Lock_Cleanup because + main_cleanup calls it indirectly anyway. + * patch.c (patch): Catch SIGABRT. + * rcs.c (rcs_internal_lockfile): Ditto. + * server.c (server): Ditto. + + * fileattr.c (fileattr_write): Don't delete the unrecog_head list + when writing... + (fileattr_free): Delete it when freeing! + +2000-07-05 Larry Jones <larry.jones@sdrc.com> + + * admin.c (admin): Handle -t in client so reading from files works + correctly in client/server mode. + * sanity.sh (log2): Update to match. + +2000-07-04 Karl Fogel <kfogel@red-bean.com> + + * server.c (pserver_authenticate_connection): use new + getline_safe() during authentication phase, to avoid a + denial-of-service attack in which client sends arbitrary + amounts of data with no newlines. + (Reported by <jpmg@eng.cam.ac.uk>.) + + * sanity.sh: new test pserver-14 for above. + + * myndbm.c: #include getline.h. + (mydbm_load_file): pass new GETLINE_NO_LIMIT flag to getstr(). + +2000-07-03 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (modules): Rewrite using dotest. Add "modules-" + prefix to test names. + +2000-06-28 Larry Jones <larry.jones@sdrc.com> + + * error.c (error_exit): Call rcs_cleanup () to release any rcs locks. + * rcs.c, rcs.h (rcs_cleanup): Make public, close file before trying + to remove (some systems won't remove open files). + (RCS_putdtree): Don't worry about cleaning up before call error + since it now does it for us. + (rcs_internal_lockfile, rcs_internal_unlockfile): Keep track of + lock file fd for rcs_cleanup (). + + * client.c (handle_set_checkin_prog, handle_set_update_prog): + Just ignore the request when exporting. + +2000-06-27 Larry Jones <larry.jones@sdrc.com> + + * create_adm.c, cvs.h (Create_Admin): Add dotemplate argument. + Change all callers. + * checkout.c (checkout_proc): Don't create CVS/Template if + exporting. + +2000-06-26 Pavel Roskin <proski@gnu.org> + and Larry Jones <larry.jones@sdrc.com> + + * server.c (switch_to_user): Only set CVS_Username if + AUTH_SERVER_SUPPORT is defined. + +2000-06-23 Larry Jones <larry.jones@sdrc.com> + + * client.c (send_dirent_proc): Don't allocate ignlist if you're + going to skip the directory (plugs memory leak). + (send_dirleave_proc): New function. + (send_files): Use it (plugs memory leak). + * root.c (root_allow_free): Plug memory leaks. + * server.c (serve_directory, serve_notify, check_password, + pserver_authenticate_connection): Ditto. + * update.c (update): Ditto. + + This completes the memory leak shoot-out -- the Purify'ed version + of CVS now runs the entire test suite, both local and remote (except + for remote crerepos, which causes Purify to choke) with *no* memory + leaks. + + * server.c (pserver_authenticate_connection): Don't free null pointer. + +2000-06-21 Larry Jones <larry.jones@sdrc.com> + + * client.c (update_entries, get_responses_and_close): Plug memory leaks. + * commit.c (find_fileproc, commit): Ditto. + * import.c (import): Ditto. + * log.c (cvslog): Ditto. + * recurse.c (start_recursion): Ditto. + * remove.c (cvsremove): Ditto. + * server.c (fd_buffer_initialize, server_notify, do_cvs_command): Ditto. + (fd_buffer_shutdown): New function. + +2000-06-20 Larry Jones <larry.jones@sdrc.com> + + * root.c (parse_cvsroot): Put the terminating NUL byte into the + string *before* copying it, not after. :-( + +2000-06-19 Larry Jones <larry.jones@sdrc.com> + + * main.c (main): Plug memory leaks. + * root.c (parse_cvsroot, set_local_cvsroot): Ditto. + * server.c (serve_root): Ditto. + +2000-06-16 Larry Jones <larry.jones@sdrc.com> + + * fileattr.c (fileattr_read): Plug memory leak. + * rcs.c (RCS_whatbranch): Ditto. + * update.c (update_dirleave_proc): Ditto. + + * ignore.c (ign_dir_add): Duplicate string so caller can free. + + * modules.c (do_module): Don't write into dbm's memory! + +2000-06-15 Larry Jones <larry.jones@sdrc.com> + + * checkout.c (checkout_proc): Fix non-ANSI code in call to + findslash(), minor cleanups. + +2000-06-14 Larry Jones <larry.jones@sdrc.com> + + * tag.c (val_direntproc): Return R_PROCESS instead of 0. + + * client.c (update_entries): Fix type clash calling gunzip_and_write(). + * server.c (receive_file): Fix type clash calling gunzip_and_write(). + (server_updated): Fix type clash calling buf_output(). + * error.c (error): Make buf char instead of unsigned char to avoid + type clashes. + + * modules.c (do_module): Change callback_proc to pass argc by + value instead of by reference: callback procs shouldn't be + messing with the callers argc/argv, it makes correct memory + management impossible. Plug memory leaks. + * cvs.h: Change to match. + * checkout.c (checkout_proc): Ditto; use a local argv array instead + of messing with caller's. + * modules.c (callback_proc): Ditto. + * patch.c (patch_proc): Ditto; use a local argv array instead + of messing with caller's. + * rtag.c (rtag_proc): Ditto; use a local argv array instead + of messing with caller's. + * server.c (expand_proc): Ditto. + * subr.c (line2argv): Change initial argv_allocated back to 1. + + * checkout.c (findslash): Fix non-ANSI code. + + * sanity.sh (modes3): Fix test names. + +2000-06-13 Larry Jones <larry.jones@sdrc.com> + + * add.c (add): Plug memory leaks. + * admin.c (admin_fileproc): Ditto. + * checkout.c (build_dirs_and_chdir): Ditto. + * edit.c (editors_fileproc): Ditto. + * log.c (cvslog, log_parse_revlist, log_parse_date): Ditto. + * rcs.c (RCS_addaccess): Ditto. + * tag.c (check_fileproc): Ditto. + * vers_ts.c (Version_TS): Ditto. + * watch.c (watchers_fileproc): Ditto. + +2000-06-12 Larry Jones <larry.jones@sdrc.com> + + * rcs.c (rcsbuf_valword): Set rcsbuf->vlen to keep rcsbuf_valcopy() + from allocating more memory than needed for @ strings. Don't declare + unless PRESERVE_PERMISSIONS_SUPPORT (since not defined). + + * rcs.c (RCS_abandon): New function to abandon changes. + * rcs.h: Declare it. + * admin.c (admin_fileproc): Use it instead of RCS_reparsercsfile. + + * commit.c (commit_fileproc): Fix memory leaks. + * patch.c (patch_fileproc): Ditto. + * rcs.c (RCS_nodeisbranch, RCS_copydeltas): Ditto. + * tag.c (tag_fileproc): Ditto. + * update.c (update): Ditto. + +2000-06-09 Larry Jones <larry.jones@sdrc.com> + + * rcs.c (RCS_reparsercsfile, RCS_fully_parse, getdelta, + RCS_getdeltatext): Handle newphrases with composite values. + (rcsbuf_getkey): Don't remove @s in composite values -- it makes + it impossible to parse the value! Set special flag to indicate + a composite value. + (rcsbuf_valcopy, rcsbuf_valpolish_internal): Handle composite values. + (putrcsfield): Write composite values. + (RCS_checkin): Set node types in other_delta list. + * hash.h: Add RCSCMPFLD. + * hash.c (nodetypestring): Ditto. + + * rcs.c (getdelta): Never allocate space for value, just return + pointer into rcsbuf (fixes memory leaks). Use rcsbuf_getkey to + read a key and value and then parse the value if needed rather + than trying to read it in bits and pieces with rcsbuf_getid, + rcsbuf_getstring, and rcsbuf_getword. + (RCS_reparsercsfile): Change callers to compensate. + (rcsbuf_valcmp, rcsbuf_valword): New functions. + (rcsbuf_getid, rcsbuf_getstring, rcsbuf_getword): Deleted. + * sanity.sh (rcs3-1): Now get slightly different error message. + +2000-06-08 Larry Jones <larry.jones@sdrc.com> + + * main.c (usg): Update CVS home page URL. + + * main.c (main): Provide an actual error message for an unknown + command in addition to the usage message. + +2000-06-07 Larry Jones <larry.jones@sdrc.com> + + * server.c (serve_root, dirswitch, serve_repository, + serve_static_directory, serve_sticky, receive_partial_file, + receive_file, serve_modified, server_write_entries, serve_notify, + serve_checkin_prog, serve_update_prog, server): Don't set + pending_error before calling alloc_pending, it makes it fail; + use alloc_pending instead of malloc when reasonable; be sure to + save errno before calling functions that might change it. + (Patch submitted by Dietmar Petras <dietmar.petras@elsa.de>.) + +2000-06-03 Larry Jones <larry.jones@sdrc.com> + + * commit.c (checkaddfile): Plug memory leak. + * rcs.c (RCS_checkin): Plug memory leaks. + * server.c (do_cvs_command): Plug file descriptor leaks. + * tag.c (check_fileproc): Plug memory leak. + +2000-05-26 Larry Jones <larry.jones@sdrc.com> + + * recurse.c (unroll_files_proc): Plug memory leak. + + * recurse.c (addfile): Fix nonportable pointer cast. + + * rcs.c (rcsbuf_getstring, rcsbuf_getword, getdelta): Plug memory + leaks. + +2000-05-25 Larry Jones <larry.jones@sdrc.com> + + * checkout.c (checkout, build_one_dir, checkout_proc): Move m_type + to file scope and use it instead of continually doing strcmp on + command_name. + (build_one_dir, checkout_proc): Don't allow export if CVSADM + directory already exists. + +2000-05-23 Larry Jones <larry.jones@sdrc.com> + + * rcs.c (RCS_checkin, RCS_cmp_file): Plug memory leaks. (Patch + submitted by Chris G. Demetriou <cgd@sibyte.com>.) + +2000-05-20 Ian Lance Taylor <ian@zembu.com> + + * client.c (connect_to_gserver): Handle server error messages + reasonably. + +2000-05-19 Larry Jones <larry.jones@sdrc.com> + + * server.c (requests): Make Global_option RQ_ROOTLESS so it can be + used with init. + +2000-05-18 Larry Jones <larry.jones@sdrc.com> + + * client.c (start_server): Don't do encryption, authentication, + compression, or case insensitivity when doing init because init + is ROOTLESS and they're not. + + * client.c (connect_to_pserver): Include repository and username in + authorization failed message -- if a directory tree crosses multiple + repositories, it can be quite difficult for the user to figure out + which one is the problem. + +2000-05-17 Larry Jones <larry.jones@sdrc.com> + + * main.c (main): Use full set of options when looking for -f to + avoid misparsing options that take values (previously, -sVAR=foo + was incorrectly parsed as though it were -s -V -A -R -= -f -o -o + because it didn't know that -s takes a value). + * sanity.sh (info-6b): New test for above. + + * sanity.sh (conflicts-status): Fix tests so they work remotely, too. + +2000-05-17 Jim Meyering <meyering@lucent.com> + + * sanity.sh (TESTDIR): Fix braino in last change: + cd to /tmp before invoking pwd. + + * sanity.sh: Set TESTDIR so that `make check' passes even when /tmp + is a symlink. + (join-36): Use $TESTDIR rather than hard-coding `/tmp/cvs-sanity'. + (conflicts-132): Remove unnecessary `rm aa'. + +2000-05-16 Jim Kingdon <kingdon@redhat.com> + + * cvs.h, checkout.c (safe_location): Make extern. + * import.c (import): Call it rather than reimplementing + (incompletely) the same check. + +2000-05-16 Larry Jones <larry.jones@sdrc.com> + + * rcs.h, subr.c (file_has_markers): Check for any of the three + conflict marker lines, not just one. + * sanity.sh (conflicts-status): New tests for above. + * sanity.sh: Revise to avoid tripping the above check when merging + changes into sanity.sh itself. + +2000-05-15 Larry Jones <larry.jones@sdrc.com> + + * update.c (join_file): When registering the result of the merge, + make sure that the version number is valid (vers->vn_rcs may be + null if the file doesn't exist on the branch yet). (Patch submitted + by Robert de Vries <rhdv@rhdv.cistron.nl>.) + * update.c (join_file): Correct diagnostics (previous change was not + correct -- the file *does* exist in the specified revision, it just + doesn't exist in the sandbox). + * sanity.sh (import-113, join): New tests and changes for above. + +2000-05-04 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh: Look for a useful id program. Since we're getting + the real username for some tests anyway, use it for all the + tests instead of a generic regular expression that may or may + not match the actual username. + +2000-05-04 Larry Jones <larry.jones@sdrc.com> + + * server.c: More error messages. + +2000-05-02 Donald Sharp <sharpd@cisco.com> + and Larry Jones <larry.jones@sdrc.com> + + * history.c (report_hrecs): Added code to print out year instead of + just month/day. + * sanity.sh (basic2-64, history): Update to match. + +2000-04-19 Larry Jones <larry.jones@sdrc.com> + + * server.c (dirswitch): Set pending_error_text in addition to + pending_error to aid in problem determination. + +2000-03-23 Larry Jones <larry.jones@sdrc.com> + + * mkmodules.c (mkmodules): Return without doing anything if noexec + is set to avoid trashing existing files. + +2000-03-23 Larry Jones <larry.jones@sdrc.com> + + * main.c: Alphabetize cmds[] and cmd_usage[] and add server + commands to cmd_usage[]. + +2000-03-21 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (client-1): May get "Broken pipe" message from the + "server" in addition to the expected output. + +2000-03-17 Larry Jones <larry.jones@sdrc.com> + + * server.c (switch_to_user): Set CVS_Username if it hasn't already + been set elsewhere. (Patch submitted by Gordon Matzigkeit + <gord@fig.org>). + +2000-03-13 Larry Jones <larry.jones@sdrc.com> + + * parseinfo.c: Add extern to logHistory declaration. (Reported by + <John.Tytgat@aaug.net>.) + (parse_config): Reformat logHistory code. + +2000-03-10 Larry Jones <larry.jones@sdrc.com> + + * add.c (add): Don't try to set cvsroot_len until after checking + for help only -- CVSroot_directory isn't set in that case. + +2000-03-03 Larry Jones <larry.jones@sdrc.com> + + * mkmodules.c (init): Use mkdir_if_needed to create CVSROOT/Emptydir + so we don't fail if run multiple times. (Reported by KOIE Hidetaka + <hide@koie.org>.) + * sanity.sh (1a): New test for above. + +2000-03-02 Larry Jones <larry.jones@sdrc.com> + + * main.c: Use identical #if's in the command table and the code + for pserver and kserver to prevent "peculiar" configurations from + having really perverse behavior because the command table entries + are present but the related code isn't. + +2000-03-01 Larry Jones <larry.jones@sdrc.com> + + * import.c (import): Don't allow importing the repository. + * sanity.sh (errmsg2-20, errmsg2-21): New tests for above. + +2000-03-01 Larry Jones <larry.jones@sdrc.com> + + * main.c (main): Update year in copyright message. + +2000-03-01 Larry Jones <larry.jones@sdrc.com> + + * logmsg.c (do_editor): Correct previous change. + +2000-02-29 Larry Jones <larry.jones@sdrc.com> + + * logmsg.c (do_editor): When reading temp file, check that message + buffer is large enough to hold the next line and expand if needed. + +2000-02-28 Larry Jones <larry.jones@sdrc.com> + + * commit.c (commit): Use get_file() to read log file correctly + and in text mode rather than binary mode. + + * subr.c (get_file): Ignore bufsize if buf is NULL. Include + terminating NUL byte when estimating required buffer size. + +2000-02-28 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (find_tool): New function to replace duplicated code. + +2000-02-25 Larry Jones <larry.jones@sdrc.com> + + * import.c (add_rcs_file): Don't abort just because lstat fails. + +2000-02-16 Jim Meyering <meyering@lucent.com> + + Avoid race condition whereby a catchable signal could + end up corrupting the repository. + * commit.c (checkaddfile): Put a critical section around the code + that handles the first commit on the trunk of a file that's already + been committed on a branch. + * cvs.h (Sig_inCrSect): Declare new function. + +2000-02-21 Karl Fogel <kfogel@red-bean.com> + + * main.c (main): still check for repository, but not history file + (correction to 2000-02-18 change -- that's what I get for + believing the comment rather than the code). + +2000-02-21 K.J. Paradise <kj@sourcegear.com> + + * history.c mkmodules.c parseinfo.c: control which actions + get logged to the cvs history file via CVSROOT/config file + and LogHistory keyword. (John P Cavanaugh <cavanaug@sr.hp.com>) + +2000-02-18 Karl Fogel <kfogel@red-bean.com> + + * history.c (history_write): don't die if history file not + writable, just warn (unless `really_quiet') and skip out. + + * main.c (main): don't bother checking if history file is + writable. + + * server.c (serve_root): same. + +2000-02-17 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (perms symlinks symlinks2 hardlinks): Don't run by + default since PreservePermissions code is now disabled. + +2000-02-17 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (import-113): Revise to match Jim Meyering's fix. + +2000-02-16 Larry Jones <larry.jones@sdrc.com> + + * add.c (add): Don't allow adding files or directories to Emptydir. + (Patch submitted by Chris Cameron <chris.cameron@ot.co.nz>.) + * sanity.sh (emptydir): Revise (emptydir-7 and emptydir-8) for this. + +2000-02-16 Jim Meyering <meyering@lucent.com> + + * update.c (join_file): Correct typo in diagnostic: + change `file %s is present...' to `file %s is not present...'. + +2000-02-10 Larry Jones <larry.jones@sdrc.com> + + * parseinfo.c (Parse_Info): Treat matching lines with bad expansions + as errors rather than just ignoring. + +2000-02-10 Larry Jones <larry.jones@sdrc.com> + + * edit.c (edit): Check for invalid characters in hostname and CurDir. + (Reported by "Andrew S. Townley" <atownley@informix.com>.) + * sanity.sh (devcom2): New tests for above. + +2000-02-10 Larry Jones <larry.jones@sdrc.com> + + * cvs.h: Always #include "server.h" to prevent compile errors when + neither CLIENT_SUPPORT nor SERVER_SUPPORT is defined. + (Reported by "Crow, Ian" <ian.crow@linklaters.com>.) + * log.c (send_one, send_arg_list): Only define when CLIENT_SUPPORT + is defined to prevent link errors. + + * server.c (server): Always create a new temporary directory, don't + try to reuse an existing one since we might not have correct + permissions. Also, include directory name in error messages. + +2000-01-29 Jim Kingdon <http://developer.redhat.com/> + + * ignore.c (ignore_files): Correctly set errno to 0 when we go + back to the top of the loop. Fixes spurious errors like "cvs + update: error reading current directory: No such file or + directory". + +2000-01-26 Larry Jones <larry.jones@sdrc.com> + + * run.c (run_exec): Conditionalize K.J.'s change so that it only + applies when SETXID_SUPPORT is defined since some platforms don't + have setegid(). + +2000-01-26 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh: Make TESTDIR earlier then use it to check for versions + of expr that don't work right with long expressions. + + * sanity.sh (dotest_line_by_line): Have wc read from stdin so it + doesn't output the file name and confuse expr. Make the output a + bit less verbose and easier to read. + +2000-01-24 K.J. Paradise <kj@sourcegear.com> + + * run.c :> prevents a user from creating a privileged shell from the + text editor when the SETXID_SUPPORT option is selected. This came from + Bob Colle <bcolle@ilx.com>, and is his completely. + +2000-01-22 Jim Kingdon <http://developer.redhat.com/> + + * sanity.sh (emptydir): Add a case in which one might hope for a + non-Emptydir result, but which result? + +2000-01-18 Larry Jones <larry.jones@sdrc.com> + + * main.c (main): Allow -z0 to disable gzip compression. + +2000-01-17 Larry Jones <larry.jones@sdrc.com> for + K.J. Paradise (kj@sourcegear.com) + + * version.c: Push version number to 1.10.8.1. + + * version.c: Version 1.10.8. + +2000-01-17 Larry Jones <larry.jones@sdrc.com> + + * mkmodules.c (init): Create CVSROOT/Emptydir to avoid problems + with users not having sufficient permissions to create it later. + +2000-01-04 Larry Jones <larry.jones@sdrc.com> + + * client.c (get_responses_and_close): Simplify time-stamp race + avoidance code. + * commit.c (commit): Ditto. + * update.c (do_update): Ditto. + (Prompted by patch submitted by Pavel Roskin + <pavel_roskin@geocities.com>.) + + * hardlink.c: sizeof (char) is 1, by definition. + * logmsg.c: Ditto. + * rcs.c: Ditto. + +2000-01-03 Karl Fogel <kfogel@red-bean.com> + + * filesubr.c, subr.c (backup_file): moved this function from + filesubr.c to subr.c, at JimK's suggestion. + +2000-01-03 Jim Kingdon <http://developer.redhat.com/> + + * sanity.sh (clean): Test the contents of the .#cleanme.txt.1.1 + file, not just its existence. + +2000-01-03 Karl Fogel <kfogel@red-bean.com> + + * cvs.h, filesubr.c (backup_file): use `const' for suffix too; + correct suffix length calculation and appending behavior; discard + unnecessary `void' cast. Thanks to Jim Meyering for noticing. + +2000-01-03 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (clean): Fix up expected output. + +2000-01-02 John P Cavanaugh <cavanaug@sr.hp.com> + and Karl Fogel <kfogel@red-bean.com> + + New -C option to update: overwrites local changes with clean + copies from the repository. (This is an unreversion of the + 1999-12-10 change, further modified to work remotely.) + + * client.h (BACKUP_MODIFIED_FILES): new #define. + + * client.c (struct send_data): new element `backup_modified'. + (send_files): set above element if BACKUP_MODIFIED_FILES flag is + present. + + * filesubr.c (backup_file): new function. + + * cvs.h: prototype for new function `backup_file'. + + * update.c (toss_local_changes): new file-scoped global. + (update): set toss_local_changes if -C flag seen. If + client_active, send "-C" to server, and set SEND_NO_CONTENTS and + BACKUP_MODIFIED_FILES flags before calling send_files(). + + (update_fileproc): if file is modified and toss_local_changes is + set, then back the file up and then check out a fresh copy from + the repository. Also, fixed indentation and formatting for a + particularly bad stretch of code near (but unrelated to) these + changes. + + * sanity.sh: new test `clean', for update -C option. + +1999-12-29 Jim Kingdon <http://developer.redhat.com/> + + * history.c (read_hrecs): st_blksize is unsigned long, not int. + This isn't just cosmetic - getting it wrong will cause coredumps + and such on 64 bit machines. + + * import.c (import_descend), ignore.c (ignore_files): Placate gcc + -Wall by parenthesizing foo || (bar && baz). + +1999-12-24 Larry Jones <larry.jones@sdrc.com> + + * release.c (release): Use fputs to echo lines from update instead + of printf to avoid problems with lines containing "%". (Reported + by Jean-Luc Simard <Jean-Luc.Simard@matrox.com>.) + + * history.c (read_hrecs): Allocate a single 2-block buffer instead + of allocating and freeing a buffer for each block. + (fill_hrec): Remove redundant code. + (select_hrec): Plug memory leak. + +1999-12-22 Larry Jones <larry.jones@sdrc.com> + + * history.c (history): For "modified" or "checkout", sort on + file if user specified -l, even if user also specified a date- + oriented flag. + * sanity.sh (history): Update to match; add new tests. + +1999-12-15 Pavel Roskin <pavel_roskin@geocities.com> + and Larry Jones <larry.jones@sdrc.com> + + * lock.c (lock_name): fixed assertion failure for the + top-level CVS directory when LockDir is used + * sanity.sh (lockfiles-9): new test for this case + +1999-12-11 Karl Fogel <kfogel@red-bean.com> + + * Revert previous change -- it doesn't work remotely yet. + +1999-12-10 John P Cavanaugh <cavanaug@sr.hp.com> + and Karl Fogel <kfogel@red-bean.com> + + * update.c: new -C option to update, overwrites local changes with + clean copies from the repository. + Also, fixed indentation and formatting for a particularly bad + stretch of code near these changes in update_fileproc(). + + * sanity.sh: test new update -C option. + +1999-12-10 Larry Jones <larry.jones@sdrc.com> + + * commit.c (remove_file): Call history_write with update_dir NULL + like Checkin() does for add and modify. + * sanity.sh (basic2-64): Update to match, add "R" records to expected + remote output. + +1999-12-09 K.J. Paradise (kj@sourcegear.com) + + * history.c, commit.c, sanity.sh: found (I think) final + cause of seg fault in history command. Also, added the "R" + history functionality. Fixed basic2-64 so it looks correct for + the change. + +1999-11-30 K.J. Paradise (kj@sourcegear.com) + + * history.c: fixed seg fault caused by 11-03 changes. + off by one in block memory allocations. + +1999-11-29 Karl Fogel <kfogel@red-bean.com> + + * login.c (logout): free `tmp_name' when done. + Correct a comment. + +1999-11-29 Larry Jones <larry.jones@sdrc.com> + + * cvs.h, error.c, import.c: Rename fperror to avoid name clash + on LynxOS. (Reported by Markus Braun <MarkusBraun@gmx.de>.) + +1999-11-23 Larry Jones <larry.jones@sdrc.com> + + * checkout.c (checkout_proc): Split declaration and initialization + of rp to placate neurotic compilers that gripe about jumping past + an initialization, even when the variable is not subsequently used. + +1999-11-19 Larry Jones <larry.jones@sdrc.com> + + * server.c (switch_to_user): Correct setgid error messages. + +1999-11-19 Karl Fogel <kfogel@red-bean.com> + + * edit.c (unedit_usage, unedit): new struct, use it. Now "cvs + unedit" prints an accurate usage message (formerly it printed the + message for "cvs edit", even though the two commands do not have + identical usages). + +1999-11-19 Larry Jones <larry.jones@sdrc.com> + + * history.c: Move -e documentation from Flags to Reports. + (history): Add -e to list of report types in error message. + + * history.c (history): Process file arguments before client/server + processing so they get sent to the server. + * sanity.sh (history): New tests for above. (Also remove comments + about variable spacing -- history output is in variable-width + columns with exactly one space between.) + +1999-11-19 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh: Reestablish check for running as root (using ``id -u'' + instead of ``whoami''). + + * sanity.sh(dotest, dotest_lit, dotest_fail, dotest_status, + dotest_sort): Eval the command so quoting and pipes work right. + (spacefiles, dirs, rcslib, modules, unedit-without-baserev, + ignore, rcs, rcs2, history, tagdate, pserver, server, server2) + Simplify various tests based on above. + +1999-11-19 Karl Fogel <kfogel@red-bean.com> + + * mkmodules.c (init): make history file world-writeable after + creating it, since it needs to be writeable for virtually any + CVS operation. + +1999-11-10 Jim Kingdon <http://developer.redhat.com/> + + * admin.c: Revert change to add -H command option. The help + invocation is "cvs -H admin" not "cvs admin -H" (see cvs.texinfo, + basicb-21 in sanity.sh; fix to cvs.1) + +1999-11-08 Jim Kingdon <http://developer.redhat.com/> + + * log.c (cvslog): If client_active, send options to the server + based on our parsed options rather than trying to send the exact + strings specified (using canonical forms, like RFC822/1123 + dates, in the protocol is just cleaner). + (send_one, send_arg_list): New functions, helpers for above. + * sanity.sh (logopt-6a): New test, for this fix. + +1999-11-09 K.J. Paradise <kj@sourcegear.com> + + * admin.c: made the -H option do what it is documented to + do. a + +1999-11-08 Tom Tromey <tromey@cygnus.com> + + * client.c (connect_to_gserver): Print more error text if gssapi + initialization fails. From Assar Westerlund <assar@sics.se>. + +1999-11-06 Larry Jones <larry.jones@sdrc.com> + + *sanity.sh(rcs3-5): Remote output can be out-of-order, so need a + more general pattern to match the assertion failure. + +1999-11-05 K.J. Paradise (kj@sourcegear.com) + + * history.c: Added a trap to verify that if a + read(file, buffer,blocksize) returns less than blocksize, + that we really are at the end of the file. I can't easily + come up with a test case where this code gets touched, so + it may cause problems. All sanity tests still pass though. + +1999-11-05 Jim Kingdon <http://developer.redhat.com/> + + * sanity.sh (logopt): New test, for Larry's fix. + * sanity.sh (log-18a, rcs-15 to rcs-19): New tests, to test -d + and -r more thoroughly. + +1999-11-05 Larry Jones <larry.jones@sdrc.com> + + * log.c (cvslog): Fix -s and -d with spaces on client side. + (log_usage): Revert Karl's change once again. + sanity.sh(rcs3-5): No longer get different results from local + and client/server. + +1999-11-04 Karl Fogel <kfogel@red-bean.com> + + * log.c (log_usage): Revert Jim Kingdon's reversion of my change + of 1999-11-03. Allowing a space between option and argument + results in lossage; here is a reproduction recipe: run this from + the top of a remote copy of the cvs source tree + + cvs log -d '>1999-03-01' > log-out.with-space + + and then run this (note there's no space after -d now): + + cvs log -d'>1999-03-01' > log-out.no-space + + The resulting files differ; furthermore, a glance at the output of + cvs shows that the first command failed to recurse into + subdirectories. Until this misbehavior can be fixed in the source + code, the documentation should reflect the true state of affairs: + if one simply omits the space, everything works fine. + +1999-11-04 Jim Kingdon <http://developer.redhat.com/> + + * log.c (log_usage): Revert Karl's change regarding -d and + -s. A space is allowed (see sanity.sh for example). + +1999-11-03 K.J. Paradise (kj@sourcegear.com> + + * history.c: cleaned up my prior change a bit, per Larry Jones' + comments, and John O'Conner's additional comments about bits of + non MS-Visual C++ compliancy of my code. + +1999-11-04 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh: Check that tr that correctly handles NULs; if not, try + to find a version that does; if none can be found, warn user. + Also fix warnings for defective expr. + +1999-11-04 Karl Fogel <kfogel@red-bean.com> + + Changes for empty/random passwords in anon pserver access: + + * server.c (check_repository_password): if password empty, grant + access no matter what password is received; this is so anon CVS no + longer requires a password but remains backwards-compatible with + all those clients out there. + + * client.c (connect_to_pserver): proceed with login even if + password not found in .cvspass file -- just use empty string as + password. And if such a login fails, print a descriptive error. + + * login.c (get_cvs_password): don't complain if file or password + not found. That condition is no longer a showstopper, now that + empty passwords are permissible. + Cleaned up conditional chaining a bit, too. + + * sanity.sh (pserver-9, pserver-10, pserver-11, pserver-12, + pserver-13): new tests, about empty-password pserver access. + +1999-11-03 K.J. Paradise (kj@sourcegear.com> + + * history.c: modify parsing routines to parse the history + file a block at a time, rather than all at once. This allows + people with large history files and small amount of memory + to still get some functionality out of the history file. + +1999-11-03 Karl Fogel <kfogel@red-bean.com> + + * log.c (log_usage): correct usage message for -d and -s options. + Because the space between the option letter and its argument has + been eliminated, I capitalized the argument portion to distinguish + it from the option letter. This makes it slightly inconsistent + with other such usage summaries, but at least it is now both + correct and readable. + +1999-10-22 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (dotest_sort): Old versions of tr don't understand \t + so use a literal tab instead. + +1999-10-21 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (dotest_sort): Convert any tabs in the output into spaces + before sorting to avoid POSIX.2 sort weirdness. + (import-106, importb-2): Change expected output per above. + +1999-10-18 K.J. Paradise <kj@sourcegear.com> + + Bug: users 'stan' and 'cartman' both have full read/write access + to the cvs repository. 'cartman' does a 'cvs admin -l foo.c'. + 'stan' then does a 'cvs admin -u foo.c'. The lock wouldn't be + removed, and no warning/error would be given. This is now fixed. + * rcs.c:(c.6157) remove caller/user check on the multiple lock + detection routines. Sanity.sh runs with no errors after this fix. + +1999-10-14 Larry Jones <larry.jones@sdrc.com> + + Make "cvs admin -e" (with no list of users) work: + * admin.c (admin): Remove error message. + (admin_fileproc): If no args for -e, call RCS_delaccess with NULL user. + * rcs.c (RCS_delaccess): Interpret NULL user as request to delete + entire access list. + * sanity.sh (admin-19a-*): Test. + +1999-09-29 Larry Jones <larry.jones@sdrc.com> + + * entries.c (Subdirs_Known): Use entfilename when opening CVSADM_ENTLOG + like everywhere else. Although this isn't strictly necessary (since + we immediately close it again), it keeps the code consistent and fixes + a bug where an open error reported the wrong file name. + +1999-09-16 Larry Jones <larry.jones@sdrc.com> + + * log.c (log_parse_revlist): Handle peculiar revision specs like + "-r.", "-r:", and "-r," correctly. (Thanks to Pavel Roskin + <pavel_roskin@geocities.com> for submitting a patch, this fix is + somewhat different.) + * sanity.sh (log): New tests for above. + +1999-09-15 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (basica-8b1): New test to check fix for bad diff options + causing cvs to crash. + +1999-09-02 Larry Jones <larry.jones@sdrc.com> + + * modules.c (do_module): Handle case where module definition has + options and special options but no directory; fix potential problems + running off beginning of string while stripping trailing blanks. + * sanity.sh (modules2): New tests for above. + +1999-08-26 Larry Jones <larry.jones@sdrc.com> + + * lock.c (lock_name): Remove side-effects from assert() expression + since they won't occur if NDEBUG is defined (not that that's a good + thing to do). (Reported by KOIE Hidetaka <hide@koie.org>.) + +1999-08-25 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh: Use "${AWK}" instead of "awk" to make it easier for + people to use nawk/gawk/etc.; use an explicit "-print" with find + since some older version don't assume it; rename tests to avoid + duplicate importc-8. (Changes along these lines suggested by + Chris Cameron <chris.cameron@ot.co.nz>.) + +1999-08-24 Larry Jones <larry.jones@sdrc.com> + + * commit.c (check_fileproc): Don't crash when a file has no + repository, just treat it as unknown. (Reported by Stefaan + Diericx <stefaan.diericx@argenta.be>.) + * sanity.sh (errmsg2): New tests, for this fix. + +1999-08-18 Larry Jones <larry.jones@sdrc.com> + + * update.c (special_file_mismatch): Initialize *_hardlinks to + avoid trying to free garbage later on. (Reported by Jan + Scheffczyk <herta@Xterminator.StudFB.UniBw-Muenchen.de>.) + +1999-08-17 Larry Jones <larry.jones@sdrc.com> + + * sanity.sh (basicc-11): Older versions of sh don't understand + ``if ! test...''. (Patch submitted by David J N Begley + <david@avarice.nepean.uws.edu.au>.) + +1999-08-17 Larry Jones <larry.jones@sdrc.com> + + * client.c, hardlink.c, hash.c, hash.h, main.c, recurse.c: Change + enum constant UNKNOWN to avoid conflicts on HPUX 11.0. (Reported + by Laurent Duperval <laurent.duperval@cgi.ca>.) + +1999-08-16 Larry Jones <larry.jones@sdrc.com> + + client.c: Eliminate redundant #if. (Patch submitted by Assar + Westerlund <assar@sics.se>.) + +1999-07-30 Larry Jones <larry.jones@sdrc.com> + + * rcs.c (RCS_checkin): Terminate cleanly if RCS_addbranch fails + rather than blithely continuing on and crashing. + * sanity.sh (basica): New tests, for this fix. + +1999-07-29 Larry Jones <larry.jones@sdrc.com> + + * import.c (add_rcs_file): change "cannot lstat" message to include + userfile (the actual file causing the problem) instead of user + (which may or may not be the same). + +1999-07-29 Eric Sink <eric@sourcegear.com> + + * version.c: Push version number to 1.10.7.1. + + * version.c: Version 1.10.7. + 1999-07-28 Eric Sink <eric@sourcegear.com> * sanity.sh: before running basicc-11, we need to see if diff --git a/gnu/usr.bin/cvs/src/add.c b/gnu/usr.bin/cvs/src/add.c index db1f5278f71..dbefda59ca5 100644 --- a/gnu/usr.bin/cvs/src/add.c +++ b/gnu/usr.bin/cvs/src/add.c @@ -59,6 +59,7 @@ add (argc, argv) /* Nonzero if we found a slash, and are thus adding files in a subdirectory. */ int found_slash = 0; + size_t cvsroot_len; if (argc == 1 || argc == -1) usage (add_usage); @@ -92,6 +93,8 @@ add (argc, argv) if (argc <= 0) usage (add_usage); + cvsroot_len = strlen (CVSroot_directory); + /* First some sanity checks. I know that the CVS case is (sort of) also handled by add_directory, but we need to check here so the client won't get all confused in send_file_names. */ @@ -157,7 +160,11 @@ add (argc, argv) start_server (); ign_setup (); - if (options) send_arg(options); + if (options) + { + send_arg (options); + free (options); + } option_with_arg ("-m", message); /* If !found_slash, refrain from sending "Directory", for @@ -219,6 +226,17 @@ add (argc, argv) /* find the repository associated with our current dir */ repository = Name_Repository (NULL, update_dir); + /* don't add stuff to Emptydir */ + if (strncmp (repository, CVSroot_directory, cvsroot_len) == 0 + && ISDIRSEP (repository[cvsroot_len]) + && strncmp (repository + cvsroot_len + 1, + CVSROOTADM, + sizeof CVSROOTADM - 1) == 0 + && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM]) + && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, + CVSNULLREPOS) == 0) + error (1, 0, "cannot add to %s", repository); + /* before we do anything else, see if we have any per-directory tags */ ParseTag (&tag, &date, &nonbranch); @@ -227,7 +245,7 @@ add (argc, argv) sprintf (rcsdir, "%s/%s", repository, p); Create_Admin (p, argv[i], rcsdir, tag, date, - nonbranch, 0); + nonbranch, 0, 1); if (found_slash) send_a_repository ("", repository, update_dir); @@ -304,6 +322,17 @@ add (argc, argv) /* Find the repository associated with our current dir. */ repository = Name_Repository (NULL, finfo.update_dir); + /* don't add stuff to Emptydir */ + if (strncmp (repository, CVSroot_directory, cvsroot_len) == 0 + && ISDIRSEP (repository[cvsroot_len]) + && strncmp (repository + cvsroot_len + 1, + CVSROOTADM, + sizeof CVSROOTADM - 1) == 0 + && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM]) + && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, + CVSNULLREPOS) == 0) + error (1, 0, "cannot add to %s", repository); + entries = Entries_Open (0, NULL); finfo.repository = repository; @@ -619,6 +648,8 @@ cannot resurrect %s; RCS file removed by second party", finfo.fullname); if (message) free (message); + if (options) + free (options); return (err); } @@ -769,10 +800,8 @@ add_directory (finfo) #ifdef SERVER_SUPPORT if (!server_active) - Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); -#else - Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); #endif + Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1); if (tag) free (tag); if (date) diff --git a/gnu/usr.bin/cvs/src/client.h b/gnu/usr.bin/cvs/src/client.h index c323cf38ffb..b4d404ad106 100644 --- a/gnu/usr.bin/cvs/src/client.h +++ b/gnu/usr.bin/cvs/src/client.h @@ -109,6 +109,7 @@ send_files PROTO((int argc, char **argv, int local, int aflag, #define SEND_BUILD_DIRS 1 #define SEND_FORCE 2 #define SEND_NO_CONTENTS 4 +#define BACKUP_MODIFIED_FILES 8 /* Send an argument to the remote server. */ void diff --git a/gnu/usr.bin/cvs/src/create_adm.c b/gnu/usr.bin/cvs/src/create_adm.c index 7f8f581b0b9..878b05891a4 100644 --- a/gnu/usr.bin/cvs/src/create_adm.c +++ b/gnu/usr.bin/cvs/src/create_adm.c @@ -22,7 +22,8 @@ don't print warnings; all errors are fatal then. */ int -Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn) +Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn, + dotemplate) char *dir; char *update_dir; char *repository; @@ -30,6 +31,7 @@ Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn) char *date; int nonbranch; int warn; + int dotemplate; { FILE *fout; char *cp; @@ -168,7 +170,7 @@ Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn) WriteTag (dir, tag, date, nonbranch, update_dir, repository); #ifdef SERVER_SUPPORT - if (server_active) + if (server_active && dotemplate) { server_template (update_dir, repository); } diff --git a/gnu/usr.bin/cvs/src/error.c b/gnu/usr.bin/cvs/src/error.c index 64b686cae44..fe5877f3d6e 100644 --- a/gnu/usr.bin/cvs/src/error.c +++ b/gnu/usr.bin/cvs/src/error.c @@ -64,7 +64,8 @@ extern char *strerror (); void error_exit PROTO ((void)) { - Lock_Cleanup(); + rcs_cleanup (); + Lock_Cleanup (); #ifdef SERVER_SUPPORT if (server_active) server_cleanup (0); @@ -122,7 +123,7 @@ error (status, errnum, message, va_alist) int num; unsigned int unum; int ch; - unsigned char buf[100]; + char buf[100]; cvs_outerr (program_name, 0); if (command_name && *command_name) @@ -201,9 +202,9 @@ error (status, errnum, message, va_alist) /* VARARGS */ void #if defined (HAVE_VPRINTF) && defined (__STDC__) -fperror (FILE *fp, int status, int errnum, char *message, ...) +fperrmsg (FILE *fp, int status, int errnum, char *message, ...) #else -fperror (fp, status, errnum, message, va_alist) +fperrmsg (fp, status, errnum, message, va_alist) FILE *fp; int status; int errnum; diff --git a/gnu/usr.bin/cvs/src/hardlink.c b/gnu/usr.bin/cvs/src/hardlink.c index 046cf3a470a..309d27f078d 100644 --- a/gnu/usr.bin/cvs/src/hardlink.c +++ b/gnu/usr.bin/cvs/src/hardlink.c @@ -63,7 +63,7 @@ lookup_file_by_inode (filepath) /* inodestr contains the hexadecimal representation of an inode, so it requires two bytes of text to represent each byte of the inode number. */ - inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1); if (stat (file, &sb) < 0) { if (existence_error (errno)) @@ -85,7 +85,7 @@ lookup_file_by_inode (filepath) if (hp == NULL) { hp = getnode (); - hp->type = UNKNOWN; + hp->type = NT_UNKNOWN; hp->key = inodestr; hp->data = (char *) getlist(); hp->delproc = dellist; @@ -100,7 +100,7 @@ lookup_file_by_inode (filepath) if (p == NULL) { p = getnode(); - p->type = UNKNOWN; + p->type = NT_UNKNOWN; p->key = xstrdup (filepath); p->data = NULL; (void) addnode ((List *) hp->data, p); @@ -128,7 +128,7 @@ update_hardlink_info (file) /* file is a relative pathname; assume it's from the current working directory. */ char *dir = xgetwd(); - path = xmalloc (sizeof(char) * (strlen(dir) + strlen(file) + 2)); + path = xmalloc (strlen(dir) + strlen(file) + 2); sprintf (path, "%s/%s", dir, file); free (dir); } @@ -176,8 +176,7 @@ list_linked_files_on_disk (file) else { char *dir = xgetwd(); - path = (char *) xmalloc (sizeof(char) * - (strlen(dir) + strlen(file) + 2)); + path = (char *) xmalloc (strlen(dir) + strlen(file) + 2); sprintf (path, "%s/%s", dir, file); free (dir); } @@ -193,7 +192,7 @@ list_linked_files_on_disk (file) /* inodestr contains the hexadecimal representation of an inode, so it requires two bytes of text to represent each byte of the inode number. */ - inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + inodestr = (char *) xmalloc (2*sizeof(ino_t) + 1); sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); /* Make sure the files linked to this inode are sorted. */ @@ -280,7 +279,7 @@ find_checkedout_proc (node, data) /* Look at this file in the hardlist and see whether the checked_out field is 1, meaning that it has been checked out during this CVS run. */ path = (char *) - xmalloc (sizeof(char) * (strlen (dir) + strlen (node->key) + 2)); + xmalloc (strlen (dir) + strlen (node->key) + 2); sprintf (path, "%s/%s", dir, node->key); link = lookup_file_by_inode (path); free (path); diff --git a/gnu/usr.bin/cvs/src/hash.c b/gnu/usr.bin/cvs/src/hash.c index af000acdee9..7e562b89f0d 100644 --- a/gnu/usr.bin/cvs/src/hash.c +++ b/gnu/usr.bin/cvs/src/hash.c @@ -102,7 +102,7 @@ dellist (listp) { /* put the nodes into the cache */ #ifndef NOCACHE - p->type = UNKNOWN; + p->type = NT_UNKNOWN; p->next = nodecache; nodecache = p; #else @@ -147,7 +147,7 @@ getnode () /* always make it clean */ memset ((char *) p, 0, sizeof (Node)); - p->type = UNKNOWN; + p->type = NT_UNKNOWN; return (p); } @@ -211,7 +211,7 @@ freenode (p) /* then put it in the cache */ #ifndef NOCACHE - p->type = UNKNOWN; + p->type = NT_UNKNOWN; p->next = nodecache; nodecache = p; #else @@ -456,7 +456,7 @@ nodetypestring (type) Ntype type; { switch (type) { - case UNKNOWN: return("UNKNOWN"); + case NT_UNKNOWN: return("UNKNOWN"); case HEADER: return("HEADER"); case ENTRIES: return("ENTRIES"); case FILES: return("FILES"); @@ -470,6 +470,7 @@ nodetypestring (type) case FILEATTR: return("FILEATTR"); case VARIABLE: return("VARIABLE"); case RCSFIELD: return("RCSFIELD"); + case RCSCMPFLD: return("RCSCMPFLD"); } return("<trash>"); diff --git a/gnu/usr.bin/cvs/src/hash.h b/gnu/usr.bin/cvs/src/hash.h index 06867a721c6..f98353dd8ee 100644 --- a/gnu/usr.bin/cvs/src/hash.h +++ b/gnu/usr.bin/cvs/src/hash.h @@ -16,9 +16,9 @@ */ enum ntype { - UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, + NT_UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE, FILEATTR, - VARIABLE, RCSFIELD + VARIABLE, RCSFIELD, RCSCMPFLD }; typedef enum ntype Ntype; diff --git a/gnu/usr.bin/cvs/src/myndbm.c b/gnu/usr.bin/cvs/src/myndbm.c index f674ac11d9f..7795f6652cb 100644 --- a/gnu/usr.bin/cvs/src/myndbm.c +++ b/gnu/usr.bin/cvs/src/myndbm.c @@ -211,7 +211,8 @@ mydbm_load_file (fp, list) value = xmalloc (value_allocated); cont = 0; - while ((line_length = getstr (&line, &line_size, fp, '\012', 0)) >= 0) + while ((line_length = + getstr (&line, &line_size, fp, '\012', 0, GETLINE_NO_LIMIT)) >= 0) { if (line_length > 0 && line[line_length - 1] == '\012') { diff --git a/gnu/usr.bin/cvs/src/rcs.h b/gnu/usr.bin/cvs/src/rcs.h index 0a281613848..3466398f204 100644 --- a/gnu/usr.bin/cvs/src/rcs.h +++ b/gnu/usr.bin/cvs/src/rcs.h @@ -8,8 +8,10 @@ * RCS source control definitions needed by rcs.c and friends */ -/* String which indicates a conflict if it occurs at the start of a line. */ -#define RCS_MERGE_PAT ">>>>>>> " +/* Strings which indicate a conflict if they occur at the start of a line. */ +#define RCS_MERGE_PAT_1 "<<<<<<< " +#define RCS_MERGE_PAT_2 "=======\n" +#define RCS_MERGE_PAT_3 ">>>>>>> " #define RCSEXT ",v" #define RCSPAT "*,v" @@ -227,7 +229,9 @@ int RCS_delete_revs PROTO ((RCSNode *, char *, char *, int)); void RCS_addaccess PROTO ((RCSNode *, char *)); void RCS_delaccess PROTO ((RCSNode *, char *)); char *RCS_getaccess PROTO ((RCSNode *)); +RETSIGTYPE rcs_cleanup PROTO ((void)); void RCS_rewrite PROTO ((RCSNode *, Deltatext *, char *)); +void RCS_abandon PROTO ((RCSNode *)); int rcs_change_text PROTO ((const char *, char *, size_t, const char *, size_t, char **, size_t *)); char *make_file_label PROTO ((char *, char *, RCSNode *)); diff --git a/gnu/usr.bin/cvs/src/remove.c b/gnu/usr.bin/cvs/src/remove.c index 2dacdf15988..f0055d9c424 100644 --- a/gnu/usr.bin/cvs/src/remove.c +++ b/gnu/usr.bin/cvs/src/remove.c @@ -103,6 +103,7 @@ cvsremove (argc, argv) /* FIXME: Can't we set SEND_NO_CONTENTS here? Needs investigation. */ send_files (argc, argv, local, 0, 0); send_file_names (argc, argv, 0); + free_names (&argc, argv); send_to_server ("remove\012", 0); return get_responses_and_close (); } diff --git a/gnu/usr.bin/cvs/src/run.c b/gnu/usr.bin/cvs/src/run.c index 41f445756d6..d382cef6081 100644 --- a/gnu/usr.bin/cvs/src/run.c +++ b/gnu/usr.bin/cvs/src/run.c @@ -207,6 +207,18 @@ run_exec (stin, stout, sterr, flags) (void) close (sherr); } +#ifdef SETXID_SUPPORT + /* + ** This prevents a user from creating a privileged shell + ** from the text editor when the SETXID_SUPPORT option is selected. + */ + if (!strcmp (run_argv[0], Editor) && setegid (getgid ())) + { + error (0, errno, "cannot set egid to gid"); + _exit (127); + } +#endif + /* dup'ing is done. try to run it now */ (void) execvp (run_argv[0], run_argv); error (0, errno, "cannot exec %s", run_argv[0]); diff --git a/gnu/usr.bin/cvs/src/sanity.sh b/gnu/usr.bin/cvs/src/sanity.sh index 6130289c42d..84e67ccd663 100644 --- a/gnu/usr.bin/cvs/src/sanity.sh +++ b/gnu/usr.bin/cvs/src/sanity.sh @@ -25,21 +25,6 @@ # See TODO list at end of file. -# You can't run CVS as root; print a nice error message here instead -# of somewhere later, after making a mess. -# Commented out because: -# (1) whoami is not portable. If memory serves the POSIX way is "id -un". -# ("logname" or "who am i" are similar but different--they have more to -# do with who logged in on your tty than your uid). -# (2) This definition of "root" doesn't quite match CVS's (which is based -# on uid 0, not username "root"). -#case "`whoami`" in -# "root" ) -# echo "sanity.sh: test suite does not work correctly when run as root" >&2 -# exit 1 -# ;; -#esac - # required to make this script work properly. unset CVSREAD @@ -68,7 +53,8 @@ export LC_ALL # So this would be lost if everything was `pwd`-based. I suppose # if we wanted to get baroque we could start making symlinks # to ensure the two are different. -TESTDIR=${TESTDIR:-/tmp/cvs-sanity} +tmp=`(cd /tmp; /bin/pwd || pwd) 2>/dev/null` +: ${TESTDIR=$tmp/cvs-sanity} # "debugger" #set -x @@ -121,6 +107,7 @@ PROG=`basename ${testcvs}` # should be here. a-zA-Z obviously. People complained when 0-9 were # not allowed in usernames. Other than that I'm not sure. username="[-a-zA-Z0-9][-a-zA-Z0-9]*" +author="[-a-zA-Z0-9][-a-zA-Z0-9]*" # Regexp to match the name of a temporary file (from cvs_temp_name). # This appears in certain diff output. @@ -154,34 +141,87 @@ if test -f check.log; then mv check.log check.plog fi -GEXPRLOCS="`echo $PATH | sed 's/:/ /g'` /usr/local/bin /usr/contrib/bin /usr/gnu/bin /local/bin /local/gnu/bin /gun/bin" +# clean any old remnants (we need the chmod because some tests make +# directories read-only) +if test -d ${TESTDIR}; then + chmod -R a+wx ${TESTDIR} + rm -rf ${TESTDIR} +fi +mkdir ${TESTDIR} +cd ${TESTDIR} -EXPR=expr +# Make sure various tools work the way we expect, or try to find +# versions that do. +: ${AWK=awk} +: ${EXPR=expr} +: ${ID=id} +: ${TR=tr} -# Cause NextStep 3.3 users to lose in a more graceful fashion. -if $EXPR 'abc -def' : 'abc -def' >/dev/null; then - : good, it works -else - for path in $GEXPRLOCS ; do - if test -x $path/gexpr ; then - if test "X`$path/gexpr --version`" != "X--version" ; then - EXPR=$path/gexpr +find_tool () +{ + GLOCS="`echo $PATH | sed 's/:/ /g'` /usr/local/bin /usr/contrib/bin /usr/gnu/bin /local/bin /local/gnu/bin /gun/bin" + TOOL="" + for path in $GLOCS ; do + if test -x $path/g$1 ; then + if test "X`$path/g$1 --version`" != "X--version" ; then + TOOL=$path/g$1 break fi fi - if test -x $path/expr ; then - if test "X`$path/expr --version`" != "X--version" ; then - EXPR=$path/expr + if test -x $path/$1 ; then + if test "X`$path/$1 --version`" != "X--version" ; then + TOOL=$path/$1 break fi fi done + if test -z "$TOOL"; then + : + else + echo "Notice: The default version of $1 is defective, using" >&2 + echo "$TOOL instead." >&2 + fi + echo "$TOOL" +} + +# You can't run CVS as root; print a nice error message here instead +# of somewhere later, after making a mess. +case "`$ID -u`" in + "0") + echo "Test suite does not work correctly when run as root" >&2 + exit 1 + ;; + + "") + ID=`find_tool id` + if test -z "$ID" ; then + echo 'Running these tests requires an "id" program that understands the' >&2 + echo '-u and -n flags. Make sure that such an id (GNU, or many but not' >&2 + echo 'all vendor-supplied versions) is in your path.' >&2 + exit 1 + fi + ;; +esac +username=`$ID -un` +if $EXPR "${username}" : "${username}" >/dev/null; then + : good, it works +else + echo "Test suite does not work correctly when run by a username" >&2 + echo "containing regular expression meta-characters." >&2 + exit 1 +fi + +# Cause NextStep 3.3 users to lose in a more graceful fashion. +if $EXPR 'abc +def' : 'abc +def' >/dev/null; then + : good, it works +else + EXPR=`find_tool expr` if test -z "$EXPR" ; then - echo 'Running these tests requires an "expr" program that can handle' - echo 'multi-line patterns. Make sure that such an expr (GNU, or many but' - echo 'not all vendor-supplied versions) is in your path.' + echo 'Running these tests requires an "expr" program that can handle' >&2 + echo 'multi-line patterns. Make sure that such an expr (GNU, or many but' >&2 + echo 'not all vendor-supplied versions) is in your path.' >&2 exit 1 fi fi @@ -191,20 +231,7 @@ fi if $EXPR 'a b' : 'a c' >/dev/null; then - for path in $GEXPRLOCS ; do - if test -x $path/gexpr ; then - if test "X`$path/gexpr --version`" != "X--version" ; then - EXPR=$path/gexpr - break - fi - fi - if test -x $path/expr ; then - if test "X`$path/expr --version`" != "X--version" ; then - EXPR=$path/expr - break - fi - fi - done + EXPR=`find_tool expr` if test -z "$EXPR" ; then echo 'Warning: you are using a version of expr which does not correctly' echo 'match multi-line patterns. Some tests may spuriously pass.' @@ -215,6 +242,35 @@ else : good, it works fi +# More SunOS lossage... +echo 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' >${TESTDIR}/foo +cat ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo >${TESTDIR}/bar +cat ${TESTDIR}/bar ${TESTDIR}/bar ${TESTDIR}/bar ${TESTDIR}/bar >${TESTDIR}/foo +cat ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo >${TESTDIR}/bar +if $EXPR "`cat ${TESTDIR}/bar`" : "`cat ${TESTDIR}/bar`" >/dev/null; then + : good, it works +else + EXPR=`find_tool expr` + if test -z "$EXPR" ; then + echo 'Warning: you are using a version of expr which does not correctly' + echo 'match large patterns. Some tests may spuriously fail.' + echo 'You may wish to make sure GNU expr is in your path.' + EXPR=expr + fi +fi +if $EXPR "`cat ${TESTDIR}/bar`x" : "`cat ${TESTDIR}/bar`y" >/dev/null; then + EXPR=`find_tool expr` + if test -z "$EXPR" ; then + echo 'Warning: you are using a version of expr which does not correctly' + echo 'match large patterns. Some tests may spuriously pass.' + echo 'You may wish to make sure GNU expr is in your path.' + EXPR=expr + fi +else + : good, it works +fi +rm -f ${TESTDIR}/foo ${TESTDIR}/bar + # That we should have to do this is total bogosity, but GNU expr # version 1.9.4-1.12 uses the emacs definition of "$" instead of the unix # (e.g. SunOS 4.1.3 expr) one. Rumor has it this will be fixed in the @@ -265,6 +321,19 @@ else QUESTION='\?' fi +# now make sure that tr works on NULs +if $EXPR `echo "123" | ${TR} '2' '\0'` : "123" >/dev/null; then + TR=`find_tool tr` + if test -z "$TR" ; then + echo 'Warning: you are using a version of tr which does not correctly' + echo 'handle NUL bytes. Some tests may spuriously pass or fail.' + echo 'You may wish to make sure GNU tr is in your path.' + TR=tr + fi +else + : good, it works +fi + pass () { echo "PASS: $1" >>${LOGFILE} @@ -359,15 +428,15 @@ dotest_all_in_one () dotest_line_by_line () { line=1 - while [ $line -le `wc -l ${TESTDIR}/dotest.tmp` ] ; do - echo "$line matched \c" >>$LOGFILE + while [ $line -le `wc -l <${TESTDIR}/dotest.tmp` ] ; do if $EXPR "`sed -n ${line}p ${TESTDIR}/dotest.tmp`" : \ "`sed -n ${line}p ${TESTDIR}/dotest.exp`" >/dev/null; then : else - echo "**** expected line: " >>${LOGFILE} + echo "Line $line:" >> ${LOGFILE} + echo "**** expected: " >>${LOGFILE} sed -n ${line}p ${TESTDIR}/dotest.exp >>${LOGFILE} - echo "**** got line: " >>${LOGFILE} + echo "**** got: " >>${LOGFILE} sed -n ${line}p ${TESTDIR}/dotest.tmp >>${LOGFILE} unset line return 1 @@ -443,7 +512,7 @@ dotest_internal_debug () dotest () { rm -f ${TESTDIR}/dotest.ex? 2>&1 - if $2 >${TESTDIR}/dotest.tmp 2>&1; then + if eval "$2" >${TESTDIR}/dotest.tmp 2>&1; then : so far so good else status=$? @@ -458,7 +527,7 @@ dotest () dotest_lit () { rm -f ${TESTDIR}/dotest.ex? 2>&1 - if $2 >${TESTDIR}/dotest.tmp 2>&1; then + if eval "$2" >${TESTDIR}/dotest.tmp 2>&1; then : so far so good else status=$? @@ -482,7 +551,7 @@ dotest_lit () dotest_fail () { rm -f ${TESTDIR}/dotest.ex? 2>&1 - if $2 >${TESTDIR}/dotest.tmp 2>&1; then + if eval "$2" >${TESTDIR}/dotest.tmp 2>&1; then status=$? cat ${TESTDIR}/dotest.tmp >>${LOGFILE} echo "exit status was $status" >>${LOGFILE} @@ -497,7 +566,7 @@ dotest_fail () # Like dotest except second argument is the required exitstatus. dotest_status () { - $3 >${TESTDIR}/dotest.tmp 2>&1 + eval "$3" >${TESTDIR}/dotest.tmp 2>&1 status=$? if test "$status" = "$2"; then : so far so good @@ -513,7 +582,7 @@ dotest_status () dotest_sort () { rm -f ${TESTDIR}/dotest.ex? 2>&1 - if $2 >${TESTDIR}/dotest.tmp1 2>&1; then + if eval "$2" >${TESTDIR}/dotest.tmp1 2>&1; then : so far so good else status=$? @@ -521,18 +590,10 @@ dotest_sort () echo "exit status was $status" >>${LOGFILE} fail "$1" fi - sort < ${TESTDIR}/dotest.tmp1 > ${TESTDIR}/dotest.tmp + ${TR} ' ' ' ' < ${TESTDIR}/dotest.tmp1 | sort > ${TESTDIR}/dotest.tmp dotest_internal "$@" } -# clean any old remnants (we need the chmod because some tests make -# directories read-only) -if test -d ${TESTDIR}; then - chmod -R a+wx ${TESTDIR} - rm -rf ${TESTDIR} -fi -mkdir ${TESTDIR} -cd ${TESTDIR} # This will show up in cvs history output where it prints the working # directory. It should *not* appear in any cvs output referring to the # repository; cvs should use the name of the repository as specified. @@ -573,10 +634,11 @@ if test x"$*" = x; then tests="${tests} branches branches2 tagc tagf" tests="${tests} rcslib multibranch import importb importc" tests="${tests} import-after-initial" - tests="${tests} join join2 join3 join-readonly-conflict" + tests="${tests} join join2 join3 join-readonly-conflict join-admin" tests="${tests} new newb conflicts conflicts2 conflicts3" + tests="${tests} clean" # Checking out various places (modules, checkout -d, &c) - tests="${tests} modules modules2 modules3 modules4" + tests="${tests} modules modules2 modules3 modules4 modules5" tests="${tests} mkmodules-temp-file-removal" tests="${tests} cvsadm emptydir abspath toplevel toplevel2" # Log messages, error messages. @@ -587,7 +649,7 @@ if test x"$*" = x; then tests="${tests} ignore binfiles binfiles2 binfiles3" tests="${tests} mcopy binwrap binwrap2" tests="${tests} binwrap3 mwrap info taginfo config" - tests="${tests} serverpatch log log2 ann ann-id" + tests="${tests} serverpatch log log2 logopt ann ann-id" # Repository Storage (RCS file format, CVS lock files, creating # a repository without "cvs init", &c). tests="${tests} crerepos rcs rcs2 rcs3 lockfiles backuprecover" @@ -595,7 +657,7 @@ if test x"$*" = x; then tests="${tests} history" tests="${tests} big modes modes2 modes3 stamps" # PreservePermissions stuff: permissions, symlinks et al. - tests="${tests} perms symlinks symlinks2 hardlinks" + # tests="${tests} perms symlinks symlinks2 hardlinks" # More tag and branch tests, keywords. tests="${tests} sticky keyword keyword2 keywordlog" tests="${tests} head tagdate multibranch2 tag8k" @@ -667,6 +729,7 @@ if test "x$remote" = xyes; then fi dotest 1 "${testcvs} init" '' +dotest 1a "${testcvs} init" '' ### The big loop for what in $tests; do @@ -805,6 +868,21 @@ done" ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile new revision: 3\.1; previous revision: 2\.0 done" + + # Test using -r to create a branch + dotest_fail basica-8a3 "${testcvs} -q ci -m bogus -r 3.0.0" \ +"Checking in ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v: can't find branch point 3\.0 +${PROG} [a-z]*: could not check in ssfile" + dotest basica-8a4 "${testcvs} -q ci -m valid -r 3.1.2" \ +"Checking in ssfile; +${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile +new revision: 3\.1\.2\.1; previous revision: 3\.1 +done" + # now get rid of the sticky tag and go back to the trunk + dotest basica-8a5 "${testcvs} -q up -A" "[UP] ssfile" + cd ../.. dotest basica-8b "${testcvs} -q diff -r1.2 -r1.3" \ "Index: sdir/ssdir/ssfile @@ -814,6 +892,15 @@ retrieving revision 1\.2 retrieving revision 1\.3 diff -r1\.2 -r1\.3" + dotest_fail basica-8b1 "${testcvs} -q diff -r1.2 -r1.3 -C 3isacrowd" \ +"Index: sdir/ssdir/ssfile +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v +retrieving revision 1\.2 +retrieving revision 1\.3 +diff -C3isacrowd -r1\.2 -r1\.3 +${PROG} [a-z]*: invalid context length argument" + # The .* here will normally be "No such file or directory", # but if memory serves some systems (AIX?) have a different message. : dotest_fail basica-9 \ @@ -864,6 +951,10 @@ done" deleting revision 2\.0 deleting revision 1\.3 done" + dotest basica-o6a "${testcvs} admin -o 3.1.2: ssfile" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v +deleting revision 3\.1\.2\.1 +done" dotest basica-o7 "${testcvs} log -N ssfile" " RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v Working file: ssfile @@ -1193,7 +1284,7 @@ ${PROG} [a-z]*: Updating second-dir" # For CVS to make a syntactic check for "." wouldn't suffice. # On Linux 2.2 systems, the cwd may be gone, so we recreate it # to allow basicc-11 to actually happen - if ! test -d ../first-dir; then + if test ! -d ../first-dir; then cd .. mkdir ./first-dir cd ./first-dir @@ -2067,44 +2158,50 @@ ${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1/dir2" # which don't exist in the remote output? would seem to be # a CVS bug. dotest basic2-64 "${testcvs} his -x TOFWUCGMAR -a" \ -"O [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir= ${TMPPWD}/\* -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1 == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1 == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2 == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2 == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir == ${TMPPWD} -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1 == ${TMPPWD} -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1 == ${TMPPWD} -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2 == ${TMPPWD} -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2 == ${TMPPWD} -F [0-9/]* [0-9:]* ${PLUS}0000 ${username} =first-dir= ${TMPPWD}/\* -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-revision:1\.1\] -O [0-9/]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir =first-dir= ${TMPPWD}/\* -U [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == ${TMPPWD}/first-dir -U [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir == ${TMPPWD}/first-dir" \ -"O [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir= <remote>/\* -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir == <remote> -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1 == <remote> -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2 == <remote> -M [0-9/]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2 == <remote> -F [0-9/]* [0-9:]* ${PLUS}0000 ${username} =first-dir= <remote>/\* -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-revision:1\.1\] -O [0-9/]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir =first-dir= <remote>/\*" +"O [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir= ${TMPPWD}/\* +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1 == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1 == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2 == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2 == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir == ${TMPPWD} +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == ${TMPPWD} +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1 == ${TMPPWD} +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1 == ${TMPPWD} +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1 == ${TMPPWD} +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2 == ${TMPPWD} +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2 == ${TMPPWD} +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1/dir2 == ${TMPPWD} +F [0-9-]* [0-9:]* ${PLUS}0000 ${username} =first-dir= ${TMPPWD}/\* +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\] +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-revision:1\.1\] +O [0-9-]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir =first-dir= ${TMPPWD}/\* +U [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == ${TMPPWD}/first-dir +U [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir == ${TMPPWD}/first-dir" \ +"O [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir= <remote>/\* +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1 == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1 == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2 == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2 == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir == <remote> +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir == <remote> +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1 == <remote> +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1 == <remote> +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1 == <remote> +A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2 == <remote> +M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2 == <remote> +R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1/dir2 == <remote> +F [0-9-]* [0-9:]* ${PLUS}0000 ${username} =first-dir= <remote>/\* +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\] +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-revision:1\.1\] +O [0-9-]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir =first-dir= <remote>/\*" rm -rf ${CVSROOT_DIRNAME}/first-dir rm -rf ${CVSROOT_DIRNAME}/second-dir @@ -2246,10 +2343,9 @@ done" spacefiles) # More filename tests, in particular spaces in file names. - # If we start using eval in dotest, this test should become - # easier to write (in fact, it may be possible to just - # change a few of the names in basica or some other test, - # always good to keep the testsuite concise). + # (it might be better to just change a few of the names in + # basica or some other test instead, always good to keep the + # testsuite concise). # I wrote this test to worry about problems in do_module; # but then I found that the CVS server has its own problems @@ -2283,24 +2379,16 @@ ${TESTDIR}/cvsroot/top,v <-- top initial revision: 1\.1 done" mkdir 'first dir' - if ${testcvs} add 'first dir' >${TESTDIR}/output.tmp 2>&1; then - dotest spacefiles-4 "cat ${TESTDIR}/output.tmp" \ + dotest spacefiles-4 "${testcvs} add 'first dir'" \ "Directory ${TESTDIR}/cvsroot/first dir added to the repository" - else - fail spacefiles-4 - fi mkdir ./${dashb} dotest spacefiles-5 "${testcvs} add -- ${dashb}" \ "Directory ${TESTDIR}/cvsroot/${dashb} added to the repository" cd 'first dir' touch 'a file' - if ${testcvs} add 'a file' >${TESTDIR}/output.tmp 2>&1; then - dotest spacefiles-6 "cat ${TESTDIR}/output.tmp" \ + dotest spacefiles-6 "${testcvs} add 'a file'" \ "${PROG} [a-z]*: scheduling file .a file. for addition ${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - else - fail spacefiles-6 - fi dotest spacefiles-7 "${testcvs} -q ci -m add" \ "RCS file: ${TESTDIR}/cvsroot/first dir/a file,v done @@ -2322,22 +2410,13 @@ done" dotest spacefiles-11 "${testcvs} -q co -- ${dashc}" "U \./${dashc}" rm ./${dashc} dotest spacefiles-12 "${testcvs} -q co -- /${dashc}" "U \./${dashc}" - if ${testcvs} -q co 'first dir' >${TESTDIR}/output.tmp 2>&1; then - dotest spacefiles-13 "cat ${TESTDIR}/output.tmp" \ + dotest spacefiles-13 "${testcvs} -q co 'first dir'" \ "U first dir/a file" - else - fail spacefiles-13 - fi cd .. mkdir 3; cd 3 - if ${testcvs} -q co 'first dir/a file' >${TESTDIR}/output.tmp 2>&1 - then - dotest spacefiles-14 "cat ${TESTDIR}/output.tmp" \ + dotest spacefiles-14 "${testcvs} -q co 'first dir/a file'" \ "U first dir/a file" - else - fail spacefiles-14 - fi cd .. rm -r 1 2 3 @@ -3573,15 +3652,9 @@ ${PROG} [a-z]*: skipping directory dir1/sdir" # If we say "yes", then CVS gives errors about not being able to # create lock files. - if echo no | ${testcvs} release -d dir1/sdir \ - >${TESTDIR}/output.tmp 2>&1; then - pass dirs-4 - else - fail dirs-4 - fi # The fact that it says "skipping directory " rather than # "skipping directory dir1/sdir" is some kind of bug. - dotest dirs-4a "cat ${TESTDIR}/output.tmp" \ + echo no | dotest dirs-4 "${testcvs} release -d dir1/sdir" \ "${PROG} [a-z]*: cannot open directory ${TESTDIR}/cvsroot/dir1/sdir: No such file or directory ${PROG} [a-z]*: skipping directory You have \[0\] altered files in this repository\. @@ -3603,7 +3676,6 @@ D/sdir////" cd .. rm -r imp-dir 1 - rm ${TESTDIR}/output.tmp # clean up our repositories rm -rf ${CVSROOT_DIRNAME}/dir1 @@ -3952,7 +4024,7 @@ Merging differences between 1\.1\.2\.1 and 1\.1\.2\.1\.2\.1 into file1 rcsmerge: warning: conflicts during merge" dotest branches-16 "cat file1" '<<<<<<< file1 1:ancest -======= +[=]====== 1:brbr [>]>>>>>> 1\.1\.2\.1\.2\.1' @@ -4514,7 +4586,7 @@ mumble; } EOF # Use dotest_fail because exit status from `cvs diff' must be 1. - dotest_fail rcslib-diffrgx-3 "${testcvs} diff -c -F.*( rgx.c" \ + dotest_fail rcslib-diffrgx-3 "${testcvs} diff -c -F'.*(' rgx.c" \ "Index: rgx\.c =================================================================== RCS file: ${TESTDIR}/cvsroot/first-dir/rgx\.c,v @@ -4904,7 +4976,7 @@ done" " - ${PROG} checkout -jvendor-branch:yesterday -jvendor-branch first-dir + ${PROG} checkout -jvendor-branch:yesterday -jvendor-branch first-dir 2 conflicts created by this import. C first-dir/imported-f1 C first-dir/imported-f2 @@ -4950,7 +5022,7 @@ Use the following command to help the merge:" dotest import-113 \ "${testcvs} -q co -jjunk-1_0 -jjunk-2_0 first-dir" \ -"${PROG} [a-z]*: file first-dir/imported-f1 is present in revision junk-2_0 +"${PROG} [a-z]*: file first-dir/imported-f1 does not exist, but is present in revision junk-2_0 RCS file: ${TESTDIR}/cvsroot/first-dir/imported-f2,v retrieving revision 1\.1\.1\.1 retrieving revision 1\.1\.1\.2 @@ -5014,7 +5086,7 @@ No conflicts created by this import" " - ${PROG} -d ${CVSROOT} checkout -jfreemunger:yesterday -jfreemunger first-dir + ${PROG} -d ${CVSROOT} checkout -jfreemunger:yesterday -jfreemunger first-dir 2 conflicts created by this import. C first-dir/file1 C first-dir/file2 @@ -5147,14 +5219,14 @@ ${PROG} [a-z]*: Updating bdir/subdir" "${PROG} [a-z]*: in directory adir/sub1/ssdir: ${PROG} \[[a-z]* aborted\]: there is no version here; do .${PROG} checkout. first" # The workaround is to leave off the "-r wip_test". - dotest importc-8 "${testcvs} -q ci -m modify" \ + dotest importc-7a "${testcvs} -q ci -m modify" \ "Checking in cdir/cfile; ${TESTDIR}/cvsroot/first-dir/cdir/cfile,v <-- cfile new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1 done" else # Remote doesn't have the bug in the first place. - dotest importc-7 "${testcvs} -q ci -m modify -r wip_test" \ + dotest importc-7r "${testcvs} -q ci -m modify -r wip_test" \ "Checking in cdir/cfile; ${TESTDIR}/cvsroot/first-dir/cdir/cfile,v <-- cfile new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1 @@ -5688,6 +5760,63 @@ M file2 R file3 A file8" + # Checkout the mainline again to try merging from the trunk + # to a branch. + cd .. + rm -r first-dir + dotest join-30 "${testcvs} -q co first-dir" \ +'U first-dir/file2 +U first-dir/file3 +U first-dir/file4 +U first-dir/file7' + cd first-dir + + # Tag the current revisions on the trunk. + dotest join-31 "${testcvs} -q tag T3 ." \ +'T file2 +T file3 +T file4 +T file7' + + # Modify file7. + echo 'second revision of file7' > file7 + dotest join-32 "${testcvs} -q ci -mx ." \ +"Checking in file7; +${TESTDIR}/cvsroot/first-dir/file7,v <-- file7 +new revision: 1\.2; previous revision: 1\.1 +done" + + # And Tag again. + dotest join-33 "${testcvs} -q tag T4 ." \ +'T file2 +T file3 +T file4 +T file7' + + # Now update branch to T3. + cd ../../2/first-dir + dotest join-34 "${testcvs} -q up -jT3" \ +"${PROG} [a-z]*: file file4 does not exist, but is present in revision T3 +U file7" + + # Verify that the right changes have been scheduled. + dotest join-35 "${testcvs} -q update" \ +'A file7' + + # Now update to T4. + # This is probably a bug, although in this particular case it just + # happens to do the right thing; see above join-20. + dotest join-36 "${testcvs} -q up -j T3 -j T4" \ +"A file7 +RCS file: ${TESTDIR}/cvsroot/first-dir/file7,v +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into file7" + + # Verify that the right changes have been scheduled. + dotest join-37 "${testcvs} -q update" \ +'A file7' + cd ../.. rm -r 1 2 3 @@ -5975,6 +6104,49 @@ C m" rm -rf ${CVSROOT_DIRNAME}/$module ;; + join-admin) + mkdir 1; cd 1 + dotest join-admin-1 "$testcvs -q co -l ." '' + module=x + mkdir $module + $testcvs -q add $module >>$LOGFILE 2>&1 + cd $module + + # Create a file so applying the first tag works. + echo foo > a + $testcvs -Q add a > /dev/null 2>&1 + $testcvs -Q ci -m. a > /dev/null 2>&1 + + $testcvs -Q tag -b B + $testcvs -Q tag -b M1 + echo '$''Id$' > b + $testcvs -Q add b > /dev/null 2>&1 + $testcvs -Q ci -m. b > /dev/null 2>&1 + $testcvs -Q tag -b M2 + + $testcvs -Q update -r B + $testcvs -Q update -kk -jM1 -jM2 + $testcvs -Q ci -m. b >/dev/null 2>&1 + + $testcvs -Q update -A + + # Verify that the -kk flag from the update did not + # propagate to the repository. + dotest join-admin-1 "$testcvs status b" \ +"=================================================================== +File: b Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/x/b,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + + cd ../.. + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/$module + ;; + new) # look for stray "no longer pertinent" messages. mkdir ${CVSROOT_DIRNAME}/first-dir @@ -6262,9 +6434,24 @@ File: a Status: File had conflicts on merge "${PROG} [a-z]*: file .a. had a conflict and has not been modified ${PROG} \[[a-z]* aborted\]: correct above errors first!" - echo lame attempt at resolving it >>a # Try to check in the file with the conflict markers in it. - dotest conflicts-status-2 "${testcvs} status a" \ + # Make sure we detect any one of the three conflict markers + mv a aa + grep '^<<<<<<<' aa >a + dotest conflicts-status-2 "${testcvs} -nq ci -m try a" \ +"${PROG} [a-z]*: warning: file .a. seems to still contain conflict indicators" + + grep '^=======' aa >a + dotest conflicts-status-3 "${testcvs} -nq ci -m try a" \ +"${PROG} [a-z]*: warning: file .a. seems to still contain conflict indicators" + + grep '^>>>>>>>' aa >a + dotest conflicts-status-4 "${testcvs} -qn ci -m try a" \ +"${PROG} [a-z]*: warning: file .a. seems to still contain conflict indicators" + + mv aa a + echo lame attempt at resolving it >>a + dotest conflicts-status-5 "${testcvs} status a" \ "=================================================================== File: a Status: File had conflicts on merge @@ -6283,7 +6470,7 @@ done" # OK, the user saw the warning (good user), and now # resolves it for real. echo resolve conflict >a - dotest conflicts-status-3 "${testcvs} status a" \ + dotest conflicts-status-6 "${testcvs} status a" \ "=================================================================== File: a Status: Locally Modified @@ -6297,7 +6484,7 @@ File: a Status: Locally Modified ${TESTDIR}/cvsroot/first-dir/a,v <-- a new revision: 1\.4; previous revision: 1\.3 done" - dotest conflicts-status-4 "${testcvs} status a" \ + dotest conflicts-status-7 "${testcvs} status a" \ "=================================================================== File: a Status: Up-to-date @@ -6790,6 +6977,62 @@ ${PROG} update: cannot open CVS/Entries for reading: No such file or directory" rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + clean) + # Test update -C (overwrite local mods w/ repository copies) + mkdir 1; cd 1 + dotest clean-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest clean-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + echo "The usual boring test text." > cleanme.txt + dotest clean-3 "${testcvs} add cleanme.txt" \ +"${PROG} [a-z]*: scheduling file .cleanme\.txt. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest clean-4 "${testcvs} -q ci -m clean-3" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/cleanme\.txt,v +done +Checking in cleanme\.txt; +${TESTDIR}/cvsroot/first-dir/cleanme\.txt,v <-- cleanme\.txt +initial revision: 1\.1 +done" + # Okay, preparation is done, now test. + # Check that updating an unmodified copy works. + dotest clean-5 "${testcvs} -q update" '' + # Check that updating -C an unmodified copy works. + dotest clean-6 "${testcvs} -q update -C" '' + # Check that updating a modified copy works. + echo "fish" >> cleanme.txt + dotest clean-7 "${testcvs} -q update" 'M cleanme\.txt' + # Check that updating -C a modified copy works. + dotest clean-8 "${testcvs} -q update -C" \ +"(Locally modified cleanme\.txt moved to \.#cleanme\.txt\.1\.1) +U cleanme\.txt" + # And check that the backup copy really was made. + dotest clean-9 "cat .#cleanme.txt.1.1" \ +"The usual boring test text\. +fish" + + # Do it all again, this time naming the file explicitly. + rm .#cleanme.txt.1.1 + dotest clean-10 "${testcvs} -q update cleanme.txt" '' + dotest clean-11 "${testcvs} -q update -C cleanme.txt" '' + echo "bluegill" >> cleanme.txt + dotest clean-12 "${testcvs} -q update cleanme.txt" 'M cleanme\.txt' + dotest clean-13 "${testcvs} -q update -C cleanme.txt" \ +"(Locally modified cleanme\.txt moved to \.#cleanme\.txt\.1\.1) +U cleanme\.txt" + # And check that the backup copy really was made. + dotest clean-14 "cat .#cleanme.txt.1.1" \ +"The usual boring test text\. +bluegill" + + # Done. Clean up. + cd ../.. + rm -rf 1 + rm -rf ${TESTDIR}/cvsroot/first-dir + ;; + modules) # Tests of various ways to define and use modules. # Roadmap to various modules tests: @@ -6802,6 +7045,7 @@ ${PROG} update: cannot open CVS/Entries for reading: No such file or directory" # ampersand modules: modules2 # -s: modules. # -d: modules, modules3, cvsadm + # -i, -o, -u, -e, -t: modules5 # slashes in module names: modules3 ############################################################ @@ -6893,76 +7137,88 @@ ${PROG} [a-z]*: Rebuilding administrative file database" mkdir 1 cd 1 - if ${testcvs} -q co first-dir; then - pass 143 - else - fail 143 - fi + dotest modules-143 "${testcvs} -q co first-dir" "" cd first-dir mkdir subdir - ${testcvs} add subdir >>${LOGFILE} - cd subdir + dotest modules-143a "${testcvs} add subdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository" + cd subdir mkdir ssdir - ${testcvs} add ssdir >>${LOGFILE} + dotest modules-143b "${testcvs} add ssdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/subdir/ssdir added to the repository" touch a b - if ${testcvs} add a b 2>>${LOGFILE} ; then - pass 144 - else - fail 144 - fi + dotest modules-144 "${testcvs} add a b" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: scheduling file .b. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then - pass 145 - else - fail 145 - fi + dotest modules-145 "${testcvs} ci -m added" \ +"${PROG} [a-z]*: Examining . +${PROG} [a-z]*: Examining ssdir +RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/a,v +done +Checking in a; +${TESTDIR}/cvsroot/first-dir/subdir/a,v <-- a +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/b,v +done +Checking in b; +${TESTDIR}/cvsroot/first-dir/subdir/b,v <-- b +initial revision: 1\.1 +done" cd .. - if ${testcvs} -q co CVSROOT >>${LOGFILE}; then - pass 146 - else - fail 146 - fi + dotest modules-146 "${testcvs} -q co CVSROOT" \ +"U CVSROOT/checkoutlist +U CVSROOT/commitinfo +U CVSROOT/config +U CVSROOT/cvswrappers +U CVSROOT/editinfo +U CVSROOT/loginfo +U CVSROOT/modules +U CVSROOT/notify +U CVSROOT/rcsinfo +U CVSROOT/taginfo +U CVSROOT/verifymsg" # Here we test that CVS can deal with CVSROOT (whose repository # is at top level) in the same directory as subdir (whose repository # is a subdirectory of first-dir). TODO: Might want to check that # files can actually get updated in this state. - if ${testcvs} -q update; then - pass 147 - else - fail 147 - fi + dotest modules-147 "${testcvs} -q update" "" + + cat >CVSROOT/modules <<EOF +realmodule first-dir/subdir a +dirmodule first-dir/subdir +namedmodule -d nameddir first-dir/subdir +aliasmodule -a first-dir/subdir/a +aliasnested -a first-dir/subdir/ssdir +topfiles -a first-dir/file1 first-dir/file2 +world -a . +statusmod -s Mungeable +# Options must come before arguments. It is possible this should +# be relaxed at some point (though the result would be bizarre for +# -a); for now test the current behavior. +bogusalias first-dir/subdir/a -a +EOF + dotest modules-148 "${testcvs} ci -m 'add modules' CVSROOT/modules" \ +"Checking in CVSROOT/modules; +${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" - echo realmodule first-dir/subdir a >CVSROOT/modules - echo dirmodule first-dir/subdir >>CVSROOT/modules - echo namedmodule -d nameddir first-dir/subdir >>CVSROOT/modules - echo aliasmodule -a first-dir/subdir/a >>CVSROOT/modules - echo aliasnested -a first-dir/subdir/ssdir >>CVSROOT/modules - echo topfiles -a first-dir/file1 first-dir/file2 >>CVSROOT/modules - echo world -a . >>CVSROOT/modules - echo statusmod -s Mungeable >>CVSROOT/modules - - # Options must come before arguments. It is possible this should - # be relaxed at some point (though the result would be bizarre for - # -a); for now test the current behavior. - echo bogusalias first-dir/subdir/a -a >>CVSROOT/modules - if ${testcvs} ci -m 'add modules' CVSROOT/modules \ - >>${LOGFILE} 2>&1; then - pass 148 - else - fail 148 - fi cd .. # The "statusmod" module contains an error; trying to use it # will produce "modules file missing directory" I think. # However, that shouldn't affect the ability of "cvs co -c" or # "cvs co -s" to do something reasonable with it. - dotest 148a0 "${testcvs} co -c" 'aliasmodule -a first-dir/subdir/a + dotest modules-148a0 "${testcvs} co -c" 'aliasmodule -a first-dir/subdir/a aliasnested -a first-dir/subdir/ssdir bogusalias first-dir/subdir/a -a dirmodule first-dir/subdir @@ -6974,7 +7230,7 @@ world -a \.' # There is code in modules.c:save_d which explicitly skips # modules defined with -a, which is why aliasmodule is not # listed. - dotest 148a1 "${testcvs} co -s" \ + dotest modules-148a1 "${testcvs} co -s" \ 'statusmod Mungeable bogusalias NONE first-dir/subdir/a -a dirmodule NONE first-dir/subdir @@ -6982,123 +7238,60 @@ namedmodule NONE first-dir/subdir realmodule NONE first-dir/subdir a' # Test that real modules check out to realmodule/a, not subdir/a. - if ${testcvs} co realmodule >>${LOGFILE}; then - pass 149a1 - else - fail 149a1 - fi - if test -d realmodule && test -f realmodule/a; then - pass 149a2 - else - fail 149a2 - fi - if test -f realmodule/b; then - fail 149a3 - else - pass 149a3 - fi - if ${testcvs} -q co realmodule; then - pass 149a4 - else - fail 149a4 - fi - if echo "yes" | ${testcvs} release -d realmodule >>${LOGFILE} ; then - pass 149a5 - else - fail 149a5 - fi - - dotest_fail 149b1 "${testcvs} co realmodule/a" \ + dotest modules-149a1 "${testcvs} co realmodule" "U realmodule/a" + dotest modules-149a2 "test -d realmodule && test -f realmodule/a" "" + dotest_fail modules-149a3 "test -f realmodule/b" "" + dotest modules-149a4 "${testcvs} -q co realmodule" "" + dotest modules-149a5 "echo yes | ${testcvs} release -d realmodule" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .realmodule.: " + + dotest_fail modules-149b1 "${testcvs} co realmodule/a" \ "${PROG}"' [a-z]*: module `realmodule/a'\'' is a request for a file in a module which is not a directory' \ "${PROG}"' [a-z]*: module `realmodule/a'\'' is a request for a file in a module which is not a directory '"${PROG}"' \[[a-z]* aborted\]: cannot expand modules' # Now test the ability to check out a single file from a directory - if ${testcvs} co dirmodule/a >>${LOGFILE}; then - pass 150c - else - fail 150c - fi - if test -d dirmodule && test -f dirmodule/a; then - pass 150d - else - fail 150d - fi - if test -f dirmodule/b; then - fail 150e - else - pass 150e - fi - if echo "yes" | ${testcvs} release -d dirmodule >>${LOGFILE} ; then - pass 150f - else - fail 150f - fi + dotest modules-150c "${testcvs} co dirmodule/a" "U dirmodule/a" + dotest modules-150d "test -d dirmodule && test -f dirmodule/a" "" + dotest_fail modules-150e "test -f dirmodule/b" "" + dotest modules-150f "echo yes | ${testcvs} release -d dirmodule" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .dirmodule.: " # Now test the ability to correctly reject a non-existent filename. # For maximum studliness we would check that an error message is # being output. - if ${testcvs} co dirmodule/nonexist >>${LOGFILE} 2>&1; then - # We accept a zero exit status because it is what CVS does - # (Dec 95). Probably the exit status should be nonzero, - # however. - pass 150g1 - else - pass 150g1 - fi + # We accept a zero exit status because it is what CVS does + # (Dec 95). Probably the exit status should be nonzero, + # however. + dotest modules-150g1 "${testcvs} co dirmodule/nonexist" \ +"${PROG} [a-z]*: warning: new-born dirmodule/nonexist has disappeared" # We tolerate the creation of the dirmodule directory, since that # is what CVS does, not because we view that as preferable to not # creating it. - if test -f dirmodule/a || test -f dirmodule/b; then - fail 150g2 - else - pass 150g2 - fi + dotest_fail modules-150g2 "test -f dirmodule/a || test -f dirmodule/b" "" rm -r dirmodule # Now test that a module using -d checks out to the specified # directory. - dotest 150h1 "${testcvs} -q co namedmodule" 'U nameddir/a + dotest modules-150h1 "${testcvs} -q co namedmodule" \ +'U nameddir/a U nameddir/b' - if test -f nameddir/a && test -f nameddir/b; then - pass 150h2 - else - fail 150h2 - fi + dotest modules-150h2 "test -f nameddir/a && test -f nameddir/b" "" echo add line >>nameddir/a - dotest 150h3 "${testcvs} -q co namedmodule" 'M nameddir/a' + dotest modules-150h3 "${testcvs} -q co namedmodule" 'M nameddir/a' rm nameddir/a - dotest 150h4 "${testcvs} -q co namedmodule" 'U nameddir/a' - if echo "yes" | ${testcvs} release -d nameddir >>${LOGFILE} ; then - pass 150h99 - else - fail 150h99 - fi + dotest modules-150h4 "${testcvs} -q co namedmodule" 'U nameddir/a' + dotest modules-150h99 "echo yes | ${testcvs} release -d nameddir" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .nameddir.: " # Now test that alias modules check out to subdir/a, not # aliasmodule/a. - if ${testcvs} co aliasmodule >>${LOGFILE}; then - pass 151 - else - fail 151 - fi - if test -d aliasmodule; then - fail 152 - else - pass 152 - fi + dotest modules-151 "${testcvs} co aliasmodule" "" + dotest_fail modules-152 "test -d aliasmodule" "" echo abc >>first-dir/subdir/a - if (${testcvs} -q co aliasmodule | tee test153.tmp) \ - >>${LOGFILE}; then - pass 153 - else - fail 153 - fi - echo 'M first-dir/subdir/a' >ans153.tmp - if cmp test153.tmp ans153.tmp; then - pass 154 - else - fail 154 - fi + dotest modules-153 "${testcvs} -q co aliasmodule" "M first-dir/subdir/a" cd .. rm -r 1 @@ -7218,6 +7411,8 @@ done" cd CVSROOT echo 'ampermodule &first-dir &second-dir' > modules echo 'combmodule third-dir file3 &first-dir' >> modules + echo 'ampdirmod -d newdir &first-dir &second-dir' >> modules + echo 'badmod -d newdir' >> modules # Depending on whether the user also ran the modules test # we will be checking in revision 1.2 or 1.3. dotest modules2-2 "${testcvs} -q ci -m add-modules" \ @@ -7294,11 +7489,11 @@ initial revision: 1\.1 done" cd .. fi + cd .. + rm -r 1 # Now test the "combmodule" module (combining regular modules # and ampersand modules in the same module definition). - cd .. - rm -r 1 mkdir 1; cd 1 dotest modules2-14 "${testcvs} co combmodule" \ "U combmodule/file3 @@ -7325,6 +7520,22 @@ U first-dir/amper1" cd .. rm -r 1 + # Now test the "ampdirmod" and "badmod" modules to be sure that + # options work with ampersand modules but don't prevent the + # "missing directory" error message. + mkdir 1; cd 1 + dotest modules2-20 "${testcvs} co ampdirmod" \ +"${PROG} [a-z]*: Updating first-dir +U first-dir/amper1 +${PROG} [a-z]*: Updating second-dir" + dotest modules2-21 "test -f newdir/first-dir/amper1" "" + dotest_fail modules2-22 "${testcvs} co badmod" \ +"${PROG} [a-z]*: modules file missing directory for module badmod" \ +"${PROG} [a-z]*: modules file missing directory for module badmod +${PROG} \[[a-z]* aborted\]: cannot expand modules" + cd .. + rm -r 1 + # Test that CVS gives an error if one combines -a with # other options. # Probably would be better to break this out into a separate @@ -7646,6 +7857,226 @@ add-it rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + modules5) + # Test module programs + + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest modules5-1 "${testcvs} -q co first-dir" "" + cd first-dir + mkdir subdir + dotest modules5-2 "${testcvs} add subdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository" + cd subdir + mkdir ssdir + dotest modules5-3 "${testcvs} add ssdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/subdir/ssdir added to the repository" + touch a b + dotest modules5-4 "${testcvs} add a b" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: scheduling file .b. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + + dotest modules5-5 "${testcvs} ci -m added" \ +"${PROG} [a-z]*: Examining . +${PROG} [a-z]*: Examining ssdir +RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/a,v +done +Checking in a; +${TESTDIR}/cvsroot/first-dir/subdir/a,v <-- a +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/b,v +done +Checking in b; +${TESTDIR}/cvsroot/first-dir/subdir/b,v <-- b +initial revision: 1\.1 +done" + + cd .. + dotest modules5-6 "${testcvs} -q co CVSROOT" \ +"U CVSROOT/checkoutlist +U CVSROOT/commitinfo +U CVSROOT/config +U CVSROOT/cvswrappers +U CVSROOT/editinfo +U CVSROOT/loginfo +U CVSROOT/modules +U CVSROOT/notify +U CVSROOT/rcsinfo +U CVSROOT/taginfo +U CVSROOT/verifymsg" + + for i in checkin checkout update export tag; do + cat >> ${CVSROOT_DIRNAME}/$i.sh <<EOF +#! /bin/sh +echo "$i script invoked in \`pwd\`" +echo "args: \$@" +EOF + chmod +x ${CVSROOT_DIRNAME}/$i.sh + done + + OPTS="-i ${CVSROOT_DIRNAME}/checkin.sh -o${CVSROOT_DIRNAME}/checkout.sh -u ${CVSROOT_DIRNAME}/update.sh -e ${CVSROOT_DIRNAME}/export.sh -t${CVSROOT_DIRNAME}/tag.sh" + cat >CVSROOT/modules <<EOF +realmodule ${OPTS} first-dir/subdir a +dirmodule ${OPTS} first-dir/subdir +namedmodule -d nameddir ${OPTS} first-dir/subdir +EOF + + dotest modules5-7 "${testcvs} ci -m 'add modules' CVSROOT/modules" \ +"" \ +"Checking in CVSROOT/modules; +${TESTDIR}/cvsroot/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + + cd .. + rm -rf first-dir + # Test that real modules check out to realmodule/a, not subdir/a. + if test "$remote" = "yes"; then + dotest modules5-8 "${testcvs} co realmodule" \ +"U realmodule/a +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .realmodule.. +checkout script invoked in .* +args: realmodule" + else + dotest modules5-8 "${testcvs} co realmodule" \ +"U realmodule/a +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .realmodule.. +checkout script invoked in ${TESTDIR}/1 +args: realmodule" + fi + dotest modules5-9 "test -d realmodule && test -f realmodule/a" "" + dotest_fail modules5-10 "test -f realmodule/b" "" + if test "$remote" = "yes"; then + dotest modules5-11 "${testcvs} -q co realmodule" \ +"checkout script invoked in .* +args: realmodule" + dotest modules5-12 "${testcvs} -q update" \ +"${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/update\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +update script invoked in /.*/realmodule +args: ${CVSROOT_DIRNAME}/first-dir/subdir" + echo "change" >>realmodule/a + dotest modules5-13 "${testcvs} -q ci -m." \ +"Checking in realmodule/a; +${CVSROOT_DIRNAME}/first-dir/subdir/a,v <-- a +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkin\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +checkin script invoked in /.*/realmodule +args: ${CVSROOT_DIRNAME}/first-dir/subdir" + else + dotest modules5-11 "${testcvs} -q co realmodule" \ +"checkout script invoked in ${TESTDIR}/1 +args: realmodule" + dotest modules5-12 "${testcvs} -q update" \ +"${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/update\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +update script invoked in ${TESTDIR}/1/realmodule +args: ${CVSROOT_DIRNAME}/first-dir/subdir" + echo "change" >>realmodule/a + dotest modules5-13 "${testcvs} -q ci -m." \ +"Checking in realmodule/a; +${CVSROOT_DIRNAME}/first-dir/subdir/a,v <-- a +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkin\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +checkin script invoked in ${TESTDIR}/1/realmodule +args: ${CVSROOT_DIRNAME}/first-dir/subdir" + fi + dotest modules5-14 "echo yes | ${testcvs} release -d realmodule" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .realmodule.: " + dotest modules5-15 "${testcvs} -q rtag -Dnow MYTAG realmodule" \ +"tag script invoked in ${TESTDIR}/1 +args: realmodule MYTAG" + if test "$remote" = "yes"; then + dotest modules5-16 "${testcvs} -q export -r MYTAG realmodule" \ +"U realmodule/a +export script invoked in .* +args: realmodule" + else + dotest modules5-16 "${testcvs} -q export -r MYTAG realmodule" \ +"U realmodule/a +export script invoked in ${TESTDIR}/1 +args: realmodule" + fi + + dotest_fail modules5-17 "${testcvs} co realmodule/a" \ +"${PROG}"' [a-z]*: module `realmodule/a'\'' is a request for a file in a module which is not a directory' \ +"${PROG}"' [a-z]*: module `realmodule/a'\'' is a request for a file in a module which is not a directory +'"${PROG}"' \[[a-z]* aborted\]: cannot expand modules' + + # FIXCVS: The client gets confused in these cases and tries to + # store the scripts in the wrong places. + if test "$remote" != "yes"; then + # Now test the ability to check out a single file from a directory + dotest modules5-18 "${testcvs} co dirmodule/a" \ +"U dirmodule/a +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .dirmodule.. +checkout script invoked in ${TESTDIR}/1 +args: dirmodule" + dotest modules5-19 "test -d dirmodule && test -f dirmodule/a" "" + dotest_fail modules5-20 "test -f dirmodule/b" "" + dotest modules5-21 "echo yes | ${testcvs} release -d dirmodule" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .dirmodule.: " + + # Now test the ability to correctly reject a non-existent filename. + # For maximum studliness we would check that an error message is + # being output. + # We accept a zero exit status because it is what CVS does + # (Dec 95). Probably the exit status should be nonzero, + # however. + dotest modules5-22 "${testcvs} co dirmodule/nonexist" \ +"${PROG} [a-z]*: warning: new-born dirmodule/nonexist has disappeared +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .dirmodule.. +checkout script invoked in ${TESTDIR}/1 +args: dirmodule" + + # We tolerate the creation of the dirmodule directory, since that + # is what CVS does, not because we view that as preferable to not + # creating it. + dotest_fail modules5-23 "test -f dirmodule/a || test -f dirmodule/b" "" + rm -r dirmodule + + # Now test that a module using -d checks out to the specified + # directory. + dotest modules5-24 "${testcvs} -q co namedmodule" \ +"U nameddir/a +U nameddir/b +checkout script invoked in ${TESTDIR}/1 +args: nameddir" + dotest modules5-25 "test -f nameddir/a && test -f nameddir/b" "" + echo add line >>nameddir/a + # This seems suspicious: when we checkout an existing directory, + # the checkout script gets executed in addition to the update + # script. Is that by design or accident? + dotest modules5-26 "${testcvs} -q co namedmodule" \ +"M nameddir/a +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/update\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +update script invoked in ${TESTDIR}/1/nameddir +args: ${CVSROOT_DIRNAME}/first-dir/subdir +checkout script invoked in ${TESTDIR}/1 +args: nameddir" + rm nameddir/a + dotest modules5-27 "${testcvs} -q co namedmodule" \ +"U nameddir/a +${PROG} [a-z]*: Executing ..${CVSROOT_DIRNAME}/update\.sh. .${CVSROOT_DIRNAME}/first-dir/subdir.. +update script invoked in ${TESTDIR}/1/nameddir +args: ${CVSROOT_DIRNAME}/first-dir/subdir +checkout script invoked in ${TESTDIR}/1 +args: nameddir" + dotest modules5-28 "echo yes | ${testcvs} release -d nameddir" \ +"You have \[0\] altered files in this repository\. +Are you sure you want to release (and delete) directory .nameddir.: " + fi + + cd .. + rm -rf 1 ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/*.sh + ;; + mkmodules-temp-file-removal) # When a file listed in checkoutlist doesn't exist, cvs-1.10.4 # would fail to remove the CVSROOT/.#[0-9]* temporary file it @@ -7777,7 +8208,9 @@ Checking in CVSROOT/modules; ${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- modules new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done -${PROG} [a-z]*: Rebuilding administrative file database" +${PROG} [a-z]*: Rebuilding administrative file database" \ +"${PROG} [a-z]*: Examining . +${PROG} [a-z]*: Examining CVSROOT" rm -rf CVS CVSROOT; # Create the various modules @@ -9115,6 +9548,8 @@ ${PROG} [a-z]*: Rebuilding administrative file database" "U CVSROOT/modules" echo "# Module defs for emptydir tests" > CVSROOT/modules echo "2d1mod -d dir2d1/sub2d1 mod1" >> CVSROOT/modules + echo "2d1moda -d dir2d1/suba moda/modasub" >> CVSROOT/modules + echo "comb -a 2d1mod 2d1moda" >> CVSROOT/modules dotest emptydir-2 "${testcvs} ci -m add-modules" \ "${PROG} [a-z]*: Examining CVSROOT @@ -9122,27 +9557,39 @@ Checking in CVSROOT/modules; ${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- modules new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done -${PROG} [a-z]*: Rebuilding administrative file database" +${PROG} [a-z]*: Rebuilding administrative file database" \ +"${PROG} [a-z]*: Examining CVSROOT" rm -rf CVS CVSROOT - mkdir ${CVSROOT_DIRNAME}/mod1 + mkdir ${CVSROOT_DIRNAME}/mod1 ${CVSROOT_DIRNAME}/moda # Populate. Not sure we really need to do this. - dotest emptydir-3 "${testcvs} co mod1" \ -"${PROG} [a-z]*: Updating mod1" + dotest emptydir-3 "${testcvs} -q co -l ." "" + dotest emptydir-3a "${testcvs} co mod1 moda" \ +"${PROG} [a-z]*: Updating mod1 +${PROG} [a-z]*: Updating moda" echo "file1" > mod1/file1 - cd mod1 - dotest emptydir-4 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use '${PROG} commit' to add this file permanently" - cd .. - dotest emptydir-5 "${testcvs} -q ci -m yup mod1" \ + mkdir moda/modasub + dotest emptydir-3b "${testcvs} add moda/modasub" \ +"Directory ${TESTDIR}/cvsroot/moda/modasub added to the repository" + echo "filea" > moda/modasub/filea + dotest emptydir-4 "${testcvs} add mod1/file1 moda/modasub/filea" \ +"${PROG} [a-z]*: scheduling file .mod1/file1. for addition +${PROG} [a-z]*: scheduling file .moda/modasub/filea. for addition +${PROG} [a-z]*: use '${PROG} commit' to add these files permanently" + dotest emptydir-5 "${testcvs} -q ci -m yup" \ "RCS file: ${CVSROOT_DIRNAME}/mod1/file1,v done Checking in mod1/file1; ${CVSROOT_DIRNAME}/mod1/file1,v <-- file1 initial revision: 1\.1 +done +RCS file: ${CVSROOT_DIRNAME}/moda/modasub/filea,v +done +Checking in moda/modasub/filea; +${CVSROOT_DIRNAME}/moda/modasub/filea,v <-- filea +initial revision: 1\.1 done" - rm -rf mod1 CVS + rm -rf mod1 moda CVS # End Populate. dotest emptydir-6 "${testcvs} co 2d1mod" \ @@ -9154,11 +9601,11 @@ U dir2d1/sub2d1/file1" # else) in Emptydir; Emptydir is a placeholder indicating that # the working directory doesn't correspond to anything in # the repository. - dotest emptydir-7 "${testcvs} add emptyfile" \ -"${PROG} [a-z]*: scheduling file .emptyfile. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest_fail emptydir-8 "${testcvs} -q ci -m add" \ -"${PROG} \[[a-z]* aborted\]: cannot check in to ${TESTDIR}/cvsroot/CVSROOT/Emptydir" + dotest_fail emptydir-7 "${testcvs} add emptyfile" \ +"${PROG} \[[a-z]* aborted\]: cannot add to ${TESTDIR}/cvsroot/CVSROOT/Emptydir" + mkdir emptydir + dotest_fail emptydir-8 "${testcvs} add emptydir" \ +"${PROG} \[[a-z]* aborted\]: cannot add to ${TESTDIR}/cvsroot/CVSROOT/Emptydir" cd .. rm -rf CVS dir2d1 @@ -9173,11 +9620,25 @@ ${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" dotest emptydir-11 "${testcvs} -q -n update -d -P" '' cd ../.. rm -r edir + cd .. + # Now start playing with moda. + mkdir 2; cd 2 + dotest emptydir-12 "${testcvs} -q co 2d1moda" \ +"U dir2d1/suba/filea" + # OK, this is the crux of the matter. Some people think + # it would be more logical if this showed "moda". But why + # "moda" (from module 2d1moda) and not "." (from module 2d1mod)? + dotest emptydir-13 "cat dir2d1/CVS/Repository" "CVSROOT/Emptydir" + dotest emptydir-14 "${testcvs} co comb" \ +"${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1 +${PROG} [a-z]*: Updating dir2d1/suba" + dotest emptydir-15 "cat dir2d1/CVS/Repository" "CVSROOT/Emptydir" cd .. - rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/mod1 + rm -r 1 2 + rm -rf ${CVSROOT_DIRNAME}/mod1 ${CVSROOT_DIRNAME}/moda # I guess for the moment the convention is going to be # that we don't need to remove ${CVSROOT_DIRNAME}/CVSROOT/Emptydir ;; @@ -10002,6 +10463,14 @@ done" cd .. dotest errmsg2-8 "${testcvs} add first-dir/sdir" \ "Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" + # while we're here... check commit with no CVS directory + dotest_fail errmsg2-8a "${testcvs} -q ci first-dir nonexistant" \ +"${PROG} [a-z]*: nothing known about .nonexistant' +${PROG} \[[a-z]* aborted\]: correct above errors first!" + dotest_fail errmsg2-8b "${testcvs} -q ci nonexistant first-dir" \ +"${PROG} [a-z]*: nothing known about .nonexistant' +${PROG} \[[a-z]* aborted\]: correct above errors first!" + dotest errmsg2-8c "${testcvs} -q ci first-dir" "" cd first-dir @@ -10075,6 +10544,16 @@ done" dotest_fail errmsg2-19 "${testcvs} annotate -rtest -Dyesterday" \ "${PROG} \[[a-z]* aborted\]: rcsbuf_open: internal error" + # trying to import the repository + + if test "$remote" = "no"; then + cd ${CVSROOT_DIRNAME} + dotest_fail errmsg2-20 "${testcvs} import -mtest . A B" \ +"${PROG} \[[a-z]* aborted\]: attempt to import the repository" + dotest_fail errmsg2-21 "${testcvs} import -mtest first-dir A B" \ +"${PROG} \[[a-z]* aborted\]: attempt to import the repository" + fi + cd .. rm -r 1 rm -rf ${TESTDIR}/cvsroot/first-dir @@ -10366,7 +10845,25 @@ Fnw1 _watched= D _watched=" cd .. - cd .. + # Do a little error testing + dotest devcom2-18 "${testcvs} -q co -d first+dir first-dir" \ +"U first${PLUS}dir/nw1 +U first${PLUS}dir/w1 +U first${PLUS}dir/w2 +U first${PLUS}dir/w3" + cd first+dir + dotest_fail devcom2-19 "${testcvs} edit" \ +"${PROG} \[[a-z]* aborted\]: current directory (${TESTDIR}/2/first${PLUS}dir) contains an invalid character (${PLUS},>;=\\\\t\\\\n)" + + # Make sure there are no droppings lying around + dotest devcom2-20 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw1 _watched= +Fw2 _watched= +Fw3 _watched= +Fnw1 _watched= +D _watched=" + + cd ../.. # Use -f because of the readonly files. rm -rf 1 2 @@ -10639,14 +11136,9 @@ done" rm -f CVS/Baserev # This will fail on most systems. - if echo "yes" | ${testcvs} -Q unedit $file \ - >${TESTDIR}/test.tmp 2>&1 ; then - dotest unedit-without-baserev-4 "cat ${TESTDIR}/test.tmp" \ + echo "yes" | dotest unedit-without-baserev-4 "${testcvs} -Q unedit $file" \ "m has been modified; revert changes${QUESTION} ${PROG} unedit: m not mentioned in CVS/Baserev ${PROG} unedit: run update to complete the unedit" - else - fail unedit-without-baserev-4 - fi # SunOS4.1.4 systems make it this far, but with a corrupted # CVS/Entries file. Demonstrate the corruption! @@ -10694,13 +11186,9 @@ rcsmerge: warning: conflicts during merge ${PROG} [a-z]*: conflicts found in m C m" rm CVS/Baserev - if (echo yes | ${testcvs} unedit m) >${TESTDIR}/test.tmp 2>&1; then - dotest unedit-without-baserev-14 "cat ${TESTDIR}/test.tmp" \ + echo yes | dotest unedit-without-baserev-14 "${testcvs} unedit m" \ "m has been modified; revert changes${QUESTION} ${PROG} unedit: m not mentioned in CVS/Baserev ${PROG} unedit: run update to complete the unedit" - else - fail unedit-without-baserev-14 - fi if test "$remote" = yes; then dotest unedit-without-baserev-15 "${testcvs} -q update" "U m" else @@ -10844,26 +11332,14 @@ ${QUESTION} first-dir/rootig.c ${QUESTION} second-dir/.cvsignore ${QUESTION} second-dir/notig.c" - if echo yes | ${testcvs} release -d first-dir \ - >${TESTDIR}/ignore.tmp; then - pass ignore-192 - else - fail ignore-192 - fi - dotest ignore-193 "cat ${TESTDIR}/ignore.tmp" \ + echo yes | dotest ignore-192 "${testcvs} release -d first-dir" \ "${QUESTION} \.cvsignore You have \[0\] altered files in this repository. Are you sure you want to release (and delete) directory .first-dir': " echo add a line >>second-dir/foobar.c rm second-dir/notig.c second-dir/.cvsignore - if echo yes | ${testcvs} release -d second-dir \ - >${TESTDIR}/ignore.tmp; then - pass ignore-194 - else - fail ignore-194 - fi - dotest ignore-195 "cat ${TESTDIR}/ignore.tmp" \ + echo yes | dotest ignore-194 "${testcvs} release -d second-dir" \ "M foobar.c You have \[1\] altered files in this repository. Are you sure you want to release (and delete) directory .second-dir': " @@ -10871,7 +11347,6 @@ Are you sure you want to release (and delete) directory .second-dir': " rm -r 1 cd .. rm -r wnt - rm ${TESTDIR}/ignore.tmp rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir ;; @@ -10887,8 +11362,8 @@ Are you sure you want to release (and delete) directory .second-dir': " mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1; cd 1 dotest binfiles-1 "${testcvs} -q co first-dir" '' - awk 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ - </dev/null | tr '@' '\000' >binfile.dat + ${AWK} 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ + </dev/null | ${TR} '@' '\000' >binfile.dat cat binfile.dat binfile.dat >binfile2.dat cd first-dir cp ../binfile.dat binfile @@ -11159,8 +11634,8 @@ total revisions: 1 # each be distinct from each other. We also make sure to include # a few likely end-of-line patterns to make sure nothing is # being munged as if in text mode. - awk 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ - </dev/null | tr '@' '\000' >../binfile + ${AWK} 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ + </dev/null | ${TR} '@' '\000' >../binfile cat ../binfile ../binfile >../binfile2 cat ../binfile2 ../binfile >../binfile3 @@ -11321,8 +11796,8 @@ checkin mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1; cd 1 dotest binfiles3-1 "${testcvs} -q co first-dir" '' - awk 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ - </dev/null | tr '@' '\000' >binfile.dat + ${AWK} 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ + </dev/null | ${TR} '@' '\000' >binfile.dat cd first-dir echo hello >file1 dotest binfiles3-2 "${testcvs} add file1" \ @@ -11377,9 +11852,9 @@ total revisions: 3 # OK, now test admin -o on a binary file. See "admin" # test for a more complete list of admin -o tests. cp ${TESTDIR}/1/binfile.dat ${TESTDIR}/1/binfile4.dat - echo '%%$$##@@!!jjiiuull' | tr j '\000' >>${TESTDIR}/1/binfile4.dat + echo '%%$$##@@!!jjiiuull' | ${TR} j '\000' >>${TESTDIR}/1/binfile4.dat cp ${TESTDIR}/1/binfile4.dat ${TESTDIR}/1/binfile5.dat - echo 'aawwee%$$##@@!!jjil' | tr w '\000' >>${TESTDIR}/1/binfile5.dat + echo 'aawwee%$$##@@!!jjil' | ${TR} w '\000' >>${TESTDIR}/1/binfile5.dat cp ../binfile4.dat file1 dotest binfiles3-9 "${testcvs} -q ci -m change" \ @@ -12011,6 +12486,7 @@ ${PROG} [a-z]*: Rebuilding administrative file database" dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" cd CVSROOT + rm -f $TESTDIR/testlog $TESTDIR/testlog2 echo "ALL sh -c \"echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat >/dev/null\"" > loginfo # The following cases test the format string substitution echo "ALL echo %{sVv} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo @@ -12051,11 +12527,18 @@ ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done ${PROG} [a-z]*: loginfo:1: no such user variable \${=ZEE}" + echo line0 >>file1 + dotest info-6b "${testcvs} -q -sOTHER=foo ci -m mod-it" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: loginfo:1: no such user variable \${=ZEE}" echo line1 >>file1 dotest info-7 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m mod-it" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 +new revision: 1\.3; previous revision: 1\.2 done" cd .. dotest info-9 "cat $TESTDIR/testlog" "xenv-valueyz=${username}=${TESTDIR}/cvsroot=" @@ -12068,6 +12551,11 @@ first-dir file1,1.1,1.2 first-dir 1.2 first-dir file1 first-dir 1.1AX +first-dir file1ux +first-dir file1,1.2,1.3 +first-dir 1.3 +first-dir file1 +first-dir 1.2AX first-dir file1ux' cd CVSROOT @@ -12075,7 +12563,7 @@ first-dir file1ux' dotest info-11 "${testcvs} -q -s ZEE=garbage ci -m nuke-loginfo" \ "Checking in loginfo; ${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo -new revision: 1\.[0-9]; previous revision: 1\.[0-9] +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done ${PROG} [a-z]*: Rebuilding administrative file database" @@ -12086,6 +12574,7 @@ if head -1 < \$1 | grep '^BugId:[ ]*[0-9][0-9]*$' > /dev/null; then exit 0 else echo "No BugId found." + sleep 1 exit 1 fi EOF @@ -12094,7 +12583,7 @@ EOF dotest info-v1 "${testcvs} -q ci -m add-verification" \ "Checking in verifymsg; ${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg -new revision: 1\.2; previous revision: 1\.1 +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done ${PROG} [a-z]*: Rebuilding administrative file database" @@ -12111,7 +12600,7 @@ EOF dotest info-v3 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3; previous revision: 1\.2 +new revision: 1\.4; previous revision: 1\.3 done" cd .. mkdir another-dir @@ -12130,7 +12619,7 @@ ${PROG} \[[a-z]* aborted\]: Message verification failed" dotest info-cleanup-verifymsg "${testcvs} -q ci -m nuke-verifymsg" \ "Checking in verifymsg; ${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg -new revision: 1\.[0-9]; previous revision: 1\.[0-9] +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done ${PROG} [a-z]*: Rebuilding administrative file database" cd .. @@ -12392,8 +12881,10 @@ U file1' # -h: admin-19a-log # -N: log, log2, admin-19a-log # -b, -r: log - # -d: rcs - # -s, -R: rcs3 + # -d: logopt, rcs + # -s: logopt, rcs3 + # -R: logopt, rcs3 + # -w, -t: not tested yet (TODO) # Check in a file with a few revisions and branches. mkdir ${CVSROOT_DIRNAME}/first-dir @@ -12560,6 +13051,33 @@ description: ${log_rev3} ${log_trailer}" +# Check that unusual syntax works correctly. + + dotest log-14c "${testcvs} log -r: file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" + dotest log-14d "${testcvs} log -r, file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" + dotest log-14e "${testcvs} log -r. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" + dotest log-15 "${testcvs} log -r1.2 file1" \ "${log_header} ${log_tags} @@ -12600,6 +13118,18 @@ description: ${log_rev2b} ${log_trailer}" + # Multiple -r options are undocumented; see comments in + # cvs.texinfo about whether they should be deprecated. + dotest log-18a "${testcvs} log -r1.2.2.2 -r1.3:1.3 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev3} +${log_rev2b} +${log_trailer}" + # This test would fail with the old invocation of rlog, but it # works with the builtin log support. dotest log-19 "${testcvs} log -rbranch. file1" \ @@ -12730,18 +13260,13 @@ date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; 1 =============================================================================" - # I believe that in Real Life (TM), this is broken for remote. - # That is, the filename in question must be the filename of a - # file on the server. It only happens to work here because the - # client machine and the server machine are one and the same. echo 'longer description' >${TESTDIR}/descrip echo 'with two lines' >>${TESTDIR}/descrip dotest log2-7 "${testcvs} admin -t${TESTDIR}/descrip file1" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done" dotest_fail log2-7a "${testcvs} admin -t${TESTDIR}/nonexist file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -${PROG} \[[a-z]* aborted\]: can't stat ${TESTDIR}/nonexist: No such file or directory" +"${PROG} \[[a-z]* aborted\]: can't stat ${TESTDIR}/nonexist: No such file or directory" dotest log2-8 "${testcvs} log -N file1" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 @@ -12760,22 +13285,13 @@ date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; 1 =============================================================================" - # Reading the description from stdin is broken for remote. - # See comments in cvs.texinfo for a few more notes on this. - if test "x$remote" = xno; then + # TODO: `cvs admin -t "my message" file1' is a request to + # read the message from stdin and to operate on two files. + # Should test that there is an error because "my message" + # doesn't exist. - # TODO: `cvs admin -t "my message" file1' is a request to - # read the message from stdin and to operate on two files. - # Should test that there is an error because "my message" - # doesn't exist. - - if echo change from stdin | ${testcvs} admin -t -q file1 - then - pass log2-9 - else - fail log2-9 - fi - dotest log2-10 "${testcvs} log -N file1" " + dotest log2-9 "echo change from stdin | ${testcvs} admin -t -q file1" "" + dotest log2-10 "${testcvs} log -N file1" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 head: 1\.1 @@ -12792,8 +13308,6 @@ date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; 1 =============================================================================" - fi # end of tests skipped for remote - cd .. rm ${TESTDIR}/descrip rm -r first-dir @@ -12801,6 +13315,49 @@ date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; ;; + logopt) + # Some tests of log.c's option parsing and such things. + mkdir 1; cd 1 + dotest logopt-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest logopt-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + echo hi >file1 + dotest logopt-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest logopt-4 "${testcvs} -q ci -m add file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + cd .. + + dotest logopt-5 "${testcvs} log -R -d 2038-01-01" \ +"${PROG} [a-z]*: Logging \. +${PROG} [a-z]*: Logging first-dir +${TESTDIR}/cvsroot/first-dir/file1,v" + dotest logopt-6 "${testcvs} log -d 2038-01-01 -R" \ +"${PROG} [a-z]*: Logging \. +${PROG} [a-z]*: Logging first-dir +${TESTDIR}/cvsroot/first-dir/file1,v" + dotest logopt-6a "${testcvs} log -Rd 2038-01-01" \ +"${PROG} [a-z]*: Logging \. +${PROG} [a-z]*: Logging first-dir +${TESTDIR}/cvsroot/first-dir/file1,v" + dotest logopt-7 "${testcvs} log -s Exp -R" \ +"${PROG} [a-z]*: Logging \. +${PROG} [a-z]*: Logging first-dir +${TESTDIR}/cvsroot/first-dir/file1,v" + + cd .. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + ann) # Tests of "cvs annotate". See also: # basica-10 A simple annotate test @@ -13296,7 +13853,7 @@ add file1 # ISO8601 format. There are many, many, other variations # specified by ISO8601 which we should be testing too. - dotest rcs-3 "${testcvs} -q log -d 1996-12-11<" " + dotest rcs-3 "${testcvs} -q log -d '1996-12-11<'" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 head: 1\.3 @@ -13315,9 +13872,8 @@ delete second line; modify twelfth line =============================================================================" # RFC822 format (as amended by RFC1123). - if ${testcvs} -q log -d '<3 Apr 2000 00:00' >${TESTDIR}/rcs4.tmp - then - dotest rcs-4 "cat ${TESTDIR}/rcs4.tmp" " + dotest rcs-4 "${testcvs} -q log -d '<3 Apr 2000 00:00'" \ +" RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 head: 1\.3 @@ -13338,9 +13894,6 @@ revision 1\.1 date: 1996/11/24 15:56:05; author: kingdon; state: Exp; add file1 =============================================================================" - else - fail rcs-4 - fi # Intended behavior for "cvs annotate" is that it displays the # last two digits of the year. Make sure it does that rather @@ -13545,47 +14098,22 @@ a1 1 next branch revision @" - if ${testcvs} -q update -p -D '1970-12-31 11:30 UT' file2 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs-9 "cat ${TESTDIR}/rcs4.tmp" "start revision" - else - fail rcs-9 - fi + dotest rcs-9 "${testcvs} -q update -p -D '1970-12-31 11:30 UT' file2" \ +"start revision" - if ${testcvs} -q update -p -D '1970-12-31 12:30 UT' file2 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs-10 "cat ${TESTDIR}/rcs4.tmp" "mid revision" - else - fail rcs-10 - fi + dotest rcs-10 "${testcvs} -q update -p -D '1970-12-31 12:30 UT' file2" \ +"mid revision" - if ${testcvs} -q update -p -D '1971-01-01 00:30 UT' file2 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs-11 "cat ${TESTDIR}/rcs4.tmp" "new year revision" - else - fail rcs-11 - fi + dotest rcs-11 "${testcvs} -q update -p -D '1971-01-01 00:30 UT' file2" \ +"new year revision" # Same test as rcs-10, but with am/pm. - if ${testcvs} -q update -p -D 'December 31, 1970 12:30pm UT' file2 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs-12 "cat ${TESTDIR}/rcs4.tmp" "mid revision" - else - fail rcs-12 - fi + dotest rcs-12 "${testcvs} -q update -p -D 'December 31, 1970 12:30pm UT' file2" \ +"mid revision" # Same test as rcs-11, but with am/pm. - if ${testcvs} -q update -p -D 'January 1, 1971 12:30am UT' file2 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs-13 "cat ${TESTDIR}/rcs4.tmp" "new year revision" - else - fail rcs-13 - fi + dotest rcs-13 "${testcvs} -q update -p -D 'January 1, 1971 12:30am UT' file2" \ +"new year revision" # OK, now make sure cvs log doesn't have any trouble with the # newphrases and such. @@ -13631,9 +14159,49 @@ revision 1\.2\.6\.1 date: 1971/01/01 08:00:05; author: joe; state: Exp; lines: ${PLUS}1 -1 \*\*\* empty log message \*\*\* =============================================================================" + # Now test each date format for "cvs log -d". + # Earlier than 1971-01-01 + dotest rcs-15 "${testcvs} -q log -d '<1971-01-01 00:00 GMT' file2 \ + | grep revision" \ +"total revisions: 7; selected revisions: 3 +revision 1\.3 +revision 1\.2 +revision 1\.1" + # Later than 1971-01-01 + dotest rcs-16 "${testcvs} -q log -d '1971-01-01 00:00 GMT<' file2 \ + | grep revision" \ +"total revisions: 7; selected revisions: 4 +revision 1\.5 +revision 1\.4 +revision 1\.2\.6\.2 +revision 1\.2\.6\.1" + # Alternate syntaxes for later and earlier; multiple -d options + dotest rcs-17 "${testcvs} -q log -d '>1971-01-01 00:00 GMT' \ + -d '1970-12-31 12:15 GMT>' file2 | grep revision" \ +"total revisions: 7; selected revisions: 5 +revision 1\.5 +revision 1\.4 +revision 1\.1 +revision 1\.2\.6\.2 +revision 1\.2\.6\.1" + # Range, and single date + dotest rcs-18 "${testcvs} -q log -d '1970-12-31 11:30 GMT' \ + -d '1971-01-01 00:00:05 GMT<1971-01-01 01:00:01 GMT' \ + file2 | grep revision" \ +"total revisions: 7; selected revisions: 2 +revision 1\.5 +revision 1\.1" + # Alternate range syntax; equality + dotest rcs-19 "${testcvs} -q log \ + -d '1971-01-01 01:00:01 GMT>=1971-01-01 00:00:05 GMT' \ + file2 | grep revision" \ +"total revisions: 7; selected revisions: 2 +revision 1\.5 +revision 1\.4" + cd .. - rm -r first-dir ${TESTDIR}/rcs4.tmp + rm -r first-dir rm -rf ${CVSROOT_DIRNAME}/first-dir ;; @@ -13684,41 +14252,17 @@ EOF cd first-dir # 9 Sep 1999 - if ${testcvs} -q update -p -D '1999-09-09 11:30 UT' file1 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs2-2 "cat ${TESTDIR}/rcs4.tmp" \ + dotest rcs2-2 "${testcvs} -q update -p -D '1999-09-09 11:30 UT' file1" \ "Tonight we're going to party like it's a certain year" - else - fail rcs2-2 - fi # 1 Jan 2001. - if ${testcvs} -q update -p -D '2001-01-01 11:30 UT' file1 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs2-3 "cat ${TESTDIR}/rcs4.tmp" \ + dotest rcs2-3 "${testcvs} -q update -p -D '2001-01-01 11:30 UT' file1" \ "two year hiatus" - else - fail rcs2-3 - fi # 29 Feb 2000 - if ${testcvs} -q update -p -D '2000-02-29 11:30 UT' file1 \ - >${TESTDIR}/rcs4.tmp - then - dotest rcs2-4 "cat ${TESTDIR}/rcs4.tmp" \ + dotest rcs2-4 "${testcvs} -q update -p -D '2000-02-29 11:30 UT' file1" \ "2000 is also a good year for leaping" - else - fail rcs2-4 - fi # 29 Feb 2003 is invalid - if ${testcvs} -q update -p -D '2003-02-29 11:30 UT' file1 \ - >${TESTDIR}/rcs4.tmp 2>&1 - then - fail rcs2-5 - else - dotest rcs2-5 "cat ${TESTDIR}/rcs4.tmp" \ + dotest_fail rcs2-5 "${testcvs} -q update -p -D '2003-02-29 11:30 UT' file1" \ "${PROG} \[[a-z]* aborted\]: Can't parse date/time: 2003-02-29 11:30 UT" - fi dotest rcs2-6 "${testcvs} -q update -p -D 2007-01-07 file1" \ "head revision" @@ -13736,22 +14280,10 @@ EOF # 31 May 1999), it seems to fail. # # Sigh. - if ${testcvs} -q update -p -D '100 months' file1 \ - >${TESTDIR}/rcs4.tmp 2>&1 - then - dotest rcs2-7 "cat ${TESTDIR}/rcs4.tmp" "head revision" - else - fail rcs2-7 - fi - if ${testcvs} -q update -p -D '8 years' file1 \ - >${TESTDIR}/rcs4.tmp 2>&1 - then - dotest rcs2-8 "cat ${TESTDIR}/rcs4.tmp" "head revision" - else - fail rcs2-8 - fi - - rm ${TESTDIR}/rcs4.tmp + dotest rcs2-7 "${testcvs} -q update -p -D '100 months' file1" \ +"head revision" + dotest rcs2-8 "${testcvs} -q update -p -D '8 years' file1" \ +"head revision" cd .. rm -r first-dir @@ -13772,7 +14304,7 @@ EOF # question one way or the other (it has a grammar but almost # nothing about lexical analysis). dotest_fail rcs3-1 "${testcvs} -q co first-dir" \ -"${PROG} \[[a-z]* aborted\]: unexpected end of file reading ${TESTDIR}/cvsroot/first-dir/file1,v" +"${PROG} \[[a-z]* aborted\]: EOF while looking for value in RCS file ${TESTDIR}/cvsroot/first-dir/file1,v" cat <<EOF >${CVSROOT_DIRNAME}/first-dir/file1,v head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02 ; author jeremiah ;state ; branches; next;desc @@1.1log@@text@head@ @@ -13793,25 +14325,15 @@ head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02 ; author jeremiah ;state ; branches; next;desc @@1.1 log @@text @head@ EOF dotest rcs3-4 "${testcvs} -q co first-dir" 'U first-dir/file1' - if test "$remote" = no; then - # Ouch, didn't expect this one. FIXCVS. Or maybe just remove - # the feature, if this is a -s problem? - dotest_fail rcs3-5 "${testcvs} log -s nostate first-dir/file1" \ -".*[Aa]ssertion.*failed${DOTSTAR}" ".*failed assertion${DOTSTAR}" - else # remote - # Is this a reaction to the lack of TopLevelAdmin or something? - # Seems pretty strange to me. Seems vaguely similar to the - # "no repository" message in errmsg2-16 although I'm leaving - # it here in case there is a difference between "cvs add" and a - # normal start_recursion command like "cvs log". - dotest_fail rcs3-5 "${testcvs} log -s nostate first-dir/file1" \ -"${PROG} log: cannot open CVS/Entries for reading: No such file or directory -${PROG} \[log aborted\]: no repository" - cd first-dir - dotest_fail rcs3-5a "${testcvs} log -s nostate file1" \ + + # Ouch, didn't expect this one. FIXCVS. Or maybe just remove + # the feature, if this is a -s problem? + dotest_fail rcs3-5 "${testcvs} log -s nostate first-dir/file1" \ "${DOTSTAR}ssertion.*failed${DOTSTAR}" "${DOTSTAR}failed assertion${DOTSTAR}" - cd .. - fi # remote + cd first-dir + dotest_fail rcs3-5a "${testcvs} log -s nostate file1" \ +"${DOTSTAR}ssertion.*failed${DOTSTAR}" "${DOTSTAR}failed assertion${DOTSTAR}" + cd .. # See remote code above for rationale for cd. cd first-dir @@ -13819,7 +14341,7 @@ ${PROG} \[log aborted\]: no repository" "${TESTDIR}/cvsroot/first-dir/file1,v" # OK, now put an extraneous '\0' at the end. - awk </dev/null 'BEGIN { printf "@%c", 10 }' | tr '@' '\000' \ + ${AWK} </dev/null 'BEGIN { printf "@%c", 10 }' | ${TR} '@' '\000' \ >>${CVSROOT_DIRNAME}/first-dir/file1,v dotest_fail rcs3-7 "${testcvs} log -s nostate file1" \ "${PROG} \[[a-z]* aborted\]: unexpected '.x0' reading revision number in RCS file ${TESTDIR}/cvsroot/first-dir/file1,v" @@ -13882,6 +14404,7 @@ ${PROG} \[[a-z]* aborted\]: cannot stat ${TESTDIR}/locks: No such file or direct cd ../../.. dotest lockfiles-8 "${testcvs} -q update" "" + dotest lockfiles-9 "${testcvs} -q co -l ." "" cd CVSROOT echo "# nobody here but us comments" >config @@ -14099,9 +14622,9 @@ ${PROG} \[[a-z]* aborted\]: could not find desired version 1\.6 in ${TESTDIR}/cv # database dirs in a workspace with later revisions than those in the # recovered repository cd repos-first-dir -DATADIRS=\`find . -name CVS\` +DATADIRS=\`find . -name CVS -print\` cd ../first-dir -find . -name CVS |xargs rm -rf +find . -name CVS -print | xargs rm -rf for file in \${DATADIRS}; do cp -r ../repos-first-dir/\${file} \${file} done" >fixit @@ -14204,42 +14727,79 @@ done" # slowly and carefully. cat >${CVSROOT_DIRNAME}/CVSROOT/history <<EOF O3395c677|anonymous|<remote>/*0|ccvs||ccvs +O3396c677|anonymous|<remote>/src|ccvs||src +O3397c677|kingdon|<remote>/*0|ccvs||ccvs M339cafae|nk|<remote>|ccvs/src|1.229|sanity.sh +M339cafff|anonymous|<remote>|ccvs/src|1.23|Makefile M339dc339|kingdon|~/work/*0|ccvs/src|1.231|sanity.sh W33a6eada|anonymous|<remote>*4|ccvs/emx||Makefile.in C3b235f50|kingdon|<remote>|ccvs/emx|1.3|README M3b23af50|kingdon|~/work/*0|ccvs/doc|1.281|cvs.texinfo EOF dotest history-1 "${testcvs} history -e -a" \ -"O 06/04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\* -W 06/17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx == <remote>/emx -M 06/10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src -C 06/10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> -M 06/10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc -M 06/10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" - if ${testcvs} history -e -a -D '10 Jun 1997 13:00 UT' \ - >${TESTDIR}/output.tmp - then - dotest history-2 "cat ${TESTDIR}/output.tmp" \ -"W 06/17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx == <remote>/emx -M 06/10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src -C 06/10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> -M 06/10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc" - else - fail history-2 - fi - if ${testcvs} history -e -a -D '10 Jun 2001 13:00 UT' \ - >${TESTDIR}/output.tmp - then - # For reasons that are completely unclear to me, the number - # of spaces betwen "kingdon" and "1.281" is different than - # for the other tests. - dotest history-3 "cat ${TESTDIR}/output.tmp" \ -"M 06/10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc" - else - fail history-3 - fi - rm ${TESTDIR}/output.tmp +"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\* +O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\* +M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src == <remote> +W 1997-06-17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx == <remote>/emx +O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\* +M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> +M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc +M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" + + dotest history-2 "${testcvs} history -e -a -D '10 Jun 1997 13:00 UT'" \ +"W 1997-06-17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx == <remote>/emx +M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> +M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc" + + dotest history-3 "${testcvs} history -e -a -D '10 Jun 2001 13:00 UT'" \ +"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc == ~/work/ccvs/doc" + + dotest history-4 "${testcvs} history -ac sanity.sh" \ +"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" + + dotest history-5 "${testcvs} history -a -xCGUWAMR README sanity.sh" \ +"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> +M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" + + dotest history-6 "${testcvs} history -xCGUWAMR -a -f README -f sanity.sh" \ +"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> +M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" + + dotest history-7 "${testcvs} history -xCGUWAMR -a -f sanity.sh README" \ +"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src == ~/work/ccvs/src +C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote> +M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>" + + dotest history-8 "${testcvs} history -ca -D '1970-01-01 00:00 UT'" \ +"M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity.sh ccvs/src == <remote> +M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src == <remote> +M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src == ~/work/ccvs/src +M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc == ~/work/ccvs/doc" + + dotest history-9 "${testcvs} history -acl" \ +"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc == ~/work/ccvs/doc +M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src == <remote> +M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src == ~/work/ccvs/src" + + dotest history-10 "${testcvs} history -lca -D '1970-01-01 00:00 UT'" \ +"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc == ~/work/ccvs/doc +M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src == <remote> +M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src == ~/work/ccvs/src" + + dotest history-11 "${testcvs} history -aw" \ +"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\* +O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\* +O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\*" + + dotest history-12 "${testcvs} history -aw -D'1970-01-01 00:00 UT'" \ +"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\* +O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\* +O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\*" ;; big) @@ -14481,17 +15041,17 @@ done" # the attic (may that one can remain a fatal error, seems less # useful for access control). mkdir 1; cd 1 - dotest modes-1 "${testcvs} -q co -l ." '' + dotest modes3-1 "${testcvs} -q co -l ." '' mkdir first-dir second-dir - dotest modes-2 "${testcvs} add first-dir second-dir" \ + dotest modes3-2 "${testcvs} add first-dir second-dir" \ "Directory ${TESTDIR}/cvsroot/first-dir added to the repository Directory ${TESTDIR}/cvsroot/second-dir added to the repository" touch first-dir/aa second-dir/ab - dotest modes-3 "${testcvs} add first-dir/aa second-dir/ab" \ + dotest modes3-3 "${testcvs} add first-dir/aa second-dir/ab" \ "${PROG} [a-z]*: scheduling file .first-dir/aa. for addition ${PROG} [a-z]*: scheduling file .second-dir/ab. for addition ${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest modes-4 "${testcvs} -q ci -m add" \ + dotest modes3-4 "${testcvs} -q ci -m add" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v done Checking in first-dir/aa; @@ -14505,7 +15065,7 @@ ${TESTDIR}/cvsroot/second-dir/ab,v <-- ab initial revision: 1\.1 done" chmod a= ${TESTDIR}/cvsroot/first-dir - dotest modes-5 "${testcvs} update" \ + dotest modes3-5 "${testcvs} update" \ "${PROG} [a-z]*: Updating \. ${PROG} [a-z]*: Updating first-dir ${PROG} [a-z]*: cannot open directory ${TESTDIR}/cvsroot/first-dir: Permission denied @@ -14517,7 +15077,7 @@ ${PROG} [a-z]*: Updating second-dir" # won't have it in their working directory. But the next # one is more of a problem if it is fatal. rm -r first-dir - dotest modes-6 "${testcvs} update -dP" \ + dotest modes3-6 "${testcvs} update -dP" \ "${PROG} [a-z]*: Updating . ${PROG} [a-z]*: Updating CVSROOT U ${DOTSTAR} @@ -15470,9 +16030,9 @@ xx" "${PROG} [a-z]*: scheduling file .file1. for addition ${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - awk 'BEGIN { printf "%c%c%c%sRevision: 1.1 $@%c%c", \ + ${AWK} 'BEGIN { printf "%c%c%c%sRevision: 1.1 $@%c%c", \ 2, 10, 137, "$", 13, 10 }' \ - </dev/null | tr '@' '\000' >../binfile.dat + </dev/null | ${TR} '@' '\000' >../binfile.dat cp ../binfile.dat . dotest keyword2-5 "${testcvs} add -kb binfile.dat" \ "${PROG} [a-z]*: scheduling file .binfile\.dat. for addition @@ -15577,8 +16137,8 @@ done" T file1" dotest keyword2-18 "${testcvs} -q update -r branch2" '' - awk 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ - </dev/null | tr '@' '\000' >>binfile.dat + ${AWK} 'BEGIN { printf "%c%c%c@%c%c", 2, 10, 137, 13, 10 }' \ + </dev/null | ${TR} '@' '\000' >>binfile.dat dotest keyword2-19 "${testcvs} -q ci -m badbadbad" \ "Checking in binfile\.dat; ${TESTDIR}/cvsroot/first-dir/binfile\.dat,v <-- binfile\.dat @@ -15851,51 +16411,16 @@ new revision: 1\.1\.4\.2; previous revision: 1\.1\.4\.1 done" cd ../.. mkdir 2; cd - if ${testcvs} -q export -r br2 -D'1 minute ago' first-dir \ - >${TESTDIR}/tagdate.tmp 2>&1; then - if ${EXPR} "`cat ${TESTDIR}/tagdate.tmp`" : \ -"[UP] first-dir/file1" >/dev/null; then - pass tagdate-14 - else - echo "** expected: " >>${LOGFILE} - echo "[UP] first-dir/file1" >>${LOGFILE} - echo "** got: " >>${LOGFILE} - cat ${TESTDIR}/tagdate.tmp >>${LOGFILE} - fail tagdate-14 - fi - else - echo "Bad exit status" >>${LOGFILE} - fail tagdate-14 - fi - - if ${EXPR} "`cat first-dir/file1`" : "br2-1" >/dev/null; then - pass tagdate-15 - else - fail tagdate-15 - fi + dotest tagdate-14 "${testcvs} -q export -r br2 -D'1 minute ago' first-dir" \ +"[UP] first-dir/file1" + dotest tagdate-15 "cat first-dir/file1" "br2-1" # Now for annotate cd ../1/first-dir - if ${testcvs} annotate -rbr2 -D'1 minute ago' \ - >${TESTDIR}/tagdate.tmp 2>&1; then - if ${EXPR} "`cat ${TESTDIR}/tagdate.tmp`" : \ + dotest tagdate-16 "${testcvs} annotate -rbr2 -D'1 minute ago'" \ "Annotations for file1 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -1\.1\.4\.1 (${username} *[0-9a-zA-Z-]*): br2-1" >/dev/null; then - pass tagdate-16 - else - echo "** expected: " >>${LOGFILE} - echo "Annotations for file1 -\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -1\.1\.4\.1 (${username} *[0-9a-zA-Z-]*): br2-1" >>${LOGFILE} - echo "** got: " >>${LOGFILE} - cat ${TESTDIR}/tagdate.tmp >>${LOGFILE} - fail tagdate-16 - fi - else - echo "Bad exit status" >>${LOGFILE} - fail tagdate-16 - fi +1\.1\.4\.1 (${username} *[0-9a-zA-Z-]*): br2-1" dotest tagdate-17 "${testcvs} annotate -rbr2 -Dnow" \ "Annotations for file1 @@ -15908,7 +16433,6 @@ done" fi cd ../.. - rm ${TESTDIR}/tagdate.tmp rm -r 1 2 rm -rf ${CVSROOT_DIRNAME}/first-dir ;; @@ -16452,16 +16976,27 @@ access list: keyword substitution: kv total revisions: 2 =============================================================================" - # Put the access list back, to avoid special cases later. - dotest admin-19a-fix "${testcvs} -q admin -eauth3 file1" \ + fi # end of tests skipped for remote + + # Now test that plain -e works right. + dotest admin-19a-2 "${testcvs} -q admin -e file1" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done" - fi # end of tests skipped for remote + dotest admin-19a-3 "${testcvs} -q log -h -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 2 +=============================================================================" - # Now test that plain -e is at least parsed right. CVS 1.10 - # would wrongly treat "-e file1" as "-efile1". - dotest_fail admin-19a-2 "${testcvs} -q admin -e file1" \ -"${PROG} \[[a-z]* aborted\]: removing entire access list not yet implemented" + # Put the access list back, to avoid special cases later. + dotest admin-19a-4 "${testcvs} -q admin -afoo,auth2 file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" # Add another revision to file2, so we can delete one. echo 'add a line' >> file2 @@ -17020,13 +17555,13 @@ add cat >${TESTDIR}/lockme <<EOF #!${TESTSHELL} -line=\`grep <\$1/\$2,v 'locks ${username}:1\.[0-9];'\` +line=\`grep <\$1/\$2,v 'locks ${author}:1\.[0-9];'\` if test -z "\$line"; then # It isn't locked exit 0 else - user=\`echo \$line | sed -e 's/locks \\(${username}\\):[0-9.]*;.*/\\1/'\` - version=\`echo \$line | sed -e 's/locks ${username}:\\([0-9.]*\\);.*/\\1/'\` + user=\`echo \$line | sed -e 's/locks \\(${author}\\):[0-9.]*;.*/\\1/'\` + version=\`echo \$line | sed -e 's/locks ${author}:\\([0-9.]*\\);.*/\\1/'\` echo "\$user has file a-lock locked for version \$version" exit 1 fi @@ -19479,7 +20014,7 @@ done" # something which doesn't make sense. dotest_fail multiroot3-10 \ "${testcvs} -q -d ${CVSROOT1} diff dir1/file1 dir2/file2" \ -"${PROG} [a-z]*: failed to create lock directory in repository .${TESTDIR}/root1/dir2': No such file or directory +"${PROG} [a-z]*: failed to create lock directory for .${TESTDIR}/root1/dir2' (${TESTDIR}/root1/dir2/#cvs.lock): No such file or directory ${PROG} [a-z]*: failed to obtain dir lock in repository .${TESTDIR}/root1/dir2' ${PROG} \[[a-z]* aborted\]: read lock failed - giving up" else @@ -19500,7 +20035,37 @@ ${PROG} \[[a-z]* aborted\]: read lock failed - giving up" # This one is supposed to work. dotest multiroot3-11 "${testcvs} -q diff dir1/file1 dir2/file2" "" - cd .. + # make sure we can't access across repositories + # FIXCVS: we probably shouldn't even create the local directories + # in this case, but we do, so deal with it. + mkdir 1a + cd 1a + dotest_fail multiroot3-12 \ +"${testcvs} -d ${CVSROOT1} -q co ../root2/dir2" \ +"${PROG} [a-z]*: in directory \.\./root2/dir2: +${PROG} [a-z]*: .\.\..-relative repositories are not supported. +${PROG} \[[a-z]* aborted\]: illegal source repository" + rm -rf ../root2 + dotest_fail multiroot3-13 \ +"${testcvs} -d ${CVSROOT2} -q co ../root1/dir1" \ +"${PROG} [a-z]*: in directory \.\./root1/dir1: +${PROG} [a-z]*: .\.\..-relative repositories are not supported. +${PROG} \[[a-z]* aborted\]: illegal source repository" + rm -rf ../root1 + dotest_fail multiroot3-14 \ +"${testcvs} -d ${CVSROOT1} -q co ./../root2/dir2" \ +"${PROG} [a-z]*: in directory \./\.\./root2/dir2: +${PROG} [a-z]*: .\.\..-relative repositories are not supported. +${PROG} \[[a-z]* aborted\]: illegal source repository" + rm -rf ../root2 + dotest_fail multiroot3-15 \ +"${testcvs} -d ${CVSROOT2} -q co ./../root1/dir1" \ +"${PROG} [a-z]*: in directory \./\.\./root1/dir1: +${PROG} [a-z]*: .\.\..-relative repositories are not supported. +${PROG} \[[a-z]* aborted\]: illegal source repository" + rm -rf ../root1 + + cd ../.. if test "$keep" = yes; then echo Keeping ${TESTDIR} and exiting due to --keep @@ -19733,7 +20298,7 @@ No such file or directory" pserver) # Test basic pserver functionality. if test "$remote" = yes; then - # First set SystemAuth=no. Not really necessary, I don't + # First set SystemAuth=no. Not really necessary, I don't # think, but somehow it seems like the clean thing for # the testsuite. mkdir 1; cd 1 @@ -19746,22 +20311,27 @@ ${TESTDIR}/cvsroot/CVSROOT/config,v <-- config new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done ${PROG} [a-z]*: Rebuilding administrative file database" - echo "testme:q6WV9d2t848B2:`id -un`" \ - >${CVSROOT_DIRNAME}/CVSROOT/passwd - ${testcvs} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + cat >${CVSROOT_DIRNAME}/CVSROOT/passwd <<EOF +testme:q6WV9d2t848B2:$username +anonymous::$username +$username: +willfail: :whocares +EOF + dotest_fail pserver-3 "${testcvs} pserver" \ +"error 0 Server configuration missing --allow-root in inetd.conf" <<EOF BEGIN AUTH REQUEST ${CVSROOT_DIRNAME} testme Ay::'d END AUTH REQUEST EOF - dotest pserver-3 "cat ${TESTDIR}/pserver.tmp" \ -"error 0 Server configuration missing --allow-root in inetd.conf" # Sending the Root and noop before waiting for the # "I LOVE YOU" is bogus, but hopefully we can get # away with it. - ${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + dotest pserver-4 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU +ok" <<EOF BEGIN AUTH REQUEST ${CVSROOT_DIRNAME} testme @@ -19770,11 +20340,11 @@ END AUTH REQUEST Root ${CVSROOT_DIRNAME} noop EOF - dotest pserver-4 "cat ${TESTDIR}/pserver.tmp" \ -"${DOTSTAR} LOVE YOU -ok" - ${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + dotest pserver-5 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU +E Protocol error: Root says \"${TESTDIR}/1\" but pserver says \"${CVSROOT_DIRNAME}\" +error " <<EOF BEGIN AUTH REQUEST ${CVSROOT_DIRNAME} testme @@ -19783,40 +20353,85 @@ END AUTH REQUEST Root ${TESTDIR}/1 noop EOF - dotest pserver-5 "cat ${TESTDIR}/pserver.tmp" \ -"${DOTSTAR} LOVE YOU -E Protocol error: Root says \"${TESTDIR}/1\" but pserver says \"${CVSROOT_DIRNAME}\" -error " - ${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + dotest_fail pserver-6 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"I HATE YOU" <<EOF BEGIN AUTH REQUEST ${CVSROOT_DIRNAME} testme Ay::'d^b?hd END AUTH REQUEST EOF - dotest pserver-6 "cat ${TESTDIR}/pserver.tmp" \ -"I HATE YOU" - ${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + dotest_fail pserver-7 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"I HATE YOU" <<EOF BEGIN VERIFICATION REQUEST ${CVSROOT_DIRNAME} testme Ay::'d^b?hd END VERIFICATION REQUEST EOF - dotest pserver-7 "cat ${TESTDIR}/pserver.tmp" \ -"I HATE YOU" - ${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver >${TESTDIR}/pserver.tmp 2>&1 <<EOF + dotest pserver-8 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU" <<EOF BEGIN VERIFICATION REQUEST ${CVSROOT_DIRNAME} testme Ay::'d END VERIFICATION REQUEST EOF - dotest pserver-8 "cat ${TESTDIR}/pserver.tmp" \ -"${DOTSTAR} LOVE YOU" + +# Tests pserver-9 through pserver-13 are about empty passwords + + # Test empty password (both sides) for aliased user + dotest pserver-9 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU" <<EOF +BEGIN AUTH REQUEST +${CVSROOT_DIRNAME} +anonymous +A +END AUTH REQUEST +EOF + + # Test empty password (server side only) for aliased user + dotest pserver-10 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU" <<EOF +BEGIN AUTH REQUEST +${CVSROOT_DIRNAME} +anonymous +Aanythingwouldworkhereittrulydoesnotmatter +END AUTH REQUEST +EOF + + # Test empty (both sides) password for non-aliased user + dotest pserver-11 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU" <<EOF +BEGIN AUTH REQUEST +${CVSROOT_DIRNAME} +${username} +A +END AUTH REQUEST +EOF + + # Test empty (server side only) password for non-aliased user + dotest pserver-12 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} LOVE YOU" <<EOF +BEGIN AUTH REQUEST +${CVSROOT_DIRNAME} +${username} +Anypasswordwouldworkwhynotthisonethen +END AUTH REQUEST +EOF + + # Test failure of whitespace password + dotest_fail pserver-13 "${testcvs} --allow-root=${CVSROOT_DIRNAME} pserver" \ +"${DOTSTAR} HATE YOU" <<EOF +BEGIN AUTH REQUEST +${CVSROOT_DIRNAME} +willfail +Amquiteunabletocomeupwithinterestingpasswordsanymore +END AUTH REQUEST +EOF # Clean up. echo "# comments only" >config @@ -19835,38 +20450,28 @@ ${PROG} [a-z]*: Rebuilding administrative file database" server) # Some tests of the server (independent of the client). if test "$remote" = yes; then - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-1 "${testcvs} server" \ +"E Protocol error: Root request missing +error " <<EOF Directory bogus mumble/bar update EOF - dotest server-1 "cat ${TESTDIR}/server.tmp" \ -"E Protocol error: Root request missing -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server-1 - fi # Could also test for relative pathnames here (so that crerepos-6a # and crerepos-6b can use :fork:). - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-2 "${testcvs} server" "ok" <<EOF Set OTHER=variable Set MYENV=env-value init ${TESTDIR}/crerepos EOF - dotest server-2 "cat ${TESTDIR}/server.tmp" "ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-2 - fi dotest server-3 "test -d ${TESTDIR}/crerepos/CVSROOT" "" # Now some tests of gzip-file-contents (used by jCVS). - awk 'BEGIN { \ + ${AWK} 'BEGIN { \ printf "%c%c%c%c%c%c.6%c%c+I-.%c%c%c%c5%c;%c%c%c%c", \ 31, 139, 8, 64, 5, 7, 64, 3, 225, 2, 64, 198, 185, 5, 64, 64, 64}' \ - </dev/null | tr '\100' '\000' >gzipped.dat + </dev/null | ${TR} '\100' '\000' >gzipped.dat # Note that the CVS client sends "-b 1.1.1", and this # test doesn't. But the server also defaults to that. cat <<EOF >session.dat @@ -19887,21 +20492,20 @@ z25 EOF cat gzipped.dat >>session.dat echo import >>session.dat - if ${testcvs} server >${TESTDIR}/server.tmp <session.dat; then - dotest server-4 "cat ${TESTDIR}/server.tmp" "M N dir1/file1 + dotest server-4 "${testcvs} server" \ +"M N dir1/file1 M M No conflicts created by this import M -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-4 - fi +ok" <session.dat dotest server-5 \ "${testcvs} -q -d ${TESTDIR}/crerepos co -p dir1/file1" "test" # OK, here are some notify tests. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-6 "${testcvs} server" \ +"Notified \./ +${TESTDIR}/crerepos/dir1/file1 +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 @@ -19909,18 +20513,16 @@ Notify file1 E Fri May 7 13:21:09 1999 GMT myhost some-work-dir EUC noop EOF - dotest server-6 "cat ${TESTDIR}/server.tmp" \ -"Notified \./ -${TESTDIR}/crerepos/dir1/file1 -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-6 - fi # Sending the second "noop" before waiting for the output # from the first is bogus but hopefully we can get away # with it. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-7 "${testcvs} server" \ +"Notified \./ +${TESTDIR}/crerepos/dir1/file1 +ok +Notified \./ +${TESTDIR}/crerepos/dir1/file1 +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 @@ -19931,20 +20533,14 @@ Notify file1 E The 57th day of Discord in the YOLD 3165 myhost some-work-dir EUC noop EOF - dotest server-7 "cat ${TESTDIR}/server.tmp" \ -"Notified \./ -${TESTDIR}/crerepos/dir1/file1 -ok -Notified \./ -${TESTDIR}/crerepos/dir1/file1 -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-7 - fi # OK, now test a few error conditions. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + # FIXCVS: should give "error" and no "Notified", like server-9 + dotest server-8 "${testcvs} server" \ +"E ${PROG} server: invalid character in editor value +Notified \./ +${TESTDIR}/crerepos/dir1/file1 +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 @@ -19952,18 +20548,10 @@ Notify file1 E Setting Orange, the 52th day of Discord in the YOLD 3165 myhost some-work-dir EUC noop EOF - # FIXCVS: should give "error" and no "Notified", like server-9 - dotest server-8 "cat ${TESTDIR}/server.tmp" \ -"E ${PROG} server: invalid character in editor value -Notified \./ -${TESTDIR}/crerepos/dir1/file1 -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-8 - fi - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-9 "${testcvs} server" \ +"E Protocol error; misformed Notify request +error " <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 @@ -19971,64 +20559,43 @@ Notify file1 E Setting Orange+57th day of Discord myhost some-work-dir EUC noop EOF - dotest server-9 "cat ${TESTDIR}/server.tmp" \ -"E Protocol error; misformed Notify request -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server-9 - fi # First demonstrate an interesting quirk in the protocol. # The "watchers" request selects the files to operate based # on files which exist in the working directory. So if we # don't send "Entry" or the like, it won't do anything. # Wants to be documented in cvsclient.texi... - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-10 "${testcvs} server" "ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 watchers EOF - dotest server-10 "cat ${TESTDIR}/server.tmp" \ -"ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-10 - fi - # See if "watchers" and "editors" display the right thing. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-11 "${testcvs} server" \ +"M file1 ${username} tedit tunedit tcommit +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 Entry /file1/1.1//// watchers EOF - dotest server-11 "cat ${TESTDIR}/server.tmp" \ -"M file1 ${username} tedit tunedit tcommit -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-11 - fi - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-12 "${testcvs} server" \ +"M file1 ${username} The 57th day of Discord in the YOLD 3165 myhost some-work-dir +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 Entry /file1/1.1//// editors EOF - dotest server-12 "cat ${TESTDIR}/server.tmp" \ -"M file1 ${username} The 57th day of Discord in the YOLD 3165 myhost some-work-dir -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-12 - fi # Now do an unedit. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-13 "${testcvs} server" \ +"Notified \./ +${TESTDIR}/crerepos/dir1/file1 +ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 @@ -20036,40 +20603,20 @@ Notify file1 U 7 May 1999 15:00 GMT myhost some-work-dir EUC noop EOF - dotest server-13 "cat ${TESTDIR}/server.tmp" \ -"Notified \./ -${TESTDIR}/crerepos/dir1/file1 -ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-13 - fi # Now try "watchers" and "editors" again. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-14 "${testcvs} server" "ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 watchers EOF - dotest server-14 "cat ${TESTDIR}/server.tmp" \ -"ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-14 - fi - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server-15 "${testcvs} server" "ok" <<EOF Root ${TESTDIR}/crerepos Directory . ${TESTDIR}/crerepos/dir1 editors EOF - dotest server-15 "cat ${TESTDIR}/server.tmp" \ -"ok" - else - echo "exit status was $?" >>${LOGFILE} - fail server-15 - fi if test "$keep" = yes; then echo Keeping ${TESTDIR} and exiting due to --keep @@ -20078,7 +20625,6 @@ EOF rm -rf ${TESTDIR}/crerepos rm gzipped.dat session.dat - rm ${TESTDIR}/server.tmp fi # skip the whole thing for local ;; @@ -20086,66 +20632,46 @@ EOF # More server tests, in particular testing that various # possible security holes are plugged. if test "$remote" = yes; then - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server2-1 "${testcvs} server" \ +"E protocol error: directory '${TESTDIR}/cvsroot/\.\./dir1' not within root '${TESTDIR}/cvsroot' +error " <<EOF Root ${TESTDIR}/cvsroot Directory . ${TESTDIR}/cvsroot/../dir1 noop EOF - dotest server2-1 "cat ${TESTDIR}/server.tmp" \ -"E protocol error: directory '${TESTDIR}/cvsroot/\.\./dir1' not within root '${TESTDIR}/cvsroot' -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server2-1 - fi - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server2-2 "${testcvs} server" \ +"E protocol error: directory '${TESTDIR}/cvsrootdir1' not within root '${TESTDIR}/cvsroot' +error " <<EOF Root ${TESTDIR}/cvsroot Directory . ${TESTDIR}/cvsrootdir1 noop EOF - dotest server2-2 "cat ${TESTDIR}/server.tmp" \ -"E protocol error: directory '${TESTDIR}/cvsrootdir1' not within root '${TESTDIR}/cvsroot' -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server2-2 - fi - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest 2-3 "${testcvs} server" \ +"E protocol error: directory '${TESTDIR}' not within root '${TESTDIR}/cvsroot' +error " <<EOF Root ${TESTDIR}/cvsroot Directory . ${TESTDIR} noop EOF - dotest server2-3 "cat ${TESTDIR}/server.tmp" \ -"E protocol error: directory '${TESTDIR}' not within root '${TESTDIR}/cvsroot' -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server2-3 - fi # OK, now a few tests for the rule that one cannot pass a # filename containing a slash to Modified, Is-modified, # Notify, Questionable, or Unchanged. For completeness # we'd try them all. For lazyness/conciseness we don't. - if ${testcvs} server >${TESTDIR}/server.tmp <<EOF; then + dotest server2-4 "${testcvs} server" \ +"E protocol error: directory 'foo/bar' not within current directory +error " <<EOF Root ${TESTDIR}/cvsroot Directory . ${TESTDIR}/cvsroot Unchanged foo/bar noop EOF - dotest server2-4 "cat ${TESTDIR}/server.tmp" \ -"E protocol error: directory 'foo/bar' not within current directory -error " - else - echo "exit status was $?" >>${LOGFILE} - fail server2-4 - fi fi ;; @@ -20175,7 +20701,7 @@ EOF CVS_SERVER=${TESTDIR}/serveme; export CVS_SERVER mkdir 1; cd 1 dotest_fail client-1 "${testcvs} -q co first-dir" \ -"${PROG} \[checkout aborted\]: This server does not support the global -q option\." +"${PROG} \[checkout aborted\]: This server does not support the global -q option${DOTSTAR}" dotest client-2 "${testcvs} co first-dir" "special message" cat >${TESTDIR}/serveme <<EOF @@ -20197,8 +20723,10 @@ cat >/dev/null EOF cd first-dir mkdir ${TESTDIR}/bogus + # The ${DOTSTAR} is to match a potential "broken pipe" if the + # client exits before the server script sends everything dotest_fail client-3 "${testcvs} update" "merge-it -${PROG} \[update aborted\]: protocol error: Copy-file tried to specify directory" +${PROG} \[update aborted\]: protocol error: Copy-file tried to specify director${DOTSTAR}" cat >${TESTDIR}/serveme <<EOF #!${TESTSHELL} echo "Valid-requests Root Valid-responses valid-requests Directory Entry Modified Unchanged Argument Argumentx ci co update" diff --git a/gnu/usr.bin/cvs/src/tag.c b/gnu/usr.bin/cvs/src/tag.c index fb19cbae78a..a9d8534160a 100644 --- a/gnu/usr.bin/cvs/src/tag.c +++ b/gnu/usr.bin/cvs/src/tag.c @@ -234,9 +234,12 @@ check_fileproc (callerdat, finfo) if ((status != T_UPTODATE) && (status != T_CHECKOUT)) { error (0, 0, "%s is locally modified", finfo->fullname); + freevers_ts (&vers); return (1); } } + else + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (finfo->update_dir[0] == '\0') xdir = "."; @@ -266,11 +269,12 @@ check_fileproc (callerdat, finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = tag_delproc; - vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (vers->srcfile == NULL) { if (!really_quiet) error (0, 0, "nothing known about %s", finfo->file); + freevers_ts (&vers); + freenode (p); return (1); } @@ -579,6 +583,8 @@ tag_fileproc (callerdat, finfo) if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) { free (oversion); + if (branch_mode) + free (rev); freevers_ts (&vers); return (0); } @@ -600,6 +606,8 @@ tag_fileproc (callerdat, finfo) cvs_output (rev, 0); cvs_output ("\n", 1); free (oversion); + if (branch_mode) + free (rev); freevers_ts (&vers); return (0); } @@ -611,9 +619,13 @@ tag_fileproc (callerdat, finfo) error (1, retcode == -1 ? errno : 0, "failed to set tag %s to revision %s in %s", symtag, rev, vers->srcfile->path); + if (branch_mode) + free (rev); freevers_ts (&vers); return (1); } + if (branch_mode) + free (rev); RCS_rewrite (vers->srcfile, NULL, NULL); /* more warm fuzzies */ @@ -718,7 +730,7 @@ val_direntproc (callerdat, dir, repository, update_dir, entries) files in a directory which does not exist yet, but which is about to be created. */ if (isdir (dir)) - return 0; + return R_PROCESS; return R_SKIP_ALL; } diff --git a/gnu/usr.bin/cvs/src/vers_ts.c b/gnu/usr.bin/cvs/src/vers_ts.c index e1ba32d2c4c..8d552845a03 100644 --- a/gnu/usr.bin/cvs/src/vers_ts.c +++ b/gnu/usr.bin/cvs/src/vers_ts.c @@ -87,22 +87,17 @@ Version_TS (finfo, options, tag, date, force_tag_match, set_time) vers_ts->vn_user = xstrdup (entdata->version); vers_ts->ts_rcs = xstrdup (entdata->timestamp); vers_ts->ts_conflict = xstrdup (entdata->conflict); - if (!tag) + if (!(tag || date) && !(sdtp && sdtp->aflag)) { - if (!(sdtp && sdtp->aflag)) - vers_ts->tag = xstrdup (entdata->tag); - } - if (!date) - { - if (!(sdtp && sdtp->aflag)) - vers_ts->date = xstrdup (entdata->date); + vers_ts->tag = xstrdup (entdata->tag); + vers_ts->date = xstrdup (entdata->date); } vers_ts->entdata = entdata; } /* Even if we don't have an "entries line" as such (vers_ts->entdata), we want to pick up options which could have been from a Kopt protocol request. */ - if (!options || (options && *options == '\0')) + if (!options || *options == '\0') { if (!(sdtp && sdtp->aflag)) vers_ts->options = xstrdup (entdata->options); @@ -126,6 +121,8 @@ Version_TS (finfo, options, tag, date, force_tag_match, set_time) char *rcsexpand = RCS_getexpand (finfo->rcs); if (rcsexpand != NULL) { + if (vers_ts->options != NULL) + free (vers_ts->options); vers_ts->options = xmalloc (strlen (rcsexpand) + 3); strcpy (vers_ts->options, "-k"); strcat (vers_ts->options, rcsexpand); diff --git a/gnu/usr.bin/cvs/src/version.c b/gnu/usr.bin/cvs/src/version.c index 97622ffa797..c8273fb55c5 100644 --- a/gnu/usr.bin/cvs/src/version.c +++ b/gnu/usr.bin/cvs/src/version.c @@ -12,7 +12,7 @@ #include "cvs.h" -char *version_string = "\nConcurrent Versions System (CVS) 1.10.7"; +char *version_string = "Concurrent Versions System (CVS) 1.11"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT @@ -27,3 +27,49 @@ char *config_string = " (server)\n"; char *config_string = "\n"; #endif #endif + +static const char *const version_usage[] = +{ + "Usage: %s %s\n", + NULL +}; + +int +version (argc, argv) + int argc; + char **argv; +{ + int err = 0; + + if (argc == -1) + usage (version_usage); + +#ifdef CLIENT_SUPPORT + if (client_active) + (void) fputs ("Client: ", stdout); +#endif + + /* Having the year here is a good idea, so people have + some idea of how long ago their version of CVS was + released. */ + (void) fputs (version_string, stdout); + (void) fputs (config_string, stdout); + +#ifdef CLIENT_SUPPORT + if (client_active) + { + (void) fputs ("Server: ", stdout); + start_server (); + if (supported_request ("version")) + send_to_server ("version\012", 0); + else + { + send_to_server ("noop\012", 0); + fputs ("(unknown)\n", stdout); + } + err = get_responses_and_close (); + } +#endif + return err; +} + diff --git a/gnu/usr.bin/cvs/src/watch.c b/gnu/usr.bin/cvs/src/watch.c index b2935ac3dd0..1a0ecfe61f4 100644 --- a/gnu/usr.bin/cvs/src/watch.c +++ b/gnu/usr.bin/cvs/src/watch.c @@ -473,6 +473,7 @@ watchers_fileproc (callerdat, finfo) cvs_output ("\n", 1); } out:; + free (them); return 0; } diff --git a/gnu/usr.bin/cvs/windows-NT/ChangeLog b/gnu/usr.bin/cvs/windows-NT/ChangeLog index c6f954be2e5..dd0d855dadc 100644 --- a/gnu/usr.bin/cvs/windows-NT/ChangeLog +++ b/gnu/usr.bin/cvs/windows-NT/ChangeLog @@ -1,3 +1,12 @@ +2000-06-26 Larry Jones <larry.jones@sdrc.com> + + * config.h: Define REGEX_MALLOC and _REGEX_RE_COMP. + +2000-01-02 Karl Fogel <kfogel@red-bean.com> + + * pwd.c (getlogin): try to find login name in environment + variables before asking the operating system. + 1999-02-26 Jim Kingdon <http://www.cyclic.com> * options.h: Make RELATIVE_REPOS the default, as in diff --git a/gnu/usr.bin/cvs/windows-NT/config.h b/gnu/usr.bin/cvs/windows-NT/config.h index 69b0fe75c61..33550d595cc 100644 --- a/gnu/usr.bin/cvs/windows-NT/config.h +++ b/gnu/usr.bin/cvs/windows-NT/config.h @@ -325,3 +325,8 @@ extern char *sock_strerror (int errnum); moot since the use of buffer.c ensures that writes will only be as big as the buffers). */ #define SEND_NEVER_PARTIAL 1 + +/* Force lib/regex.c to use malloc instead of messing around with alloca + and define the old re_comp routines that we use. */ +#define REGEX_MALLOC 1 +#define _REGEX_RE_COMP 1 diff --git a/gnu/usr.bin/cvs/windows-NT/pwd.c b/gnu/usr.bin/cvs/windows-NT/pwd.c index 683bb0c3653..62f32cd3ad8 100644 --- a/gnu/usr.bin/cvs/windows-NT/pwd.c +++ b/gnu/usr.bin/cvs/windows-NT/pwd.c @@ -87,13 +87,16 @@ getgrnam (char *name) char * getlogin () { + /* This is how a windows user would override their login name. */ if (!login) - login = win32getlogin(); - - if (!login) /* have we been called before? */ login = lookup_env (login_strings); - if (!login) /* have we been successful? */ + /* In the absence of user override, ask the operating system. */ + if (!login) + login = win32getlogin(); + + /* If all else fails, fall back on Old Faithful. */ + if (!login) login = anonymous; return login; |