From 0c111e2b51cac7eead56494b30c5977e4ec9a8ea Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 10 Mar 1998 13:40:57 +0000 Subject: Import cvs-1.9.26 onto vendor branch --- contrib/cvs/src/ChangeLog | 485 ++ contrib/cvs/src/Makefile.in | 8 +- contrib/cvs/src/add.c | 2 +- contrib/cvs/src/admin.c | 24 +- contrib/cvs/src/buffer.c | 48 +- contrib/cvs/src/buffer.h | 6 + contrib/cvs/src/checkin.c | 85 +- contrib/cvs/src/checkout.c | 125 +- contrib/cvs/src/classify.c | 4 +- contrib/cvs/src/client.c | 68 +- contrib/cvs/src/client.h | 2 +- contrib/cvs/src/commit.c | 124 +- contrib/cvs/src/create_adm.c | 13 +- contrib/cvs/src/cvs.h | 8 +- contrib/cvs/src/diff.c | 19 +- contrib/cvs/src/entries.c | 34 +- contrib/cvs/src/filesubr.c | 205 +- contrib/cvs/src/find_names.c | 16 +- contrib/cvs/src/hardlink.c | 298 + contrib/cvs/src/hardlink.h | 33 + contrib/cvs/src/hash.c | 11 + contrib/cvs/src/hash.h | 1 + contrib/cvs/src/import.c | 211 +- contrib/cvs/src/lock.c | 12 - contrib/cvs/src/main.c | 2 +- contrib/cvs/src/mkmodules.c | 6 +- contrib/cvs/src/myndbm.c | 31 +- contrib/cvs/src/no_diff.c | 7 + contrib/cvs/src/parseinfo.c | 20 + contrib/cvs/src/patch.c | 68 +- contrib/cvs/src/rcs.c | 2451 +++++--- contrib/cvs/src/rcs.h | 8 +- contrib/cvs/src/rcscmds.c | 63 +- contrib/cvs/src/recurse.c | 2 +- contrib/cvs/src/sanity.sh | 12773 +++++++++++++++++++++-------------------- contrib/cvs/src/server.c | 88 +- contrib/cvs/src/server.h | 31 +- contrib/cvs/src/subr.c | 37 +- contrib/cvs/src/update.c | 650 ++- contrib/cvs/src/vers_ts.c | 4 +- contrib/cvs/src/version.c | 2 +- 41 files changed, 10910 insertions(+), 7175 deletions(-) create mode 100644 contrib/cvs/src/hardlink.c create mode 100644 contrib/cvs/src/hardlink.h (limited to 'contrib/cvs/src') diff --git a/contrib/cvs/src/ChangeLog b/contrib/cvs/src/ChangeLog index de2f535..96e1701 100644 --- a/contrib/cvs/src/ChangeLog +++ b/contrib/cvs/src/ChangeLog @@ -1,5 +1,490 @@ +1998-03-04 Jim Kingdon + + * entries.c, cvs.h (Entries_Open): New argument update_dir; use it + in error message. + * add.c, checkout.c, client.c, find_names.c, import.c, recurse.c, + update.c: Pass it (as NULL except in call_in_directory). + * entries.c (Subdirs_Known): Just return if there is no CVSADM + directory (as in subdir_record). + * sanity.sh (conflicts3): New tests conflicts3-20a and + conflicts3-23 test for these fixes. + + * commit.c (commit): Only set up hardlist if preserve_perms. + + * commit.c, import.c, no_diff.c, parseinfo.c, rcs.c, rcscmds.c, + update.c: Omit the preserve_perms code if + PRESERVE_PERMISSIONS_SUPPORT is not defined. Much of that code + won't even compile on non-unix systems. + + * hardlink.c, hardlink.h: Use the 'standard' copyright (as found + in server.c). + * commit.c, rcs.c: Minor whitespace changes to Tim's submission. + * commit.c (check_fileproc), update.c (get_linkinfo_proc): Remove + unused variable delta. + * hardlink.c (set_hardlink_field_proc), update.c + (get_linkinfo_proc): Return a value rather than falling off the + end of the function. + +1998-03-02 Tim Pierce + + * update.c (special_file_mismatch): Compare the hard links of the + two revisions. + + * rcs.c (RCS_checkout): + + * hardlink.c, hardlink.h: New files. + (hardlink_info): New struct. + (hardlist, working_dir): New variables. + (list_files_proc, cache_hardlinks_proc, set_hardlink_field_proc, + lookup_file_by_inode, update_hardlink_info, list_files_linked_to): + New functions. + + * Makefile.in (SOURCES): Add hardlink.c. + (OBJECTS): Add hardlink.o. + (HEADERS): Add hardlink.h. + * commit.c: Include hardlink.h. + (commit): Save the working directory before recursing. Walk the + hardlink list, calling set_hardlink_field_proc on each node. + (check_fileproc): Add each file's link information to hardlist. + * rcs.c: Include hardlink.h. + (RCS_checkin): Save list of hardlinks in delta node. + (RCS_checkout): Look up the file's `hardlinks' delta field, and + see if any of the files linked to it have been checked out + already. Link to one of those files if so. + * update.c: Include hardlink.h. + (get_linkinfo_proc): New function. + (do_update): Extra recursion to collect hardlink info. + (special_file_mismatch): Reparse the RCS file if necessary. + + fsortcmp is now used by several files, so let's make it extern. + * hash.c, hash.h (fsortcmp): New function. + * find_names.c (fsortcmp): Removed. + * lock.c (fsortcmp): Removed. + +1998-03-03 Jim Kingdon + + * sanity.sh (conflicts3): New tests conflicts3-14a, + conflicts3-14b, and conflicts3-21, conflicts3-22 test that we can + skip over a working directory with a CVSADM directory missing. + +1998-02-26 Jim Kingdon + + * sanity.sh (conflicts3): Tests conflicts3-16 and conflicts3-20 + test that we include update_dir in messages. Rename test + conflicts3-14 to fix typo. + +Sun Feb 22 23:14:25 1998 Steve Cameron + and Ian Lance Taylor + + * update.c (tag_update_dir): New static variable. + (update_dirent_proc): If no tag or date were specified when + creating a subdirectory, use the tag and/or date of the parent + directory. + (update_dirleave_proc): If we set the tag and/or date in + update_dirent_proc, reset them when we leave the directory. + * sanity.sh (branches2): New set of tests for above patch, and + related behaviour. + +Sun Feb 22 13:31:51 1998 Ian Lance Taylor + + * commit.c (lock_RCS): Don't call RCS_rewrite. + + * update.c (patch_file): If the revision is dead, let + checkout_file handle it. + * sanity.sh (death2): Add test for above patch: add + death2-10a, death2-10b, death2-13a, and adjust + death2-{2,4,5,11,14,diff-11,diff-12,19}. + + * cvs.h (RCS_FLAGS_KEEPFILE): Define. + * rcs.c (RCS_checkin): If RCS_FLAGS_KEEPFILE is set in the flags + parameter, don't unlink the working file. + * checkin.c (Checkin): Don't copy the file. Instead pass + RCS_FLAGS_KEEPFILE to RCS_checkin, and only check the file out + again if it has changed. + +1998-02-21 Jim Kingdon + + * rcs.c (rcs_internal_unlockfile, RCS_rewrite): Don't assume errno + means anything just because ferror is set. + +Sat Feb 21 20:02:24 1998 Ian Lance Taylor + + * Makefile.in (clean): Change "/bin/rm" to "rm". + + * buffer.c (buf_append_buffer): Correct typo in comment. + * rcs.c (RCS_putadmin): Likewise. + +Fri Feb 20 17:53:06 1998 Ian Lance Taylor + + * rcs.c (rcs_internal_unlockfile): Pass errno when calling error + because ferror is true. + +1998-02-20 Jim Kingdon + + * sanity.sh (abspath): Don't assume that we can't write to /; this + is the kind of thing that is sure to break sooner or later + (especially on Windows). + + * sanity.sh: Add summary of which modules tests are which (at + "modules"). Move cvsadm, abspath, and toplevel next to modules. + Add comments to clarify the structure (such as it is). + +Fri Feb 20 12:47:14 1998 Larry Jones + + * admin.c (admin_fileproc): Better fix for -b. + + * rcs.c (RCS_whatbranch): Back out previous change. + (RCS_getversion): Ditto. + (RCS_setbranch): Treat an empty revision string like a null pointer. + +1998-02-18 Jim Kingdon + + * rcs.c (RCS_whatbranch): Fix indentation. + + * patch.c (patch_fileproc): Check for errors from fclose; check + for errors from fopen properly. + +Wed Feb 18 16:03:37 1998 Larry Jones + + * admin.c (admin_fileproc): Convert -b argument from symbolic name + to revision number before storing in the RCS file. + * rcs.c (RCS_whatbranch): Allow numeric as well as symbolic revision. + (RCS_getversion): Take advantage of above. + * sanity.sh (admin): Add/revise/renumber admin-10c, admin-11a, + admin-12, and admin-12a to check above. + + * commmit.c (lock_RCS): Minor clean-up. + + * sanity.sh (abspath-6a): Don't depend on the sepcific contents of + CVSROOT, it depends on which other tests have been run. + +Wed Feb 18 01:56:04 1998 Ian Lance Taylor + + * rcs.c (putsymbol_proc): Use putc and fputs rather than fprintf. + (RCS_putadmin): Don't call RCS_symbols if the symbols have not yet + been converted to a list. + + * rcs.c (rcsbuf_cache, rcsbuf_cache_open, rcsbuf_cache_close): New + static functions to avoid closing and reopening the RCS file. + (cached_rcs, cached_rcsbuf): New static variables. + (RCS_parse): Call rcsbuf_cache_close. Don't call fclose. + (RCS_parsercsfile): Likewise. + (RCS_parsercsfile_i): Call rcsbuf_cache rather than + rcsbuf_close. Call fclose on error. Remove comment about + inefficiency of opening file twice. + (RCS_reparsercsfile): Call rcsbuf_cache_open rather than fopen and + rcsbuf_open. Call rcsbuf_cache rather than rcsbuf_close and + fclose. + (RCS_fully_parse, RCS_checkout, RCS_deltas): Likewise. + (RCS_rewrite): Likewise. + (RCS_checkin): Call rcsbuf_cache_close. + + * rcs.c (RCS_copydeltas): Fix code which checks for an extra + newline in buffered data. + + * rcs.c (rcsbuf_getkey): Save an indirection by using start rather + than *valp when trimming trailing whitespace from value. + + * rcs.c (rcsbuf_get_buffered): New static function. + (RCS_copydeltas): After we have done all the required special + actions, and inserted any new revision, just copy the file bytes + directly, rather than interpreting all the data. + (count_delta_actions): New static function. + * sanity.sh (rcs): Add rcs-6a and rcs-6b to commit a new branch + revision, to force CVS to interpret all the data, rather than just + copying it. Adjust rcs-5 to add a branch tag. Adjust rcs-8a and + rcs-14 for the changes created by rcs-6b. + +Tue Feb 17 18:34:01 1998 Ian Lance Taylor + + * sanity.sh (cvsadm, diffmerge2): Remove directories at the end of + the test. + + * import.c (expand_at_signs): Rewrite to use memchr and fwrite + rather than putc. + + Rewrite RCS file reading routines for speed: + * rcs.c (struct rcsbuffer): Define. + (rcsbuf_open, rcsbuf_close, rcsbuf_getkey, rcsbuf_getrevnum, + rcsbuf_fill, rcsbuf_valcopy, rcsbuf_valpolish, + rcsbuf_valpolish_internal, rcsbuf_ftell): New static functions. + (getrcskey, getrcsrev, getrevnum): Remove. + (many functions): Change to use new rcsbuf functions instead of + old getrcskey/getrcsrev/getrevnum functions. + (RCS_reparsercsfile): Add rcsbufp parameter. Change all callers. + (RCS_deltas): Add rcsbuf parameter. Change all callers. + (getdelta): Change fp parameter to rcsbuf parameter. Change all + callers. + (RCS_getdeltatext): Add rcsbuf parameter. Change all callers. + (RCS_copydeltas): Add rcsbufin parameter. Change all callers. + * rcs.h (RCS_reparsercsfile): Update declaration. + * admin.c (admin_fileproc): Update calls to RCS_reparsercsfile for + new parameters. + +1998-02-17 Jim Kingdon + + * sanity.sh (toplevel): Also clean up second-dir (not a new + bug, but triggered by running tests as "toplevel abspath"). + + * create_adm.c (Create_Admin): Just print update_dir to tell the + user where we are; not the whole xgetwd. Cleaner than + Noel's change (which also had problems in errno handling). + * sanity.sh (toplevel-12): Update accordingly. + +Tue Feb 17 02:32:21 1998 Noel Cragg + + [These mods make "checkout" work with "-d /absolute/pathname" + once again.] + + * checkout.c (checkout_proc): the -d flag on the command line + should override the -d flag in the modules file if the latter is + an absolute path. The loop that assembles the list of directories + to build has been reorganized slightly to prepare for rewriting + with last_component rather than assuming '/' as a path separator. + Also added to that loop was some code to handle absolute + pathnames. + (build_dirs_and_chdir): add a new argument that tells this routine + whether or not to check before it creates and populates + directories or not. + + * filesubr.c (last_component): return the top-level directory when + asked about the top-level directory. + + * sanity.sh (toplevel-12): change test to reflect the new style of + this error message. + + * create_adm.c (Create_Admin): include the directory in the error + message. + +1998-02-16 Jim Kingdon + + * diff.c (diff_fileproc), import.c (import, add_rcs_file), rcs.c + (RCS_cmp_file): Don't ignore errors from CVS_UNLINK and fclose. + + * patch.c (patch_fileproc): Check for errors from fclose; if we + get -1 from getline check for end of file vs. error. + + * rcs.c (RCS_checkout): Comment return value (0/1, not -1). + * commit.c, diff.c, mkmodules.c, patch.c, rcs.c, update.c: Update + to match this convention. Don't suppress errors based on + quiet or really_quiet variables. + + Fix a longstanding bug which also makes stamps-8kw in make + remotecheck work again (it stopped working with Ian's 8 Feb 98 + checkin): + * client.c, client.h (change_mode): If new argument respect_umask + is set, then honor the umask. + * client.c, server.c: Update callers. + + Cleanups to Tim's checkin: + * rcs.c (RCS_checkout): Use existence_error not ENOENT. + * commit.c (checkaddfile): Remove comment about whether we want to + check for errors from fclose; there is no reason not to. + * rcs.c (RCS_checkout), update.c (special_file_mismatch): sscanf + on %ld requires an unsigned long, not a dev_t. + * update.c (special_file_mismatch): Remove unused variable + check_devnums. + * mkmodules.c (config_contents): Between two settings, use a blank + line not a "#" line. + +1998-02-15 Tim Pierce + + [This is the code as submitted. I'll be checking in my cleanups + shortly. This work sponsored by Abbott Labs. -kingdon] + + Support for device special files, symbolic links, user and group + ownerships, and file permissions. + + * parseinfo.c: (parse_config): Handle new config variable + `PreservePermissions'. + * mkmodules.c (config_contents): Add new PreservePermissions var. + + * rcs.c, rcs.h (preserve_perms): New variable. + (RCS_checkout, RCS_checkin): Support for newphrases `owner', + `group', `permissions', `special', `symlink'. + (RCS_checkout): If `workfile' and `sout' are symlinks, remove them + before attempting to open them for writing. + * import.c (add_rcs_file): Support for newphrases. Do not attempt + to read data from special files or symlinks. Error message + `cannot fstat' is now `cannot lstat'. + + New metrics for deciding when two files are different: + + * update.c, cvs.h (special_file_mismatch): New function. + (merge_file, join_file): Call it. + * no_diff.c (No_Difference): Call it. + + * filesubr.c (xcmp): Consider files to be different if they are of + different types; if they are symlinks which link to different + pathnames; or if they are devices with different device numbers. + Error message is now `cannot lstat'. + * rcs.c (RCS_cmp_file): Use `xcmp' to compare files, simplifying + the special handling for nonregular files. + + * rcscmds.c (diff_exec, diff_execv): If asked to obtain diffs for + special files, report no differences. + + Miscellaneous changes to make special file support possible: + + * commit.c (fix_rcs_modes): Don't attempt to `fix' permissions on + a symlink. + + * import.c (add_rcs_file): Don't try to close fpuser if it was + never opened (e.g. when operating on a symlink). + + * filesubr.c, cvs.h (isdevice, xreadlink): New functions. + * filesubr.c (copy_file): Handle special files and symlinks. + (xchmod): Do nothing if `preserve_perms' is set. + + * commit.c (checkaddfile): Replace `copy_file (DEVNULL, ...)' with + fopen/fclose calls. Copy_file no longer attempts to read data + from device files. + + * filesubr.c (islink): Use CVS_LSTAT, not lstat. + * vers_ts.c (time_stamp, time_stamp_server): Use CVS_LSTAT, not stat, + to get symlinks right. + * subr.c (get_file): Same. Don't attempt to read from special + files or symlinks. + + * classify.c (Classify_File): Doc fix. + +Fri Feb 13 17:07:32 1998 Eric Mumpower + and Ian Lance Taylor + + Fix some file system ordering problems found on Irix 6.4: + * sanity.sh (basic2): Use dotest_sort for test 56. + (importb): Use dotest_sort for tests importb-1 and importb-2. + (head): Use dotest_sort for test head-1. + +Thu Feb 12 15:15:33 1998 Jim Kingdon + + * import.c (add_rcs_file): If add_logfp is NULL, don't call fperror. + +11 Feb 1998 Andy Piper + + * server.c (cvs_output_binary): Use OPEN_BINARY not _O_BINARY. + +Mon Feb 9 18:34:39 1998 Jim Kingdon + + Tweaks to Ian's checkin: + * update.c (merge_file): Remove comment about sending file to + client before the message. It doesn't apply to this code any more + (it does apply to checkout_file, but I'm not sure it is important + to have such a comment anyway). + * buffer.c (buf_default_memory_error, buf_length): Reindent. + * server.h: Declare struct buffer before use. + +Mon Feb 9 21:05:28 1998 Ian Lance Taylor + + * rcs.c (RCS_fully_parse): Call getrevnum rather than getrcsrev. + Don't bother with ungetc. + + * rcs.c (getrcsrev): Rewrite to simply call getrevnum. + +Sun Feb 8 15:49:39 1998 Ian Lance Taylor + + Don't have the server check out a revision into a file and then + immediately read the file; just read into a buffer instead. + * update.c: Include buffer.h. + (update_fileproc): Let checkout_file call server_updated. + (checkout_file): Add merging and update_server parameters. Change + all callers. If server_active, don't mess with backup files. If + server_active, copy the revision into a buffer rather than a file + when possible. If update_server, call server_updated. Fix + handling of error status. + (checkout_to_buffer): New static function used by checkout_file. + (merge_file): Let checkout_file call server_updated. + (join_file): Likewise. + * server.c (server_updated): Change file_info parameter to mode + parameter. Add filebuf parameter. Change all callers. If + filebuf is not NULL, don't read the file. + * server.h (server_updated): Update declaration. + * buffer.c (buf_free): New function. + (buf_append_buffer): New function. + (buf_length): New function. + * buffer.h (buf_free, buf_append_buffer, buf_length): Declare. + + * buffer.c: (buf_initialize): If the memory parameter is NULL, use + buf_default_memory_error. + (buf_default_memory_error): New static function. + * buffer.h (BUFMEMERRPROC): Define typedef. + * client.c (buf_memory_error): Remove. + (start_server): Pass NULL rather than buf_memory_error as buffer + memory error function. + +Sat Feb 7 16:27:30 1998 Ian Lance Taylor + + * rcs.c (RCS_parsercsfile_i): Read the expand keyword from the RCS + file. We do this because Version_TS calls RCS_getexpand in many + common cases, and we don't want to reopen the file just for that. + (RCS_reparsercsfile): Skip the expand keyword. + (RCS_getexpand): Don't call RCS_reparsercsfile. + + * rcs.c (STREQ): New macro. In all string equality tests in the + file, replace strcmp with STREQ. + +Fri Feb 6 16:14:49 1998 Ian Lance Taylor + + * update.c (checkout_file): If we've already removed the backup + file once, don't try to remove it again. + + * filesubr.c (unlink_file_dir): Call stat rather than isdir, and + don't call unlink if the file does not exist. + + * myndbm.c (mydbm_load_file): Rename line_len to line_size. Call + getstr rather than getline, to avoid any confusion between \n and + \012. Use the line length returned by getstr rather than calling + strlen. Remove local variable len. + +Fri Feb 6 13:23:46 1998 Jim Kingdon + + * rcs.c (RCS_parsercsfile_i): Don't suppress errors on + really_quiet. + (RCS_parsercsfile_i, RCS_reparsercsfile, RCS_fully_parse, + RCS_deltas, getdelta, getrcskey, RCS_getdeltatext): + Check for errors. Include errno in error messages. Include + filename in error messages. Pass new argument to getrcskey. + (getrcskey): New argument NAME, so we can report errors ourself. + +Fri Feb 6 12:10:18 1998 Ian Lance Taylor + + * rcs.c (RCS_reparsercsfile): Don't use ftell/fseek; just keep + track of whether we've already read a key/value pair. Use sizeof + rather than strlen for a constant string. Pass the current key + and value to getdelta, and get them back as well. + (getdelta): Add keyp and valp parameters. Don't use ftell/fseek; + just return the key/value pair to the caller. Don't allocate + vnode before we know we need it. Check one getrcskey return + value. Use sizeof rather than strlen for a constant string. + + * rcs.c (getrcskey): Correct comment describing return value. + +Thu Feb 5 22:51:13 1998 Ian Lance Taylor + + * subr.c (getcaller): Cache the result, so that we don't keep + searching the password file. + +Wed Feb 4 23:31:08 1998 Jim Kingdon + + * rcs.c (max_rev): Don't prototype. Interesting that noone + complained about this until now. + +4 Feb 1998 Jim Kingdon + + * rcs.c (RCS_checkin): When adding a new file, read it + with "rb" if binary. + +Fri Jan 30 11:32:41 1998 Jim Kingdon + + * sanity.sh: Also test "first-dir" as the regexp in loginfo in + addition to ALL. + + * main.c (main): Update year in copyright notice to 1998. + Thu Jan 29 00:01:05 1998 Jim Kingdon + * version.c: Change version number to 1.9.25. + * Version 1.9.24. * sanity.sh (multibranch2): File file2 and tests multibranch2-13 diff --git a/contrib/cvs/src/Makefile.in b/contrib/cvs/src/Makefile.in index 296554a..61b4fe2 100644 --- a/contrib/cvs/src/Makefile.in +++ b/contrib/cvs/src/Makefile.in @@ -39,7 +39,7 @@ LIBS = @LIBS@ SOURCES = add.c admin.c buffer.c checkin.c checkout.c classify.c client.c \ commit.c create_adm.c cvsrc.c diff.c edit.c entries.c error.c expand_path.c \ -fileattr.c find_names.c hash.c history.c ignore.c import.c \ +fileattr.c find_names.c hardlink.c hash.c history.c ignore.c import.c \ lock.c log.c login.c logmsg.c main.c mkmodules.c modules.c myndbm.c no_diff.c \ parseinfo.c patch.c rcs.c rcscmds.c recurse.c release.c remove.c repos.c \ root.c rtag.c scramble.c server.c status.c subr.c filesubr.c run.c \ @@ -47,14 +47,14 @@ tag.c update.c watch.c wrapper.c vers_ts.c version.c zlib.c OBJECTS = add.o admin.o buffer.o checkin.o checkout.o classify.o client.o \ commit.o create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \ -fileattr.o find_names.o hash.o history.o ignore.o import.o \ +fileattr.o find_names.o hardlink.o hash.o history.o ignore.o import.o \ lock.o log.o login.o logmsg.o main.o mkmodules.o modules.o myndbm.o no_diff.o \ parseinfo.o patch.o rcs.o rcscmds.o recurse.o release.o remove.o repos.o \ root.o rtag.o scramble.o server.o status.o tag.o update.o \ watch.o wrapper.o vers_ts.o \ subr.o filesubr.o run.o version.o error.o zlib.o -HEADERS = buffer.h cvs.h rcs.h hash.h myndbm.h \ +HEADERS = buffer.h cvs.h rcs.h hardlink.h hash.h myndbm.h \ update.h server.h client.h error.h fileattr.h edit.h watch.h TAGFILES = $(HEADERS) options.h.in $(SOURCES) @@ -125,7 +125,7 @@ ls: .PHONY: ls clean: - /bin/rm -f $(PROGS) *.o core check.log check.plog + rm -f $(PROGS) *.o core check.log check.plog .PHONY: clean distclean: clean diff --git a/contrib/cvs/src/add.c b/contrib/cvs/src/add.c index ab667df..d44435a 100644 --- a/contrib/cvs/src/add.c +++ b/contrib/cvs/src/add.c @@ -264,7 +264,7 @@ add (argc, argv) /* Find the repository associated with our current dir. */ repository = Name_Repository (NULL, finfo.update_dir); - entries = Entries_Open (0); + entries = Entries_Open (0, NULL); finfo.repository = repository; finfo.entries = entries; diff --git a/contrib/cvs/src/admin.c b/contrib/cvs/src/admin.c index a78bada..3c50f4a 100644 --- a/contrib/cvs/src/admin.c +++ b/contrib/cvs/src/admin.c @@ -478,7 +478,7 @@ admin_fileproc (callerdat, finfo) rcs = vers->srcfile; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); status = 0; @@ -490,9 +490,23 @@ admin_fileproc (callerdat, finfo) } if (admin_data->branch != NULL) - RCS_setbranch (rcs, (admin_data->branch[2] == '\0' - ? NULL - : admin_data->branch + 2)); + { + char *branch = &admin_data->branch[2]; + if (*branch != '\0' && ! isdigit (*branch)) + { + branch = RCS_whatbranch (rcs, admin_data->branch + 2); + if (branch == NULL) + { + error (0, 0, "%s: Symbolic name %s is undefined.", + rcs->path, admin_data->branch + 2); + status = 1; + } + } + if (status == 0) + RCS_setbranch (rcs, branch); + if (branch != NULL && branch != &admin_data->branch[2]) + free (branch); + } if (admin_data->comment != NULL) { if (rcs->comment != NULL) @@ -793,7 +807,7 @@ admin_fileproc (callerdat, finfo) RCS data structure. Forcing a reparse does the trick, but leaks memory and is kludgey. Should we export free_rcsnode_contents for this purpose? */ - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); } exitfunc: diff --git a/contrib/cvs/src/buffer.c b/contrib/cvs/src/buffer.c index 0042e4c..8f90545 100644 --- a/contrib/cvs/src/buffer.c +++ b/contrib/cvs/src/buffer.c @@ -16,6 +16,7 @@ static struct buffer_data *free_buffer_data; /* Local functions. */ +static void buf_default_memory_error PROTO ((struct buffer *)); static void allocate_buffer_datas PROTO((void)); static struct buffer_data *get_buffer_data PROTO((void)); @@ -42,11 +43,25 @@ buf_initialize (input, output, flush, block, shutdown, memory, closure) buf->flush = flush; buf->block = block; buf->shutdown = shutdown; - buf->memory_error = memory; + buf->memory_error = memory ? memory : buf_default_memory_error; buf->closure = closure; return buf; } +/* Free a buffer structure. */ + +void +buf_free (buf) + struct buffer *buf; +{ + if (buf->data != NULL) + { + buf->last->next = free_buffer_data; + free_buffer_data = buf->data; + } + free (buf); +} + /* Initialize a buffer structure which is not to be used for I/O. */ struct buffer * @@ -63,6 +78,15 @@ buf_nonio_initialize (memory) (void *) NULL)); } +/* Default memory error handler. */ + +static void +buf_default_memory_error (buf) + struct buffer *buf; +{ + error (1, 0, "out of memory"); +} + /* Allocate more buffer_data structures. */ static void @@ -470,6 +494,19 @@ buf_append_data (buf, data, last) } } +/* Append the data on one buffer to another. This removes the data + from the source buffer. */ + +void +buf_append_buffer (to, from) + struct buffer *to; + struct buffer *from; +{ + buf_append_data (to, from->data, from->last); + from->data = NULL; + from->last = NULL; +} + /* * Copy the contents of file F into buffer_data structures. We can't * copy directly into an buffer, because we want to handle failure and @@ -621,6 +658,15 @@ buf_chain_length (buf) return size; } +/* Return the number of bytes in a buffer. */ + +int +buf_length (buf) + struct buffer *buf; +{ + return buf_chain_length (buf->data); +} + /* * Read an arbitrary amount of data into an input buffer. The buffer * will be in nonblocking mode, and we just grab what we can. Return diff --git a/contrib/cvs/src/buffer.h b/contrib/cvs/src/buffer.h index c632490..0556781 100644 --- a/contrib/cvs/src/buffer.h +++ b/contrib/cvs/src/buffer.h @@ -96,6 +96,9 @@ struct buffer_data /* The size we allocate for each buffer_data structure. */ #define BUFFER_DATA_SIZE (4096) +/* The type of a function passed as a memory error handler. */ +typedef void (*BUFMEMERRPROC) PROTO ((struct buffer *)); + extern struct buffer *buf_initialize PROTO((int (*) (void *, char *, int, int, int *), int (*) (void *, const char *, @@ -105,6 +108,7 @@ extern struct buffer *buf_initialize PROTO((int (*) (void *, char *, int, int (*) (void *), void (*) (struct buffer *), void *)); +extern void buf_free PROTO((struct buffer *)); extern struct buffer *buf_nonio_initialize PROTO((void (*) (struct buffer *))); extern struct buffer *stdio_buffer_initialize PROTO((FILE *, int, void (*) (struct buffer *))); @@ -127,6 +131,7 @@ extern int buf_send_special_count PROTO((struct buffer *, int)); extern void buf_append_data PROTO((struct buffer *, struct buffer_data *, struct buffer_data *)); +extern void buf_append_buffer PROTO((struct buffer *, struct buffer *)); extern int buf_read_file PROTO((FILE *, long, struct buffer_data **, struct buffer_data **)); extern int buf_read_file_to_eof PROTO((FILE *, struct buffer_data **, @@ -137,6 +142,7 @@ extern int buf_read_data PROTO((struct buffer *, int, char **, int *)); extern void buf_copy_lines PROTO((struct buffer *, struct buffer *, int)); extern int buf_copy_counted PROTO((struct buffer *, struct buffer *, int *)); extern int buf_chain_length PROTO((struct buffer_data *)); +extern int buf_length PROTO((struct buffer *)); extern int buf_shutdown PROTO((struct buffer *)); #ifdef SERVER_FLOWCONTROL diff --git a/contrib/cvs/src/checkin.c b/contrib/cvs/src/checkin.c index 1d89ae1..70fb74c 100644 --- a/contrib/cvs/src/checkin.c +++ b/contrib/cvs/src/checkin.c @@ -29,7 +29,6 @@ Checkin (type, finfo, rcs, rev, tag, options, message) char *options; char *message; { - char *fname; Vers_TS *vers; int set_time; char *tocvsPath = NULL; @@ -42,69 +41,63 @@ Checkin (type, finfo, rcs, rev, tag, options, message) cvs_output (finfo->fullname, 0); cvs_output (";\n", 0); - fname = xmalloc (strlen (finfo->file) + 80); - (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); - - /* - * Move the user file to a backup file, so as to preserve its - * modification times, then place a copy back in the original file name - * for the checkin and checkout. - */ - tocvsPath = wrap_tocvs_process_file (finfo->file); - if (!noexec) { if (tocvsPath) { - copy_file (tocvsPath, fname); if (unlink_file_dir (finfo->file) < 0) if (! existence_error (errno)) error (1, errno, "cannot remove %s", finfo->fullname); - copy_file (tocvsPath, finfo->file); - } - else - { - copy_file (finfo->file, fname); + rename_file (tocvsPath, finfo->file); } } if (finfo->rcs == NULL) finfo->rcs = RCS_parse (finfo->file, finfo->repository); - switch (RCS_checkin (finfo->rcs, NULL, message, rev, 0)) + switch (RCS_checkin (finfo->rcs, NULL, message, rev, RCS_FLAGS_KEEPFILE)) { case 0: /* everything normal */ - /* - * The checkin succeeded, so now check the new file back out and - * see if it matches exactly with the one we checked in. If it - * does, just move the original user file back, thus preserving - * the modes; otherwise, we have no recourse but to leave the - * newly checkout file as the user file and remove the old - * original user file. - */ + /* The checkin succeeded. If checking the file out again + would not cause any changes, we are done. Otherwise, + we need to check out the file, which will change the + modification time of the file. + + The only way checking out the file could cause any + changes is if the file contains RCS keywords. So we if + we are not expanding RCS keywords, we are done. */ if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */ options[0] = '\0'; - /* FIXME: should be checking for errors. */ - (void) RCS_checkout (finfo->rcs, finfo->file, rev, - (char *) NULL, options, RUN_TTY, - (RCSCHECKOUTPROC) NULL, (void *) NULL); - - xchmod (finfo->file, 1); - if (xcmp (finfo->file, fname) == 0) + /* FIXME: If PreservePermissions is on, RCS_cmp_file is + going to call RCS_checkout into a temporary file + anyhow. In that case, it would be more efficient to + call RCS_checkout here, compare the resulting files + using xcmp, and rename if necessary. I think this + should be fixed in RCS_cmp_file. */ + if ((! preserve_perms + && options != NULL + && (strcmp (options, "-ko") == 0 + || strcmp (options, "-kb") == 0)) + || RCS_cmp_file (finfo->rcs, rev, options, finfo->file) == 0) { - rename_file (fname, finfo->file); - /* the time was correct, so leave it alone */ + /* The existing file is correct. We don't have to do + anything. */ set_time = 0; } else { - if (unlink_file (fname) < 0) - error (0, errno, "cannot remove %s", fname); - /* sync up with the time from the RCS file */ + /* The existing file is incorrect. We need to check + out the correct file contents. */ + if (RCS_checkout (finfo->rcs, finfo->file, rev, (char *) NULL, + options, RUN_TTY, (RCSCHECKOUTPROC) NULL, + (void *) NULL) != 0) + error (1, 0, "failed when checking out new copy of %s", + finfo->fullname); + xchmod (finfo->file, 1); set_time = 1; } @@ -140,25 +133,19 @@ Checkin (type, finfo, rcs, rev, tag, options, message) if (!noexec) error (1, errno, "could not check in %s -- fork failed", finfo->fullname); - free (fname); return (1); default: /* ci failed */ - /* - * The checkin failed, for some unknown reason, so we restore the - * original user file, print an error, and return an error - */ + /* The checkin failed, for some unknown reason, so we + print an error, and return an error. We assume that + the original file has not been touched. */ if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); if (!noexec) - { - rename_file (fname, finfo->file); error (0, 0, "could not check in %s", finfo->fullname); - } - free (fname); return (1); } @@ -179,7 +166,8 @@ Checkin (type, finfo, rcs, rev, tag, options, message) if (set_time) /* Need to update the checked out file on the client side. */ server_updated (finfo, vers, SERVER_UPDATED, - NULL, NULL); + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); else server_checked_in (finfo->file, finfo->update_dir, finfo->repository); } @@ -188,6 +176,5 @@ Checkin (type, finfo, rcs, rev, tag, options, message) mark_up_to_date (finfo->file); freevers_ts (&vers); - free (fname); return (0); } diff --git a/contrib/cvs/src/checkout.c b/contrib/cvs/src/checkout.c index 3c1eef7..46151eb 100644 --- a/contrib/cvs/src/checkout.c +++ b/contrib/cvs/src/checkout.c @@ -33,6 +33,7 @@ * edited by the user, if necessary (when the repository is moved, e.g.) */ +#include #include "cvs.h" static char *findslash PROTO((char *start, char *p)); @@ -431,7 +432,7 @@ struct dir_to_build }; static int build_dirs_and_chdir PROTO ((struct dir_to_build *list, - int sticky)); + int sticky, int check_existing_dirs)); static void build_one_dir PROTO ((char *, char *, int)); @@ -580,7 +581,11 @@ checkout_proc (pargc, argv, where_orig, mwhere, mfile, shorten, (void) strcat (where, "/"); } - if (mwhere != NULL) + /* If the -d flag in the modules file specified an absolute + directory, let the user override it with the command-line + -d option. */ + + if ((mwhere != NULL) && (! isabsolute (mwhere))) (void) strcat (where, mwhere); else (void) strcat (where, argv[0]); @@ -723,19 +728,39 @@ internal error: %s doesn't start with %s in checkout_proc", /* Make a copy of the repository name to play with. */ reposcopy = xstrdup (repository); - /* FIXME: this should be written in terms of last_component instead - of hardcoding '/'. This presumably affects OS/2, NT, &c, if - the user specifies '\'. Likewise for the call to findslash. */ - cp = strrchr (where, '/'); - while (cp != NULL) + /* FIXME: this should be written in terms of last_component + instead of hardcoding '/'. This presumably affects OS/2, + NT, &c, if the user specifies '\'. Likewise for the call + to findslash. */ + cp = where + strlen (where); + while (1) { struct dir_to_build *new; + + cp = findslash (where, cp - 1); + if (cp == NULL) + break; /* we're done */ + new = (struct dir_to_build *) xmalloc (sizeof (struct dir_to_build)); new->dirpath = xmalloc (strlen (where)); - strncpy (new->dirpath, where, cp - where); - new->dirpath[cp - where] = '\0'; + /* If the user specified an absolute path for where, the + last path element we create should be the top-level + directory. */ + + if (cp - where) + { + strncpy (new->dirpath, where, cp - where); + new->dirpath[cp - where] = '\0'; + } + else + { + /* where should always be at least one character long. */ + assert (strlen (where)); + strcpy (new->dirpath, "/"); + } + /* Now figure out what repository directory to generate. The most complete case would be something like this: @@ -813,31 +838,41 @@ internal error: %s doesn't start with %s in checkout_proc", new->next = head; head = new; - - cp = findslash (where, cp - 1); } /* clean up */ free (reposcopy); - /* The top-level CVSADM directory should always be - CVSroot_directory. Create it. - - It may be argued that we shouldn't set any sticky bits for - the top-level repository. FIXME? */ - build_one_dir (CVSroot_directory, ".", *pargc <= 1); - - /* - * build dirs on the path if necessary and leave us in the bottom - * directory (where if where was specified) doesn't contain a CVS - * subdir yet, but all the others contain CVS and Entries.Static - * files - */ - if (build_dirs_and_chdir (head, *pargc <= 1) != 0) { - error (0, 0, "ignoring module %s", omodule); - err = 1; - goto out; + int where_is_absolute = isabsolute (where); + + /* The top-level CVSADM directory should always be + CVSroot_directory. Create it, but only if WHERE is + relative. If WHERE is absolute, our current directory + may not have a thing to do with where the sources are + being checked out. If it does, build_dirs_and_chdir + will take care of creating adm files here. */ + + if (! where_is_absolute) + { + /* It may be argued that we shouldn't set any sticky + bits for the top-level repository. FIXME? */ + build_one_dir (CVSroot_directory, ".", *pargc <= 1); + } + + + /* Build dirs on the path if necessary and leave us in the + bottom directory (where if where was specified) doesn't + contain a CVS subdir yet, but all the others contain + CVS and Entries.Static files */ + + if (build_dirs_and_chdir (head, *pargc <= 1, + where_is_absolute) != 0) + { + error (0, 0, "ignoring module %s", omodule); + err = 1; + goto out; + } } /* set up the repository (or make sure the old one matches) */ @@ -969,7 +1004,7 @@ internal error: %s doesn't start with %s in checkout_proc", List *entries; /* we are only doing files, so register them */ - entries = Entries_Open (0); + entries = Entries_Open (0, NULL); for (i = 1; i < *pargc; i++) { char *line; @@ -1066,13 +1101,22 @@ emptydir_name () return repository; } -/* Build all the dirs along the path to DIRS with CVS subdirs with appropriate - repositories. If ->repository is NULL, do not create a CVSADM directory - for that subdirectory; just CVS_CHDIR into it. */ + +/* Build all the dirs along the path to DIRS with CVS subdirs with + appropriate repositories. If ->repository is NULL, do not create a + CVSADM directory for that subdirectory; just CVS_CHDIR into it. If + check_existing_dirs is nonzero, don't create directories if they + already exist, and don't try to write adm files in directories + where we don't have write permission. We use this last option + primarily when a user has specified an absolute path for checkout + -- we will often not have permission to top-level directories, so + we shouldn't complain. */ + static int -build_dirs_and_chdir (dirs, sticky) +build_dirs_and_chdir (dirs, sticky, check_existing_dirs) struct dir_to_build *dirs; int sticky; + int check_existing_dirs; { int retval = 0; struct dir_to_build *nextdir; @@ -1080,20 +1124,31 @@ build_dirs_and_chdir (dirs, sticky) while (dirs != NULL) { char *dir = last_component (dirs->dirpath); + int dir_is_writeable; + + if ((! check_existing_dirs) || (! isdir (dir))) + mkdir_if_needed (dir); - mkdir_if_needed (dir); Subdir_Register (NULL, NULL, dir); + + /* This is an expensive call -- only make it if necessary. */ + if (check_existing_dirs) + dir_is_writeable = iswritable (dir); + if (CVS_CHDIR (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); retval = 1; goto out; } - if (dirs->repository != NULL) + + if ((dirs->repository != NULL) + && ((! check_existing_dirs) || dir_is_writeable)) { build_one_dir (dirs->repository, dirs->dirpath, sticky); free (dirs->repository); } + nextdir = dirs->next; free (dirs->dirpath); free (dirs); diff --git a/contrib/cvs/src/classify.c b/contrib/cvs/src/classify.c index 57c23cd..b33c945 100644 --- a/contrib/cvs/src/classify.c +++ b/contrib/cvs/src/classify.c @@ -302,6 +302,8 @@ conflict: %s created independently by second party", * has changed. If the sticky tag has changed, we just need * to re-register the entry */ + /* TODO: decide whether we need to check file permissions + for a mismatch, and return T_CONFLICT if so. */ if (vers->entdata->options && strcmp (vers->entdata->options, vers->options) != 0) ret = T_CHECKOUT; @@ -377,7 +379,7 @@ conflict: %s created independently by second party", * The user file is still unmodified, so just get it as well */ #ifdef SERVER_SUPPORT - if (strcmp (vers->entdata->options ? + if (strcmp (vers->entdata->options ? vers->entdata->options : "", vers->options) != 0 || (vers->srcfile != NULL && (vers->srcfile->flags & INATTIC) != 0)) diff --git a/contrib/cvs/src/client.c b/contrib/cvs/src/client.c index ca6f464..aa897d5 100644 --- a/contrib/cvs/src/client.c +++ b/contrib/cvs/src/client.c @@ -134,7 +134,6 @@ static void handle_e PROTO((char *, int)); static void handle_f PROTO((char *, int)); static void handle_notified PROTO((char *, int)); -static void buf_memory_error PROTO((struct buffer *)); static size_t try_read_from_server PROTO ((char *, size_t)); #endif /* CLIENT_SUPPORT */ @@ -182,11 +181,13 @@ mode_to_string (mode) /* * Change mode of FILENAME to MODE_STRING. * Returns 0 for success or errno code. + * If RESPECT_UMASK is set, then honor the umask. */ int -change_mode (filename, mode_string) +change_mode (filename, mode_string, respect_umask) char *filename; char *mode_string; + int respect_umask; { #ifdef CHMOD_BROKEN char *p; @@ -217,6 +218,10 @@ change_mode (filename, mode_string) ++p; } + /* xchmod honors the umask for us. In the !respect_umask case, we + don't try to cope with it (probably to handle that well, the server + needs to deal with modes in data structures, rather than via the + modes in temporary files). */ xchmod (filename, writeable); return 0; @@ -224,6 +229,7 @@ change_mode (filename, mode_string) char *p; mode_t mode = 0; + mode_t oumask; p = mode_string; while (*p != '\0') @@ -277,6 +283,13 @@ change_mode (filename, mode_string) ++p; } + if (respect_umask) + { + oumask = umask (0); + (void) umask (oumask); + mode &= ~oumask; + } + if (chmod (filename, mode) < 0) return errno; return 0; @@ -305,16 +318,6 @@ static FILE *from_server_fp; static int rsh_pid = -1; -/* This routine is called when one of the buffer routines runs out of - memory. */ - -static void -buf_memory_error (buf) - struct buffer *buf; -{ - error (1, 0, "out of memory"); -} - /* We want to be able to log data sent between us and the server. We do it using log buffers. Each log buffer has another buffer which handles the actual I/O, and a file to log information to. @@ -1138,7 +1141,7 @@ warning: server is not creating directories one at a time"); if (strcmp (command_name, "export") != 0) { - last_entries = Entries_Open (0); + last_entries = Entries_Open (0, dir_name); /* If this is a newly created directory, we will record all subdirectory information, so call Subdirs_Known in @@ -1928,8 +1931,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) } { - /* FIXME: we should be respecting the umask. */ - int status = change_mode (filename, mode_string); + int status = change_mode (filename, mode_string, 1); if (status != 0) error (0, status, "cannot change mode of %s", short_pathname); } @@ -1939,7 +1941,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) } if (stored_mode_valid) - change_mode (filename, stored_mode); + change_mode (filename, stored_mode, 1); stored_mode_valid = 0; if (stored_modtime_valid) @@ -4012,9 +4014,9 @@ the :server: access method is not supported by this port of CVS"); if (use_socket_style) { to_server = socket_buffer_initialize (server_sock, 0, - buf_memory_error); + (BUFMEMERRPROC) NULL); from_server = socket_buffer_initialize (server_sock, 1, - buf_memory_error); + (BUFMEMERRPROC) NULL); } else #endif /* NO_SOCKET_TO_FD */ @@ -4038,13 +4040,13 @@ the :server: access method is not supported by this port of CVS"); if (to_server_fp == NULL) error (1, errno, "cannot fdopen %d for write", tofd); to_server = stdio_buffer_initialize (to_server_fp, 0, - buf_memory_error); + (BUFMEMERRPROC) NULL); from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ); if (from_server_fp == NULL) error (1, errno, "cannot fdopen %d for read", fromfd); from_server = stdio_buffer_initialize (from_server_fp, 1, - buf_memory_error); + (BUFMEMERRPROC) NULL); } /* Set up logfiles, if any. */ @@ -4071,7 +4073,7 @@ the :server: access method is not supported by this port of CVS"); error (0, errno, "opening to-server logfile %s", buf); else to_server = log_buffer_initialize (to_server, fp, 0, - buf_memory_error); + (BUFMEMERRPROC) NULL); strcpy (p, ".out"); fp = open_file (buf, "wb"); @@ -4079,7 +4081,7 @@ the :server: access method is not supported by this port of CVS"); error (0, errno, "opening from-server logfile %s", buf); else from_server = log_buffer_initialize (from_server, fp, 1, - buf_memory_error); + (BUFMEMERRPROC) NULL); free (buf); } @@ -4240,10 +4242,10 @@ the :server: access method is not supported by this port of CVS"); send_to_server ("Kerberos-encrypt\012", 0); to_server = krb_encrypt_buffer_initialize (to_server, 0, sched, kblock, - buf_memory_error); + (BUFMEMERRPROC) NULL); from_server = krb_encrypt_buffer_initialize (from_server, 1, sched, kblock, - buf_memory_error); + (BUFMEMERRPROC) NULL); } else #endif /* HAVE_KERBEROS */ @@ -4255,10 +4257,12 @@ the :server: access method is not supported by this port of CVS"); send_to_server ("Gssapi-encrypt\012", 0); to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0, gcontext, - buf_memory_error); + ((BUFMEMERRPROC) + NULL)); from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1, gcontext, - buf_memory_error); + ((BUFMEMERRPROC) + NULL)); cvs_gssapi_encrypt = 1; } else @@ -4283,10 +4287,10 @@ the :server: access method is not supported by this port of CVS"); compressed. */ to_server = compress_buffer_initialize (to_server, 0, gzip_level, - buf_memory_error); + (BUFMEMERRPROC) NULL); from_server = compress_buffer_initialize (from_server, 1, gzip_level, - buf_memory_error); + (BUFMEMERRPROC) NULL); } #ifndef NO_CLIENT_GZIP_PROCESS else if (supported_request ("gzip-file-contents")) @@ -4327,10 +4331,12 @@ the :server: access method is not supported by this port of CVS"); send_to_server ("Gssapi-authenticate\012", 0); to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0, gcontext, - buf_memory_error); + ((BUFMEMERRPROC) + NULL)); from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1, gcontext, - buf_memory_error); + ((BUFMEMERRPROC) + NULL)); } else error (1, 0, "Stream authentication is only supported when using GSSAPI"); @@ -5093,7 +5099,7 @@ send_file_names (argc, argv, flags) command line, not the case of the directory in the filesystem. This is correct behavior. */ - entries = Entries_Open (0); + entries = Entries_Open (0, NULL); node = findnode_fn (entries, p); if (node != NULL) { diff --git a/contrib/cvs/src/client.h b/contrib/cvs/src/client.h index 119168c..996dc63 100644 --- a/contrib/cvs/src/client.h +++ b/contrib/cvs/src/client.h @@ -2,7 +2,7 @@ /* Stuff shared with the server. */ extern char *mode_to_string PROTO((mode_t)); -extern int change_mode PROTO((char *, char *)); +extern int change_mode PROTO((char *, char *, int)); extern int gzip_level; extern int file_gzip_level; diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index a7fec5f..4aa2438 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -19,6 +19,7 @@ #include "getline.h" #include "edit.h" #include "fileattr.h" +#include "hardlink.h" static Dtype check_direntproc PROTO ((void *callerdat, char *dir, char *repos, char *update_dir, @@ -81,7 +82,6 @@ static List *mulist; static char *message; static time_t last_register_time; - static const char *const commit_usage[] = { "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n", @@ -622,10 +622,23 @@ commit (argc, argv) lock_tree_for_write (argc, argv, local, aflag); /* - * Set up the master update list + * Set up the master update list and hard link list */ mulist = getlist (); +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + hardlist = getlist (); + + /* + * We need to save the working directory so that + * check_fileproc can construct a full pathname for each file. + */ + working_dir = xgetwd(); + } +#endif + /* * Run the recursion processor to verify the files are all up-to-date */ @@ -638,6 +651,17 @@ commit (argc, argv) error (1, 0, "correct above errors first!"); } +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + /* hardlist now includes a complete index of the files + to be committed, indexed by inode. For each inode, + compile a list of the files that are linked to it, + and save this list in each file's hardlink_info node. */ + (void) walklist (hardlist, cache_hardlinks_proc, NULL); + } +#endif + /* * Run the recursion processor to commit the files */ @@ -992,6 +1016,43 @@ warning: file `%s' seems to still contain conflict indicators", ci->options = xstrdup(vers->options); p->data = (char *) ci; (void) addnode (cilist, p); + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + /* Add this file to hardlist, indexed on its inode. When + we are done, we can find out what files are hardlinked + to a given file by looking up its inode in hardlist. */ + char *fullpath; + Node *linkp; + struct hardlink_info *hlinfo; + + /* Get the full pathname of the current file. */ + fullpath = xmalloc (strlen(working_dir) + + strlen(finfo->fullname) + 2); + sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); + + /* To permit following links in subdirectories, files + are keyed on finfo->fullname, not on finfo->name. */ + linkp = lookup_file_by_inode (fullpath); + + /* If linkp is NULL, the file doesn't exist... maybe + we're doing a remove operation? */ + if (linkp != NULL) + { + /* Create a new hardlink_info node, which will record + the current file's status and the links listed in its + `hardlinks' delta field. We will append this + hardlink_info node to the appropriate hardlist entry. */ + hlinfo = (struct hardlink_info *) + xmalloc (sizeof (struct hardlink_info)); + hlinfo->status = status; + hlinfo->links = NULL; + linkp->data = (char *) hlinfo; + } + } +#endif + break; case T_UNKNOWN: error (0, 0, "nothing known about `%s'", finfo->fullname); @@ -1280,8 +1341,9 @@ commit_fileproc (callerdat, finfo) /* Doesn't matter, it won't get checked. */ SERVER_UPDATED, - (struct stat *) NULL, - (unsigned char *) NULL); + (mode_t) -1, + (unsigned char *) NULL, + (struct buffer *) NULL); } #endif } @@ -1645,9 +1707,8 @@ remove_file (finfo, tag, message) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - if (!quiet) - error (0, retcode == -1 ? errno : 0, - "failed to check out `%s'", finfo->fullname); + error (0, 0, + "failed to check out `%s'", finfo->fullname); return (1); } @@ -1981,13 +2042,21 @@ internal error: `%s' didn't move out of the attic", if (tag && newfile) { char *tmp; + FILE *fp; /* move the new file out of the way. */ fname = xmalloc (strlen (file) + sizeof (CVSADM) + sizeof (CVSPREFIX) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); rename_file (file, fname); - copy_file (DEVNULL, file); + + /* Create empty FILE. Can't use copy_file with a DEVNULL + argument -- copy_file now ignores device files. */ + fp = fopen (file, "w"); + if (fp == NULL) + error (1, errno, "cannot open %s for writing", file); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", file); tmp = xmalloc (strlen (file) + strlen (tag) + 80); /* commit a dead revision. */ @@ -2156,18 +2225,31 @@ lock_RCS (user, rcs, rev, repository) { (void) RCS_lock(rcs, rev, 1); } - RCS_rewrite (rcs, NULL, NULL); + + /* We used to call RCS_rewrite here, and that might seem + appropriate in order to write out the locked revision + information. However, such a call would actually serve no + purpose. CVS locks will prevent any interference from other + CVS processes. The comment above rcs_internal_lockfile + explains that it is already unsafe to use RCS and CVS + simultaneously. It follows that writing out the locked + revision information here would add no additional security. + + If we ever do care about it, the proper fix is to create the + RCS lock file before calling this function, and maintain it + until the checkin is complete. + + The call to RCS_lock is still required at present, since in + some cases RCS_checkin will determine which revision to check + in by looking for a lock. FIXME: This is rather roundabout, + and a more straightforward approach would probably be easier to + understand. */ if (err == 0) { if (sbranch != NULL) free (sbranch); - if (branch) - { - sbranch = branch; - } - else - sbranch = NULL; + sbranch = branch; return (0); } @@ -2182,7 +2264,8 @@ lock_RCS (user, rcs, rev, repository) /* Called when "add"ing files to the RCS respository. It doesn't seem to be possible to get RCS to use the right mode, so we change it after - the fact. */ + the fact. TODO: now that RCS has been librarified, we have the power + to change this. */ static void fix_rcs_modes (rcs, user) @@ -2192,6 +2275,12 @@ fix_rcs_modes (rcs, user) struct stat sb; mode_t rcs_mode; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Do ye nothing to the modes on a symbolic link. */ + if (preserve_perms && islink (user)) + return; +#endif + if (CVS_STAT (user, &sb) < 0) { /* FIXME: Should be ->fullname. */ @@ -2201,6 +2290,9 @@ fix_rcs_modes (rcs, user) /* Now we compute the new mode. + TODO: decide whether this whole thing can/should be skipped + when `preserve_perms' is set. Almost certainly so. -twp + The algorithm that we use is: Write permission is always off (this is what RCS and CVS have always diff --git a/contrib/cvs/src/create_adm.c b/contrib/cvs/src/create_adm.c index c1772b2..c51785c 100644 --- a/contrib/cvs/src/create_adm.c +++ b/contrib/cvs/src/create_adm.c @@ -59,6 +59,12 @@ Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn) if (CVS_MKDIR (tmp, 0777) < 0) { + /* We want to print out the entire update_dir, since a lot of + our code calls this function with dir == "." or dir == + NULL. I hope that gives enough information in cases like + absolute pathnames; printing out xgetwd or something would + be way too verbose in the common cases. */ + if (warn) { /* The reason that this is a warning, rather than silently @@ -66,11 +72,14 @@ Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn) CVS's behavior to vary subtly based on factors (like directory permissions) which are not made clear to the user. With the warning at least we let them know what is going on. */ - error (0, errno, "warning: cannot make directory %s", tmp); + error (0, errno, "warning: cannot make directory %s in %s", + CVSADM, update_dir); + free (tmp); return 1; } else - error (1, errno, "cannot make directory %s", tmp); + error (1, errno, "cannot make directory %s in %s", + CVSADM, update_dir); } /* record the current cvs root for later use */ diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index 5d735f1..3a7e9e4 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -402,6 +402,7 @@ int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, char *)); #define RCS_FLAGS_DEAD 2 #define RCS_FLAGS_QUIET 4 #define RCS_FLAGS_MODTIME 8 +#define RCS_FLAGS_KEEPFILE 16 extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile, char *opts, char *options, @@ -422,7 +423,7 @@ DBM *open_module PROTO((void)); FILE *open_file PROTO((const char *, const char *)); List *Find_Directories PROTO((char *repository, int which, List *entries)); void Entries_Close PROTO((List *entries)); -List *Entries_Open PROTO((int aflag)); +List *Entries_Open PROTO ((int aflag, char *update_dir)); void Subdirs_Known PROTO((List *entries)); void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); @@ -461,10 +462,12 @@ int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); int isdir PROTO((const char *file)); int isfile PROTO((const char *file)); int islink PROTO((const char *file)); +int isdevice PROTO ((const char *)); int isreadable PROTO((const char *file)); int iswritable PROTO((const char *file)); int isaccessible PROTO((const char *file, const int mode)); int isabsolute PROTO((const char *filename)); +char *xreadlink PROTO((const char *link)); char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); @@ -727,6 +730,9 @@ void freevers_ts PROTO ((Vers_TS ** versp)); int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev, char *tag, char *options, char *message)); int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers)); +/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */ +int special_file_mismatch PROTO ((struct file_info *finfo, + char *rev1, char *rev2)); /* CVSADM_BASEREV stuff, from entries.c. */ extern char *base_get PROTO ((struct file_info *)); diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c index a894c5b..7a4c105 100644 --- a/contrib/cvs/src/diff.c +++ b/contrib/cvs/src/diff.c @@ -636,13 +636,11 @@ RCS file: ", 0); : vers->options), tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL); - if (retcode == -1) + if (retcode != 0) { - (void) CVS_UNLINK (tmp); - error (1, errno, "fork failed during checkout of %s", - vers->srcfile->path); + diff_mark_errors (err); + return err; } - /* FIXME: what if retcode > 0? */ status = diff_exec (DEVNULL, tmp, opts, RUN_TTY); } @@ -657,13 +655,11 @@ RCS file: ", 0); *options ? options : vers->options, tmp, (RCSCHECKOUTPROC) NULL, (void *) NULL); - if (retcode == -1) + if (retcode != 0) { - (void) CVS_UNLINK (tmp); - error (1, errno, "fork failed during checkout of %s", - vers->srcfile->path); + diff_mark_errors (err); + return err; } - /* FIXME: what if retcode > 0? */ status = diff_exec (tmp, DEVNULL, opts, RUN_TTY); } @@ -719,7 +715,8 @@ RCS file: ", 0); if (empty_file == DIFF_REMOVED || (empty_file == DIFF_ADDED && use_rev2 != NULL)) { - (void) CVS_UNLINK (tmp); + if (CVS_UNLINK (tmp) < 0) + error (0, errno, "cannot remove %s", tmp); free (tmp); } diff --git a/contrib/cvs/src/entries.c b/contrib/cvs/src/entries.c index 538cd21..aeab313 100644 --- a/contrib/cvs/src/entries.c +++ b/contrib/cvs/src/entries.c @@ -450,12 +450,15 @@ fputentent(fp, p) } -/* - * Read the entries file into a list, hashing on the file name. - */ +/* Read the entries file into a list, hashing on the file name. + + UPDATE_DIR is the name of the current directory, for use in error + messages, or NULL if not known (that is, noone has gotten around + to updating the caller to pass in the information). */ List * -Entries_Open (aflag) +Entries_Open (aflag, update_dir) int aflag; + char *update_dir; { List *entries; struct stickydirtag *sdtp = NULL; @@ -492,7 +495,11 @@ Entries_Open (aflag) fpin = CVS_FOPEN (CVSADM_ENT, "r"); if (fpin == NULL) + { + if (update_dir != NULL) + error (0, 0, "in directory %s:", update_dir); error (0, errno, "cannot open %s for reading", CVSADM_ENT); + } else { while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) @@ -797,9 +804,22 @@ Subdirs_Known (entries) if (!noexec) { /* Create Entries.Log so that Entries_Close will do something. */ - fp = open_file (CVSADM_ENTLOG, "a"); - if (fclose (fp) == EOF) - error (1, errno, "cannot close %s", CVSADM_ENTLOG); + fp = CVS_FOPEN (CVSADM_ENTLOG, "a"); + if (fp == NULL) + { + int save_errno = errno; + + /* As in subdir_record, just silently skip the whole thing + if there is no CVSADM directory. */ + if (! isdir (CVSADM)) + return; + error (1, save_errno, "cannot open %s", entfilename); + } + else + { + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTLOG); + } } } } diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c index b5121a7..0ccc3df 100644 --- a/contrib/cvs/src/filesubr.c +++ b/contrib/cvs/src/filesubr.c @@ -43,46 +43,66 @@ copy_file (from, to) if (noexec) return; - if ((fdin = open (from, O_RDONLY)) < 0) - error (1, errno, "cannot open %s for copying", from); - if (fstat (fdin, &sb) < 0) - error (1, errno, "cannot fstat %s", from); - if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) - error (1, errno, "cannot create %s for copying", to); - if (sb.st_size > 0) + /* If the file to be copied is a link or a device, then just create + the new link or device appropriately. */ + if (islink (from)) { - char buf[BUFSIZ]; - int n; + char *source = xreadlink (from); + symlink (source, to); + free (source); + return; + } - for (;;) + if (isdevice (from)) + { + if (stat (from, &sb) < 0) + error (1, errno, "cannot stat %s", from); + mknod (to, sb.st_mode, sb.st_rdev); + } + else + { + /* Not a link or a device... probably a regular file. */ + if ((fdin = open (from, O_RDONLY)) < 0) + error (1, errno, "cannot open %s for copying", from); + if (fstat (fdin, &sb) < 0) + error (1, errno, "cannot fstat %s", from); + if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) + error (1, errno, "cannot create %s for copying", to); + if (sb.st_size > 0) { - n = read (fdin, buf, sizeof(buf)); - if (n == -1) + char buf[BUFSIZ]; + int n; + + for (;;) { + n = read (fdin, buf, sizeof(buf)); + if (n == -1) + { #ifdef EINTR - if (errno == EINTR) - continue; + if (errno == EINTR) + continue; #endif - error (1, errno, "cannot read file %s for copying", from); - } - else if (n == 0) - break; - - if (write(fdout, buf, n) != n) { - error (1, errno, "cannot write file %s for copying", to); + error (1, errno, "cannot read file %s for copying", from); + } + else if (n == 0) + break; + + if (write(fdout, buf, n) != n) { + error (1, errno, "cannot write file %s for copying", to); + } } - } #ifdef HAVE_FSYNC - if (fsync (fdout)) - error (1, errno, "cannot fsync file %s after copying", to); + if (fsync (fdout)) + error (1, errno, "cannot fsync file %s after copying", to); #endif - } + } - if (close (fdin) < 0) - error (0, errno, "cannot close %s", from); - if (close (fdout) < 0) - error (1, errno, "cannot close %s", to); + if (close (fdin) < 0) + error (0, errno, "cannot close %s", from); + if (close (fdout) < 0) + error (1, errno, "cannot close %s", to); + } /* now, set the times for the copied file to match those of the original */ memset ((char *) &t, 0, sizeof (t)); @@ -119,7 +139,7 @@ islink (file) #ifdef S_ISLNK struct stat sb; - if (lstat (file, &sb) < 0) + if (CVS_LSTAT (file, &sb) < 0) return (0); return (S_ISLNK (sb.st_mode)); #else @@ -128,6 +148,29 @@ islink (file) } /* + * Returns non-zero if the argument file is a block or + * character special device. + */ +int +isdevice (file) + const char *file; +{ + struct stat sb; + + if (CVS_LSTAT (file, &sb) < 0) + return (0); +#ifdef S_ISBLK + if (S_ISBLK (sb.st_mode)) + return 1; +#endif +#ifdef S_ISCHR + if (S_ISCHR (sb.st_mode)) + return 1; +#endif + return 0; +} + +/* * Returns non-zero if the argument file exists. */ int @@ -298,6 +341,9 @@ mkdir_if_needed (name) /* * Change the mode of a file, either adding write permissions, or removing * all write permissions. Either change honors the current umask setting. + * + * Don't do anything if PreservePermissions is set to `yes'. This may + * have unexpected consequences for some uses of xchmod. */ void xchmod (fname, writable) @@ -307,6 +353,9 @@ xchmod (fname, writable) struct stat sb; mode_t mode, oumask; + if (preserve_perms) + return; + if (stat (fname, &sb) < 0) { if (!noexec) @@ -417,6 +466,8 @@ int unlink_file_dir (f) const char *f; { + struct stat sb; + if (trace #ifdef SERVER_SUPPORT /* This is called by the server parent process in contexts where @@ -433,20 +484,23 @@ unlink_file_dir (f) /* For at least some unices, if root tries to unlink() a directory, instead of doing something rational like returning EISDIR, the system will gleefully go ahead and corrupt the filesystem. - So we first call isdir() to see if it is OK to call unlink(). This + So we first call stat() to see if it is OK to call unlink(). This doesn't quite work--if someone creates a directory between the - call to isdir() and the call to unlink(), we'll still corrupt + call to stat() and the call to unlink(), we'll still corrupt the filesystem. Where is the Unix Haters Handbook when you need it? */ - if (isdir(f)) - return deep_remove_dir(f); - else + if (stat (f, &sb) < 0) { - if (unlink (f) != 0) + if (existence_error (errno)) + { + /* The file or directory doesn't exist anyhow. */ return -1; + } } - /* We were able to remove the file from the disk */ - return 0; + else if (S_ISDIR (sb.st_mode)) + return deep_remove_dir (f); + + return unlink (f); } /* Remove a directory and everything it contains. Returns 0 for @@ -557,6 +611,8 @@ block_read (fd, buf, nchars) /* * Compare "file1" to "file2". Return non-zero if they don't compare exactly. + * If FILE1 and FILE2 are special files, compare their salient characteristics + * (i.e. major/minor device numbers, links, etc. */ int xcmp (file1, file2) @@ -568,14 +624,42 @@ xcmp (file1, file2) int fd1, fd2; int ret; + if (CVS_LSTAT (file1, &sb1) < 0) + error (1, errno, "cannot lstat %s", file1); + if (CVS_LSTAT (file2, &sb2) < 0) + error (1, errno, "cannot lstat %s", file2); + + /* If FILE1 and FILE2 are not the same file type, they are unequal. */ + if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT)) + return 1; + + /* If FILE1 and FILE2 are symlinks, they are equal if they point to + the same thing. */ + if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode)) + { + int result; + buf1 = xreadlink (file1); + buf2 = xreadlink (file2); + result = (strcmp (buf1, buf2) == 0); + free (buf1); + free (buf2); + return result; + } + + /* If FILE1 and FILE2 are devices, they are equal if their device + numbers match. */ + if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode)) + { + if (sb1.st_rdev == sb2.st_rdev) + return 0; + else + return 1; + } + if ((fd1 = open (file1, O_RDONLY)) < 0) error (1, errno, "cannot open file %s for comparing", file1); if ((fd2 = open (file2, O_RDONLY)) < 0) error (1, errno, "cannot open file %s for comparing", file2); - if (fstat (fd1, &sb1) < 0) - error (1, errno, "cannot fstat %s", file1); - if (fstat (fd2, &sb2) < 0) - error (1, errno, "cannot fstat %s", file2); /* A generic file compare routine might compare st_dev & st_ino here to see if the two files being compared are actually the same file. @@ -677,6 +761,39 @@ isabsolute (filename) return filename[0] == '/'; } +/* + * Return a string (dynamically allocated) with the name of the file to which + * LINK is symlinked. + */ +char * +xreadlink (link) + const char *link; +{ + char *file = NULL; + int buflen = BUFSIZ; + + if (!islink (link)) + return NULL; + + /* Get the name of the file to which `from' is linked. + FIXME: what portability issues arise here? Are readlink & + ENAMETOOLONG defined on all systems? -twp */ + do + { + file = xrealloc (file, buflen); + errno = 0; + readlink (link, file, buflen); + buflen *= 2; + } + while (errno == ENAMETOOLONG); + + if (errno) + error (1, errno, "cannot readlink %s", link); + + return file; +} + + /* Return a pointer into PATH's last component. */ char * @@ -684,8 +801,8 @@ last_component (path) char *path; { char *last = strrchr (path, '/'); - - if (last) + + if (last && (last != path)) return last + 1; else return path; diff --git a/contrib/cvs/src/find_names.c b/contrib/cvs/src/find_names.c index ed6c5c4..4fa795a 100644 --- a/contrib/cvs/src/find_names.c +++ b/contrib/cvs/src/find_names.c @@ -50,18 +50,6 @@ add_entries_proc (node, closure) return (0); } -/* - * compare two files list node (for sort) - */ -static int fsortcmp PROTO ((const Node *, const Node *)); -static int -fsortcmp (p, q) - const Node *p; - const Node *q; -{ - return (strcmp (p->key, q->key)); -} - List * Find_Names (repository, which, aflag, optentries) char *repository; @@ -79,7 +67,7 @@ Find_Names (repository, which, aflag, optentries) if (which & W_LOCAL) { /* parse the entries file (if it exists) */ - entries = Entries_Open (aflag); + entries = Entries_Open (aflag, NULL); if (entries != NULL) { /* walk the entries file adding elements to the files list */ @@ -182,7 +170,7 @@ Find_Directories (repository, which, entries) if (entries != NULL) tmpentries = entries; else if (isfile (CVSADM_ENT)) - tmpentries = Entries_Open (0); + tmpentries = Entries_Open (0, NULL); else tmpentries = NULL; diff --git a/contrib/cvs/src/hardlink.c b/contrib/cvs/src/hardlink.c new file mode 100644 index 0000000..51bd2a6 --- /dev/null +++ b/contrib/cvs/src/hardlink.c @@ -0,0 +1,298 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Collect and manage hardlink info associated with a particular file. */ + +#include "cvs.h" +#include "hardlink.h" + +/* The structure currently used to manage hardlink info is a list. + Therefore, most of the functions which manipulate hardlink data + are walklist procedures. This is not a very efficient implementation; + if someone decides to use a real hash table (for instance), then + much of this code can be rewritten to be a little less arcane. + + Each element of `hardlist' represents an inode. It is keyed on the + inode number, and points to a list of files. This is to make it + easy to find out what files are linked to a given file FOO: find + FOO's inode, look it up in hardlist, and retrieve the list of files + associated with that inode. + + Each file node, in turn, is represented by a `hardlink_info' struct, + which includes `status' and `links' fields. The `status' field should + be used by a procedure like commit_fileproc or update_fileproc to + record each file's status; that way, after all file links have been + recorded, CVS can check the linkage of files which are in doubt + (i.e. T_NEEDS_MERGE files). + + TODO: a diagram of an example hardlist would help here. */ + +/* TODO: change this to something with a marginal degree of + efficiency, like maybe a hash table. Yeah. */ + +List *hardlist; /* Record hardlink information for working files */ +char *working_dir; /* The top-level working directory, used for + constructing full pathnames. */ + +/* For check_link_proc: list all of the files named in an inode list. */ +static int +list_files_proc (node, vstrp) + Node *node; + void *vstrp; +{ + char **strp, *file; + int len; + + /* Get the file's basename. This is because -- VERY IMPORTANT -- + the `hardlinks' field is presently defined only to include links + within a directory. So the hardlinks field might be `foo' or + `mumble grump flink', but not `foo bar com/baz' or `wham ../bam + ../thank/you'. Someday it would be nice to extend this to + permit cross-directory links, but the issues involved are + hideous. */ + + file = strrchr (node->key, '/'); + if (file) + ++file; + else + file = node->key; + + /* Is it safe to cast vstrp to (char **) here, and then play with + the contents? I think so, since vstrp will have started out + a char ** to begin with, so we should not have alignment bugs. */ + strp = (char **) vstrp; + len = (*strp == NULL ? 0 : strlen (*strp)); + *strp = (char *) xrealloc (*strp, len + strlen (file) + 2); + if (*strp == NULL) + { + error (0, errno, "could not allocate memory"); + return 1; + } + if (sprintf (*strp + len, "%s ", file) < 0) + { + error (0, errno, "could not compile file list"); + return 1; + } + + return 0; +} + +/* Set the link field of each hardlink_info node to `data', which is a + list of linked files. */ +static int +set_hardlink_field_proc (node, data) + Node *node; + void *data; +{ + struct hardlink_info *hlinfo = (struct hardlink_info *) node->data; + hlinfo->links = xstrdup ((char *) data); + + return 0; +} + +/* For each file being checked in, compile a list of the files linked + to it, and cache the list in the file's hardlink_info field. */ +int +cache_hardlinks_proc (node, data) + Node *node; + void *data; +{ + List *inode_links; + char *p, *linked_files = NULL; + int err; + + inode_links = (List *) node->data; + + /* inode->data is a list of hardlink_info structures: all the + files linked to this inode. We compile a string of each file + named in this list, in alphabetical order, separated by spaces. + Then store this string in the `links' field of each + hardlink_info structure, so that RCS_checkin can easily add + it to the `hardlinks' field of a new delta node. */ + + sortlist (inode_links, fsortcmp); + err = walklist (inode_links, list_files_proc, &linked_files); + if (err) + return err; + + /* Trim trailing whitespace. */ + p = linked_files + strlen(linked_files) - 1; + while (p > linked_files && isspace (*p)) + *p-- = '\0'; + + err = walklist (inode_links, set_hardlink_field_proc, linked_files); + return err; +} + +/* Return a pointer to FILEPATH's node in the hardlist. This means + looking up its inode, retrieving the list of files linked to that + inode, and then looking up FILE in that list. If the file doesn't + seem to exist, return NULL. */ +Node * +lookup_file_by_inode (filepath) + const char *filepath; +{ + char *inodestr, *file; + struct stat sb; + Node *hp, *p; + + /* Get file's basename, so that we can stat it. */ + file = strrchr (filepath, '/'); + if (file) + ++file; + else + file = (char *) filepath; + + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + if (stat (file, &sb) < 0) + { + if (errno == ENOENT) + { + /* The file doesn't exist; we may be doing an update on a + file that's been removed. A nonexistent file has no + link information, so return without changing hardlist. */ + free (inodestr); + return NULL; + } + error (1, errno, "cannot stat %s", file); + } + + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Find out if this inode is already in the hardlist, adding + a new entry to the list if not. */ + hp = findnode (hardlist, inodestr); + if (hp == NULL) + { + hp = getnode (); + hp->type = UNKNOWN; + hp->key = inodestr; + hp->data = (char *) getlist(); + hp->delproc = dellist; + (void) addnode (hardlist, hp); + } + else + { + free (inodestr); + } + + p = findnode ((List *) hp->data, filepath); + if (p == NULL) + { + p = getnode(); + p->type = UNKNOWN; + p->key = xstrdup (filepath); + p->data = NULL; + (void) addnode ((List *) hp->data, p); + } + + return p; +} + +/* After a file has been checked out, add a node for it to the hardlist + (if necessary) and mark it as checked out. */ +void +update_hardlink_info (file) + const char *file; +{ + char *path; + Node *n; + struct hardlink_info *hlinfo; + + if (file[0] == '/') + { + path = xstrdup (file); + } + else + { + /* file is a relative pathname; assume it's from the current + working directory. */ + char *dir = xgetwd(); + path = xmalloc (sizeof(char) * (strlen(dir) + strlen(file) + 2)); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + n = lookup_file_by_inode (path); + if (n == NULL) + { + /* Something is *really* wrong if the file doesn't exist here; + update_hardlink_info should be called only when a file has + just been checked out to a working directory. */ + error (1, 0, "lost hardlink info for %s", file); + } + + if (n->data == NULL) + n->data = (char *) xmalloc (sizeof (struct hardlink_info)); + hlinfo = (struct hardlink_info *) n->data; + hlinfo->status = T_UPTODATE; + hlinfo->checked_out = 1; + hlinfo->links = NULL; +} + +/* Return a string listing all the files known to be linked to FILE in + the working directory. Used by special_file_mismatch, to determine + whether it is safe to merge two files. */ +char * +list_files_linked_to (file) + const char *file; +{ + char *inodestr, *filelist, *path; + struct stat sb; + Node *n; + int err; + + /* If hardlist is NULL, we have not been doing an operation that + would permit us to know anything about the file's hardlinks + (cvs update, cvs commit, etc). Return an empty string. */ + if (hardlist == NULL) + return xstrdup (""); + + /* Get the full pathname of file (assuming the working directory) */ + if (file[0] == '/') + path = xstrdup (file); + else + { + char *dir = xgetwd(); + path = (char *) xmalloc (sizeof(char) * + (strlen(dir) + strlen(file) + 2)); + sprintf (path, "%s/%s", dir, file); + free (dir); + } + + /* We do an extra lookup_file here just to make sure that there + is a node for `path' in the hardlist. If that were not so, + comparing the working directory linkage against the repository + linkage for a file would always fail. */ + (void) lookup_file_by_inode (path); + + if (stat (path, &sb) < 0) + error (1, errno, "cannot stat %s", file); + /* inodestr contains the hexadecimal representation of an + inode, so it requires two bytes of text to represent + each byte of the inode number. */ + inodestr = (char *) xmalloc (2*sizeof(ino_t)*sizeof(char) + 1); + sprintf (inodestr, "%lx", (unsigned long) sb.st_ino); + + /* Make sure the files linked to this inode are sorted. */ + n = findnode (hardlist, inodestr); + sortlist ((List *) n->data, fsortcmp); + + filelist = NULL; + err = walklist ((List *) n->data, list_files_proc, &filelist); + if (err) + error (1, 0, "cannot get list of hardlinks for %s", file); + + free (inodestr); + return filelist; +} diff --git a/contrib/cvs/src/hardlink.h b/contrib/cvs/src/hardlink.h new file mode 100644 index 0000000..cce3f33 --- /dev/null +++ b/contrib/cvs/src/hardlink.h @@ -0,0 +1,33 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Data type definitions and declarations for hardlink management. */ + +/* This file should be #included in CVS source files after cvs.h + since it relies on types and macros defined there. */ + +/* The `checked_out' member of a hardlink_info struct is used only + when files are being checked out or updated. It is used only when + hardlinked files are being checked out. */ + +struct hardlink_info +{ + Ctype status; /* as returned from Classify_File() */ + int checked_out; /* has this file been checked out lately? */ + char *links; /* contents of `hardlinks' RCS field */ +}; + +extern List *hardlist; +extern char *working_dir; + +int cache_hardlinks_proc PROTO ((Node *, void *)); +Node *lookup_file_by_inode PROTO ((const char *)); +void update_hardlink_info PROTO ((const char *)); +char *list_files_linked_to PROTO ((const char *)); diff --git a/contrib/cvs/src/hash.c b/contrib/cvs/src/hash.c index ff3f122..af000ac 100644 --- a/contrib/cvs/src/hash.c +++ b/contrib/cvs/src/hash.c @@ -436,6 +436,17 @@ sortlist (list, comp) free (array); } +/* + * compare two files list node (for sort) + */ +int +fsortcmp (p, q) + const Node *p; + const Node *q; +{ + return (strcmp (p->key, q->key)); +} + /* Debugging functions. Quite useful to call from within gdb. */ static char *nodetypestring PROTO ((Ntype)); diff --git a/contrib/cvs/src/hash.h b/contrib/cvs/src/hash.h index d29c757..06867a7 100644 --- a/contrib/cvs/src/hash.h +++ b/contrib/cvs/src/hash.h @@ -56,3 +56,4 @@ void dellist PROTO((List ** listp)); void delnode PROTO((Node * p)); void freenode PROTO((Node * p)); void sortlist PROTO((List * list, int (*)(const Node *, const Node *))); +int fsortcmp PROTO((const Node * p, const Node * q)); diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index 0a2af7f..d79a10e 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -260,7 +260,11 @@ import (argc, argv) tmpfile = cvs_temp_name (); if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); - (void) CVS_UNLINK (tmpfile); /* to be sure it goes away */ + /* On systems where we can unlink an open file, do so, so it will go + away no matter how we exit. FIXME-maybe: Should be checking for + errors but I'm not sure which error(s) we get if we are on a system + where one can't unlink open files. */ + (void) CVS_UNLINK (tmpfile); (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); (void) fprintf (logfp, "Release Tags:\t"); for (i = 2; i < argc; i++) @@ -320,11 +324,13 @@ import (argc, argv) (void) addnode (ulist, p); Update_Logfile (repository, message, logfp, ulist); dellist (&ulist); - (void) fclose (logfp); + if (fclose (logfp) < 0) + error (0, errno, "error closing %s", tmpfile); /* Make sure the temporary file goes away, even on systems that don't let you delete a file that's in use. */ - CVS_UNLINK (tmpfile); + if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile); free (tmpfile); if (message) @@ -491,7 +497,7 @@ process_import_file (message, vfile, vtag, targc, targv) /* Reading all the entries for each file is fairly silly, and probably slow. But I am too lazy at the moment to do anything else. */ - entries = Entries_Open (0); + entries = Entries_Open (0, NULL); node = findnode_fn (entries, vfile); if (node != NULL) { @@ -977,6 +983,7 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, char *userfile; char *local_opt = key_opt; char *free_opt = NULL; + mode_t file_type; if (noexec) return (0); @@ -1004,18 +1011,39 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, which does not depend on what the client or server OS is, as documented in cvsclient.texi), but as long as the server just runs on unix it is a moot point. */ - fpuser = CVS_FOPEN (userfile, - ((local_opt != NULL && strcmp (local_opt, "b") == 0) - ? "rb" - : "r") - ); - if (fpuser == NULL) + + /* If PreservePermissions is set, then make sure that the file + is a plain file before trying to open it. Longstanding (although + often unpopular) CVS behavior has been to follow symlinks, so we + maintain that behavior if PreservePermissions is not on. + + NOTE: this error message used to be `cannot fstat', but is now + `cannot lstat'. I don't see a way around this, since we must + stat the file before opening it. -twp */ + + if (CVS_LSTAT (userfile, &sb) < 0) + error (1, errno, "cannot lstat %s", user); + file_type = sb.st_mode & S_IFMT; + + fpuser = NULL; + if (!preserve_perms || file_type == S_IFREG) { - /* not fatal, continue import */ - fperror (add_logfp, 0, errno, "ERROR: cannot read file %s", userfile); - error (0, errno, "ERROR: cannot read file %s", userfile); - goto read_error; + fpuser = CVS_FOPEN (userfile, + ((local_opt != NULL && strcmp (local_opt, "b") == 0) + ? "rb" + : "r") + ); + if (fpuser == NULL) + { + /* not fatal, continue import */ + if (add_logfp != NULL) + fperror (add_logfp, 0, errno, + "ERROR: cannot read file %s", userfile); + error (0, errno, "ERROR: cannot read file %s", userfile); + goto read_error; + } } + fprcs = CVS_FOPEN (rcs, "w+b"); if (fprcs == NULL) { @@ -1082,10 +1110,6 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (fprintf (fprcs, "\012") < 0) goto write_error; - /* Get information on modtime and mode. */ - if (fstat (fileno (fpuser), &sb) < 0) - error (1, errno, "cannot fstat %s", user); - /* Write the revision(s), with the date and author and so on (that is "delta" rather than "deltatext" from rcsfile(5)). */ if (add_vhead != NULL) @@ -1118,13 +1142,102 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (fprintf (fprcs, "next ;\012") < 0) goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0) + goto write_error; + if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0) + goto write_error; + if (fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; + break; + default: + error (0, 0, + "can't import %s: unknown kind of special file", + userfile); + } + } + } +#endif + if (add_vbranch != NULL) { if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || fprintf (fprcs, "date %s; author %s; state Exp;\012", altdate1, author) < 0 || fprintf (fprcs, "branches ;\012") < 0 || - fprintf (fprcs, "next ;\012\012") < 0) + fprintf (fprcs, "next ;\012") < 0) + goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 || + fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 || + fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; + break; + default: + error (0, 0, + "cannot import %s: special file of unknown type", + userfile); + } + } + } +#endif + + if (fprintf (fprcs, "\012") < 0) goto write_error; } } @@ -1170,7 +1283,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, goto write_error; } - /* Now copy over the contents of the file, expanding at signs. */ + /* Now copy over the contents of the file, expanding at signs. + If preserve_perms is set, do this only for regular files. */ + if (!preserve_perms || file_type == S_IFREG) { char buf[8192]; unsigned int len; @@ -1208,7 +1323,12 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, ierrno = errno; goto write_error_noclose; } - (void) fclose (fpuser); + /* Close fpuser only if we opened it to begin with. */ + if (fpuser != NULL) + { + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + } /* * Fix the modes on the RCS files. The user modes of the original @@ -1224,8 +1344,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, if (chmod (rcs, mode) < 0) { ierrno = errno; - fperror (add_logfp, 0, ierrno, - "WARNING: cannot change mode of file %s", rcs); + if (add_logfp != NULL) + fperror (add_logfp, 0, ierrno, + "WARNING: cannot change mode of file %s", rcs); error (0, ierrno, "WARNING: cannot change mode of file %s", rcs); err++; } @@ -1238,15 +1359,20 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt, write_error: ierrno = errno; - (void) fclose (fprcs); + if (fclose (fprcs) < 0) + error (0, errno, "cannot close %s", rcs); write_error_noclose: - (void) fclose (fpuser); - fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + if (add_logfp != NULL) + fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); error (0, ierrno, "ERROR: cannot write file %s", rcs); if (ierrno == ENOSPC) { - (void) CVS_UNLINK (rcs); - fperror (add_logfp, 0, 0, "ERROR: out of space - aborting"); + if (CVS_UNLINK (rcs) < 0) + error (0, errno, "cannot remove %s", rcs); + if (add_logfp != NULL) + fperror (add_logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } read_error: @@ -1271,20 +1397,27 @@ expand_at_signs (buf, size, fp) off_t size; FILE *fp; { - char *cp, *end; + register char *cp, *next; - errno = 0; - for (cp = buf, end = buf + size; cp < end; cp++) + cp = buf; + while ((next = memchr (cp, '@', size)) != NULL) { - if (*cp == '@') - { - if (putc ('@', fp) == EOF && errno != 0) - return EOF; - } - if (putc (*cp, fp) == EOF && errno != 0) - return (EOF); + int len; + + ++next; + len = next - cp; + if (fwrite (cp, 1, len, fp) != len) + return EOF; + if (putc ('@', fp) == EOF) + return EOF; + cp = next; + size -= len; } - return (1); + + if (fwrite (cp, 1, size, fp) != size) + return EOF; + + return 1; } /* diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c index be2dd1c..72818f3 100644 --- a/contrib/cvs/src/lock.c +++ b/contrib/cvs/src/lock.c @@ -704,7 +704,6 @@ lock_obtained (repos) static int lock_filesdoneproc PROTO ((void *callerdat, int err, char *repository, char *update_dir, List *entries)); -static int fsortcmp PROTO((const Node * p, const Node * q)); /* * Create a list of repositories to lock @@ -733,17 +732,6 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries) return (err); } -/* - * compare two lock list nodes (for sort) - */ -static int -fsortcmp (p, q) - const Node *p; - const Node *q; -{ - return (strcmp (p->key, q->key)); -} - void lock_tree_for_write (argc, argv, local, aflag) int argc; diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c index 2397d7e..9465be6 100644 --- a/contrib/cvs/src/main.c +++ b/contrib/cvs/src/main.c @@ -522,7 +522,7 @@ main (argc, argv) (void) fputs (config_string, stdout); (void) fputs ("\n", stdout); (void) fputs ("\ -Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\ +Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\ Jeff Polk, and other authors\n", stdout); (void) fputs ("\n", stdout); (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c index dab5b3f..c3c530d 100644 --- a/contrib/cvs/src/mkmodules.c +++ b/contrib/cvs/src/mkmodules.c @@ -279,6 +279,10 @@ static const char *const modules_contents[] = { static const char *const config_contents[] = { "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", "#SystemAuth=no\n", + "\n", + "# Set `PreservePermissions' to `yes' to save file status information\n", + "# in the repository.\n", + "#PreservePermissions=no\n", NULL }; @@ -538,7 +542,7 @@ checkout_file (file, temp) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - error (0, retcode == -1 ? errno : 0, "failed to check out %s file", + error (0, 0, "failed to check out %s file", file); } freercsnode (&rcsnode); diff --git a/contrib/cvs/src/myndbm.c b/contrib/cvs/src/myndbm.c index 949ac78..6b15e77 100644 --- a/contrib/cvs/src/myndbm.c +++ b/contrib/cvs/src/myndbm.c @@ -200,27 +200,34 @@ mydbm_load_file (fp, list) List *list; { char *line = NULL; - size_t line_len; + size_t line_size; char *value; size_t value_allocated; char *cp, *vp; - int len, cont; + int cont; int line_length; value_allocated = 1; value = xmalloc (value_allocated); - for (cont = 0; (line_length = getline (&line, &line_len, fp)) >= 0;) + cont = 0; + while ((line_length = getstr (&line, &line_size, fp, '\012', 0)) >= 0) { - if ((cp = strrchr (line, '\012')) != NULL) - *cp = '\0'; /* strip the newline */ - cp = line + strlen (line); - if (cp > line && cp[-1] == '\015') + if (line_length > 0 && line[line_length - 1] == '\012') + { + /* Strip the newline. */ + --line_length; + line[line_length] = '\0'; + } + if (line_length > 0 && line[line_length - 1] == '\015') + { /* If the file (e.g. modules) was written on an NT box, it will contain CRLF at the ends of lines. Strip them (we can't do this by opening the file in text mode because we might be running on unix). */ - cp[-1] = '\0'; + --line_length; + line[line_length] = '\0'; + } /* * Add the line to the value, at the end if this is a continuation @@ -234,15 +241,15 @@ mydbm_load_file (fp, list) * See if the line we read is a continuation line, and strip the * backslash if so. */ - len = strlen (line); - if (len > 0) - cp = &line[len - 1]; + if (line_length > 0) + cp = &line[line_length - 1]; else cp = line; if (*cp == '\\') { cont = 1; *cp = '\0'; + --line_length; } else { @@ -250,7 +257,7 @@ mydbm_load_file (fp, list) } expand_string (&value, &value_allocated, - strlen (value) + strlen (line) + 5); + strlen (value) + line_length + 5); strcat (value, line); if (value[0] == '#') diff --git a/contrib/cvs/src/no_diff.c b/contrib/cvs/src/no_diff.c index 6d6a6fb..078343e 100644 --- a/contrib/cvs/src/no_diff.c +++ b/contrib/cvs/src/no_diff.c @@ -36,6 +36,13 @@ No_Difference (finfo, vers) if (!vers->srcfile || !vers->srcfile->path) return (-1); /* different since we couldn't tell */ +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If special files are in use, then any mismatch of file metadata + information also means that the files should be considered different. */ + if (preserve_perms && special_file_mismatch (finfo, vers->vn_user, NULL)) + return 1; +#endif + if (vers->entdata && vers->entdata->options) options = xstrdup (vers->entdata->options); else diff --git a/contrib/cvs/src/parseinfo.c b/contrib/cvs/src/parseinfo.c index c492be0..a8a1b4a 100644 --- a/contrib/cvs/src/parseinfo.c +++ b/contrib/cvs/src/parseinfo.c @@ -326,6 +326,26 @@ parse_config (cvsroot) goto error_return; } } + else if (strcmp (line, "PreservePermissions") == 0) + { + if (strcmp (p, "no") == 0) + preserve_perms = 0; + else if (strcmp (p, "yes") == 0) + { +#ifdef PRESERVE_PERMISSIONS_SUPPORT + preserve_perms = 1; +#else + error (0, 0, "\ +warning: this CVS does not support PreservePermissions"); +#endif + } + else + { + error (0, 0, "unrecognized value '%s' for PreservePermissions", + p); + goto error_return; + } + } else { /* We may be dealing with a keyword which was added in a diff --git a/contrib/cvs/src/patch.c b/contrib/cvs/src/patch.c index 9d091c3..9f56b62 100644 --- a/contrib/cvs/src/patch.c +++ b/contrib/cvs/src/patch.c @@ -499,23 +499,43 @@ patch_fileproc (callerdat, finfo) ret = 0; goto out2; } + + /* Create 3 empty files. I'm not really sure there is any advantage + to doing so now rather than just waiting until later. */ tmpfile1 = cvs_temp_name (); - if ((fp1 = CVS_FOPEN (tmpfile1, "w+")) != NULL) - (void) fclose (fp1); + fp1 = CVS_FOPEN (tmpfile1, "w+"); + if (fp1 == NULL) + { + error (0, errno, "cannot create temporary file %s", tmpfile1); + ret = 1; + goto out; + } + else + if (fclose (fp1) < 0) + error (0, errno, "warning: cannot close %s", tmpfile1); tmpfile2 = cvs_temp_name (); - if ((fp2 = CVS_FOPEN (tmpfile2, "w+")) != NULL) - (void) fclose (fp2); + fp2 = CVS_FOPEN (tmpfile2, "w+"); + if (fp2 == NULL) + { + error (0, errno, "cannot create temporary file %s", tmpfile2); + ret = 1; + goto out; + } + else + if (fclose (fp2) < 0) + error (0, errno, "warning: cannot close %s", tmpfile2); tmpfile3 = cvs_temp_name (); - if ((fp3 = CVS_FOPEN (tmpfile3, "w+")) != NULL) - (void) fclose (fp3); - if (fp1 == NULL || fp2 == NULL || fp3 == NULL) + fp3 = CVS_FOPEN (tmpfile3, "w+"); + if (fp3 == NULL) { - /* FIXME: should be printing a proper error message, with errno-based - message, and the filename which we could not create. */ - error (0, 0, "cannot create temporary files"); + error (0, errno, "cannot create temporary file %s", tmpfile3); ret = 1; goto out; } + else + if (fclose (fp3) < 0) + error (0, errno, "warning: cannot close %s", tmpfile3); + if (vers_tag != NULL) { retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag, @@ -523,9 +543,8 @@ patch_fileproc (callerdat, finfo) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - if (!really_quiet) - error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, - "co of revision %s in %s failed", vers_tag, rcs); + error (0, 0, + "cannot check out revision %s of %s", vers_tag, rcs); ret = 1; goto out; } @@ -548,9 +567,8 @@ patch_fileproc (callerdat, finfo) (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) { - if (!really_quiet) - error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, - "co of revision %s in %s failed", vers_head, rcs); + error (0, 0, + "cannot check out revision %s of %s", vers_head, rcs); ret = 1; goto out; } @@ -584,10 +602,16 @@ patch_fileproc (callerdat, finfo) if (getline (&line1, &line1_chars_allocated, fp) < 0 || getline (&line2, &line2_chars_allocated, fp) < 0) { - error (0, errno, "failed to read diff file header %s for %s", - tmpfile3, rcs); + if (feof (fp)) + error (0, 0, "\ +failed to read diff file header %s for %s: end of file", tmpfile3, rcs); + else + error (0, errno, + "failed to read diff file header %s for %s", + tmpfile3, rcs); ret = 1; - (void) fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); goto out; } if (!unidiff) @@ -599,7 +623,8 @@ patch_fileproc (callerdat, finfo) { error (0, 0, "invalid diff header for %s", rcs); ret = 1; - (void) fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); goto out; } } @@ -612,7 +637,8 @@ patch_fileproc (callerdat, finfo) { error (0, 0, "invalid unidiff header for %s", rcs); ret = 1; - (void) fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "error closing %s", tmpfile3); goto out; } } diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c index c134d67..89af760 100644 --- a/contrib/cvs/src/rcs.c +++ b/contrib/cvs/src/rcs.c @@ -11,6 +11,9 @@ #include #include "cvs.h" #include "edit.h" +#include "hardlink.h" + +int preserve_perms = 0; /* The RCS -k options, and a set of enums that must match the array. These come first so that we can use enum kflag in function @@ -19,11 +22,57 @@ static const char *const kflags[] = {"kv", "kvl", "k", "v", "o", "b", (char *) NULL}; enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B }; +/* A structure we use to buffer the contents of an RCS file. The + various fields are only referenced directly by the rcsbuf_* + functions. We declare the struct here so that we can allocate it + on the stack, rather than in memory. */ + +struct rcsbuffer +{ + /* Points to the current position in the buffer. */ + char *ptr; + /* Points just after the last valid character in the buffer. */ + char *ptrend; + /* The file. */ + FILE *fp; + /* The name of the file, used for error messages. */ + const char *filename; + /* The starting file position of the data in the buffer. */ + unsigned long pos; + /* The length of the value. */ + size_t vlen; + /* Whether the value contains an '@' string. If so, we can not + compress whitespace characters. */ + int at_string; + /* The number of embedded '@' characters in an '@' string. If + this is non-zero, we must search the string for pairs of '@' + and convert them to a single '@'. */ + int embedded_at; +}; + static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile)); static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch)); -static int getrcskey PROTO((FILE * fp, char **keyp, char **valp, - size_t *lenp)); -static void getrcsrev PROTO ((FILE *fp, char **revp)); +static void rcsbuf_open PROTO ((struct rcsbuffer *, FILE *fp, + const char *filename, unsigned long pos)); +static void rcsbuf_close PROTO ((struct rcsbuffer *)); +static int rcsbuf_getkey PROTO ((struct rcsbuffer *, char **keyp, + char **valp)); +static int rcsbuf_getrevnum PROTO ((struct rcsbuffer *, char **revp)); +static char *rcsbuf_fill PROTO ((struct rcsbuffer *, char *ptr, char **keyp, + char **valp)); +static char *rcsbuf_valcopy PROTO ((struct rcsbuffer *, char *val, int polish, + size_t *lenp)); +static void rcsbuf_valpolish PROTO ((struct rcsbuffer *, char *val, int polish, + size_t *lenp)); +static void rcsbuf_valpolish_internal PROTO ((struct rcsbuffer *, char *to, + const char *from, size_t *lenp)); +static unsigned long rcsbuf_ftell PROTO ((struct rcsbuffer *)); +static void rcsbuf_get_buffered PROTO ((struct rcsbuffer *, char **datap, + size_t *lenp)); +static void rcsbuf_cache PROTO ((RCSNode *, struct rcsbuffer *)); +static void rcsbuf_cache_close PROTO ((void)); +static void rcsbuf_cache_open PROTO ((RCSNode *, long, FILE **, + struct rcsbuffer *)); static int checkmagic_proc PROTO((Node *p, void *closure)); static void do_branches PROTO((List * list, char *val)); static void do_symbols PROTO((List * list, char *val)); @@ -40,12 +89,15 @@ static void expand_keywords PROTO((RCSNode *, RCSVers *, const char *, static void cmp_file_buffer PROTO((void *, const char *, size_t)); enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; -static void RCS_deltas PROTO ((RCSNode *, FILE *, char *, enum rcs_delta_op, - char **, size_t *, char **, size_t *)); +static void RCS_deltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, char *, + enum rcs_delta_op, char **, size_t *, + char **, size_t *)); /* Routines for reading, parsing and writing RCS files. */ -static RCSVers *getdelta PROTO ((FILE *, char *)); -static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *)); +static RCSVers *getdelta PROTO ((struct rcsbuffer *, char *, char **, + char **)); +static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *, + struct rcsbuffer *)); static void freedeltatext PROTO ((Deltatext *)); static void RCS_putadmin PROTO ((RCSNode *, FILE *)); @@ -54,13 +106,21 @@ static void RCS_putdesc PROTO ((RCSNode *, FILE *)); static void putdelta PROTO ((RCSVers *, FILE *)); static int putrcsfield_proc PROTO ((Node *, void *)); static int putsymbol_proc PROTO ((Node *, void *)); -static void RCS_copydeltas PROTO ((RCSNode *, FILE *, FILE *, Deltatext *, char *)); +static void RCS_copydeltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, + FILE *, Deltatext *, char *)); +static int count_delta_actions PROTO ((Node *, void *)); static void putdeltatext PROTO ((FILE *, Deltatext *)); static FILE *rcs_internal_lockfile PROTO ((char *)); static void rcs_internal_unlockfile PROTO ((FILE *, char *)); static char *rcs_lockfilename PROTO ((char *)); +/* The RCS file reading functions are called a lot, and they do some + string comparisons. This macro speeds things up a bit by skipping + the function call when the first characters are different. It + evaluates its arguments multiple times. */ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0) + /* * We don't want to use isspace() from the C library because: * @@ -90,7 +150,6 @@ static const char spacetab[] = { #define whitespace(c) (spacetab[(unsigned char)c] != 0) - /* Parse an rcsfile given a user file name and a repository. If there is an error, we print an error message and return NULL. If the file does not exist, we return NULL without printing anything (I'm not @@ -106,6 +165,10 @@ RCS_parse (file, repos) RCSNode *retval; char *rcsfile; + /* We're creating a new RCSNode, so there is no hope of finding it + in the cache. */ + rcsbuf_cache_close (); + rcsfile = xmalloc (strlen (repos) + strlen (file) + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10); (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT); @@ -115,7 +178,6 @@ RCS_parse (file, repos) if (rcs != NULL) rcs->flags |= VALID; - fclose (fp); retval = rcs; goto out; } @@ -136,7 +198,6 @@ RCS_parse (file, repos) rcs->flags |= VALID; } - fclose (fp); retval = rcs; goto out; } @@ -165,7 +226,6 @@ RCS_parse (file, repos) if (rcs != NULL) rcs->flags |= VALID; - fclose (fp); free (rcs->path); rcs->path = found_path; retval = rcs; @@ -189,7 +249,6 @@ RCS_parse (file, repos) rcs->flags |= VALID; } - fclose (fp); free (rcs->path); rcs->path = found_path; retval = rcs; @@ -221,6 +280,10 @@ RCS_parsercsfile (rcsfile) FILE *fp; RCSNode *rcs; + /* We're creating a new RCSNode, so there is no hope of finding it + in the cache. */ + rcsbuf_cache_close (); + /* open the rcsfile */ if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL) { @@ -230,7 +293,6 @@ RCS_parsercsfile (rcsfile) rcs = RCS_parsercsfile_i (fp, rcsfile); - fclose (fp); return (rcs); } @@ -243,6 +305,7 @@ RCS_parsercsfile_i (fp, rcsfile) const char *rcsfile; { RCSNode *rdata; + struct rcsbuffer rcsbuf; char *key, *value; /* make a node */ @@ -251,40 +314,32 @@ RCS_parsercsfile_i (fp, rcsfile) rdata->refcount = 1; rdata->path = xstrdup (rcsfile); - /* Process HEAD and BRANCH keywords from the RCS header. + /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header. Most cvs operations on the main branch don't need any more information. Those that do call RCS_reparsercsfile to parse - the rest of the header and the deltas. - - People often wonder whether this is inefficient, to open the - file once here and once in RCS_reparsercsfile. Well, it might - help a little bit if we kept the file open (I haven't tried - timing this myself), but basically the common case, which we - want to optimize, is the one in which we call - RCS_parsercsfile_i and not RCS_reparsercsfile (for example, - "cvs update" on a lot of files most of which are unmodified). - So making the case in which we call RCS_reparsercsfile fast is - not as important. */ - - if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) + the rest of the header and the deltas. */ + + rcsbuf_open (&rcsbuf, fp, rcsfile, 0); + + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) goto l_error; - if (strcmp (key, RCSDESC) == 0) + if (STREQ (key, RCSDESC)) goto l_error; - if (strcmp (RCSHEAD, key) == 0 && value != NULL) - rdata->head = xstrdup (value); + if (STREQ (RCSHEAD, key) && value != NULL) + rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); - if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) goto l_error; - if (strcmp (key, RCSDESC) == 0) + if (STREQ (key, RCSDESC)) goto l_error; - if (strcmp (RCSBRANCH, key) == 0 && value != NULL) + if (STREQ (RCSBRANCH, key) && value != NULL) { char *cp; - rdata->branch = xstrdup (value); + rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); if ((numdots (rdata->branch) & 1) != 0) { /* turn it into a branch if it's a revision */ @@ -293,23 +348,43 @@ RCS_parsercsfile_i (fp, rcsfile) } } - rdata->flags |= PARTIAL; - return rdata; - -l_error: - if (!really_quiet) + /* Look ahead for expand, stopping when we see desc or a revision + number. */ + while (1) { - if (ferror(fp)) - { - error (1, 0, "error reading `%s'", rcsfile); - } - else + char *cp; + + if (STREQ (RCSEXPAND, key)) { - error (0, 0, "`%s' does not appear to be a valid rcs file", - rcsfile); + rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); + break; } + + for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) + /* do nothing */ ; + if (*cp == '\0') + break; + + if (STREQ (RCSDESC, key)) + break; + + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + break; } + + rdata->flags |= PARTIAL; + + rcsbuf_cache (rdata, &rcsbuf); + + return rdata; + +l_error: + error (0, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); + rcsbuf_close (&rcsbuf); freercsnode (&rdata); + fclose (fp); return (NULL); } @@ -321,25 +396,24 @@ l_error: If PFP is NULL, close the file when done. Otherwise, leave it open and store the FILE * in *PFP. */ void -RCS_reparsercsfile (rdata, pfp) +RCS_reparsercsfile (rdata, pfp, rcsbufp) RCSNode *rdata; FILE **pfp; + struct rcsbuffer *rcsbufp; { FILE *fp; char *rcsfile; - + struct rcsbuffer rcsbuf; Node *q, *kv; RCSVers *vnode; - long fpos; + int gotkey; char *cp; char *key, *value; assert (rdata != NULL); rcsfile = rdata->path; - fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ); - if (fp == NULL) - error (1, 0, "unable to reopen `%s'", rcsfile); + rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf); /* make a node */ /* This probably shouldn't be done until later: if a file has an @@ -351,66 +425,68 @@ RCS_reparsercsfile (rdata, pfp) * process all the special header information, break out when we get to * the first revision delta */ + gotkey = 0; for (;;) { - fpos = ftell (fp); - /* get the next key/value pair */ - - /* if key is NULL here, then the file is missing some headers - or we had trouble reading the file. */ - if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) + if (!gotkey) { - if (ferror(fp)) - { - error (1, 0, "error reading `%s'", rcsfile); - } - else + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) { error (1, 0, "`%s' does not appear to be a valid rcs file", rcsfile); } } - /* Skip head and branch tags; we already have them. */ - if (strcmp (key, RCSHEAD) == 0 || strcmp (key, RCSBRANCH) == 0) + gotkey = 0; + + /* Skip head, branch and expand tags; we already have them. */ + if (STREQ (key, RCSHEAD) + || STREQ (key, RCSBRANCH) + || STREQ (key, RCSEXPAND)) + { continue; + } - if (strcmp (key, "access") == 0) + if (STREQ (key, "access")) { if (value != NULL) - rdata->access = xstrdup (value); + { + /* We pass the POLISH parameter as 1 because + RCS_addaccess expects nothing but spaces. FIXME: + It would be easy and more efficient to change + RCS_addaccess. */ + rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, + (size_t *) NULL); + } continue; } /* We always save lock information, so that we can handle -kkvl correctly when checking out a file. */ - if (strcmp (key, "locks") == 0) + if (STREQ (key, "locks")) { if (value != NULL) - rdata->locks_data = xstrdup (value); - fpos = ftell (fp); - if (getrcskey (fp, &key, &value, NULL) >= 0 && - strcmp (key, "strict") == 0 && - value == NULL) + rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); + if (! rcsbuf_getkey (&rcsbuf, &key, &value)) + { + error (1, 0, "premature end of file reading %s", rcsfile); + } + if (STREQ (key, "strict") && value == NULL) { rdata->strict_locks = 1; } else - (void) fseek (fp, fpos, SEEK_SET); + gotkey = 1; continue; } - if (strcmp (RCSSYMBOLS, key) == 0) + if (STREQ (RCSSYMBOLS, key)) { if (value != NULL) - rdata->symbols_data = xstrdup(value); - continue; - } - - if (strcmp (RCSEXPAND, key) == 0) - { - rdata->expand = xstrdup (value); + rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); continue; } @@ -421,15 +497,19 @@ RCS_reparsercsfile (rdata, pfp) */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; - if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK + since we know exactly what to expect. */ + if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0) break; - if (strcmp (key, RCSDESC) == 0) + if (STREQ (key, RCSDESC)) break; - if (strcmp (key, "comment") == 0) + if (STREQ (key, "comment")) { - rdata->comment = xstrdup (value); + rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, + (size_t *) NULL); continue; } if (rdata->other == NULL) @@ -437,7 +517,7 @@ RCS_reparsercsfile (rdata, pfp) kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = xstrdup (value); + kv->data = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL); if (addnode (rdata->other, kv) != 0) { error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", @@ -448,15 +528,11 @@ RCS_reparsercsfile (rdata, pfp) /* if we haven't grabbed it yet, we didn't want it */ } - /* - * we got out of the loop, so we have the first part of the first - * revision delta in our hand key=the revision and value=the date key and - * its value - */ - /* First, seek back to the start of the delta block. */ - (void) fseek (fp, fpos, SEEK_SET); + /* We got out of the loop, so we have the first part of the first + revision delta in KEY (the revision) and VALUE (the date key + and its value). This is what getdelta expects to receive. */ - while ((vnode = getdelta (fp, rcsfile)) != NULL) + while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL) { /* get the node */ q = getnode (); @@ -476,8 +552,9 @@ RCS_reparsercsfile (rdata, pfp) } } - (void) getrcskey (fp, &key, &value, NULL); - if (key != NULL && strcmp (key, RCSDESC) == 0) + /* Here KEY and VALUE are whatever caused getdelta to return NULL. */ + + if (STREQ (key, RCSDESC)) { if (rdata->desc != NULL) { @@ -486,19 +563,17 @@ RCS_reparsercsfile (rdata, pfp) key, rcsfile); free (rdata->desc); } - rdata->desc = xstrdup (value); + rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); } - rdata->delta_pos = ftell (fp); + rdata->delta_pos = rcsbuf_ftell (&rcsbuf); if (pfp == NULL) - { - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", rcsfile); - } + rcsbuf_cache (rdata, &rcsbuf); else { *pfp = fp; + *rcsbufp = rcsbuf; } rdata->flags &= ~PARTIAL; } @@ -520,31 +595,21 @@ RCS_fully_parse (rcs) RCSNode *rcs; { FILE *fp; + struct rcsbuffer rcsbuf; - RCS_reparsercsfile (rcs, &fp); + RCS_reparsercsfile (rcs, &fp, &rcsbuf); while (1) { - int c; char *key, *value; - size_t vallen; Node *vers; RCSVers *vnode; /* Rather than try to keep track of how much information we have read, just read to the end of the file. */ - do - { - c = getc (fp); - if (c == EOF) - break; - } while (whitespace (c)); - if (c == EOF) + if (! rcsbuf_getrevnum (&rcsbuf, &key)) break; - if (ungetc (c, fp) == EOF) - error (1, errno, "ungetc failed"); - getrcsrev (fp, &key); vers = findnode (rcs->versions, key); if (vers == NULL) error (1, 0, @@ -553,9 +618,9 @@ RCS_fully_parse (rcs) vnode = (RCSVers *) vers->data; - while (getrcskey (fp, &key, &value, &vallen) >= 0) + while (rcsbuf_getkey (&rcsbuf, &key, &value)) { - if (strcmp (key, "text") != 0) + if (! STREQ (key, "text")) { Node *kv; @@ -564,7 +629,7 @@ RCS_fully_parse (rcs) kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = xstrdup (value); + kv->data = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL); if (addnode (vnode->other, kv) != 0) { error (0, 0, @@ -577,7 +642,7 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", continue; } - if (strcmp (vnode->version, rcs->head) != 0) + if (! STREQ (vnode->version, rcs->head)) { unsigned long add, del; char buf[50]; @@ -589,8 +654,10 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", del = 0; if (value != NULL) { + size_t vallen; const char *cp; + rcsbuf_valpolish (&rcsbuf, value, 0, &vallen); cp = value; while (cp < value + vallen) { @@ -670,8 +737,7 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'", } } - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", rcs->path); + rcsbuf_cache (rcs, &rcsbuf); } /* @@ -766,353 +832,877 @@ rcsvers_delproc (p) { free_rcsvers_contents ((RCSVers *) p->data); } + +/* These functions retrieve keys and values from an RCS file using a + buffer. We use this somewhat complex approach because it turns out + that for many common operations, CVS spends most of its time + reading keys, so it's worth doing some fairly hairy optimization. */ -/* - * getrcskey - fill in the key and value from the rcs file the algorithm is - * as follows - * - * o skip whitespace - * o fill in key with everything up to next white - * space or semicolon - * o if key == "desc" then key and data are NULL and return -1 - * o if key wasn't terminated by a semicolon, skip white space and fill - * in value with everything up to a semicolon - * o compress all whitespace down to a single space - * o if a word starts with @, do funky rcs processing - * o strip whitespace off end of value or set value to NULL if it empty - * o return 0 since we found something besides "desc" - * - * Sets *KEYP and *VALUEP to point to storage managed by the getrcskey - * function; the contents are only valid until the next call to - * getrcskey or getrcsrev. If LENP is not NULL, this sets *LENP to - * the length of *VALUEP; this is needed if the string might contain - * binary data. - */ +/* The number of bytes we try to read each time we need more data. */ -static char *key = NULL; -static char *value = NULL; -static size_t keysize = 0; -static size_t valsize = 0; +#define RCSBUF_BUFSIZE (8192) -static int -getrcskey (fp, keyp, valp, lenp) +/* The buffer we use to store data. This grows as needed. */ + +static char *rcsbuf_buffer = NULL; +static size_t rcsbuf_buffer_size = 0; + +/* Whether rcsbuf_buffer is in use. This is used as a sanity check. */ + +static int rcsbuf_inuse; + +/* Set up to start gathering keys and values from an RCS file. This + initializes RCSBUF. */ + +static void +rcsbuf_open (rcsbuf, fp, filename, pos) + struct rcsbuffer *rcsbuf; FILE *fp; + const char *filename; + unsigned long pos; +{ + if (rcsbuf_inuse) + error (1, 0, "rcsbuf_open: internal error"); + rcsbuf_inuse = 1; + + if (rcsbuf_buffer_size < RCSBUF_BUFSIZE) + expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE); + + rcsbuf->ptr = rcsbuf_buffer; + rcsbuf->ptrend = rcsbuf_buffer; + rcsbuf->fp = fp; + rcsbuf->filename = filename; + rcsbuf->pos = pos; + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; +} + +/* Stop gathering keys from an RCS file. */ + +static void +rcsbuf_close (rcsbuf) + struct rcsbuffer *rcsbuf; +{ + if (! rcsbuf_inuse) + error (1, 0, "rcsbuf_close: internal error"); + rcsbuf_inuse = 0; +} + +/* Read a key/value pair from an RCS file. This sets *KEYP to point + to the key, and *VALUEP to point to the value. A missing or empty + value is indicated by setting *VALUEP to NULL. + + This function returns 1 on success, or 0 on EOF. If there is an + error reading the file, or an EOF in an unexpected location, it + gives a fatal error. + + This sets *KEYP and *VALUEP to point to storage managed by + rcsbuf_getkey. Moreover, *VALUEP has not been massaged from the + RCS format: it may contain embedded whitespace and embedded '@' + characters. Call rcsbuf_valcopy or rcsbuf_valpolish to do + appropriate massaging. */ + +static int +rcsbuf_getkey (rcsbuf, keyp, valp) + struct rcsbuffer *rcsbuf; char **keyp; char **valp; - size_t *lenp; { - char *cur, *max; - int c; - int just_string; + register const char * const my_spacetab = spacetab; + register char *ptr, *ptrend; + char c; - if (lenp != NULL) - *lenp = 0; +#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) - /* skip leading whitespace */ - do + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + /* Sanity check. */ + if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size) + abort (); + + /* If the pointer is more than RCSBUF_BUFSIZE bytes into the + buffer, move back to the start of the buffer. This keeps the + buffer from growing indefinitely. */ + if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) { - c = getc (fp); - if (c == EOF) - { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); - } - } while (whitespace (c)); + int len; + + len = ptrend - ptr; - /* fill in key */ - cur = key; - max = key + keysize; - while (!whitespace (c) && c != ';') + /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes + at a time, so we can't have more bytes than that past PTR. */ + if (len > RCSBUF_BUFSIZE) + abort (); + + /* Update the POS field, which holds the file offset of the + first byte in the RCSBUF_BUFFER buffer. */ + rcsbuf->pos += ptr - rcsbuf_buffer; + + memcpy (rcsbuf_buffer, ptr, len); + ptr = rcsbuf_buffer; + ptrend = ptr + len; + rcsbuf->ptrend = ptrend; + } + + /* Skip leading whitespace. */ + + while (1) { - if (cur >= max) + if (ptr >= ptrend) { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + return 0; + ptrend = rcsbuf->ptrend; } - *cur++ = c; - c = getc (fp); - if (c == EOF) + c = *ptr; + if (! my_whitespace (c)) + break; + + ++ptr; + } + + /* We've found the start of the key. */ + + *keyp = ptr; + + if (c != ';') + { + while (1) { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); + ++ptr; + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); + if (ptr == NULL) + error (1, 0, "EOF in key in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + c = *ptr; + if (c == ';' || my_whitespace (c)) + break; } } - if (cur >= max) + + /* Here *KEYP points to the key in the buffer, C is the character + we found at the of the key, and PTR points to the location in + the buffer where we found C. We must set *PTR to \0 in order + to terminate the key. If the key ended with ';', then there is + no value. */ + + *ptr = '\0'; + ++ptr; + + if (c == ';') { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + *valp = NULL; + rcsbuf->ptr = ptr; + return 1; } - *cur = '\0'; - /* skip whitespace between key and val */ - while (whitespace (c)) + /* C must be whitespace. Skip whitespace between the key and the + value. If we find ';' now, there is no value. */ + + while (1) { - c = getc (fp); - if (c == EOF) + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL); + if (ptr == NULL) + error (1, 0, "EOF while looking for value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + c = *ptr; + if (c == ';') { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); + *valp = NULL; + rcsbuf->ptr = ptr + 1; + return 1; } - } + if (! my_whitespace (c)) + break; + ++ptr; + } - /* if we ended key with a semicolon, there is no value */ - if (c == ';') + /* Now PTR points to the start of the value, and C is the first + character of the value. */ + + if (c != '@') + *valp = ptr; + else { - *keyp = key; - *valp = (char *) NULL; - return (0); - } + char *pat; + size_t vlen; - /* otherwise, there might be a value, so fill it in */ - cur = value; - max = value + valsize; + /* Optimize the common case of a value composed of a single + '@' string. */ - just_string = (strcmp (key, RCSDESC) == 0 - || strcmp (key, "text") == 0 - || strcmp (key, "log") == 0); + rcsbuf->at_string = 1; - /* process the value */ - for (;;) - { - /* handle RCS "strings" */ - if (c == '@') + ++ptr; + + *valp = ptr; + + while (1) { - for (;;) + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) { - c = getc (fp); - if (c == EOF) - { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); - } + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to + the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } - if (c == '@') + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + /* Note that we pass PAT, not PTR, here. */ + pat = rcsbuf_fill (rcsbuf, pat, keyp, valp); + if (pat == NULL) { - c = getc (fp); - if (c == EOF) - { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); - } - - if (c != '@') - break; - } + /* EOF here is OK; it just means that the last + character of the file was an '@' terminating a + value for a key type which does not require a + trailing ';'. */ + pat = rcsbuf->ptrend - 1; - if (cur >= max) - { - size_t curoff = cur - value; - expand_string (&value, &valsize, valsize + 1); - cur = value + curoff; - max = value + valsize; } - *cur++ = c; + ptrend = rcsbuf->ptrend; + + /* Note that the value of PTR is bogus here. This is + OK, because we don't use it. */ } + + if (pat + 1 >= ptrend || pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ++rcsbuf->embedded_at; + ptr = pat + 2; } - /* The syntax for some key-value pairs is different; they - don't end with a semicolon. */ - if (just_string) - break; + /* Here PAT points to the final '@' in the string. */ - /* compress whitespace down to a single space */ - if (whitespace (c)) + *pat = '\0'; + + vlen = pat - *valp; + if (vlen == 0) + *valp = NULL; + rcsbuf->vlen = vlen; + + ptr = pat + 1; + } + + /* Certain keywords only have a '@' string. If there is no '@' + string, then the old getrcskey function assumed that they had + no value, and we do the same. */ + + { + char *k; + + k = *keyp; + if (STREQ (k, RCSDESC) + || STREQ (k, "text") + || STREQ (k, "log")) { - do { - c = getc (fp); - if (c == EOF) - { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); - } - } while (whitespace (c)); + if (c != '@') + *valp = NULL; + rcsbuf->ptr = ptr; + return 1; + } + } + + /* If we've already gathered a '@' string, try to skip whitespace + and find a ';'. */ + if (c == '@') + { + while (1) + { + char n; - /* Do not include any trailing whitespace in the value. */ - if (c != ';') + if (ptr >= ptrend) { - if (cur >= max) - { - size_t curoff = cur - value; - expand_string (&value, &valsize, valsize + 1); - cur = value + curoff; - max = value + valsize; - } - *cur++ = ' '; + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + n = *ptr; + if (n == ';') + { + /* We're done. We already set everything up for this + case above. */ + rcsbuf->ptr = ptr + 1; + return 1; } + if (! my_whitespace (n)) + break; + ++ptr; } - /* if we got a semi-colon we are done with the entire value */ - if (c == ';') - break; + /* The value extends past the '@' string. We need to undo the + closing of the '@' done in the default case above. This + case never happens in a plain RCS file, but it can happen + if user defined phrases are used. */ + if (rcsbuf->vlen != 0) + (*valp)[rcsbuf->vlen] = ' '; + else + *valp = ptr; + } + + /* Here we have a value which is not a simple '@' string. We need + to gather up everything until the next ';', including any '@' + strings. *VALP points to the start of the value. If + RCSBUF->VLEN is not zero, then we have already read an '@' + string, and PTR points to the data following the '@' string. + Otherwise, PTR points to the start of the value. */ + + while (1) + { + char *start, *psemi, *pat; + + /* Find the ';' which must end the value. */ + start = ptr; + while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL) + { + int slen; + + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to the + location corresponding to the old PTREND, so that we + don't search the same bytes again. */ + slen = start - *valp; + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", rcsbuf->filename); + start = *valp + slen; + ptrend = rcsbuf->ptrend; + } + + /* See if there are any '@' strings in the value. */ + pat = memchr (start, '@', psemi - start); + + if (pat == NULL) + { + size_t vlen; + + /* We're done with the value. Trim any trailing + whitespace. */ + + rcsbuf->ptr = psemi + 1; + + start = *valp; + while (psemi > start && my_whitespace (psemi[-1])) + --psemi; + *psemi = '\0'; + + vlen = psemi - start; + if (vlen == 0) + *valp = NULL; + rcsbuf->vlen = vlen; - if (cur >= max) + return 1; + } + + /* We found an '@' string in the value. We set + RCSBUF->AT_STRING, which means that we won't be able to + compress whitespace correctly for this type of value. + Since this type of value never arises in a normal RCS file, + this should not be a big deal. It means that if anybody + adds a phrase which can have both an '@' string and regular + text, they will have to handle whitespace compression + themselves. */ + + rcsbuf->at_string = 1; + + *pat = ' '; + + ptr = pat + 1; + + while (1) { - size_t curoff = cur - value; - expand_string (&value, &valsize, valsize + 1); - cur = value + curoff; - max = value + valsize; + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) + { + /* Note that we pass PTREND as the PTR value to + rcsbuff_fill, so that we will wind up setting PTR + to the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp); + if (ptr == NULL) + error (1, 0, "EOF in value in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + if (pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ++rcsbuf->embedded_at; + ptr = pat + 2; } - *cur++ = c; - c = getc (fp); - if (c == EOF) + /* Here PAT points to the final '@' in the string. */ + + *pat = ' '; + + ptr = pat + 1; + } + +#undef my_whitespace +} + +/* Read an RCS revision number from an RCS file. This sets *REVP to + point to the revision number; it will point to space that is + managed by the rcsbuf functions, and is only good until the next + call to rcsbuf_getkey or rcsbuf_getrevnum. + + This function returns 1 on success, or 0 on EOF. If there is an + error reading the file, or an EOF in an unexpected location, it + gives a fatal error. */ + +static int +rcsbuf_getrevnum (rcsbuf, revp) + struct rcsbuffer *rcsbuf; + char **revp; +{ + char *ptr, *ptrend; + char c; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + *revp = NULL; + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + return 0; + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! whitespace (c)) + break; + + ++ptr; + } + + if (! isdigit (c) && c != '.') + error (1, 0, + "unexpected `%c' reading revision number in RCS file %s", + c, rcsbuf->filename); + + *revp = ptr; + + do + { + ++ptr; + if (ptr >= ptrend) { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); + ptr = rcsbuf_fill (rcsbuf, ptr, revp, (char **) NULL); + if (ptr == NULL) + error (1, 0, + "unexpected EOF reading revision number in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; } + + c = *ptr; + } + while (isdigit (c) || c == '.'); + + if (! whitespace (c)) + error (1, 0, "unexpected `%c' reading revision number in RCS file %s", + c, rcsbuf->filename); + + *ptr = '\0'; + + rcsbuf->ptr = ptr + 1; + + return 1; +} + +/* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF, + updating PTR and the PTREND field. If KEYP and *KEYP are not NULL, + then *KEYP points into the buffer, and must be adjusted if the + buffer is changed. Likewise for VALP. Returns the new value of + PTR, or NULL on error. */ + +static char * +rcsbuf_fill (rcsbuf, ptr, keyp, valp) + struct rcsbuffer *rcsbuf; + char *ptr; + char **keyp; + char **valp; +{ + int got; + + if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size) + { + int poff, peoff, koff, voff; + + poff = ptr - rcsbuf_buffer; + peoff = rcsbuf->ptrend - rcsbuf_buffer; + if (keyp != NULL && *keyp != NULL) + koff = *keyp - rcsbuf_buffer; + if (valp != NULL && *valp != NULL) + voff = *valp - rcsbuf_buffer; + + expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, + rcsbuf_buffer_size + RCSBUF_BUFSIZE); + + ptr = rcsbuf_buffer + poff; + rcsbuf->ptrend = rcsbuf_buffer + peoff; + if (keyp != NULL && *keyp != NULL) + *keyp = rcsbuf_buffer + koff; + if (valp != NULL && *valp != NULL) + *valp = rcsbuf_buffer + voff; } - /* terminate the string */ - if (cur >= max) + got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp); + if (got == 0) { - size_t curoff = cur - value; - expand_string (&value, &valsize, valsize + 1); - cur = value + curoff; - max = value + valsize; + if (ferror (rcsbuf->fp)) + error (1, errno, "cannot read %s", rcsbuf->filename); + return NULL; } - *cur = '\0'; - /* if the string is empty, make it null */ - if (value && cur != value) + rcsbuf->ptrend += got; + + return ptr; +} + +/* Copy the value VAL returned by rcsbuf_getkey into a memory buffer, + returning the memory buffer. Polish the value like + rcsbuf_valpolish, q.v. */ + +static char * +rcsbuf_valcopy (rcsbuf, val, polish, lenp) + struct rcsbuffer *rcsbuf; + char *val; + int polish; + size_t *lenp; +{ + size_t vlen; + int embedded_at; + char *ret; + + if (val == NULL) { - *valp = value; if (lenp != NULL) - *lenp = cur - value; + *lenp = 0; + return NULL; } - else - *valp = NULL; - *keyp = key; - return (0); + + vlen = rcsbuf->vlen; + embedded_at = rcsbuf->embedded_at; + + ret = xmalloc (vlen - embedded_at + 1); + + if (rcsbuf->at_string ? embedded_at == 0 : ! polish) + { + /* No special action to take. */ + memcpy (ret, val, vlen + 1); + if (lenp != NULL) + *lenp = vlen; + return ret; + } + + rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp); + return ret; } -/* Read an RCS revision number from FP. Put a pointer to it in *REVP; - it points to space managed by getrcsrev which is only good until - the next call to getrcskey or getrcsrev. */ +/* Polish the value VAL returned by rcsbuf_getkey. The POLISH + parameter is non-zero if multiple embedded whitespace characters + should be compressed into a single whitespace character. Note that + leading and trailing whitespace was already removed by + rcsbuf_getkey. Within an '@' string, pairs of '@' characters are + compressed into a single '@' character regardless of the value of + POLISH. If LENP is not NULL, set *LENP to the length of the value. */ + static void -getrcsrev (fp, revp) - FILE *fp; - char **revp; +rcsbuf_valpolish (rcsbuf, val, polish, lenp) + struct rcsbuffer *rcsbuf; + char *val; + int polish; + size_t *lenp; { - char *cur; - char *max; - int c; + if (val == NULL) + { + if (lenp != NULL) + *lenp= 0; + return; + } - do { - c = getc (fp); - if (c == EOF) + if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish) + { + /* No special action to take. */ + if (lenp != NULL) + *lenp = rcsbuf->vlen; + return; + } + + rcsbuf_valpolish_internal (rcsbuf, val, val, lenp); +} + +/* Internal polishing routine, called from rcsbuf_valcopy and + rcsbuf_valpolish. */ + +static void +rcsbuf_valpolish_internal (rcsbuf, to, from, lenp) + struct rcsbuffer *rcsbuf; + char *to; + const char *from; + size_t *lenp; +{ + size_t len; + + len = rcsbuf->vlen; + + if (! rcsbuf->at_string) + { + char *orig_to; + size_t clen; + + orig_to = to; + + for (clen = len; clen > 0; ++from, --clen) { - /* FIXME: should be including filename in error message. */ - if (ferror (fp)) - error (1, errno, "cannot read rcs file"); - else - error (1, 0, "unexpected end of file reading rcs file"); + char c; + + c = *from; + if (whitespace (c)) + { + /* Note that we know that clen can not drop to zero + while we have whitespace, because we know there is + no trailing whitespace. */ + while (whitespace (from[1])) + { + ++from; + --clen; + } + c = ' '; + } + *to++ = c; } - } while (whitespace (c)); - if (!(isdigit (c) || c == '.')) - /* FIXME: should be including filename in error message. */ - error (1, 0, "error reading rcs file; revision number expected"); + *to = '\0'; - cur = key; - max = key + keysize; - while (isdigit (c) || c == '.') + if (lenp != NULL) + *lenp = to - orig_to; + } + else { - if (cur >= max) + const char *orig_from; + char *orig_to; + int embedded_at; + size_t clen; + + orig_from = from; + orig_to = to; + + embedded_at = rcsbuf->embedded_at; + + if (lenp != NULL) + *lenp = len - embedded_at; + + for (clen = len; clen > 0; ++from, --clen) { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + char c; + + c = *from; + *to++ = c; + if (c == '@') + { + ++from; + + /* Sanity check. */ + if (*from != '@' || clen == 0) + abort (); + + --clen; + + --embedded_at; + if (embedded_at == 0) + { + /* We've found all the embedded '@' characters. + We can just memcpy the rest of the buffer after + this '@' character. */ + if (orig_to != orig_from) + memcpy (to, from + 1, clen - 1); + else + memmove (to, from + 1, clen - 1); + from += clen; + to += clen - 1; + break; + } + } } - *cur++ = c; - c = getc (fp); - if (c == EOF) + /* Sanity check. */ + if (from != orig_from + len + || to != orig_to + (len - rcsbuf->embedded_at)) { - /* FIXME: should be including filename in error message. */ - if (ferror (fp)) - error (1, errno, "cannot read rcs file"); - else - error (1, 0, "unexpected end of file reading rcs file"); + abort (); } - } - if (cur >= max) - { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + *to = '\0'; } - *cur = '\0'; - *revp = key; } -/* Like getrcsrev, but don't die on error. Return the last character - read (last call to getc, which may be EOF). TODO: implement getrcsrev - in terms of this function. */ -static int -getrevnum (fp, revp) - FILE *fp; - char **revp; +/* Return the current position of an rcsbuf. */ + +static unsigned long +rcsbuf_ftell (rcsbuf) + struct rcsbuffer *rcsbuf; { - char *cur; - char *max; - int c; + return rcsbuf->pos + (rcsbuf->ptr - rcsbuf_buffer); +} - *revp = NULL; - do { - c = getc (fp); - if (c == EOF) - return c; - } while (whitespace (c)); +/* Return a pointer to any data buffered for RCSBUF, along with the + length. */ + +static void +rcsbuf_get_buffered (rcsbuf, datap, lenp) + struct rcsbuffer *rcsbuf; + char **datap; + size_t *lenp; +{ + *datap = rcsbuf->ptr; + *lenp = rcsbuf->ptrend - rcsbuf->ptr; +} + +/* CVS optimizes by quickly reading some header information from a + file. If it decides it needs to do more with the file, it reopens + it. We speed that up here by maintaining a cache of a single open + file, to save the time it takes to reopen the file in the common + case. */ + +static RCSNode *cached_rcs; +static struct rcsbuffer cached_rcsbuf; - if (!(isdigit (c) || c == '.')) - return c; +/* Cache RCS and RCSBUF. This takes responsibility for closing + RCSBUF->FP. */ - cur = key; - max = key + keysize; - while (isdigit (c) || c == '.') +static void +rcsbuf_cache (rcs, rcsbuf) + RCSNode *rcs; + struct rcsbuffer *rcsbuf; +{ + if (cached_rcs != NULL) + rcsbuf_cache_close (); + cached_rcs = rcs; + ++rcs->refcount; + cached_rcsbuf = *rcsbuf; +} + +/* If there is anything in the cache, close it. */ + +static void +rcsbuf_cache_close () +{ + if (cached_rcs != NULL) { - if (cur >= max) + if (fclose (cached_rcsbuf.fp) != 0) + error (0, errno, "cannot close %s", cached_rcsbuf.filename); + rcsbuf_close (&cached_rcsbuf); + freercsnode (&cached_rcs); + cached_rcs = NULL; + } +} + +/* Open an rcsbuffer for RCS, getting it from the cache if possible. + Set *FPP to the file, and *RCSBUFP to the rcsbuf. The file should + be put at position POS. */ + +static void +rcsbuf_cache_open (rcs, pos, pfp, prcsbuf) + RCSNode *rcs; + long pos; + FILE **pfp; + struct rcsbuffer *prcsbuf; +{ + if (cached_rcs == rcs) + { + if (rcsbuf_ftell (&cached_rcsbuf) != pos) { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + if (fseek (cached_rcsbuf.fp, pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file %s", + cached_rcsbuf.filename); + cached_rcsbuf.ptr = rcsbuf_buffer; + cached_rcsbuf.ptrend = rcsbuf_buffer; + cached_rcsbuf.pos = pos; } - *cur = c; + *pfp = cached_rcsbuf.fp; - c = getc (fp); - if (c == EOF) - break; - cur++; - } + /* When RCS_parse opens a file using fopen_case, it frees the + filename which we cached in CACHED_RCSBUF and stores a new + file name in RCS->PATH. We avoid problems here by always + copying the filename over. FIXME: This is hackish. */ + cached_rcsbuf.filename = rcs->path; + + *prcsbuf = cached_rcsbuf; + + cached_rcs = NULL; - if (cur >= max) + /* Removing RCS from the cache removes a reference to it. */ + --rcs->refcount; + if (rcs->refcount <= 0) + error (1, 0, "rcsbuf_cache_open: internal error"); + } + else { - size_t curoff = cur - key; - expand_string (&key, &keysize, keysize + 1); - cur = key + curoff; - max = key + keysize; + if (cached_rcs != NULL) + rcsbuf_cache_close (); + + *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (*pfp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (pos != 0) + { + if (fseek (*pfp, pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file %s", rcs->path); + } + rcsbuf_open (prcsbuf, *pfp, rcs->path, pos); } - *cur = '\0'; - *revp = key; - return c; } + /* * process the symbols list of the rcs file */ @@ -1312,10 +1902,10 @@ RCS_gettag (rcs, symtag, force_tag_match, simple_tag) /* XXX this is probably not necessary, --jtc */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* If tag is "HEAD", special case to get head RCS revision */ - if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0')) + if (tag && (STREQ (tag, TAG_HEAD) || *tag == '\0')) #if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */ if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC)) return ((char *) NULL); /* head request for removed file */ @@ -1501,7 +2091,7 @@ checkmagic_proc (p, closure) Node *p; void *closure; { - if (strcmp (check_rev, p->data) == 0) + if (STREQ (check_rev, p->data)) return (1); else return (0); @@ -1655,7 +2245,7 @@ RCS_getbranch (rcs, tag, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* find out if the tag contains a dot, or is on the trunk */ cp = strrchr (tag, '.'); @@ -1873,7 +2463,7 @@ RCS_getdate (rcs, date, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* if the head is on a branch, try the branch first */ if (rcs->branch != NULL) @@ -1911,7 +2501,7 @@ RCS_getdate (rcs, date, force_tag_match) */ /* if we found what we're looking for, and it's not 1.1 return it */ - if (cur_rev != NULL && strcmp (cur_rev, "1.1") != 0) + if (cur_rev != NULL && ! STREQ (cur_rev, "1.1")) return (xstrdup (cur_rev)); /* look on the vendor branch */ @@ -1960,7 +2550,7 @@ RCS_getdatebranch (rcs, date, branch) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); p = findnode (rcs->versions, xrev); free (xrev); @@ -2057,7 +2647,7 @@ RCS_getrevtime (rcs, rev, date, fudge) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* look up the revision */ p = findnode (rcs->versions, rev); @@ -2109,7 +2699,7 @@ RCS_getlocks (rcs) assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); if (rcs->locks_data) { rcs->locks = getlist (); @@ -2128,7 +2718,7 @@ RCS_symbols(rcs) assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); if (rcs->symbols_data) { rcs->symbols = getlist (); @@ -2150,7 +2740,7 @@ translate_symtag (rcs, tag) const char *tag; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); if (rcs->symbols != NULL) { @@ -2234,7 +2824,7 @@ RCS_check_kflag (arg) { for (cpp = kflags; *cpp != NULL; cpp++) { - if (strcmp (arg, *cpp) == 0) + if (STREQ (arg, *cpp)) break; } } @@ -2292,7 +2882,7 @@ RCS_isdead (rcs, tag) RCSVers *version; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); p = findnode (rcs->versions, tag); if (p == NULL) @@ -2312,8 +2902,6 @@ RCS_getexpand (rcs) RCSNode *rcs; { assert (rcs != NULL); - if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); return rcs->expand; } @@ -2902,7 +3490,16 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) OPTIONS is a string such as "-kb" or "-kv" for keyword expansion options. It may be NULL to use the default expansion mode of the - file, typically "-kkv". */ + file, typically "-kkv". + + On an error which prevented checking out the file, either print a + nonfatal error and return 1, or give a fatal error. On success, + return 0. */ + +/* This function mimics the behavior of `rcs co' almost exactly. The + chief difference is in its support for preserving file ownership, + permissions, and special files across checkin and checkout -- see + comments in RCS_checkin for some issues about this. -twp */ int RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) @@ -2917,15 +3514,27 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) { int free_rev = 0; enum kflag expand; - FILE *fp; + FILE *fp, *ofp; struct stat sb; + struct rcsbuffer rcsbuf; char *key; char *value; size_t len; int free_value = 0; char *log = NULL; size_t loglen; - FILE *ofp; + Node *vp = NULL; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + uid_t rcs_owner; + gid_t rcs_group; + mode_t rcs_mode; + int change_rcs_owner = 0; + int change_rcs_group = 0; + int change_rcs_mode = 0; + int special_file = 0; + unsigned long devnum_long; + dev_t devnum = 0; +#endif if (trace) { @@ -2962,34 +3571,25 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) free_rev = 1; } - if (rev == NULL || strcmp (rev, rcs->head) == 0) + if (rev == NULL || STREQ (rev, rcs->head)) { int gothead; /* We want the head revision. Try to read it directly. */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, &fp); - else - { - fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); - if (fp == NULL) - error (1, 0, "unable to reopen `%s'", rcs->path); - if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) - error (1, 0, "cannot fseek RCS file"); - } + RCS_reparsercsfile (rcs, &fp, &rcsbuf); + else + rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf); gothead = 0; - getrcsrev (fp, &key); - while (getrcskey (fp, &key, &value, &len) >= 0) + if (! rcsbuf_getrevnum (&rcsbuf, &key)) + error (1, 0, "unexpected EOF reading %s", rcs->path); + while (rcsbuf_getkey (&rcsbuf, &key, &value)) { - if (strcmp (key, "log") == 0) - { - log = xmalloc (len); - memcpy (log, value, len); - loglen = len; - } - if (strcmp (key, "text") == 0) + if (STREQ (key, "log")) + log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen); + else if (STREQ (key, "text")) { gothead = 1; break; @@ -3004,20 +3604,23 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) return 1; } + rcsbuf_valpolish (&rcsbuf, value, 0, &len); + if (fstat (fileno (fp), &sb) < 0) error (1, errno, "cannot fstat %s", rcs->path); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", rcs->path); + rcsbuf_cache (rcs, &rcsbuf); } else { + struct rcsbuffer *rcsbufp; + /* It isn't the head revision of the trunk. We'll need to walk through the deltas. */ fp = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, &fp); + RCS_reparsercsfile (rcs, &fp, &rcsbuf); if (fp == NULL) { @@ -3025,14 +3628,17 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) here too. Probably should change it thusly.... */ if (stat (rcs->path, &sb) < 0) error (1, errno, "cannot stat %s", rcs->path); + rcsbufp = NULL; } else { if (fstat (fileno (fp), &sb) < 0) error (1, errno, "cannot fstat %s", rcs->path); + rcsbufp = &rcsbuf; } - RCS_deltas (rcs, fp, rev, RCS_FETCH, &value, &len, &log, &loglen); + RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len, + &log, &loglen); free_value = 1; } @@ -3055,7 +3661,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) ouroptions = rcs->expand; for (cpp = kflags; *cpp != NULL; cpp++) - if (strcmp (*cpp, ouroptions) == 0) + if (STREQ (*cpp, ouroptions)) break; if (*cpp != NULL) @@ -3069,17 +3675,186 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } } - if (expand != KFLAG_O && expand != KFLAG_B) +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Handle special files and permissions, if that is desired. */ + if (preserve_perms) { - Node *p; - char *newvalue; + RCSVers *vers; + Node *info; + struct hardlink_info *hlinfo; - p = findnode (rcs->versions, rev == NULL ? rcs->head : rev); - if (p == NULL) + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) error (1, 0, "internal error: no revision information for %s", rev == NULL ? rcs->head : rev); + vers = (RCSVers *) vp->data; + + /* First we look for symlinks, which are simplest to handle. */ + info = findnode (vers->other_delta, "symlink"); + if (info != NULL) + { + char *dest; + + if (pfn != NULL || (workfile == NULL && sout == RUN_TTY)) + error (1, 0, "symbolic link %s:%s cannot be piped", + rcs->path, vers->version); + if (workfile == NULL) + dest = sout; + else + dest = workfile; + + /* Remove `dest', just in case. It's okay to get ENOENT here, + since we just want the file not to be there. (TODO: decide + whether it should be considered an error for `dest' to exist + at this point. If so, the unlink call should be removed and + `symlink' should signal the error. -twp) */ + if (unlink (dest) < 0 && existence_error (errno)) + error (1, errno, "cannot remove %s", dest); + if (symlink (info->data, dest) < 0) + error (1, errno, "cannot create symbolic link from %s to %s", + dest, info->data); + if (free_value) + free (value); + if (free_rev) + free (rev); + return 0; + } + + /* Next, we look at this file's hardlinks field, and see whether + it is linked to any other file that has been checked out. + If so, we don't do anything else -- just link it to that file. + + If we are checking out a file to a pipe or temporary storage, + none of this should matter. Hence the `workfile != NULL' + wrapper around the whole thing. -twp */ + + if (workfile != NULL) + { + info = findnode (vers->other_delta, "hardlinks"); + if (info != NULL) + { + char *links = xstrdup (info->data); + char *working_dir = xgetwd(); + char *p, *file = NULL; + Node *n, *uptodate_link; + + /* For each file in the hardlinks field, check to see + if it exists, and if so, if it has been checked out + this iteration. */ + uptodate_link = NULL; + for (p = strtok (links, " "); + p != NULL && uptodate_link == NULL; + p = strtok (NULL, " ")) + { + file = (char *) + xmalloc (sizeof(char) * + (strlen(working_dir) + strlen(p) + 2)); + sprintf (file, "%s/%s", working_dir, p); + n = lookup_file_by_inode (file); + if (n == NULL) + { + if (strcmp (p, workfile) != 0) + { + /* One of the files that WORKFILE should be + linked to is not even in the working directory. + The user should probably be warned. */ + error (0, 0, + "warning: %s should be hardlinked to %s, but is missing", + p, workfile); + } + free (file); + continue; + } + + /* hlinfo may be NULL if, for instance, a file is being + removed. */ + hlinfo = (struct hardlink_info *) n->data; + if (hlinfo && hlinfo->checked_out) + uptodate_link = n; + free (file); + } + free (links); + free (working_dir); - expand_keywords (rcs, (RCSVers *) p->data, nametag, log, loglen, + /* If we've found a file that `workfile' is supposed to be + linked to, and it has been checked out since CVS was + invoked, then simply link workfile to that file. + + If one of these conditions is not met, then we're + checking out workfile to a temp file or stdout, or + workfile is the first one in its hardlink group to be + checked out. Either way we must continue with a full + checkout. */ + + if (uptodate_link != NULL) + { + if (link (uptodate_link->key, workfile) < 0) + error (1, errno, "cannot link %s to %s", + workfile, uptodate_link->key); + hlinfo->checked_out = 1; /* probably unnecessary */ + if (free_value) + free (value); + if (free_rev) + free (rev); + return 0; + } + } + } + + info = findnode (vers->other_delta, "owner"); + if (info != NULL) + { + change_rcs_owner = 1; + rcs_owner = (uid_t) strtoul (info->data, NULL, 10); + } + info = findnode (vers->other_delta, "group"); + if (info != NULL) + { + change_rcs_group = 1; + rcs_group = (gid_t) strtoul (info->data, NULL, 10); + } + info = findnode (vers->other_delta, "permissions"); + if (info != NULL) + { + change_rcs_mode = 1; + rcs_mode = (mode_t) strtoul (info->data, NULL, 8); + } + info = findnode (vers->other_delta, "special"); + if (info != NULL) + { + /* If the size of `devtype' changes, fix the sscanf call also */ + char devtype[16]; + + if (sscanf (info->data, "%16s %lu", + devtype, &devnum_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + workfile, vers->version, info->data); + devnum = devnum_long; + if (strcmp (devtype, "character") == 0) + special_file = S_IFCHR; + else if (strcmp (devtype, "block") == 0) + special_file = S_IFBLK; + else + error (0, 0, "%s is a special file of unsupported type `%s'", + workfile, info->data); + } + } +#endif + + if (expand != KFLAG_O && expand != KFLAG_B) + { + char *newvalue; + + /* Don't fetch the delta node again if we already have it. */ + if (vp == NULL) + { + vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); + if (vp == NULL) + error (1, 0, "internal error: no revision information for %s", + rev == NULL ? rcs->head : rev); + } + + expand_keywords (rcs, (RCSVers *) vp->data, nametag, log, loglen, expand, value, len, &newvalue, &len); if (newvalue != value) @@ -3099,19 +3874,55 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (pfn != NULL) { +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (special_file) + error (1, 0, "special file %s cannot be piped to anything", + rcs->path); +#endif /* The PFN interface is very simple to implement right now, as we always have the entire file in memory. */ if (len != 0) pfn (callerdat, value, len); } +#ifdef PRESERVE_PERMISSIONS_SUPPORT + else if (special_file) + { + char *dest; + + /* Can send either to WORKFILE or to SOUT, as long as SOUT is + not RUN_TTY. */ + dest = workfile; + if (dest == NULL) + { + if (sout == RUN_TTY) + error (1, 0, "special file %s cannot be written to stdout", + rcs->path); + dest = sout; + } + + /* Unlink `dest', just in case. It's okay if this provokes a + ENOENT error. */ + if (unlink (dest) < 0 && existence_error (errno)) + error (1, errno, "cannot remove %s", dest); + if (mknod (dest, special_file, devnum) < 0) + error (1, errno, "could not create special file %s", + dest); + } +#endif else { + /* Not a special file: write to WORKFILE or SOUT. */ if (workfile == NULL) { if (sout == RUN_TTY) ofp = stdout; else { + /* Symbolic links should be removed before replacement, so that + `fopen' doesn't follow the link and open the wrong file. */ + if (islink (sout)) + if (unlink_file (sout) < 0) + error (1, errno, "cannot remove %s", sout); ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w"); if (ofp == NULL) error (1, errno, "cannot open %s", sout); @@ -3119,6 +3930,11 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } else { + /* Output is supposed to go to WORKFILE, so we should open that + file. Symbolic links should be removed first (see above). */ + if (islink (workfile)) + if (unlink_file (workfile) < 0) + error (1, errno, "cannot remove %s", workfile); ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); if (ofp == NULL) error (1, errno, "cannot open %s", workfile); @@ -3163,22 +3979,56 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) nstep = nleft; } } + } - if (workfile != NULL) + if (workfile != NULL) + { + int ret; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (!special_file && fclose (ofp) < 0) + error (1, errno, "cannot close %s", workfile); + + if (change_rcs_owner || change_rcs_group) { - if (fclose (ofp) < 0) - error (1, errno, "cannot close %s", workfile); - if (chmod (workfile, - sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) - error (0, errno, "cannot change mode of file %s", + if (chown (workfile, rcs_owner, rcs_group) < 0) + error (0, errno, "could not change file ownership on %s", workfile); } - else if (sout != RUN_TTY) + + ret = chmod (workfile, + change_rcs_mode + ? rcs_mode + : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); +#else + if (fclose (ofp) < 0) + error (1, errno, "cannot close %s", workfile); + + ret = chmod (workfile, + sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); +#endif + if (ret < 0) { - if (fclose (ofp) < 0) - error (1, errno, "cannot close %s", sout); + error (0, errno, "cannot change mode of file %s", + workfile); } } + else if (sout != RUN_TTY) + { + if ( +#ifdef PRESERVE_PERMISSIONS_SUPPORT + !special_file && +#endif + fclose (ofp) < 0) + error (1, errno, "cannot close %s", sout); + } + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If we are in the business of preserving hardlinks, then + mark this file as having been checked out. */ + if (preserve_perms && workfile != NULL) + update_hardlink_info (workfile); +#endif if (free_value) free (value); @@ -3227,7 +4077,7 @@ RCS_findlock_or_tip (rcs) lock = NULL; for (p = locklist->list->next; p != locklist->list; p = p->next) { - if (strcmp (p->data, user) == 0) + if (STREQ (p->data, user)) { if (lock != NULL) { @@ -3333,8 +4183,11 @@ compare_truncated_revnums (r, s) FIXME: isn't the max rev always the last one? If so, we don't even need a loop. */ +static char *max_rev PROTO ((const RCSVers *)); + static char * -max_rev (const RCSVers *branchnode) +max_rev (branchnode) + const RCSVers *branchnode; { Node *head; Node *bp; @@ -3469,22 +4322,27 @@ RCS_addbranch (rcs, branch) return newrevnum; } -/* Check in to RCSFILE with revision REV (which must be greater than the - largest revision) and message MESSAGE (which is checked for legality). - If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. If FLAGS & - RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & RCS_FLAGS_MODTIME, - use the working file's modification time for the checkin time. - WORKFILE is the working file to check in from, or NULL to use the usual - RCS rules for deriving it from the RCSFILE. +/* Check in to RCSFILE with revision REV (which must be greater than + the largest revision) and message MESSAGE (which is checked for + legality). If FLAGS & RCS_FLAGS_DEAD, check in a dead revision. + If FLAGS & RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS & + RCS_FLAGS_MODTIME, use the working file's modification time for the + checkin time. WORKFILE is the working file to check in from, or + NULL to use the usual RCS rules for deriving it from the RCSFILE. + If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file; + unlinking the working file is standard RCS behavior, but is rarely + appropriate for CVS. + + This function should almost exactly mimic the behavior of `rcs ci'. The + principal point of difference is the support here for preserving file + ownership and permissions in the delta nodes. This is not a clean + solution -- precisely because it diverges from RCS's behavior -- but + it doesn't seem feasible to do this anywhere else in the code. [-twp] Return value is -1 for error (and errno is set to indicate the error), positive for error (and an error message has been printed), or zero for success. */ -/* TODO: RCS_checkin always unlinks the working file after checkin -- - then RCS_checkout checks it out again. The logic should probably - be reversed here. */ - int RCS_checkin (rcs, workfile, message, rev, flags) RCSNode *rcs; @@ -3507,7 +4365,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) commitpt = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* Get basename of working file. Is there a library function to do this? I couldn't find one. -twp */ @@ -3561,6 +4419,92 @@ RCS_checkin (rcs, workfile, message, rev, flags) else delta->state = xstrdup ("Exp"); +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If permissions should be preserved on this project, then + save the permission info. */ + if (preserve_perms) + { + Node *np; + struct stat sb; + char buf[64]; /* static buffer should be safe: see usage. -twp */ + char *fullpath; + + delta->other_delta = getlist(); + + if (CVS_LSTAT (workfile, &sb) < 0) + error (1, 1, "cannot lstat %s", workfile); + + if (S_ISLNK (sb.st_mode)) + { + np = getnode(); + np->key = xstrdup ("symlink"); + np->data = xreadlink (workfile); + addnode (delta->other_delta, np); + } + else + { + (void) sprintf (buf, "%u", sb.st_uid); + np = getnode(); + np->key = xstrdup ("owner"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + (void) sprintf (buf, "%u", sb.st_gid); + np = getnode(); + np->key = xstrdup ("group"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + (void) sprintf (buf, "%o", sb.st_mode & 07777); + np = getnode(); + np->key = xstrdup ("permissions"); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + + /* Save device number. */ + switch (sb.st_mode & S_IFMT) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: + np = getnode(); + np->key = xstrdup ("special"); + sprintf (buf, "%s %lu", + ((sb.st_mode & S_IFMT) == S_IFCHR + ? "character" : "block"), + (unsigned long) sb.st_rdev); + np->data = xstrdup (buf); + addnode (delta->other_delta, np); + break; + + default: + error (0, 0, "special file %s has unknown type", workfile); + } + + /* Save hardlinks. */ + fullpath = xgetwd(); + fullpath = xrealloc (fullpath, + strlen(fullpath) + strlen(workfile) + 2); + sprintf (fullpath + strlen(fullpath), "/%s", workfile); + + np = lookup_file_by_inode (fullpath); + if (np == NULL) + { + error (1, 0, "lost information on %s's linkage", workfile); + } + else + { + struct hardlink_info *hlinfo; + hlinfo = (struct hardlink_info *) np->data; + np = getnode(); + np->key = xstrdup ("hardlinks"); + np->data = xstrdup (hlinfo->links); + (void) addnode (delta->other_delta, np); + } + } + } +#endif + /* Create a new deltatext node. */ dtext = (Deltatext *) xmalloc (sizeof (Deltatext)); memset (dtext, 0, sizeof (Deltatext)); @@ -3599,7 +4543,9 @@ RCS_checkin (rcs, workfile, message, rev, flags) dtext->version = xstrdup (newrev); bufsize = 0; - get_file(workfile, workfile, "r", &dtext->text, &bufsize, &dtext->len); + get_file (workfile, workfile, + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", + &dtext->text, &bufsize, &dtext->len); if (!checkin_quiet) { @@ -3608,6 +4554,9 @@ RCS_checkin (rcs, workfile, message, rev, flags) cvs_output ("\n", 1); } + /* We are probably about to invalidate any cached file. */ + rcsbuf_cache_close (); + fout = rcs_internal_lockfile (rcs->path); RCS_putadmin (rcs, fout); RCS_putdtree (rcs, rcs->head, fout); @@ -3619,12 +4568,12 @@ RCS_checkin (rcs, workfile, message, rev, flags) rcs_internal_unlockfile (fout, rcs->path); freedeltatext (dtext); - /* Removing the file here is an RCS user-visible behavior which - we almost surely do not need in the CVS case. In fact, getting - rid of it should clean up link_file and friends in import.c. */ - if (unlink_file (workfile) < 0) - /* FIXME-update-dir: message does not include update_dir. */ - error (0, errno, "cannot remove %s", workfile); + if ((flags & RCS_FLAGS_KEEPFILE) == 0) + { + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (0, errno, "cannot remove %s", workfile); + } if (!checkin_quiet) cvs_output ("done\n", 5); @@ -3664,7 +4613,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) goto checkin_done; } else if (commitpt->next == NULL - || strcmp (commitpt->version, rcs->head) == 0) + || STREQ (commitpt->version, rcs->head)) delta->version = increment_revnum (commitpt->version); else delta->version = RCS_addbranch (rcs, commitpt->version); @@ -3768,7 +4717,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) nodep = findnode (RCS_getlocks (rcs), commitpt->version); if (nodep != NULL) { - if (strcmp (nodep->data, delta->author) != 0) + if (! STREQ (nodep->data, delta->author)) { error (0, 0, "%s: revision %s locked by %s", rcs->path, @@ -3790,13 +4739,13 @@ RCS_checkin (rcs, workfile, message, rev, flags) tmpfile = cvs_temp_name(); status = RCS_checkout (rcs, NULL, commitpt->version, NULL, ((rcs->expand != NULL - && strcmp (rcs->expand, "b") == 0) + && STREQ (rcs->expand, "b")) ? "-kb" : "-ko"), tmpfile, (RCSCHECKOUTPROC)0, NULL); if (status != 0) - error (1, status < 0 ? errno : 0, + error (1, 0, "could not check out revision %s of `%s'", commitpt->version, rcs->path); @@ -3807,18 +4756,18 @@ RCS_checkin (rcs, workfile, message, rev, flags) /* Diff options should include --binary if the RCS file has -kb set in its `expand' field. */ - diffopts = (rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 + diffopts = (rcs->expand != NULL && STREQ (rcs->expand, "b") ? "-a -n --binary" : "-a -n"); - if (strcmp (commitpt->version, rcs->head) == 0 && + if (STREQ (commitpt->version, rcs->head) && numdots (delta->version) == 1) { /* If this revision is being inserted on the trunk, the change text for the new delta should be the contents of the working file ... */ bufsize = 0; get_file (workfile, workfile, - rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", &dtext->text, &bufsize, &dtext->len); /* ... and the change text for the old delta should be a diff. */ @@ -3854,7 +4803,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) This should cause no harm, but doesn't strike me as immensely clean. */ get_file (changefile, changefile, - rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", &commitpt->text->text, &bufsize, &commitpt->text->len); /* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE @@ -3891,7 +4840,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) /* See the comment above, at the other get_file invocation, regarding binary vs. text. */ get_file (changefile, changefile, - rcs->expand != NULL && strcmp (rcs->expand, "b") == 0 ? "rb" : "r", + rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r", &dtext->text, &bufsize, &dtext->len); if (dtext->text == NULL) @@ -3915,7 +4864,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) if (numdots (commitpt->version) == numdots (delta->version)) { - if (strcmp (commitpt->version, rcs->head) == 0) + if (STREQ (commitpt->version, rcs->head)) { delta->next = rcs->head; rcs->head = xstrdup (delta->version); @@ -3945,12 +4894,12 @@ RCS_checkin (rcs, workfile, message, rev, flags) RCS_rewrite (rcs, dtext, commitpt->version); - /* Removing the file here is an RCS user-visible behavior which - we almost surely do not need in the CVS case. In fact, getting - rid of it should clean up link_file and friends in import.c. */ - if (unlink_file (workfile) < 0) - /* FIXME-update-dir: message does not include update_dir. */ - error (1, errno, "cannot remove %s", workfile); + if ((flags & RCS_FLAGS_KEEPFILE) == 0) + { + if (unlink_file (workfile) < 0) + /* FIXME-update-dir: message does not include update_dir. */ + error (1, errno, "cannot remove %s", workfile); + } if (unlink_file (tmpfile) < 0) error (0, errno, "cannot remove %s", tmpfile); if (unlink_file (changefile) < 0) @@ -4003,42 +4952,70 @@ RCS_cmp_file (rcs, rev, options, filename) int retcode; if (options != NULL && options[0] != '\0') - binary = (strcmp (options, "-kb") == 0); + binary = STREQ (options, "-kb"); else { char *expand; expand = RCS_getexpand (rcs); - if (expand != NULL && strcmp (expand, "b") == 0) + if (expand != NULL && STREQ (expand, "b")) binary = 1; else binary = 0; } - fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r"); - - data.filename = filename; - data.fp = fp; - data.different = 0; - - retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL, - options, RUN_TTY, cmp_file_buffer, - (void *) &data); +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If CVS is to deal properly with special files (when + PreservePermissions is on), the best way is to check out the + revision to a temporary file and call `xcmp' on the two disk + files. xcmp needs to handle non-regular files properly anyway, + so calling it simplifies RCS_cmp_file. We *could* just yank + the delta node out of the version tree and look for device + numbers, but writing to disk and calling xcmp is a better + abstraction (therefore probably more robust). -twp */ - /* If we have not yet found a difference, make sure that we are at - the end of the file. */ - if (! data.different) + if (preserve_perms) { - if (getc (fp) != EOF) - data.different = 1; - } + char *tmp; - fclose (fp); + tmp = cvs_temp_name(); + retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL); + if (retcode != 0) + return 1; - if (retcode != 0) - return 1; + retcode = xcmp (tmp, filename); + if (CVS_UNLINK (tmp) < 0) + error (0, errno, "cannot remove %s", tmp); + return retcode; + } + else +#endif + { + fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r"); + + data.filename = filename; + data.fp = fp; + data.different = 0; + + retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL, + options, RUN_TTY, cmp_file_buffer, + (void *) &data); + + /* If we have not yet found a difference, make sure that we are at + the end of the file. */ + if (! data.different) + { + if (getc (fp) != EOF) + data.different = 1; + } + + fclose (fp); - return data.different; + if (retcode != 0) + return 1; + + return data.different; + } } /* This is a subroutine of RCS_cmp_file. It is passed to @@ -4106,12 +5083,12 @@ RCS_settag (rcs, tag, rev) Node *node; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* FIXME: This check should be moved to RCS_check_tag. There is no reason for it to be here. */ - if (strcmp (tag, TAG_BASE) == 0 - || strcmp (tag, TAG_HEAD) == 0) + if (STREQ (tag, TAG_BASE) + || STREQ (tag, TAG_HEAD)) { /* Print the name of the tag might be considered redundant with the caller, which also prints it. Perhaps this helps @@ -4165,7 +5142,7 @@ RCS_deltag (rcs, tag) List *symbols; Node *node; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); symbols = RCS_symbols (rcs); if (symbols == NULL) @@ -4188,11 +5165,14 @@ RCS_setbranch (rcs, rev) const char *rev; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (rev && ! *rev) + rev = NULL; if (rev == NULL && rcs->branch == NULL) return 0; - if (rev != NULL && rcs->branch != NULL && strcmp (rev, rcs->branch) == 0) + if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch)) return 0; if (rcs->branch != NULL) @@ -4222,7 +5202,7 @@ RCS_lock (rcs, rev, lock_quiet) char *xrev = NULL; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); locks = RCS_getlocks (rcs); if (locks == NULL) @@ -4266,7 +5246,7 @@ RCS_lock (rcs, rev, lock_quiet) p = findnode (locks, xrev); if (p != NULL) { - if (strcmp (p->data, user) == 0) + if (STREQ (p->data, user)) { /* We already own the lock on this revision, so do nothing. */ free (xrev); @@ -4318,7 +5298,7 @@ RCS_unlock (rcs, rev, unlock_quiet) user = getcaller(); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); /* If rev is NULL, unlock the latest revision (first in rcs->locks) held by the caller. */ @@ -4345,7 +5325,7 @@ RCS_unlock (rcs, rev, unlock_quiet) lock = NULL; for (p = locks->list->next; p != locks->list; p = p->next) { - if (strcmp (p->data, user) == 0) + if (STREQ (p->data, user)) { if (lock != NULL) { @@ -4384,7 +5364,7 @@ RCS_unlock (rcs, rev, unlock_quiet) return 0; } - if (strcmp (lock->data, user) != 0) + if (! STREQ (lock->data, user)) { /* If the revision is locked by someone else, notify them. Note that this shouldn't ever happen if RCS_unlock @@ -4420,7 +5400,7 @@ RCS_addaccess (rcs, user) char *access, *a; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); if (rcs->access == NULL) rcs->access = xstrdup (user); @@ -4429,7 +5409,7 @@ RCS_addaccess (rcs, user) access = xstrdup (rcs->access); for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " ")) { - if (strcmp (a, user) == 0) + if (STREQ (a, user)) { free (access); return; @@ -4453,7 +5433,7 @@ RCS_delaccess (rcs, user) int ulen; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); if (rcs->access == NULL) return; @@ -4484,7 +5464,7 @@ RCS_getaccess (rcs) RCSNode *rcs; { if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); return rcs->access; } @@ -4500,7 +5480,7 @@ findtag (node, arg) { char *rev = (char *)arg; - if (strcmp (node->data, rev) == 0) + if (STREQ (node->data, rev)) return 1; else return 0; @@ -4593,7 +5573,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) { /* A range consisting of a branch number means the latest revision on that branch. */ - if (RCS_isbranch (rcs, rev1) && strcmp (rev1, rev2) == 0) + if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2)) rev1 = rev2 = RCS_getbranch (rcs, rev1, 0); else { @@ -4684,12 +5664,12 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) *bp = '.'; } } - else if (strcmp (rev1, branchpoint) != 0) + else if (! STREQ (rev1, branchpoint)) { /* Walk deltas from BRANCHPOINT on, looking for REV1. */ nodep = findnode (rcs->versions, branchpoint); revp = (RCSVers *) nodep->data; - while (revp->next != NULL && strcmp (revp->next, rev1) != 0) + while (revp->next != NULL && ! STREQ (revp->next, rev1)) { revp = (RCSVers *) nodep->data; nodep = findnode (rcs->versions, revp->next); @@ -4738,7 +5718,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) revp = (RCSVers *) nodep->data; if (rev2 != NULL) - found = (strcmp (revp->version, rev2) == 0); + found = STREQ (revp->version, rev2); next = revp->next; if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL) @@ -4835,13 +5815,6 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) if (status > 0) goto delrev_done; - else if (status < 0) - { - error (0, errno, - "cannot check out revision %s of %s", after, rcs->path); - goto delrev_done; - } - if (before == NULL) { /* We are deleting revisions from the head of the tree, @@ -4869,12 +5842,6 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) (RCSCHECKOUTPROC)0, NULL); if (status > 0) goto delrev_done; - else if (status < 0) - { - error (0, errno, "cannot check out revision %s of %s", - before, rcs->path); - goto delrev_done; - } outfile = cvs_temp_name(); status = diff_exec (beforefile, afterfile, "-n", outfile); @@ -4919,7 +5886,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) outdated. (FIXME: would it be safe to use the `dead' field for this? Doubtful.) */ for (next = rev1; - next != NULL && (after == NULL || strcmp (next, after) != 0); + next != NULL && (after == NULL || ! STREQ (next, after)); next = revp->next) { nodep = findnode (rcs->versions, next); @@ -4944,13 +5911,13 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) /* beforep's ->next field already should be equal to after, which I think is always NULL in this case. */ ; - else if (strcmp (rev1, branchpoint) == 0) + else if (STREQ (rev1, branchpoint)) { nodep = findnode (rcs->versions, before); revp = (RCSVers *) nodep->data; nodep = revp->branches->list->next; while (nodep != revp->branches->list && - strcmp (nodep->key, rev1) != 0) + ! STREQ (nodep->key, rev1)) nodep = nodep->next; assert (nodep != revp->branches->list); if (after == NULL) @@ -5470,9 +6437,10 @@ rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) On error, give a fatal error. */ static void -RCS_deltas (rcs, fp, version, op, text, len, log, loglen) +RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) RCSNode *rcs; FILE *fp; + struct rcsbuffer *rcsbuf; char *version; enum rcs_delta_op op; char **text; @@ -5480,6 +6448,7 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) char **log; size_t *loglen; { + struct rcsbuffer rcsbuf_local; char *branchversion; char *cpversion; char *key; @@ -5489,7 +6458,6 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) RCSVers *prev_vers; RCSVers *trunk_vers; char *next; - int n; int ishead, isnext, isversion, onbranch; Node *node; struct linevector headlines; @@ -5499,11 +6467,8 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) if (fp == NULL) { - fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); - if (fp == NULL) - error (1, 0, "unable to reopen `%s'", rcs->path); - if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) - error (1, 0, "cannot fseek RCS file"); + rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local); + rcsbuf = &rcsbuf_local; } ishead = 1; @@ -5530,9 +6495,10 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) *cpversion = '\0'; do { - getrcsrev (fp, &key); + if (! rcsbuf_getrevnum (rcsbuf, &key)) + error (1, 0, "unexpected EOF reading RCS file %s", rcs->path); - if (next != NULL && strcmp (next, key) != 0) + if (next != NULL && ! STREQ (next, key)) { /* This is not the next version we need. It is a branch version which we want to ignore. */ @@ -5557,27 +6523,30 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) next = vers->next; /* Compare key and trunkversion now, because key points to - storage controlled by getrcskey. */ - if (strcmp (branchversion, key) == 0) + storage controlled by rcsbuf_getkey. */ + if (STREQ (branchversion, key)) isversion = 1; else isversion = 0; } - while ((n = getrcskey (fp, &key, &value, &vallen)) >= 0) + while (1) { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s does not appear to be a valid rcs file", + rcs->path); + if (log != NULL && isversion - && strcmp (key, "log") == 0 - && strcmp (branchversion, version) == 0) + && STREQ (key, "log") + && STREQ (branchversion, version)) { - *log = xmalloc (vallen); - memcpy (*log, value, vallen); - *loglen = vallen; + *log = rcsbuf_valcopy (rcsbuf, value, 0, loglen); } - if (strcmp (key, "text") == 0) + if (STREQ (key, "text")) { + rcsbuf_valpolish (rcsbuf, value, 0, &vallen); if (ishead) { if (! linevector_add (&curlines, value, vallen, NULL, 0)) @@ -5596,14 +6565,12 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) break; } } - if (n < 0) - goto l_error; if (isversion) { /* This is either the version we want, or it is the branchpoint to the version we want. */ - if (strcmp (branchversion, version) == 0) + if (STREQ (branchversion, version)) { /* This is the version we want. */ linevector_copy (&headlines, &curlines); @@ -5683,9 +6650,8 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) } while (next != NULL); free (branchversion); - - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", rcs->path); + + rcsbuf_cache (rcs, rcsbuf); if (! foundhead) error (1, 0, "could not find desired version %s in %s", @@ -5789,125 +6755,148 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) linevector_free (&trunklines); return; - - l_error: - if (ferror (fp)) - error (1, errno, "cannot read %s", rcs->path); - else - error (1, 0, "%s does not appear to be a valid rcs file", - rcs->path); } +/* Read the information for a single delta from the RCS buffer RCSBUF, + whose name is RCSFILE. *KEYP and *VALP are either NULL, or the + first key/value pair to read, as set by rcsbuf_getkey. Return NULL + if there are no more deltas. Store the key/value pair which + terminated the read in *KEYP and *VALP. */ + static RCSVers * -getdelta (fp, rcsfile) - FILE *fp; +getdelta (rcsbuf, rcsfile, keyp, valp) + struct rcsbuffer *rcsbuf; char *rcsfile; + char **keyp; + char **valp; { RCSVers *vnode; char *key, *value, *cp; - long fpos; Node *kv; - vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); - memset (vnode, 0, sizeof (RCSVers)); - - /* Get revision number. This uses getrcskey because it doesn't - croak when encountering unexpected input. As a result, we have - to play unholy games with `key' and `value'. */ - fpos = ftell (fp); - getrcskey (fp, &key, &value, NULL); + /* Get revision number if it wasn't passed in. This uses + rcsbuf_getkey because it doesn't croak when encountering + unexpected input. As a result, we have to play unholy games + with `key' and `value'. */ + if (*keyp != NULL) + { + key = *keyp; + value = *valp; + } + else + { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s: unexpected EOF", rcsfile); + } /* Make sure that it is a revision number and not a cabbage or something. */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; - if (*cp != '\0' || strncmp (RCSDATE, value, strlen (RCSDATE)) != 0) + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK since + we know exactly what to expect. */ + if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0) { - (void) fseek (fp, fpos, SEEK_SET); - free (vnode); + *keyp = key; + *valp = value; return NULL; } + + vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); + memset (vnode, 0, sizeof (RCSVers)); + vnode->version = xstrdup (key); - /* grab the value of the date from value */ - cp = value + strlen (RCSDATE);/* skip the "date" keyword */ + /* Grab the value of the date from value. Note that we are not + massaging VALUE from the string found in the RCS file. */ + cp = value + (sizeof RCSDATE) - 1; /* skip the "date" keyword */ while (whitespace (*cp)) /* take space off front of value */ cp++; vnode->date = xstrdup (cp); /* Get author field. */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be using errno in case of ferror. */ - if (key == NULL || strcmp (key, "author") != 0) + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (! STREQ (key, "author")) error (1, 0, "\ -unable to parse rcs file; `author' not in the expected place"); - vnode->author = xstrdup (value); +unable to parse %s; `author' not in the expected place", rcsfile); + vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); /* Get state field. */ - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be using errno in case of ferror. */ - if (key == NULL || strcmp (key, "state") != 0) + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (! STREQ (key, "state")) error (1, 0, "\ -unable to parse rcs file; `state' not in the expected place"); - vnode->state = xstrdup (value); - if (strcmp (value, "dead") == 0) +unable to parse %s; `state' not in the expected place", rcsfile); + vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); + if (STREQ (value, "dead")) { vnode->dead = 1; } /* Note that "branches" and "next" are in fact mandatory, according - to doc/RCSFILES. We perhaps should be giving an error if they - are not there. */ + to doc/RCSFILES. */ /* fill in the branch list (if any branches exist) */ - fpos = ftell (fp); - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be handling various error conditions better. */ - if (key != NULL && strcmp (key, RCSDESC) == 0) + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (STREQ (key, RCSDESC)) { - (void) fseek (fp, fpos, SEEK_SET); + *keyp = key; + *valp = value; + /* Probably could/should be a fatal error. */ + error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile); return vnode; } if (value != (char *) NULL) { vnode->branches = getlist (); + /* Note that we are not massaging VALUE from the string found + in the RCS file. */ do_branches (vnode->branches, value); } /* fill in the next field if there is a next revision */ - fpos = ftell (fp); - (void) getrcskey (fp, &key, &value, NULL); - /* FIXME: should be handling various error conditions better. */ - if (key != NULL && strcmp (key, RCSDESC) == 0) + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + { + error (1, 0, "unexpected end of file reading %s", rcsfile); + } + if (STREQ (key, RCSDESC)) { - (void) fseek (fp, fpos, SEEK_SET); + *keyp = key; + *valp = value; + /* Probably could/should be a fatal error. */ + error (0, 0, "warning: 'next' keyword missing from %s", rcsfile); return vnode; } if (value != (char *) NULL) - vnode->next = xstrdup (value); + vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); /* * XXX - this is where we put the symbolic link stuff??? * (into newphrases in the deltas). */ - /* FIXME: Does not correctly handle errors, e.g. from stdio. */ while (1) { - fpos = ftell (fp); - if (getrcskey (fp, &key, &value, NULL) < 0) - break; + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "unexpected end of file reading %s", rcsfile); - assert (key != NULL); - - if (strcmp (key, RCSDESC) == 0) + if (STREQ (key, RCSDESC)) break; /* Enable use of repositories created by certain obsolete versions of CVS. This code should remain indefinately; there is no procedure for converting old repositories, and checking for it is harmless. */ - if (strcmp(key, RCSDEAD) == 0) + if (STREQ (key, RCSDEAD)) { vnode->dead = 1; if (vnode->state != NULL) @@ -5918,6 +6907,9 @@ unable to parse rcs file; `state' not in the expected place"); /* if we have a new revision number, we're done with this delta */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; + /* Note that when comparing with RCSDATE, we are not massaging + VALUE from the string found in the RCS file. This is OK + since we know exactly what to expect. */ if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; @@ -5928,7 +6920,7 @@ unable to parse rcs file; `state' not in the expected place"); kv = getnode (); kv->type = RCSFIELD; kv->key = xstrdup (key); - kv->data = xstrdup (value); + kv->data = rcsbuf_valcopy (rcsbuf, value, 1, (size_t *) NULL); if (addnode (vnode->other_delta, kv) != 0) { /* Complaining about duplicate keys in newphrases seems @@ -5940,11 +6932,11 @@ unable to parse rcs file; `state' not in the expected place"); key, rcsfile); freenode (kv); } - } + } - /* We got here because we read beyond the end of a delta. Seek back - to the beginning of the erroneous read. */ - (void) fseek (fp, fpos, SEEK_SET); + /* Return the key which caused us to fail back to the caller. */ + *keyp = key; + *valp = value; return vnode; } @@ -5965,25 +6957,21 @@ freedeltatext (d) } static Deltatext * -RCS_getdeltatext (rcs, fp) +RCS_getdeltatext (rcs, fp, rcsbuf) RCSNode *rcs; FILE *fp; + struct rcsbuffer *rcsbuf; { char *num; char *key, *value; - int n; Node *p; Deltatext *d; - size_t textlen; /* Get the revision number. */ - n = getrevnum (fp, &num); - if (ferror (fp)) - error (1, errno, "%s: cannot read", rcs->path); - if (n == EOF) + if (! rcsbuf_getrevnum (rcsbuf, &num)) { - /* If n == EOF and num == NULL, it means we reached EOF - naturally. That's fine. */ + /* If num == NULL, it means we reached EOF naturally. That's + fine. */ if (num == NULL) return NULL; else @@ -5999,36 +6987,36 @@ RCS_getdeltatext (rcs, fp) d->version = xstrdup (num); /* Get the log message. */ - if (getrcskey (fp, &key, &value, NULL) < 0) + if (! rcsbuf_getkey (rcsbuf, &key, &value)) error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); - if (strcmp (key, "log") != 0) + if (! STREQ (key, "log")) error (1, 0, "%s, delta %s: expected `log', got `%s'", rcs->path, num, key); - d->log = xstrdup (value); + d->log = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); /* Get random newphrases. */ d->other = getlist(); - for (n = getrcskey (fp, &key, &value, &textlen); - n >= 0 && strcmp (key, "text") != 0; - n = getrcskey (fp, &key, &value, &textlen)) + while (1) { + if (! rcsbuf_getkey (rcsbuf, &key, &value)) + error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); + + if (STREQ (key, "text")) + break; + p = getnode(); p->type = RCSFIELD; p->key = xstrdup (key); - p->data = xstrdup (value); + p->data = rcsbuf_valcopy (rcsbuf, value, 1, (size_t *) NULL); if (addnode (d->other, p) < 0) { error (0, 0, "warning: %s, delta %s: duplicate field `%s'", rcs->path, num, key); } } - if (n < 0) - error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num); /* Get the change text. We already know that this key is `text'. */ - d->text = (char *) malloc (textlen + 1); - d->len = textlen; - memcpy (d->text, value, textlen); + d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len); return d; } @@ -6045,11 +7033,23 @@ RCS_getdeltatext (rcs, fp) not get corrupted. */ static int -putsymbol_proc (symnode, fp) +putsymbol_proc (symnode, fparg) Node *symnode; - void *fp; + void *fparg; { - return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->key, symnode->data); + FILE *fp = (FILE *) fparg; + + /* A fiddly optimization: this code used to just call fprintf, but + in an old repository with hundreds of tags this can get called + hundreds of thousands of times when doing a cvs tag. Since + tagging is a relatively common operation, and using putc and + fputs is just as comprehensible, the change is worthwhile. */ + putc ('\n', fp); + putc ('\t', fp); + fputs (symnode->key, fp); + putc (':', fp); + fputs (symnode->data, fp); + return 0; } static int putlock_proc PROTO ((Node *, void *)); @@ -6101,9 +7101,9 @@ putrcsfield_proc (node, vfp) /* desc, log and text fields should not be terminated with semicolon; all other fields should be. */ - if (strcmp (node->key, "desc") != 0 && - strcmp (node->key, "log") != 0 && - strcmp (node->key, "text") != 0) + if (! STREQ (node->key, "desc") && + ! STREQ (node->key, "log") && + ! STREQ (node->key, "text")) { putc (';', fp); } @@ -6133,7 +7133,15 @@ RCS_putadmin (rcs, fp) fputs (";\n", fp); fputs (RCSSYMBOLS, fp); - walklist (RCS_symbols(rcs), putsymbol_proc, (void *) fp); + /* If we haven't had to convert the symbols to a list yet, don't + force a conversion now; just write out the string. */ + if (rcs->symbols == NULL && rcs->symbols_data != NULL) + { + fputs ("\n\t", fp); + fputs (rcs->symbols_data, fp); + } + else + walklist (RCS_symbols (rcs), putsymbol_proc, (void *) fp); fputs (";\n", fp); fputs ("locks", fp); @@ -6151,7 +7159,7 @@ RCS_putadmin (rcs, fp) expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp); fputs ("@;\n", fp); } - if (rcs->expand && strcmp (rcs->expand, "kv") != 0) + if (rcs->expand && ! STREQ (rcs->expand, "kv")) fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand); walklist (rcs->other, putrcsfield_proc, (void *) fp); @@ -6273,40 +7281,68 @@ putdeltatext (fp, d) increasing order.) */ static void -RCS_copydeltas (rcs, fin, fout, newdtext, insertpt) +RCS_copydeltas (rcs, fin, rcsbufin, fout, newdtext, insertpt) RCSNode *rcs; FILE *fin; + struct rcsbuffer *rcsbufin; FILE *fout; Deltatext *newdtext; char *insertpt; { - Deltatext *dtext; + int actions; RCSVers *dadmin; Node *np; int insertbefore, found; + char *bufrest; + int nls; + size_t buflen; + char buf[8192]; + int got; + + /* Count the number of versions for which we have to do some + special operation. */ + actions = walklist (rcs->versions, count_delta_actions, (void *) NULL); /* Make a note of whether NEWDTEXT should be inserted before or after its INSERTPT. */ insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1); - found = 0; - while ((dtext = RCS_getdeltatext (rcs, fin)) != NULL) + while (actions != 0 || newdtext != NULL) { - found = (insertpt != NULL && strcmp (dtext->version, insertpt) == 0); + Deltatext *dtext; + + dtext = RCS_getdeltatext (rcs, fin, rcsbufin); + + /* We shouldn't hit EOF here, because that would imply that + some action was not taken, or that we could not insert + NEWDTEXT. */ + if (dtext == NULL) + error (1, 0, "internal error: EOF too early in RCS_copydeltas"); + + found = (insertpt != NULL && STREQ (dtext->version, insertpt)); if (found && insertbefore) + { putdeltatext (fout, newdtext); + newdtext = NULL; + insertpt = NULL; + } np = findnode (rcs->versions, dtext->version); dadmin = (RCSVers *) np->data; /* If this revision has been outdated, just skip it. */ if (dadmin->outdated) + { + --actions; continue; + } /* Update the change text for this delta. New change text data may come from cvs admin -m, cvs admin -o, or cvs ci. */ if (dadmin->text != NULL) { + if (dadmin->text->log != NULL || dadmin->text->text != NULL) + --actions; if (dadmin->text->log != NULL) { free (dtext->log); @@ -6325,9 +7361,92 @@ RCS_copydeltas (rcs, fin, fout, newdtext, insertpt) freedeltatext (dtext); if (found && !insertbefore) + { putdeltatext (fout, newdtext); + newdtext = NULL; + insertpt = NULL; + } + } + + /* Copy the rest of the file directly, without bothering to + interpret it. The caller will handle error checking by calling + ferror. + + We just wrote a newline to the file, either in putdeltatext or + in the caller. However, we may not have read the corresponding + newline from the file, because rcsbuf_getkey returns as soon as + it finds the end of the '@' string for the desc or text key. + Therefore, we may read three newlines when we should really + only write two, and we check for that case here. This is not + an semantically important issue; we only do it to make our RCS + files look traditional. */ + + nls = 3; + + rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen); + if (buflen > 0) + { + if (bufrest[0] != '\n' + || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0) + { + nls = 0; + } + else + { + if (buflen < 3) + nls -= buflen; + else + { + ++bufrest; + --buflen; + nls = 0; + } + } + + fwrite (bufrest, 1, buflen, fout); + } + + while ((got = fread (buf, 1, sizeof buf, fin)) != 0) + { + if (nls > 0 + && got >= nls + && buf[0] == '\n' + && strncmp (buf, "\n\n\n", nls) == 0) + { + fwrite (buf + 1, 1, got - 1, fout); + } + else + { + fwrite (buf, 1, got, fout); + } + + nls = 0; + } +} + +/* A helper procedure for RCS_copydeltas. This is called via walklist + to count the number of RCS revisions for which some special action + is required. */ + +int +count_delta_actions (np, ignore) + Node *np; + void *ignore; +{ + RCSVers *dadmin; + + dadmin = (RCSVers *) np->data; + + if (dadmin->outdated) + return 1; + + if (dadmin->text != NULL + && (dadmin->text->log != NULL || dadmin->text->text != NULL)) + { + return 1; } - putc ('\n', fout); + + return 0; } /* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style @@ -6435,6 +7554,12 @@ rcs_internal_unlockfile (fp, rcsfile) corrupting the repository. */ if (ferror (fp)) + /* The only case in which using errno here would be meaningful + is if we happen to have left errno unmolested since the call + which produced the error (e.g. fprintf). That is pretty + fragile even if it happens to sometimes be true. The real + solution is to check each call to fprintf rather than waiting + until the end like this. */ error (1, 0, "error writing to lock file %s", lockfile); if (fclose (fp) == EOF) error (1, errno, "error closing lock file %s", lockfile); @@ -6478,6 +7603,7 @@ RCS_rewrite (rcs, newdtext, insertpt) char *insertpt; { FILE *fin, *fout; + struct rcsbuffer rcsbufin; if (noexec) return; @@ -6489,10 +7615,7 @@ RCS_rewrite (rcs, newdtext, insertpt) RCS_putdesc (rcs, fout); /* Open the original RCS file and seek to the first delta text. */ - if ((fin = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ)) == NULL) - error (1, errno, "cannot open RCS file `%s' for reading", rcs->path); - if (fseek (fin, rcs->delta_pos, SEEK_SET) < 0) - error (1, errno, "cannot fseek in RCS file %s", rcs->path); + rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin); /* Update delta_pos to the current position in the output file. Do NOT move these statements: they must be done after fin has @@ -6502,10 +7625,19 @@ RCS_rewrite (rcs, newdtext, insertpt) if (rcs->delta_pos == -1) error (1, errno, "cannot ftell in RCS file %s", rcs->path); - RCS_copydeltas (rcs, fin, fout, newdtext, insertpt); + RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt); + /* We don't want to call rcsbuf_cache here, since we're about to + delete the file. */ + rcsbuf_close (&rcsbufin); if (ferror (fin)) - error (0, errno, "warning: when closing RCS file `%s'", rcs->path); + /* The only case in which using errno here would be meaningful + is if we happen to have left errno unmolested since the call + which produced the error (e.g. fread). That is pretty + fragile even if it happens to sometimes be true. The real + solution is to make sure that all the code which reads + from fin checks for errors itself (some does, some doesn't). */ + error (0, 0, "warning: when closing RCS file `%s'", rcs->path); if (fclose (fin) < 0) error (0, errno, "warning: closing RCS file `%s'", rcs->path); @@ -6530,13 +7662,18 @@ annotate_fileproc (callerdat, finfo) struct file_info *finfo; { FILE *fp = NULL; + struct rcsbuffer *rcsbufp = NULL; + struct rcsbuffer rcsbuf; char *version; if (finfo->rcs == NULL) return (1); if (finfo->rcs->flags & PARTIAL) - RCS_reparsercsfile (finfo->rcs, &fp); + { + RCS_reparsercsfile (finfo->rcs, &fp, &rcsbuf); + rcsbufp = &rcsbuf; + } version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, (int *) NULL); @@ -6549,7 +7686,7 @@ annotate_fileproc (callerdat, finfo) cvs_outerr (finfo->fullname, 0); cvs_outerr ("\n***************\n", 0); - RCS_deltas (finfo->rcs, fp, version, RCS_ANNOTATE, (char **) NULL, + RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, (char **) NULL, (size_t) NULL, (char **) NULL, (size_t *) NULL); free (version); return 0; diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index d0f47bb..400d1a0 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -169,13 +169,17 @@ typedef struct rcsversnode RCSVers; /* The type of a function passed to RCS_checkout. */ typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t)); +#ifdef __STDC__ +struct rcsbuffer; +#endif + /* * exported interfaces */ RCSNode *RCS_parse PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); void RCS_fully_parse PROTO((RCSNode *)); -void RCS_reparsercsfile PROTO((RCSNode *, FILE **)); +void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *)); char *RCS_check_kflag PROTO((const char *arg)); char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match)); @@ -217,6 +221,8 @@ 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 *)); +extern int preserve_perms; + /* From import.c. */ extern int add_rcs_file PROTO ((char *, char *, char *, char *, char *, char *, char *, int, char **, diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index 9a237a9..3086b57 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -487,7 +487,42 @@ diff_exec (file1, file2, options, out) char *options; char *out; { - char *args = xmalloc (strlen (options) + 10); + char *args; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* If either file1 or file2 are special files, pretend they are + /dev/null. Reason: suppose a file that represents a block + special device in one revision becomes a regular file. CVS + must find the `difference' between these files, but a special + file contains no data useful for calculating this metric. The + safe thing to do is to treat the special file as an empty file, + thus recording the regular file's full contents. Doing so will + create extremely large deltas at the point of transition + between device files and regular files, but this is probably + very rare anyway. + + There may be ways around this, but I think they are fraught + with danger. -twp */ + + if (preserve_perms && + strcmp (file1, DEVNULL) != 0 && + strcmp (file2, DEVNULL) != 0) + { + struct stat sb1, sb2; + + if (CVS_LSTAT (file1, &sb1) < 0) + error (1, errno, "cannot get file information for %s", file1); + if (CVS_LSTAT (file2, &sb2) < 0) + error (1, errno, "cannot get file information for %s", file2); + + if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) + file1 = DEVNULL; + if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) + file2 = DEVNULL; + } +#endif + + args = xmalloc (strlen (options) + 10); /* The first word in this string is used only for error reporting. */ sprintf (args, "diff %s", options); call_diff_setup (args); @@ -507,7 +542,31 @@ diff_execv (file1, file2, label1, label2, options, out) char *options; char *out; { - char *args = xmalloc (strlen (options) + 10); + char *args; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Pretend that special files are /dev/null for purposes of making + diffs. See comments in diff_exec. */ + + if (preserve_perms && + strcmp (file1, DEVNULL) != 0 && + strcmp (file2, DEVNULL) != 0) + { + struct stat sb1, sb2; + + if (CVS_LSTAT (file1, &sb1) < 0) + error (1, errno, "cannot get file information for %s", file1); + if (CVS_LSTAT (file2, &sb2) < 0) + error (1, errno, "cannot get file information for %s", file2); + + if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode)) + file1 = DEVNULL; + if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode)) + file2 = DEVNULL; + } +#endif + + args = xmalloc (strlen (options) + 10); /* The first word in this string is used only for error reporting. */ /* I guess we are pretty confident that options starts with a space. */ sprintf (args, "diff%s", options); diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index d2fae6f..3896bc7 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -448,7 +448,7 @@ do_recursion (frame) { /* we will process files, so pre-parse entries */ if (frame->which & W_LOCAL) - entries = Entries_Open (frame->aflag); + entries = Entries_Open (frame->aflag, NULL); } } diff --git a/contrib/cvs/src/sanity.sh b/contrib/cvs/src/sanity.sh index e407cbd..1d877fe 100755 --- a/contrib/cvs/src/sanity.sh +++ b/contrib/cvs/src/sanity.sh @@ -548,20 +548,29 @@ RCSINIT=; export RCSINIT # tests. if test x"$*" = x; then + # Basic/miscellaneous functionality tests="basica basicb basicc basic1 deep basic2" - tests="${tests} rdiff death death2 branches" + # Branching, tagging, removing, adding, multiple directories + tests="${tests} rdiff death death2 branches branches2" tests="${tests} rcslib multibranch import importb join join2 join3" tests="${tests} new newb conflicts conflicts2 conflicts3" + # Checking out various places (modules, checkout -d, &c) tests="${tests} modules modules2 modules3 modules4" + tests="${tests} cvsadm abspath toplevel" + # Log messages, error messages. tests="${tests} mflag editor errmsg1 errmsg2" + # Watches, binary files, history browsing, &c. tests="${tests} devcom devcom2 devcom3 watch4" tests="${tests} ignore binfiles binfiles2 mcopy binwrap binwrap2" tests="${tests} binwrap3 mwrap info config" tests="${tests} serverpatch log log2 ann crerepos rcs big modes stamps" + # More tag and branch tests, keywords. tests="${tests} sticky keyword keywordlog" - tests="${tests} toplevel head tagdate multibranch2" + tests="${tests} head tagdate multibranch2" + # "cvs admin", reserved checkouts. tests="${tests} admin reserved" - tests="${tests} cvsadm diffmerge1 diffmerge2" + # Nuts and bolts of diffing/merging (diff library, &c) + tests="${tests} diffmerge1 diffmerge2" else tests="$*" fi @@ -1903,20 +1912,21 @@ done" # interrupt, while we've got a clean 1.1 here, let's import it # into a couple of other modules. cd export-dir - dotest 56 "${testcvs} import -m first-import second-dir first-immigration immigration1 immigration1_0" \ -"N second-dir/file14 -N second-dir/file6 -N second-dir/file7 -${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1 -N second-dir/dir1/file14 -N second-dir/dir1/file6 -N second-dir/dir1/file7 -${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1/dir2 + dotest_sort 56 "${testcvs} import -m first-import second-dir first-immigration immigration1 immigration1_0" \ +" + N second-dir/dir1/dir2/file14 N second-dir/dir1/dir2/file6 N second-dir/dir1/dir2/file7 - -No conflicts created by this import" +N second-dir/dir1/file14 +N second-dir/dir1/file6 +N second-dir/dir1/file7 +N second-dir/file14 +N second-dir/file6 +N second-dir/file7 +No conflicts created by this import +${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1 +${PROG} [a-z]*: Importing ${TESTDIR}/cvsroot/second-dir/dir1/dir2" cd .. if ${CVS} export -r HEAD second-dir ; then @@ -2532,11 +2542,13 @@ U first-dir/file3' cd first-dir - # Add a file on the trunk. + # Add two files on the trunk. echo "first revision" > file1 - dotest death2-2 "${testcvs} add file1" \ + echo "file4 first revision" > file4 + dotest death2-2 "${testcvs} add file1 file4" \ "${PROG}"' [a-z]*: scheduling file `file1'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' +'"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add these files permanently' dotest death2-3 "${testcvs} -q commit -m add" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v @@ -2544,11 +2556,21 @@ done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file4,v +done +Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +initial revision: 1\.1 done" # Make a branch and a non-branch tag. - dotest death2-4 "${testcvs} -q tag -b branch" 'T file1' - dotest death2-5 "${testcvs} -q tag tag" 'T file1' + dotest death2-4 "${testcvs} -q tag -b branch" \ +'T file1 +T file4' + dotest death2-5 "${testcvs} -q tag tag" \ +'T file1 +T file4' # Switch over to the branch. dotest death2-6 "${testcvs} -q update -r branch" '' @@ -2659,8 +2681,20 @@ ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1 done" + # Delete file4 from the branch + dotest death2-10a "${testcvs} rm -f file4" \ +"${PROG} [a-z]*: scheduling .file4. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + dotest death2-10b "${testcvs} -q ci -m removed" \ +"Removing file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: delete; previous revision: 1\.1\.2 +done" + # Back to the trunk. - dotest death2-11 "${testcvs} -q update -A" 'U file1' 'P file1' + dotest death2-11 "${testcvs} -q update -A" \ +"[UP] file1 +U file4" # Add another file on the trunk. echo "first revision" > file2 @@ -2675,15 +2709,22 @@ ${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 initial revision: 1\.1 done" + # Modify file4 on the trunk. + echo "new file4 revision" > file4 + dotest death2-13a "${testcvs} -q commit -m mod" \ +"Checking in file4; +${TESTDIR}/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2; previous revision: 1\.1 +done" + # Back to the branch. # The ``no longer in the repository'' message doesn't really # look right to me, but that's what CVS currently prints for # this case. dotest death2-14 "${testcvs} -q update -r branch" \ -"U file1 -${PROG} [a-z]*: file2 is no longer in the repository" \ -"P file1 -${PROG} [a-z]*: file2 is no longer in the repository" +"[UP] file1 +${PROG} [a-z]*: file2 is no longer in the repository +${PROG} [a-z]*: warning: file4 is not (any longer) pertinent" # Add a file on the branch with the same name. echo "branch revision" > file2 @@ -2740,7 +2781,8 @@ diff -c -r1\.1 -r1\.1\.2\.2 --- 1 ---- ! second revision ${PROG} [a-z]*: tag tag is not in file file2 -${PROG} [a-z]*: tag tag is not in file file3" +${PROG} [a-z]*: tag tag is not in file file3 +${PROG} [a-z]*: file4 no longer exists, no comparison available" dotest_fail death2-diff-12 "${testcvs} -q diff -rtag -c -N ." \ "Index: file1 @@ -2775,16 +2817,24 @@ diff -N file3 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* \*\*\* 0 \*\*\*\* --- 1 ---- -${PLUS} first revision" +${PLUS} first revision +Index: file4 +=================================================================== +RCS file: file4 +diff -N file4 +\*\*\* ${tempname}[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- file4 first revision +--- 0 ----" # Switch to the nonbranch tag. dotest death2-19 "${testcvs} -q update -r tag" \ -"U file1 -${PROG} [a-z]*: file2 is no longer in the repository -${PROG} [a-z]*: file3 is no longer in the repository" \ -"P file1 +"[UP] file1 ${PROG} [a-z]*: file2 is no longer in the repository -${PROG} [a-z]*: file3 is no longer in the repository" +${PROG} [a-z]*: file3 is no longer in the repository +U file4" dotest_fail death2-20 "test -f file2" @@ -3036,6 +3086,272 @@ done" rm -r first-dir ;; + branches2) + # More branch tests. + # Test that when updating a new subdirectory in a directory + # which was checked out on a branch, the new subdirectory is + # created on the appropriate branch. Test this when joining + # as well. + + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir trunk; cd trunk + + # Create a file. + dotest branches2-1 "${testcvs} -q co first-dir" + cd first-dir + echo "file1 first revision" > file1 + dotest branches2-2 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest branches2-3 "${testcvs} commit -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" + + # Tag the file. + dotest branches2-4 "${testcvs} -q tag tag1" 'T file1' + + # Make two branches. + dotest branches2-5 "${testcvs} -q rtag -b -r tag1 b1 first-dir" '' + dotest branches2-6 "${testcvs} -q rtag -b -r tag1 b2 first-dir" '' + + # Create some files and a subdirectory on branch b1. + cd ../.. + mkdir b1; cd b1 + dotest branches2-7 "${testcvs} -q co -r b1 first-dir" \ +"U first-dir/file1" + cd first-dir + echo "file2 first revision" > file2 + dotest branches2-8 "${testcvs} add file2" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition on branch `b1'\'' +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + mkdir dir1 + dotest branches2-9 "${testcvs} add dir1" \ +"Directory ${TESTDIR}/cvsroot/first-dir/dir1 added to the repository +--> Using per-directory sticky tag "'`'"b1'" + echo "file3 first revision" > dir1/file3 + dotest branches2-10 "${testcvs} add dir1/file3" \ +"${PROG}"' [a-z]*: scheduling file `dir1/file3'\'' for addition on branch `b1'\'' +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest branches2-11 "${testcvs} -q ci -madd ." \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/Attic/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/dir1/Attic/file3,v +done +Checking in dir1/file3; +${TESTDIR}/cvsroot/first-dir/dir1/Attic/file3,v <-- file3 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + + # Check out the second branch, and update the working + # directory to the first branch, to make sure the right + # happens with dir1. + cd ../.. + mkdir b2; cd b2 + dotest branches2-12 "${testcvs} -q co -r b2 first-dir" \ +'U first-dir/file1' + cd first-dir + dotest branches2-13 "${testcvs} update -d -r b1 dir1" \ +"${PROG} [a-z]*: Updating dir1 +U dir1/file3" + dotest branches2-14 "${testcvs} -q status" \ +"=================================================================== +File: file1 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file1,v + Sticky Tag: b2 (branch: 1\.1\.4) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Up-to-date + + Working revision: 1\.1\.2\.1.* + Repository revision: 1\.1\.2\.1 ${TESTDIR}/cvsroot/first-dir/dir1/Attic/file3,v + Sticky Tag: b1 (branch: 1\.1\.2) + Sticky Date: (none) + Sticky Options: (none)" + + # FIXME: Just clobbering the directory like this is a bit + # tacky, although people generally expect it to work. Maybe + # we should release it instead. We do it a few other places + # below as well. + rm -r dir1 + dotest branches2-15 "${testcvs} update -d -j b1 dir1" \ +"${PROG} [a-z]*: Updating dir1 +U dir1/file3" + # FIXCVS: The `No revision control file' stuff seems to be + # CVS's way of telling us that we're adding the file on a + # branch, and the file is not on that branch yet. This + # should be nicer. + dotest branches2-16 "${testcvs} -q status" \ +"=================================================================== +File: file1 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file1,v + Sticky Tag: b2 (branch: 1\.1\.4) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: b2 - MISSING from RCS file! + Sticky Date: (none) + Sticky Options: (none)" + + cd ../../trunk/first-dir + dotest branches2-17 "${testcvs} update -d -P dir1" \ +"${PROG} [a-z]*: Updating dir1" + dotest_fail branches2-18 "test -d dir1" + dotest branches2-19 "${testcvs} update -d -P -r b1 dir1" \ +"${PROG} [a-z]*: Updating dir1 +U dir1/file3" + dotest branches2-20 "${testcvs} -q status" \ +"=================================================================== +File: file1 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file1,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Up-to-date + + Working revision: 1\.1\.2\.1.* + Repository revision: 1\.1\.2\.1 ${TESTDIR}/cvsroot/first-dir/dir1/Attic/file3,v + Sticky Tag: b1 (branch: 1\.1\.2) + Sticky Date: (none) + Sticky Options: (none)" + + rm -r dir1 + dotest branches2-21 "${testcvs} update -d -P -j b1 dir1" \ +"${PROG} [a-z]*: Updating dir1 +U dir1/file3" + dotest branches2-22 "${testcvs} -q status" \ +"=================================================================== +File: file1 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/file1,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: file3 Status: Locally Added + + Working revision: New file! + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/dir1/Attic/file3,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + + cd ../.. + rm -r b1 b2 + + # Check out branch b1 twice. Crate a new directory in one + # working directory, then do a cvs update in the other + # working directory and see if the tags are right. + mkdir b1a + mkdir b1b + cd b1b + dotest branches2-23 "${testcvs} -q co -r b1 first-dir" \ +'U first-dir/file1 +U first-dir/file2 +U first-dir/dir1/file3' + cd ../b1a + dotest branches2-24 "${testcvs} -q co -r b1 first-dir" \ +'U first-dir/file1 +U first-dir/file2 +U first-dir/dir1/file3' + cd first-dir + mkdir dir2 + dotest branches2-25 "${testcvs} add dir2" \ +"Directory ${TESTDIR}/cvsroot/first-dir/dir2 added to the repository +--> Using per-directory sticky tag "'`'"b1'" + echo "file4 first revision" > dir2/file4 + dotest branches2-26 "${testcvs} add dir2/file4" \ +"${PROG}"' [a-z]*: scheduling file `dir2/file4'\'' for addition on branch `b1'\'' +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest branches2-27 "${testcvs} -q commit -madd" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/dir2/Attic/file4,v +done +Checking in dir2/file4; +${TESTDIR}/cvsroot/first-dir/dir2/Attic/file4,v <-- file4 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + + cd ../../b1b/first-dir + dotest branches2-28 "${testcvs} update -d dir2" \ +"${PROG} [a-z]*: Updating dir2 +U dir2/file4" + cd dir2 + dotest branches2-29 "${testcvs} -q status" \ +"=================================================================== +File: file4 Status: Up-to-date + + Working revision: 1\.1\.2\.1.* + Repository revision: 1\.1\.2\.1 ${TESTDIR}/cvsroot/first-dir/dir2/Attic/file4,v + Sticky Tag: b1 (branch: 1\.1\.2) + Sticky Date: (none) + Sticky Options: (none)" + dotest branches2-30 "cat CVS/Tag" 'Tb1' + + # Test update -A on a subdirectory + cd .. + rm -r dir2 + dotest branches2-31 "${testcvs} update -A -d dir2" \ +"${PROG} [a-z]*: Updating dir2" + cd dir2 + dotest branches2-32 "${testcvs} -q status" '' + dotest_fail branches2-33 "test -f CVS/Tag" + + # Add a file on the trunk. + echo "file5 first revision" > file5 + dotest branches2-34 "${testcvs} add file5" \ +"${PROG}"' [a-z]*: scheduling file `file5'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest branches2-35 "${testcvs} -q commit -madd" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/dir2/file5,v +done +Checking in file5; +${TESTDIR}/cvsroot/first-dir/dir2/file5,v <-- file5 +initial revision: 1\.1 +done" + + cd ../../../trunk/first-dir + dotest branches2-36 "${testcvs} -q update -d dir2" 'U dir2/file5' + cd dir2 + dotest branches2-37 "${testcvs} -q status" \ +"=================================================================== +File: file5 Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/dir2/file5,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest_fail branches2-38 "test -f CVS/status" + + cd ../../.. + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -r trunk b1a b1b + ;; + rcslib) # Test librarification of RCS. # First: test whether `cvs diff' handles $Name expansion @@ -3567,11 +3883,12 @@ rev 2 of file 2 cd imp-dir echo 'OpenMunger sources' >file1 echo 'OpenMunger sources' >file2 - dotest importb-1 \ + dotest_sort importb-1 \ "${testcvs} import -m add first-dir openmunger openmunger-1_0" \ -"N first-dir/file1 -N first-dir/file2 +" +N first-dir/file1 +N first-dir/file2 No conflicts created by this import" cd .. rm -r imp-dir @@ -3583,15 +3900,16 @@ No conflicts created by this import" echo 'FreeMunger sources' >file2 # Not completely sure how the conflict detection is supposed to # be working here (haven't really thought about it). - dotest importb-2 \ + dotest_sort importb-2 \ "${testcvs} import -m add -b 1.1.3 first-dir freemunger freemunger-1_0" \ -"C first-dir/file1 -C first-dir/file2 +" -2 conflicts created by this import. -Use the following command to help the merge: - ${PROG} checkout -jfreemunger:yesterday -jfreemunger first-dir" + ${PROG} checkout -jfreemunger:yesterday -jfreemunger first-dir +2 conflicts created by this import. +C first-dir/file1 +C first-dir/file2 +Use the following command to help the merge:" cd .. rm -r imp-dir @@ -4920,8 +5238,20 @@ ${PROG} [a-z]*: warning: file2 is not (any longer) pertinent" # OK, now add a directory to both working directories # and see that CVS doesn't lose its mind. mkdir sdir - dotest conficts3-14 "${testcvs} add sdir" \ + dotest conflicts3-14 "${testcvs} add sdir" \ "Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" + touch sdir/sfile + dotest conflicts3-14a "${testcvs} add sdir/sfile" \ +"${PROG} [a-z]*: scheduling file .sdir/sfile. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest conflicts3-14b "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/sfile,v +done +Checking in sdir/sfile; +${TESTDIR}/cvsroot/first-dir/sdir/sfile,v <-- sfile +initial revision: 1\.1 +done" + cd ../../2/first-dir # Create a CVS directory without the proper administrative @@ -4946,6 +5276,11 @@ ${PROG} [a-z]*: ignoring sdir (CVS/Repository missing)" dotest conflicts3-16 "${testcvs} -q update" \ "${QUESTION} sdir ${PROG} [a-z]*: ignoring sdir (CVS/Entries missing)" + cd .. + dotest conflicts3-16a "${testcvs} -q update first-dir" \ +"${QUESTION} first-dir/sdir +${PROG} [a-z]*: ignoring first-dir/sdir (CVS/Entries missing)" + cd first-dir fi rm -r sdir @@ -4960,8 +5295,40 @@ ${PROG} [a-z]*: ignoring sdir (CVS/Entries missing)" touch newdir/CVS/Repository dotest conflicts3-19 "${testcvs} -q update" \ "${PROG} [a-z]*: ignoring newdir (CVS/Entries missing)" + cd .. + dotest conflicts3-20 "${testcvs} -q update first-dir" \ +"${PROG} [a-z]*: ignoring first-dir/newdir (CVS/Entries missing)" + cd first-dir rm -r newdir + # The previous tests have left CVS/Entries in something of a mess. + # While we "should" be able to deal with that (maybe), for now + # we just start over. + cd .. + rm -r first-dir + dotest conflicts3-20a "${testcvs} -q co -l first-dir" '' + cd first-dir + + dotest conflicts3-21 "${testcvs} -q update -d sdir" "U sdir/sfile" + rm -r sdir/CVS + dotest conflicts3-22 "${testcvs} -q update" "? sdir" + if test "x$remote" = xyes; then + # It isn't particularly swift that CVS prints this + # "cannot open CVS/Entries" where it has already printed + # "? sdir". At least I don't think so. But do note: (1) + # non-fatal error, and (2) tells us which directory has + # the problem. + dotest_fail conflicts3-23 "${testcvs} -q update -PdA" \ +"${QUESTION} sdir +${PROG} update: in directory sdir: +${PROG} update: cannot open CVS/Entries for reading: No such file or directory +${PROG} update: move away sdir/sfile; it is in the way +C sdir/sfile" + else + dotest conflicts3-23 "${testcvs} -q update -PdA" \ +"${QUESTION} sdir" + fi + cd ../.. rm -r 1 2 @@ -4970,6 +5337,17 @@ ${PROG} [a-z]*: ignoring sdir (CVS/Entries missing)" modules) # Tests of various ways to define and use modules. + # Roadmap to various modules tests: + # -a: + # error on incorrect placement: modules + # error combining with other options: modules2-a* + # use to specify a file more than once: modules3 + # use with ! feature: modules4 + # regular modules: modules, modules2, cvsadm + # ampersand modules: modules2 + # -s: modules. + # -d: modules, modules3, cvsadm + # slashes in module names: modules3 ############################################################ # These tests are to make sure that administrative files get @@ -5805,5182 +6183,4911 @@ add-it rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - mflag) - for message in '' ' ' ' - ' ' test' ; do - # Set up - mkdir a-dir; cd a-dir - # Test handling of -m during import - echo testa >>test - if ${testcvs} import -m "$message" a-dir A A1 >>${LOGFILE} 2>&1;then - pass 156 - else - fail 156 - fi - # Must import twice since the first time uses inline code that - # avoids RCS call. - echo testb >>test - if ${testcvs} import -m "$message" a-dir A A2 >>${LOGFILE} 2>&1;then - pass 157 - else - fail 157 - fi - # Test handling of -m during ci - cd ..; rm -r a-dir - if ${testcvs} co a-dir >>${LOGFILE} 2>&1; then - pass 158 - else - fail 158 - fi - cd a-dir - echo testc >>test - if ${testcvs} ci -m "$message" >>${LOGFILE} 2>&1; then - pass 159 - else - fail 159 - fi - # Test handling of -m during rm/ci - rm test; - if ${testcvs} rm test >>${LOGFILE} 2>&1; then - pass 160 - else - fail 160 - fi - if ${testcvs} ci -m "$message" >>${LOGFILE} 2>&1; then - pass 161 - else - fail 161 - fi - # Clean up - cd .. - rm -r a-dir - rm -rf ${CVSROOT_DIRNAME}/a-dir - done - ;; - - editor) - # More tests of log messages, in this case the ability to - # run an external editor. - # TODO: - # * also test $EDITOR, $CVSEDITOR, &c. - # * test what happens if up-to-date check fails. + cvsadm) + # These test check the content of CVS' administrative + # files as they are checked out in various configurations. + # (As a side note, I'm not using the "-q" flag in any of + # this code, which should provide some extra checking for + # those messages which don't seem to be checked thoroughly + # anywhere else.) To do a thorough test, we need to make + # a bunch of modules in various configurations. + # + # <1mod> is a directory at the top level of cvsroot + # ``foo bar'' + # <2mod> is a directory at the second level of cvsroot + # ``foo bar/baz'' + # <1d1mod> is a directory at the top level which is + # checked out into another directory + # ``foo -d bar baz'' + # <1d2mod> is a directory at the second level which is + # checked out into another directory + # ``foo -d bar baz/quux'' + # <2d1mod> is a directory at the top level which is + # checked out into a directory that is two deep + # ``foo -d bar/baz quux'' + # <2d2mod> is a directory at the second level which is + # checked out into a directory that is two deep + # ``foo -d bar/baz quux'' + # + # The tests do each of these types separately and in twos. + # We also repeat each test -d flag for 1-deep and 2-deep + # directories. + # + # Each test should check the output for the Repository + # file, since that is the one which varies depending on + # the directory and how it was checked out. + # + # Yes, this is verbose, but at least it's very thorough. - # Our "editor" puts "x" at the start of each line, so we - # can see the "CVS:" lines. - cat >${TESTDIR}/editme <${TESTDIR}/edit.new -mv ${TESTDIR}/edit.new \$1 -exit 0 -EOF - chmod +x ${TESTDIR}/editme + # convenience variables + REP=${CVSROOT} + # First, check out the modules file and edit it. mkdir 1; cd 1 - dotest editor-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest editor-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - touch file1 file2 - dotest editor-3 "${testcvs} add file1 file2" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: scheduling file .file2. for addition -${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest editor-4 "${testcvs} -e ${TESTDIR}/editme -q ci" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v + dotest cvsadm-1 "${testcvs} co CVSROOT/modules" \ +"U CVSROOT/modules" + + # Try to determine whether RELATIVE_REPOS is defined + # so that we can make the following a lot less + # verbose. + + echo "${CVSROOT_DIRNAME}/." > ${TESTDIR}/dotest.abs + echo "." > ${TESTDIR}/dotest.rel + if cmp ${TESTDIR}/dotest.abs CVS/Repository >/dev/null 2>&1; then + AREP="${CVSROOT_DIRNAME}/" + elif cmp ${TESTDIR}/dotest.rel CVS/Repository >/dev/null 2>&1; then + AREP="" + else + fail "Cannot figure out if RELATIVE_REPOS is defined." + fi + + # Test CVS/Root once. Since there is only one part of + # the code which writes CVS/Root files (Create_Admin), + # there is no point in testing this every time. + dotest cvsadm-1a "cat CVS/Root" ${REP} + dotest cvsadm-1b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1c "cat CVSROOT/CVS/Root" ${REP} + dotest cvsadm-1d "cat CVSROOT/CVS/Repository" \ +"${AREP}CVSROOT" + # All of the defined module names begin with a number. + # All of the top-level directory names begin with "dir". + # All of the subdirectory names begin with "sub". + # All of the top-level modules begin with "mod". + echo "# Module defs for cvsadm tests" > CVSROOT/modules + echo "1mod mod1" >> CVSROOT/modules + echo "1mod-2 mod1-2" >> CVSROOT/modules + echo "2mod mod2/sub2" >> CVSROOT/modules + echo "2mod-2 mod2-2/sub2-2" >> CVSROOT/modules + echo "1d1mod -d dir1d1 mod1" >> CVSROOT/modules + echo "1d1mod-2 -d dir1d1-2 mod1-2" >> CVSROOT/modules + echo "1d2mod -d dir1d2 mod2/sub2" >> CVSROOT/modules + echo "1d2mod-2 -d dir1d2-2 mod2-2/sub2-2" >> CVSROOT/modules + echo "2d1mod -d dir2d1/sub2d1 mod1" >> CVSROOT/modules + echo "2d1mod-2 -d dir2d1-2/sub2d1-2 mod1-2" >> CVSROOT/modules + echo "2d2mod -d dir2d2/sub2d2 mod2/sub2" >> CVSROOT/modules + echo "2d2mod-2 -d dir2d2-2/sub2d2-2 mod2-2/sub2-2" >> CVSROOT/modules + dotest cvsadm-1e "${testcvs} ci -m add-modules" \ +"${PROG} [a-z]*: Examining . +${PROG} [a-z]*: Examining CVSROOT +Checking in CVSROOT/modules; +${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- modules +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 +${PROG} [a-z]*: Rebuilding administrative file database" + rm -rf CVS CVSROOT; + + # Create the various modules + mkdir ${CVSROOT_DIRNAME}/mod1 + mkdir ${CVSROOT_DIRNAME}/mod1-2 + mkdir ${CVSROOT_DIRNAME}/mod2 + mkdir ${CVSROOT_DIRNAME}/mod2/sub2 + mkdir ${CVSROOT_DIRNAME}/mod2-2 + mkdir ${CVSROOT_DIRNAME}/mod2-2/sub2-2 + dotest cvsadm-2 "${testcvs} co mod1 mod1-2 mod2 mod2-2" \ +"${PROG} [a-z]*: Updating mod1 +${PROG} [a-z]*: Updating mod1-2 +${PROG} [a-z]*: Updating mod2 +${PROG} [a-z]*: Updating mod2/sub2 +${PROG} [a-z]*: Updating mod2-2 +${PROG} [a-z]*: Updating mod2-2/sub2-2" + + # Populate the directories for the halibut + echo "file1" > mod1/file1 + echo "file1-2" > mod1-2/file1-2 + echo "file2" > mod2/sub2/file2 + echo "file2-2" > mod2-2/sub2-2/file2-2 + dotest cvsadm-2a "${testcvs} add mod1/file1 mod1-2/file1-2 mod2/sub2/file2 mod2-2/sub2-2/file2-2" \ +"${PROG} [a-z]*: scheduling file .mod1/file1. for addition +${PROG} [a-z]*: scheduling file .mod1-2/file1-2. for addition +${PROG} [a-z]*: scheduling file .mod2/sub2/file2. for addition +${PROG} [a-z]*: scheduling file .mod2-2/sub2-2/file2-2. for addition +${PROG} [a-z]*: use '${PROG} commit' to add these files permanently" + + dotest cvsadm-2b "${testcvs} ci -m yup mod1 mod1-2 mod2 mod2-2" \ +"${PROG} [a-z]*: Examining mod1 +${PROG} [a-z]*: Examining mod1-2 +${PROG} [a-z]*: Examining mod2 +${PROG} [a-z]*: Examining mod2/sub2 +${PROG} [a-z]*: Examining mod2-2 +${PROG} [a-z]*: Examining mod2-2/sub2-2 +RCS file: ${CVSROOT_DIRNAME}/mod1/file1,v done -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Checking in mod1/file1; +${CVSROOT_DIRNAME}/mod1/file1,v <-- file1 +initial revision: 1.1 done -Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -initial revision: 1\.1 -done" - dotest editor-5 "${testcvs} -q tag -b br" "T file1 -T file2" - dotest editor-6 "${testcvs} -q update -r br" '' - echo modify >>file1 - dotest editor-7 "${testcvs} -e ${TESTDIR}/editme -q ci" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done" - # OK, now we want to make sure "ci -r" puts in the branch - # where appropriate. Note that we can check in on the branch - # without being on the branch, because there is not a revision - # already on the branch. If there were a revision on the branch, - # CVS would correctly give an up-to-date check failed. - dotest editor-8 "${testcvs} -q update -A" "U file1" - echo add a line >>file2 - dotest editor-9 "${testcvs} -q -e ${TESTDIR}/editme ci -rbr file2" \ -"Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -new revision: 1\.1\.2\.1; previous revision: 1\.1 +RCS file: ${CVSROOT_DIRNAME}/mod1-2/file1-2,v +done +Checking in mod1-2/file1-2; +${CVSROOT_DIRNAME}/mod1-2/file1-2,v <-- file1-2 +initial revision: 1.1 +done +RCS file: ${CVSROOT_DIRNAME}/mod2/sub2/file2,v +done +Checking in mod2/sub2/file2; +${CVSROOT_DIRNAME}/mod2/sub2/file2,v <-- file2 +initial revision: 1.1 +done +RCS file: ${CVSROOT_DIRNAME}/mod2-2/sub2-2/file2-2,v +done +Checking in mod2-2/sub2-2/file2-2; +${CVSROOT_DIRNAME}/mod2-2/sub2-2/file2-2,v <-- file2-2 +initial revision: 1.1 done" + # Finished creating the modules -- clean up. + rm -rf CVS mod1 mod1-2 mod2 mod2-2 + # Done. - dotest editor-log-file1 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 2; selected revisions: 2 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -branches: 1\.1\.2; -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Committing in . -xCVS: -xCVS: Added Files: -xCVS: file1 file2 -xCVS: ---------------------------------------------------------------------- ----------------------------- -revision 1\.1\.2\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Committing in . -xCVS: -xCVS: Modified Files: -xCVS: Tag: br -xCVS: file1 -xCVS: ---------------------------------------------------------------------- -=============================================================================" + ################################################## + ## Start the dizzying array of possibilities. + ## Begin with each module type separately. + ################################################## + + # Pattern -- after each checkout, first check the top-level + # CVS directory. Then, check the directories in numerical + # order. - # The only difference between the two expect strings is the - # presence or absence of "Committing in ." for 1.1.2.1. - dotest editor-log-file2 "${testcvs} log -N file2" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -Working file: file2 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 2; selected revisions: 2 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -branches: 1\.1\.2; -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Committing in . -xCVS: -xCVS: Added Files: -xCVS: file1 file2 -xCVS: ---------------------------------------------------------------------- ----------------------------- -revision 1\.1\.2\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Modified Files: -xCVS: Tag: br -xCVS: file2 -xCVS: ---------------------------------------------------------------------- -=============================================================================" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -Working file: file2 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 2; selected revisions: 2 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -branches: 1\.1\.2; -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Committing in . -xCVS: -xCVS: Added Files: -xCVS: file1 file2 -xCVS: ---------------------------------------------------------------------- ----------------------------- -revision 1\.1\.2\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -x -xCVS: ---------------------------------------------------------------------- -xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically -xCVS: -xCVS: Committing in . -xCVS: -xCVS: Modified Files: -xCVS: Tag: br -xCVS: file2 -xCVS: ---------------------------------------------------------------------- -=============================================================================" - cd ../.. - rm -r 1 - rm ${TESTDIR}/editme - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest cvsadm-3 "${testcvs} co 1mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1" + dotest cvsadm-3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-3d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 1mod - errmsg1) - mkdir ${CVSROOT_DIRNAME}/1dir - mkdir 1 - cd 1 - if ${testcvs} -q co 1dir; then - pass 162 - else - fail 162 - fi - cd 1dir - touch foo - if ${testcvs} add foo 2>>${LOGFILE}; then - pass 163 - else - fail 163 - fi - if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then - pass 164 - else - fail 164 - fi - cd ../.. - mkdir 2 - cd 2 - if ${testcvs} -q co 1dir >>${LOGFILE}; then - pass 165 - else - fail 165 - fi - chmod a-w 1dir - cd ../1/1dir - rm foo; - if ${testcvs} rm foo >>${LOGFILE} 2>&1; then - pass 166 - else - fail 166 - fi - if ${testcvs} ci -m removed >>${LOGFILE} 2>&1; then - pass 167 - else - fail 167 - fi + dotest cvsadm-4 "${testcvs} co 2mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2" + dotest cvsadm-4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-4d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 2mod - cd ../../2/1dir - # FIXME: should be using dotest. - ${testcvs} -q update 2>../tst167.err - cat ../tst167.err >>${LOGFILE} - cat <../tst167.ans -${PROG} server: warning: foo is not (any longer) pertinent -${PROG} update: unable to remove ./foo: Permission denied -EOF - if cmp ../tst167.ans ../tst167.err >/dev/null || - ( echo "${PROG} [update aborted]: cannot rename file foo to CVS/,,foo: Permission denied" | cmp - ../tst167.err >/dev/null ) - then - pass 168 - else - fail 168 - fi + dotest cvsadm-5 "${testcvs} co 1d1mod" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1" + dotest cvsadm-5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-5d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir1d1 - cd .. - chmod u+w 1dir - cd .. - rm -r 1 2 - rm -rf ${CVSROOT_DIRNAME}/1dir - ;; + dotest cvsadm-6 "${testcvs} co 1d2mod" \ +"${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2" + dotest cvsadm-6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-6d "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir1d2 - errmsg2) - # More tests of various miscellaneous error handling, - # and cvs add behavior in general. - # See also test basicb-4a, concerning "cvs ci CVS". - # Too many tests to mention test the simple cases of - # adding files and directories. - # Test basicb-2a10 tests cvs -n add. + dotest cvsadm-7 "${testcvs} co 2d1mod" \ +"${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + dotest cvsadm-7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-7d "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-7f "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir2d1 - # First the usual setup; create a directory first-dir. - mkdir 1; cd 1 - dotest errmsg2-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest errmsg2-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - dotest_fail errmsg2-3 "${testcvs} add CVS" \ -"${PROG} [a-z]*: cannot add special file .CVS.; skipping" - touch file1 - # For the most part add returns a failure exitstatus if - # there are any errors, even if the remaining files are - # processed without incident. The "cannot add - # special file" message fits this pattern, at - # least currently. - dotest_fail errmsg2-4 "${testcvs} add CVS file1" \ -"${PROG} [a-z]*: cannot add special file .CVS.; skipping -${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - # I'm not sure these tests completely convey the various strange - # behaviors that CVS had before it specially checked for "." and - # "..". Suffice it to say that these are unlikely to work right - # without a special case. - dotest_fail errmsg2-5 "${testcvs} add ." \ -"${PROG} [a-z]*: cannot add special file .\..; skipping" - dotest_fail errmsg2-6 "${testcvs} add .." \ -"${PROG} [a-z]*: cannot add special file .\.\..; skipping" - # Make sure that none of the error messages left droppings - # which interfere with normal operation. - dotest errmsg2-7 "${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" - mkdir sdir - cd .. - dotest errmsg2-8 "${testcvs} add first-dir/sdir" \ -"Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" + dotest cvsadm-8 "${testcvs} co 2d2mod" \ +"${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + dotest cvsadm-8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-8d "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-8f "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir2d2 - cd first-dir + ################################################## + ## You are in a shell script of twisted little + ## module combination statements, all alike. + ################################################## - touch file10 - mkdir sdir10 - dotest errmsg2-10 "${testcvs} add file10 sdir10" \ -"${PROG} [a-z]*: scheduling file .file10. for addition -Directory ${TESTDIR}/cvsroot/first-dir/sdir10 added to the repository -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest errmsg2-11 "${testcvs} -q ci -m add-file10" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file10,v -done -Checking in file10; -${TESTDIR}/cvsroot/first-dir/file10,v <-- file10 -initial revision: 1\.1 -done" - # Try to see that there are no droppings left by - # any of the previous tests. - dotest errmsg2-12 "${testcvs} -q update" "" + ### 1mod + + dotest cvsadm-9 "${testcvs} co 1mod 1mod-2" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating 1mod-2 +U 1mod-2/file1-2" + # the usual for the top level + dotest cvsadm-9b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-9d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1mod copy + dotest cvsadm-9f "cat 1mod-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS 1mod 1mod-2 - # Now test adding files with '/' in the name, both one level - # down and more than one level down. - cd .. - mkdir first-dir/sdir10/ssdir - dotest errmsg2-13 "${testcvs} add first-dir/sdir10/ssdir" \ -"Directory ${TESTDIR}/cvsroot/first-dir/sdir10/ssdir added to the repository" + # 1mod 2mod redmod bluemod + dotest cvsadm-10 "${testcvs} co 1mod 2mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating 2mod +U 2mod/file2" + # the usual for the top level + dotest cvsadm-10b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-10d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2dmod + dotest cvsadm-10f "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 1mod 2mod - touch first-dir/sdir10/ssdir/ssfile - dotest errmsg2-14 \ - "${testcvs} add first-dir/sdir10/ssdir/ssfile" \ -"${PROG} [a-z]*: scheduling file .first-dir/sdir10/ssdir/ssfile. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - touch first-dir/file15 - dotest errmsg2-15 "${testcvs} add first-dir/file15" \ -"${PROG} [a-z]*: scheduling file .first-dir/file15. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest cvsadm-11 "${testcvs} co 1mod 1d1mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1" + # the usual for the top level + dotest cvsadm-11b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-11d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d1mod + dotest cvsadm-11f "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 1mod dir1d1 - # Now the case where we try to give it a directory which is not - # under CVS control. - mkdir bogus-dir - touch bogus-dir/file16 - # The first message, from local CVS, is nice. The second one - # is not nice; would be good to fix remote CVS to give a clearer - # message (e.g. the one from local CVS). But at least it is an - # error message. - dotest_fail errmsg2-16 "${testcvs} add bogus-dir/file16" \ -"${PROG} [a-z]*: in directory bogus-dir: -${PROG} \[[a-z]* aborted\]: there is no version here; do .${PROG} checkout. first" \ -"${PROG} [a-z]*: cannot open CVS/Entries for reading: No such file or directory -${PROG} \[add aborted\]: no repository" - rm -r bogus-dir + dotest cvsadm-12 "${testcvs} co 1mod 1d2mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2" + # the usual for the top level + dotest cvsadm-12b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-12d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d2mod + dotest cvsadm-12f "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 1mod dir1d2 - # One error condition we don't test for is trying to add a file - # or directory which already is there. + dotest cvsadm-13 "${testcvs} co 1mod 2d1mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + # the usual for the top level + dotest cvsadm-13b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-13d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-13f "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-13h "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 1mod dir2d1 - dotest errmsg2-17 "${testcvs} -q ci -m checkin" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file15,v -done -Checking in first-dir/file15; -${TESTDIR}/cvsroot/first-dir/file15,v <-- file15 -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/sdir10/ssdir/ssfile,v -done -Checking in first-dir/sdir10/ssdir/ssfile; -${TESTDIR}/cvsroot/first-dir/sdir10/ssdir/ssfile,v <-- ssfile -initial revision: 1\.1 -done" + dotest cvsadm-14 "${testcvs} co 1mod 2d2mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1 +${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + # the usual for the top level + dotest cvsadm-14b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-14d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-14f "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-14h "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 1mod dir2d2 - cd .. - rm -r 1 - rm -rf ${TESTDIR}/cvsroot/first-dir - ;; - devcom) - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1 - cd 1 - if ${testcvs} -q co first-dir >>${LOGFILE} ; then - pass 169 - else - fail 169 - fi + ### 2mod + + dotest cvsadm-15 "${testcvs} co 2mod 2mod-2" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2 +${PROG} [a-z]*: Updating 2mod-2 +U 2mod-2/file2-2" + # the usual for the top level + dotest cvsadm-15b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-15d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2mod copy + dotest cvsadm-15f "cat 2mod-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS 2mod 2mod-2 - cd first-dir - echo abb >abb - if ${testcvs} add abb 2>>${LOGFILE}; then - pass 170 - else - fail 170 - fi - if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then - pass 171 - else - fail 171 - fi - dotest_fail 171a0 "${testcvs} watch" "Usage${DOTSTAR}" - if ${testcvs} watch on; then - pass 172 - else - fail 172 - fi - echo abc >abc - if ${testcvs} add abc 2>>${LOGFILE}; then - pass 173 - else - fail 173 - fi - if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then - pass 174 - else - fail 174 - fi - cd ../.. - mkdir 2 - cd 2 + dotest cvsadm-16 "${testcvs} co 2mod 1d1mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2 +${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1" + # the usual for the top level + dotest cvsadm-16b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-16d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d1mod + dotest cvsadm-16f "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 2mod dir1d1 - if ${testcvs} -q co first-dir >>${LOGFILE}; then - pass 175 - else - fail 175 - fi - cd first-dir - if test -w abb; then - fail 176 - else - pass 176 - fi - if test -w abc; then - fail 177 - else - pass 177 - fi + dotest cvsadm-17 "${testcvs} co 2mod 1d2mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2 +${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2" + # the usual for the top level + dotest cvsadm-17b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-17d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d2mod + dotest cvsadm-17f "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 2mod dir1d2 - if ${testcvs} editors >../ans178.tmp; then - pass 178 - else - fail 178 - fi - cat ../ans178.tmp >>${LOGFILE} - if test -s ../ans178.tmp; then - fail 178a - else - pass 178a - fi - - if ${testcvs} edit abb; then - pass 179 - else - fail 179 - fi + dotest cvsadm-18 "${testcvs} co 2mod 2d1mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2 +${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + # the usual for the top level + dotest cvsadm-18b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-18d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d1mod + dotest cvsadm-18f "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-18h "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 2mod dir2d1 - if ${testcvs} editors >../ans180.tmp; then - pass 180 - else - fail 180 - fi - cat ../ans180.tmp >>${LOGFILE} - if test -s ../ans180.tmp; then - pass 181 - else - fail 181 - fi + dotest cvsadm-19 "${testcvs} co 2mod 2d2mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2 +${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + # the usual for the top level + dotest cvsadm-19b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-19d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-19f "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-19h "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 2mod dir2d2 - echo aaaa >>abb - if ${testcvs} ci -m modify abb >>${LOGFILE} 2>&1; then - pass 182 - else - fail 182 - fi - # Unedit of a file not being edited should be a noop. - dotest 182.5 "${testcvs} unedit abb" '' - if ${testcvs} editors >../ans183.tmp; then - pass 183 - else - fail 183 - fi - cat ../ans183.tmp >>${LOGFILE} - if test -s ../ans183.tmp; then - fail 184 - else - pass 184 - fi + ### 1d1mod - if test -w abb; then - fail 185 - else - pass 185 - fi + dotest cvsadm-20 "${testcvs} co 1d1mod 1d1mod-2" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1 +${PROG} [a-z]*: Updating dir1d1-2 +U dir1d1-2/file1-2" + # the usual for the top level + dotest cvsadm-20b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-20d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d1mod copy + dotest cvsadm-20f "cat dir1d1-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS dir1d1 dir1d1-2 - if ${testcvs} edit abc; then - pass 186a1 - else - fail 186a1 - fi - # Unedit of an unmodified file. - if ${testcvs} unedit abc; then - pass 186a2 - else - fail 186a2 - fi - if ${testcvs} edit abc; then - pass 186a3 - else - fail 186a3 - fi - echo changedabc >abc - # Try to unedit a modified file; cvs should ask for confirmation - if (echo no | ${testcvs} unedit abc) >>${LOGFILE}; then - pass 186a4 - else - fail 186a4 - fi - if echo changedabc | cmp - abc; then - pass 186a5 - else - fail 186a5 - fi - # OK, now confirm the unedit - if (echo yes | ${testcvs} unedit abc) >>${LOGFILE}; then - pass 186a6 - else - fail 186a6 - fi - if echo abc | cmp - abc; then - pass 186a7 - else - fail 186a7 - fi + dotest cvsadm-21 "${testcvs} co 1d1mod 1d2mod" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1 +${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2" + # the usual for the top level + dotest cvsadm-21b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-21d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d2mod + dotest cvsadm-21f "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir1d1 dir1d2 - dotest devcom-a0 "${testcvs} watchers" '' + dotest cvsadm-22 "${testcvs} co 1d1mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1 +${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + # the usual for the top level + dotest cvsadm-22b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-22d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-22f "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-22h "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir1d1 dir2d1 - # FIXME: This probably should be an error message instead - # of silently succeeding and printing nothing. - dotest devcom-a-nonexist "${testcvs} watchers nonexist" '' + dotest cvsadm-23 "${testcvs} co 1d1mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1 +${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + # the usual for the top level + dotest cvsadm-23b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-23d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-23f "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-23h "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir1d1 dir2d2 - dotest devcom-a1 "${testcvs} watch add" '' - dotest devcom-a2 "${testcvs} watchers" \ -"abb ${username} edit unedit commit -abc ${username} edit unedit commit" - dotest devcom-a3 "${testcvs} watch remove -a unedit abb" '' - dotest devcom-a4 "${testcvs} watchers abb" \ -"abb ${username} edit commit" - # Check tagging and checking out while we have a CVS - # directory in the repository. - dotest devcom-t0 "${testcvs} -q tag tag" \ -'T abb -T abc' - cd ../.. - mkdir 3 - cd 3 + ### 1d2mod - # Test commented out because the bug it tests for is not fixed - # The error is: - # cvs watchers: cannot open CVS/Entries for reading: No such file or directory - # cvs: ../../work/ccvs/src/fileattr.c:75: fileattr_read: Assertion `fileattr_stored_repos != ((void *)0)' failed. -: dotest devcom-t-nonexist "${testcvs} watchers nonexist" fixme + dotest cvsadm-24 "${testcvs} co 1d2mod 1d2mod-2" \ +"${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2 +${PROG} [a-z]*: Updating dir1d2-2 +U dir1d2-2/file2-2" + # the usual for the top level + dotest cvsadm-24b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-24d "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d2mod copy + dotest cvsadm-24f "cat dir1d2-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS dir1d2 dir1d2-2 - dotest devcom-t1 "${testcvs} -q co -rtag first-dir/abb" \ -'U first-dir/abb' - cd .. - # Since first-dir/abb is readonly, use -f. - rm -rf 3 + dotest cvsadm-25 "${testcvs} co 1d2mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2 +${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + # the usual for the top level + dotest cvsadm-25b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-25d "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d1mod + dotest cvsadm-25f "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-25h "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir1d2 dir2d1 - # Test checking out the directory rather than the file. - mkdir 3 - cd 3 - dotest devcom-t2 "${testcvs} -q co -rtag first-dir" \ -'U first-dir/abb -U first-dir/abc' - cd .. - # Since the files are readonly, use -f. - rm -rf 3 + dotest cvsadm-26 "${testcvs} co 1d2mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2 +${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + # the usual for the top level + dotest cvsadm-26b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-26d "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-26f "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-26h "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir1d2 dir2d2 - # Now do it again, after removing the val-tags file created - # by devcom-t1 to force CVS to search the repository - # containing CVS directories. - rm ${CVSROOT_DIRNAME}/CVSROOT/val-tags - mkdir 3 - cd 3 - dotest devcom-t3 "${testcvs} -q co -rtag first-dir" \ -'U first-dir/abb -U first-dir/abc' - cd .. - # Since the files are readonly, use -f. - rm -rf 3 - # Now remove all the file attributes - cd 2/first-dir - dotest devcom-b0 "${testcvs} watch off" '' - dotest devcom-b1 "${testcvs} watch remove" '' - # Test that CVS 1.6 and earlier can handle the repository. - dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" + # 2d1mod - # Now test watching just some, not all, files. - dotest devcom-some0 "${testcvs} watch on abc" '' - cd ../.. - mkdir 3 - cd 3 - dotest devcom-some1 "${testcvs} -q co first-dir" 'U first-dir/abb -U first-dir/abc' - dotest devcom-some2 "test -w first-dir/abb" '' - dotest_fail devcom-some3 "test -w first-dir/abc" '' - cd .. + dotest cvsadm-27 "${testcvs} co 2d1mod 2d1mod-2" \ +"${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1 +${PROG} [a-z]*: Updating dir2d1-2/sub2d1-2 +U dir2d1-2/sub2d1-2/file1-2" + # the usual for the top level + dotest cvsadm-27b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2d1mod + dotest cvsadm-27d "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-27f "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-27h "cat dir2d1-2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-27j "cat dir2d1-2/sub2d1-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS dir2d1 dir2d1-2 - if test "$keep" = yes; then - echo Keeping ${TESTDIR} and exiting due to --keep - exit 0 - fi + dotest cvsadm-28 "${testcvs} co 2d1mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1 +${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + # the usual for the top level + dotest cvsadm-28b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2d1mod + dotest cvsadm-28d "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-28f "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-28h "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-28j "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir2d1 dir2d2 - # Use -f because of the readonly files. - rm -rf 1 2 3 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + + # 2d2mod - devcom2) - # More watch tests, most notably setting watches on - # files in various different states. - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1 - cd 1 - dotest devcom2-1 "${testcvs} -q co first-dir" '' - cd first-dir + dotest cvsadm-29 "${testcvs} co 2d2mod 2d2mod-2" \ +"${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2 +${PROG} [a-z]*: Updating dir2d2-2/sub2d2-2 +U dir2d2-2/sub2d2-2/file2-2" + # the usual for the top level + dotest cvsadm-29b "cat CVS/Repository" \ +"${AREP}\." + # the usual for 2d2mod + dotest cvsadm-29d "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-29f "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-29h "cat dir2d2-2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-29j "cat dir2d2-2/sub2d2-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS dir2d2 dir2d2-2 - # This should probably be an error; setting a watch on a totally - # unknown file is more likely to be a typo than intentional. - # But that isn't the currently implemented behavior. - dotest devcom2-2 "${testcvs} watch on w1" '' + ################################################## + ## And now, all of that again using the "-d" flag + ## on the command line. + ################################################## - touch w1 w2 w3 nw1 - dotest devcom2-3 "${testcvs} add w1 w2 w3 nw1" "${DOTSTAR}" - # Letting the user set the watch here probably can be considered - # a feature--although it leads to a few potentially strange - # consequences like one user can set the watch and another actually - # adds the file. - dotest devcom2-4 "${testcvs} watch on w2" '' - dotest devcom2-5 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + dotest cvsadm-1d3 "${testcvs} co -d dir 1mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file1" + dotest cvsadm-1d3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d3d "cat dir/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - # Note that this test differs in a subtle way from devcom-some0; - # in devcom-some0 the watch is creating a new fileattr file, and - # here we are modifying an existing one. - dotest devcom2-6 "${testcvs} watch on w3" '' + dotest cvsadm-1d4 "${testcvs} co -d dir 2mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file2" + dotest cvsadm-1d4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d4d "cat dir/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Now test that all the watches got set on the correct files - # FIXME: CVS should have a way to report whether watches are - # set, I think. The "check it out and see if it read-only" is - # sort of OK, but is complicated by CVSREAD and doesn't help - # if the file is added and not yet committed or some such. - # Probably "cvs status" should report "watch: on" if watch is on - # (and nothing if watch is off, so existing behavior is preserved). - cd ../.. - mkdir 2 - cd 2 - dotest devcom2-7 "${testcvs} -q co first-dir" 'U first-dir/nw1 -U first-dir/w1 -U first-dir/w2 -U first-dir/w3' - dotest devcom2-8 "test -w first-dir/nw1" '' - dotest_fail devcom2-9 "test -w first-dir/w1" '' - dotest_fail devcom2-10 "test -w first-dir/w2" '' - dotest_fail devcom2-11 "test -w first-dir/w3" '' + dotest cvsadm-1d5 "${testcvs} co -d dir 1d1mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file1" + dotest cvsadm-1d5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d5d "cat dir/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - cd first-dir - # OK, now we want to try files in various states with cvs edit. - dotest devcom2-12 "${testcvs} edit w4" \ -"${PROG} edit: no such file w4; ignored" - # Try the same thing with a per-directory watch set. - dotest devcom2-13 "${testcvs} watch on" '' - dotest devcom2-14 "${testcvs} edit w5" \ -"${PROG} edit: no such file w5; ignored" - dotest devcom2-15 "${testcvs} editors" '' - dotest devcom2-16 "${testcvs} editors w4" '' - # Make sure there are no droppings lying around - dotest devcom2-17 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw1 _watched= -Fw2 _watched= -Fw3 _watched= -Fnw1 _watched= -D _watched=" - cd .. + dotest cvsadm-1d6 "${testcvs} co -d dir 1d2mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file2" + dotest cvsadm-1d6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d6d "cat dir/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - cd .. + dotest cvsadm-1d7 "${testcvs} co -d dir 2d1mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file1" + dotest cvsadm-1d7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d7d "cat dir/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - # Use -f because of the readonly files. - rm -rf 1 2 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest cvsadm-1d8 "${testcvs} co -d dir 2d2mod" \ +"${PROG} [a-z]*: Updating dir +U dir/file2" + dotest cvsadm-1d8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-1d8d "cat dir/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - devcom3) - # More watch tests, most notably handling of features designed - # for future expansion. - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1 - cd 1 - dotest devcom3-1 "${testcvs} -q co first-dir" '' - cd first-dir + ################################################## + ## Los Combonaciones + ################################################## - touch w1 w2 - dotest devcom3-2 "${testcvs} add w1 w2" "${DOTSTAR}" - dotest devcom3-3 "${testcvs} watch on w1 w2" '' - dotest devcom3-4 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + ### 1mod - # OK, since we are about to delve into CVS's internals, make - # sure that we seem to be correct about how they work. - dotest devcom3-5 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw1 _watched= -Fw2 _watched=" - # Now write a few more lines, just as if we were a newer version - # of CVS implementing some new feature. - cat <>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr -Enew line here -G@#$^!@#=& -EOF - # Now get CVS to write to the fileattr file.... - dotest devcom3-6 "${testcvs} watch off w1" '' - # ...and make sure that it hasn't clobbered our new lines. - # Note that writing these lines in another order would be OK - # too. - dotest devcom3-7 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw2 _watched= -G@#..!@#=& -Enew line here" - - # See what CVS does when a file name is duplicated. The - # behavior of all versions of CVS since file attributes were - # implemented is that it nukes the duplications. This seems - # reasonable enough, although it means it isn't clear how - # useful duplicates would be for purposes of future - # expansion. But in the interests of keeping behaviors - # predictable, might as well test for it, I guess. - echo 'Fw2 duplicate=' >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr - dotest devcom3-8 "${testcvs} watch on w1" '' - dotest devcom3-9 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw2 _watched= -Fw1 _watched= -Enew line here -G@#..!@#=&" + dotest cvsadm-1d9 "${testcvs} co -d dir 1mod 1mod-2" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/1mod-2 +U dir/1mod-2/file1-2" + # the usual for the top level + dotest cvsadm-1d9b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d9d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d9f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1mod copy + dotest cvsadm-1d9h "cat dir/1mod-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS dir - cd ../.. + # 1mod 2mod redmod bluemod + dotest cvsadm-1d10 "${testcvs} co -d dir 1mod 2mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2" + dotest cvsadm-1d10b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d10d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d10f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2dmod + dotest cvsadm-1d10h "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Use -f because of the readonly files. - rm -rf 1 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest cvsadm-1d11 "${testcvs} co -d dir 1mod 1d1mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1" + dotest cvsadm-1d11b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d11d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d11f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d1mod + dotest cvsadm-1d11h "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - watch4) - # More watch tests, including adding directories. - mkdir 1; cd 1 - dotest watch4-0a "${testcvs} -q co -l ." '' - mkdir first-dir - dotest watch4-0b "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + dotest cvsadm-1d12 "${testcvs} co -d dir 1mod 1d2mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2" + dotest cvsadm-1d12b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d12d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d12f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d2mod + dotest cvsadm-1d12h "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - cd first-dir - dotest watch4-1 "${testcvs} watch on" '' - # This is just like the 173 test - touch file1 - dotest watch4-2 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest watch4-3 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 -done" - # Now test the analogous behavior for directories. - mkdir subdir - dotest watch4-4 "${testcvs} add subdir" \ -"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository" - cd subdir - touch sfile - dotest watch4-5 "${testcvs} add sfile" \ -"${PROG} [a-z]*: scheduling file .sfile. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest watch4-6 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/sfile,v -done -Checking in sfile; -${TESTDIR}/cvsroot/first-dir/subdir/sfile,v <-- sfile -initial revision: 1\.1 -done" - cd ../../.. - mkdir 2; cd 2 - dotest watch4-7 "${testcvs} -q co first-dir" "U first-dir/file1 -U first-dir/subdir/sfile" - dotest_fail watch4-8 "test -w first-dir/file1" '' - dotest_fail watch4-9 "test -w first-dir/subdir/sfile" '' - cd first-dir - dotest watch4-10 "${testcvs} edit file1" '' - echo 'edited in 2' >file1 - cd ../.. + dotest cvsadm-1d13 "${testcvs} co -d dir 1mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1" + dotest cvsadm-1d13b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d13d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d13f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-1d13h "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d13j "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - cd 1/first-dir - dotest watch4-11 "${testcvs} edit file1" '' - echo 'edited in 1' >file1 - dotest watch4-12 "${testcvs} -q ci -m edit-in-1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 -done" - cd ../.. - cd 2/first-dir - dotest watch4-13 "${testcvs} -q update" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -retrieving revision 1\.1 -retrieving revision 1\.2 -Merging differences between 1\.1 and 1\.2 into file1 -rcsmerge: warning: conflicts during merge -${PROG} [a-z]*: conflicts found in file1 -C file1" - if (echo yes | ${testcvs} unedit file1) >>${LOGFILE}; then - pass watch4-14 - else - fail watch4-15 - fi - # This could plausibly be defined to either go back to the revision - # which was cvs edit'd (the status quo), or back to revision 1.2 - # (that is, the merge could update CVS/Base/file1). We pick the - # former because it is easier to implement, not because we have - # thought much about which is better. - dotest watch4-16 "cat file1" '' - # Make sure CVS really thinks we are at 1.1. - dotest watch4-17 "${testcvs} -q update" "U file1" - dotest watch4-18 "cat file1" "edited in 1" - cd ../.. + dotest cvsadm-1d14 "${testcvs} co -d dir 1mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1 +${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-1d14b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d14d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1mod + dotest cvsadm-1d14f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-1d14h "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d14j "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # As a sanity check, make sure we are in the right place. - dotest watch4-cleanup-1 "test -d 1" '' - dotest watch4-cleanup-1 "test -d 2" '' - # Specify -f because of the readonly files. - rm -rf 1 2 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; - ignore) - # On Windows, we can't check out CVSROOT, because the case - # insensitivity means that this conflicts with cvsroot. - mkdir wnt - cd wnt + ### 2mod - dotest 187a1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}" - cd CVSROOT - echo rootig.c >cvsignore - dotest 187a2 "${testcvs} add cvsignore" "${PROG}"' [a-z]*: scheduling file `cvsignore'"'"' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest cvsadm-1d15 "${testcvs} co -d dir 2mod 2mod-2" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2 +${PROG} [a-z]*: Updating dir/2mod-2 +U dir/2mod-2/file2-2" + dotest cvsadm-1d15b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d15d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-1d15f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2mod copy + dotest cvsadm-1d15h "cat dir/2mod-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS dir - # As of Jan 96, local CVS prints "Examining ." and remote doesn't. - # Accept either. - dotest 187a3 " ${testcvs} ci -m added" \ -"${PROG} [a-z]*: Examining \. -RCS file: ${TESTDIR}/cvsroot/CVSROOT/cvsignore,v -done -Checking in cvsignore; -${TESTDIR}/cvsroot/CVSROOT/cvsignore,v <-- cvsignore -initial revision: 1\.1 -done -${PROG} [a-z]*: Rebuilding administrative file database" + dotest cvsadm-1d16 "${testcvs} co -d dir 2mod 1d1mod" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2 +${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1" + dotest cvsadm-1d16b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d16d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-1d16f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d1mod + dotest cvsadm-1d16h "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - cd .. - if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then - pass 187a4 - else - fail 187a4 - fi + dotest cvsadm-1d17 "${testcvs} co -d dir 2mod 1d2mod" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2 +${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2" + dotest cvsadm-1d17b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d17d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-1d17f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d2mod + dotest cvsadm-1d17h "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # CVS looks at the home dir from getpwuid, not HOME (is that correct - # behavior?), so this is hard to test and we won't try. - # echo foobar.c >${HOME}/.cvsignore - CVSIGNORE=envig.c; export CVSIGNORE - mkdir dir-to-import - cd dir-to-import - touch foobar.c bar.c rootig.c defig.o envig.c optig.c - # We use sort because we can't predict the order in which - # the files will be listed. - dotest_sort 188a "${testcvs} import -m m -I optig.c first-dir tag1 tag2" \ -' + dotest cvsadm-1d18 "${testcvs} co -d dir 2mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2 +${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1" + dotest cvsadm-1d18b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d18d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-1d18f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d1mod + dotest cvsadm-1d18h "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d18j "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir -I first-dir/defig.o -I first-dir/envig.c -I first-dir/optig.c -I first-dir/rootig.c -N first-dir/bar.c -N first-dir/foobar.c -No conflicts created by this import' - dotest_sort 188b "${testcvs} import -m m -I ! second-dir tag3 tag4" \ -' + dotest cvsadm-1d19 "${testcvs} co -d dir 2mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2 +${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-1d19b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d19d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2mod + dotest cvsadm-1d19f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-1d19h "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d19j "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir -N second-dir/bar.c -N second-dir/defig.o -N second-dir/envig.c -N second-dir/foobar.c -N second-dir/optig.c -N second-dir/rootig.c -No conflicts created by this import' - cd .. - rm -r dir-to-import - mkdir 1 - cd 1 - dotest 189a "${testcvs} -q co second-dir" \ -'U second-dir/bar.c -U second-dir/defig.o -U second-dir/envig.c -U second-dir/foobar.c -U second-dir/optig.c -U second-dir/rootig.c' - dotest 189b "${testcvs} -q co first-dir" 'U first-dir/bar.c -U first-dir/foobar.c' - cd first-dir - touch rootig.c defig.o envig.c optig.c notig.c - dotest 189c "${testcvs} -q update -I optig.c" "${QUESTION} notig.c" - # The fact that CVS requires us to specify -I CVS here strikes me - # as a bug. - dotest_sort 189d "${testcvs} -q update -I ! -I CVS" \ -"${QUESTION} defig.o -${QUESTION} envig.c -${QUESTION} notig.c -${QUESTION} optig.c -${QUESTION} rootig.c" + ### 1d1mod - # Now test that commands other than update also print "? notig.c" - # where appropriate. Only test this for remote, because local - # CVS only prints it on update. - rm optig.c - if test "x$remote" = xyes; then - dotest 189e "${testcvs} -q diff" "${QUESTION} notig.c" + dotest cvsadm-1d20 "${testcvs} co -d dir 1d1mod 1d1mod-2" \ +"${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1 +${PROG} [a-z]*: Updating dir/dir1d1-2 +U dir/dir1d1-2/file1-2" + dotest cvsadm-1d20b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d20d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-1d20f "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d1mod copy + dotest cvsadm-1d20h "cat dir/dir1d1-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS dir - # Force the server to be contacted. Ugh. Having CVS - # contact the server for the sole purpose of checking - # the CVSROOT/cvsignore file does not seem like such a - # good idea, so I imagine this will continue to be - # necessary. Oh well, at least we test CVS's ablity to - # handle a file with a modified timestamp but unmodified - # contents. - touch bar.c + dotest cvsadm-1d21 "${testcvs} co -d dir 1d1mod 1d2mod" \ +"${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1 +${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2" + dotest cvsadm-1d21b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d21d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-1d21f "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 1d2mod + dotest cvsadm-1d21h "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - dotest 189f "${testcvs} -q ci -m commit-it" "${QUESTION} notig.c" - fi + dotest cvsadm-1d22 "${testcvs} co -d dir 1d1mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1 +${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1" + dotest cvsadm-1d22b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d22d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-1d22f "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-1d22h "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d22j "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - # now test .cvsignore files - cd .. - echo notig.c >first-dir/.cvsignore - echo foobar.c >second-dir/.cvsignore - touch first-dir/notig.c second-dir/notig.c second-dir/foobar.c - dotest_sort 190 "${testcvs} -qn update" \ -"${QUESTION} first-dir/.cvsignore -${QUESTION} second-dir/.cvsignore -${QUESTION} second-dir/notig.c" - dotest_sort 191 "${testcvs} -qn update -I! -I CVS" \ -"${QUESTION} first-dir/.cvsignore -${QUESTION} first-dir/defig.o -${QUESTION} first-dir/envig.c -${QUESTION} first-dir/rootig.c -${QUESTION} second-dir/.cvsignore -${QUESTION} second-dir/notig.c" + dotest cvsadm-1d23 "${testcvs} co -d dir 1d1mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1 +${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-1d23b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d23d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d1mod + dotest cvsadm-1d23f "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-1d23h "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d23j "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - if echo yes | ${testcvs} release -d first-dir \ - >${TESTDIR}/ignore.tmp; then - pass ignore-192 - else - fail ignore-192 - fi - dotest ignore-193 "cat ${TESTDIR}/ignore.tmp" \ -"${QUESTION} \.cvsignore -You have \[0\] altered files in this repository. -Are you sure you want to release (and delete) directory .first-dir': " - echo add a line >>second-dir/foobar.c - rm second-dir/notig.c second-dir/.cvsignore - if echo yes | ${testcvs} release -d second-dir \ - >${TESTDIR}/ignore.tmp; then - pass ignore-194 - else - fail ignore-194 - fi - dotest ignore-195 "cat ${TESTDIR}/ignore.tmp" \ -"M foobar.c -You have \[1\] altered files in this repository. -Are you sure you want to release (and delete) directory .second-dir': " - cd .. - rm -r 1 - cd .. - rm -r wnt - rm ${TESTDIR}/ignore.tmp - rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir - ;; - - binfiles) - # Test cvs's ability to handle binary files. - 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%c", 2, 10, 137, 0, 13, 10 }' \ - binfile.dat - cat binfile.dat binfile.dat >binfile2.dat - cd first-dir - cp ../binfile.dat binfile - dotest binfiles-2 "${testcvs} add -kb binfile" \ -"${PROG}"' [a-z]*: scheduling file `binfile'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - dotest binfiles-3 "${testcvs} -q ci -m add-it" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v -done -Checking in binfile; -${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile -initial revision: 1\.1 -done" - cd ../.. - mkdir 2; cd 2 - dotest binfiles-4 "${testcvs} -q co first-dir" 'U first-dir/binfile' - cd first-dir - dotest binfiles-5 "cmp ../../1/binfile.dat binfile" '' - # Testing that sticky options is -kb is the closest thing we have - # to testing that binary files work right on non-unix machines - # (until there is automated testing for such machines, of course). - dotest binfiles-5.5 "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + ### 1d2mod - Working revision: 1\.1.* - Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" + dotest cvsadm-1d24 "${testcvs} co -d dir 1d2mod 1d2mod-2" \ +"${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2 +${PROG} [a-z]*: Updating dir/dir1d2-2 +U dir/dir1d2-2/file2-2" + dotest cvsadm-1d24b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d24d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-1d24f "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 1d2mod copy + dotest cvsadm-1d24h "cat dir/dir1d2-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS dir - # Test whether the default options from the RCS file are - # also used when operating on files instead of whole - # directories - cd ../.. - mkdir 3; cd 3 - dotest binfiles-5.5b0 "${testcvs} -q co first-dir/binfile" \ -'U first-dir/binfile' - cd first-dir - dotest binfiles-5.5b1 "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + dotest cvsadm-1d25 "${testcvs} co -d dir 1d2mod 2d1mod" \ +"${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2 +${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1" + dotest cvsadm-1d25b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d25d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-1d25f "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d1mod + dotest cvsadm-1d25h "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d25j "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - Working revision: 1\.1.* - Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" - cd ../.. - rm -r 3 - cd 2/first-dir + dotest cvsadm-1d26 "${testcvs} co -d dir 1d2mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2 +${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-1d26b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d26d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 1d2mod + dotest cvsadm-1d26f "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-1d26h "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d26j "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - cp ../../1/binfile2.dat binfile - dotest binfiles-6 "${testcvs} -q ci -m modify-it" \ -"Checking in binfile; -${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1\.2; previous revision: 1\.1 -done" - cd ../../1/first-dir - dotest binfiles-7 "${testcvs} -q update" '[UP] binfile' - dotest binfiles-8 "cmp ../binfile2.dat binfile" '' - # Now test handling of conflicts with binary files. - cp ../binfile.dat binfile - dotest binfiles-con0 "${testcvs} -q ci -m modify-it" \ -"Checking in binfile; -${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1\.3; previous revision: 1\.2 -done" - cd ../../2/first-dir - echo 'edits in dir 2' >binfile - dotest binfiles-con1 "${testcvs} -q update" \ -"U binfile -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1\.3 from repository is now in binfile -${PROG} [a-z]*: file from working directory is now in \.#binfile\.1\.2 -C binfile" - dotest binfiles-con2 "cmp binfile ../../1/binfile.dat" '' - dotest binfiles-con3 "cat .#binfile.1.2" 'edits in dir 2' + # 2d1mod - cp ../../1/binfile2.dat binfile - dotest binfiles-con4 "${testcvs} -q ci -m resolve-it" \ -"Checking in binfile; -${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1\.4; previous revision: 1\.3 -done" - cd ../../1/first-dir - dotest binfiles-con5 "${testcvs} -q update" '[UP] binfile' + dotest cvsadm-1d27 "${testcvs} co -d dir 2d1mod 2d1mod-2" \ +"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1 +${PROG} [a-z]*: Updating dir/dir2d1-2/sub2d1-2 +U dir/dir2d1-2/sub2d1-2/file1-2" + dotest cvsadm-1d27b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d27d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2d1mod + dotest cvsadm-1d27f "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d27h "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d1mod + dotest cvsadm-1d27j "cat dir/dir2d1-2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d27l "cat dir/dir2d1-2/sub2d1-2/CVS/Repository" \ +"${AREP}mod1-2" + rm -rf CVS dir - dotest binfiles-9 "${testcvs} -q update -A" '' - dotest binfiles-10 "${testcvs} -q update -kk" '[UP] binfile' - dotest binfiles-11 "${testcvs} -q update" '' - dotest binfiles-12 "${testcvs} -q update -A" '[UP] binfile' - dotest binfiles-13 "${testcvs} -q update -A" '' + dotest cvsadm-1d28 "${testcvs} co -d dir 2d1mod 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1 +${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-1d28b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d28d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2d1mod + dotest cvsadm-1d28f "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d28h "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + # the usual for 2d2mod + dotest cvsadm-1d28j "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d28l "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - cd ../.. - rm -r 1 + + # 2d2mod - mkdir 3 - cd 3 - dotest binfiles-13a0 "${testcvs} -q co -r HEAD first-dir" \ -'U first-dir/binfile' - cd first-dir - dotest binfiles-13a1 "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + dotest cvsadm-1d29 "${testcvs} co -d dir 2d2mod 2d2mod-2" \ +"${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2 +${PROG} [a-z]*: Updating dir/dir2d2-2/sub2d2-2 +U dir/dir2d2-2/sub2d2-2/file2-2" + dotest cvsadm-1d29b "cat CVS/Repository" \ +"${AREP}\." + # the usual for the dir level + dotest cvsadm-1d29d "cat dir/CVS/Repository" \ +"${AREP}\." + # the usual for 2d2mod + dotest cvsadm-1d29f "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d29h "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + # the usual for 2d2mod + dotest cvsadm-1d29j "cat dir/dir2d2-2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-1d29l "cat dir/dir2d2-2/sub2d2-2/CVS/Repository" \ +"${AREP}mod2-2/sub2-2" + rm -rf CVS dir - Working revision: 1\.4.* - Repository revision: 1\.4 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: HEAD (revision: 1\.4) - Sticky Date: (none) - Sticky Options: -kb" - cd ../.. - rm -r 3 + ################################################## + ## And now, some of that again using the "-d" flag + ## on the command line, but use a longer path. + ################################################## - cd 2/first-dir - echo 'this file is $''RCSfile$' >binfile - dotest binfiles-14a "${testcvs} -q ci -m modify-it" \ -"Checking in binfile; -${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1\.5; previous revision: 1\.4 -done" - dotest binfiles-14b "cat binfile" 'this file is $''RCSfile$' - # See binfiles-5.5 for discussion of -kb. - dotest binfiles-14c "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + dotest cvsadm-2d3 "${testcvs} co -d dir/dir2 1mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file1" + dotest cvsadm-2d3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d3d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d3f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - Working revision: 1\.5.* - Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" - dotest binfiles-14d "${testcvs} admin -kv binfile" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v -done" - # cvs admin doesn't change the checked-out file or its sticky - # kopts. There probably should be a way which does (but - # what if the file is modified? And do we try to version - # control the kopt setting?) - dotest binfiles-14e "cat binfile" 'this file is $''RCSfile$' - dotest binfiles-14f "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + dotest cvsadm-2d4 "${testcvs} co -d dir/dir2 2mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file2" + dotest cvsadm-2d4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d4d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d4f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - Working revision: 1\.5.* - Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" - dotest binfiles-14g "${testcvs} -q update -A" '[UP] binfile' - dotest binfiles-14h "cat binfile" 'this file is binfile,v' - dotest binfiles-14i "${testcvs} status binfile" \ -"=================================================================== -File: binfile Status: Up-to-date + dotest cvsadm-2d5 "${testcvs} co -d dir/dir2 1d1mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file1" + dotest cvsadm-2d5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d5d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d5f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - Working revision: 1\.5.* - Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kv" + dotest cvsadm-2d6 "${testcvs} co -d dir/dir2 1d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file2" + dotest cvsadm-2d6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d6d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d6f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Do sticky options work when used with 'cvs update'? - echo "Not a binary file." > nibfile - dotest binfiles-sticky1 "${testcvs} -q add nibfile" \ -"${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest binfiles-sticky2 "${testcvs} -q ci -m add-it nibfile" \ - "RCS file: ${TESTDIR}/cvsroot/first-dir/nibfile,v -done -Checking in nibfile; -${TESTDIR}/cvsroot/first-dir/nibfile,v <-- nibfile -initial revision: 1\.1 -done" - dotest binfiles-sticky3 "${testcvs} -q update -kb nibfile" \ - '[UP] nibfile' - dotest binfiles-sticky4 "${testcvs} -q status nibfile" \ -"=================================================================== -File: nibfile Status: Up-to-date + dotest cvsadm-2d7 "${testcvs} co -d dir/dir2 2d1mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file1" + dotest cvsadm-2d7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d7d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d7f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - Working revision: 1\.1.* - Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" + dotest cvsadm-2d8 "${testcvs} co -d dir/dir2 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2 +U dir/dir2/file2" + dotest cvsadm-2d8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-2d8d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-2d8f "cat dir/dir2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Now test that -A can clear the sticky option. - dotest binfiles-sticky5 "${testcvs} -q update -A nibfile" \ -"[UP] nibfile" - dotest binfiles-sticky6 "${testcvs} -q status nibfile" \ -"=================================================================== -File: nibfile Status: Up-to-date + ################################################## + ## And now, a few of those tests revisited to + ## test the behavior of the -N flag. + ################################################## - Working revision: 1\.1.* - Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: (none)" - dotest binfiles-15 "${testcvs} -q admin -kb nibfile" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/nibfile,v -done" - dotest binfiles-16 "${testcvs} -q update nibfile" "[UP] nibfile" - dotest binfiles-17 "${testcvs} -q status nibfile" \ -"=================================================================== -File: nibfile Status: Up-to-date + dotest cvsadm-N3 "${testcvs} co -N 1mod" \ +"${PROG} [a-z]*: Updating 1mod +U 1mod/file1" + dotest cvsadm-N3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N3d "cat 1mod/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS 1mod - Working revision: 1\.1.* - Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" + dotest cvsadm-N4 "${testcvs} co -N 2mod" \ +"${PROG} [a-z]*: Updating 2mod +U 2mod/file2" + dotest cvsadm-N4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N4d "cat 2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS 2mod - dotest binfiles-o1 "${testcvs} admin -o1.3:: binfile" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v -deleting revision 1\.5 -deleting revision 1\.4 -done" - dotest binfiles-o2 "${testcvs} admin -o::1.3 binfile" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v -deleting revision 1\.2 -deleting revision 1\.1 -done" - dotest binfiles-o3 "${testcvs} -q log -h -N binfile" " -RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v -Working file: binfile -head: 1\.3 -branch: -locks: strict -access list: -keyword substitution: v -total revisions: 1 -=============================================================================" + dotest cvsadm-N5 "${testcvs} co -N 1d1mod" \ +"${PROG} [a-z]*: Updating dir1d1 +U dir1d1/file1" + dotest cvsadm-N5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N5d "cat dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir1d1 - cd ../.. - rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -r 2 - ;; + dotest cvsadm-N6 "${testcvs} co -N 1d2mod" \ +"${PROG} [a-z]*: Updating dir1d2 +U dir1d2/file2" + dotest cvsadm-N6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N6d "cat dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir1d2 - binfiles2) - # Test cvs's ability to handle binary files, particularly branching - # and joining. The key thing we are worrying about is that CVS - # doesn't print "cannot merge binary files" or some such, in - # situations where no merging is required. - # See also "join" which does this with non-binary files. - # - # Cases (we are merging from the branch to the trunk): - # binfile.dat) File added on branch, not on trunk. - # File should be marked for addition. - # brmod) File modified on branch, not on trunk. - # File should be copied over to trunk (no merging is needed). - # brmod-trmod) File modified on branch, also on trunk. - # This is a conflict. Present the user with both files and - # let them figure it out. - # brmod-wdmod) File modified on branch, not modified in the trunk - # repository, but modified in the (trunk) working directory. - # This is also a conflict. + dotest cvsadm-N7 "${testcvs} co -N 2d1mod" \ +"${PROG} [a-z]*: Updating dir2d1/sub2d1 +U dir2d1/sub2d1/file1" + dotest cvsadm-N7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N7d "cat dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N7f "cat dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir2d1 - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1; cd 1 - dotest binfiles2-1 "${testcvs} -q co first-dir" '' - cd first-dir + dotest cvsadm-N8 "${testcvs} co -N 2d2mod" \ +"${PROG} [a-z]*: Updating dir2d2/sub2d2 +U dir2d2/sub2d2/file2" + dotest cvsadm-N8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N8d "cat dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N8f "cat dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir2d2 - # The most important thing here is that binfile, binfile2, &c - # 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%c", 2, 10, 137, 0, 13, 10 }' \ - ../binfile - cat ../binfile ../binfile >../binfile2 - cat ../binfile2 ../binfile >../binfile3 - - # FIXCVS: unless a branch has at least one file on it, - # tag_check_valid won't know it exists. So if brmod didn't - # exist, we would have to invent it. - cp ../binfile brmod - cp ../binfile brmod-trmod - cp ../binfile brmod-wdmod - dotest binfiles2-1a \ -"${testcvs} add -kb brmod brmod-trmod brmod-wdmod" \ -"${PROG} [a-z]*: scheduling file .brmod. for addition -${PROG} [a-z]*: scheduling file .brmod-trmod. for addition -${PROG} [a-z]*: scheduling file .brmod-wdmod. for addition -${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest binfiles2-1b "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod,v -done -Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v -done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -initial revision: 1\.1 -done" - dotest binfiles2-2 "${testcvs} -q tag -b br" 'T brmod -T brmod-trmod -T brmod-wdmod' - dotest binfiles2-3 "${testcvs} -q update -r br" '' - cp ../binfile binfile.dat - dotest binfiles2-4 "${testcvs} add -kb binfile.dat" \ -"${PROG} [a-z]*: scheduling file .binfile\.dat. for addition on branch .br. -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - cp ../binfile2 brmod - cp ../binfile2 brmod-trmod - cp ../binfile2 brmod-wdmod - dotest binfiles2-5 "${testcvs} -q ci -m br-changes" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/binfile\.dat,v -done -Checking in binfile\.dat; -${TESTDIR}/cvsroot/first-dir/Attic/binfile\.dat,v <-- binfile\.dat -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done -Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done" - dotest binfiles2-6 "${testcvs} -q update -A" \ -"${PROG} [a-z]*: warning: binfile\.dat is not (any longer) pertinent -[UP] brmod -[UP] brmod-trmod -[UP] brmod-wdmod" - dotest_fail binfiles2-7 "test -f binfile.dat" '' - dotest binfiles2-7-brmod "cmp ../binfile brmod" - cp ../binfile3 brmod-trmod - dotest binfiles2-7a "${testcvs} -q ci -m tr-modify" \ -"Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.2; previous revision: 1\.1 -done" - cp ../binfile3 brmod-wdmod + ## the ones in one-deep directories - dotest binfiles2-8 "${testcvs} -q update -j br" \ -"U binfile\.dat -U brmod -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-trmod -${PROG} [a-z]*: file from working directory is now in .#brmod-trmod.1.2 -C brmod-trmod -M brmod-wdmod -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-wdmod -${PROG} [a-z]*: file from working directory is now in .#brmod-wdmod.1.1 -C brmod-wdmod" + dotest cvsadm-N1d3 "${testcvs} co -N -d dir 1mod" \ +"${PROG} [a-z]*: Updating dir/1mod +U dir/1mod/file1" + dotest cvsadm-N1d3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d3d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d3f "cat dir/1mod/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - dotest binfiles2-9 "cmp ../binfile binfile.dat" - dotest binfiles2-9-brmod "cmp ../binfile2 brmod" - dotest binfiles2-9-brmod-trmod "cmp ../binfile2 brmod-trmod" - dotest binfiles2-9-brmod-trmod "cmp ../binfile2 brmod-wdmod" - dotest binfiles2-9a-brmod-trmod "cmp ../binfile3 .#brmod-trmod.1.2" - dotest binfiles2-9a-brmod-wdmod "cmp ../binfile3 .#brmod-wdmod.1.1" + dotest cvsadm-N1d4 "${testcvs} co -N -d dir 2mod" \ +"${PROG} [a-z]*: Updating dir/2mod +U dir/2mod/file2" + dotest cvsadm-N1d4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d4d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d4f "cat dir/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Test that everything was properly scheduled. - dotest binfiles2-10 "${testcvs} -q ci -m checkin" \ -"Checking in binfile\.dat; -${TESTDIR}/cvsroot/first-dir/binfile\.dat,v <-- binfile\.dat -new revision: 1\.2; previous revision: 1\.1 -done -Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -new revision: 1\.2; previous revision: 1\.1 -done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.3; previous revision: 1\.2 -done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -new revision: 1\.2; previous revision: 1\.1 -done" + dotest cvsadm-N1d5 "${testcvs} co -N -d dir 1d1mod" \ +"${PROG} [a-z]*: Updating dir/dir1d1 +U dir/dir1d1/file1" + dotest cvsadm-N1d5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d5d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d5d "cat dir/dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - dotest_fail binfiles2-o1 "${testcvs} -q admin -o :1.2 brmod-trmod" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -deleting revision 1\.2 -${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v: can't remove branch point 1\.1 -${PROG} [a-z]*: cannot modify RCS file for .brmod-trmod." - dotest binfiles2-o2 "${testcvs} -q admin -o 1.1.2.1: brmod-trmod" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -deleting revision 1\.1\.2\.1 -done" - dotest binfiles2-o3 "${testcvs} -q admin -o :1.2 brmod-trmod" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -deleting revision 1\.2 -deleting revision 1\.1 -done" - dotest binfiles2-o4 "${testcvs} -q log -N brmod-trmod" " -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -Working file: brmod-trmod -head: 1\.3 -branch: -locks: strict -access list: -keyword substitution: b -total revisions: 1; selected revisions: 1 -description: ----------------------------- -revision 1\.3 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -checkin -=============================================================================" - cd .. - cd .. + dotest cvsadm-N1d6 "${testcvs} co -N -d dir 1d2mod" \ +"${PROG} [a-z]*: Updating dir/dir1d2 +U dir/dir1d2/file2" + dotest cvsadm-N1d6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d6d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d6f "cat dir/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -r 1 - ;; + dotest cvsadm-N1d7 "${testcvs} co -N -d dir 2d1mod" \ +"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 +U dir/dir2d1/sub2d1/file1" + dotest cvsadm-N1d7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d7d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d7f "cat dir/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N1d7h "cat dir/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - mcopy) - # See comment at "mwrap" test for list of other wrappers tests. - # Test cvs's ability to handle nonmergeable files specified with - # -m 'COPY' in wrappers. Similar to the binfiles2 test, - # which tests the same thing for binary files - # (which are non-mergeable in the same sense). - # - # Cases (we are merging from the branch to the trunk): - # brmod) File modified on branch, not on trunk. - # File should be copied over to trunk (no merging is needed). - # brmod-trmod) File modified on branch, also on trunk. - # This is a conflict. Present the user with both files and - # let them figure it out. - # brmod-wdmod) File modified on branch, not modified in the trunk - # repository, but modified in the (trunk) working directory. - # This is also a conflict. + dotest cvsadm-N1d8 "${testcvs} co -N -d dir 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 +U dir/dir2d2/sub2d2/file2" + dotest cvsadm-N1d8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d8d "cat dir/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N1d8d "cat dir/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N1d8d "cat dir/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # For the moment, remote CVS can't pass wrappers from CVSWRAPPERS - # (see wrap_send). So skip these tests for remote. - if test "x$remote" = xno; then + ## the ones in two-deep directories - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1; cd 1 - dotest mcopy-1 "${testcvs} -q co first-dir" '' - cd first-dir + dotest cvsadm-N2d3 "${testcvs} co -N -d dir/dir2 1mod" \ +"${PROG} [a-z]*: Updating dir/dir2/1mod +U dir/dir2/1mod/file1" + dotest cvsadm-N2d3b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d3d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d3f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d3h "cat dir/dir2/1mod/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - # FIXCVS: unless a branch has at least one file on it, - # tag_check_valid won't know it exists. So if brmod didn't - # exist, we would have to invent it. - echo 'brmod initial contents' >brmod - echo 'brmod-trmod initial contents' >brmod-trmod - echo 'brmod-wdmod initial contents' >brmod-wdmod - echo "* -m 'COPY'" >.cvswrappers - dotest mcopy-1a \ -"${testcvs} add .cvswrappers brmod brmod-trmod brmod-wdmod" \ -"${PROG} [a-z]*: scheduling file .\.cvswrappers. for addition -${PROG} [a-z]*: scheduling file .brmod. for addition -${PROG} [a-z]*: scheduling file .brmod-trmod. for addition -${PROG} [a-z]*: scheduling file .brmod-wdmod. for addition -${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest mcopy-1b "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/\.cvswrappers,v -done -Checking in \.cvswrappers; -${TESTDIR}/cvsroot/first-dir/\.cvswrappers,v <-- \.cvswrappers -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod,v -done -Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v -done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v -done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -initial revision: 1\.1 -done" + dotest cvsadm-N2d4 "${testcvs} co -N -d dir/dir2 2mod" \ +"${PROG} [a-z]*: Updating dir/dir2/2mod +U dir/dir2/2mod/file2" + dotest cvsadm-N2d4b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d4d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d4f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d4h "cat dir/dir2/2mod/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # NOTE: .cvswrappers files are broken (see comment in - # src/wrapper.c). So doing everything via the environment - # variable is a workaround. Better would be to test them - # both. - CVSWRAPPERS="* -m 'COPY'" - export CVSWRAPPERS - dotest mcopy-2 "${testcvs} -q tag -b br" 'T \.cvswrappers -T brmod -T brmod-trmod -T brmod-wdmod' - dotest mcopy-3 "${testcvs} -q update -r br" '' - echo 'modify brmod on br' >brmod - echo 'modify brmod-trmod on br' >brmod-trmod - echo 'modify brmod-wdmod on br' >brmod-wdmod - dotest mcopy-5 "${testcvs} -q ci -m br-changes" \ -"Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done" - dotest mcopy-6 "${testcvs} -q update -A" \ -"[UP] brmod -[UP] brmod-trmod -[UP] brmod-wdmod" - dotest mcopy-7 "cat brmod brmod-trmod brmod-wdmod" \ -"brmod initial contents -brmod-trmod initial contents -brmod-wdmod initial contents" + dotest cvsadm-N2d5 "${testcvs} co -N -d dir/dir2 1d1mod" \ +"${PROG} [a-z]*: Updating dir/dir2/dir1d1 +U dir/dir2/dir1d1/file1" + dotest cvsadm-N2d5b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d5d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d5f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d5h "cat dir/dir2/dir1d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - echo 'modify brmod-trmod again on trunk' >brmod-trmod - dotest mcopy-7a "${testcvs} -q ci -m tr-modify" \ -"Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.2; previous revision: 1\.1 -done" - echo 'modify brmod-wdmod in working dir' >brmod-wdmod + dotest cvsadm-N2d6 "${testcvs} co -N -d dir/dir2 1d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2/dir1d2 +U dir/dir2/dir1d2/file2" + dotest cvsadm-N2d6b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d6d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d6f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d6h "cat dir/dir2/dir1d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - dotest mcopy-8 "${testcvs} -q update -j br" \ -"U brmod -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-trmod -${PROG} [a-z]*: file from working directory is now in .#brmod-trmod.1.2 -C brmod-trmod -M brmod-wdmod -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-wdmod -${PROG} [a-z]*: file from working directory is now in .#brmod-wdmod.1.1 -C brmod-wdmod" + dotest cvsadm-N2d7 "${testcvs} co -N -d dir/dir2 2d1mod" \ +"${PROG} [a-z]*: Updating dir/dir2/dir2d1/sub2d1 +U dir/dir2/dir2d1/sub2d1/file1" + dotest cvsadm-N2d7b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d7d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d7f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d7f "cat dir/dir2/dir2d1/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d7h "cat dir/dir2/dir2d1/sub2d1/CVS/Repository" \ +"${AREP}mod1" + rm -rf CVS dir - dotest mcopy-9 "cat brmod brmod-trmod brmod-wdmod" \ -"modify brmod on br -modify brmod-trmod on br -modify brmod-wdmod on br" - dotest mcopy-9a "cat .#brmod-trmod.1.2 .#brmod-wdmod.1.1" \ -"modify brmod-trmod again on trunk -modify brmod-wdmod in working dir" + dotest cvsadm-N2d8 "${testcvs} co -N -d dir/dir2 2d2mod" \ +"${PROG} [a-z]*: Updating dir/dir2/dir2d2/sub2d2 +U dir/dir2/dir2d2/sub2d2/file2" + dotest cvsadm-N2d8b "cat CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d8d "cat dir/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d8f "cat dir/dir2/CVS/Repository" \ +"${AREP}\." + dotest cvsadm-N2d8h "cat dir/dir2/dir2d2/CVS/Repository" \ +"${AREP}CVSROOT/Emptydir" + dotest cvsadm-N2d8j "cat dir/dir2/dir2d2/sub2d2/CVS/Repository" \ +"${AREP}mod2/sub2" + rm -rf CVS dir - # Test that everything was properly scheduled. - dotest mcopy-10 "${testcvs} -q ci -m checkin" \ -"Checking in brmod; -${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod -new revision: 1\.2; previous revision: 1\.1 + ################################################## + ## That's enough of that, thank you very much. + ################################################## + + # remove our junk + cd .. + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/1mod + rm -rf ${CVSROOT_DIRNAME}/1mod-2 + rm -rf ${CVSROOT_DIRNAME}/2mod + rm -rf ${CVSROOT_DIRNAME}/2mod-2 + rm -rf ${CVSROOT_DIRNAME}/mod1 + rm -rf ${CVSROOT_DIRNAME}/mod1-2 + rm -rf ${CVSROOT_DIRNAME}/mod2 + rm -rf ${CVSROOT_DIRNAME}/mod2-2 + ;; + + abspath) + + # These tests test the thituations thin thwitch thoo theck + # things thout twith thabsolute thaths. Threally. + + # + # CHECKOUTS + # + + # Create a few modules to use + mkdir ${CVSROOT_DIRNAME}/mod1 ${CVSROOT_DIRNAME}/mod2 + dotest abspath-1a "${testcvs} co mod1 mod2" \ +"${PROG} [a-z]*: Updating mod1 +${PROG} [a-z]*: Updating mod2" + + # Populate the module + echo "file1" > mod1/file1 + echo "file2" > mod2/file2 + dotest abspath-1b "${testcvs} add mod1/file1 mod2/file2" \ +"${PROG} [a-z]*: scheduling file .mod1/file1. for addition +${PROG} [a-z]*: scheduling file .mod2/file2. for addition +${PROG} [a-z]*: use '${PROG} commit' to add these files permanently" + + dotest abspath-1c "${testcvs} ci -m yup mod1 mod2" \ +"${PROG} [a-z]*: Examining mod1 +${PROG} [a-z]*: Examining mod2 +RCS file: ${CVSROOT_DIRNAME}/mod1/file1,v done -Checking in brmod-trmod; -${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod -new revision: 1\.3; previous revision: 1\.2 +Checking in mod1/file1; +${CVSROOT_DIRNAME}/mod1/file1,v <-- file1 +initial revision: 1.1 done -Checking in brmod-wdmod; -${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod -new revision: 1\.2; previous revision: 1\.1 +RCS file: ${CVSROOT_DIRNAME}/mod2/file2,v +done +Checking in mod2/file2; +${CVSROOT_DIRNAME}/mod2/file2,v <-- file2 +initial revision: 1.1 done" - cd .. - cd .. + # Finished creating the module -- clean up. + rm -rf CVS mod1 mod2 + # Done. + + # Try checking out the module in a local directory + dotest abspath-2a "${testcvs} co -d ${TESTDIR}/1 mod1" \ +"${PROG} [a-z]*: Updating ${TESTDIR}/1 +U ${TESTDIR}/1/file1" + + # Are we relative or absolute in our Repository file? + echo "${CVSROOT_DIRNAME}/mod1" > ${TESTDIR}/dotest.abs + echo "mod1" > ${TESTDIR}/dotest.rel + if cmp ${TESTDIR}/dotest.abs ${TESTDIR}/1/CVS/Repository >/dev/null 2>&1; then + AREP="${CVSROOT_DIRNAME}/" + elif cmp ${TESTDIR}/dotest.rel ${TESTDIR}/1/CVS/Repository >/dev/null 2>&1; then + AREP="" + else + fail "Cannot figure out if RELATIVE_REPOS is defined." + fi + rm -f ${TESTDIR}/dotest.rel ${TESTDIR}/dotest.abs - rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -r 1 - unset CVSWRAPPERS + dotest abspath-2b "cat ${TESTDIR}/1/CVS/Repository" \ +"${AREP}mod1" - fi # end of tests to be skipped for remote + # Done. Clean up. + rm -rf ${TESTDIR}/1 - ;; - binwrap) - # Test the ability to specify binary-ness based on file name. - # See "mwrap" for a list of other wrappers tests. + # Now try in a subdirectory. We're not covering any more + # code here, but we might catch a future error if someone + # changes the checkout code. + dotest abspath-3a "${testcvs} co -d ${TESTDIR}/1/2 mod1" \ +"${PROG} [a-z]*: Updating ${TESTDIR}/1/2 +U ${TESTDIR}/1/2/file1" + dotest abspath-3b "cat ${TESTDIR}/1/2/CVS/Repository" \ +"${AREP}mod1" + # Done. Clean up. + rm -rf ${TESTDIR}/1 + + + # Now try someplace where we don't have permission. + mkdir ${TESTDIR}/barf + chmod -w ${TESTDIR}/barf + dotest_fail abspath-4 "${testcvs} co -d ${TESTDIR}/barf/sub mod1" \ +"${PROG} \[[a-z]* aborted\]: cannot make directory sub: No such file or directory" + chmod +w ${TESTDIR}/barf + rmdir ${TESTDIR}/barf + # Done. Nothing to clean up. + + + # Try checking out two modules into the same directory. + dotest abspath-5a "${testcvs} co -d ${TESTDIR}/1 mod1 mod2" \ +"${PROG} [a-z]*: Updating ${TESTDIR}/1/mod1 +U ${TESTDIR}/1/mod1/file1 +${PROG} [a-z]*: Updating ${TESTDIR}/1/mod2 +U ${TESTDIR}/1/mod2/file2" + dotest abspath-5b "cat ${TESTDIR}/1/CVS/Repository" \ +"${AREP}." + dotest abspath-5c "cat ${TESTDIR}/1/mod1/CVS/Repository" \ +"${AREP}mod1" + dotest abspath-5d "cat ${TESTDIR}/1/mod2/CVS/Repository" \ +"${AREP}mod2" + # Done. Clean up. + rm -rf ${TESTDIR}/1 - mkdir dir-to-import - cd dir-to-import - touch foo.c foo.exe - # While we're here, test for rejection of duplicate tag names. - dotest_fail binwrap-0 \ - "${testcvs} import -m msg -I ! first-dir dup dup" \ -"${PROG} \[[a-z]* aborted\]: tag .dup. was specified more than once" + # Try checking out the top-level module. + dotest abspath-6a "${testcvs} co -d ${TESTDIR}/1 ." \ +"${PROG} [a-z]*: Updating ${TESTDIR}/1 +${PROG} [a-z]*: Updating ${TESTDIR}/1/CVSROOT +${DOTSTAR} +${PROG} [a-z]*: Updating ${TESTDIR}/1/mod1 +U ${TESTDIR}/1/mod1/file1 +${PROG} [a-z]*: Updating ${TESTDIR}/1/mod2 +U ${TESTDIR}/1/mod2/file2" + dotest abspath-6b "cat ${TESTDIR}/1/CVS/Repository" \ +"${AREP}." + dotest abspath-6c "cat ${TESTDIR}/1/CVSROOT/CVS/Repository" \ +"${AREP}CVSROOT" + dotest abspath-6c "cat ${TESTDIR}/1/mod1/CVS/Repository" \ +"${AREP}mod1" + dotest abspath-6d "cat ${TESTDIR}/1/mod2/CVS/Repository" \ +"${AREP}mod2" + # Done. Clean up. + rm -rf ${TESTDIR}/1 - if ${testcvs} import -m message -I ! -W "*.exe -k 'b'" \ - first-dir tag1 tag2 >>${LOGFILE}; then - pass binwrap-1 - else - fail binwrap-1 - fi - cd .. - rm -r dir-to-import - dotest binwrap-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c -U first-dir/foo.exe' - dotest binwrap-3 "${testcvs} -q status first-dir" \ -"=================================================================== -File: foo\.c Status: Up-to-date + # + # FIXME: do other functions here (e.g. update /tmp/foo) + # - Working revision: 1\.1\.1\.1.* - Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.c,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: (none) + # Finished with all tests. Remove the module. + rm -rf ${CVSROOT_DIRNAME}/mod1 ${CVSROOT_DIRNAME}/mod1 -=================================================================== -File: foo\.exe Status: Up-to-date + # FIXME: the absolute pathname fixes create CVS directories + # wherever they can. That means for the standard TESTDIR, a + # /tmp/CVS directory will be created as well. It's not safe + # to remove it, however. - Working revision: 1\.1\.1\.1.* - Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.exe,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" - rm -r first-dir - rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - binwrap2) - # Test the ability to specify binary-ness based on file name. - # See "mwrap" for a list of other wrappers tests. + toplevel) + # test the feature that cvs creates a CVS subdir also for + # the toplevel directory - mkdir dir-to-import - cd dir-to-import - touch foo.c foo.exe + # Some test, somewhere, is creating Emptydir. That test + # should, perhaps, clean up for itself, but I don't know which + # one it is. + rm -rf ${CVSROOT_DIRNAME}/CVSROOT/Emptydir - # Specify that all files are binary except *.c. - # The order seems to matter, with the earlier rules taking - # precedence. I'm not sure whether that is good or not, - # but it is the current behavior. - if ${testcvs} import -m message -I ! \ - -W "*.c -k 'o'" -W "* -k 'b'" \ - first-dir tag1 tag2 >>${LOGFILE}; then - pass binwrap2-1 - else - fail binwrap2-1 - fi + mkdir 1; cd 1 + dotest toplevel-1 "${testcvs} -q co -l ." '' + mkdir top-dir second-dir + dotest toplevel-2 "${testcvs} add top-dir second-dir" \ +"Directory ${TESTDIR}/cvsroot/top-dir added to the repository +Directory ${TESTDIR}/cvsroot/second-dir added to the repository" + cd top-dir + + touch file1 + dotest toplevel-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest toplevel-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/top-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/top-dir/file1,v <-- file1 +initial revision: 1\.1 +done" cd .. - rm -r dir-to-import - dotest binwrap2-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c -U first-dir/foo.exe' - dotest binwrap2-3 "${testcvs} -q status first-dir" \ -"=================================================================== -File: foo\.c Status: Up-to-date - Working revision: 1\.1\.1\.1.* - Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.c,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -ko + cd second-dir + touch file2 + dotest toplevel-3s "${testcvs} add file2" \ +"${PROG} [a-z]*: scheduling file .file2. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest toplevel-4s "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/second-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/second-dir/file2,v <-- file2 +initial revision: 1\.1 +done" -=================================================================== -File: foo\.exe Status: Up-to-date + cd ../.. + rm -r 1; mkdir 1; cd 1 + dotest toplevel-5 "${testcvs} co top-dir" \ +"${PROG} [a-z]*: Updating top-dir +U top-dir/file1" - Working revision: 1\.1\.1\.1.* - Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.exe,v - Sticky Tag: (none) - Sticky Date: (none) - Sticky Options: -kb" - rm -r first-dir - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest toplevel-6 "${testcvs} update top-dir" \ +"${PROG} [a-z]*: Updating top-dir" + dotest toplevel-7 "${testcvs} update" \ +"${PROG} [a-z]*: Updating \. +${PROG} [a-z]*: Updating top-dir" - binwrap3) - # Test communication of file-specified -k wrappers between - # client and server, in `import': - # - # 1. Set up a directory tree, populate it with files. - # 2. Give each directory a different .cvswrappers file. - # 3. Give the server its own .cvswrappers file. - # 4. Import the whole tree, see if the right files got set - # to binary. - # - # The tree has a top ("0th") level, and two subdirs, sub1/ - # and sub2/; sub2/ contains directory subsub/. Every - # directory has a .cvswrappers file as well as regular - # files. - # - # In the file names, "foo-b.*" should end up binary, and - # "foo-t.*" should end up text. Don't worry about the two - # letter extensions; they're just there to help me keep - # things straight. - # - # Here's the directory tree: - # - # ./ - # .cvswrappers - # foo-b.c0 - # foo-b.sb - # foo-t.c1 - # foo-t.st - # - # sub1/ sub2/ - # .cvswrappers .cvswrappers - # foo-b.c1 foo-b.sb - # foo-b.sb foo-b.st - # foo-t.c0 foo-t.c0 - # foo-t.st foo-t.c1 - # foo-t.c2 - # foo-t.c3 - # - # subsub/ - # .cvswrappers - # foo-b.c3 - # foo-b.sb - # foo-t.c0 - # foo-t.c1 - # foo-t.c2 - # foo-t.st + dotest toplevel-8 "${testcvs} update -d top-dir" \ +"${PROG} [a-z]*: Updating top-dir" + # There is some sentiment that + # "${PROG} [a-z]*: Updating \. + # ${PROG} [a-z]*: Updating top-dir" + # is correct but it isn't clear why that would be correct instead + # of the remote CVS behavior (which also updates CVSROOT). + # + # The DOTSTAR matches of a bunch of lines like + # "U CVSROOT/checkoutlist". Trying to match them more precisely + # seemed to cause trouble. For example CVSROOT/cvsignore will + # be present or absent depending on whether we ran the "ignore" + # test or not. + dotest toplevel-9 "${testcvs} update -d" \ +"${PROG} [a-z]*: Updating \. +${PROG} [a-z]*: Updating CVSROOT +${DOTSTAR} +${PROG} [a-z]*: Updating top-dir" - binwrap3_line1="This is a test file " - binwrap3_line2="containing little of use " - binwrap3_line3="except this non-haiku" + cd .. + rm -r 1; mkdir 1; cd 1 + dotest toplevel-10 "${testcvs} co top-dir" \ +"${PROG} [a-z]*: Updating top-dir +U top-dir/file1" + # This tests more or less the same thing, in a particularly + # "real life" example. + dotest toplevel-11 "${testcvs} -q update -d second-dir" \ +"U second-dir/file2" - binwrap3_text="${binwrap3_line1}${binwrap3_line2}${binwrap3_line3}" + # Now remove the CVS directory (people may do this manually, + # especially if they formed their habits with CVS + # 1.9 and older, which didn't create it. Or perhaps the working + # directory itself was created with 1.9 or older). + rm -r CVS + # Now set the permissions so we can't recreate it. + chmod -w ../1 + # Now see whether CVS has trouble because it can't create CVS. + dotest toplevel-12 "${testcvs} co top-dir" \ +"${PROG} [a-z]*: warning: cannot make directory CVS in \.: Permission denied +${PROG} [a-z]*: Updating top-dir" + chmod +w ../1 - cd ${TESTDIR} + cd .. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/top-dir ${CVSROOT_DIRNAME}/second-dir + ;; - # On Windows, we can't check out CVSROOT, because the case - # insensitivity means that this conflicts with cvsroot. - mkdir wnt - cd wnt + mflag) + for message in '' ' ' ' + ' ' test' ; do + # Set up + mkdir a-dir; cd a-dir + # Test handling of -m during import + echo testa >>test + if ${testcvs} import -m "$message" a-dir A A1 >>${LOGFILE} 2>&1;then + pass 156 + else + fail 156 + fi + # Must import twice since the first time uses inline code that + # avoids RCS call. + echo testb >>test + if ${testcvs} import -m "$message" a-dir A A2 >>${LOGFILE} 2>&1;then + pass 157 + else + fail 157 + fi + # Test handling of -m during ci + cd ..; rm -r a-dir + if ${testcvs} co a-dir >>${LOGFILE} 2>&1; then + pass 158 + else + fail 158 + fi + cd a-dir + echo testc >>test + if ${testcvs} ci -m "$message" >>${LOGFILE} 2>&1; then + pass 159 + else + fail 159 + fi + # Test handling of -m during rm/ci + rm test; + if ${testcvs} rm test >>${LOGFILE} 2>&1; then + pass 160 + else + fail 160 + fi + if ${testcvs} ci -m "$message" >>${LOGFILE} 2>&1; then + pass 161 + else + fail 161 + fi + # Clean up + cd .. + rm -r a-dir + rm -rf ${CVSROOT_DIRNAME}/a-dir + done + ;; - mkdir binwrap3 # the 0th dir - mkdir binwrap3/sub1 - mkdir binwrap3/sub2 - mkdir binwrap3/sub2/subsub - - echo "*.c0 -k 'b'" > binwrap3/.cvswrappers - echo "whatever -k 'b'" >> binwrap3/.cvswrappers - echo ${binwrap3_text} > binwrap3/foo-b.c0 - echo ${binwrap3_text} > binwrap3/foo-b.sb - echo ${binwrap3_text} > binwrap3/foo-t.c1 - echo ${binwrap3_text} > binwrap3/foo-t.st + editor) + # More tests of log messages, in this case the ability to + # run an external editor. + # TODO: + # * also test $EDITOR, $CVSEDITOR, &c. + # * test what happens if up-to-date check fails. - echo "*.c1 -k 'b'" > binwrap3/sub1/.cvswrappers - echo "whatever -k 'b'" >> binwrap3/sub1/.cvswrappers - echo ${binwrap3_text} > binwrap3/sub1/foo-b.c1 - echo ${binwrap3_text} > binwrap3/sub1/foo-b.sb - echo ${binwrap3_text} > binwrap3/sub1/foo-t.c0 - echo ${binwrap3_text} > binwrap3/sub1/foo-t.st - - echo "*.st -k 'b'" > binwrap3/sub2/.cvswrappers - echo ${binwrap3_text} > binwrap3/sub2/foo-b.sb - echo ${binwrap3_text} > binwrap3/sub2/foo-b.st - echo ${binwrap3_text} > binwrap3/sub2/foo-t.c0 - echo ${binwrap3_text} > binwrap3/sub2/foo-t.c1 - echo ${binwrap3_text} > binwrap3/sub2/foo-t.c2 - echo ${binwrap3_text} > binwrap3/sub2/foo-t.c3 - - echo "*.c3 -k 'b'" > binwrap3/sub2/subsub/.cvswrappers - echo "foo -k 'b'" >> binwrap3/sub2/subsub/.cvswrappers - echo "c0* -k 'b'" >> binwrap3/sub2/subsub/.cvswrappers - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-b.c3 - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-b.sb - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c0 - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c1 - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c2 - echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.st - - # Now set up CVSROOT/cvswrappers, the easy way: - dotest binwrap3-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" - cd CVSROOT - # This destroys anything currently in cvswrappers, but - # presumably other tests will take care of it themselves if - # they use cvswrappers: - echo "foo*.sb -k 'b'" > cvswrappers - dotest binwrap3-2 "${testcvs} -q ci -m cvswrappers-mod" \ -"Checking in cvswrappers; -${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers -new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* -done -${PROG} [a-z]*: Rebuilding administrative file database" - cd .. - - # Avoid environmental interference - CVSWRAPPERS_SAVED=${CVSWRAPPERS} - unset CVSWRAPPERS - - # Do the import - cd binwrap3 - # Not importing .cvswrappers tests whether the client is really - # letting the server know "honestly" whether the file is binary, - # rather than just letting the server see the .cvswrappers file. - dotest binwrap3-2a \ -"${testcvs} import -m . -I .cvswrappers binwrap3 tag1 tag2" \ -"[NI] ${DOTSTAR}" + # Our "editor" puts "x" at the start of each line, so we + # can see the "CVS:" lines. + cat >${TESTDIR}/editme <${TESTDIR}/edit.new +mv ${TESTDIR}/edit.new \$1 +exit 0 +EOF + chmod +x ${TESTDIR}/editme - # OK, now test "cvs add". - cd .. - rm -r binwrap3 - dotest binwrap3-2b "${testcvs} co binwrap3" "${DOTSTAR}" - cd binwrap3 - cd sub2 - echo "*.newbin -k 'b'" > .cvswrappers - echo .cvswrappers >.cvsignore - echo .cvsignore >>.cvsignore - touch file1.newbin file1.txt - dotest binwrap3-2c "${testcvs} add file1.newbin file1.txt" \ -"${PROG} [a-z]*: scheduling file .file1\.newbin. for addition -${PROG} [a-z]*: scheduling file .file1\.txt. for addition + mkdir 1; cd 1 + dotest editor-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest editor-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch file1 file2 + dotest editor-3 "${testcvs} add file1 file2" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: scheduling file .file2. for addition ${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest binwrap3-2d "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/binwrap3/sub2/file1\.newbin,v + dotest editor-4 "${testcvs} -e ${TESTDIR}/editme -q ci" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done -Checking in file1\.newbin; -${TESTDIR}/cvsroot/binwrap3/sub2/file1\.newbin,v <-- file1\.newbin +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done -RCS file: ${TESTDIR}/cvsroot/binwrap3/sub2/file1\.txt,v +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v done -Checking in file1\.txt; -${TESTDIR}/cvsroot/binwrap3/sub2/file1\.txt,v <-- file1\.txt +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 initial revision: 1\.1 done" - cd .. - - # Now check out the module and see which files are binary. - cd .. - rm -r binwrap3 - dotest binwrap3-3 "${testcvs} co binwrap3" "${DOTSTAR}" - cd binwrap3 - - # Running "cvs status" and matching output is too - # error-prone, too likely to falsely fail. Instead, we'll - # just grep the Entries lines: - - dotest binwrap3-top1 "grep foo-b.c0 ./CVS/Entries" \ - "/foo-b.c0/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - - dotest binwrap3-top2 "grep foo-b.sb ./CVS/Entries" \ - "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - - dotest binwrap3-top3 "grep foo-t.c1 ./CVS/Entries" \ - "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" + dotest editor-5 "${testcvs} -q tag -b br" "T file1 +T file2" + dotest editor-6 "${testcvs} -q update -r br" '' + echo modify >>file1 + dotest editor-7 "${testcvs} -e ${TESTDIR}/editme -q ci" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + # OK, now we want to make sure "ci -r" puts in the branch + # where appropriate. Note that we can check in on the branch + # without being on the branch, because there is not a revision + # already on the branch. If there were a revision on the branch, + # CVS would correctly give an up-to-date check failed. + dotest editor-8 "${testcvs} -q update -A" "U file1" + echo add a line >>file2 + dotest editor-9 "${testcvs} -q -e ${TESTDIR}/editme ci -rbr file2" \ +"Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" - dotest binwrap3-top4 "grep foo-t.st ./CVS/Entries" \ - "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" + dotest editor-log-file1 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Committing in . +xCVS: +xCVS: Added Files: +xCVS: file1 file2 +xCVS: ---------------------------------------------------------------------- +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Committing in . +xCVS: +xCVS: Modified Files: +xCVS: Tag: br +xCVS: file1 +xCVS: ---------------------------------------------------------------------- +=============================================================================" - dotest binwrap3-sub1-1 "grep foo-b.c1 sub1/CVS/Entries" \ - "/foo-b.c1/1.1.1.1/[A-Za-z0-9 :]*/-kb/" + # The only difference between the two expect strings is the + # presence or absence of "Committing in ." for 1.1.2.1. + dotest editor-log-file2 "${testcvs} log -N file2" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Working file: file2 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Committing in . +xCVS: +xCVS: Added Files: +xCVS: file1 file2 +xCVS: ---------------------------------------------------------------------- +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Modified Files: +xCVS: Tag: br +xCVS: file2 +xCVS: ---------------------------------------------------------------------- +=============================================================================" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Working file: file2 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Committing in . +xCVS: +xCVS: Added Files: +xCVS: file1 file2 +xCVS: ---------------------------------------------------------------------- +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +x +xCVS: ---------------------------------------------------------------------- +xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically +xCVS: +xCVS: Committing in . +xCVS: +xCVS: Modified Files: +xCVS: Tag: br +xCVS: file2 +xCVS: ---------------------------------------------------------------------- +=============================================================================" + cd ../.. + rm -r 1 + rm ${TESTDIR}/editme + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest binwrap3-sub1-2 "grep foo-b.sb sub1/CVS/Entries" \ - "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - - dotest binwrap3-sub1-3 "grep foo-t.c0 sub1/CVS/Entries" \ - "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-sub1-4 "grep foo-t.st sub1/CVS/Entries" \ - "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-sub2-1 "grep foo-b.sb sub2/CVS/Entries" \ - "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - - dotest binwrap3-sub2-2 "grep foo-b.st sub2/CVS/Entries" \ - "/foo-b.st/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - - dotest binwrap3-sub2-3 "grep foo-t.c0 sub2/CVS/Entries" \ - "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-sub2-4 "grep foo-t.c1 sub2/CVS/Entries" \ - "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-sub2-5 "grep foo-t.c2 sub2/CVS/Entries" \ - "/foo-t.c2/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-sub2-6 "grep foo-t.c3 sub2/CVS/Entries" \ - "/foo-t.c3/1.1.1.1/[A-Za-z0-9 :]*//" - - dotest binwrap3-subsub1 "grep foo-b.c3 sub2/subsub/CVS/Entries" \ - "/foo-b.c3/1.1.1.1/[A-Za-z0-9 :]*/-kb/" + errmsg1) + mkdir ${CVSROOT_DIRNAME}/1dir + mkdir 1 + cd 1 + if ${testcvs} -q co 1dir; then + pass 162 + else + fail 162 + fi + cd 1dir + touch foo + if ${testcvs} add foo 2>>${LOGFILE}; then + pass 163 + else + fail 163 + fi + if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then + pass 164 + else + fail 164 + fi + cd ../.. + mkdir 2 + cd 2 + if ${testcvs} -q co 1dir >>${LOGFILE}; then + pass 165 + else + fail 165 + fi + chmod a-w 1dir + cd ../1/1dir + rm foo; + if ${testcvs} rm foo >>${LOGFILE} 2>&1; then + pass 166 + else + fail 166 + fi + if ${testcvs} ci -m removed >>${LOGFILE} 2>&1; then + pass 167 + else + fail 167 + fi - dotest binwrap3-subsub2 "grep foo-b.sb sub2/subsub/CVS/Entries" \ - "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" + cd ../../2/1dir + # FIXME: should be using dotest. + ${testcvs} -q update 2>../tst167.err + cat ../tst167.err >>${LOGFILE} + cat <../tst167.ans +${PROG} server: warning: foo is not (any longer) pertinent +${PROG} update: unable to remove ./foo: Permission denied +EOF + if cmp ../tst167.ans ../tst167.err >/dev/null || + ( echo "${PROG} [update aborted]: cannot rename file foo to CVS/,,foo: Permission denied" | cmp - ../tst167.err >/dev/null ) + then + pass 168 + else + fail 168 + fi - dotest binwrap3-subsub3 "grep foo-t.c0 sub2/subsub/CVS/Entries" \ - "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" + cd .. + chmod u+w 1dir + cd .. + rm -r 1 2 + rm -rf ${CVSROOT_DIRNAME}/1dir + ;; - dotest binwrap3-subsub4 "grep foo-t.c1 sub2/subsub/CVS/Entries" \ - "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" + errmsg2) + # More tests of various miscellaneous error handling, + # and cvs add behavior in general. + # See also test basicb-4a, concerning "cvs ci CVS". + # Too many tests to mention test the simple cases of + # adding files and directories. + # Test basicb-2a10 tests cvs -n add. - dotest binwrap3-subsub5 "grep foo-t.c2 sub2/subsub/CVS/Entries" \ - "/foo-t.c2/1.1.1.1/[A-Za-z0-9 :]*//" + # First the usual setup; create a directory first-dir. + mkdir 1; cd 1 + dotest errmsg2-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest errmsg2-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + dotest_fail errmsg2-3 "${testcvs} add CVS" \ +"${PROG} [a-z]*: cannot add special file .CVS.; skipping" + touch file1 + # For the most part add returns a failure exitstatus if + # there are any errors, even if the remaining files are + # processed without incident. The "cannot add + # special file" message fits this pattern, at + # least currently. + dotest_fail errmsg2-4 "${testcvs} add CVS file1" \ +"${PROG} [a-z]*: cannot add special file .CVS.; skipping +${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + # I'm not sure these tests completely convey the various strange + # behaviors that CVS had before it specially checked for "." and + # "..". Suffice it to say that these are unlikely to work right + # without a special case. + dotest_fail errmsg2-5 "${testcvs} add ." \ +"${PROG} [a-z]*: cannot add special file .\..; skipping" + dotest_fail errmsg2-6 "${testcvs} add .." \ +"${PROG} [a-z]*: cannot add special file .\.\..; skipping" + # Make sure that none of the error messages left droppings + # which interfere with normal operation. + dotest errmsg2-7 "${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" + mkdir sdir + cd .. + dotest errmsg2-8 "${testcvs} add first-dir/sdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository" - dotest binwrap3-subsub6 "grep foo-t.st sub2/subsub/CVS/Entries" \ - "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" + cd first-dir - dotest binwrap3-sub2-add1 "grep file1.newbin sub2/CVS/Entries" \ - "/file1.newbin/1.1/[A-Za-z0-9 :]*/-kb/" - dotest binwrap3-sub2-add2 "grep file1.txt sub2/CVS/Entries" \ - "/file1.txt/1.1/[A-Za-z0-9 :]*//" + touch file10 + mkdir sdir10 + dotest errmsg2-10 "${testcvs} add file10 sdir10" \ +"${PROG} [a-z]*: scheduling file .file10. for addition +Directory ${TESTDIR}/cvsroot/first-dir/sdir10 added to the repository +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest errmsg2-11 "${testcvs} -q ci -m add-file10" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file10,v +done +Checking in file10; +${TESTDIR}/cvsroot/first-dir/file10,v <-- file10 +initial revision: 1\.1 +done" + # Try to see that there are no droppings left by + # any of the previous tests. + dotest errmsg2-12 "${testcvs} -q update" "" - # Restore and clean up - cd .. - rm -r binwrap3 CVSROOT + # Now test adding files with '/' in the name, both one level + # down and more than one level down. cd .. - rm -r wnt - rm -rf ${CVSROOT_DIRNAME}/binwrap3 - CVSWRAPPERS=${CVSWRAPPERS_SAVED} - ;; + mkdir first-dir/sdir10/ssdir + dotest errmsg2-13 "${testcvs} add first-dir/sdir10/ssdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/sdir10/ssdir added to the repository" - mwrap) - # Tests of various wrappers features: - # -m 'COPY' and cvs update: mwrap - # -m 'COPY' and joining: mcopy - # -k: binwrap, binwrap2 - # -t/-f: hasn't been written yet. - # - # Tests of different ways of specifying wrappers: - # CVSROOT/cvswrappers: mwrap - # -W: binwrap, binwrap2 - # .cvswrappers in working directory, local: mcopy - # CVSROOT/cvswrappers, .cvswrappers remote: binwrap3 - # CVSWRAPPERS environment variable: mcopy + touch first-dir/sdir10/ssdir/ssfile + dotest errmsg2-14 \ + "${testcvs} add first-dir/sdir10/ssdir/ssfile" \ +"${PROG} [a-z]*: scheduling file .first-dir/sdir10/ssdir/ssfile. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + touch first-dir/file15 + dotest errmsg2-15 "${testcvs} add first-dir/file15" \ +"${PROG} [a-z]*: scheduling file .first-dir/file15. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - # This test is similar to binfiles-con1; -m 'COPY' specifies - # non-mergeableness the same way that -kb does. + # Now the case where we try to give it a directory which is not + # under CVS control. + mkdir bogus-dir + touch bogus-dir/file16 + # The first message, from local CVS, is nice. The second one + # is not nice; would be good to fix remote CVS to give a clearer + # message (e.g. the one from local CVS). But at least it is an + # error message. + dotest_fail errmsg2-16 "${testcvs} add bogus-dir/file16" \ +"${PROG} [a-z]*: in directory bogus-dir: +${PROG} \[[a-z]* aborted\]: there is no version here; do .${PROG} checkout. first" \ +"${PROG} [a-z]*: cannot open CVS/Entries for reading: No such file or directory +${PROG} \[add aborted\]: no repository" + rm -r bogus-dir - # On Windows, we can't check out CVSROOT, because the case - # insensitivity means that this conflicts with cvsroot. - mkdir wnt - cd wnt + # One error condition we don't test for is trying to add a file + # or directory which already is there. - dotest mwrap-c1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" - cd CVSROOT - echo "* -m 'COPY'" >>cvswrappers - dotest mwrap-c2 "${testcvs} -q ci -m wrapper-mod" \ -"Checking in cvswrappers; -${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers -new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* + dotest errmsg2-17 "${testcvs} -q ci -m checkin" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file15,v done -${PROG} [a-z]*: Rebuilding administrative file database" - cd .. - mkdir m1; cd m1 - dotest mwrap-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest mwrap-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - touch aa - dotest mwrap-3 "${testcvs} add aa" \ -"${PROG} [a-z]*: scheduling file .aa. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest mwrap-4 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v +Checking in first-dir/file15; +${TESTDIR}/cvsroot/first-dir/file15,v <-- file15 +initial revision: 1\.1 done -Checking in aa; -${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +RCS file: ${TESTDIR}/cvsroot/first-dir/sdir10/ssdir/ssfile,v +done +Checking in first-dir/sdir10/ssdir/ssfile; +${TESTDIR}/cvsroot/first-dir/sdir10/ssdir/ssfile,v <-- ssfile initial revision: 1\.1 done" - cd ../.. - mkdir m2; cd m2 - dotest mwrap-5 "${testcvs} -q co first-dir" "U first-dir/aa" - cd first-dir - echo "changed in m2" >aa - dotest mwrap-6 "${testcvs} -q ci -m m2-mod" \ -"Checking in aa; -${TESTDIR}/cvsroot/first-dir/aa,v <-- aa -new revision: 1\.2; previous revision: 1\.1 -done" - cd ../.. - cd m1/first-dir - echo "changed in m1" >aa - dotest_fail mwrap-7 "${testcvs} -nq update" "C aa" - dotest mwrap-8 "${testcvs} -q update" \ -"U aa -${PROG} [a-z]*: nonmergeable file needs merge -${PROG} [a-z]*: revision 1\.2 from repository is now in aa -${PROG} [a-z]*: file from working directory is now in \.#aa\.1\.1 -C aa" - dotest mwrap-9 "cat aa" "changed in m2" - dotest mwrap-10 "cat .#aa.1.1" "changed in m1" - cd ../.. - cd CVSROOT - echo '# comment out' >cvswrappers - dotest mwrap-ce "${testcvs} -q ci -m wrapper-mod" \ -"Checking in cvswrappers; -${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers -new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* -done -${PROG} [a-z]*: Rebuilding administrative file database" - cd .. - rm -r CVSROOT - rm -r m1 m2 - cd .. - rm -r wnt - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; - - info) - # Administrative file tests. - # Here is a list of where each administrative file is tested: - # loginfo: info - # modules: modules, modules2, modules3 - # cvsignore: ignore - # verifymsg: info - # cvswrappers: mwrap - # config: config - - # On Windows, we can't check out CVSROOT, because the case - # insensitivity means that this conflicts with cvsroot. - mkdir wnt - cd wnt - - dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" - cd CVSROOT - echo "ALL sh -c \"echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat >/dev/null\"" > loginfo - # The following cases test the format string substitution - echo "ALL echo %{sVv} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo - echo "ALL echo %{v} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo - echo "ALL echo %s >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo - echo "ALL echo %{V}AX >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo - echo "ALL echo %sux >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo - - # Might be nice to move this to crerepos tests; it should - # work to create a loginfo file if you didn't create one - # with "cvs init". - : dotest info-2 "${testcvs} add loginfo" \ -"${PROG}"' [a-z]*: scheduling file `loginfo'"'"' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - dotest info-3 "${testcvs} -q ci -m new-loginfo" \ -"Checking in loginfo; -${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo -new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* -done -${PROG} [a-z]*: Rebuilding administrative file database" cd .. + rm -r 1 + rm -rf ${TESTDIR}/cvsroot/first-dir + ;; + devcom) mkdir ${CVSROOT_DIRNAME}/first-dir - dotest info-5 "${testcvs} -q co first-dir" '' - cd first-dir - touch file1 - dotest info-6 "${testcvs} add file1" \ -"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc - dotest info-6a "${testcvs} -q -s OTHER=value ci -m add-it" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial 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 -done" - cd .. - dotest info-9 "cat $TESTDIR/testlog" "xenv-valueyz=${username}=${TESTDIR}/cvsroot=" - dotest info-10 "cat $TESTDIR/testlog2" 'first-dir file1,NONE,1.1 -first-dir 1.1 -first-dir file1 -first-dir NONEAX -first-dir file1ux -first-dir file1,1.1,1.2 -first-dir 1.2 -first-dir file1 -first-dir 1.1AX -first-dir file1ux' - - cd CVSROOT - echo '# do nothing' >loginfo - dotest info-11 "${testcvs} -q -s ZEE=garbage ci -m nuke-loginfo" \ -"Checking in loginfo; -${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo -new revision: 1\.[0-9]; previous revision: 1\.[0-9] -done -${PROG} [a-z]*: Rebuilding administrative file database" - - # Now test verifymsg - cat >${TESTDIR}/vscript < /dev/null; then - exit 0 -else - echo "No BugId found." - exit 1 -fi -EOF - chmod +x ${TESTDIR}/vscript - echo "^first-dir ${TESTDIR}/vscript" >>verifymsg - dotest info-v1 "${testcvs} -q ci -m add-verification" \ -"Checking in verifymsg; -${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg -new revision: 1\.2; previous revision: 1\.1 -done -${PROG} [a-z]*: Rebuilding administrative file database" + mkdir 1 + cd 1 + if ${testcvs} -q co first-dir >>${LOGFILE} ; then + pass 169 + else + fail 169 + fi - cd ../first-dir - echo line2 >>file1 - dotest_fail info-v2 "${testcvs} -q ci -m bogus" \ -"No BugId found\. -${PROG} \[[a-z]* aborted\]: Message verification failed" + cd first-dir + echo abb >abb + if ${testcvs} add abb 2>>${LOGFILE}; then + pass 170 + else + fail 170 + fi + if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then + pass 171 + else + fail 171 + fi + dotest_fail 171a0 "${testcvs} watch" "Usage${DOTSTAR}" + if ${testcvs} watch on; then + pass 172 + else + fail 172 + fi + echo abc >abc + if ${testcvs} add abc 2>>${LOGFILE}; then + pass 173 + else + fail 173 + fi + if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then + pass 174 + else + fail 174 + fi - cat >${TESTDIR}/comment.tmp <verifymsg - dotest info-cleanup-verifymsg "${testcvs} -q ci -m nuke-verifymsg" \ -"Checking in verifymsg; -${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg -new revision: 1\.[0-9]; previous revision: 1\.[0-9] -done -${PROG} [a-z]*: Rebuilding administrative file database" - cd .. + if ${testcvs} -q co first-dir >>${LOGFILE}; then + pass 175 + else + fail 175 + fi + cd first-dir + if test -w abb; then + fail 176 + else + pass 176 + fi + if test -w abc; then + fail 177 + else + pass 177 + fi - if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then - pass info-cleanup + if ${testcvs} editors >../ans178.tmp; then + pass 178 else - fail info-cleanup + fail 178 fi - if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE} ; then - pass info-cleanup-2 + cat ../ans178.tmp >>${LOGFILE} + if test -s ../ans178.tmp; then + fail 178a else - fail info-cleanup-2 + pass 178a fi - cd .. - rm -r wnt - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; - config) - # Tests of the CVSROOT/config file. See the comment at the - # "info" tests for a full list of administrative file tests. + if ${testcvs} edit abb; then + pass 179 + else + fail 179 + fi - # On Windows, we can't check out CVSROOT, because the case - # insensitivity means that this conflicts with cvsroot. - mkdir wnt - cd wnt + if ${testcvs} editors >../ans180.tmp; then + pass 180 + else + fail 180 + fi + cat ../ans180.tmp >>${LOGFILE} + if test -s ../ans180.tmp; then + pass 181 + else + fail 181 + fi - dotest config-1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}" - cd CVSROOT - echo 'bogus line' >config - dotest config-3 "${testcvs} -q ci -m change-to-bogus-line" \ -"Checking in config; -${TESTDIR}/cvsroot/CVSROOT/config,v <-- config -new revision: 1\.2; previous revision: 1\.1 -done -${PROG} [a-z]*: Rebuilding administrative file database" - echo 'BogusOption=yes' >config - dotest config-4 "${testcvs} -q ci -m change-to-bogus-opt" \ -"${PROG} [a-z]*: syntax error in ${TESTDIR}/cvsroot/CVSROOT/config: line 'bogus line' is missing '=' -Checking in config; -${TESTDIR}/cvsroot/CVSROOT/config,v <-- config -new revision: 1\.3; previous revision: 1\.2 -done -${PROG} [a-z]*: Rebuilding administrative file database" - echo '# No config is a good config' > config - dotest config-5 "${testcvs} -q ci -m change-to-comment" \ -"${PROG} [a-z]*: ${TESTDIR}/cvsroot/CVSROOT/config: unrecognized keyword 'BogusOption' -Checking in config; -${TESTDIR}/cvsroot/CVSROOT/config,v <-- config -new revision: 1\.4; previous revision: 1\.3 -done -${PROG} [a-z]*: Rebuilding administrative file database" - dotest config-6 "${testcvs} -q update" '' + echo aaaa >>abb + if ${testcvs} ci -m modify abb >>${LOGFILE} 2>&1; then + pass 182 + else + fail 182 + fi + # Unedit of a file not being edited should be a noop. + dotest 182.5 "${testcvs} unedit abb" '' - cd .. - rm -r CVSROOT - cd .. - rm -r wnt - ;; + if ${testcvs} editors >../ans183.tmp; then + pass 183 + else + fail 183 + fi + cat ../ans183.tmp >>${LOGFILE} + if test -s ../ans183.tmp; then + fail 184 + else + pass 184 + fi - serverpatch) - # Test remote CVS handling of unpatchable files. This isn't - # much of a test for local CVS. - # We test this with some keyword expansion games, but the situation - # also arises if the user modifies the file while CVS is running. - mkdir ${CVSROOT_DIRNAME}/first-dir - mkdir 1 - cd 1 - dotest serverpatch-1 "${testcvs} -q co first-dir" '' + if test -w abb; then + fail 185 + else + pass 185 + fi - cd first-dir + if ${testcvs} edit abc; then + pass 186a1 + else + fail 186a1 + fi + # Unedit of an unmodified file. + if ${testcvs} unedit abc; then + pass 186a2 + else + fail 186a2 + fi + if ${testcvs} edit abc; then + pass 186a3 + else + fail 186a3 + fi + echo changedabc >abc + # Try to unedit a modified file; cvs should ask for confirmation + if (echo no | ${testcvs} unedit abc) >>${LOGFILE}; then + pass 186a4 + else + fail 186a4 + fi + if echo changedabc | cmp - abc; then + pass 186a5 + else + fail 186a5 + fi + # OK, now confirm the unedit + if (echo yes | ${testcvs} unedit abc) >>${LOGFILE}; then + pass 186a6 + else + fail 186a6 + fi + if echo abc | cmp - abc; then + pass 186a7 + else + fail 186a7 + fi - # Add a file with an RCS keyword. - echo '$''Name$' > file1 - echo '1' >> file1 - dotest serverpatch-2 "${testcvs} add file1" \ -"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest devcom-a0 "${testcvs} watchers" '' - dotest serverpatch-3 "${testcvs} -q commit -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 -done" + # FIXME: This probably should be an error message instead + # of silently succeeding and printing nothing. + dotest devcom-a-nonexist "${testcvs} watchers nonexist" '' - # Tag the file. - dotest serverpatch-4 "${testcvs} -q tag tag file1" 'T file1' + dotest devcom-a1 "${testcvs} watch add" '' + dotest devcom-a2 "${testcvs} watchers" \ +"abb ${username} edit unedit commit +abc ${username} edit unedit commit" + dotest devcom-a3 "${testcvs} watch remove -a unedit abb" '' + dotest devcom-a4 "${testcvs} watchers abb" \ +"abb ${username} edit commit" - # Check out a tagged copy of the file. + # Check tagging and checking out while we have a CVS + # directory in the repository. + dotest devcom-t0 "${testcvs} -q tag tag" \ +'T abb +T abc' cd ../.. - mkdir 2 - cd 2 - dotest serverpatch-5 "${testcvs} -q co -r tag first-dir" \ -'U first-dir/file1' + mkdir 3 + cd 3 - # Remove the tag. This will leave the tag string in the - # expansion of the Name keyword. - dotest serverpatch-6 "${testcvs} -q update -A" '' + # Test commented out because the bug it tests for is not fixed + # The error is: + # cvs watchers: cannot open CVS/Entries for reading: No such file or directory + # cvs: ../../work/ccvs/src/fileattr.c:75: fileattr_read: Assertion `fileattr_stored_repos != ((void *)0)' failed. +: dotest devcom-t-nonexist "${testcvs} watchers nonexist" fixme - # Modify and check in the first copy. - cd ../1/first-dir - echo '2' >> file1 - dotest serverpatch-7 "${testcvs} -q ci -mx file1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 -done" + dotest devcom-t1 "${testcvs} -q co -rtag first-dir/abb" \ +'U first-dir/abb' + cd .. + # Since first-dir/abb is readonly, use -f. + rm -rf 3 - # Now update the second copy. When using remote CVS, the - # patch will fail, forcing the file to be refetched. - cd ../../2/first-dir - dotest serverpatch-8 "${testcvs} -q update" \ -'U file1' \ -'P file1 -'"${PROG}"' [a-z]*: checksum failure after patch to ./file1; will refetch -'"${PROG}"' [a-z]*: refetching unpatchable files -U file1' + # Test checking out the directory rather than the file. + mkdir 3 + cd 3 + dotest devcom-t2 "${testcvs} -q co -rtag first-dir" \ +'U first-dir/abb +U first-dir/abc' + cd .. + # Since the files are readonly, use -f. + rm -rf 3 + + # Now do it again, after removing the val-tags file created + # by devcom-t1 to force CVS to search the repository + # containing CVS directories. + rm ${CVSROOT_DIRNAME}/CVSROOT/val-tags + mkdir 3 + cd 3 + dotest devcom-t3 "${testcvs} -q co -rtag first-dir" \ +'U first-dir/abb +U first-dir/abc' + cd .. + # Since the files are readonly, use -f. + rm -rf 3 + + # Now remove all the file attributes + cd 2/first-dir + dotest devcom-b0 "${testcvs} watch off" '' + dotest devcom-b1 "${testcvs} watch remove" '' + # Test that CVS 1.6 and earlier can handle the repository. + dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" + # Now test watching just some, not all, files. + dotest devcom-some0 "${testcvs} watch on abc" '' cd ../.. - rm -r 1 2 + mkdir 3 + cd 3 + dotest devcom-some1 "${testcvs} -q co first-dir" 'U first-dir/abb +U first-dir/abc' + dotest devcom-some2 "test -w first-dir/abb" '' + dotest_fail devcom-some3 "test -w first-dir/abc" '' + cd .. + + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi + + # Use -f because of the readonly files. + rm -rf 1 2 3 rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - log) - # Test selecting revisions with cvs log. - # See also log2 tests for more tests. - # See also branches-14.3 for logging with a branch off of a branch. - # See also multibranch-14 for logging with several branches off the - # same branchpoint. - # Tests of each option to cvs log: - # -h: admin-19a-log - # -N: log, log2, admin-19a-log - # -b, -r: log - # -d: rcs - - # Check in a file with a few revisions and branches. + devcom2) + # More watch tests, most notably setting watches on + # files in various different states. mkdir ${CVSROOT_DIRNAME}/first-dir - dotest log-1 "${testcvs} -q co first-dir" '' + mkdir 1 + cd 1 + dotest devcom2-1 "${testcvs} -q co first-dir" '' cd first-dir - echo 'first revision' > file1 - dotest log-2 "${testcvs} add file1" \ -"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - # While we're at it, check multi-line comments, input from file, - # and trailing whitespace trimming - echo 'line 1 ' >${TESTDIR}/comment.tmp - echo ' ' >>${TESTDIR}/comment.tmp - echo 'line 2 ' >>${TESTDIR}/comment.tmp - echo ' ' >>${TESTDIR}/comment.tmp - echo ' ' >>${TESTDIR}/comment.tmp - dotest log-3 "${testcvs} -q commit -F ${TESTDIR}/comment.tmp" \ + # This should probably be an error; setting a watch on a totally + # unknown file is more likely to be a typo than intentional. + # But that isn't the currently implemented behavior. + dotest devcom2-2 "${testcvs} watch on w1" '' + + touch w1 w2 w3 nw1 + dotest devcom2-3 "${testcvs} add w1 w2 w3 nw1" "${DOTSTAR}" + # Letting the user set the watch here probably can be considered + # a feature--although it leads to a few potentially strange + # consequences like one user can set the watch and another actually + # adds the file. + dotest devcom2-4 "${testcvs} watch on w2" '' + dotest devcom2-5 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + + # Note that this test differs in a subtle way from devcom-some0; + # in devcom-some0 the watch is creating a new fileattr file, and + # here we are modifying an existing one. + dotest devcom2-6 "${testcvs} watch on w3" '' + + # Now test that all the watches got set on the correct files + # FIXME: CVS should have a way to report whether watches are + # set, I think. The "check it out and see if it read-only" is + # sort of OK, but is complicated by CVSREAD and doesn't help + # if the file is added and not yet committed or some such. + # Probably "cvs status" should report "watch: on" if watch is on + # (and nothing if watch is off, so existing behavior is preserved). + cd ../.. + mkdir 2 + cd 2 + dotest devcom2-7 "${testcvs} -q co first-dir" 'U first-dir/nw1 +U first-dir/w1 +U first-dir/w2 +U first-dir/w3' + dotest devcom2-8 "test -w first-dir/nw1" '' + dotest_fail devcom2-9 "test -w first-dir/w1" '' + dotest_fail devcom2-10 "test -w first-dir/w2" '' + dotest_fail devcom2-11 "test -w first-dir/w3" '' + + cd first-dir + # OK, now we want to try files in various states with cvs edit. + dotest devcom2-12 "${testcvs} edit w4" \ +"${PROG} edit: no such file w4; ignored" + # Try the same thing with a per-directory watch set. + dotest devcom2-13 "${testcvs} watch on" '' + dotest devcom2-14 "${testcvs} edit w5" \ +"${PROG} edit: no such file w5; ignored" + dotest devcom2-15 "${testcvs} editors" '' + dotest devcom2-16 "${testcvs} editors w4" '' + # Make sure there are no droppings lying around + dotest devcom2-17 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw1 _watched= +Fw2 _watched= +Fw3 _watched= +Fnw1 _watched= +D _watched=" + cd .. + + cd .. + + # Use -f because of the readonly files. + rm -rf 1 2 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + + devcom3) + # More watch tests, most notably handling of features designed + # for future expansion. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest devcom3-1 "${testcvs} -q co first-dir" '' + cd first-dir + + touch w1 w2 + dotest devcom3-2 "${testcvs} add w1 w2" "${DOTSTAR}" + dotest devcom3-3 "${testcvs} watch on w1 w2" '' + dotest devcom3-4 "${testcvs} -q ci -m add-them" "${DOTSTAR}" + + # OK, since we are about to delve into CVS's internals, make + # sure that we seem to be correct about how they work. + dotest devcom3-5 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw1 _watched= +Fw2 _watched=" + # Now write a few more lines, just as if we were a newer version + # of CVS implementing some new feature. + cat <>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr +Enew line here +G@#$^!@#=& +EOF + # Now get CVS to write to the fileattr file.... + dotest devcom3-6 "${testcvs} watch off w1" '' + # ...and make sure that it hasn't clobbered our new lines. + # Note that writing these lines in another order would be OK + # too. + dotest devcom3-7 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw2 _watched= +G@#..!@#=& +Enew line here" + + # See what CVS does when a file name is duplicated. The + # behavior of all versions of CVS since file attributes were + # implemented is that it nukes the duplications. This seems + # reasonable enough, although it means it isn't clear how + # useful duplicates would be for purposes of future + # expansion. But in the interests of keeping behaviors + # predictable, might as well test for it, I guess. + echo 'Fw2 duplicate=' >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr + dotest devcom3-8 "${testcvs} watch on w1" '' + dotest devcom3-9 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ +"Fw2 _watched= +Fw1 _watched= +Enew line here +G@#..!@#=&" + + cd ../.. + + # Use -f because of the readonly files. + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + + watch4) + # More watch tests, including adding directories. + mkdir 1; cd 1 + dotest watch4-0a "${testcvs} -q co -l ." '' + mkdir first-dir + dotest watch4-0b "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + + cd first-dir + dotest watch4-1 "${testcvs} watch on" '' + # This is just like the 173 test + touch file1 + dotest watch4-2 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest watch4-3 "${testcvs} -q ci -m add" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done" - rm -f ${TESTDIR}/comment.tmp + # Now test the analogous behavior for directories. + mkdir subdir + dotest watch4-4 "${testcvs} add subdir" \ +"Directory ${TESTDIR}/cvsroot/first-dir/subdir added to the repository" + cd subdir + touch sfile + dotest watch4-5 "${testcvs} add sfile" \ +"${PROG} [a-z]*: scheduling file .sfile. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest watch4-6 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/subdir/sfile,v +done +Checking in sfile; +${TESTDIR}/cvsroot/first-dir/subdir/sfile,v <-- sfile +initial revision: 1\.1 +done" + cd ../../.. + mkdir 2; cd 2 + dotest watch4-7 "${testcvs} -q co first-dir" "U first-dir/file1 +U first-dir/subdir/sfile" + dotest_fail watch4-8 "test -w first-dir/file1" '' + dotest_fail watch4-9 "test -w first-dir/subdir/sfile" '' + cd first-dir + dotest watch4-10 "${testcvs} edit file1" '' + echo 'edited in 2' >file1 + cd ../.. - echo 'second revision' > file1 - dotest log-4 "${testcvs} -q ci -m2 file1" \ + cd 1/first-dir + dotest watch4-11 "${testcvs} edit file1" '' + echo 'edited in 1' >file1 + dotest watch4-12 "${testcvs} -q ci -m edit-in-1" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.2; previous revision: 1\.1 done" + cd ../.. + cd 2/first-dir + dotest watch4-13 "${testcvs} -q update" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into file1 +rcsmerge: warning: conflicts during merge +${PROG} [a-z]*: conflicts found in file1 +C file1" + if (echo yes | ${testcvs} unedit file1) >>${LOGFILE}; then + pass watch4-14 + else + fail watch4-15 + fi + # This could plausibly be defined to either go back to the revision + # which was cvs edit'd (the status quo), or back to revision 1.2 + # (that is, the merge could update CVS/Base/file1). We pick the + # former because it is easier to implement, not because we have + # thought much about which is better. + dotest watch4-16 "cat file1" '' + # Make sure CVS really thinks we are at 1.1. + dotest watch4-17 "${testcvs} -q update" "U file1" + dotest watch4-18 "cat file1" "edited in 1" + cd ../.. - dotest log-5 "${testcvs} -q tag -b branch file1" 'T file1' + # As a sanity check, make sure we are in the right place. + dotest watch4-cleanup-1 "test -d 1" '' + dotest watch4-cleanup-1 "test -d 2" '' + # Specify -f because of the readonly files. + rm -rf 1 2 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - echo 'third revision' > file1 - dotest log-6 "${testcvs} -q ci -m3 file1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3; previous revision: 1\.2 -done" + ignore) + # On Windows, we can't check out CVSROOT, because the case + # insensitivity means that this conflicts with cvsroot. + mkdir wnt + cd wnt - dotest log-7 "${testcvs} -q update -r branch" '[UP] file1' + dotest 187a1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}" + cd CVSROOT + echo rootig.c >cvsignore + dotest 187a2 "${testcvs} add cvsignore" "${PROG}"' [a-z]*: scheduling file `cvsignore'"'"' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - echo 'first branch revision' > file1 - dotest log-8 "${testcvs} -q ci -m1b file1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2\.2\.1; previous revision: 1\.2 -done" + # As of Jan 96, local CVS prints "Examining ." and remote doesn't. + # Accept either. + dotest 187a3 " ${testcvs} ci -m added" \ +"${PROG} [a-z]*: Examining \. +RCS file: ${TESTDIR}/cvsroot/CVSROOT/cvsignore,v +done +Checking in cvsignore; +${TESTDIR}/cvsroot/CVSROOT/cvsignore,v <-- cvsignore +initial revision: 1\.1 +done +${PROG} [a-z]*: Rebuilding administrative file database" - dotest log-9 "${testcvs} -q tag tag file1" 'T file1' + cd .. + if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then + pass 187a4 + else + fail 187a4 + fi - echo 'second branch revision' > file1 - dotest log-10 "${testcvs} -q ci -m2b file1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 -done" + # CVS looks at the home dir from getpwuid, not HOME (is that correct + # behavior?), so this is hard to test and we won't try. + # echo foobar.c >${HOME}/.cvsignore + CVSIGNORE=envig.c; export CVSIGNORE + mkdir dir-to-import + cd dir-to-import + touch foobar.c bar.c rootig.c defig.o envig.c optig.c + # We use sort because we can't predict the order in which + # the files will be listed. + dotest_sort 188a "${testcvs} import -m m -I optig.c first-dir tag1 tag2" \ +' - # Set up a bunch of shell variables to make the later tests - # easier to describe.= - log_header=" -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.3 -branch: -locks: strict -access list:" - log_tags='symbolic names: - tag: 1\.2\.2\.1 - branch: 1\.2\.0\.2' - log_header2='keyword substitution: kv' - log_dash='---------------------------- -revision' - log_date="date: [0-9/]* [0-9:]*; author: ${username}; state: Exp;" - log_lines=" lines: ${PLUS}1 -1" - log_rev1="${log_dash} 1\.1 -${log_date} -line 1 +I first-dir/defig.o +I first-dir/envig.c +I first-dir/optig.c +I first-dir/rootig.c +N first-dir/bar.c +N first-dir/foobar.c +No conflicts created by this import' + dotest_sort 188b "${testcvs} import -m m -I ! second-dir tag3 tag4" \ +' -line 2" - log_rev2="${log_dash} 1\.2 -${log_date}${log_lines} -branches: 1\.2\.2; -2" - log_rev3="${log_dash} 1\.3 -${log_date}${log_lines} -3" - log_rev1b="${log_dash} 1\.2\.2\.1 -${log_date}${log_lines} -1b" - log_rev2b="${log_dash} 1\.2\.2\.2 -${log_date}${log_lines} -2b" - log_trailer='=============================================================================' +N second-dir/bar.c +N second-dir/defig.o +N second-dir/envig.c +N second-dir/foobar.c +N second-dir/optig.c +N second-dir/rootig.c +No conflicts created by this import' + cd .. + rm -r dir-to-import - # Now, finally, test the log output. + mkdir 1 + cd 1 + dotest 189a "${testcvs} -q co second-dir" \ +'U second-dir/bar.c +U second-dir/defig.o +U second-dir/envig.c +U second-dir/foobar.c +U second-dir/optig.c +U second-dir/rootig.c' + dotest 189b "${testcvs} -q co first-dir" 'U first-dir/bar.c +U first-dir/foobar.c' + cd first-dir + touch rootig.c defig.o envig.c optig.c notig.c + dotest 189c "${testcvs} -q update -I optig.c" "${QUESTION} notig.c" + # The fact that CVS requires us to specify -I CVS here strikes me + # as a bug. + dotest_sort 189d "${testcvs} -q update -I ! -I CVS" \ +"${QUESTION} defig.o +${QUESTION} envig.c +${QUESTION} notig.c +${QUESTION} optig.c +${QUESTION} rootig.c" - dotest log-11 "${testcvs} log file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 5 -description: -${log_rev3} -${log_rev2} -${log_rev1} -${log_rev2b} -${log_rev1b} -${log_trailer}" + # Now test that commands other than update also print "? notig.c" + # where appropriate. Only test this for remote, because local + # CVS only prints it on update. + rm optig.c + if test "x$remote" = xyes; then + dotest 189e "${testcvs} -q diff" "${QUESTION} notig.c" - dotest log-12 "${testcvs} log -N file1" \ -"${log_header} -${log_header2} -total revisions: 5; selected revisions: 5 -description: -${log_rev3} -${log_rev2} -${log_rev1} -${log_rev2b} -${log_rev1b} -${log_trailer}" + # Force the server to be contacted. Ugh. Having CVS + # contact the server for the sole purpose of checking + # the CVSROOT/cvsignore file does not seem like such a + # good idea, so I imagine this will continue to be + # necessary. Oh well, at least we test CVS's ablity to + # handle a file with a modified timestamp but unmodified + # contents. + touch bar.c - dotest log-13 "${testcvs} log -b file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 3 -description: -${log_rev3} -${log_rev2} -${log_rev1} -${log_trailer}" + dotest 189f "${testcvs} -q ci -m commit-it" "${QUESTION} notig.c" + fi - dotest log-14 "${testcvs} log -r file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 1 -description: -${log_rev3} -${log_trailer}" + # now test .cvsignore files + cd .. + echo notig.c >first-dir/.cvsignore + echo foobar.c >second-dir/.cvsignore + touch first-dir/notig.c second-dir/notig.c second-dir/foobar.c + dotest_sort 190 "${testcvs} -qn update" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" + dotest_sort 191 "${testcvs} -qn update -I! -I CVS" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} first-dir/defig.o +${QUESTION} first-dir/envig.c +${QUESTION} first-dir/rootig.c +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" - dotest log-15 "${testcvs} log -r1.2 file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 1 -description: -${log_rev2} -${log_trailer}" + if echo yes | ${testcvs} release -d first-dir \ + >${TESTDIR}/ignore.tmp; then + pass ignore-192 + else + fail ignore-192 + fi + dotest ignore-193 "cat ${TESTDIR}/ignore.tmp" \ +"${QUESTION} \.cvsignore +You have \[0\] altered files in this repository. +Are you sure you want to release (and delete) directory .first-dir': " - dotest log-16 "${testcvs} log -r1.2.2 file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 2 -description: -${log_rev2b} -${log_rev1b} -${log_trailer}" + echo add a line >>second-dir/foobar.c + rm second-dir/notig.c second-dir/.cvsignore + if echo yes | ${testcvs} release -d second-dir \ + >${TESTDIR}/ignore.tmp; then + pass ignore-194 + else + fail ignore-194 + fi + dotest ignore-195 "cat ${TESTDIR}/ignore.tmp" \ +"M foobar.c +You have \[1\] altered files in this repository. +Are you sure you want to release (and delete) directory .second-dir': " + cd .. + rm -r 1 + cd .. + rm -r wnt + rm ${TESTDIR}/ignore.tmp + rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir + ;; - # This test would fail with the old invocation of rlog, but it - # works with the builtin log support. - dotest log-17 "${testcvs} log -rbranch file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 2 -description: -${log_rev2b} -${log_rev1b} -${log_trailer}" + binfiles) + # Test cvs's ability to handle binary files. + 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%c", 2, 10, 137, 0, 13, 10 }' \ + binfile.dat + cat binfile.dat binfile.dat >binfile2.dat + cd first-dir + cp ../binfile.dat binfile + dotest binfiles-2 "${testcvs} add -kb binfile" \ +"${PROG}"' [a-z]*: scheduling file `binfile'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest binfiles-3 "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v +done +Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +initial revision: 1\.1 +done" + cd ../.. + mkdir 2; cd 2 + dotest binfiles-4 "${testcvs} -q co first-dir" 'U first-dir/binfile' + cd first-dir + dotest binfiles-5 "cmp ../../1/binfile.dat binfile" '' + # Testing that sticky options is -kb is the closest thing we have + # to testing that binary files work right on non-unix machines + # (until there is automated testing for such machines, of course). + dotest binfiles-5.5 "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date - dotest log-18 "${testcvs} log -r1.2.2. file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 1 -description: -${log_rev2b} -${log_trailer}" + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" - # This test would fail with the old invocation of rlog, but it - # works with the builtin log support. - dotest log-19 "${testcvs} log -rbranch. file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 1 -description: -${log_rev2b} -${log_trailer}" + # Test whether the default options from the RCS file are + # also used when operating on files instead of whole + # directories + cd ../.. + mkdir 3; cd 3 + dotest binfiles-5.5b0 "${testcvs} -q co first-dir/binfile" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-5.5b1 "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date - dotest log-20 "${testcvs} log -r1.2: file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 2 -description: -${log_rev3} -${log_rev2} -${log_trailer}" + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + cd ../.. + rm -r 3 + cd 2/first-dir - dotest log-21 "${testcvs} log -r:1.2 file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 2 -description: -${log_rev2} -${log_rev1} -${log_trailer}" + cp ../../1/binfile2.dat binfile + dotest binfiles-6 "${testcvs} -q ci -m modify-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.2; previous revision: 1\.1 +done" + cd ../../1/first-dir + dotest binfiles-7 "${testcvs} -q update" '[UP] binfile' + dotest binfiles-8 "cmp ../binfile2.dat binfile" '' - dotest log-22 "${testcvs} log -r1.1:1.2 file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 5; selected revisions: 2 -description: -${log_rev2} -${log_rev1} -${log_trailer}" + # Now test handling of conflicts with binary files. + cp ../binfile.dat binfile + dotest binfiles-con0 "${testcvs} -q ci -m modify-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.3; previous revision: 1\.2 +done" + cd ../../2/first-dir + echo 'edits in dir 2' >binfile + dotest binfiles-con1 "${testcvs} -q update" \ +"U binfile +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1\.3 from repository is now in binfile +${PROG} [a-z]*: file from working directory is now in \.#binfile\.1\.2 +C binfile" + dotest binfiles-con2 "cmp binfile ../../1/binfile.dat" '' + dotest binfiles-con3 "cat .#binfile.1.2" 'edits in dir 2' - dotest log-o0 "${testcvs} admin -o 1.2.2.2:: file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v + cp ../../1/binfile2.dat binfile + dotest binfiles-con4 "${testcvs} -q ci -m resolve-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.4; previous revision: 1\.3 done" - dotest log-o1 "${testcvs} admin -o ::1.2.2.1 file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v + cd ../../1/first-dir + dotest binfiles-con5 "${testcvs} -q update" '[UP] binfile' + + dotest binfiles-9 "${testcvs} -q update -A" '' + dotest binfiles-10 "${testcvs} -q update -kk" '[UP] binfile' + dotest binfiles-11 "${testcvs} -q update" '' + dotest binfiles-12 "${testcvs} -q update -A" '[UP] binfile' + dotest binfiles-13 "${testcvs} -q update -A" '' + + cd ../.. + rm -r 1 + + mkdir 3 + cd 3 + dotest binfiles-13a0 "${testcvs} -q co -r HEAD first-dir" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-13a1 "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.4.* + Repository revision: 1\.4 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: HEAD (revision: 1\.4) + Sticky Date: (none) + Sticky Options: -kb" + cd ../.. + rm -r 3 + + cd 2/first-dir + echo 'this file is $''RCSfile$' >binfile + dotest binfiles-14a "${testcvs} -q ci -m modify-it" \ +"Checking in binfile; +${TESTDIR}/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.5; previous revision: 1\.4 done" - dotest log-o2 "${testcvs} admin -o 1.2.2.1:: file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -deleting revision 1\.2\.2\.2 + dotest binfiles-14b "cat binfile" 'this file is $''RCSfile$' + # See binfiles-5.5 for discussion of -kb. + dotest binfiles-14c "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + dotest binfiles-14d "${testcvs} admin -kv binfile" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v done" - dotest log-o3 "${testcvs} log file1" \ -"${log_header} -${log_tags} -${log_header2} -total revisions: 4; selected revisions: 4 -description: -${log_rev3} -${log_rev2} -${log_rev1} -${log_rev1b} -${log_trailer}" - dotest log-o4 "${testcvs} -q update -p -r 1.2.2.1 file1" \ -"first branch revision" - cd .. - rm -r first-dir - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + # cvs admin doesn't change the checked-out file or its sticky + # kopts. There probably should be a way which does (but + # what if the file is modified? And do we try to version + # control the kopt setting?) + dotest binfiles-14e "cat binfile" 'this file is $''RCSfile$' + dotest binfiles-14f "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date - log2) - # More "cvs log" tests, for example the file description. + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + dotest binfiles-14g "${testcvs} -q update -A" '[UP] binfile' + dotest binfiles-14h "cat binfile" 'this file is binfile,v' + dotest binfiles-14i "${testcvs} status binfile" \ +"=================================================================== +File: binfile Status: Up-to-date - # Check in a file - mkdir ${CVSROOT_DIRNAME}/first-dir - dotest log2-1 "${testcvs} -q co first-dir" '' - cd first-dir - echo 'first revision' > file1 - dotest log2-2 "${testcvs} add -m file1-is-for-testing file1" \ -"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition -'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - dotest log2-3 "${testcvs} -q commit -m 1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v + Working revision: 1\.5.* + Repository revision: 1\.5 ${TESTDIR}/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kv" + + # Do sticky options work when used with 'cvs update'? + echo "Not a binary file." > nibfile + dotest binfiles-sticky1 "${testcvs} -q add nibfile" \ +"${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest binfiles-sticky2 "${testcvs} -q ci -m add-it nibfile" \ + "RCS file: ${TESTDIR}/cvsroot/first-dir/nibfile,v done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +Checking in nibfile; +${TESTDIR}/cvsroot/first-dir/nibfile,v <-- nibfile initial revision: 1\.1 done" - # Setting the file description with add -m doesn't yet work - # client/server, so skip log2-4 for remote. - if test "x$remote" = xno; then + dotest binfiles-sticky3 "${testcvs} -q update -kb nibfile" \ + '[UP] nibfile' + dotest binfiles-sticky4 "${testcvs} -q status nibfile" \ +"=================================================================== +File: nibfile Status: Up-to-date - dotest log2-4 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: -file1-is-for-testing ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -1 -=============================================================================" + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" - fi # end of tests skipped for remote + # Now test that -A can clear the sticky option. + dotest binfiles-sticky5 "${testcvs} -q update -A nibfile" \ +"[UP] nibfile" + dotest binfiles-sticky6 "${testcvs} -q status nibfile" \ +"=================================================================== +File: nibfile Status: Up-to-date - dotest log2-5 "${testcvs} admin -t-change-description file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done" - dotest log2-6 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: -change-description ----------------------------- -revision 1\.1 -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 + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + dotest binfiles-15 "${testcvs} -q admin -kb nibfile" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/nibfile,v done" - dotest log2-8 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: -longer description -with two lines ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -1 -=============================================================================" + dotest binfiles-16 "${testcvs} -q update nibfile" "[UP] nibfile" + dotest binfiles-17 "${testcvs} -q status nibfile" \ +"=================================================================== +File: nibfile Status: Up-to-date - # 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 + Working revision: 1\.1.* + Repository revision: 1\.1 ${TESTDIR}/cvsroot/first-dir/nibfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" - 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" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 + dotest binfiles-o1 "${testcvs} admin -o1.3:: binfile" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v +deleting revision 1\.5 +deleting revision 1\.4 +done" + dotest binfiles-o2 "${testcvs} admin -o::1.3 binfile" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v +deleting revision 1\.2 +deleting revision 1\.1 +done" + dotest binfiles-o3 "${testcvs} -q log -h -N binfile" " +RCS file: ${TESTDIR}/cvsroot/first-dir/binfile,v +Working file: binfile +head: 1\.3 branch: locks: strict access list: -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: -change from stdin ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -1 +keyword substitution: v +total revisions: 1 =============================================================================" - fi # end of tests skipped for remote - - cd .. - rm ${TESTDIR}/descrip - rm -r first-dir + cd ../.. rm -rf ${CVSROOT_DIRNAME}/first-dir - + rm -r 2 ;; - ann) - # Tests of "cvs annotate". See also basica-10. + binfiles2) + # Test cvs's ability to handle binary files, particularly branching + # and joining. The key thing we are worrying about is that CVS + # doesn't print "cannot merge binary files" or some such, in + # situations where no merging is required. + # See also "join" which does this with non-binary files. + # + # Cases (we are merging from the branch to the trunk): + # binfile.dat) File added on branch, not on trunk. + # File should be marked for addition. + # brmod) File modified on branch, not on trunk. + # File should be copied over to trunk (no merging is needed). + # brmod-trmod) File modified on branch, also on trunk. + # This is a conflict. Present the user with both files and + # let them figure it out. + # brmod-wdmod) File modified on branch, not modified in the trunk + # repository, but modified in the (trunk) working directory. + # This is also a conflict. + + mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1; cd 1 - dotest ann-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest ann-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + dotest binfiles2-1 "${testcvs} -q co first-dir" '' cd first-dir - cat >file1 <../binfile + cat ../binfile ../binfile >../binfile2 + cat ../binfile2 ../binfile >../binfile3 + + # FIXCVS: unless a branch has at least one file on it, + # tag_check_valid won't know it exists. So if brmod didn't + # exist, we would have to invent it. + cp ../binfile brmod + cp ../binfile brmod-trmod + cp ../binfile brmod-wdmod + dotest binfiles2-1a \ +"${testcvs} add -kb brmod brmod-trmod brmod-wdmod" \ +"${PROG} [a-z]*: scheduling file .brmod. for addition +${PROG} [a-z]*: scheduling file .brmod-trmod. for addition +${PROG} [a-z]*: scheduling file .brmod-wdmod. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + dotest binfiles2-1b "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod,v done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +Checking in brmod; +${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +done +Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v +done +Checking in brmod-wdmod; +${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod initial revision: 1\.1 done" - cat >file1 <file1 <file1 <>${LOGFILE}; then - pass crerepos-5 - else - fail crerepos-5 - fi - rm -r CVS - cd .. - # The directory tmp should be empty - dotest crerepos-6 "rmdir tmp" '' - - CREREPOS_ROOT=${TESTDIR}/crerepos - - else - # For remote, just create the repository. We don't yet do - # the various other tests above for remote but that should be - # changed. - mkdir crerepos - mkdir crerepos/CVSROOT + dotest binfiles2-8 "${testcvs} -q update -j br" \ +"U binfile\.dat +U brmod +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-trmod +${PROG} [a-z]*: file from working directory is now in .#brmod-trmod.1.2 +C brmod-trmod +M brmod-wdmod +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-wdmod +${PROG} [a-z]*: file from working directory is now in .#brmod-wdmod.1.1 +C brmod-wdmod" - CREREPOS_ROOT=:ext:`hostname`:${TESTDIR}/crerepos + dotest binfiles2-9 "cmp ../binfile binfile.dat" + dotest binfiles2-9-brmod "cmp ../binfile2 brmod" + dotest binfiles2-9-brmod-trmod "cmp ../binfile2 brmod-trmod" + dotest binfiles2-9-brmod-trmod "cmp ../binfile2 brmod-wdmod" + dotest binfiles2-9a-brmod-trmod "cmp ../binfile3 .#brmod-trmod.1.2" + dotest binfiles2-9a-brmod-wdmod "cmp ../binfile3 .#brmod-wdmod.1.1" - fi + # Test that everything was properly scheduled. + dotest binfiles2-10 "${testcvs} -q ci -m checkin" \ +"Checking in binfile\.dat; +${TESTDIR}/cvsroot/first-dir/binfile\.dat,v <-- binfile\.dat +new revision: 1\.2; previous revision: 1\.1 +done +Checking in brmod; +${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod +new revision: 1\.2; previous revision: 1\.1 +done +Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +new revision: 1\.3; previous revision: 1\.2 +done +Checking in brmod-wdmod; +${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod +new revision: 1\.2; previous revision: 1\.1 +done" - if test "x$remote" = "xno"; then - # Test that CVS rejects a relative path in CVSROOT. - mkdir 1; cd 1 - dotest_fail crerepos-6a "${testcvs} -q -d ../crerepos get ." \ -"${PROG} \[[a-z]* aborted\]: CVSROOT ../crerepos must be an absolute pathname" - cd .. - rm -r 1 + dotest_fail binfiles2-o1 "${testcvs} -q admin -o :1.2 brmod-trmod" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +deleting revision 1\.2 +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v: can't remove branch point 1\.1 +${PROG} [a-z]*: cannot modify RCS file for .brmod-trmod." + dotest binfiles2-o2 "${testcvs} -q admin -o 1.1.2.1: brmod-trmod" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +deleting revision 1\.1\.2\.1 +done" + dotest binfiles2-o3 "${testcvs} -q admin -o :1.2 brmod-trmod" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +deleting revision 1\.2 +deleting revision 1\.1 +done" + dotest binfiles2-o4 "${testcvs} -q log -N brmod-trmod" " +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +Working file: brmod-trmod +head: 1\.3 +branch: +locks: strict +access list: +keyword substitution: b +total revisions: 1; selected revisions: 1 +description: +---------------------------- +revision 1\.3 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +checkin +=============================================================================" + cd .. + cd .. - mkdir 1; cd 1 - dotest_fail crerepos-6b "${testcvs} -d crerepos init" \ -"${PROG} \[[a-z]* aborted\]: CVSROOT crerepos must be an absolute pathname" - cd .. - rm -r 1 - else # remote - # Test that CVS rejects a relative path in CVSROOT. - mkdir 1; cd 1 - dotest_fail crerepos-6a \ -"${testcvs} -q -d :ext:`hostname`:../crerepos get ." \ -"Root ../crerepos must be an absolute pathname" - cd .. - rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -r 1 + ;; - mkdir 1; cd 1 - dotest_fail crerepos-6b \ -"${testcvs} -d :ext:`hostname`:crerepos init" \ -"Root crerepos must be an absolute pathname" - cd .. - rm -r 1 - fi # end of tests to be skipped for remote + mcopy) + # See comment at "mwrap" test for list of other wrappers tests. + # Test cvs's ability to handle nonmergeable files specified with + # -m 'COPY' in wrappers. Similar to the binfiles2 test, + # which tests the same thing for binary files + # (which are non-mergeable in the same sense). + # + # Cases (we are merging from the branch to the trunk): + # brmod) File modified on branch, not on trunk. + # File should be copied over to trunk (no merging is needed). + # brmod-trmod) File modified on branch, also on trunk. + # This is a conflict. Present the user with both files and + # let them figure it out. + # brmod-wdmod) File modified on branch, not modified in the trunk + # repository, but modified in the (trunk) working directory. + # This is also a conflict. - # CVS better not create a history file--if the administrator - # doesn't need it and wants to save on disk space, they just - # delete it. - dotest_fail crerepos-7 \ -"test -f ${TESTDIR}/crerepos/CVSROOT/history" '' + # For the moment, remote CVS can't pass wrappers from CVSWRAPPERS + # (see wrap_send). So skip these tests for remote. + if test "x$remote" = xno; then - # Now test mixing repositories. This kind of thing tends to - # happen accidentally when people work with several repositories. + mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1; cd 1 - dotest crerepos-8 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest crerepos-9 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + dotest mcopy-1 "${testcvs} -q co first-dir" '' cd first-dir - touch file1 - dotest crerepos-10 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest crerepos-11 "${testcvs} -q ci -m add-it" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 -done" - cd ../.. - rm -r 1 - mkdir 1; cd 1 - dotest crerepos-12 "${testcvs} -d ${CREREPOS_ROOT} -q co -l ." '' - mkdir crerepos-dir - dotest crerepos-13 "${testcvs} add crerepos-dir" \ -"Directory ${TESTDIR}/crerepos/crerepos-dir added to the repository" - cd crerepos-dir - touch cfile - dotest crerepos-14 "${testcvs} add cfile" \ -"${PROG} [a-z]*: scheduling file .cfile. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest crerepos-15 "${testcvs} -q ci -m add-it" \ -"RCS file: ${TESTDIR}/crerepos/crerepos-dir/cfile,v + # FIXCVS: unless a branch has at least one file on it, + # tag_check_valid won't know it exists. So if brmod didn't + # exist, we would have to invent it. + echo 'brmod initial contents' >brmod + echo 'brmod-trmod initial contents' >brmod-trmod + echo 'brmod-wdmod initial contents' >brmod-wdmod + echo "* -m 'COPY'" >.cvswrappers + dotest mcopy-1a \ +"${testcvs} add .cvswrappers brmod brmod-trmod brmod-wdmod" \ +"${PROG} [a-z]*: scheduling file .\.cvswrappers. for addition +${PROG} [a-z]*: scheduling file .brmod. for addition +${PROG} [a-z]*: scheduling file .brmod-trmod. for addition +${PROG} [a-z]*: scheduling file .brmod-wdmod. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + dotest mcopy-1b "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/\.cvswrappers,v done -Checking in cfile; -${TESTDIR}/crerepos/crerepos-dir/cfile,v <-- cfile +Checking in \.cvswrappers; +${TESTDIR}/cvsroot/first-dir/\.cvswrappers,v <-- \.cvswrappers +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod,v +done +Checking in brmod; +${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-trmod,v +done +Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v +done +Checking in brmod-wdmod; +${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod initial revision: 1\.1 done" - cd ../.. - rm -r 1 - mkdir 1; cd 1 - dotest crerepos-16 "${testcvs} co first-dir" \ -"${PROG} [a-z]*: Updating first-dir -U first-dir/file1" - dotest crerepos-17 "${testcvs} -d ${CREREPOS_ROOT} co crerepos-dir" \ -"${PROG} [a-z]*: Updating crerepos-dir -U crerepos-dir/cfile" + # NOTE: .cvswrappers files are broken (see comment in + # src/wrapper.c). So doing everything via the environment + # variable is a workaround. Better would be to test them + # both. + CVSWRAPPERS="* -m 'COPY'" + export CVSWRAPPERS + dotest mcopy-2 "${testcvs} -q tag -b br" 'T \.cvswrappers +T brmod +T brmod-trmod +T brmod-wdmod' + dotest mcopy-3 "${testcvs} -q update -r br" '' + echo 'modify brmod on br' >brmod + echo 'modify brmod-trmod on br' >brmod-trmod + echo 'modify brmod-wdmod on br' >brmod-wdmod + dotest mcopy-5 "${testcvs} -q ci -m br-changes" \ +"Checking in brmod; +${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in brmod-wdmod; +${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + dotest mcopy-6 "${testcvs} -q update -A" \ +"[UP] brmod +[UP] brmod-trmod +[UP] brmod-wdmod" + dotest mcopy-7 "cat brmod brmod-trmod brmod-wdmod" \ +"brmod initial contents +brmod-trmod initial contents +brmod-wdmod initial contents" - if test x`cat CVS/Repository` = x.; then - # RELATIVE_REPOS - # Fatal error so that we don't go traipsing through the - # directories which happen to have the same names from the - # wrong repository. - dotest_fail crerepos-18 "${testcvs} -q update" \ -"${PROG} \[[a-z]* aborted\]: cannot open directory ${TESTDIR}/cvsroot/crerepos-dir: .*" '' - else - if test "$remote" = no; then - # The lack of an error doesn't mean CVS is really - # working (things are getting logged to the wrong - # history file and such). - dotest crerepos-18 "${testcvs} -q update" '' - else - # Fatal error so that we don't go traipsing through the - # directories which happen to have the same names from the - # wrong repository. - dotest_fail crerepos-18 "${testcvs} -q update" \ -"protocol error: directory .${TESTDIR}/crerepos/crerepos-dir. not within root .${TESTDIR}/cvsroot." - fi - fi + echo 'modify brmod-trmod again on trunk' >brmod-trmod + dotest mcopy-7a "${testcvs} -q ci -m tr-modify" \ +"Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +new revision: 1\.2; previous revision: 1\.1 +done" + echo 'modify brmod-wdmod in working dir' >brmod-wdmod + + dotest mcopy-8 "${testcvs} -q update -j br" \ +"U brmod +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-trmod +${PROG} [a-z]*: file from working directory is now in .#brmod-trmod.1.2 +C brmod-trmod +M brmod-wdmod +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1.1.2.1 from repository is now in brmod-wdmod +${PROG} [a-z]*: file from working directory is now in .#brmod-wdmod.1.1 +C brmod-wdmod" + + dotest mcopy-9 "cat brmod brmod-trmod brmod-wdmod" \ +"modify brmod on br +modify brmod-trmod on br +modify brmod-wdmod on br" + dotest mcopy-9a "cat .#brmod-trmod.1.2 .#brmod-wdmod.1.1" \ +"modify brmod-trmod again on trunk +modify brmod-wdmod in working dir" + # Test that everything was properly scheduled. + dotest mcopy-10 "${testcvs} -q ci -m checkin" \ +"Checking in brmod; +${TESTDIR}/cvsroot/first-dir/brmod,v <-- brmod +new revision: 1\.2; previous revision: 1\.1 +done +Checking in brmod-trmod; +${TESTDIR}/cvsroot/first-dir/brmod-trmod,v <-- brmod-trmod +new revision: 1\.3; previous revision: 1\.2 +done +Checking in brmod-wdmod; +${TESTDIR}/cvsroot/first-dir/brmod-wdmod,v <-- brmod-wdmod +new revision: 1\.2; previous revision: 1\.1 +done" + cd .. cd .. + rm -rf ${CVSROOT_DIRNAME}/first-dir rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/first-dir ${TESTDIR}/crerepos - ;; - - rcs) - # Test ability to import an RCS file. Note that this format - # is fixed--files written by RCS5, and other software which - # implements this format, will be out there "forever" and - # CVS must always be able to import such files. + unset CVSWRAPPERS - # See tests admin-13, admin-25 and rcs-8a for exporting RCS files. + fi # end of tests to be skipped for remote - mkdir ${CVSROOT_DIRNAME}/first-dir + ;; - # Currently the way to import an RCS file is to copy it - # directly into the repository. - # - # This file was written by RCS 5.7, and then the dates were - # hacked so that we test year 2000 stuff. Note also that - # "author" names are just strings, as far as importing - # RCS files is concerned--they need not correspond to user - # IDs on any particular system. - # - # I also tried writing a file with the RCS supplied with - # HPUX A.09.05. According to "man rcsintro" this is - # "Revision Number: 3.0; Release Date: 83/05/11". There - # were a few minor differences like whitespace but at least - # in simple cases like this everything else seemed the same - # as the file written by RCS 5.7 (so I won't try to make it - # a separate test case). + binwrap) + # Test the ability to specify binary-ness based on file name. + # See "mwrap" for a list of other wrappers tests. - cat <${CVSROOT_DIRNAME}/first-dir/file1,v -head 1.3; -access; -symbols; -locks; strict; -comment @# @; + mkdir dir-to-import + cd dir-to-import + touch foo.c foo.exe + # While we're here, test for rejection of duplicate tag names. + dotest_fail binwrap-0 \ + "${testcvs} import -m msg -I ! first-dir dup dup" \ +"${PROG} \[[a-z]* aborted\]: tag .dup. was specified more than once" -1.3 -date 2000.11.24.15.58.37; author kingdon; state Exp; -branches; -next 1.2; + if ${testcvs} import -m message -I ! -W "*.exe -k 'b'" \ + first-dir tag1 tag2 >>${LOGFILE}; then + pass binwrap-1 + else + fail binwrap-1 + fi + cd .. + rm -r dir-to-import + dotest binwrap-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c +U first-dir/foo.exe' + dotest binwrap-3 "${testcvs} -q status first-dir" \ +"=================================================================== +File: foo\.c Status: Up-to-date -1.2 -date 96.11.24.15.57.41; author kingdon; state Exp; -branches; -next 1.1; + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.c,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) -1.1 -date 96.11.24.15.56.05; author kingdon; state Exp; -branches; -next ; +=================================================================== +File: foo\.exe Status: Up-to-date + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.exe,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + rm -r first-dir + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; -desc -@file1 is for testing CVS -@ + binwrap2) + # Test the ability to specify binary-ness based on file name. + # See "mwrap" for a list of other wrappers tests. + mkdir dir-to-import + cd dir-to-import + touch foo.c foo.exe -1.3 -log -@delete second line; modify twelfth line -@ -text -@This is the first line -This is the third line -This is the fourth line -This is the fifth line -This is the sixth line -This is the seventh line -This is the eighth line -This is the ninth line -This is the tenth line -This is the eleventh line -This is the twelfth line (and what a line it is) -This is the thirteenth line -@ - - -1.2 -log -@add more lines -@ -text -@a1 1 -This is the second line -d11 1 -a11 1 -This is the twelfth line -@ - - -1.1 -log -@add file1 -@ -text -@d2 12 -@ -EOF - dotest rcs-1 "${testcvs} -q co first-dir" 'U first-dir/file1' - cd first-dir - dotest rcs-2 "${testcvs} -q log" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.3 -branch: -locks: strict -access list: -symbolic names: -keyword substitution: kv -total revisions: 3; selected revisions: 3 -description: -file1 is for testing CVS ----------------------------- -revision 1\.3 -date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 -delete second line; modify twelfth line ----------------------------- -revision 1\.2 -date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 -add more lines ----------------------------- -revision 1\.1 -date: 1996/11/24 15:56:05; author: kingdon; state: Exp; -add file1 -=============================================================================" - - # Note that the dates here are chosen so that (a) we test - # at least one date after 2000, (b) we will notice if the - # month and day are getting mixed up with each other. - # TODO: also test that year isn't getting mixed up with month - # or day, for example 01-02-03. - - # ISO8601 format. There are many, many, other variations - # specified by ISO8601 which we should be testing too. - dotest rcs-3 "${testcvs} -q log -d 1996-12-11<" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.3 -branch: -locks: strict -access list: -symbolic names: -keyword substitution: kv -total revisions: 3; selected revisions: 1 -description: -file1 is for testing CVS ----------------------------- -revision 1\.3 -date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 -delete second line; modify twelfth line -=============================================================================" - - # RFC822 format (as amended by RFC1123). - if ${testcvs} -q log -d '<3 Apr 2000 00:00' >${TESTDIR}/rcs4.tmp - then - dotest rcs-4 "cat ${TESTDIR}/rcs4.tmp" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.3 -branch: -locks: strict -access list: -symbolic names: -keyword substitution: kv -total revisions: 3; selected revisions: 2 -description: -file1 is for testing CVS ----------------------------- -revision 1\.2 -date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 -add more lines ----------------------------- -revision 1\.1 -date: 1996/11/24 15:56:05; author: kingdon; state: Exp; -add file1 -=============================================================================" + # Specify that all files are binary except *.c. + # The order seems to matter, with the earlier rules taking + # precedence. I'm not sure whether that is good or not, + # but it is the current behavior. + if ${testcvs} import -m message -I ! \ + -W "*.c -k 'o'" -W "* -k 'b'" \ + first-dir tag1 tag2 >>${LOGFILE}; then + pass binwrap2-1 else - fail rcs-4 + fail binwrap2-1 fi + cd .. + rm -r dir-to-import + dotest binwrap2-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c +U first-dir/foo.exe' + dotest binwrap2-3 "${testcvs} -q status first-dir" \ +"=================================================================== +File: foo\.c Status: Up-to-date - # OK, here is another one. This one was written by hand based on - # doc/RCSFILES and friends. - cat <${CVSROOT_DIRNAME}/first-dir/file2,v -head 1.5 ; - branch 1.2.6; -access ; -symbols; -locks; -testofanewphrase @without newphrase we'd have trouble extending @@ all@ ; -1.5 date 71.01.01.01.00.00; author joe; state bogus; branches; next 1.4; -1.4 date 71.01.01.00.00.05; author joe; state bogus; branches; next 1.3; -1.3 date 70.12.31.15.00.05; author joe; state bogus; branches; next 1.2; -1.2 date 70.12.31.12.15.05; author me; state bogus; branches 1.2.6.1; next 1.1; -1.1 date 70.12.31.11.00.05; author joe; state bogus; branches; next; newph; -1.2.6.1 date 71.01.01.08.00.05; author joe; state Exp; branches; next; -desc @@ -1.5 log @@ newphrase1; newphrase2 42; text @head revision@ -1.4 log @@ text @d1 1 -a1 1 -new year revision@ -1.3 log @@ text @d1 1 -a1 1 -old year revision@ -1.2 log @@ text @d1 1 -a1 1 -mid revision@ 1.1 + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.c,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -ko -log @@ text @d1 1 -a1 1 -start revision@ -1.2.6.1 log @@ text @d1 1 -a1 1 -branch revision@ -EOF - # First test the default branch. - dotest rcs-5 "${testcvs} -q update file2" "U file2" - dotest rcs-6 "cat file2" "branch revision" +=================================================================== +File: foo\.exe Status: Up-to-date - # Now get rid of the default branch, it will get in the way. - dotest rcs-7 "${testcvs} admin -b file2" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -done" - # But we do want to make sure that "cvs admin" leaves the newphrases - # in the file. - # The extra whitespace regexps are for the RCS library, which does - # not preserve whitespace in the dogmatic manner of RCS 5.7. -twp - dotest rcs-8 \ -"grep testofanewphrase ${CVSROOT_DIRNAME}/first-dir/file2,v" \ -"testofanewphrase[ ][ ]*@without newphrase we'd have trouble extending @@ all@[ ]*;" - # The easiest way to test for newphrases in deltas and deltatexts - # is to just look at the whole file, I guess. - dotest rcs-8a "cat ${CVSROOT_DIRNAME}/first-dir/file2,v" \ -"head 1\.5; -access; -symbols; -locks; + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 ${TESTDIR}/cvsroot/first-dir/foo\.exe,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb" + rm -r first-dir + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; -testofanewphrase @without newphrase we'd have trouble extending @@ all@; + binwrap3) + # Test communication of file-specified -k wrappers between + # client and server, in `import': + # + # 1. Set up a directory tree, populate it with files. + # 2. Give each directory a different .cvswrappers file. + # 3. Give the server its own .cvswrappers file. + # 4. Import the whole tree, see if the right files got set + # to binary. + # + # The tree has a top ("0th") level, and two subdirs, sub1/ + # and sub2/; sub2/ contains directory subsub/. Every + # directory has a .cvswrappers file as well as regular + # files. + # + # In the file names, "foo-b.*" should end up binary, and + # "foo-t.*" should end up text. Don't worry about the two + # letter extensions; they're just there to help me keep + # things straight. + # + # Here's the directory tree: + # + # ./ + # .cvswrappers + # foo-b.c0 + # foo-b.sb + # foo-t.c1 + # foo-t.st + # + # sub1/ sub2/ + # .cvswrappers .cvswrappers + # foo-b.c1 foo-b.sb + # foo-b.sb foo-b.st + # foo-t.c0 foo-t.c0 + # foo-t.st foo-t.c1 + # foo-t.c2 + # foo-t.c3 + # + # subsub/ + # .cvswrappers + # foo-b.c3 + # foo-b.sb + # foo-t.c0 + # foo-t.c1 + # foo-t.c2 + # foo-t.st -1\.5 -date 71\.01\.01\.01\.00\.00; author joe; state bogus; -branches; -next 1\.4; + binwrap3_line1="This is a test file " + binwrap3_line2="containing little of use " + binwrap3_line3="except this non-haiku" -1\.4 -date 71\.01\.01\.00\.00\.05; author joe; state bogus; -branches; -next 1\.3; + binwrap3_text="${binwrap3_line1}${binwrap3_line2}${binwrap3_line3}" -1\.3 -date 70\.12\.31\.15\.00\.05; author joe; state bogus; -branches; -next 1\.2; + cd ${TESTDIR} -1\.2 -date 70\.12\.31\.12\.15\.05; author me; state bogus; -branches - 1\.2\.6\.1; -next 1\.1; + # On Windows, we can't check out CVSROOT, because the case + # insensitivity means that this conflicts with cvsroot. + mkdir wnt + cd wnt -1\.1 -date 70\.12\.31\.11\.00\.05; author joe; state bogus; -branches; -next ; -newph ; + mkdir binwrap3 # the 0th dir + mkdir binwrap3/sub1 + mkdir binwrap3/sub2 + mkdir binwrap3/sub2/subsub + + echo "*.c0 -k 'b'" > binwrap3/.cvswrappers + echo "whatever -k 'b'" >> binwrap3/.cvswrappers + echo ${binwrap3_text} > binwrap3/foo-b.c0 + echo ${binwrap3_text} > binwrap3/foo-b.sb + echo ${binwrap3_text} > binwrap3/foo-t.c1 + echo ${binwrap3_text} > binwrap3/foo-t.st -1\.2\.6\.1 -date 71\.01\.01\.08\.00\.05; author joe; state Exp; -branches; -next ; + echo "*.c1 -k 'b'" > binwrap3/sub1/.cvswrappers + echo "whatever -k 'b'" >> binwrap3/sub1/.cvswrappers + echo ${binwrap3_text} > binwrap3/sub1/foo-b.c1 + echo ${binwrap3_text} > binwrap3/sub1/foo-b.sb + echo ${binwrap3_text} > binwrap3/sub1/foo-t.c0 + echo ${binwrap3_text} > binwrap3/sub1/foo-t.st + echo "*.st -k 'b'" > binwrap3/sub2/.cvswrappers + echo ${binwrap3_text} > binwrap3/sub2/foo-b.sb + echo ${binwrap3_text} > binwrap3/sub2/foo-b.st + echo ${binwrap3_text} > binwrap3/sub2/foo-t.c0 + echo ${binwrap3_text} > binwrap3/sub2/foo-t.c1 + echo ${binwrap3_text} > binwrap3/sub2/foo-t.c2 + echo ${binwrap3_text} > binwrap3/sub2/foo-t.c3 -desc -@@ + echo "*.c3 -k 'b'" > binwrap3/sub2/subsub/.cvswrappers + echo "foo -k 'b'" >> binwrap3/sub2/subsub/.cvswrappers + echo "c0* -k 'b'" >> binwrap3/sub2/subsub/.cvswrappers + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-b.c3 + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-b.sb + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c0 + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c1 + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.c2 + echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.st + # Now set up CVSROOT/cvswrappers, the easy way: + dotest binwrap3-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" + cd CVSROOT + # This destroys anything currently in cvswrappers, but + # presumably other tests will take care of it themselves if + # they use cvswrappers: + echo "foo*.sb -k 'b'" > cvswrappers + dotest binwrap3-2 "${testcvs} -q ci -m cvswrappers-mod" \ +"Checking in cvswrappers; +${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. -1\.5 -log -@@ -newphrase1 ; -newphrase2 42; -text -@head revision@ + # Avoid environmental interference + CVSWRAPPERS_SAVED=${CVSWRAPPERS} + unset CVSWRAPPERS + # Do the import + cd binwrap3 + # Not importing .cvswrappers tests whether the client is really + # letting the server know "honestly" whether the file is binary, + # rather than just letting the server see the .cvswrappers file. + dotest binwrap3-2a \ +"${testcvs} import -m . -I .cvswrappers binwrap3 tag1 tag2" \ +"[NI] ${DOTSTAR}" -1\.4 -log -@@ -text -@d1 1 -a1 1 -new year revision@ + # OK, now test "cvs add". + cd .. + rm -r binwrap3 + dotest binwrap3-2b "${testcvs} co binwrap3" "${DOTSTAR}" + cd binwrap3 + cd sub2 + echo "*.newbin -k 'b'" > .cvswrappers + echo .cvswrappers >.cvsignore + echo .cvsignore >>.cvsignore + touch file1.newbin file1.txt + dotest binwrap3-2c "${testcvs} add file1.newbin file1.txt" \ +"${PROG} [a-z]*: scheduling file .file1\.newbin. for addition +${PROG} [a-z]*: scheduling file .file1\.txt. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + dotest binwrap3-2d "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/binwrap3/sub2/file1\.newbin,v +done +Checking in file1\.newbin; +${TESTDIR}/cvsroot/binwrap3/sub2/file1\.newbin,v <-- file1\.newbin +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/binwrap3/sub2/file1\.txt,v +done +Checking in file1\.txt; +${TESTDIR}/cvsroot/binwrap3/sub2/file1\.txt,v <-- file1\.txt +initial revision: 1\.1 +done" + cd .. + # Now check out the module and see which files are binary. + cd .. + rm -r binwrap3 + dotest binwrap3-3 "${testcvs} co binwrap3" "${DOTSTAR}" + cd binwrap3 -1\.3 -log -@@ -text -@d1 1 -a1 1 -old year revision@ + # Running "cvs status" and matching output is too + # error-prone, too likely to falsely fail. Instead, we'll + # just grep the Entries lines: + dotest binwrap3-top1 "grep foo-b.c0 ./CVS/Entries" \ + "/foo-b.c0/1.1.1.1/[A-Za-z0-9 :]*/-kb/" -1\.2 -log -@@ -text -@d1 1 -a1 1 -mid revision@ + dotest binwrap3-top2 "grep foo-b.sb ./CVS/Entries" \ + "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" + dotest binwrap3-top3 "grep foo-t.c1 ./CVS/Entries" \ + "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" -1\.1 -log -@@ -text -@d1 1 -a1 1 -start revision@ + dotest binwrap3-top4 "grep foo-t.st ./CVS/Entries" \ + "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" + dotest binwrap3-sub1-1 "grep foo-b.c1 sub1/CVS/Entries" \ + "/foo-b.c1/1.1.1.1/[A-Za-z0-9 :]*/-kb/" -1\.2\.6\.1 -log -@@ -text -@d1 1 -a1 1 -branch revision@" + dotest binwrap3-sub1-2 "grep foo-b.sb sub1/CVS/Entries" \ + "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - # For remote, the "update -p -D" usage seems not to work. - # I'm not sure what is going on. - if test "x$remote" = "xno"; then + dotest binwrap3-sub1-3 "grep foo-t.c0 sub1/CVS/Entries" \ + "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" - 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 binwrap3-sub1-4 "grep foo-t.st sub1/CVS/Entries" \ + "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" - 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 binwrap3-sub2-1 "grep foo-b.sb sub2/CVS/Entries" \ + "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - 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 binwrap3-sub2-2 "grep foo-b.st sub2/CVS/Entries" \ + "/foo-b.st/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - # 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 binwrap3-sub2-3 "grep foo-t.c0 sub2/CVS/Entries" \ + "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" - # 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 binwrap3-sub2-4 "grep foo-t.c1 sub2/CVS/Entries" \ + "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" - fi # end of tests skipped for remote + dotest binwrap3-sub2-5 "grep foo-t.c2 sub2/CVS/Entries" \ + "/foo-t.c2/1.1.1.1/[A-Za-z0-9 :]*//" - # OK, now make sure cvs log doesn't have any trouble with the - # newphrases and such. - dotest rcs-14 "${testcvs} -q log file2" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -Working file: file2 -head: 1\.5 -branch: -locks: -access list: -symbolic names: -keyword substitution: kv -total revisions: 6; selected revisions: 6 -description: ----------------------------- -revision 1\.5 -date: 1971/01/01 01:00:00; author: joe; state: bogus; lines: ${PLUS}1 -1 -\*\*\* empty log message \*\*\* ----------------------------- -revision 1\.4 -date: 1971/01/01 00:00:05; author: joe; state: bogus; lines: ${PLUS}1 -1 -\*\*\* empty log message \*\*\* ----------------------------- -revision 1\.3 -date: 1970/12/31 15:00:05; author: joe; state: bogus; lines: ${PLUS}1 -1 -\*\*\* empty log message \*\*\* ----------------------------- -revision 1\.2 -date: 1970/12/31 12:15:05; author: me; state: bogus; lines: ${PLUS}1 -1 -branches: 1\.2\.6; -\*\*\* empty log message \*\*\* ----------------------------- -revision 1\.1 -date: 1970/12/31 11:00:05; author: joe; state: bogus; -\*\*\* empty log message \*\*\* ----------------------------- -revision 1\.2\.6\.1 -date: 1971/01/01 08:00:05; author: joe; state: Exp; lines: ${PLUS}1 -1 -\*\*\* empty log message \*\*\* -=============================================================================" - cd .. + dotest binwrap3-sub2-6 "grep foo-t.c3 sub2/CVS/Entries" \ + "/foo-t.c3/1.1.1.1/[A-Za-z0-9 :]*//" - rm -r first-dir ${TESTDIR}/rcs4.tmp - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest binwrap3-subsub1 "grep foo-b.c3 sub2/subsub/CVS/Entries" \ + "/foo-b.c3/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - big) + dotest binwrap3-subsub2 "grep foo-b.sb sub2/subsub/CVS/Entries" \ + "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/" - # Test ability to operate on big files. Intention is to - # test various realloc'ing code in RCS_deltas, rcsgetkey, - # etc. "big" is currently defined to be 1000 lines (64000 - # bytes), which in terms of files that users will use is not - # large, merely average, but my reasoning is that this - # should be big enough to make sure realloc'ing is going on - # and that raising it a lot would start to stress resources - # on machines which run the tests, without any significant - # benefit. + dotest binwrap3-subsub3 "grep foo-t.c0 sub2/subsub/CVS/Entries" \ + "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//" - mkdir ${CVSROOT_DIRNAME}/first-dir - dotest big-1 "${testcvs} -q co first-dir" '' - cd first-dir - for i in 0 1 2 3 4 5 6 7 8 9; do - for j in 0 1 2 3 4 5 6 7 8 9; do - for k in 0 1 2 3 4 5 6 7 8 9; do - echo \ -"This is line ($i,$j,$k) which goes into the file file1 for testing" >>file1 - done - done - done - dotest big-2 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest big-3 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 -done" - cd .. - mkdir 2 - cd 2 - dotest big-4 "${testcvs} -q get first-dir" "U first-dir/file1" - cd ../first-dir - echo "add a line to the end" >>file1 - dotest big-5 "${testcvs} -q ci -m modify" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 -done" - cd ../2/first-dir - # The idea here is particularly to test the Rcs-diff response - # and the reallocing thereof, for remote. - dotest big-6 "${testcvs} -q update" "[UP] file1" - cd ../.. + dotest binwrap3-subsub4 "grep foo-t.c1 sub2/subsub/CVS/Entries" \ + "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//" - if test "$keep" = yes; then - echo Keeping ${TESTDIR} and exiting due to --keep - exit 0 - fi + dotest binwrap3-subsub5 "grep foo-t.c2 sub2/subsub/CVS/Entries" \ + "/foo-t.c2/1.1.1.1/[A-Za-z0-9 :]*//" - rm -r first-dir 2 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest binwrap3-subsub6 "grep foo-t.st sub2/subsub/CVS/Entries" \ + "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//" - modes) - # Test repository permissions (CVSUMASK and so on). - # Although the tests in this section "cheat" by testing - # repository permissions, which are sort of not a user-visible - # sort of thing, the modes do have user-visible consequences, - # such as whether a second user can check out the files. But - # it would be awkward to test the consequences, so we don't. + dotest binwrap3-sub2-add1 "grep file1.newbin sub2/CVS/Entries" \ + "/file1.newbin/1.1/[A-Za-z0-9 :]*/-kb/" + dotest binwrap3-sub2-add2 "grep file1.txt sub2/CVS/Entries" \ + "/file1.txt/1.1/[A-Za-z0-9 :]*//" - # Solaris /bin/sh doesn't support export -n. I'm not sure - # what we can do about this, other than hope that whoever - # is running the tests doesn't have CVSUMASK set. - #export -n CVSUMASK # if unset, defaults to 002 + # Restore and clean up + cd .. + rm -r binwrap3 CVSROOT + cd .. + rm -r wnt + rm -rf ${CVSROOT_DIRNAME}/binwrap3 + CVSWRAPPERS=${CVSWRAPPERS_SAVED} + ;; - umask 077 - mkdir 1; cd 1 - dotest modes-1 "${testcvs} -q co -l ." '' + mwrap) + # Tests of various wrappers features: + # -m 'COPY' and cvs update: mwrap + # -m 'COPY' and joining: mcopy + # -k: binwrap, binwrap2 + # -t/-f: hasn't been written yet. + # + # Tests of different ways of specifying wrappers: + # CVSROOT/cvswrappers: mwrap + # -W: binwrap, binwrap2 + # .cvswrappers in working directory, local: mcopy + # CVSROOT/cvswrappers, .cvswrappers remote: binwrap3 + # CVSWRAPPERS environment variable: mcopy + + # This test is similar to binfiles-con1; -m 'COPY' specifies + # non-mergeableness the same way that -kb does. + + # On Windows, we can't check out CVSROOT, because the case + # insensitivity means that this conflicts with cvsroot. + mkdir wnt + cd wnt + + dotest mwrap-c1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" + cd CVSROOT + echo "* -m 'COPY'" >>cvswrappers + dotest mwrap-c2 "${testcvs} -q ci -m wrapper-mod" \ +"Checking in cvswrappers; +${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. + mkdir m1; cd m1 + dotest mwrap-1 "${testcvs} -q co -l ." '' mkdir first-dir - dotest modes-2 "${testcvs} add first-dir" \ + dotest mwrap-2 "${testcvs} add first-dir" \ "Directory ${TESTDIR}/cvsroot/first-dir added to the repository" cd first-dir touch aa - dotest modes-3 "${testcvs} add aa" \ + dotest mwrap-3 "${testcvs} add aa" \ "${PROG} [a-z]*: scheduling file .aa. for addition ${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest modes-4 "${testcvs} -q ci -m add" \ + dotest mwrap-4 "${testcvs} -q ci -m add" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v done Checking in aa; ${TESTDIR}/cvsroot/first-dir/aa,v <-- aa initial revision: 1\.1 done" - dotest modes-5 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ -"-r--r--r-- .*" - - # Test for whether we can set the execute bit. - chmod +x aa - echo change it >>aa - dotest modes-6 "${testcvs} -q ci -m set-execute-bit" \ + cd ../.. + mkdir m2; cd m2 + dotest mwrap-5 "${testcvs} -q co first-dir" "U first-dir/aa" + cd first-dir + echo "changed in m2" >aa + dotest mwrap-6 "${testcvs} -q ci -m m2-mod" \ "Checking in aa; ${TESTDIR}/cvsroot/first-dir/aa,v <-- aa new revision: 1\.2; previous revision: 1\.1 done" - # If CVS let us update the execute bit, it would be set here. - # But it doesn't, and as far as I know that is longstanding - # CVS behavior. - dotest modes-7 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ -"-r--r--r-- .*" + cd ../.. + cd m1/first-dir + echo "changed in m1" >aa + dotest_fail mwrap-7 "${testcvs} -nq update" "C aa" + dotest mwrap-8 "${testcvs} -q update" \ +"U aa +${PROG} [a-z]*: nonmergeable file needs merge +${PROG} [a-z]*: revision 1\.2 from repository is now in aa +${PROG} [a-z]*: file from working directory is now in \.#aa\.1\.1 +C aa" + dotest mwrap-9 "cat aa" "changed in m2" + dotest mwrap-10 "cat .#aa.1.1" "changed in m1" + cd ../.. + cd CVSROOT + echo '# comment out' >cvswrappers + dotest mwrap-ce "${testcvs} -q ci -m wrapper-mod" \ +"Checking in cvswrappers; +${TESTDIR}/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. + rm -r CVSROOT + rm -r m1 m2 + cd .. + rm -r wnt + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - # OK, now manually change the modes and see what happens. - chmod g=r,o= ${TESTDIR}/cvsroot/first-dir/aa,v - echo second line >>aa - dotest modes-7a "${testcvs} -q ci -m set-execute-bit" \ -"Checking in aa; -${TESTDIR}/cvsroot/first-dir/aa,v <-- aa -new revision: 1\.3; previous revision: 1\.2 -done" - dotest modes-7b "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ -"-r--r----- .*" + info) + # Administrative file tests. + # Here is a list of where each administrative file is tested: + # loginfo: info + # modules: modules, modules2, modules3 + # cvsignore: ignore + # verifymsg: info + # cvswrappers: mwrap + # config: config - CVSUMASK=007 - export CVSUMASK - touch ab - # Might as well test the execute bit too. - chmod +x ab - dotest modes-8 "${testcvs} add ab" \ -"${PROG} [a-z]*: scheduling file .ab. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest modes-9 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/ab,v -done -Checking in ab; -${TESTDIR}/cvsroot/first-dir/ab,v <-- ab -initial revision: 1\.1 -done" - if test "x$remote" = xyes; then - # The problem here is that the CVSUMASK environment variable - # needs to be set on the server (e.g. .bashrc). This is, of - # course, bogus, but that is the way it is currently. - dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ -"-r-xr-x---.*" "-r-xr-xr-x.*" - else - dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ -"-r-xr-x---.*" - fi + # On Windows, we can't check out CVSROOT, because the case + # insensitivity means that this conflicts with cvsroot. + mkdir wnt + cd wnt - # OK, now add a file on a branch. Check that the mode gets - # set the same way (it is a different code path in CVS). - dotest modes-11 "${testcvs} -q tag -b br" 'T aa -T ab' - dotest modes-12 "${testcvs} -q update -r br" '' - touch ac - dotest modes-13 "${testcvs} add ac" \ -"${PROG} [a-z]*: scheduling file .ac. for addition on branch .br. -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - # Not sure it really makes sense to refer to a "previous revision" - # when we are just now adding the file; as far as I know - # that is longstanding CVS behavior, for what it's worth. - dotest modes-14 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/ac,v -done -Checking in ac; -${TESTDIR}/cvsroot/first-dir/Attic/ac,v <-- ac -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done" - if test "x$remote" = xyes; then - # The problem here is that the CVSUMASK environment variable - # needs to be set on the server (e.g. .bashrc). This is, of - # course, bogus, but that is the way it is currently. - dotest modes-15 \ -"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ -"-r--r--r--.*" - else - dotest modes-15 \ -"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ -"-r--r-----.*" - fi - - cd ../.. - rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/first-dir - # Perhaps should restore the umask and CVSUMASK. But the other - # tests "should" not care about them... - ;; + dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" + cd CVSROOT + echo "ALL sh -c \"echo x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat >/dev/null\"" > loginfo + # The following cases test the format string substitution + echo "ALL echo %{sVv} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %{v} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %s >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "ALL echo %{V}AX >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo + echo "first-dir echo %sux >>$TESTDIR/testlog2; cat >/dev/null" \ + >> loginfo - stamps) - # Test timestamps. - mkdir 1; cd 1 - dotest stamps-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest stamps-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - touch aa - echo '$''Id$' >kw - ls -l aa >${TESTDIR}/1/stamp.aa.touch - ls -l kw >${TESTDIR}/1/stamp.kw.touch - # "sleep 1" would suffice if we could assume ls --full-time, but - # that is as far as I know unique to GNU ls. Is there some POSIX.2 - # way to get the timestamp of a file, including the seconds? - sleep 60 - dotest stamps-3 "${testcvs} add aa kw" \ -"${PROG} [a-z]*: scheduling file .aa. for addition -${PROG} [a-z]*: scheduling file .kw. for addition -${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - ls -l aa >${TESTDIR}/1/stamp.aa.add - ls -l kw >${TESTDIR}/1/stamp.kw.add - # "cvs add" should not muck with the timestamp. - dotest stamps-4aa \ -"cmp ${TESTDIR}/1/stamp.aa.touch ${TESTDIR}/1/stamp.aa.add" '' - dotest stamps-4kw \ -"cmp ${TESTDIR}/1/stamp.kw.touch ${TESTDIR}/1/stamp.kw.add" '' - sleep 60 - dotest stamps-5 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v -done -Checking in aa; -${TESTDIR}/cvsroot/first-dir/aa,v <-- aa -initial revision: 1\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/kw,v -done -Checking in kw; -${TESTDIR}/cvsroot/first-dir/kw,v <-- kw -initial revision: 1\.1 -done" - ls -l aa >${TESTDIR}/1/stamp.aa.ci - ls -l kw >${TESTDIR}/1/stamp.kw.ci - # If there are no keywords, "cvs ci" leaves the timestamp alone - # If there are, it sets the timestamp to the date of the commit. - # I'm not sure how logical this is, but it is intentional. - # If we wanted to get fancy we would make sure the time as - # reported in "cvs log kw" matched stamp.kw.ci. But that would - # be a lot of work. - dotest stamps-6aa \ - "cmp ${TESTDIR}/1/stamp.aa.add ${TESTDIR}/1/stamp.aa.ci" '' - if cmp ${TESTDIR}/1/stamp.kw.add ${TESTDIR}/1/stamp.kw.ci >/dev/null - then - fail stamps-6kw - else - pass stamps-6kw - fi - cd ../.. - sleep 60 - mkdir 2 - cd 2 - dotest stamps-7 "${testcvs} -q get first-dir" "U first-dir/aa -U first-dir/kw" - cd first-dir - ls -l aa >${TESTDIR}/1/stamp.aa.get - ls -l kw >${TESTDIR}/1/stamp.kw.get - # On checkout, CVS should set the timestamp to the date that the - # file was committed. Could check that the time as reported in - # "cvs log aa" matches stamp.aa.get, but that would be a lot of - # work. - if cmp ${TESTDIR}/1/stamp.aa.ci ${TESTDIR}/1/stamp.aa.get >/dev/null - then - fail stamps-8aa - else - pass stamps-8aa - fi - dotest stamps-8kw \ - "cmp ${TESTDIR}/1/stamp.kw.ci ${TESTDIR}/1/stamp.kw.get" '' + # Might be nice to move this to crerepos tests; it should + # work to create a loginfo file if you didn't create one + # with "cvs init". + : dotest info-2 "${testcvs} add loginfo" \ +"${PROG}"' [a-z]*: scheduling file `loginfo'"'"' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - # Now we want to see what "cvs update" does. - sleep 60 - echo add a line >>aa - echo add a line >>kw - dotest stamps-9 "${testcvs} -q ci -m change-them" \ -"Checking in aa; -${TESTDIR}/cvsroot/first-dir/aa,v <-- aa -new revision: 1\.2; previous revision: 1\.1 + dotest info-3 "${testcvs} -q ci -m new-loginfo" \ +"Checking in loginfo; +${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo +new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* done -Checking in kw; -${TESTDIR}/cvsroot/first-dir/kw,v <-- kw -new revision: 1\.2; previous revision: 1\.1 -done" - ls -l aa >${TESTDIR}/1/stamp.aa.ci2 - ls -l kw >${TESTDIR}/1/stamp.kw.ci2 - cd ../.. - cd 1/first-dir - sleep 60 - dotest stamps-10 "${testcvs} -q update" '[UP] aa -[UP] kw' - # this doesn't serve any function other than being able to - # look at it manually, as we have no machinery for dates being - # newer or older than other dates. - date >${TESTDIR}/1/stamp.debug.update - ls -l aa >${TESTDIR}/1/stamp.aa.update - ls -l kw >${TESTDIR}/1/stamp.kw.update - # stamp.aa.update and stamp.kw.update should both be approximately - # the same as stamp.debug.update. Perhaps we could be testing - # this in a more fancy fashion by "touch stamp.before" before - # stamps-10, "touch stamp.after" after, and then using ls -t - # to check them. But for now we just make sure that the *.update - # stamps differ from the *.ci2 ones. - # As for the rationale, this is so that if one updates and gets - # a new revision, then "make" will be sure to regard those files - # as newer than .o files which may be sitting around. - if cmp ${TESTDIR}/1/stamp.aa.update ${TESTDIR}/1/stamp.aa.ci2 \ - >/dev/null - then - fail stamps-11aa - else - pass stamps-11aa - fi - if cmp ${TESTDIR}/1/stamp.kw.update ${TESTDIR}/1/stamp.kw.ci2 \ - >/dev/null - then - fail stamps-11kw - else - pass stamps-11kw - fi - - cd ../.. - - if test "$keep" = yes; then - echo Keeping ${TESTDIR} and exiting due to --keep - exit 0 - fi - - rm -r 1 2 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. - sticky) - # More tests of sticky tags, particularly non-branch sticky tags. - # See many tests (e.g. multibranch) for ordinary sticky tag - # operations such as adding files on branches. - # See "head" test for interaction between stick tags and HEAD. - mkdir 1; cd 1 - dotest sticky-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest sticky-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest info-5 "${testcvs} -q co first-dir" '' cd first-dir - touch file1 - dotest sticky-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest sticky-4 "${testcvs} -q ci -m add" \ + dotest info-6 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc + dotest info-6a "${testcvs} -q -s OTHER=value ci -m add-it" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 -done" - dotest sticky-5 "${testcvs} -q tag tag1" "T file1" - echo add a line >>file1 - dotest sticky-6 "${testcvs} -q ci -m modify" \ +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 done" - dotest sticky-7 "${testcvs} -q update -r tag1" "[UP] file1" - dotest sticky-8 "cat file1" '' - dotest sticky-9 "${testcvs} -q update" '' - dotest sticky-10 "cat file1" '' - touch file2 - dotest_fail sticky-11 "${testcvs} add file2" \ -"${PROG} [a-z]*: cannot add file on non-branch tag tag1" - dotest sticky-12 "${testcvs} -q update -A" "[UP] file1 -${QUESTION} file2" "${QUESTION} file2 -[UP] file1" - dotest sticky-13 "${testcvs} add file2" \ -"${PROG} [a-z]*: scheduling file .file2. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest sticky-14 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -done -Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -initial revision: 1\.1 -done" + cd .. + dotest info-9 "cat $TESTDIR/testlog" "xenv-valueyz=${username}=${TESTDIR}/cvsroot=" + dotest info-10 "cat $TESTDIR/testlog2" 'first-dir file1,NONE,1.1 +first-dir 1.1 +first-dir file1 +first-dir NONEAX +first-dir file1ux +first-dir file1,1.1,1.2 +first-dir 1.2 +first-dir file1 +first-dir 1.1AX +first-dir file1ux' - # Now back to tag1 - dotest sticky-15 "${testcvs} -q update -r tag1" "[UP] file1 -${PROG} [a-z]*: file2 is no longer in the repository" + cd CVSROOT + echo '# do nothing' >loginfo + dotest info-11 "${testcvs} -q -s ZEE=garbage ci -m nuke-loginfo" \ +"Checking in loginfo; +${TESTDIR}/cvsroot/CVSROOT/loginfo,v <-- loginfo +new revision: 1\.[0-9]; previous revision: 1\.[0-9] +done +${PROG} [a-z]*: Rebuilding administrative file database" - rm file1 - dotest sticky-16 "${testcvs} rm file1" \ -"${PROG} [a-z]*: scheduling .file1. for removal -${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" - # Hmm, this command seems to silently remove the tag from - # the file. This appears to be intentional. - # The silently part especially strikes me as odd, though. - dotest sticky-17 "${testcvs} -q ci -m remove-it" "" - dotest sticky-18 "${testcvs} -q update -A" "U file1 -U file2" - dotest sticky-19 "${testcvs} -q update -r tag1" \ -"${PROG} [a-z]*: file1 is no longer in the repository -${PROG} [a-z]*: file2 is no longer in the repository" - dotest sticky-20 "${testcvs} -q update -A" "U file1 -U file2" + # Now test verifymsg + cat >${TESTDIR}/vscript < /dev/null; then + exit 0 +else + echo "No BugId found." + exit 1 +fi +EOF + chmod +x ${TESTDIR}/vscript + echo "^first-dir ${TESTDIR}/vscript" >>verifymsg + dotest info-v1 "${testcvs} -q ci -m add-verification" \ +"Checking in verifymsg; +${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Rebuilding administrative file database" - # Now try with a numeric revision. - dotest sticky-21 "${testcvs} -q update -r 1.1 file1" "U file1" - rm file1 - dotest sticky-22 "${testcvs} rm file1" \ -"${PROG} [a-z]*: cannot remove file .file1. which has a numeric sticky tag of .1\.1." - # The old behavior was that remove allowed this and then commit - # gave an error, which was somewhat hard to clear. I mean, you - # could get into a long elaborate discussion of this being a - # conflict and two ways to resolve it, but I don't really see - # why CVS should have a concept of conflict that arises, not from - # parallel development, but from CVS's own sticky tags. + cd ../first-dir + echo line2 >>file1 + dotest_fail info-v2 "${testcvs} -q ci -m bogus" \ +"No BugId found\. +${PROG} \[[a-z]* aborted\]: Message verification failed" - # I'm kind of surprised that the "file1 was lost" doesn't crop - # up elsewhere in the testsuite. It is a long-standing - # discrepency between local and remote CVS and should probably - # be cleaned up at some point. - dotest sticky-23 "${testcvs} -q update -A" \ -"${PROG} [a-z]*: warning: file1 was lost -U file1" "U file1" + cat >${TESTDIR}/comment.tmp <verifymsg + dotest info-cleanup-verifymsg "${testcvs} -q ci -m nuke-verifymsg" \ +"Checking in verifymsg; +${TESTDIR}/cvsroot/CVSROOT/verifymsg,v <-- verifymsg +new revision: 1\.[0-9]; previous revision: 1\.[0-9] +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd .. + + if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then + pass info-cleanup + else + fail info-cleanup + fi + if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE} ; then + pass info-cleanup-2 + else + fail info-cleanup-2 + fi + cd .. + rm -r wnt rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - keyword) - # Test keyword expansion. - # Various other tests relate to our ability to correctly - # set the keyword expansion mode. - # "binfiles" tests "cvs admin -k". - # "binfiles" and "binfiles2" test "cvs add -k". - # "rdiff" tests "cvs co -k". - # "binfiles" (and this test) test "cvs update -k". - # "binwrap" tests setting the mode from wrappers. - # I don't think any test is testing "cvs import -k". - mkdir 1; cd 1 - dotest keyword-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest keyword-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir + config) + # Tests of the CVSROOT/config file. See the comment at the + # "info" tests for a full list of administrative file tests. - echo '$''Author$' > file1 - echo '$''Date$' >> file1 - echo '$''Header$' >> file1 - echo '$''Id$' >> file1 - echo '$''Locker$' >> file1 - echo '$''Name$' >> file1 - echo '$''RCSfile$' >> file1 - echo '$''Revision$' >> file1 - echo '$''Source$' >> file1 - echo '$''State$' >> file1 - echo '$''Nonkey$' >> file1 - # Omit the trailing dollar sign - echo '$''Date' >> file1 - # Put two keywords on one line - echo '$''State$' '$''State$' >> file1 - # Use a header for Log - echo 'xx $''Log$' >> file1 + # On Windows, we can't check out CVSROOT, because the case + # insensitivity means that this conflicts with cvsroot. + mkdir wnt + cd wnt - dotest keyword-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest keyword-4 "${testcvs} -q ci -m add" \ + dotest config-1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}" + cd CVSROOT + echo 'bogus line' >config + dotest config-3 "${testcvs} -q ci -m change-to-bogus-line" \ +"Checking in config; +${TESTDIR}/cvsroot/CVSROOT/config,v <-- config +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Rebuilding administrative file database" + echo 'BogusOption=yes' >config + dotest config-4 "${testcvs} -q ci -m change-to-bogus-opt" \ +"${PROG} [a-z]*: syntax error in ${TESTDIR}/cvsroot/CVSROOT/config: line 'bogus line' is missing '=' +Checking in config; +${TESTDIR}/cvsroot/CVSROOT/config,v <-- config +new revision: 1\.3; previous revision: 1\.2 +done +${PROG} [a-z]*: Rebuilding administrative file database" + echo '# No config is a good config' > config + dotest config-5 "${testcvs} -q ci -m change-to-comment" \ +"${PROG} [a-z]*: ${TESTDIR}/cvsroot/CVSROOT/config: unrecognized keyword 'BogusOption' +Checking in config; +${TESTDIR}/cvsroot/CVSROOT/config,v <-- config +new revision: 1\.4; previous revision: 1\.3 +done +${PROG} [a-z]*: Rebuilding administrative file database" + dotest config-6 "${testcvs} -q update" '' + + cd .. + rm -r CVSROOT + cd .. + rm -r wnt + ;; + + serverpatch) + # Test remote CVS handling of unpatchable files. This isn't + # much of a test for local CVS. + # We test this with some keyword expansion games, but the situation + # also arises if the user modifies the file while CVS is running. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest serverpatch-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add a file with an RCS keyword. + echo '$''Name$' > file1 + echo '1' >> file1 + dotest serverpatch-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + + dotest serverpatch-3 "${testcvs} -q commit -m add" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done" - dotest keyword-5 "cat file1" \ -'\$'"Author: ${username} "'\$'" -"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" -"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" -"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" -"'\$'"Locker: "'\$'" -"'\$'"Name: "'\$'" -"'\$'"RCSfile: file1,v "'\$'" -"'\$'"Revision: 1\.1 "'\$'" -"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" -"'\$'"State: Exp "'\$'" -"'\$'"Nonkey"'\$'" -"'\$'"Date -"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" -xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.1 [0-9/]* [0-9:]* ${username} -xx add -xx" - # Use cvs admin to lock the RCS file in order to check -kkvl - # vs. -kkv. CVS does not normally lock RCS files, but some - # people use cvs admin to enforce reserved checkouts. - dotest keyword-6 "${testcvs} admin -l file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -1\.1 locked -done" + # Tag the file. + dotest serverpatch-4 "${testcvs} -q tag tag file1" 'T file1' - dotest keyword-7 "${testcvs} update -kkv file1" "U file1" - dotest keyword-8 "cat file1" \ -'\$'"Author: ${username} "'\$'" -"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" -"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" -"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" -"'\$'"Locker: "'\$'" -"'\$'"Name: "'\$'" -"'\$'"RCSfile: file1,v "'\$'" -"'\$'"Revision: 1\.1 "'\$'" -"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" -"'\$'"State: Exp "'\$'" -"'\$'"Nonkey"'\$'" -"'\$'"Date -"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" -xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.1 [0-9/]* [0-9:]* ${username} -xx add -xx" + # Check out a tagged copy of the file. + cd ../.. + mkdir 2 + cd 2 + dotest serverpatch-5 "${testcvs} -q co -r tag first-dir" \ +'U first-dir/file1' - dotest keyword-9 "${testcvs} update -kkvl file1" "U file1" - dotest keyword-10 "cat file1" \ -'\$'"Author: ${username} "'\$'" -"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" -"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" -"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" -"'\$'"Locker: ${username} "'\$'" -"'\$'"Name: "'\$'" -"'\$'"RCSfile: file1,v "'\$'" -"'\$'"Revision: 1\.1 "'\$'" -"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" -"'\$'"State: Exp "'\$'" -"'\$'"Nonkey"'\$'" -"'\$'"Date -"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" -xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.1 [0-9/]* [0-9:]* ${username} -xx add -xx" - - dotest keyword-11 "${testcvs} update -kk file1" "U file1" - dotest keyword-12 "cat file1" \ -'\$'"Author"'\$'" -"'\$'"Date"'\$'" -"'\$'"Header"'\$'" -"'\$'"Id"'\$'" -"'\$'"Locker"'\$'" -"'\$'"Name"'\$'" -"'\$'"RCSfile"'\$'" -"'\$'"Revision"'\$'" -"'\$'"Source"'\$'" -"'\$'"State"'\$'" -"'\$'"Nonkey"'\$'" -"'\$'"Date -"'\$'"State"'\$'" "'\$'"State"'\$'" -xx "'\$'"Log"'\$'" -xx Revision 1\.1 [0-9/]* [0-9:]* ${username} -xx add -xx" - - dotest keyword-13 "${testcvs} update -kv file1" "U file1" - dotest keyword-14 "cat file1" \ -"${username} -[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] -${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp -file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp - - -file1,v -1\.1 -${TESTDIR}/cvsroot/first-dir/file1,v -Exp -"'\$'"Nonkey"'\$'" -"'\$'"Date -Exp Exp -xx file1,v -xx Revision 1\.1 [0-9/]* [0-9:]* ${username} -xx add -xx" - - dotest keyword-15 "${testcvs} update -ko file1" "U file1" - dotest keyword-16 "cat file1" \ -'\$'"Author"'\$'" -"'\$'"Date"'\$'" -"'\$'"Header"'\$'" -"'\$'"Id"'\$'" -"'\$'"Locker"'\$'" -"'\$'"Name"'\$'" -"'\$'"RCSfile"'\$'" -"'\$'"Revision"'\$'" -"'\$'"Source"'\$'" -"'\$'"State"'\$'" -"'\$'"Nonkey"'\$'" -"'\$'"Date -"'\$'"State"'\$'" "'\$'"State"'\$'" -xx "'\$'"Log"'\$' - - # Test the Name keyword. First go back to normal expansion. - - dotest keyword-17 "${testcvs} update -A file1" "U file1" + # Remove the tag. This will leave the tag string in the + # expansion of the Name keyword. + dotest serverpatch-6 "${testcvs} -q update -A" '' - echo '$''Name$' > file1 - dotest keyword-18 "${testcvs} ci -m modify file1" \ + # Modify and check in the first copy. + cd ../1/first-dir + echo '2' >> file1 + dotest serverpatch-7 "${testcvs} -q ci -mx file1" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.2; previous revision: 1\.1 done" - dotest keyword-19 "${testcvs} -q tag tag1" "T file1" - echo "change" >> file1 - dotest keyword-20 "${testcvs} -q ci -m mod2 file1" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3; previous revision: 1\.2 -done" - dotest keyword-21 "${testcvs} -q update -r tag1" "[UP] file1" - - # FIXME: This test fails when remote. The second expect - # string below should be removed when this is fixed. - dotest keyword-22 "cat file1" '\$'"Name: tag1 "'\$' \ -'\$'"Name: "'\$' - dotest keyword-23 "${testcvs} update -A file1" "[UP] file1" + # Now update the second copy. When using remote CVS, the + # patch will fail, forcing the file to be refetched. + cd ../../2/first-dir + dotest serverpatch-8 "${testcvs} -q update" \ +'U file1' \ +'P file1 +'"${PROG}"' [a-z]*: checksum failure after patch to ./file1; will refetch +'"${PROG}"' [a-z]*: refetching unpatchable files +U file1' cd ../.. - rm -r 1 + rm -r 1 2 rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - keywordlog) - # Test the Log keyword. - mkdir 1; cd 1 - dotest keywordlog-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest keywordlog-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - echo change >file1 - dotest keywordlog-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + log) + # Test selecting revisions with cvs log. + # See also log2 tests for more tests. + # See also branches-14.3 for logging with a branch off of a branch. + # See also multibranch-14 for logging with several branches off the + # same branchpoint. + # Tests of each option to cvs log: + # -h: admin-19a-log + # -N: log, log2, admin-19a-log + # -b, -r: log + # -d: rcs - # Note that we wanted to try "ci -r 1.3 -m add file1" and CVS - # seemed to get all confused, thinking it was adding on a branch - # or something. FIXME? Do something about this? Document it - # in BUGS or someplace? + # Check in a file with a few revisions and branches. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest log-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 'first revision' > file1 + dotest log-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' - dotest keywordlog-4 "${testcvs} -q ci -m add file1" \ + # While we're at it, check multi-line comments, input from file, + # and trailing whitespace trimming + echo 'line 1 ' >${TESTDIR}/comment.tmp + echo ' ' >>${TESTDIR}/comment.tmp + echo 'line 2 ' >>${TESTDIR}/comment.tmp + echo ' ' >>${TESTDIR}/comment.tmp + echo ' ' >>${TESTDIR}/comment.tmp + dotest log-3 "${testcvs} -q commit -F ${TESTDIR}/comment.tmp" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done" + rm -f ${TESTDIR}/comment.tmp - cd ../.. - mkdir 2; cd 2 - dotest keywordlog-4a "${testcvs} -q co first-dir" "U first-dir/file1" - cd ../1/first-dir - - echo 'xx $''Log$' > file1 - cat >${TESTDIR}/comment.tmp < file1 + dotest log-4 "${testcvs} -q ci -m2 file1" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.2; previous revision: 1\.1 done" - rm -f ${TESTDIR}/comment.tmp - dotest keywordlog-6 "${testcvs} -q tag -b br" "T file1" - dotest keywordlog-7 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx" - cd ../../2/first-dir - dotest keywordlog-8 "${testcvs} -q update" "[UP] file1" - dotest keywordlog-9 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx" - cd ../../1/first-dir + dotest log-5 "${testcvs} -q tag -b branch file1" 'T file1' - echo "change" >> file1 - dotest keywordlog-10 "${testcvs} ci -m modify file1" \ + echo 'third revision' > file1 + dotest log-6 "${testcvs} -q ci -m3 file1" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.3; previous revision: 1\.2 done" - dotest keywordlog-11 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.3 [0-9/]* [0-9:]* ${username} -xx modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -change" - cd ../../2/first-dir - dotest keywordlog-12 "${testcvs} -q update" "[UP] file1" - dotest keywordlog-13 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.3 [0-9/]* [0-9:]* ${username} -xx modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -change" + dotest log-7 "${testcvs} -q update -r branch" '[UP] file1' - cd ../../1/first-dir - dotest keywordlog-14 "${testcvs} -q update -r br" "[UP] file1" - echo br-change >>file1 - dotest keywordlog-15 "${testcvs} -q ci -m br-modify" \ + echo 'first branch revision' > file1 + dotest log-8 "${testcvs} -q ci -m1b file1" \ "Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.2\.2\.1; previous revision: 1\.2 done" - dotest keywordlog-16 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} -xx br-modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -br-change" - cd ../../2/first-dir - dotest keywordlog-17 "${testcvs} -q update -r br" "[UP] file1" - dotest keywordlog-18 "cat file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} -xx br-modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -br-change" - cd ../.. - dotest keywordlog-19 "${testcvs} -q co -p -r br first-dir/file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} -xx br-modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -br-change" - dotest keywordlog-20 "${testcvs} -q co -p first-dir/file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.3 [0-9/]* [0-9:]* ${username} -xx modify -xx -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx -change" - dotest keywordlog-21 "${testcvs} -q co -p -r 1.2 first-dir/file1" \ -"xx "'\$'"Log: file1,v "'\$'" -xx Revision 1\.2 [0-9/]* [0-9:]* ${username} -xx First log line -xx Second log line -xx" - rm -r 1 2 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest log-9 "${testcvs} -q tag tag file1" 'T file1' - toplevel) - # test the feature that cvs creates a CVS subdir also for - # the toplevel directory + echo 'second branch revision' > file1 + dotest log-10 "${testcvs} -q ci -m2b file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 +done" - # Some test, somewhere, is creating Emptydir. That test - # should, perhaps, clean up for itself, but I don't know which - # one it is. - rm -rf ${CVSROOT_DIRNAME}/CVSROOT/Emptydir + # Set up a bunch of shell variables to make the later tests + # easier to describe.= + log_header=" +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list:" + log_tags='symbolic names: + tag: 1\.2\.2\.1 + branch: 1\.2\.0\.2' + log_header2='keyword substitution: kv' + log_dash='---------------------------- +revision' + log_date="date: [0-9/]* [0-9:]*; author: ${username}; state: Exp;" + log_lines=" lines: ${PLUS}1 -1" + log_rev1="${log_dash} 1\.1 +${log_date} +line 1 - mkdir 1; cd 1 - dotest toplevel-1 "${testcvs} -q co -l ." '' - mkdir top-dir second-dir - dotest toplevel-2 "${testcvs} add top-dir second-dir" \ -"Directory ${TESTDIR}/cvsroot/top-dir added to the repository -Directory ${TESTDIR}/cvsroot/second-dir added to the repository" - cd top-dir +line 2" + log_rev2="${log_dash} 1\.2 +${log_date}${log_lines} +branches: 1\.2\.2; +2" + log_rev3="${log_dash} 1\.3 +${log_date}${log_lines} +3" + log_rev1b="${log_dash} 1\.2\.2\.1 +${log_date}${log_lines} +1b" + log_rev2b="${log_dash} 1\.2\.2\.2 +${log_date}${log_lines} +2b" + log_trailer='=============================================================================' - touch file1 - dotest toplevel-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest toplevel-4 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/top-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/top-dir/file1,v <-- file1 -initial revision: 1\.1 -done" - cd .. + # Now, finally, test the log output. - cd second-dir - touch file2 - dotest toplevel-3s "${testcvs} add file2" \ -"${PROG} [a-z]*: scheduling file .file2. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest toplevel-4s "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/second-dir/file2,v -done -Checking in file2; -${TESTDIR}/cvsroot/second-dir/file2,v <-- file2 -initial revision: 1\.1 -done" + dotest log-11 "${testcvs} log file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" - cd ../.. - rm -r 1; mkdir 1; cd 1 - dotest toplevel-5 "${testcvs} co top-dir" \ -"${PROG} [a-z]*: Updating top-dir -U top-dir/file1" + dotest log-12 "${testcvs} log -N file1" \ +"${log_header} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" - dotest toplevel-6 "${testcvs} update top-dir" \ -"${PROG} [a-z]*: Updating top-dir" - dotest toplevel-7 "${testcvs} update" \ -"${PROG} [a-z]*: Updating \. -${PROG} [a-z]*: Updating top-dir" + dotest log-13 "${testcvs} log -b file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 3 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_trailer}" - dotest toplevel-8 "${testcvs} update -d top-dir" \ -"${PROG} [a-z]*: Updating top-dir" - # There is some sentiment that - # "${PROG} [a-z]*: Updating \. - # ${PROG} [a-z]*: Updating top-dir" - # is correct but it isn't clear why that would be correct instead - # of the remote CVS behavior (which also updates CVSROOT). - # - # The DOTSTAR matches of a bunch of lines like - # "U CVSROOT/checkoutlist". Trying to match them more precisely - # seemed to cause trouble. For example CVSROOT/cvsignore will - # be present or absent depending on whether we ran the "ignore" - # test or not. - dotest toplevel-9 "${testcvs} update -d" \ -"${PROG} [a-z]*: Updating \. -${PROG} [a-z]*: Updating CVSROOT -${DOTSTAR} -${PROG} [a-z]*: Updating top-dir" + dotest log-14 "${testcvs} log -r file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" - cd .. - rm -r 1; mkdir 1; cd 1 - dotest toplevel-10 "${testcvs} co top-dir" \ -"${PROG} [a-z]*: Updating top-dir -U top-dir/file1" - # This tests more or less the same thing, in a particularly - # "real life" example. - dotest toplevel-11 "${testcvs} -q update -d second-dir" \ -"U second-dir/file2" + dotest log-15 "${testcvs} log -r1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2} +${log_trailer}" - # Now remove the CVS directory (people may do this manually, - # especially if they formed their habits with CVS - # 1.9 and older, which didn't create it. Or perhaps the working - # directory itself was created with 1.9 or older). - rm -r CVS - # Now set the permissions so we can't recreate it. - chmod -w ../1 - # Now see whether CVS has trouble because it can't create CVS. - dotest toplevel-12 "${testcvs} co top-dir" \ -"${PROG} [a-z]*: warning: cannot make directory ./CVS: Permission denied -${PROG} [a-z]*: Updating top-dir" - chmod +w ../1 + dotest log-16 "${testcvs} log -r1.2.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" - cd .. - rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/top-dir - ;; + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-17 "${testcvs} log -rbranch file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" - head) - # Testing handling of the HEAD special tag. - # There are many cases involving added and removed files - # which we don't yet try to deal with. - # TODO: We also could be paying much closer attention to - # "head of the trunk" versus "head of the default branch". - # That is what "cvs import" is doing here (but I didn't really - # fully follow through on writing the tests for that case). - mkdir imp-dir - cd imp-dir - echo 'imported contents' >file1 - # It may seem like we don't do much with file2, but do note that - # the "cvs diff" invocations do also diff file2 (and come up empty). - echo 'imported contents' >file2 - dotest head-1 "${testcvs} import -m add first-dir tag1 tag2" \ -"N first-dir/file1 -N first-dir/file2 + dotest log-18 "${testcvs} log -r1.2.2. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" -No conflicts created by this import" - cd .. - rm -r imp-dir - mkdir 1 - cd 1 - dotest head-2 "${testcvs} -q co first-dir" \ -"U first-dir/file1 -U first-dir/file2" - cd first-dir - echo 'add a line on trunk' >> file1 - dotest head-3 "${testcvs} -q ci -m modify" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 -done" - dotest head-4 "${testcvs} -q tag trunktag" "T file1 -T file2" - echo 'add a line on trunk after trunktag' >> file1 - dotest head-5 "${testcvs} -q ci -m modify" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3; previous revision: 1\.2 -done" - dotest head-6 "${testcvs} -q tag -b br1" "T file1 -T file2" - dotest head-7 "${testcvs} -q update -r br1" "" - echo 'modify on branch' >>file1 - dotest head-8 "${testcvs} -q ci -m modify" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3\.2\.1; previous revision: 1\.3 -done" - dotest head-9 "${testcvs} -q tag brtag" "T file1 -T file2" - echo 'modify on branch after brtag' >>file1 - dotest head-10 "${testcvs} -q ci -m modify" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1 -done" - # With no sticky tags, HEAD is the head of the trunk. - dotest head-trunk-setup "${testcvs} -q update -A" "[UP] file1" - dotest head-trunk-update "${testcvs} -q update -r HEAD -p file1" \ -"imported contents -add a line on trunk -add a line on trunk after trunktag" - # and diff thinks so too. Case (a) from the comment in - # cvs.texinfo (Common options). - dotest_fail head-trunk-diff "${testcvs} -q diff -c -r HEAD -r br1" \ -"Index: file1 -=================================================================== -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -retrieving revision 1\.3 -retrieving revision 1\.3\.2\.2 -diff -c -r1\.3 -r1\.3\.2\.2 -\*\*\* file1 [0-9/]* [0-9:]* 1\.3 ---- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 -\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -\*\*\* 1,3 \*\*\*\* ---- 1,5 ---- - imported contents - add a line on trunk - add a line on trunk after trunktag -${PLUS} modify on branch -${PLUS} modify on branch after brtag" - - # With a branch sticky tag, HEAD is the head of the trunk. - dotest head-br1-setup "${testcvs} -q update -r br1" "[UP] file1" - dotest head-br1-update "${testcvs} -q update -r HEAD -p file1" \ -"imported contents -add a line on trunk -add a line on trunk after trunktag" - # But diff thinks that HEAD is "br1". Case (b) from cvs.texinfo. - # Probably people are relying on it. - dotest head-br1-diff "${testcvs} -q diff -c -r HEAD -r br1" "" + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-19 "${testcvs} log -rbranch. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" - # With a nonbranch sticky tag on a branch, - # HEAD is the head of the trunk - dotest head-brtag-setup "${testcvs} -q update -r brtag" "[UP] file1" - dotest head-brtag-update "${testcvs} -q update -r HEAD -p file1" \ -"imported contents -add a line on trunk -add a line on trunk after trunktag" - # But diff thinks that HEAD is "brtag". Case (c) from - # cvs.texinfo (the "strange, maybe accidental" case). - dotest_fail head-brtag-diff "${testcvs} -q diff -c -r HEAD -r br1" \ -"Index: file1 -=================================================================== -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -retrieving revision 1\.3\.2\.1 -retrieving revision 1\.3\.2\.2 -diff -c -r1\.3\.2\.1 -r1\.3\.2\.2 -\*\*\* file1 [0-9/]* [0-9:]* 1\.3\.2\.1 ---- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 -\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -\*\*\* 2,4 \*\*\*\* ---- 2,5 ---- - add a line on trunk - add a line on trunk after trunktag - modify on branch -${PLUS} modify on branch after brtag" + dotest log-20 "${testcvs} log -r1.2: file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev3} +${log_rev2} +${log_trailer}" - # With a nonbranch sticky tag on the trunk, HEAD is the head - # of the trunk, I think. - dotest head-trunktag-setup "${testcvs} -q update -r trunktag" \ -"[UP] file1" - dotest head-trunktag-check "cat file1" "imported contents -add a line on trunk" - dotest head-trunktag-update "${testcvs} -q update -r HEAD -p file1" \ -"imported contents -add a line on trunk -add a line on trunk after trunktag" - # Like head-brtag-diff, HEAD is the sticky tag. Similarly - # questionable. - dotest_fail head-trunktag-diff \ - "${testcvs} -q diff -c -r HEAD -r br1" \ -"Index: file1 -=================================================================== -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -retrieving revision 1\.2 -retrieving revision 1\.3\.2\.2 -diff -c -r1\.2 -r1\.3\.2\.2 -\*\*\* file1 [0-9/]* [0-9:]* 1\.2 ---- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 -\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -\*\*\* 1,2 \*\*\*\* ---- 1,5 ---- - imported contents - add a line on trunk -${PLUS} add a line on trunk after trunktag -${PLUS} modify on branch -${PLUS} modify on branch after brtag" + dotest log-21 "${testcvs} log -r:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" - # Also might test what happens if we setup with update -r - # HEAD. In general, if sticky tags matter, does the - # behavior of "update -r " (without -p) depend on the - # sticky tags before or after the update? + dotest log-22 "${testcvs} log -r1.1:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" - # Note that we are testing both the case where this deletes - # a revision (file1) and the case where it does not (file2) - dotest_fail head-o0a "${testcvs} admin -o ::br1" \ -"${PROG} [a-z]*: Administrating \. -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -${PROG} [a-z]*: cannot remove revision 1\.3\.2\.1 because it has tags -${PROG} [a-z]*: cannot modify RCS file for .file1. -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v + dotest log-o0 "${testcvs} admin -o 1.2.2.2:: file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done" - dotest head-o0b "${testcvs} tag -d brtag" \ -"${PROG} [a-z]*: Untagging \. -D file1 -D file2" - dotest head-o1 "${testcvs} admin -o ::br1" \ -"${PROG} [a-z]*: Administrating \. -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -deleting revision 1\.3\.2\.1 -done -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v + dotest log-o1 "${testcvs} admin -o ::1.2.2.1 file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done" - cd ../.. - rm -r 1 + dotest log-o2 "${testcvs} admin -o 1.2.2.1:: file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +deleting revision 1\.2\.2\.2 +done" + dotest log-o3 "${testcvs} log file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 4; selected revisions: 4 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev1b} +${log_trailer}" + dotest log-o4 "${testcvs} -q update -p -r 1.2.2.1 file1" \ +"first branch revision" + cd .. + rm -r first-dir rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - tagdate) - # Test combining -r and -D. - mkdir 1; cd 1 - dotest tagdate-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest tagdate-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir + log2) + # More "cvs log" tests, for example the file description. - echo trunk-1 >file1 - dotest tagdate-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest tagdate-4 "${testcvs} -q ci -m add" \ + # Check in a file + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest log2-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 'first revision' > file1 + dotest log2-2 "${testcvs} add -m file1-is-for-testing file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use .'"${PROG}"' commit. to add this file permanently' + dotest log2-3 "${testcvs} -q commit -m 1" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done Checking in file1; ${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done" - dotest tagdate-5 "${testcvs} -q tag -b br1" "T file1" - dotest tagdate-6 "${testcvs} -q tag -b br2" "T file1" - echo trunk-2 >file1 - dotest tagdate-7 "${testcvs} -q ci -m modify-on-trunk" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.2; previous revision: 1\.1 -done" - # We are testing -r -D where br1 is a (magic) branch without - # any revisions. First the case where br2 doesn't have any - # revisions either: - dotest tagdate-8 "${testcvs} -q update -p -r br1 -D now" "trunk-1" - dotest tagdate-9 "${testcvs} -q update -r br2" "[UP] file1" - echo br2-1 >file1 - dotest tagdate-10 "${testcvs} -q ci -m modify-on-br2" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.1\.4\.1; previous revision: 1\.1 -done" - # Then the case where br2 does have revisions: - dotest tagdate-11 "${testcvs} -q update -p -r br1 -D now" "trunk-1" + # Setting the file description with add -m doesn't yet work + # client/server, so skip log2-4 for remote. + if test "x$remote" = xno; then - cd ../.. - rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + dotest log2-4 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +file1-is-for-testing +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +1 +=============================================================================" - multibranch2) - # Commit the first delta on branch A when there is an older - # branch, B, that already has a delta. A and B come from the - # same branch point. Then verify that branches A and B are - # in the right order. - mkdir 1; cd 1 - dotest multibranch2-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest multibranch2-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir + fi # end of tests skipped for remote - echo trunk-1 >file1 - echo trunk-1 >file2 - dotest multibranch2-3 "${testcvs} add file1 file2" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: scheduling file .file2. for addition -${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" - dotest multibranch2-4 "${testcvs} -q ci -m add" \ + dotest log2-5 "${testcvs} admin -t-change-description 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 -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -done -Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -initial revision: 1\.1 -done" - dotest multibranch2-5 "${testcvs} -q tag -b A" "T file1 -T file2" - dotest multibranch2-6 "${testcvs} -q tag -b B" "T file1 -T file2" - - dotest multibranch2-7 "${testcvs} -q update -r B" '' - echo branch-B >file1 - echo branch-B >file2 - dotest multibranch2-8 "${testcvs} -q ci -m modify-on-B" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.1\.4\.1; previous revision: 1\.1 -done -Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -new revision: 1\.1\.4\.1; previous revision: 1\.1 done" + dotest log2-6 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +change-description +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +1 +=============================================================================" - dotest multibranch2-9 "${testcvs} -q update -r A" '[UP] file1 -[UP] file2' - echo branch-A >file1 - # When using cvs-1.9.20, this commit gets a failed assertion in rcs.c. - dotest multibranch2-10 "${testcvs} -q ci -m modify-on-A" \ -"Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -new revision: 1\.1\.2\.1; previous revision: 1\.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 multibranch2-11 "${testcvs} -q log file1" \ -" + dotest log2-8 "${testcvs} log -N file1" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 head: 1\.1 branch: locks: strict access list: -symbolic names: - B: 1\.1\.0\.4 - A: 1\.1\.0\.2 keyword substitution: kv -total revisions: 3; selected revisions: 3 +total revisions: 1; selected revisions: 1 description: +longer description +with two lines ---------------------------- revision 1\.1 -date: [0-9/]* [0-9:]*; author: $username; state: Exp; -branches: 1\.1\.2; 1\.1\.4; -add ----------------------------- -revision 1\.1\.4\.1 -date: [0-9/]* [0-9:]*; author: $username; state: Exp; lines: ${PLUS}1 -1 -modify-on-B ----------------------------- -revision 1\.1\.2\.1 -date: [0-9/]* [0-9:]*; author: $username; state: Exp; lines: ${PLUS}1 -1 -modify-on-A +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +1 =============================================================================" - # This one is more concise. - dotest multibranch2-12 "${testcvs} -q log -r1.1 file1" \ -" + # 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 + + 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" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 head: 1\.1 branch: locks: strict access list: -symbolic names: - B: 1\.1\.0\.4 - A: 1\.1\.0\.2 keyword substitution: kv -total revisions: 3; selected revisions: 1 +total revisions: 1; selected revisions: 1 description: +change from stdin ---------------------------- revision 1\.1 -date: [0-9/]* [0-9:]*; author: $username; state: Exp; -branches: 1\.1\.2; 1\.1\.4; -add +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +1 =============================================================================" - # OK, try very much the same thing except we run update -j to - # bring the changes from B to A. Probably tests many of the - # same code paths but might as well keep it separate, I guess. + fi # end of tests skipped for remote - dotest multibranch2-13 "${testcvs} -q update -r B" "[UP] file1 -[UP] file2" - dotest multibranch2-14 "${testcvs} -q update -r A -j B file2" \ -"[UP] file2 -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -retrieving revision 1.1 -retrieving revision 1.1.4.1 -Merging differences between 1.1 and 1.1.4.1 into file2" - dotest multibranch2-15 "${testcvs} -q ci -m commit-on-A file2" \ -"Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -new revision: 1\.1\.2\.1; previous revision: 1\.1 -done" - cd ../.. - rm -r 1 + cd .. + rm ${TESTDIR}/descrip + rm -r first-dir rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; - - admin) - # More "cvs admin" tests. - # The basicb-21 test tests rejecting an illegal option. - # For -l and -u, see "reserved" and "keyword" tests. - # "binfiles" test has a test of "cvs admin -k". - # "log2" test has tests of -t and -q options to cvs admin. - # "rcs" tests -b option also. - # For -o, see: - # admin-22-o1 through admin-23 (various cases not involving ::) - # binfiles2-o* (:rev, rev on trunk; rev:, deleting entire branch) - # basica-o1 through basica-o3 (basic :: usage) - # head-o1 (::branch, where this deletes a revision or is noop) - # branches-o1 (::branch, similar, with different branch topology) - # log-o1 (1.3.2.1::) - # binfiles-o1 (1.3:: and ::1.3) - # Also could be testing: - # 1.3.2.6::1.3.2.8 - # 1.3.2.6::1.3.2 - # 1.3.2.1::1.3.2.6 - # 1.3::1.3.2.6 (error? or synonym for ::1.3.2.6?) + ;; + ann) + # Tests of "cvs annotate". See also basica-10. mkdir 1; cd 1 - dotest admin-1 "${testcvs} -q co -l ." '' + dotest ann-1 "${testcvs} -q co -l ." '' mkdir first-dir - dotest admin-2 "${testcvs} add first-dir" \ + dotest ann-2 "${testcvs} add first-dir" \ "Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - - dotest_fail admin-3 "${testcvs} -q admin -i file1" \ -"${PROG} admin: the -i option to admin is not supported -${PROG} admin: run add or import to create an RCS file -${PROG} \[admin aborted\]: specify ${PROG} -H admin for usage information" - dotest_fail admin-4 "${testcvs} -q log file1" \ -"${PROG} [a-z]*: nothing known about file1" - - # Set up some files, file2 a plain one and file1 with a revision - # on a branch. - touch file1 file2 - dotest admin-5 "${testcvs} add file1 file2" \ + cd first-dir + cat >file1 <> file1 - dotest admin-9 "${testcvs} -q ci -m modify-on-branch" \ + cat >file1 <file1 <file1 <>${LOGFILE}; then + pass crerepos-5 + else + fail crerepos-5 + fi + rm -r CVS + cd .. + # The directory tmp should be empty + dotest crerepos-6 "rmdir tmp" '' + CREREPOS_ROOT=${TESTDIR}/crerepos -1\.1\.2\.1 -log -@modify-on-branch -@ -text -@a0 1 -add a line on the branch -@" - dotest admin-14 "${testcvs} -q admin -aauth3 -aauth2,foo \ --soneone:1.1 -m1.1:changed-log-message -ntagone: file2" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -done" - dotest admin-15 "${testcvs} -q log file2" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -Working file: file2 -head: 1\.1 -branch: -locks: strict -access list: - auth3 - auth2 - foo -symbolic names: - tagone: 1\.1 - br: 1\.1\.0\.2 -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: oneone; -changed-log-message -=============================================================================" + else + # For remote, just create the repository. We don't yet do + # the various other tests above for remote but that should be + # changed. + mkdir crerepos + mkdir crerepos/CVSROOT - dotest admin-16 "${testcvs} -q admin \ --A${CVSROOT_DIRNAME}/first-dir/file2,v -b -L -Nbr:1.1 file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done" - dotest admin-17 "${testcvs} -q log file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: - foo - bar - baz - auth3 - auth2 -symbolic names: - br: 1\.1 -keyword substitution: kv -total revisions: 2; selected revisions: 2 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -branches: 1\.1\.2; -add ----------------------------- -revision 1\.1\.2\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 -modify-on-branch -=============================================================================" + CREREPOS_ROOT=:ext:`hostname`:${TESTDIR}/crerepos - dotest_fail admin-18 "${testcvs} -q admin -nbr:1.1.2 file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/file1,v: symbolic name br already bound to 1\.1 -${PROG} [a-z]*: cannot modify RCS file for .file1." - dotest admin-19 "${testcvs} -q admin -ebaz -ebar,auth3 -nbr file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done" - dotest admin-20 "${testcvs} -q log file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: - foo - auth2 -symbolic names: -keyword substitution: kv -total revisions: 2; selected revisions: 2 -description: ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -branches: 1\.1\.2; -add ----------------------------- -revision 1.1.2.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 -modify-on-branch -=============================================================================" + fi - # OK, this is starting to get ridiculous, in terms of - # testing a feature (access lists) which doesn't do anything - # useful, but what about nonexistent files and - # relative pathnames in admin -A? - dotest_fail admin-19a-nonexist \ -"${testcvs} -q admin -A${TESTDIR}/foo/bar file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -${PROG} [a-z]*: Couldn't open rcs file .${TESTDIR}/foo/bar.: No such file or directory -${PROG} \[[a-z]* aborted\]: cannot continue" + if test "x$remote" = "xno"; then + # Test that CVS rejects a relative path in CVSROOT. + mkdir 1; cd 1 + dotest_fail crerepos-6a "${testcvs} -q -d ../crerepos get ." \ +"${PROG} \[[a-z]* aborted\]: CVSROOT ../crerepos must be an absolute pathname" + cd .. + rm -r 1 - # In the remote case, we are cd'd off into the temp directory - # and so these tests give "No such file or directory" errors. - if test "x$remote" = xno; then + mkdir 1; cd 1 + dotest_fail crerepos-6b "${testcvs} -d crerepos init" \ +"${PROG} \[[a-z]* aborted\]: CVSROOT crerepos must be an absolute pathname" + cd .. + rm -r 1 + else # remote + # Test that CVS rejects a relative path in CVSROOT. + mkdir 1; cd 1 + dotest_fail crerepos-6a \ +"${testcvs} -q -d :ext:`hostname`:../crerepos get ." \ +"Root ../crerepos must be an absolute pathname" + cd .. + rm -r 1 - dotest admin-19a-admin "${testcvs} -q admin -A../../cvsroot/first-dir/file2,v file1" \ + mkdir 1; cd 1 + dotest_fail crerepos-6b \ +"${testcvs} -d :ext:`hostname`:crerepos init" \ +"Root crerepos must be an absolute pathname" + cd .. + rm -r 1 + fi # end of tests to be skipped for remote + + # CVS better not create a history file--if the administrator + # doesn't need it and wants to save on disk space, they just + # delete it. + dotest_fail crerepos-7 \ +"test -f ${TESTDIR}/crerepos/CVSROOT/history" '' + + # Now test mixing repositories. This kind of thing tends to + # happen accidentally when people work with several repositories. + mkdir 1; cd 1 + dotest crerepos-8 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest crerepos-9 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch file1 + dotest crerepos-10 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest crerepos-11 "${testcvs} -q ci -m add-it" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 done" - dotest admin-19a-log "${testcvs} -q log -h -N file1" " + cd ../.. + rm -r 1 + + mkdir 1; cd 1 + dotest crerepos-12 "${testcvs} -d ${CREREPOS_ROOT} -q co -l ." '' + mkdir crerepos-dir + dotest crerepos-13 "${testcvs} add crerepos-dir" \ +"Directory ${TESTDIR}/crerepos/crerepos-dir added to the repository" + cd crerepos-dir + touch cfile + dotest crerepos-14 "${testcvs} add cfile" \ +"${PROG} [a-z]*: scheduling file .cfile. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest crerepos-15 "${testcvs} -q ci -m add-it" \ +"RCS file: ${TESTDIR}/crerepos/crerepos-dir/cfile,v +done +Checking in cfile; +${TESTDIR}/crerepos/crerepos-dir/cfile,v <-- cfile +initial revision: 1\.1 +done" + cd ../.. + rm -r 1 + + mkdir 1; cd 1 + dotest crerepos-16 "${testcvs} co first-dir" \ +"${PROG} [a-z]*: Updating first-dir +U first-dir/file1" + dotest crerepos-17 "${testcvs} -d ${CREREPOS_ROOT} co crerepos-dir" \ +"${PROG} [a-z]*: Updating crerepos-dir +U crerepos-dir/cfile" + + if test x`cat CVS/Repository` = x.; then + # RELATIVE_REPOS + # Fatal error so that we don't go traipsing through the + # directories which happen to have the same names from the + # wrong repository. + dotest_fail crerepos-18 "${testcvs} -q update" \ +"${PROG} \[[a-z]* aborted\]: cannot open directory ${TESTDIR}/cvsroot/crerepos-dir: .*" '' + else + if test "$remote" = no; then + # The lack of an error doesn't mean CVS is really + # working (things are getting logged to the wrong + # history file and such). + dotest crerepos-18 "${testcvs} -q update" '' + else + # Fatal error so that we don't go traipsing through the + # directories which happen to have the same names from the + # wrong repository. + dotest_fail crerepos-18 "${testcvs} -q update" \ +"protocol error: directory .${TESTDIR}/crerepos/crerepos-dir. not within root .${TESTDIR}/cvsroot." + fi + fi + + cd .. + + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir ${TESTDIR}/crerepos + ;; + + rcs) + # Test ability to import an RCS file. Note that this format + # is fixed--files written by RCS5, and other software which + # implements this format, will be out there "forever" and + # CVS must always be able to import such files. + + # See tests admin-13, admin-25 and rcs-8a for exporting RCS files. + + mkdir ${CVSROOT_DIRNAME}/first-dir + + # Currently the way to import an RCS file is to copy it + # directly into the repository. + # + # This file was written by RCS 5.7, and then the dates were + # hacked so that we test year 2000 stuff. Note also that + # "author" names are just strings, as far as importing + # RCS files is concerned--they need not correspond to user + # IDs on any particular system. + # + # I also tried writing a file with the RCS supplied with + # HPUX A.09.05. According to "man rcsintro" this is + # "Revision Number: 3.0; Release Date: 83/05/11". There + # were a few minor differences like whitespace but at least + # in simple cases like this everything else seemed the same + # as the file written by RCS 5.7 (so I won't try to make it + # a separate test case). + + cat <${CVSROOT_DIRNAME}/first-dir/file1,v +head 1.3; +access; +symbols; +locks; strict; +comment @# @; + + +1.3 +date 2000.11.24.15.58.37; author kingdon; state Exp; +branches; +next 1.2; + +1.2 +date 96.11.24.15.57.41; author kingdon; state Exp; +branches; +next 1.1; + +1.1 +date 96.11.24.15.56.05; author kingdon; state Exp; +branches; +next ; + + +desc +@file1 is for testing CVS +@ + + +1.3 +log +@delete second line; modify twelfth line +@ +text +@This is the first line +This is the third line +This is the fourth line +This is the fifth line +This is the sixth line +This is the seventh line +This is the eighth line +This is the ninth line +This is the tenth line +This is the eleventh line +This is the twelfth line (and what a line it is) +This is the thirteenth line +@ + + +1.2 +log +@add more lines +@ +text +@a1 1 +This is the second line +d11 1 +a11 1 +This is the twelfth line +@ + + +1.1 +log +@add file1 +@ +text +@d2 12 +@ +EOF + dotest rcs-1 "${testcvs} -q co first-dir" 'U first-dir/file1' + cd first-dir + dotest rcs-2 "${testcvs} -q log" " RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v Working file: file1 -head: 1\.1 +head: 1\.3 branch: locks: strict access list: - foo - auth2 - auth3 +symbolic names: keyword substitution: kv -total revisions: 2 +total revisions: 3; selected revisions: 3 +description: +file1 is for testing CVS +---------------------------- +revision 1\.3 +date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 +delete second line; modify twelfth line +---------------------------- +revision 1\.2 +date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 +add more lines +---------------------------- +revision 1\.1 +date: 1996/11/24 15:56:05; author: kingdon; state: Exp; +add file1 =============================================================================" - # Put the access list back, to avoid special cases later. - dotest admin-19a-fix "${testcvs} -q admin -eauth3 file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done" - fi # end of tests skipped for remote - # Add another revision to file2, so we can delete one. - echo 'add a line' >> file2 - dotest admin-21 "${testcvs} -q ci -m modify file2" \ -"Checking in file2; -${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 -new revision: 1\.2; previous revision: 1\.1 -done" - dotest admin-22 "${testcvs} -q admin -o1.1 file2" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -deleting revision 1\.1 -done" - # Test admin -o. More variants that we could be testing: - # * REV: [on branch] - # * REV1:REV2 [deleting whole branch] - # * high branch numbers (e.g. 1.2.2.3.2.3) - # ... and probably others. See RCS_delete_revs for ideas. + # Note that the dates here are chosen so that (a) we test + # at least one date after 2000, (b) we will notice if the + # month and day are getting mixed up with each other. + # TODO: also test that year isn't getting mixed up with month + # or day, for example 01-02-03. - echo first rev > aaa - dotest admin-22-o1 "${testcvs} add aaa" \ -"${PROG} [a-z]*: scheduling file .aaa. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest admin-22-o2 "${testcvs} -q ci -m first aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -done -Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -initial revision: 1\.1 -done" - echo second rev >> aaa - dotest admin-22-o3 "${testcvs} -q ci -m second aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.2; previous revision: 1\.1 -done" - echo third rev >> aaa - dotest admin-22-o4 "${testcvs} -q ci -m third aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.3; previous revision: 1\.2 -done" - echo fourth rev >> aaa - dotest admin-22-o5 "${testcvs} -q ci -m fourth aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.4; previous revision: 1\.3 -done" - echo fifth rev >>aaa - dotest admin-22-o6 "${testcvs} -q ci -m fifth aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.5; previous revision: 1\.4 -done" - echo sixth rev >> aaa - dotest admin-22-o7 "${testcvs} -q ci -m sixth aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.6; previous revision: 1\.5 -done" - dotest admin-22-o8 "${testcvs} admin -l1.6 aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -1\.6 locked -done" - dotest admin-22-o9 "${testcvs} log -r1.6 aaa" " -RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -Working file: aaa -head: 1\.6 -branch: -locks: strict - ${username}: 1\.6 -access list: -symbolic names: -keyword substitution: kv -total revisions: 6; selected revisions: 1 -description: ----------------------------- -revision 1\.6 locked by: ${username}; -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -sixth -=============================================================================" - dotest_fail admin-22-o10 "${testcvs} admin -o1.5: aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/aaa,v: can't remove locked revision 1\.6 -${PROG} [a-z]*: cannot modify RCS file for .aaa." - dotest admin-22-o11 "${testcvs} admin -u aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -1\.6 unlocked -done" - dotest admin-22-o12 "${testcvs} admin -o1.5: aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -deleting revision 1\.6 -deleting revision 1\.5 -done" - dotest admin-22-o13 "${testcvs} log aaa" " -RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -Working file: aaa -head: 1\.4 + # ISO8601 format. There are many, many, other variations + # specified by ISO8601 which we should be testing too. + dotest rcs-3 "${testcvs} -q log -d 1996-12-11<" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 branch: locks: strict access list: symbolic names: keyword substitution: kv -total revisions: 4; selected revisions: 4 +total revisions: 3; selected revisions: 1 description: ----------------------------- -revision 1\.4 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -fourth +file1 is for testing CVS ---------------------------- revision 1\.3 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -third ----------------------------- -revision 1\.2 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -second ----------------------------- -revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -first +date: 2000/11/24 15:58:37; author: kingdon; state: Exp; lines: ${PLUS}1 -2 +delete second line; modify twelfth line =============================================================================" - dotest admin-22-o14 "${testcvs} tag -b -r1.3 br1 aaa" "T aaa" - dotest admin-22-o15 "${testcvs} update -rbr1 aaa" "U aaa" - echo new branch rev >> aaa - dotest admin-22-o16 "${testcvs} ci -m new-branch aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.3\.2\.1; previous revision: 1\.3 -done" - dotest_fail admin-22-o17 "${testcvs} admin -o1.2:1.4 aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -deleting revision 1\.4 -${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/aaa,v: can't remove branch point 1\.3 -${PROG} [a-z]*: cannot modify RCS file for .aaa." - dotest admin-22-o18 "${testcvs} update -p -r1.4 aaa" \ -"=================================================================== -Checking out aaa -RCS: ${TESTDIR}/cvsroot/first-dir/aaa,v -VERS: 1\.4 -\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -first rev -second rev -third rev -fourth rev" - echo second branch rev >> aaa - dotest admin-22-o19 "${testcvs} ci -m branch-two aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1 -done" - echo third branch rev >> aaa - dotest admin-22-o20 "${testcvs} ci -m branch-three aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.3\.2\.3; previous revision: 1\.3\.2\.2 -done" - echo fourth branch rev >> aaa - dotest admin-22-o21 "${testcvs} ci -m branch-four aaa" \ -"Checking in aaa; -${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa -new revision: 1\.3\.2\.4; previous revision: 1\.3\.2\.3 -done" - dotest admin-22-o22 "${testcvs} admin -o:1.3.2.3 aaa" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -deleting revision 1\.3\.2\.1 -deleting revision 1\.3\.2\.2 -deleting revision 1\.3\.2\.3 -done" - dotest admin-22-o23 "${testcvs} log aaa" " -RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v -Working file: aaa -head: 1\.4 + # RFC822 format (as amended by RFC1123). + if ${testcvs} -q log -d '<3 Apr 2000 00:00' >${TESTDIR}/rcs4.tmp + then + dotest rcs-4 "cat ${TESTDIR}/rcs4.tmp" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 branch: locks: strict access list: symbolic names: - br1: 1\.3\.0\.2 keyword substitution: kv -total revisions: 5; selected revisions: 5 +total revisions: 3; selected revisions: 2 description: ----------------------------- -revision 1\.4 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -fourth ----------------------------- -revision 1\.3 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -branches: 1\.3\.2; -third +file1 is for testing CVS ---------------------------- revision 1\.2 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 -second +date: 1996/11/24 15:57:41; author: kingdon; state: Exp; lines: ${PLUS}12 -0 +add more lines ---------------------------- revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -first ----------------------------- -revision 1\.3\.2\.4 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}4 -0 -branch-four +date: 1996/11/24 15:56:05; author: kingdon; state: Exp; +add file1 =============================================================================" + else + fail rcs-4 + fi - dotest admin-22-o24 "${testcvs} -q update -p -r 1.3.2.4 aaa" \ -"first rev -second rev -third rev -new branch rev -second branch rev -third branch rev -fourth branch rev" + # OK, here is another one. This one was written by hand based on + # doc/RCSFILES and friends. + cat <${CVSROOT_DIRNAME}/first-dir/file2,v +head 1.5 ; + branch 1.2.6; +access ; +symbols branch:1.2.6; +locks; +testofanewphrase @without newphrase we'd have trouble extending @@ all@ ; +1.5 date 71.01.01.01.00.00; author joe; state bogus; branches; next 1.4; +1.4 date 71.01.01.00.00.05; author joe; state bogus; branches; next 1.3; +1.3 date 70.12.31.15.00.05; author joe; state bogus; branches; next 1.2; +1.2 date 70.12.31.12.15.05; author me; state bogus; branches 1.2.6.1; next 1.1; +1.1 date 70.12.31.11.00.05; author joe; state bogus; branches; next; newph; +1.2.6.1 date 71.01.01.08.00.05; author joe; state Exp; branches; next; +desc @@ +1.5 log @@ newphrase1; newphrase2 42; text @head revision@ +1.4 log @@ text @d1 1 +a1 1 +new year revision@ +1.3 log @@ text @d1 1 +a1 1 +old year revision@ +1.2 log @@ text @d1 1 +a1 1 +mid revision@ 1.1 - # The bit here about how there is a "tagone" tag pointing to - # a nonexistent revision is documented by rcs. I dunno, I - # wonder whether the "cvs admin -o" should give a warning in - # this case. - dotest admin-23 "${testcvs} -q log file2" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v -Working file: file2 -head: 1\.2 -branch: -locks: strict -access list: - auth3 - auth2 - foo -symbolic names: - tagone: 1\.1 - br: 1\.1\.0\.2 -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: ----------------------------- -revision 1\.2 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -modify -=============================================================================" +log @@ text @d1 1 +a1 1 +start revision@ +1.2.6.1 log @@ text @d1 1 +a1 1 +branch revision@ +EOF + # First test the default branch. + dotest rcs-5 "${testcvs} -q update file2" "U file2" + dotest rcs-6 "cat file2" "branch revision" - dotest admin-25 "cat ${CVSROOT_DIRNAME}/first-dir/file1,v" \ -"head 1\.1; -access - foo - auth2; -symbols; -locks; strict; -comment @xx@; + # Check in a revision on the branch to force CVS to + # interpret every revision in the file. + dotest rcs-6a "${testcvs} -q update -r branch file2" "" + echo "next branch revision" > file2 + dotest rcs-6b "${testcvs} -q ci -m mod file2" \ +"Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.2\.6\.2; previous revision: 1\.2\.6\.1 +done" + + # Now get rid of the default branch, it will get in the way. + dotest rcs-7 "${testcvs} admin -b file2" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done" + # But we do want to make sure that "cvs admin" leaves the newphrases + # in the file. + # The extra whitespace regexps are for the RCS library, which does + # not preserve whitespace in the dogmatic manner of RCS 5.7. -twp + dotest rcs-8 \ +"grep testofanewphrase ${CVSROOT_DIRNAME}/first-dir/file2,v" \ +"testofanewphrase[ ][ ]*@without newphrase we'd have trouble extending @@ all@[ ]*;" + # The easiest way to test for newphrases in deltas and deltatexts + # is to just look at the whole file, I guess. + dotest rcs-8a "cat ${CVSROOT_DIRNAME}/first-dir/file2,v" \ +"head 1\.5; +access; +symbols + branch:1.2.6; +locks; +testofanewphrase @without newphrase we'd have trouble extending @@ all@; -1\.1 -date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state Exp; +1\.5 +date 71\.01\.01\.01\.00\.00; author joe; state bogus; +branches; +next 1\.4; + +1\.4 +date 71\.01\.01\.00\.00\.05; author joe; state bogus; +branches; +next 1\.3; + +1\.3 +date 70\.12\.31\.15\.00\.05; author joe; state bogus; +branches; +next 1\.2; + +1\.2 +date 70\.12\.31\.12\.15\.05; author me; state bogus; branches - 1\.1\.2\.1; + 1\.2\.6\.1; +next 1\.1; + +1\.1 +date 70\.12\.31\.11\.00\.05; author joe; state bogus; +branches; next ; +newph ; -1\.1\.2\.1 -date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state foo; +1\.2\.6\.1 +date 71\.01\.01\.08\.00\.05; author joe; state Exp; +branches; +next 1\.2\.6\.2; + +1\.2\.6\.2 +date [0-9.]*; author ${username}; state Exp; branches; next ; @@ -10989,1596 +11096,2077 @@ desc @@ +1\.5 +log +@@ +newphrase1 ; +newphrase2 42; +text +@head revision@ + + +1\.4 +log +@@ +text +@d1 1 +a1 1 +new year revision@ + + +1\.3 +log +@@ +text +@d1 1 +a1 1 +old year revision@ + + +1\.2 +log +@@ +text +@d1 1 +a1 1 +mid revision@ + + 1\.1 log -@add -@ +@@ text +@d1 1 +a1 1 +start revision@ + + +1\.2\.6\.1 +log @@ +text +@d1 1 +a1 1 +branch revision@ -1\.1\.2\.1 +1\.2\.6\.2 log -@modify-on-branch +@mod @ text -@a0 1 -add a line on the branch +@d1 1 +a1 1 +next branch revision @" - cd ../.. - rm -r 1 - rm -rf ${CVSROOT_DIRNAME}/first-dir - ;; + # For remote, the "update -p -D" usage seems not to work. + # I'm not sure what is going on. + if test "x$remote" = "xno"; then - reserved) - # Tests of reserved checkouts. Eventually this will test - # rcslock.pl (or equivalent) and all kinds of stuff. Right - # now it just does some very basic checks on cvs admin -u - # and cvs admin -l. - # Also should test locking on a branch (and making sure that - # locks from one branch don't get mixed up with those from - # another. Both the case where one of the branches is the - # main branch, and in which neither one is). - # See also test keyword, which tests that keywords and -kkvl - # do the right thing in the presence of locks. + 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 - # The usual setup, directory first-dir containing file file1. - mkdir 1; cd 1 - dotest reserved-1 "${testcvs} -q co -l ." '' - mkdir first-dir - dotest reserved-2 "${testcvs} add first-dir" \ -"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" - cd first-dir - touch file1 - dotest reserved-3 "${testcvs} add file1" \ -"${PROG} [a-z]*: scheduling file .file1. for addition -${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest reserved-4 "${testcvs} -q ci -m add" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -done -Checking in file1; -${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1\.1 -done" + 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 reserved-5 "${testcvs} -q admin -l file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -1\.1 locked -done" - dotest reserved-6 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 + 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 + + # 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 + + # 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 + + fi # end of tests skipped for remote + + # OK, now make sure cvs log doesn't have any trouble with the + # newphrases and such. + dotest rcs-14 "${testcvs} -q log file2" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Working file: file2 +head: 1\.5 branch: -locks: strict - ${username}: 1\.1 +locks: access list: +symbolic names: + branch: 1\.2\.6 keyword substitution: kv -total revisions: 1; selected revisions: 1 +total revisions: 7; selected revisions: 7 description: ---------------------------- -revision 1\.1 locked by: ${username}; -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -add -=============================================================================" - - # Note that this just tests the owner of the lock giving - # it up. It doesn't test breaking a lock. - dotest reserved-7 "${testcvs} -q admin -u file1" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -1\.1 unlocked -done" - - dotest reserved-8 "${testcvs} log -N file1" " -RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v -Working file: file1 -head: 1\.1 -branch: -locks: strict -access list: -keyword substitution: kv -total revisions: 1; selected revisions: 1 -description: +revision 1\.5 +date: 1971/01/01 01:00:00; author: joe; state: bogus; lines: ${PLUS}1 -1 +\*\*\* empty log message \*\*\* +---------------------------- +revision 1\.4 +date: 1971/01/01 00:00:05; author: joe; state: bogus; lines: ${PLUS}1 -1 +\*\*\* empty log message \*\*\* +---------------------------- +revision 1\.3 +date: 1970/12/31 15:00:05; author: joe; state: bogus; lines: ${PLUS}1 -1 +\*\*\* empty log message \*\*\* +---------------------------- +revision 1\.2 +date: 1970/12/31 12:15:05; author: me; state: bogus; lines: ${PLUS}1 -1 +branches: 1\.2\.6; +\*\*\* empty log message \*\*\* ---------------------------- revision 1\.1 -date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; -add +date: 1970/12/31 11:00:05; author: joe; state: bogus; +\*\*\* empty log message \*\*\* +---------------------------- +revision 1\.2\.6\.2 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -1 +mod +---------------------------- +revision 1\.2\.6\.1 +date: 1971/01/01 08:00:05; author: joe; state: Exp; lines: ${PLUS}1 -1 +\*\*\* empty log message \*\*\* =============================================================================" + cd .. - # rcslock.pl tests. Of course, the point isn't to test - # rcslock.pl from the distribution but equivalent - # functionality (for example, many sites may have an old - # rcslock.pl). The functionality of this hook falls - # short of the real rcslock.pl though. - # Note that we can use rlog or look at the RCS file directly, - # but we can't use "cvs log" because "cvs commit" has a lock. + rm -r first-dir ${TESTDIR}/rcs4.tmp + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - cat >${TESTDIR}/lockme < a-lock - dotest reserved-9 "${testcvs} add a-lock" \ -"${PROG} [a-z]*: scheduling file .a-lock. for addition + # Test ability to operate on big files. Intention is to + # test various realloc'ing code in RCS_deltas, rcsgetkey, + # etc. "big" is currently defined to be 1000 lines (64000 + # bytes), which in terms of files that users will use is not + # large, merely average, but my reasoning is that this + # should be big enough to make sure realloc'ing is going on + # and that raising it a lot would start to stress resources + # on machines which run the tests, without any significant + # benefit. + + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest big-1 "${testcvs} -q co first-dir" '' + cd first-dir + for i in 0 1 2 3 4 5 6 7 8 9; do + for j in 0 1 2 3 4 5 6 7 8 9; do + for k in 0 1 2 3 4 5 6 7 8 9; do + echo \ +"This is line ($i,$j,$k) which goes into the file file1 for testing" >>file1 + done + done + done + dotest big-2 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition ${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" - dotest reserved-10 "${testcvs} -q ci -m new a-lock" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/a-lock,v + dotest big-3 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v done -Checking in a-lock; -${TESTDIR}/cvsroot/first-dir/a-lock,v <-- a-lock +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 initial revision: 1\.1 done" - # FIXME: the contents of CVSROOT fluctuate a lot - # here. Maybe the expect pattern should just - # confirm that commitinfo is one of the files checked out, - # but for now we just check that CVS exited with success. cd .. - if ${testcvs} -q co CVSROOT >>${LOGFILE} ; then - pass reserved-11 - else - fail reserved-11 - fi - cd CVSROOT - echo "DEFAULT ${TESTDIR}/lockme" >>commitinfo - dotest reserved-12 "${testcvs} -q ci -m rcslock commitinfo" \ -"Checking in commitinfo; -${TESTDIR}/cvsroot/CVSROOT/commitinfo,v <-- commitinfo -new revision: 1\.2; previous revision: 1\.1 -done -${PROG} [a-z]*: Rebuilding administrative file database" - cd ..; cd first-dir - - # Simulate (approximately) what a-lock would look like - # if someone else had locked revision 1.1. - sed -e 's/locks; strict;/locks fred:1.1; strict;/' ${TESTDIR}/cvsroot/first-dir/a-lock,v > a-lock,v - chmod 644 ${TESTDIR}/cvsroot/first-dir/a-lock,v - dotest reserved-13 "mv a-lock,v ${TESTDIR}/cvsroot/first-dir/a-lock,v" - chmod 444 ${TESTDIR}/cvsroot/first-dir/a-lock,v - echo more stuff >> a-lock - dotest_fail reserved-13b "${testcvs} ci -m '' a-lock" \ -"fred has file a-lock locked for version 1\.1 -${PROG} [a-z]*: Pre-commit check failed -${PROG} \[[a-z]* aborted\]: correct above errors first!" - - dotest reserved-14 "${testcvs} admin -u1.1 a-lock" \ -"RCS file: ${TESTDIR}/cvsroot/first-dir/a-lock,v -1\.1 unlocked -done" - dotest reserved-15 "${testcvs} -q ci -m success a-lock" \ -"Checking in a-lock; -${TESTDIR}/cvsroot/first-dir/a-lock,v <-- a-lock + mkdir 2 + cd 2 + dotest big-4 "${testcvs} -q get first-dir" "U first-dir/file1" + cd ../first-dir + echo "add a line to the end" >>file1 + dotest big-5 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 new revision: 1\.2; previous revision: 1\.1 done" + cd ../2/first-dir + # The idea here is particularly to test the Rcs-diff response + # and the reallocing thereof, for remote. + dotest big-6 "${testcvs} -q update" "[UP] file1" + cd ../.. - # undo commitinfo changes - cd ../CVSROOT - echo '# vanilla commitinfo' >commitinfo - dotest reserved-16 "${testcvs} -q ci -m back commitinfo" \ -"Checking in commitinfo; -${TESTDIR}/cvsroot/CVSROOT/commitinfo,v <-- commitinfo -new revision: 1\.3; previous revision: 1\.2 -done -${PROG} [a-z]*: Rebuilding administrative file database" - cd ..; rm -r CVSROOT; cd first-dir + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi - cd ../.. - rm -r 1 - rm ${TESTDIR}/lockme + rm -r first-dir 2 rm -rf ${CVSROOT_DIRNAME}/first-dir ;; - cvsadm) - # These test check the content of CVS' administrative - # files as they are checked out in various configurations. - # (As a side note, I'm not using the "-q" flag in any of - # this code, which should provide some extra checking for - # those messages which don't seem to be checked thoroughly - # anywhere else.) To do a thorough test, we need to make - # a bunch of modules in various configurations. - # - # <1mod> is a directory at the top level of cvsroot - # ``foo bar'' - # <2mod> is a directory at the second level of cvsroot - # ``foo bar/baz'' - # <1d1mod> is a directory at the top level which is - # checked out into another directory - # ``foo -d bar baz'' - # <1d2mod> is a directory at the second level which is - # checked out into another directory - # ``foo -d bar baz/quux'' - # <2d1mod> is a directory at the top level which is - # checked out into a directory that is two deep - # ``foo -d bar/baz quux'' - # <2d2mod> is a directory at the second level which is - # checked out into a directory that is two deep - # ``foo -d bar/baz quux'' - # - # The tests do each of these types separately and in twos. - # We also repeat each test -d flag for 1-deep and 2-deep - # directories. - # - # Each test should check the output for the Repository - # file, since that is the one which varies depending on - # the directory and how it was checked out. - # - # Yes, this is verbose, but at least it's very thorough. + modes) + # Test repository permissions (CVSUMASK and so on). + # Although the tests in this section "cheat" by testing + # repository permissions, which are sort of not a user-visible + # sort of thing, the modes do have user-visible consequences, + # such as whether a second user can check out the files. But + # it would be awkward to test the consequences, so we don't. - # convenience variables - REP=${CVSROOT} + # Solaris /bin/sh doesn't support export -n. I'm not sure + # what we can do about this, other than hope that whoever + # is running the tests doesn't have CVSUMASK set. + #export -n CVSUMASK # if unset, defaults to 002 - # First, check out the modules file and edit it. + umask 077 mkdir 1; cd 1 - dotest cvsadm-1 "${testcvs} co CVSROOT/modules" \ -"U CVSROOT/modules" + dotest modes-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest modes-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch aa + dotest modes-3 "${testcvs} add aa" \ +"${PROG} [a-z]*: scheduling file .aa. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest modes-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v +done +Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +initial revision: 1\.1 +done" + dotest modes-5 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r--r-- .*" - # Try to determine whether RELATIVE_REPOS is defined - # so that we can make the following a lot less - # verbose. + # Test for whether we can set the execute bit. + chmod +x aa + echo change it >>aa + dotest modes-6 "${testcvs} -q ci -m set-execute-bit" \ +"Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +new revision: 1\.2; previous revision: 1\.1 +done" + # If CVS let us update the execute bit, it would be set here. + # But it doesn't, and as far as I know that is longstanding + # CVS behavior. + dotest modes-7 "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r--r-- .*" - echo "${CVSROOT_DIRNAME}/." > ${TESTDIR}/dotest.abs - echo "." > ${TESTDIR}/dotest.rel - if cmp ${TESTDIR}/dotest.abs CVS/Repository >/dev/null 2>&1; then - AREP="${CVSROOT_DIRNAME}/" - elif cmp ${TESTDIR}/dotest.rel CVS/Repository >/dev/null 2>&1; then - AREP="" + # OK, now manually change the modes and see what happens. + chmod g=r,o= ${TESTDIR}/cvsroot/first-dir/aa,v + echo second line >>aa + dotest modes-7a "${testcvs} -q ci -m set-execute-bit" \ +"Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +new revision: 1\.3; previous revision: 1\.2 +done" + dotest modes-7b "ls -l ${TESTDIR}/cvsroot/first-dir/aa,v" \ +"-r--r----- .*" + + CVSUMASK=007 + export CVSUMASK + touch ab + # Might as well test the execute bit too. + chmod +x ab + dotest modes-8 "${testcvs} add ab" \ +"${PROG} [a-z]*: scheduling file .ab. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest modes-9 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/ab,v +done +Checking in ab; +${TESTDIR}/cvsroot/first-dir/ab,v <-- ab +initial revision: 1\.1 +done" + if test "x$remote" = xyes; then + # The problem here is that the CVSUMASK environment variable + # needs to be set on the server (e.g. .bashrc). This is, of + # course, bogus, but that is the way it is currently. + dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ +"-r-xr-x---.*" "-r-xr-xr-x.*" else - fail "Cannot figure out if RELATIVE_REPOS is defined." + dotest modes-10 "ls -l ${TESTDIR}/cvsroot/first-dir/ab,v" \ +"-r-xr-x---.*" fi - # Test CVS/Root once. Since there is only one part of - # the code which writes CVS/Root files (Create_Admin), - # there is no point in testing this every time. - dotest cvsadm-1a "cat CVS/Root" ${REP} - dotest cvsadm-1b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-1c "cat CVSROOT/CVS/Root" ${REP} - dotest cvsadm-1d "cat CVSROOT/CVS/Repository" \ -"${AREP}CVSROOT" - # All of the defined module names begin with a number. - # All of the top-level directory names begin with "dir". - # All of the subdirectory names begin with "sub". - # All of the top-level modules begin with "mod". - echo "# Module defs for cvsadm tests" > CVSROOT/modules - echo "1mod mod1" >> CVSROOT/modules - echo "1mod-2 mod1-2" >> CVSROOT/modules - echo "2mod mod2/sub2" >> CVSROOT/modules - echo "2mod-2 mod2-2/sub2-2" >> CVSROOT/modules - echo "1d1mod -d dir1d1 mod1" >> CVSROOT/modules - echo "1d1mod-2 -d dir1d1-2 mod1-2" >> CVSROOT/modules - echo "1d2mod -d dir1d2 mod2/sub2" >> CVSROOT/modules - echo "1d2mod-2 -d dir1d2-2 mod2-2/sub2-2" >> CVSROOT/modules - echo "2d1mod -d dir2d1/sub2d1 mod1" >> CVSROOT/modules - echo "2d1mod-2 -d dir2d1-2/sub2d1-2 mod1-2" >> CVSROOT/modules - echo "2d2mod -d dir2d2/sub2d2 mod2/sub2" >> CVSROOT/modules - echo "2d2mod-2 -d dir2d2-2/sub2d2-2 mod2-2/sub2-2" >> CVSROOT/modules - dotest cvsadm-1e "${testcvs} ci -m add-modules" \ -"${PROG} [a-z]*: Examining . -${PROG} [a-z]*: Examining CVSROOT -Checking in CVSROOT/modules; -${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- modules -new revision: 1\.[0-9]*; previous revision: 1\.[0-9]* + # OK, now add a file on a branch. Check that the mode gets + # set the same way (it is a different code path in CVS). + dotest modes-11 "${testcvs} -q tag -b br" 'T aa +T ab' + dotest modes-12 "${testcvs} -q update -r br" '' + touch ac + dotest modes-13 "${testcvs} add ac" \ +"${PROG} [a-z]*: scheduling file .ac. for addition on branch .br. +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + # Not sure it really makes sense to refer to a "previous revision" + # when we are just now adding the file; as far as I know + # that is longstanding CVS behavior, for what it's worth. + dotest modes-14 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/Attic/ac,v done -${PROG} [a-z]*: Rebuilding administrative file database" - rm -rf CVS CVSROOT; - - # Create the various modules - mkdir ${CVSROOT_DIRNAME}/mod1 - mkdir ${CVSROOT_DIRNAME}/mod1-2 - mkdir ${CVSROOT_DIRNAME}/mod2 - mkdir ${CVSROOT_DIRNAME}/mod2/sub2 - mkdir ${CVSROOT_DIRNAME}/mod2-2 - mkdir ${CVSROOT_DIRNAME}/mod2-2/sub2-2 - dotest cvsadm-2 "${testcvs} co mod1 mod1-2 mod2 mod2-2" \ -"${PROG} [a-z]*: Updating mod1 -${PROG} [a-z]*: Updating mod1-2 -${PROG} [a-z]*: Updating mod2 -${PROG} [a-z]*: Updating mod2/sub2 -${PROG} [a-z]*: Updating mod2-2 -${PROG} [a-z]*: Updating mod2-2/sub2-2" +Checking in ac; +${TESTDIR}/cvsroot/first-dir/Attic/ac,v <-- ac +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + if test "x$remote" = xyes; then + # The problem here is that the CVSUMASK environment variable + # needs to be set on the server (e.g. .bashrc). This is, of + # course, bogus, but that is the way it is currently. + dotest modes-15 \ +"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ +"-r--r--r--.*" + else + dotest modes-15 \ +"ls -l ${TESTDIR}/cvsroot/first-dir/Attic/ac,v" \ +"-r--r-----.*" + fi - # Populate the directories for the halibut - echo "file1" > mod1/file1 - echo "file1-2" > mod1-2/file1-2 - echo "file2" > mod2/sub2/file2 - echo "file2-2" > mod2-2/sub2-2/file2-2 - dotest cvsadm-2a "${testcvs} add mod1/file1 mod1-2/file1-2 mod2/sub2/file2 mod2-2/sub2-2/file2-2" \ -"${PROG} [a-z]*: scheduling file .mod1/file1. for addition -${PROG} [a-z]*: scheduling file .mod1-2/file1-2. for addition -${PROG} [a-z]*: scheduling file .mod2/sub2/file2. for addition -${PROG} [a-z]*: scheduling file .mod2-2/sub2-2/file2-2. for addition -${PROG} [a-z]*: use '${PROG} commit' to add these files permanently" + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + # Perhaps should restore the umask and CVSUMASK. But the other + # tests "should" not care about them... + ;; - dotest cvsadm-2b "${testcvs} ci -m yup mod1 mod1-2 mod2 mod2-2" \ -"${PROG} [a-z]*: Examining mod1 -${PROG} [a-z]*: Examining mod1-2 -${PROG} [a-z]*: Examining mod2 -${PROG} [a-z]*: Examining mod2/sub2 -${PROG} [a-z]*: Examining mod2-2 -${PROG} [a-z]*: Examining mod2-2/sub2-2 -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}/mod1-2/file1-2,v -done -Checking in mod1-2/file1-2; -${CVSROOT_DIRNAME}/mod1-2/file1-2,v <-- file1-2 -initial revision: 1.1 -done -RCS file: ${CVSROOT_DIRNAME}/mod2/sub2/file2,v + stamps) + # Test timestamps. + mkdir 1; cd 1 + dotest stamps-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest stamps-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch aa + echo '$''Id$' >kw + ls -l aa >${TESTDIR}/1/stamp.aa.touch + ls -l kw >${TESTDIR}/1/stamp.kw.touch + # "sleep 1" would suffice if we could assume ls --full-time, but + # that is as far as I know unique to GNU ls. Is there some POSIX.2 + # way to get the timestamp of a file, including the seconds? + sleep 60 + dotest stamps-3 "${testcvs} add aa kw" \ +"${PROG} [a-z]*: scheduling file .aa. for addition +${PROG} [a-z]*: scheduling file .kw. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + ls -l aa >${TESTDIR}/1/stamp.aa.add + ls -l kw >${TESTDIR}/1/stamp.kw.add + # "cvs add" should not muck with the timestamp. + dotest stamps-4aa \ +"cmp ${TESTDIR}/1/stamp.aa.touch ${TESTDIR}/1/stamp.aa.add" '' + dotest stamps-4kw \ +"cmp ${TESTDIR}/1/stamp.kw.touch ${TESTDIR}/1/stamp.kw.add" '' + sleep 60 + dotest stamps-5 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aa,v done -Checking in mod2/sub2/file2; -${CVSROOT_DIRNAME}/mod2/sub2/file2,v <-- file2 -initial revision: 1.1 +Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +initial revision: 1\.1 done -RCS file: ${CVSROOT_DIRNAME}/mod2-2/sub2-2/file2-2,v +RCS file: ${TESTDIR}/cvsroot/first-dir/kw,v done -Checking in mod2-2/sub2-2/file2-2; -${CVSROOT_DIRNAME}/mod2-2/sub2-2/file2-2,v <-- file2-2 -initial revision: 1.1 +Checking in kw; +${TESTDIR}/cvsroot/first-dir/kw,v <-- kw +initial revision: 1\.1 done" - # Finished creating the modules -- clean up. - rm -rf CVS mod1 mod1-2 mod2 mod2-2 - # Done. - - ################################################## - ## Start the dizzying array of possibilities. - ## Begin with each module type separately. - ################################################## - - # Pattern -- after each checkout, first check the top-level - # CVS directory. Then, check the directories in numerical - # order. - - dotest cvsadm-3 "${testcvs} co 1mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1" - dotest cvsadm-3b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-3d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 1mod + ls -l aa >${TESTDIR}/1/stamp.aa.ci + ls -l kw >${TESTDIR}/1/stamp.kw.ci + # If there are no keywords, "cvs ci" leaves the timestamp alone + # If there are, it sets the timestamp to the date of the commit. + # I'm not sure how logical this is, but it is intentional. + # If we wanted to get fancy we would make sure the time as + # reported in "cvs log kw" matched stamp.kw.ci. But that would + # be a lot of work. + dotest stamps-6aa \ + "cmp ${TESTDIR}/1/stamp.aa.add ${TESTDIR}/1/stamp.aa.ci" '' + if cmp ${TESTDIR}/1/stamp.kw.add ${TESTDIR}/1/stamp.kw.ci >/dev/null + then + fail stamps-6kw + else + pass stamps-6kw + fi + cd ../.. + sleep 60 + mkdir 2 + cd 2 + dotest stamps-7 "${testcvs} -q get first-dir" "U first-dir/aa +U first-dir/kw" + cd first-dir + ls -l aa >${TESTDIR}/1/stamp.aa.get + ls -l kw >${TESTDIR}/1/stamp.kw.get + # On checkout, CVS should set the timestamp to the date that the + # file was committed. Could check that the time as reported in + # "cvs log aa" matches stamp.aa.get, but that would be a lot of + # work. + if cmp ${TESTDIR}/1/stamp.aa.ci ${TESTDIR}/1/stamp.aa.get >/dev/null + then + fail stamps-8aa + else + pass stamps-8aa + fi + dotest stamps-8kw \ + "cmp ${TESTDIR}/1/stamp.kw.ci ${TESTDIR}/1/stamp.kw.get" '' - dotest cvsadm-4 "${testcvs} co 2mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2" - dotest cvsadm-4b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-4d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 2mod + # Now we want to see what "cvs update" does. + sleep 60 + echo add a line >>aa + echo add a line >>kw + dotest stamps-9 "${testcvs} -q ci -m change-them" \ +"Checking in aa; +${TESTDIR}/cvsroot/first-dir/aa,v <-- aa +new revision: 1\.2; previous revision: 1\.1 +done +Checking in kw; +${TESTDIR}/cvsroot/first-dir/kw,v <-- kw +new revision: 1\.2; previous revision: 1\.1 +done" + ls -l aa >${TESTDIR}/1/stamp.aa.ci2 + ls -l kw >${TESTDIR}/1/stamp.kw.ci2 + cd ../.. + cd 1/first-dir + sleep 60 + dotest stamps-10 "${testcvs} -q update" '[UP] aa +[UP] kw' + # this doesn't serve any function other than being able to + # look at it manually, as we have no machinery for dates being + # newer or older than other dates. + date >${TESTDIR}/1/stamp.debug.update + ls -l aa >${TESTDIR}/1/stamp.aa.update + ls -l kw >${TESTDIR}/1/stamp.kw.update + # stamp.aa.update and stamp.kw.update should both be approximately + # the same as stamp.debug.update. Perhaps we could be testing + # this in a more fancy fashion by "touch stamp.before" before + # stamps-10, "touch stamp.after" after, and then using ls -t + # to check them. But for now we just make sure that the *.update + # stamps differ from the *.ci2 ones. + # As for the rationale, this is so that if one updates and gets + # a new revision, then "make" will be sure to regard those files + # as newer than .o files which may be sitting around. + if cmp ${TESTDIR}/1/stamp.aa.update ${TESTDIR}/1/stamp.aa.ci2 \ + >/dev/null + then + fail stamps-11aa + else + pass stamps-11aa + fi + if cmp ${TESTDIR}/1/stamp.kw.update ${TESTDIR}/1/stamp.kw.ci2 \ + >/dev/null + then + fail stamps-11kw + else + pass stamps-11kw + fi - dotest cvsadm-5 "${testcvs} co 1d1mod" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1" - dotest cvsadm-5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-5d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir1d1 + cd ../.. - dotest cvsadm-6 "${testcvs} co 1d2mod" \ -"${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2" - dotest cvsadm-6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-6d "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir1d2 + if test "$keep" = yes; then + echo Keeping ${TESTDIR} and exiting due to --keep + exit 0 + fi - dotest cvsadm-7 "${testcvs} co 2d1mod" \ -"${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - dotest cvsadm-7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-7d "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-7f "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir2d1 + rm -r 1 2 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-8 "${testcvs} co 2d2mod" \ -"${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - dotest cvsadm-8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-8d "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-8f "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir2d2 + sticky) + # More tests of sticky tags, particularly non-branch sticky tags. + # See many tests (e.g. multibranch) for ordinary sticky tag + # operations such as adding files on branches. + # See "head" test for interaction between stick tags and HEAD. + mkdir 1; cd 1 + dotest sticky-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest sticky-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir - ################################################## - ## You are in a shell script of twisted little - ## module combination statements, all alike. - ################################################## + touch file1 + dotest sticky-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest sticky-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest sticky-5 "${testcvs} -q tag tag1" "T file1" + echo add a line >>file1 + dotest sticky-6 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest sticky-7 "${testcvs} -q update -r tag1" "[UP] file1" + dotest sticky-8 "cat file1" '' + dotest sticky-9 "${testcvs} -q update" '' + dotest sticky-10 "cat file1" '' + touch file2 + dotest_fail sticky-11 "${testcvs} add file2" \ +"${PROG} [a-z]*: cannot add file on non-branch tag tag1" + dotest sticky-12 "${testcvs} -q update -A" "[UP] file1 +${QUESTION} file2" "${QUESTION} file2 +[UP] file1" + dotest sticky-13 "${testcvs} add file2" \ +"${PROG} [a-z]*: scheduling file .file2. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest sticky-14 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done" - ### 1mod - - dotest cvsadm-9 "${testcvs} co 1mod 1mod-2" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating 1mod-2 -U 1mod-2/file1-2" - # the usual for the top level - dotest cvsadm-9b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-9d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1mod copy - dotest cvsadm-9f "cat 1mod-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS 1mod 1mod-2 + # Now back to tag1 + dotest sticky-15 "${testcvs} -q update -r tag1" "[UP] file1 +${PROG} [a-z]*: file2 is no longer in the repository" - # 1mod 2mod redmod bluemod - dotest cvsadm-10 "${testcvs} co 1mod 2mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating 2mod -U 2mod/file2" - # the usual for the top level - dotest cvsadm-10b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-10d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2dmod - dotest cvsadm-10f "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 1mod 2mod + rm file1 + dotest sticky-16 "${testcvs} rm file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .${PROG} commit. to remove this file permanently" + # Hmm, this command seems to silently remove the tag from + # the file. This appears to be intentional. + # The silently part especially strikes me as odd, though. + dotest sticky-17 "${testcvs} -q ci -m remove-it" "" + dotest sticky-18 "${testcvs} -q update -A" "U file1 +U file2" + dotest sticky-19 "${testcvs} -q update -r tag1" \ +"${PROG} [a-z]*: file1 is no longer in the repository +${PROG} [a-z]*: file2 is no longer in the repository" + dotest sticky-20 "${testcvs} -q update -A" "U file1 +U file2" - dotest cvsadm-11 "${testcvs} co 1mod 1d1mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1" - # the usual for the top level - dotest cvsadm-11b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-11d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d1mod - dotest cvsadm-11f "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 1mod dir1d1 + # Now try with a numeric revision. + dotest sticky-21 "${testcvs} -q update -r 1.1 file1" "U file1" + rm file1 + dotest sticky-22 "${testcvs} rm file1" \ +"${PROG} [a-z]*: cannot remove file .file1. which has a numeric sticky tag of .1\.1." + # The old behavior was that remove allowed this and then commit + # gave an error, which was somewhat hard to clear. I mean, you + # could get into a long elaborate discussion of this being a + # conflict and two ways to resolve it, but I don't really see + # why CVS should have a concept of conflict that arises, not from + # parallel development, but from CVS's own sticky tags. - dotest cvsadm-12 "${testcvs} co 1mod 1d2mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2" - # the usual for the top level - dotest cvsadm-12b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-12d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d2mod - dotest cvsadm-12f "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 1mod dir1d2 + # I'm kind of surprised that the "file1 was lost" doesn't crop + # up elsewhere in the testsuite. It is a long-standing + # discrepency between local and remote CVS and should probably + # be cleaned up at some point. + dotest sticky-23 "${testcvs} -q update -A" \ +"${PROG} [a-z]*: warning: file1 was lost +U file1" "U file1" - dotest cvsadm-13 "${testcvs} co 1mod 2d1mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - # the usual for the top level - dotest cvsadm-13b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-13d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-13f "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-13h "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 1mod dir2d1 - - dotest cvsadm-14 "${testcvs} co 1mod 2d2mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1 -${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - # the usual for the top level - dotest cvsadm-14b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-14d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-14f "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-14h "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 1mod dir2d2 + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + keyword) + # Test keyword expansion. + # Various other tests relate to our ability to correctly + # set the keyword expansion mode. + # "binfiles" tests "cvs admin -k". + # "binfiles" and "binfiles2" test "cvs add -k". + # "rdiff" tests "cvs co -k". + # "binfiles" (and this test) test "cvs update -k". + # "binwrap" tests setting the mode from wrappers. + # I don't think any test is testing "cvs import -k". + mkdir 1; cd 1 + dotest keyword-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest keyword-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir - ### 2mod - - dotest cvsadm-15 "${testcvs} co 2mod 2mod-2" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2 -${PROG} [a-z]*: Updating 2mod-2 -U 2mod-2/file2-2" - # the usual for the top level - dotest cvsadm-15b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-15d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2mod copy - dotest cvsadm-15f "cat 2mod-2/CVS/Repository" \ -"${AREP}mod2-2/sub2-2" - rm -rf CVS 2mod 2mod-2 + echo '$''Author$' > file1 + echo '$''Date$' >> file1 + echo '$''Header$' >> file1 + echo '$''Id$' >> file1 + echo '$''Locker$' >> file1 + echo '$''Name$' >> file1 + echo '$''RCSfile$' >> file1 + echo '$''Revision$' >> file1 + echo '$''Source$' >> file1 + echo '$''State$' >> file1 + echo '$''Nonkey$' >> file1 + # Omit the trailing dollar sign + echo '$''Date' >> file1 + # Put two keywords on one line + echo '$''State$' '$''State$' >> file1 + # Use a header for Log + echo 'xx $''Log$' >> file1 + dotest keyword-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest keyword-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest keyword-5 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Locker: "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" - dotest cvsadm-16 "${testcvs} co 2mod 1d1mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2 -${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1" - # the usual for the top level - dotest cvsadm-16b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-16d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d1mod - dotest cvsadm-16f "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 2mod dir1d1 + # Use cvs admin to lock the RCS file in order to check -kkvl + # vs. -kkv. CVS does not normally lock RCS files, but some + # people use cvs admin to enforce reserved checkouts. + dotest keyword-6 "${testcvs} admin -l file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +1\.1 locked +done" - dotest cvsadm-17 "${testcvs} co 2mod 1d2mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2 -${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2" - # the usual for the top level - dotest cvsadm-17b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-17d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d2mod - dotest cvsadm-17f "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 2mod dir1d2 + dotest keyword-7 "${testcvs} update -kkv file1" "U file1" + dotest keyword-8 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp "'\$'" +"'\$'"Locker: "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" - dotest cvsadm-18 "${testcvs} co 2mod 2d1mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2 -${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - # the usual for the top level - dotest cvsadm-18b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-18d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d1mod - dotest cvsadm-18f "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-18h "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 2mod dir2d1 + dotest keyword-9 "${testcvs} update -kkvl file1" "U file1" + dotest keyword-10 "cat file1" \ +'\$'"Author: ${username} "'\$'" +"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'" +"'\$'"Header: ${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" +"'\$'"Id: file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp ${username} "'\$'" +"'\$'"Locker: ${username} "'\$'" +"'\$'"Name: "'\$'" +"'\$'"RCSfile: file1,v "'\$'" +"'\$'"Revision: 1\.1 "'\$'" +"'\$'"Source: ${TESTDIR}/cvsroot/first-dir/file1,v "'\$'" +"'\$'"State: Exp "'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'" +xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" - dotest cvsadm-19 "${testcvs} co 2mod 2d2mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2 -${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - # the usual for the top level - dotest cvsadm-19b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-19d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d2mod - dotest cvsadm-19f "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-19h "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 2mod dir2d2 + dotest keyword-11 "${testcvs} update -kk file1" "U file1" + dotest keyword-12 "cat file1" \ +'\$'"Author"'\$'" +"'\$'"Date"'\$'" +"'\$'"Header"'\$'" +"'\$'"Id"'\$'" +"'\$'"Locker"'\$'" +"'\$'"Name"'\$'" +"'\$'"RCSfile"'\$'" +"'\$'"Revision"'\$'" +"'\$'"Source"'\$'" +"'\$'"State"'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State"'\$'" "'\$'"State"'\$'" +xx "'\$'"Log"'\$'" +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" + dotest keyword-13 "${testcvs} update -kv file1" "U file1" + dotest keyword-14 "cat file1" \ +"${username} +[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +${TESTDIR}/cvsroot/first-dir/file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp +file1,v 1\.1 [0-9/]* [0-9:]* ${username} Exp - ### 1d1mod - dotest cvsadm-20 "${testcvs} co 1d1mod 1d1mod-2" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1 -${PROG} [a-z]*: Updating dir1d1-2 -U dir1d1-2/file1-2" - # the usual for the top level - dotest cvsadm-20b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-20d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d1mod copy - dotest cvsadm-20f "cat dir1d1-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS dir1d1 dir1d1-2 +file1,v +1\.1 +${TESTDIR}/cvsroot/first-dir/file1,v +Exp +"'\$'"Nonkey"'\$'" +"'\$'"Date +Exp Exp +xx file1,v +xx Revision 1\.1 [0-9/]* [0-9:]* ${username} +xx add +xx" - dotest cvsadm-21 "${testcvs} co 1d1mod 1d2mod" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1 -${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2" - # the usual for the top level - dotest cvsadm-21b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-21d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d2mod - dotest cvsadm-21f "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir1d1 dir1d2 + dotest keyword-15 "${testcvs} update -ko file1" "U file1" + dotest keyword-16 "cat file1" \ +'\$'"Author"'\$'" +"'\$'"Date"'\$'" +"'\$'"Header"'\$'" +"'\$'"Id"'\$'" +"'\$'"Locker"'\$'" +"'\$'"Name"'\$'" +"'\$'"RCSfile"'\$'" +"'\$'"Revision"'\$'" +"'\$'"Source"'\$'" +"'\$'"State"'\$'" +"'\$'"Nonkey"'\$'" +"'\$'"Date +"'\$'"State"'\$'" "'\$'"State"'\$'" +xx "'\$'"Log"'\$' - dotest cvsadm-22 "${testcvs} co 1d1mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1 -${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - # the usual for the top level - dotest cvsadm-22b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-22d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-22f "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-22h "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir1d1 dir2d1 + # Test the Name keyword. First go back to normal expansion. - dotest cvsadm-23 "${testcvs} co 1d1mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1 -${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - # the usual for the top level - dotest cvsadm-23b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-23d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-23f "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-23h "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir1d1 dir2d2 + dotest keyword-17 "${testcvs} update -A file1" "U file1" + echo '$''Name$' > file1 + dotest keyword-18 "${testcvs} ci -m modify file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest keyword-19 "${testcvs} -q tag tag1" "T file1" + echo "change" >> file1 + dotest keyword-20 "${testcvs} -q ci -m mod2 file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + dotest keyword-21 "${testcvs} -q update -r tag1" "[UP] file1" - ### 1d2mod + # FIXME: This test fails when remote. The second expect + # string below should be removed when this is fixed. + dotest keyword-22 "cat file1" '\$'"Name: tag1 "'\$' \ +'\$'"Name: "'\$' - dotest cvsadm-24 "${testcvs} co 1d2mod 1d2mod-2" \ -"${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2 -${PROG} [a-z]*: Updating dir1d2-2 -U dir1d2-2/file2-2" - # the usual for the top level - dotest cvsadm-24b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-24d "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d2mod copy - dotest cvsadm-24f "cat dir1d2-2/CVS/Repository" \ -"${AREP}mod2-2/sub2-2" - rm -rf CVS dir1d2 dir1d2-2 + dotest keyword-23 "${testcvs} update -A file1" "[UP] file1" - dotest cvsadm-25 "${testcvs} co 1d2mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2 -${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - # the usual for the top level - dotest cvsadm-25b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-25d "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d1mod - dotest cvsadm-25f "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-25h "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir1d2 dir2d1 + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-26 "${testcvs} co 1d2mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2 -${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - # the usual for the top level - dotest cvsadm-26b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-26d "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d2mod - dotest cvsadm-26f "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-26h "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir1d2 dir2d2 + keywordlog) + # Test the Log keyword. + mkdir 1; cd 1 + dotest keywordlog-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest keywordlog-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + echo change >file1 + dotest keywordlog-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + # Note that we wanted to try "ci -r 1.3 -m add file1" and CVS + # seemed to get all confused, thinking it was adding on a branch + # or something. FIXME? Do something about this? Document it + # in BUGS or someplace? - # 2d1mod + dotest keywordlog-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" - dotest cvsadm-27 "${testcvs} co 2d1mod 2d1mod-2" \ -"${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1 -${PROG} [a-z]*: Updating dir2d1-2/sub2d1-2 -U dir2d1-2/sub2d1-2/file1-2" - # the usual for the top level - dotest cvsadm-27b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2d1mod - dotest cvsadm-27d "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-27f "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-27h "cat dir2d1-2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-27j "cat dir2d1-2/sub2d1-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS dir2d1 dir2d1-2 + cd ../.. + mkdir 2; cd 2 + dotest keywordlog-4a "${testcvs} -q co first-dir" "U first-dir/file1" + cd ../1/first-dir - dotest cvsadm-28 "${testcvs} co 2d1mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1 -${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - # the usual for the top level - dotest cvsadm-28b "cat CVS/Repository" \ -"${AREP}\." - # the usual for 2d1mod - dotest cvsadm-28d "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-28f "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-28h "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-28j "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir2d1 dir2d2 + echo 'xx $''Log$' > file1 + cat >${TESTDIR}/comment.tmp <> file1 + dotest keywordlog-10 "${testcvs} ci -m modify file1" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + dotest keywordlog-11 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.3 [0-9/]* [0-9:]* ${username} +xx modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +change" - dotest cvsadm-1d5 "${testcvs} co -d dir 1d1mod" \ -"${PROG} [a-z]*: Updating dir -U dir/file1" - dotest cvsadm-1d5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-1d5d "cat dir/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + cd ../../2/first-dir + dotest keywordlog-12 "${testcvs} -q update" "[UP] file1" + dotest keywordlog-13 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.3 [0-9/]* [0-9:]* ${username} +xx modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +change" - dotest cvsadm-1d6 "${testcvs} co -d dir 1d2mod" \ -"${PROG} [a-z]*: Updating dir -U dir/file2" - dotest cvsadm-1d6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-1d6d "cat dir/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + cd ../../1/first-dir + dotest keywordlog-14 "${testcvs} -q update -r br" "[UP] file1" + echo br-change >>file1 + dotest keywordlog-15 "${testcvs} -q ci -m br-modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.1; previous revision: 1\.2 +done" + dotest keywordlog-16 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} +xx br-modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +br-change" + cd ../../2/first-dir + dotest keywordlog-17 "${testcvs} -q update -r br" "[UP] file1" + dotest keywordlog-18 "cat file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} +xx br-modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +br-change" + cd ../.. + dotest keywordlog-19 "${testcvs} -q co -p -r br first-dir/file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.2\.2\.1 [0-9/]* [0-9:]* ${username} +xx br-modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +br-change" + dotest keywordlog-20 "${testcvs} -q co -p first-dir/file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.3 [0-9/]* [0-9:]* ${username} +xx modify +xx +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx +change" + dotest keywordlog-21 "${testcvs} -q co -p -r 1.2 first-dir/file1" \ +"xx "'\$'"Log: file1,v "'\$'" +xx Revision 1\.2 [0-9/]* [0-9:]* ${username} +xx First log line +xx Second log line +xx" - dotest cvsadm-1d7 "${testcvs} co -d dir 2d1mod" \ -"${PROG} [a-z]*: Updating dir -U dir/file1" - dotest cvsadm-1d7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-1d7d "cat dir/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + rm -r 1 2 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-1d8 "${testcvs} co -d dir 2d2mod" \ -"${PROG} [a-z]*: Updating dir -U dir/file2" - dotest cvsadm-1d8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-1d8d "cat dir/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + head) + # Testing handling of the HEAD special tag. + # There are many cases involving added and removed files + # which we don't yet try to deal with. + # TODO: We also could be paying much closer attention to + # "head of the trunk" versus "head of the default branch". + # That is what "cvs import" is doing here (but I didn't really + # fully follow through on writing the tests for that case). + mkdir imp-dir + cd imp-dir + echo 'imported contents' >file1 + # It may seem like we don't do much with file2, but do note that + # the "cvs diff" invocations do also diff file2 (and come up empty). + echo 'imported contents' >file2 + dotest_sort head-1 "${testcvs} import -m add first-dir tag1 tag2" \ +" - ################################################## - ## Los Combonaciones - ################################################## +N first-dir/file1 +N first-dir/file2 +No conflicts created by this import" + cd .. + rm -r imp-dir + mkdir 1 + cd 1 + dotest head-2 "${testcvs} -q co first-dir" \ +"U first-dir/file1 +U first-dir/file2" + cd first-dir + echo 'add a line on trunk' >> file1 + dotest head-3 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest head-4 "${testcvs} -q tag trunktag" "T file1 +T file2" + echo 'add a line on trunk after trunktag' >> file1 + dotest head-5 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done" + dotest head-6 "${testcvs} -q tag -b br1" "T file1 +T file2" + dotest head-7 "${testcvs} -q update -r br1" "" + echo 'modify on branch' >>file1 + dotest head-8 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3\.2\.1; previous revision: 1\.3 +done" + dotest head-9 "${testcvs} -q tag brtag" "T file1 +T file2" + echo 'modify on branch after brtag' >>file1 + dotest head-10 "${testcvs} -q ci -m modify" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1 +done" + # With no sticky tags, HEAD is the head of the trunk. + dotest head-trunk-setup "${testcvs} -q update -A" "[UP] file1" + dotest head-trunk-update "${testcvs} -q update -r HEAD -p file1" \ +"imported contents +add a line on trunk +add a line on trunk after trunktag" + # and diff thinks so too. Case (a) from the comment in + # cvs.texinfo (Common options). + dotest_fail head-trunk-diff "${testcvs} -q diff -c -r HEAD -r br1" \ +"Index: file1 +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.3 +retrieving revision 1\.3\.2\.2 +diff -c -r1\.3 -r1\.3\.2\.2 +\*\*\* file1 [0-9/]* [0-9:]* 1\.3 +--- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1,3 \*\*\*\* +--- 1,5 ---- + imported contents + add a line on trunk + add a line on trunk after trunktag +${PLUS} modify on branch +${PLUS} modify on branch after brtag" - ### 1mod + # With a branch sticky tag, HEAD is the head of the trunk. + dotest head-br1-setup "${testcvs} -q update -r br1" "[UP] file1" + dotest head-br1-update "${testcvs} -q update -r HEAD -p file1" \ +"imported contents +add a line on trunk +add a line on trunk after trunktag" + # But diff thinks that HEAD is "br1". Case (b) from cvs.texinfo. + # Probably people are relying on it. + dotest head-br1-diff "${testcvs} -q diff -c -r HEAD -r br1" "" - dotest cvsadm-1d9 "${testcvs} co -d dir 1mod 1mod-2" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/1mod-2 -U dir/1mod-2/file1-2" - # the usual for the top level - dotest cvsadm-1d9b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d9d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d9f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1mod copy - dotest cvsadm-1d9h "cat dir/1mod-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS dir + # With a nonbranch sticky tag on a branch, + # HEAD is the head of the trunk + dotest head-brtag-setup "${testcvs} -q update -r brtag" "[UP] file1" + dotest head-brtag-update "${testcvs} -q update -r HEAD -p file1" \ +"imported contents +add a line on trunk +add a line on trunk after trunktag" + # But diff thinks that HEAD is "brtag". Case (c) from + # cvs.texinfo (the "strange, maybe accidental" case). + dotest_fail head-brtag-diff "${testcvs} -q diff -c -r HEAD -r br1" \ +"Index: file1 +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.3\.2\.1 +retrieving revision 1\.3\.2\.2 +diff -c -r1\.3\.2\.1 -r1\.3\.2\.2 +\*\*\* file1 [0-9/]* [0-9:]* 1\.3\.2\.1 +--- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 2,4 \*\*\*\* +--- 2,5 ---- + add a line on trunk + add a line on trunk after trunktag + modify on branch +${PLUS} modify on branch after brtag" - # 1mod 2mod redmod bluemod - dotest cvsadm-1d10 "${testcvs} co -d dir 1mod 2mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2" - dotest cvsadm-1d10b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d10d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d10f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2dmod - dotest cvsadm-1d10h "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # With a nonbranch sticky tag on the trunk, HEAD is the head + # of the trunk, I think. + dotest head-trunktag-setup "${testcvs} -q update -r trunktag" \ +"[UP] file1" + dotest head-trunktag-check "cat file1" "imported contents +add a line on trunk" + dotest head-trunktag-update "${testcvs} -q update -r HEAD -p file1" \ +"imported contents +add a line on trunk +add a line on trunk after trunktag" + # Like head-brtag-diff, HEAD is the sticky tag. Similarly + # questionable. + dotest_fail head-trunktag-diff \ + "${testcvs} -q diff -c -r HEAD -r br1" \ +"Index: file1 +=================================================================== +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +retrieving revision 1\.2 +retrieving revision 1\.3\.2\.2 +diff -c -r1\.2 -r1\.3\.2\.2 +\*\*\* file1 [0-9/]* [0-9:]* 1\.2 +--- file1 [0-9/]* [0-9:]* 1\.3\.2\.2 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1,2 \*\*\*\* +--- 1,5 ---- + imported contents + add a line on trunk +${PLUS} add a line on trunk after trunktag +${PLUS} modify on branch +${PLUS} modify on branch after brtag" - dotest cvsadm-1d11 "${testcvs} co -d dir 1mod 1d1mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1" - dotest cvsadm-1d11b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d11d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d11f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d1mod - dotest cvsadm-1d11h "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + # Also might test what happens if we setup with update -r + # HEAD. In general, if sticky tags matter, does the + # behavior of "update -r " (without -p) depend on the + # sticky tags before or after the update? - dotest cvsadm-1d12 "${testcvs} co -d dir 1mod 1d2mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2" - dotest cvsadm-1d12b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d12d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d12f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d2mod - dotest cvsadm-1d12h "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # Note that we are testing both the case where this deletes + # a revision (file1) and the case where it does not (file2) + dotest_fail head-o0a "${testcvs} admin -o ::br1" \ +"${PROG} [a-z]*: Administrating \. +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +${PROG} [a-z]*: cannot remove revision 1\.3\.2\.1 because it has tags +${PROG} [a-z]*: cannot modify RCS file for .file1. +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done" + dotest head-o0b "${testcvs} tag -d brtag" \ +"${PROG} [a-z]*: Untagging \. +D file1 +D file2" + dotest head-o1 "${testcvs} admin -o ::br1" \ +"${PROG} [a-z]*: Administrating \. +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +deleting revision 1\.3\.2\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done" + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-1d13 "${testcvs} co -d dir 1mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1" - dotest cvsadm-1d13b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d13d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d13f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-1d13h "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d13j "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + tagdate) + # Test combining -r and -D. + mkdir 1; cd 1 + dotest tagdate-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest tagdate-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir - dotest cvsadm-1d14 "${testcvs} co -d dir 1mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1 -${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-1d14b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d14d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1mod - dotest cvsadm-1d14f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-1d14h "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d14j "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + echo trunk-1 >file1 + dotest tagdate-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest tagdate-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" + dotest tagdate-5 "${testcvs} -q tag -b br1" "T file1" + dotest tagdate-6 "${testcvs} -q tag -b br2" "T file1" + echo trunk-2 >file1 + dotest tagdate-7 "${testcvs} -q ci -m modify-on-trunk" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done" + # We are testing -r -D where br1 is a (magic) branch without + # any revisions. First the case where br2 doesn't have any + # revisions either: + dotest tagdate-8 "${testcvs} -q update -p -r br1 -D now" "trunk-1" + dotest tagdate-9 "${testcvs} -q update -r br2" "[UP] file1" + echo br2-1 >file1 + dotest tagdate-10 "${testcvs} -q ci -m modify-on-br2" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.4\.1; previous revision: 1\.1 +done" + # Then the case where br2 does have revisions: + dotest tagdate-11 "${testcvs} -q update -p -r br1 -D now" "trunk-1" + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - ### 2mod + multibranch2) + # Commit the first delta on branch A when there is an older + # branch, B, that already has a delta. A and B come from the + # same branch point. Then verify that branches A and B are + # in the right order. + mkdir 1; cd 1 + dotest multibranch2-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest multibranch2-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir - dotest cvsadm-1d15 "${testcvs} co -d dir 2mod 2mod-2" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2 -${PROG} [a-z]*: Updating dir/2mod-2 -U dir/2mod-2/file2-2" - dotest cvsadm-1d15b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d15d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-1d15f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2mod copy - dotest cvsadm-1d15h "cat dir/2mod-2/CVS/Repository" \ -"${AREP}mod2-2/sub2-2" - rm -rf CVS dir + echo trunk-1 >file1 + echo trunk-1 >file2 + dotest multibranch2-3 "${testcvs} add file1 file2" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: scheduling file .file2. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + dotest multibranch2-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done" + dotest multibranch2-5 "${testcvs} -q tag -b A" "T file1 +T file2" + dotest multibranch2-6 "${testcvs} -q tag -b B" "T file1 +T file2" - dotest cvsadm-1d16 "${testcvs} co -d dir 2mod 1d1mod" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2 -${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1" - dotest cvsadm-1d16b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d16d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-1d16f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d1mod - dotest cvsadm-1d16h "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + dotest multibranch2-7 "${testcvs} -q update -r B" '' + echo branch-B >file1 + echo branch-B >file2 + dotest multibranch2-8 "${testcvs} -q ci -m modify-on-B" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.4\.1; previous revision: 1\.1 +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.4\.1; previous revision: 1\.1 +done" - dotest cvsadm-1d17 "${testcvs} co -d dir 2mod 1d2mod" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2 -${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2" - dotest cvsadm-1d17b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d17d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-1d17f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d2mod - dotest cvsadm-1d17h "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + dotest multibranch2-9 "${testcvs} -q update -r A" '[UP] file1 +[UP] file2' + echo branch-A >file1 + # When using cvs-1.9.20, this commit gets a failed assertion in rcs.c. + dotest multibranch2-10 "${testcvs} -q ci -m modify-on-A" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" - dotest cvsadm-1d18 "${testcvs} co -d dir 2mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2 -${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1" - dotest cvsadm-1d18b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d18d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-1d18f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d1mod - dotest cvsadm-1d18h "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d18j "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + dotest multibranch2-11 "${testcvs} -q log file1" \ +" +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +symbolic names: + B: 1\.1\.0\.4 + A: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 3; selected revisions: 3 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: $username; state: Exp; +branches: 1\.1\.2; 1\.1\.4; +add +---------------------------- +revision 1\.1\.4\.1 +date: [0-9/]* [0-9:]*; author: $username; state: Exp; lines: ${PLUS}1 -1 +modify-on-B +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: $username; state: Exp; lines: ${PLUS}1 -1 +modify-on-A +=============================================================================" - dotest cvsadm-1d19 "${testcvs} co -d dir 2mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2 -${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-1d19b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d19d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2mod - dotest cvsadm-1d19f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d2mod - dotest cvsadm-1d19h "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d19j "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # This one is more concise. + dotest multibranch2-12 "${testcvs} -q log -r1.1 file1" \ +" +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +symbolic names: + B: 1\.1\.0\.4 + A: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 3; selected revisions: 1 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: $username; state: Exp; +branches: 1\.1\.2; 1\.1\.4; +add +=============================================================================" + # OK, try very much the same thing except we run update -j to + # bring the changes from B to A. Probably tests many of the + # same code paths but might as well keep it separate, I guess. - ### 1d1mod + dotest multibranch2-13 "${testcvs} -q update -r B" "[UP] file1 +[UP] file2" + dotest multibranch2-14 "${testcvs} -q update -r A -j B file2" \ +"[UP] file2 +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +retrieving revision 1.1 +retrieving revision 1.1.4.1 +Merging differences between 1.1 and 1.1.4.1 into file2" + dotest multibranch2-15 "${testcvs} -q ci -m commit-on-A file2" \ +"Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-1d20 "${testcvs} co -d dir 1d1mod 1d1mod-2" \ -"${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1 -${PROG} [a-z]*: Updating dir/dir1d1-2 -U dir/dir1d1-2/file1-2" - dotest cvsadm-1d20b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d20d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-1d20f "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d1mod copy - dotest cvsadm-1d20h "cat dir/dir1d1-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS dir - dotest cvsadm-1d21 "${testcvs} co -d dir 1d1mod 1d2mod" \ -"${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1 -${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2" - dotest cvsadm-1d21b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d21d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-1d21f "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 1d2mod - dotest cvsadm-1d21h "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + admin) + # More "cvs admin" tests. + # The basicb-21 test tests rejecting an illegal option. + # For -l and -u, see "reserved" and "keyword" tests. + # "binfiles" test has a test of "cvs admin -k". + # "log2" test has tests of -t and -q options to cvs admin. + # "rcs" tests -b option also. + # For -o, see: + # admin-22-o1 through admin-23 (various cases not involving ::) + # binfiles2-o* (:rev, rev on trunk; rev:, deleting entire branch) + # basica-o1 through basica-o3 (basic :: usage) + # head-o1 (::branch, where this deletes a revision or is noop) + # branches-o1 (::branch, similar, with different branch topology) + # log-o1 (1.3.2.1::) + # binfiles-o1 (1.3:: and ::1.3) + # Also could be testing: + # 1.3.2.6::1.3.2.8 + # 1.3.2.6::1.3.2 + # 1.3.2.1::1.3.2.6 + # 1.3::1.3.2.6 (error? or synonym for ::1.3.2.6?) - dotest cvsadm-1d22 "${testcvs} co -d dir 1d1mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1 -${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1" - dotest cvsadm-1d22b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d22d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-1d22f "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-1d22h "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d22j "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + mkdir 1; cd 1 + dotest admin-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest admin-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir - dotest cvsadm-1d23 "${testcvs} co -d dir 1d1mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1 -${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-1d23b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d23d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d1mod - dotest cvsadm-1d23f "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-1d23h "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d23j "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + dotest_fail admin-3 "${testcvs} -q admin -i file1" \ +"${PROG} admin: the -i option to admin is not supported +${PROG} admin: run add or import to create an RCS file +${PROG} \[admin aborted\]: specify ${PROG} -H admin for usage information" + dotest_fail admin-4 "${testcvs} -q log file1" \ +"${PROG} [a-z]*: nothing known about file1" + # Set up some files, file2 a plain one and file1 with a revision + # on a branch. + touch file1 file2 + dotest admin-5 "${testcvs} add file1 file2" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: scheduling file .file2. for addition +${PROG} [a-z]*: use .${PROG} commit. to add these files permanently" + dotest admin-6 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done +Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done" + dotest admin-7 "${testcvs} -q tag -b br" "T file1 +T file2" + dotest admin-8 "${testcvs} -q update -r br" "" + echo 'add a line on the branch' >> file1 + dotest admin-9 "${testcvs} -q ci -m modify-on-branch" \ +"Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done" + dotest admin-10 "${testcvs} -q update -A" "U file1" - ### 1d2mod + # Try to recurse with a numeric revision arg. + # If we wanted to comprehensive about this, we would also test + # this for -l, -u, and all the different -o syntaxes. + dotest_fail admin-10a "${testcvs} -q admin -b1.1.2" \ +"${PROG} [a-z]*: while processing more than one file: +${PROG} \[[a-z]* aborted\]: attempt to specify a numeric revision" + dotest_fail admin-10b "${testcvs} -q admin -m1.1:bogus file1 file2" \ +"${PROG} [a-z]*: while processing more than one file: +${PROG} \[[a-z]* aborted\]: attempt to specify a numeric revision" - dotest cvsadm-1d24 "${testcvs} co -d dir 1d2mod 1d2mod-2" \ -"${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2 -${PROG} [a-z]*: Updating dir/dir1d2-2 -U dir/dir1d2-2/file2-2" - dotest cvsadm-1d24b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d24d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-1d24f "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 1d2mod copy - dotest cvsadm-1d24h "cat dir/dir1d2-2/CVS/Repository" \ -"${AREP}mod2-2/sub2-2" - rm -rf CVS dir + # try a bad symbolic revision + dotest_fail admin-10c "${testcvs} -q admin -bBOGUS" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/file1,v: Symbolic name BOGUS is undefined. +${PROG} [a-z]*: cannot modify RCS file for .file1. +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/file2,v: Symbolic name BOGUS is undefined. +${PROG} [a-z]*: cannot modify RCS file for .file2." - dotest cvsadm-1d25 "${testcvs} co -d dir 1d2mod 2d1mod" \ -"${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2 -${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1" - dotest cvsadm-1d25b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d25d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-1d25f "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d1mod - dotest cvsadm-1d25h "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d25j "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + # Note that -s option applies to the new default branch, not + # the old one. + # Also note that the implementation of -a via "rcs" requires + # no space between -a and the argument. However, we expect + # to change that once CVS parses options. + dotest admin-11 "${testcvs} -q admin -afoo,bar -abaz \ +-b1.1.2 -cxx -U -sfoo file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + dotest admin-11a "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: 1\.1\.2 +locks: +access list: + foo + bar + baz +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +add +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 +modify-on-branch +=============================================================================" + dotest admin-12 "${testcvs} -q admin -bbr file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + dotest admin-12a "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: 1\.1\.2 +locks: +access list: + foo + bar + baz +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +add +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 +modify-on-branch +=============================================================================" - dotest cvsadm-1d26 "${testcvs} co -d dir 1d2mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2 -${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-1d26b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d26d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 1d2mod - dotest cvsadm-1d26f "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d2mod - dotest cvsadm-1d26h "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d26j "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # "cvs log" doesn't print the comment leader. RCS 5.7 will print + # the comment leader only if one specifies "-V4" to rlog. So it + # seems like the only way to test it is by looking at the RCS file + # directly. This also serves as a test of exporting RCS files + # (analogous to the import tests in "rcs"). + # Rather than try to write a rigorous check for whether the + # file CVS exports is legal, we just write a simpler + # test for what CVS actually exports, and figure we can revise + # the check as needed (within the confines of the RCS5 format as + # documented in RCSFILES). + dotest admin-13 "cat ${CVSROOT_DIRNAME}/first-dir/file1,v" \ +"head 1\.1; +branch 1\.1\.2; +access + foo + bar + baz; +symbols + br:1\.1\.0\.2; +locks; +comment @xx@; - # 2d1mod +1\.1 +date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state Exp; +branches + 1\.1\.2\.1; +next ; - dotest cvsadm-1d27 "${testcvs} co -d dir 2d1mod 2d1mod-2" \ -"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1 -${PROG} [a-z]*: Updating dir/dir2d1-2/sub2d1-2 -U dir/dir2d1-2/sub2d1-2/file1-2" - dotest cvsadm-1d27b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d27d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2d1mod - dotest cvsadm-1d27f "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d27h "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d1mod - dotest cvsadm-1d27j "cat dir/dir2d1-2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d27l "cat dir/dir2d1-2/sub2d1-2/CVS/Repository" \ -"${AREP}mod1-2" - rm -rf CVS dir +1\.1\.2\.1 +date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state foo; +branches; +next ; - dotest cvsadm-1d28 "${testcvs} co -d dir 2d1mod 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1 -${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-1d28b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d28d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2d1mod - dotest cvsadm-1d28f "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d28h "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - # the usual for 2d2mod - dotest cvsadm-1d28j "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d28l "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir - - # 2d2mod +desc +@@ - dotest cvsadm-1d29 "${testcvs} co -d dir 2d2mod 2d2mod-2" \ -"${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2 -${PROG} [a-z]*: Updating dir/dir2d2-2/sub2d2-2 -U dir/dir2d2-2/sub2d2-2/file2-2" - dotest cvsadm-1d29b "cat CVS/Repository" \ -"${AREP}\." - # the usual for the dir level - dotest cvsadm-1d29d "cat dir/CVS/Repository" \ -"${AREP}\." - # the usual for 2d2mod - dotest cvsadm-1d29f "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d29h "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - # the usual for 2d2mod - dotest cvsadm-1d29j "cat dir/dir2d2-2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-1d29l "cat dir/dir2d2-2/sub2d2-2/CVS/Repository" \ -"${AREP}mod2-2/sub2-2" - rm -rf CVS dir - ################################################## - ## And now, some of that again using the "-d" flag - ## on the command line, but use a longer path. - ################################################## +1\.1 +log +@add +@ +text +@@ - dotest cvsadm-2d3 "${testcvs} co -d dir/dir2 1mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file1" - dotest cvsadm-2d3b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d3d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d3f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir - dotest cvsadm-2d4 "${testcvs} co -d dir/dir2 2mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file2" - dotest cvsadm-2d4b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d4d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d4f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir +1\.1\.2\.1 +log +@modify-on-branch +@ +text +@a0 1 +add a line on the branch +@" + dotest admin-14 "${testcvs} -q admin -aauth3 -aauth2,foo \ +-soneone:1.1 -m1.1:changed-log-message -ntagone: file2" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +done" + dotest admin-15 "${testcvs} -q log file2" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Working file: file2 +head: 1\.1 +branch: +locks: strict +access list: + auth3 + auth2 + foo +symbolic names: + tagone: 1\.1 + br: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: oneone; +changed-log-message +=============================================================================" - dotest cvsadm-2d5 "${testcvs} co -d dir/dir2 1d1mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file1" - dotest cvsadm-2d5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d5d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d5f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + dotest admin-16 "${testcvs} -q admin \ +-A${CVSROOT_DIRNAME}/first-dir/file2,v -b -L -Nbr:1.1 file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + dotest admin-17 "${testcvs} -q log file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: + foo + bar + baz + auth3 + auth2 +symbolic names: + br: 1\.1 +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +add +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 +modify-on-branch +=============================================================================" - dotest cvsadm-2d6 "${testcvs} co -d dir/dir2 1d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file2" - dotest cvsadm-2d6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d6d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d6f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + dotest_fail admin-18 "${testcvs} -q admin -nbr:1.1.2 file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/file1,v: symbolic name br already bound to 1\.1 +${PROG} [a-z]*: cannot modify RCS file for .file1." + dotest admin-19 "${testcvs} -q admin -ebaz -ebar,auth3 -nbr file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + dotest admin-20 "${testcvs} -q log file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: + foo + auth2 +symbolic names: +keyword substitution: kv +total revisions: 2; selected revisions: 2 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +branches: 1\.1\.2; +add +---------------------------- +revision 1.1.2.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: foo; lines: ${PLUS}1 -0 +modify-on-branch +=============================================================================" - dotest cvsadm-2d7 "${testcvs} co -d dir/dir2 2d1mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file1" - dotest cvsadm-2d7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d7d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d7f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + # OK, this is starting to get ridiculous, in terms of + # testing a feature (access lists) which doesn't do anything + # useful, but what about nonexistent files and + # relative pathnames in admin -A? + dotest_fail admin-19a-nonexist \ +"${testcvs} -q admin -A${TESTDIR}/foo/bar file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +${PROG} [a-z]*: Couldn't open rcs file .${TESTDIR}/foo/bar.: No such file or directory +${PROG} \[[a-z]* aborted\]: cannot continue" - dotest cvsadm-2d8 "${testcvs} co -d dir/dir2 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2 -U dir/dir2/file2" - dotest cvsadm-2d8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-2d8d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-2d8f "cat dir/dir2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # In the remote case, we are cd'd off into the temp directory + # and so these tests give "No such file or directory" errors. + if test "x$remote" = xno; then - ################################################## - ## And now, a few of those tests revisited to - ## test the behavior of the -N flag. - ################################################## + dotest admin-19a-admin "${testcvs} -q admin -A../../cvsroot/first-dir/file2,v file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + dotest admin-19a-log "${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: + foo + auth2 + auth3 +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" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done" + fi # end of tests skipped for remote - dotest cvsadm-N3 "${testcvs} co -N 1mod" \ -"${PROG} [a-z]*: Updating 1mod -U 1mod/file1" - dotest cvsadm-N3b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N3d "cat 1mod/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS 1mod + # Add another revision to file2, so we can delete one. + echo 'add a line' >> file2 + dotest admin-21 "${testcvs} -q ci -m modify file2" \ +"Checking in file2; +${TESTDIR}/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.2; previous revision: 1\.1 +done" + dotest admin-22 "${testcvs} -q admin -o1.1 file2" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +deleting revision 1\.1 +done" + # Test admin -o. More variants that we could be testing: + # * REV: [on branch] + # * REV1:REV2 [deleting whole branch] + # * high branch numbers (e.g. 1.2.2.3.2.3) + # ... and probably others. See RCS_delete_revs for ideas. + + echo first rev > aaa + dotest admin-22-o1 "${testcvs} add aaa" \ +"${PROG} [a-z]*: scheduling file .aaa. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest admin-22-o2 "${testcvs} -q ci -m first aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +done +Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +initial revision: 1\.1 +done" + echo second rev >> aaa + dotest admin-22-o3 "${testcvs} -q ci -m second aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.2; previous revision: 1\.1 +done" + echo third rev >> aaa + dotest admin-22-o4 "${testcvs} -q ci -m third aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.3; previous revision: 1\.2 +done" + echo fourth rev >> aaa + dotest admin-22-o5 "${testcvs} -q ci -m fourth aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.4; previous revision: 1\.3 +done" + echo fifth rev >>aaa + dotest admin-22-o6 "${testcvs} -q ci -m fifth aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.5; previous revision: 1\.4 +done" + echo sixth rev >> aaa + dotest admin-22-o7 "${testcvs} -q ci -m sixth aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.6; previous revision: 1\.5 +done" + dotest admin-22-o8 "${testcvs} admin -l1.6 aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +1\.6 locked +done" + dotest admin-22-o9 "${testcvs} log -r1.6 aaa" " +RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +Working file: aaa +head: 1\.6 +branch: +locks: strict + ${username}: 1\.6 +access list: +symbolic names: +keyword substitution: kv +total revisions: 6; selected revisions: 1 +description: +---------------------------- +revision 1\.6 locked by: ${username}; +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +sixth +=============================================================================" + dotest_fail admin-22-o10 "${testcvs} admin -o1.5: aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/aaa,v: can't remove locked revision 1\.6 +${PROG} [a-z]*: cannot modify RCS file for .aaa." + dotest admin-22-o11 "${testcvs} admin -u aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +1\.6 unlocked +done" + dotest admin-22-o12 "${testcvs} admin -o1.5: aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +deleting revision 1\.6 +deleting revision 1\.5 +done" + dotest admin-22-o13 "${testcvs} log aaa" " +RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +Working file: aaa +head: 1\.4 +branch: +locks: strict +access list: +symbolic names: +keyword substitution: kv +total revisions: 4; selected revisions: 4 +description: +---------------------------- +revision 1\.4 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +fourth +---------------------------- +revision 1\.3 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +third +---------------------------- +revision 1\.2 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +second +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +first +=============================================================================" - dotest cvsadm-N4 "${testcvs} co -N 2mod" \ -"${PROG} [a-z]*: Updating 2mod -U 2mod/file2" - dotest cvsadm-N4b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N4d "cat 2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS 2mod + dotest admin-22-o14 "${testcvs} tag -b -r1.3 br1 aaa" "T aaa" + dotest admin-22-o15 "${testcvs} update -rbr1 aaa" "U aaa" + echo new branch rev >> aaa + dotest admin-22-o16 "${testcvs} ci -m new-branch aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.3\.2\.1; previous revision: 1\.3 +done" + dotest_fail admin-22-o17 "${testcvs} admin -o1.2:1.4 aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +deleting revision 1\.4 +${PROG} [a-z]*: ${TESTDIR}/cvsroot/first-dir/aaa,v: can't remove branch point 1\.3 +${PROG} [a-z]*: cannot modify RCS file for .aaa." + dotest admin-22-o18 "${testcvs} update -p -r1.4 aaa" \ +"=================================================================== +Checking out aaa +RCS: ${TESTDIR}/cvsroot/first-dir/aaa,v +VERS: 1\.4 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +first rev +second rev +third rev +fourth rev" + echo second branch rev >> aaa + dotest admin-22-o19 "${testcvs} ci -m branch-two aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1 +done" + echo third branch rev >> aaa + dotest admin-22-o20 "${testcvs} ci -m branch-three aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.3\.2\.3; previous revision: 1\.3\.2\.2 +done" + echo fourth branch rev >> aaa + dotest admin-22-o21 "${testcvs} ci -m branch-four aaa" \ +"Checking in aaa; +${TESTDIR}/cvsroot/first-dir/aaa,v <-- aaa +new revision: 1\.3\.2\.4; previous revision: 1\.3\.2\.3 +done" + dotest admin-22-o22 "${testcvs} admin -o:1.3.2.3 aaa" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +deleting revision 1\.3\.2\.1 +deleting revision 1\.3\.2\.2 +deleting revision 1\.3\.2\.3 +done" + dotest admin-22-o23 "${testcvs} log aaa" " +RCS file: ${TESTDIR}/cvsroot/first-dir/aaa,v +Working file: aaa +head: 1\.4 +branch: +locks: strict +access list: +symbolic names: + br1: 1\.3\.0\.2 +keyword substitution: kv +total revisions: 5; selected revisions: 5 +description: +---------------------------- +revision 1\.4 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +fourth +---------------------------- +revision 1\.3 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +branches: 1\.3\.2; +third +---------------------------- +revision 1\.2 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 +second +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +first +---------------------------- +revision 1\.3\.2\.4 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}4 -0 +branch-four +=============================================================================" - dotest cvsadm-N5 "${testcvs} co -N 1d1mod" \ -"${PROG} [a-z]*: Updating dir1d1 -U dir1d1/file1" - dotest cvsadm-N5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N5d "cat dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir1d1 + dotest admin-22-o24 "${testcvs} -q update -p -r 1.3.2.4 aaa" \ +"first rev +second rev +third rev +new branch rev +second branch rev +third branch rev +fourth branch rev" - dotest cvsadm-N6 "${testcvs} co -N 1d2mod" \ -"${PROG} [a-z]*: Updating dir1d2 -U dir1d2/file2" - dotest cvsadm-N6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N6d "cat dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir1d2 + # The bit here about how there is a "tagone" tag pointing to + # a nonexistent revision is documented by rcs. I dunno, I + # wonder whether the "cvs admin -o" should give a warning in + # this case. + dotest admin-23 "${testcvs} -q log file2" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file2,v +Working file: file2 +head: 1\.2 +branch: +locks: strict +access list: + auth3 + auth2 + foo +symbolic names: + tagone: 1\.1 + br: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +---------------------------- +revision 1\.2 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +modify +=============================================================================" - dotest cvsadm-N7 "${testcvs} co -N 2d1mod" \ -"${PROG} [a-z]*: Updating dir2d1/sub2d1 -U dir2d1/sub2d1/file1" - dotest cvsadm-N7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N7d "cat dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N7f "cat dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir2d1 + dotest admin-25 "cat ${CVSROOT_DIRNAME}/first-dir/file1,v" \ +"head 1\.1; +access + foo + auth2; +symbols; +locks; strict; +comment @xx@; - dotest cvsadm-N8 "${testcvs} co -N 2d2mod" \ -"${PROG} [a-z]*: Updating dir2d2/sub2d2 -U dir2d2/sub2d2/file2" - dotest cvsadm-N8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N8d "cat dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N8f "cat dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir2d2 - ## the ones in one-deep directories +1\.1 +date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state Exp; +branches + 1\.1\.2\.1; +next ; - dotest cvsadm-N1d3 "${testcvs} co -N -d dir 1mod" \ -"${PROG} [a-z]*: Updating dir/1mod -U dir/1mod/file1" - dotest cvsadm-N1d3b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d3d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d3f "cat dir/1mod/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir +1\.1\.2\.1 +date [0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]; author ${username}; state foo; +branches; +next ; - dotest cvsadm-N1d4 "${testcvs} co -N -d dir 2mod" \ -"${PROG} [a-z]*: Updating dir/2mod -U dir/2mod/file2" - dotest cvsadm-N1d4b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d4d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d4f "cat dir/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir - dotest cvsadm-N1d5 "${testcvs} co -N -d dir 1d1mod" \ -"${PROG} [a-z]*: Updating dir/dir1d1 -U dir/dir1d1/file1" - dotest cvsadm-N1d5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d5d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d5d "cat dir/dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir +desc +@@ - dotest cvsadm-N1d6 "${testcvs} co -N -d dir 1d2mod" \ -"${PROG} [a-z]*: Updating dir/dir1d2 -U dir/dir1d2/file2" - dotest cvsadm-N1d6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d6d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d6f "cat dir/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir - dotest cvsadm-N1d7 "${testcvs} co -N -d dir 2d1mod" \ -"${PROG} [a-z]*: Updating dir/dir2d1/sub2d1 -U dir/dir2d1/sub2d1/file1" - dotest cvsadm-N1d7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d7d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d7f "cat dir/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N1d7h "cat dir/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir +1\.1 +log +@add +@ +text +@@ - dotest cvsadm-N1d8 "${testcvs} co -N -d dir 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2d2/sub2d2 -U dir/dir2d2/sub2d2/file2" - dotest cvsadm-N1d8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d8d "cat dir/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N1d8d "cat dir/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N1d8d "cat dir/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir - ## the ones in two-deep directories +1\.1\.2\.1 +log +@modify-on-branch +@ +text +@a0 1 +add a line on the branch +@" + + cd ../.. + rm -r 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; - dotest cvsadm-N2d3 "${testcvs} co -N -d dir/dir2 1mod" \ -"${PROG} [a-z]*: Updating dir/dir2/1mod -U dir/dir2/1mod/file1" - dotest cvsadm-N2d3b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d3d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d3f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d3h "cat dir/dir2/1mod/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + reserved) + # Tests of reserved checkouts. Eventually this will test + # rcslock.pl (or equivalent) and all kinds of stuff. Right + # now it just does some very basic checks on cvs admin -u + # and cvs admin -l. + # Also should test locking on a branch (and making sure that + # locks from one branch don't get mixed up with those from + # another. Both the case where one of the branches is the + # main branch, and in which neither one is). + # See also test keyword, which tests that keywords and -kkvl + # do the right thing in the presence of locks. - dotest cvsadm-N2d4 "${testcvs} co -N -d dir/dir2 2mod" \ -"${PROG} [a-z]*: Updating dir/dir2/2mod -U dir/dir2/2mod/file2" - dotest cvsadm-N2d4b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d4d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d4f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d4h "cat dir/dir2/2mod/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # The usual setup, directory first-dir containing file file1. + mkdir 1; cd 1 + dotest reserved-1 "${testcvs} -q co -l ." '' + mkdir first-dir + dotest reserved-2 "${testcvs} add first-dir" \ +"Directory ${TESTDIR}/cvsroot/first-dir added to the repository" + cd first-dir + touch file1 + dotest reserved-3 "${testcvs} add file1" \ +"${PROG} [a-z]*: scheduling file .file1. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest reserved-4 "${testcvs} -q ci -m add" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +done +Checking in file1; +${TESTDIR}/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done" - dotest cvsadm-N2d5 "${testcvs} co -N -d dir/dir2 1d1mod" \ -"${PROG} [a-z]*: Updating dir/dir2/dir1d1 -U dir/dir2/dir1d1/file1" - dotest cvsadm-N2d5b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d5d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d5f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d5h "cat dir/dir2/dir1d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + dotest reserved-5 "${testcvs} -q admin -l file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +1\.1 locked +done" + dotest reserved-6 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict + ${username}: 1\.1 +access list: +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +---------------------------- +revision 1\.1 locked by: ${username}; +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +add +=============================================================================" - dotest cvsadm-N2d6 "${testcvs} co -N -d dir/dir2 1d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2/dir1d2 -U dir/dir2/dir1d2/file2" - dotest cvsadm-N2d6b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d6d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d6f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d6h "cat dir/dir2/dir1d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # Note that this just tests the owner of the lock giving + # it up. It doesn't test breaking a lock. + dotest reserved-7 "${testcvs} -q admin -u file1" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +1\.1 unlocked +done" - dotest cvsadm-N2d7 "${testcvs} co -N -d dir/dir2 2d1mod" \ -"${PROG} [a-z]*: Updating dir/dir2/dir2d1/sub2d1 -U dir/dir2/dir2d1/sub2d1/file1" - dotest cvsadm-N2d7b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d7d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d7f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d7f "cat dir/dir2/dir2d1/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d7h "cat dir/dir2/dir2d1/sub2d1/CVS/Repository" \ -"${AREP}mod1" - rm -rf CVS dir + dotest reserved-8 "${testcvs} log -N file1" " +RCS file: ${TESTDIR}/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +keyword substitution: kv +total revisions: 1; selected revisions: 1 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; +add +=============================================================================" - dotest cvsadm-N2d8 "${testcvs} co -N -d dir/dir2 2d2mod" \ -"${PROG} [a-z]*: Updating dir/dir2/dir2d2/sub2d2 -U dir/dir2/dir2d2/sub2d2/file2" - dotest cvsadm-N2d8b "cat CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d8d "cat dir/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d8f "cat dir/dir2/CVS/Repository" \ -"${AREP}\." - dotest cvsadm-N2d8h "cat dir/dir2/dir2d2/CVS/Repository" \ -"${AREP}CVSROOT/Emptydir" - dotest cvsadm-N2d8j "cat dir/dir2/dir2d2/sub2d2/CVS/Repository" \ -"${AREP}mod2/sub2" - rm -rf CVS dir + # rcslock.pl tests. Of course, the point isn't to test + # rcslock.pl from the distribution but equivalent + # functionality (for example, many sites may have an old + # rcslock.pl). The functionality of this hook falls + # short of the real rcslock.pl though. + # Note that we can use rlog or look at the RCS file directly, + # but we can't use "cvs log" because "cvs commit" has a lock. - ################################################## - ## That's enough of that, thank you very much. - ################################################## + cat >${TESTDIR}/lockme < a-lock + dotest reserved-9 "${testcvs} add a-lock" \ +"${PROG} [a-z]*: scheduling file .a-lock. for addition +${PROG} [a-z]*: use .${PROG} commit. to add this file permanently" + dotest reserved-10 "${testcvs} -q ci -m new a-lock" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/a-lock,v +done +Checking in a-lock; +${TESTDIR}/cvsroot/first-dir/a-lock,v <-- a-lock +initial revision: 1\.1 +done" + # FIXME: the contents of CVSROOT fluctuate a lot + # here. Maybe the expect pattern should just + # confirm that commitinfo is one of the files checked out, + # but for now we just check that CVS exited with success. cd .. - rm -rf 1 - rm -rf ${CVSROOT_DIRNAME}/1mod - rm -rf ${CVSROOT_DIRNAME}/1mod-2 - rm -rf ${CVSROOT_DIRNAME}/2mod - rm -rf ${CVSROOT_DIRNAME}/2mod-2 + if ${testcvs} -q co CVSROOT >>${LOGFILE} ; then + pass reserved-11 + else + fail reserved-11 + fi + cd CVSROOT + echo "DEFAULT ${TESTDIR}/lockme" >>commitinfo + dotest reserved-12 "${testcvs} -q ci -m rcslock commitinfo" \ +"Checking in commitinfo; +${TESTDIR}/cvsroot/CVSROOT/commitinfo,v <-- commitinfo +new revision: 1\.2; previous revision: 1\.1 +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd ..; cd first-dir + + # Simulate (approximately) what a-lock would look like + # if someone else had locked revision 1.1. + sed -e 's/locks; strict;/locks fred:1.1; strict;/' ${TESTDIR}/cvsroot/first-dir/a-lock,v > a-lock,v + chmod 644 ${TESTDIR}/cvsroot/first-dir/a-lock,v + dotest reserved-13 "mv a-lock,v ${TESTDIR}/cvsroot/first-dir/a-lock,v" + chmod 444 ${TESTDIR}/cvsroot/first-dir/a-lock,v + echo more stuff >> a-lock + dotest_fail reserved-13b "${testcvs} ci -m '' a-lock" \ +"fred has file a-lock locked for version 1\.1 +${PROG} [a-z]*: Pre-commit check failed +${PROG} \[[a-z]* aborted\]: correct above errors first!" + + dotest reserved-14 "${testcvs} admin -u1.1 a-lock" \ +"RCS file: ${TESTDIR}/cvsroot/first-dir/a-lock,v +1\.1 unlocked +done" + dotest reserved-15 "${testcvs} -q ci -m success a-lock" \ +"Checking in a-lock; +${TESTDIR}/cvsroot/first-dir/a-lock,v <-- a-lock +new revision: 1\.2; previous revision: 1\.1 +done" + + # undo commitinfo changes + cd ../CVSROOT + echo '# vanilla commitinfo' >commitinfo + dotest reserved-16 "${testcvs} -q ci -m back commitinfo" \ +"Checking in commitinfo; +${TESTDIR}/cvsroot/CVSROOT/commitinfo,v <-- commitinfo +new revision: 1\.3; previous revision: 1\.2 +done +${PROG} [a-z]*: Rebuilding administrative file database" + cd ..; rm -r CVSROOT; cd first-dir + + cd ../.. + rm -r 1 + rm ${TESTDIR}/lockme + rm -rf ${CVSROOT_DIRNAME}/first-dir ;; diffmerge1) @@ -13439,6 +14027,9 @@ d472 12 dotest diffmerge2_diff \ "${testcvs} diff -r Review_V1p3 sgrid.h" '' + cd .. + rm -rf diffmerge2 + rm -rf ${CVSROOT_DIRNAME}/diffmerge2 ;; *) diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index a833415..a005646 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -1194,7 +1194,7 @@ serve_modified (arg) } { - int status = change_mode (arg, mode_text); + int status = change_mode (arg, mode_text, 0); free (mode_text); if (status) { @@ -3355,12 +3355,13 @@ server_modtime (finfo, vers_ts) /* See server.h for description. */ void -server_updated (finfo, vers, updated, file_info, checksum) +server_updated (finfo, vers, updated, mode, checksum, filebuf) struct file_info *finfo; Vers_TS *vers; enum server_updated_arg4 updated; - struct stat *file_info; + mode_t mode; unsigned char *checksum; + struct buffer *filebuf; { if (noexec) { @@ -3379,25 +3380,43 @@ server_updated (finfo, vers, updated, file_info, checksum) if (entries_line != NULL && scratched_file == NULL) { FILE *f; - struct stat sb; struct buffer_data *list, *last; unsigned long size; char size_text[80]; - if ( CVS_STAT (finfo->file, &sb) < 0) + if (filebuf != NULL) { - if (existence_error (errno)) + size = buf_length (filebuf); + if (mode == (mode_t) -1) + error (1, 0, "\ +CVS server internal error: no mode in server_updated"); + } + else + { + struct stat sb; + + if ( CVS_STAT (finfo->file, &sb) < 0) { - /* - * If we have a sticky tag for a branch on which the - * file is dead, and cvs update the directory, it gets - * a T_CHECKOUT but no file. So in this case just - * forget the whole thing. */ - free (entries_line); - entries_line = NULL; - goto done; + if (existence_error (errno)) + { + /* If we have a sticky tag for a branch on which + the file is dead, and cvs update the directory, + it gets a T_CHECKOUT but no file. So in this + case just forget the whole thing. */ + free (entries_line); + entries_line = NULL; + goto done; + } + error (1, errno, "reading %s", finfo->fullname); + } + size = sb.st_size; + if (mode == (mode_t) -1) + { + /* FIXME: When we check out files the umask of the + server (set in .bashrc if rsh is in use) affects + what mode we send, and it shouldn't. */ + mode = sb.st_mode; } - error (1, errno, "reading %s", finfo->fullname); } if (checksum != NULL) @@ -3466,21 +3485,14 @@ server_updated (finfo, vers, updated, file_info, checksum) { char *mode_string; - /* FIXME: When we check out files the umask of the server - (set in .bashrc if rsh is in use) affects what mode we - send, and it shouldn't. */ - if (file_info != NULL) - mode_string = mode_to_string (file_info->st_mode); - else - mode_string = mode_to_string (sb.st_mode); + mode_string = mode_to_string (mode); buf_output0 (protocol, mode_string); buf_output0 (protocol, "\n"); free (mode_string); } list = last = NULL; - size = 0; - if (sb.st_size > 0) + if (size > 0) { /* Throughout this section we use binary mode to read the file we are sending. The client handles any line ending @@ -3493,11 +3505,19 @@ server_updated (finfo, vers, updated, file_info, checksum) * might be computable somehow; using 100 here is just * a first approximation. */ - && sb.st_size > 100) + && size > 100) { int status, fd, gzip_status; pid_t gzip_pid; + /* Callers must avoid passing us a buffer if + file_gzip_level is set. We could handle this case, + but it's not worth it since this case never arises + with a current client and server. */ + if (filebuf != NULL) + error (1, 0, "\ +CVS server internal error: unhandled case in server_updated"); + fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0); if (fd < 0) error (1, errno, "reading %s", finfo->fullname); @@ -3520,15 +3540,14 @@ server_updated (finfo, vers, updated, file_info, checksum) /* Prepending length with "z" is flag for using gzip here. */ buf_output0 (protocol, "z"); } - else + else if (filebuf == NULL) { long status; - size = sb.st_size; f = CVS_FOPEN (finfo->file, "rb"); if (f == NULL) error (1, errno, "reading %s", finfo->fullname); - status = buf_read_file (f, sb.st_size, &list, &last); + status = buf_read_file (f, size, &list, &last); if (status == -2) (*protocol->memory_error) (protocol); else if (status != 0) @@ -3542,7 +3561,13 @@ server_updated (finfo, vers, updated, file_info, checksum) sprintf (size_text, "%lu\n", size); buf_output0 (protocol, size_text); - buf_append_data (protocol, list, last); + if (filebuf == NULL) + buf_append_data (protocol, list, last); + else + { + buf_append_buffer (protocol, filebuf); + buf_free (filebuf); + } /* Note we only send a newline here if the file ended with one. */ /* @@ -3555,6 +3580,7 @@ server_updated (finfo, vers, updated, file_info, checksum) if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED || updated == SERVER_RCS_DIFF) + && filebuf != NULL /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) @@ -5608,7 +5634,7 @@ this client does not support writing binary files to stdout"); I assume that what they are talking about can also be helped by flushing the stream before changing the mode. */ fflush (stdout); - oldmode = _setmode (_fileno (stdout), _O_BINARY); + oldmode = _setmode (_fileno (stdout), OPEN_BINARY); if (oldmode < 0) error (0, errno, "failed to setmode on stdout"); #endif @@ -5623,7 +5649,7 @@ this client does not support writing binary files to stdout"); } #ifdef USE_SETMODE_STDOUT fflush (stdout); - if (_setmode (_fileno (stdout), oldmode) != _O_BINARY) + if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY) error (0, errno, "failed to setmode on stdout"); #endif } diff --git a/contrib/cvs/src/server.h b/contrib/cvs/src/server.h index e5a3c3f..f94b7aa 100644 --- a/contrib/cvs/src/server.h +++ b/contrib/cvs/src/server.h @@ -57,16 +57,19 @@ extern void server_checked_in extern void server_copy_file PROTO((char *file, char *update_dir, char *repository, char *newfile)); -/* Send the appropriate responses for a file described by FILE, - UPDATE_DIR, REPOSITORY, and VERS. FILE_INFO is the result of - statting the file, or NULL if it hasn't been statted yet. This is - called after server_register or server_scratch. In the latter case - the file is to be removed (and vers can be NULL). In the former - case, vers must be non-NULL, and UPDATED indicates whether the file - is now up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, - SERVER_PATCHED, yes, but file is a diff from user version to - repository version, SERVER_RCS_DIFF, yes, like SERVER_PATCHED but - with an RCS style diff). */ +/* Send the appropriate responses for a file described by FINFO and + VERS. This is called after server_register or server_scratch. In + the latter case the file is to be removed (and VERS can be NULL). + In the former case, VERS must be non-NULL, and UPDATED indicates + whether the file is now up to date (SERVER_UPDATED, yes, + SERVER_MERGED, no, SERVER_PATCHED, yes, but file is a diff from + user version to repository version, SERVER_RCS_DIFF, yes, like + SERVER_PATCHED but with an RCS style diff). MODE is the mode the + file should get, or (mode_t) -1 if this should be obtained from the + file itself. CHECKSUM is the MD5 checksum of the file, or NULL if + this need not be sent. If FILEBUF is not NULL, it holds the + contents of the file, in which case the file itself may not exist. + If FILEBUF is not NULL, server_updated will free it. */ enum server_updated_arg4 { SERVER_UPDATED, @@ -74,10 +77,14 @@ enum server_updated_arg4 SERVER_PATCHED, SERVER_RCS_DIFF }; +#ifdef __STDC__ +struct buffer; +#endif + extern void server_updated PROTO((struct file_info *finfo, Vers_TS *vers, - enum server_updated_arg4 updated, struct stat *, - unsigned char *checksum)); + enum server_updated_arg4 updated, mode_t mode, + unsigned char *checksum, struct buffer *filebuf)); /* Whether we should send RCS format patches. */ extern int server_use_rcs_diff PROTO((void)); diff --git a/contrib/cvs/src/subr.c b/contrib/cvs/src/subr.c index 61b3103..07d516f 100644 --- a/contrib/cvs/src/subr.c +++ b/contrib/cvs/src/subr.c @@ -288,18 +288,13 @@ increment_revnum (rev) /* Return the username by which the caller should be identified in CVS, in contexts such as the author field of RCS files, various - logs, etc. - - Returns a pointer to storage that we manage; it is good until the - next call to getcaller () (provided that the caller doesn't call - getlogin () or some such themself). */ + logs, etc. */ char * getcaller () { #ifndef SYSTEM_GETCALLER - static char uidname[20]; + static char *cache; struct passwd *pw; - char *name; uid_t uid; #endif @@ -316,20 +311,32 @@ getcaller () try LOGNAME USER or getlogin(). If getlogin() and getpwuid() both fail, return the uid as a string. */ + if (cache != NULL) + return cache; + uid = getuid (); if (uid == (uid_t) 0) { + char *name; + /* super-user; try getlogin() to distinguish */ if (((name = getlogin ()) || (name = getenv("LOGNAME")) || (name = getenv("USER"))) && *name) - return (name); + { + cache = xstrdup (name); + return cache; + } } if ((pw = (struct passwd *) getpwuid (uid)) == NULL) { + char uidname[20]; + (void) sprintf (uidname, "uid%lu", (unsigned long) uid); - return (uidname); + cache = xstrdup (uidname); + return cache; } - return (pw->pw_name); + cache = xstrdup (pw->pw_name); + return cache; #endif } @@ -608,8 +615,16 @@ get_file (name, fullname, mode, buf, bufsize, len) } else { - if (CVS_STAT (name, &s) < 0) + if (CVS_LSTAT (name, &s) < 0) error (1, errno, "can't stat %s", fullname); + + /* Don't attempt to read special files or symlinks. */ + if (!S_ISREG (s.st_mode)) + { + *len = 0; + return; + } + /* Convert from signed to unsigned. */ filesize = s.st_size; diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index 48bed3b..2e7bda3 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -42,9 +42,14 @@ #include "fileattr.h" #include "edit.h" #include "getline.h" +#include "buffer.h" +#include "hardlink.h" static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts, - int adding)); + int adding, int merging, int update_server)); +#ifdef SERVER_SUPPORT +static void checkout_to_buffer PROTO ((void *, const char *, size_t)); +#endif #ifdef SERVER_SUPPORT static int patch_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts, @@ -64,6 +69,9 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *)); static int update_filesdone_proc PROTO ((void *callerdat, int err, char *repository, char *update_dir, List *entries)); +#ifdef PRESERVE_PERMISSIONS_SUPPORT +static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *)); +#endif static void write_letter PROTO ((struct file_info *finfo, int letter)); static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts)); @@ -79,6 +87,10 @@ static char *date = NULL; static int rewrite_tag; static int nonbranch; +/* If we set the tag or date for a subdirectory, we use this to undo + the setting. See update_dirent_proc. */ +static char *tag_update_dir; + static char *join_rev1, *date_rev1; static char *join_rev2, *date_rev2; static int aflag = 0; @@ -437,6 +449,32 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, else date_rev2 = (char *) NULL; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (preserve_perms) + { + /* We need to do an extra recursion, bleah. It's to make sure + that we know as much as possible about file linkage. */ + hardlist = getlist(); + working_dir = xgetwd(); /* save top-level working dir */ + + /* FIXME-twp: the arguments to start_recursion make me dizzy. This + function call was copied from the update_fileproc call that + follows it; someone should make sure that I did it right. */ + err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, which, aflag, 1, + preload_update_dir, 1); + if (err) + return (err); + + /* FIXME-twp: at this point we should walk the hardlist + and update the `links' field of each hardlink_info struct + to list the files that are linked on dist. That would make + it easier & more efficient to compare the disk linkage with + the repository linkage (a simple strcmp). */ + } +#endif + /* call the recursion processor */ err = start_recursion (update_fileproc, update_filesdone_proc, update_dirent_proc, update_dirleave_proc, NULL, @@ -456,6 +494,50 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, return (err); } +#ifdef PRESERVE_PERMISSIONS_SUPPORT +/* + * The get_linkinfo_proc callback adds each file to the hardlist + * (see hardlink.c). + */ + +static int +get_linkinfo_proc (callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + char *fullpath; + Node *linkp; + struct hardlink_info *hlinfo; + + /* Get the full pathname of the current file. */ + fullpath = xmalloc (strlen(working_dir) + + strlen(finfo->fullname) + 2); + sprintf (fullpath, "%s/%s", working_dir, finfo->fullname); + + /* To permit recursing into subdirectories, files + are keyed on the full pathname and not on the basename. */ + linkp = lookup_file_by_inode (fullpath); + if (linkp == NULL) + { + /* The file isn't on disk; we are probably restoring + a file that was removed. */ + return 0; + } + + /* Create a new, empty hardlink_info node. */ + hlinfo = (struct hardlink_info *) + xmalloc (sizeof (struct hardlink_info)); + + hlinfo->status = (Ctype) 0; /* is this dumb? */ + hlinfo->checked_out = 0; + hlinfo->links = NULL; + + linkp->data = (char *) hlinfo; + + return 0; +} +#endif + /* * This is the callback proc for update. It is called for each file in each * directory by the recursion code. The current directory is the local @@ -526,7 +608,7 @@ update_fileproc (callerdat, finfo) #ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ #endif - retval = checkout_file (finfo, vers, 0); + retval = checkout_file (finfo, vers, 0, 0, 0); break; default: /* can't ever happen :-) */ @@ -629,7 +711,8 @@ update_fileproc (callerdat, finfo) (rcs_diff_patches ? SERVER_RCS_DIFF : SERVER_PATCHED), - &file_info, checksum); + file_info.st_mode, checksum, + (struct buffer *) NULL); break; } } @@ -639,13 +722,7 @@ update_fileproc (callerdat, finfo) /* Fall through. */ #endif case T_CHECKOUT: /* needs checkout */ - retval = checkout_file (finfo, vers, 0); -#ifdef SERVER_SUPPORT - if (server_active && retval == 0) - server_updated (finfo, vers, - SERVER_UPDATED, (struct stat *) NULL, - (unsigned char *) NULL); -#endif + retval = checkout_file (finfo, vers, 0, 0, 1); break; case T_ADDED: /* added but not committed */ write_letter (finfo, 'A'); @@ -663,8 +740,9 @@ update_fileproc (callerdat, finfo) if (vers->ts_user == NULL) server_scratch_entry_only (); server_updated (finfo, vers, - SERVER_UPDATED, (struct stat *) NULL, - (unsigned char *) NULL); + SERVER_UPDATED, (mode_t) -1, + (unsigned char *) NULL, + (struct buffer *) NULL); } #endif break; @@ -806,6 +884,26 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) else { /* otherwise, create the dir and appropriate adm files */ + + /* If no tag or date were specified on the command line, + and we're not using -A, we want the subdirectory to use + the tag and date, if any, of the current directory. + That way, update -d will work correctly when working on + a branch. + + We use TAG_UPDATE_DIR to undo the tag setting in + update_dirleave_proc. If we did not do this, we would + not correctly handle a working directory with multiple + tags (and maybe we should prohibit such working + directories, but they work now and we shouldn't make + them stop working without more thought). */ + if ((tag == NULL && date == NULL) && ! aflag) + { + ParseTag (&tag, &date, &nonbranch); + if (tag != NULL || date != NULL) + tag_update_dir = xstrdup (update_dir); + } + make_directory (dir); Create_Admin (dir, update_dir, repository, tag, date, /* This is a guess. We will rewrite it later @@ -897,6 +995,27 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) { FILE *fp; + /* If we set the tag or date for a new subdirectory in + update_dirent_proc, and we're now done with that subdirectory, + undo the tag/date setting. Note that we know that the tag and + date were both originally NULL in this case. */ + if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0) + { + if (tag != NULL) + { + free (tag); + tag = NULL; + } + if (date != NULL) + { + free (date); + date = NULL; + } + nonbranch = 0; + free (tag_update_dir); + tag_update_dir = NULL; + } + /* run the update_prog if there is one */ /* FIXME: should be checking for errors from CVS_FOPEN and printing them if not existence_error. */ @@ -1016,7 +1135,7 @@ isemptydir (dir, might_not_exist) if (CVS_CHDIR (dir) < 0) error (1, errno, "cannot change directory to %s", dir); - l = Entries_Open (0); + l = Entries_Open (0, NULL); files_removed = walklist (l, isremoved, 0); Entries_Close (l); @@ -1063,22 +1182,29 @@ scratch_file (finfo) * Check out a file. */ static int -checkout_file (finfo, vers_ts, adding) +checkout_file (finfo, vers_ts, adding, merging, update_server) struct file_info *finfo; Vers_TS *vers_ts; int adding; + int merging; + int update_server; { char *backup; int set_time, retval = 0; - int retcode = 0; int status; int file_is_dead; + struct buffer *revbuf; - /* Solely to suppress a warning from gcc -Wall. */ backup = NULL; + revbuf = NULL; - /* don't screw with backup files if we're going to stdout */ - if (!pipeout) + /* Don't screw with backup files if we're going to stdout, or if + we are the server. */ + if (!pipeout +#ifdef SERVER_SUPPORT + && ! server_active +#endif + ) { backup = xmalloc (strlen (finfo->file) + sizeof (CVSADM) @@ -1088,6 +1214,7 @@ checkout_file (finfo, vers_ts, adding) if (isfile (finfo->file)) rename_file (finfo->file, backup); else + { /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ if (unlink_file_dir (backup) < 0) @@ -1097,6 +1224,9 @@ checkout_file (finfo, vers_ts, adding) /* FIXME: should include update_dir in message. */ error (0, errno, "error removing %s", backup); } + free (backup); + backup = NULL; + } } file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); @@ -1124,22 +1254,64 @@ VERS: ", 0); } } - status = RCS_checkout (vers_ts->srcfile, - pipeout ? NULL : finfo->file, - vers_ts->vn_rcs, vers_ts->vn_tag, - vers_ts->options, RUN_TTY, - (RCSCHECKOUTPROC) NULL, (void *) NULL); +#ifdef SERVER_SUPPORT + if (update_server + && server_active + && ! pipeout + && ! file_gzip_level + && ! joining () + && ! wrap_name_has (finfo->file, WRAP_FROMCVS)) + { + revbuf = buf_nonio_initialize ((BUFMEMERRPROC) NULL); + status = RCS_checkout (vers_ts->srcfile, (char *) NULL, + vers_ts->vn_rcs, vers_ts->vn_tag, + vers_ts->options, RUN_TTY, + checkout_to_buffer, revbuf); + } + else +#endif + status = RCS_checkout (vers_ts->srcfile, + pipeout ? NULL : finfo->file, + vers_ts->vn_rcs, vers_ts->vn_tag, + vers_ts->options, RUN_TTY, + (RCSCHECKOUTPROC) NULL, (void *) NULL); } if (file_is_dead || status == 0) { + mode_t mode; + + mode = (mode_t) -1; + if (!pipeout) { Vers_TS *xvers_ts; + if (revbuf != NULL) + { + struct stat sb; + + /* FIXME: We should have RCS_checkout return the mode. */ + if (stat (vers_ts->srcfile->path, &sb) < 0) + error (1, errno, "cannot stat %s", + vers_ts->srcfile->path); + mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH); + } + if (cvswrite && !file_is_dead && !fileattr_get (finfo->file, "_watched")) - xchmod (finfo->file, 1); + { + if (revbuf == NULL) + xchmod (finfo->file, 1); + else + { + /* We know that we are the server here, so + although xchmod checks umask, we don't bother. */ + mode |= (((mode & S_IRUSR) ? S_IWUSR : 0) + | ((mode & S_IRGRP) ? S_IWGRP : 0) + | ((mode & S_IROTH) ? S_IWOTH : 0)); + } + } { /* A newly checked out file is never under the spell @@ -1171,6 +1343,27 @@ VERS: ", 0); if (strcmp (xvers_ts->options, "-V4") == 0) xvers_ts->options[0] = '\0'; + if (revbuf != NULL) + { + /* If we stored the file data into a buffer, then we + didn't create a file at all, so xvers_ts->ts_user + is wrong. The correct value is to have it be the + same as xvers_ts->ts_rcs, meaning that the working + file is unchanged from the RCS file. + + FIXME: We should tell Version_TS not to waste time + statting the nonexistent file. + + FIXME: Actually, I don't think the ts_user value + matters at all here. The only use I know of is + that it is printed in a trace message by + Server_Register. */ + + if (xvers_ts->ts_user != NULL) + free (xvers_ts->ts_user); + xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs); + } + (void) time (&last_register_time); if (file_is_dead) @@ -1179,7 +1372,7 @@ VERS: ", 0); { error (0, 0, "warning: %s is not (any longer) pertinent", - finfo->fullname); + finfo->fullname); } Scratch_Entry (finfo->entries, finfo->file); #ifdef SERVER_SUPPORT @@ -1226,21 +1419,29 @@ VERS: ", 0); write_letter (finfo, 'U'); } } + +#ifdef SERVER_SUPPORT + if (update_server && server_active) + server_updated (finfo, vers_ts, + merging ? SERVER_MERGED : SERVER_UPDATED, + mode, (unsigned char *) NULL, revbuf); +#endif } else { - int old_errno = errno; /* save errno value over the rename */ - - if (!pipeout && isfile (backup)) + if (backup != NULL) + { rename_file (backup, finfo->file); + free (backup); + backup = NULL; + } - error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, - "could not check out %s", finfo->fullname); + error (0, 0, "could not check out %s", finfo->fullname); - retval = retcode; + retval = status; } - if (!pipeout) + if (backup != NULL) { /* If -f/-t wrappers are being used to wrap up a directory, then backup might be a directory instead of just a file. */ @@ -1259,6 +1460,24 @@ VERS: ", 0); #ifdef SERVER_SUPPORT +/* This function is used to write data from a file being checked out + into a buffer. */ + +static void +checkout_to_buffer (callerdat, data, len) + void *callerdat; + const char *data; + size_t len; +{ + struct buffer *buf = (struct buffer *) callerdat; + + buf_output (buf, data, len); +} + +#endif /* SERVER_SUPPORT */ + +#ifdef SERVER_SUPPORT + /* This structure is used to pass information between patch_file and patch_file_write. */ @@ -1334,6 +1553,14 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) free (rev); } + /* If the revision is dead, let checkout_file handle it rather + than duplicating the processing here. */ + if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs)) + { + *docheckout = 1; + return 0; + } + backup = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + sizeof (CVSPREFIX) @@ -1638,25 +1865,32 @@ merge_file (finfo, vers) xchmod (finfo->file, 1); if (strcmp (vers->options, "-kb") == 0 - || wrap_merge_is_copy (finfo->file)) + || wrap_merge_is_copy (finfo->file) + || special_file_mismatch (finfo, NULL, vers->vn_rcs)) { - /* For binary files, a merge is always a conflict. We give the + /* For binary files, a merge is always a conflict. Same for + files whose permissions or linkage do not match. We give the user the two files, and let them resolve it. It is possible that we should require a "touch foo" or similar step before we allow a checkin. */ - status = checkout_file (finfo, vers, 0); + + /* TODO: it may not always be necessary to regard a permission + mismatch as a conflict. The working file and the RCS file + have a common ancestor `A'; if the working file's permissions + match A's, then it's probably safe to overwrite them with the + RCS permissions. Only if the working file, the RCS file, and + A all disagree should this be considered a conflict. But more + thought needs to go into this, and in the meantime it is safe + to treat any such mismatch as an automatic conflict. -twp */ + #ifdef SERVER_SUPPORT - /* Send the new contents of the file before the message. If we - wanted to be totally correct, we would have the client write - the message only after the file has safely been written. */ if (server_active) - { server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); - server_updated (finfo, vers, SERVER_MERGED, - (struct stat *) NULL, (unsigned char *) NULL); - } #endif + + status = checkout_file (finfo, vers, 0, 1, 1); + /* Is there a better term than "nonmergeable file"? What we really mean is, not something that CVS cannot or does not want to merge (there might be an external manual or @@ -1717,7 +1951,8 @@ merge_file (finfo, vers) server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); server_updated (finfo, vers, SERVER_MERGED, - (struct stat *) NULL, (unsigned char *) NULL); + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); } #endif @@ -1951,8 +2186,8 @@ join_file (finfo, vers) if (server_active) { server_scratch (finfo->file); - server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL, - (unsigned char *) NULL); + server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, + (unsigned char *) NULL, (struct buffer *) NULL); } #endif mrev = xmalloc (strlen (vers->vn_user) + 2); @@ -2005,14 +2240,7 @@ join_file (finfo, vers) /* FIXME: If checkout_file fails, we should arrange to return a non-zero exit status. */ - status = checkout_file (finfo, xvers, 1); - -#ifdef SERVER_SUPPORT - if (server_active && status == 0) - server_updated (finfo, xvers, - SERVER_UPDATED, (struct stat *) NULL, - (unsigned char *) NULL); -#endif + status = checkout_file (finfo, xvers, 1, 0, 1); freevers_ts (&xvers); @@ -2074,7 +2302,7 @@ join_file (finfo, vers) (char *) NULL, RUN_TTY, (RCSCHECKOUTPROC) NULL, (void *) NULL); if (retcode != 0) - error (1, retcode == -1 ? errno : 0, + error (1, 0, "failed to check out %s file", finfo->fullname); } #endif @@ -2147,9 +2375,11 @@ join_file (finfo, vers) write_letter (finfo, 'U'); } else if (strcmp (options, "-kb") == 0 - || wrap_merge_is_copy (finfo->file)) + || wrap_merge_is_copy (finfo->file) + || special_file_mismatch (finfo, rev1, rev2)) { - /* We are dealing with binary files, but real merging would + /* We are dealing with binary files, or files with a + permission/linkage mismatch, and real merging would need to take place. This is a conflict. We give the user the two files, and let them resolve it. It is possible that we should require a "touch foo" or similar step before @@ -2227,12 +2457,318 @@ join_file (finfo, vers) server_copy_file (finfo->file, finfo->update_dir, finfo->repository, backup); server_updated (finfo, vers, SERVER_MERGED, - (struct stat *) NULL, (unsigned char *) NULL); + (mode_t) -1, (unsigned char *) NULL, + (struct buffer *) NULL); } #endif free (backup); } +/* + * Report whether revisions REV1 and REV2 of FINFO agree on: + * . file ownership + * . permissions + * . major and minor device numbers + * . symbolic links + * . hard links + * + * If either REV1 or REV2 is NULL, the working copy is used instead. + * + * Return 1 if the files differ on these data. + */ + +int +special_file_mismatch (finfo, rev1, rev2) + struct file_info *finfo; + char *rev1; + char *rev2; +{ +#ifdef PRESERVE_PERMISSIONS_SUPPORT + struct stat sb; + RCSVers *vp; + Node *n; + uid_t rev1_uid, rev2_uid; + gid_t rev1_gid, rev2_gid; + mode_t rev1_mode, rev2_mode; + unsigned long dev_long; + dev_t rev1_dev, rev2_dev; + char *rev1_symlink = NULL; + char *rev2_symlink = NULL; + char *rev1_hardlinks = NULL; + char *rev2_hardlinks = NULL; + int check_uids, check_gids, check_modes; + int result; + + /* If we don't care about special file info, then + don't report a mismatch in any case. */ + if (!preserve_perms) + return 0; + + /* When special_file_mismatch is called from No_Difference, the + RCS file has been only partially parsed. We must read the + delta tree in order to compare special file info recorded in + the delta nodes. (I think this is safe. -twp) */ + if (finfo->rcs->flags & PARTIAL) + RCS_reparsercsfile (finfo->rcs, NULL, NULL); + + check_uids = check_gids = check_modes = 1; + + /* Obtain file information for REV1. If this is null, then stat + finfo->file and use that info. */ + /* If a revision does not know anything about its status, + then presumably it doesn't matter, and indicates no conflict. */ + + if (rev1 == NULL) + { + if (islink (finfo->file)) + rev1_symlink = xreadlink (finfo->file); + else + { + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev1_uid = sb.st_uid; + rev1_gid = sb.st_gid; + rev1_mode = sb.st_mode; + if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode)) + rev1_dev = sb.st_rdev; + } + rev1_hardlinks = list_files_linked_to (finfo->file); + } + else + { + n = findnode (finfo->rcs->versions, rev1); + vp = (RCSVers *) n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev1_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "owner"); + if (n == NULL) + check_uids = 0; /* don't care */ + else + rev1_uid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "group"); + if (n == NULL) + check_gids = 0; /* don't care */ + else + rev1_gid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + check_modes = 0; /* don't care */ + else + rev1_mode = strtoul (n->data, NULL, 8); + + n = findnode (vp->other_delta, "special"); + if (n == NULL) + rev1_mode |= S_IFREG; + else + { + /* If the size of `ftype' changes, fix the sscanf call also */ + char ftype[16]; + if (sscanf (n->data, "%16s %lu", ftype, + &dev_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + finfo->file, rev1, n->data); + rev1_dev = dev_long; + if (strcmp (ftype, "character") == 0) + rev1_mode |= S_IFCHR; + else if (strcmp (ftype, "block") == 0) + rev1_mode |= S_IFBLK; + else + error (0, 0, "%s:%s unknown file type `%s'", + finfo->file, rev1, ftype); + } + + n = findnode (vp->other_delta, "hardlinks"); + if (n == NULL) + rev1_hardlinks = xstrdup (""); + else + rev1_hardlinks = xstrdup (n->data); + } + } + + /* Obtain file information for REV2. */ + if (rev2 == NULL) + { + if (islink (finfo->file)) + rev2_symlink = xreadlink (finfo->file); + else + { + if (CVS_LSTAT (finfo->file, &sb) < 0) + error (1, errno, "could not get file information for %s", + finfo->file); + rev2_uid = sb.st_uid; + rev2_gid = sb.st_gid; + rev2_mode = sb.st_mode; + if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode)) + rev2_dev = sb.st_rdev; + } + rev2_hardlinks = list_files_linked_to (finfo->file); + } + else + { + n = findnode (finfo->rcs->versions, rev2); + vp = (RCSVers *) n->data; + + n = findnode (vp->other_delta, "symlink"); + if (n != NULL) + rev2_symlink = xstrdup (n->data); + else + { + n = findnode (vp->other_delta, "owner"); + if (n == NULL) + check_uids = 0; /* don't care */ + else + rev2_uid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "group"); + if (n == NULL) + check_gids = 0; /* don't care */ + else + rev2_gid = strtoul (n->data, NULL, 10); + + n = findnode (vp->other_delta, "permissions"); + if (n == NULL) + check_modes = 0; /* don't care */ + else + rev2_mode = strtoul (n->data, NULL, 8); + + n = findnode (vp->other_delta, "special"); + if (n == NULL) + rev2_mode |= S_IFREG; + else + { + /* If the size of `ftype' changes, fix the sscanf call also */ + char ftype[16]; + if (sscanf (n->data, "%16s %lu", ftype, + &dev_long) < 2) + error (1, 0, "%s:%s has bad `special' newphrase %s", + finfo->file, rev2, n->data); + rev2_dev = dev_long; + if (strcmp (ftype, "character") == 0) + rev2_mode |= S_IFCHR; + else if (strcmp (ftype, "block") == 0) + rev2_mode |= S_IFBLK; + else + error (0, 0, "%s:%s unknown file type `%s'", + finfo->file, rev2, ftype); + } + + n = findnode (vp->other_delta, "hardlinks"); + if (n == NULL) + rev2_hardlinks = xstrdup (""); + else + rev2_hardlinks = xstrdup (n->data); + } + } + + /* Check the user/group ownerships and file permissions, printing + an error for each mismatch found. Return 0 if all characteristics + matched, and 1 otherwise. */ + + result = 0; + + /* Compare symlinks first, since symlinks are simpler (don't have + any other characteristics). */ + if (rev1_symlink != NULL && rev2_symlink == NULL) + { + error (0, 0, "%s is a symbolic link", + (rev1 == NULL ? "working file" : rev1)); + result = 1; + } + else if (rev1_symlink == NULL && rev2_symlink != NULL) + { + error (0, 0, "%s is a symbolic link", + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + else if (rev1_symlink != NULL) + result = (strcmp (rev1_symlink, rev2_symlink) == 0); + else + { + /* Compare user ownership. */ + if (check_uids && rev1_uid != rev2_uid) + { + error (0, 0, "%s: owner mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare group ownership. */ + if (check_gids && rev1_gid != rev2_gid) + { + error (0, 0, "%s: group mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare permissions. */ + if (check_modes && + (rev1_mode & 07777) != (rev2_mode & 07777)) + { + error (0, 0, "%s: permission mismatch between %s and %s", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + + /* Compare device file characteristics. */ + if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT)) + { + error (0, 0, "%s: %s and %s are different file types", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + else if (S_ISBLK (rev1_mode)) + { + if (rev1_dev != rev2_dev) + { + error (0, 0, "%s: device numbers of %s and %s do not match", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + } + + /* Compare hard links. */ + if (strcmp (rev1_hardlinks, rev2_hardlinks) != 0) + { + error (0, 0, "%s: hard linkage of %s and %s do not match", + finfo->file, + (rev1 == NULL ? "working file" : rev1), + (rev2 == NULL ? "working file" : rev2)); + result = 1; + } + } + + if (rev1_symlink != NULL) + free (rev1_symlink); + if (rev2_symlink != NULL) + free (rev2_symlink); + if (rev1_hardlinks != NULL) + free (rev1_hardlinks); + if (rev2_hardlinks != NULL) + free (rev2_hardlinks); + + return result; +#else + return 0; +#endif +} + int joining () { diff --git a/contrib/cvs/src/vers_ts.c b/contrib/cvs/src/vers_ts.c index bae3a43..be0f588 100644 --- a/contrib/cvs/src/vers_ts.c +++ b/contrib/cvs/src/vers_ts.c @@ -251,7 +251,7 @@ time_stamp_server (file, vers_ts, entdata) struct stat sb; char *cp; - if ( CVS_STAT (file, &sb) < 0) + if (CVS_LSTAT (file, &sb) < 0) { if (! existence_error (errno)) error (1, errno, "cannot stat temp file"); @@ -322,7 +322,7 @@ time_stamp (file) char *cp; char *ts; - if ( CVS_STAT (file, &sb) < 0) + if (CVS_LSTAT (file, &sb) < 0) { ts = NULL; } diff --git a/contrib/cvs/src/version.c b/contrib/cvs/src/version.c index 1f77f58..f386895 100644 --- a/contrib/cvs/src/version.c +++ b/contrib/cvs/src/version.c @@ -12,7 +12,7 @@ #include "cvs.h" -char *version_string = "\nConcurrent Versions System (CVS) 1.9.24"; +char *version_string = "\nConcurrent Versions System (CVS) 1.9.26"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT -- cgit v1.1