[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Cvs-cvs] ccvs/src ChangeLog admin.c annotate.c commit.c ... [newtags2]
From: |
Derek Robert Price |
Subject: |
[Cvs-cvs] ccvs/src ChangeLog admin.c annotate.c commit.c ... [newtags2] |
Date: |
Tue, 17 Jan 2006 15:41:25 +0000 |
CVSROOT: /cvsroot/cvs
Module name: ccvs
Branch: newtags2
Changes by: Derek Robert Price <address@hidden> 06/01/17 15:41:24
Modified files:
src : ChangeLog admin.c annotate.c commit.c cvs.h
import.c log.c patch.c rcs.c rcs.h sanity.sh
status.c tag.c update.c vers_ts.c
Log message:
Merge from `newtags' branch. Also:
* cvs.h: New fn (Version_release_relTag)
* vers_ts: New fn (Version_release_relTag): Prepend revision number
to relative tags
* patch.c: (patch_fileproc): Prepend revision number to relative tags
* tag.c: (check_fileproc, tag_fileproc): dito
* annotate.c: (annotate_fileproc): dito
* log.c: (log_expand_revlist): dito, change params: RCSNode->file_info
* update.c: (update_fileproc): dito
(join_file): make jrev1, jrev2 fn params so relative tags can be
resolved per file.
* rcs.h: New fns: RCS_is_symbolic, RCS_is_relative
* rcs.c: (is_symbolic): Comment fixed.
New fns: RCS_is_symbolic, RCS_is_relative
(RCS_getprevious): Fixed for branches without revisions.
(RCS_getroot): dito, Gratious reformatting.
(RCS_getcommitid): Ignore dead revisions if duplicate commitids
exist (file added on branch, dead rev. on trunk);
For '@<' compare timestamp to determine predecessor
* sanity.sh: More tests
* cvs.texinfo: Improved; Mention 'commitid' and compatibility issues,
(Patch from Frank Hemer <address@hidden>.)
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/ChangeLog.diff?only_with_tag=newtags2&tr1=1.3337&tr2=1.3337.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/admin.c.diff?only_with_tag=newtags2&tr1=1.112&tr2=1.112.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/annotate.c.diff?only_with_tag=newtags2&tr1=1.21&tr2=1.21.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/commit.c.diff?only_with_tag=newtags2&tr1=1.258&tr2=1.258.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/cvs.h.diff?only_with_tag=newtags2&tr1=1.346&tr2=1.346.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/import.c.diff?only_with_tag=newtags2&tr1=1.175&tr2=1.175.8.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/log.c.diff?only_with_tag=newtags2&tr1=1.103&tr2=1.103.8.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/patch.c.diff?only_with_tag=newtags2&tr1=1.106&tr2=1.106.8.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/rcs.c.diff?only_with_tag=newtags2&tr1=1.357&tr2=1.357.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/rcs.h.diff?only_with_tag=newtags2&tr1=1.83&tr2=1.83.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/sanity.sh.diff?only_with_tag=newtags2&tr1=1.1108&tr2=1.1108.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/status.c.diff?only_with_tag=newtags2&tr1=1.68&tr2=1.68.8.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/tag.c.diff?only_with_tag=newtags2&tr1=1.142&tr2=1.142.8.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/update.c.diff?only_with_tag=newtags2&tr1=1.260&tr2=1.260.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/vers_ts.c.diff?only_with_tag=newtags2&tr1=1.65&tr2=1.65.8.1&r1=text&r2=text
Patches:
Index: ccvs/src/ChangeLog
diff -u /dev/null ccvs/src/ChangeLog:1.3337.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/ChangeLog Tue Jan 17 15:41:23 2006
@@ -0,0 +1,14382 @@
+2006-01-16 Derek Price <address@hidden>
+
+ * cvs.h: New fn (Version_release_relTag)
+ * vers_ts: New fn (Version_release_relTag): Prepend revision number
+ to relative tags
+ * patch.c: (patch_fileproc): Prepend revision number to relative tags
+ * tag.c: (check_fileproc, tag_fileproc): dito
+ * annotate.c: (annotate_fileproc): dito
+ * log.c: (log_expand_revlist): dito, change params: RCSNode->file_info
+ * update.c: (update_fileproc): dito
+ (join_file): make jrev1, jrev2 fn params so relative tags can be
+ resolved per file.
+ * rcs.h: New fns: RCS_is_symbolic, RCS_is_relative
+ * rcs.c: (is_symbolic): Comment fixed.
+ New fns: RCS_is_symbolic, RCS_is_relative
+ (RCS_getprevious): Fixed for branches without revisions.
+ (RCS_getroot): dito, Gratious reformatting.
+ (RCS_getcommitid): Ignore dead revisions if duplicate commitids
+ exist (file added on branch, dead rev. on trunk);
+ For '@<' compare timestamp to determine predecessor
+ * sanity.sh: More tests
+ * cvs.texinfo: Improved; Mention 'commitid' and compatibility issues,
+ (Patch from Frank Hemer <address@hidden>.)
+
+2005-04-19 Derek Price <address@hidden>
+
+ * admin.c (admin_fileproc): Undo last commit. TAG_TRUNK test removed.
+ * commit.c (check_fileproc): Imporve comments. TAG_TRUNK test removed.
+ (commit_fileproc): TAG_TRUNK test removed.
+ (remove_file): TAG_TRUNK test removed.
+ * update.c (update_fileproc): TAG_TRUNK test removed.
+ * rcs.c: Improve comments.
+ (RCS_gethead): Remove this fn.
+ (RCS_getversion): TAG_TRUNK test removed.
+ (RCS_gettag): Remove magic conversion.
+ (RCS_nodeisbranch): Properly detect magic for numeric rev.
+ Remove magic conversion.
+ (RCS_whatbranch): Gratious reformatting. Simplify.
+ (translate_tag): Always return non-magic branches for symbolic/extended
+ tags. Add param flag to prevent conversion for non-extended tags.
+ Improve assumption verification.
+ (RCS_extract_tag): Adapt error msg to cvs conventions.
+ (RCS_getprevious): Rootdate comparison fixed.
+ (RCS_getroot): Remove magic conversion.
+ * sanity.sh: More tests added.
+ (Patch from Frank Hemer <address@hidden>.)
+
+2005-04-06 Derek Price <address@hidden>
+
+ * rcs.c (RCS_getprevious): Remove erroneous FIXME.
+
+2005-03-20 Derek Price <address@hidden>
+
+ * rcs.c (RCS_nodeisbranch): Improve comments. Return bool. Gratuitous
+ reformatting.
+ (RCS_getprevious): NULL return from RCS_branch_head means a tag does
+ not exist. Factor common code from then/else. Add FIXME note.
+ Gratuitous reformatting. Simplify.
+ (RCS_getorigin): Simplify.
+ (translate_tag): Gratuitous reformatting.
+ * rcs.h (RCS_nodeisbranch): Update prototype.
+
+2005-03-19 Derek Price <address@hidden>
+
+ Begin cleanup. tag-ext test still broken.
+ * admin.c (admin_fileproc): Remove obfuscations.
+ * rcs.c (RCS_tag2rev): Remove obfuscations. Gratuitous reformatting.
+ (is_symbolic): New function.
+ (RCS_gettag, RCS_nodeisbranch): s/isrevnumonly/!is_symbolic/.
+ (RCS_whatbranch): s/rev/tag/ where appropriate. Improve comments.
+ Assert assumptions. Gratuitous reformatting. Process symbols first.
+ (RCS_branch_head): Remove obfuscations. Gratuitous reformatting.
+ Assert assumptions. Process symbols first.
+ (truncate_revnum_in_place): Declare inline.
+ (RCS_head): Improve comments. Perform error checking.
+ (RCS_getprevious): Remove obfuscations.
+ (translate_tag): Improve comments. Simplify. Return branches when
+ input is a branch name.
+ (truncate_revnum): Simplify.
+ * status.c: Gratuitous reformatting.
+ * subr.c (isrevnumonly): Remove this function.
+ * subr.h (isrevnumonly): Remove this proto.
+ * sanity.sh (tag-ext): Cleanup.
+
+2005-03-19 Derek Price <address@hidden>
+
+ * admin.c, subr.h, update.c, rcs.h, subr.c, import.c, sanity.sh,
+ cvs.h, vers_ts.c, tag.c, commit.c, rcs.c: Tag extensions added
+ (Patch from Frank Hemer <address@hidden>.)
+
+2006-01-16 Derek Price <address@hidden>
+
+ * sanity.sh (join-16a): Test "no such tag" error with -j.
+
+ * update.c (update): Correct typo.
+
+2006-01-13 Larry Jones <address@hidden>
+
+ * mkmodules.c (config_contents): Change SystemAuth to yes to match
+ the default value.
+
+2006-01-10 Derek Price <address@hidden>
+
+ * cvs.h (valloc): Remove unused proto.
+
+2006-01-09 Larry Jones <address@hidden>
+
+ * commit.c (remove_file): Record correct revision in history file.
+ (Reported by Chris Reed <address@hidden>.)
+
+2006-01-05 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Expand header comment block.
+
+2005-12-31 Mark D. Baushke <address@hidden>
+
+ [Add CVSNT compatible [-w width] switch to cvs (r)annotate command.]
+ * rcs.c (RCS_deltas): Provide for annotate_width for author field
+ and use Xasprintf in place of xmalloc/sprintf.
+ * rcs.h (annotate_width): New global.
+ * annotate.c (annotate_width): Default to 8.
+ (annotate_usage): New -w width option.
+ (annotate): Ditto.
+ * sanity.sh (userlen, username1): New variables.
+ (basica-10w1, basica-10wmax, ann-10w1, ann-10wmax): New tests for
+ the annotate -w width option.
+
+2005-12-20 Derek Price <address@hidden>
+
+ * sanity.sh (config3): Accept `.' in paths.
+
+ * admin.c (admin): Don't pass the global optind to getopt_long as
+ longindex - it causes infinite loops on some systems.
+
+2005-12-07 Derek Price <address@hidden>
+
+ * client.c (handle_redirect): Give stack control of string key.
+
+ * client.c (start_server), root.c (method_names), root.h (CVSmethod):
+ Handle :extssh: as a kindness to Eclipse users.
+ (Suggestion from Joseph P. Skudlarek <address@hidden>.)
+
+2005-12-07 Mark D. Baushke <address@hidden>
+
+ * buffer.c (fd_buffer_shutdown): Use error (0, ...) instead of
+ error (1, ...) to avoid infinite loops. (patch #4678)
+ Patch adapted from "Allan L. Bazinet" <address@hidden>
+
+2005-11-30 Mark D. Baushke <address@hidden>
+
+ [bug #14900] Add 'cvs server' handling for --allow-root
+ * root.c (root_allow_used): New function.
+ * root.h (root_allow_used): Ditto.
+ * server.c (serve_root): Call new function in server mode.
+ * sanity.sh (server2-3): Fix typo in test name.
+ (server2-5, server2-6): New tests for --allow-root feature.
+ (proxy-init): Fix --allow-root instances for primary-wrapper
+ to include the primary and the secondary in the list.
+ (writeproxy-noredirect): Ditto.
+ (writeproxy-ssh-noredirect): Ditto.
+
+2005-11-22 Derek Price <address@hidden>
+
+ * checkout.c (checkout_proc): Bury sacrificial chicken.
+ * sanity.sh: Update to compensate.
+
+2005-11-18 Derek Price <address@hidden>
+
+ * update.c (checkout_file): Add FIXME.
+
+2005-11-17 Derek Price <address@hidden>
+
+ * client.c (update_entries), update.c (update): Restore refetch
+ notices and add two new ones.
+ * sanity.sh: Update to compensate.
+
+2005-11-15 Derek Price <address@hidden>
+
+ * client.c (update_entries): Only print "U ..." when patches are
+ applied successfully. Only print patch failed messages for traces.
+ * update.c (update): Share flags with refetch pass. Never send
+ contents when refetching. Only print refetching message for traces.
+ Use "U" instead of "P", except for traces.
+ * sanity.sh: Update to compensate.
+
+2005-11-14 Mark D. Baushke <address@hidden>
+
+ * admin.c (opt_values): New enum for long option values.
+ (short_options): Now in file scope for validation of
+ UserAdminOptions.
+ (long_options): Use opt_value enums for option.value entries.
+ (make_UserAdminOptions): Rewrite.
+ (admin): Use opt_value enumes for long_option cases.
+ Quote the name of the restricted admin group.
+ * mkmodules.c (UserAdminOptions): Add defaults for execute and
+ no-execute.
+
+2005-11-13 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (modes-execute-*): Use $CVSROOT_DIRNAME rather than
+ default text for expected data.
+
+ * admin.c (admin_usage): Fix spelling in help string.
+ Alphabetize long options.
+ (Patch from Alexandros Manoussakis.)
+
+2005-11-12 Mark D. Baushke <address@hidden>
+
+ * admin.c: Add long option processing.
+ (make_UserAdminOptions): New option for administrative permission
+ of long options.
+ (admin): Use long option processing. Add support for --execute and
+ --no-execute options.
+ * parseinfo.c (parse_config): Use new make_UserAdminOptions
+ function to process the UserAdminOptions= configuration line.
+ * sanity.sh (modes): Added tests for admin --execute/--no-execute
+ options.
+ (Based on CVS patch #4446 from Alex Manoussakis.)
+
+2005-11-10 Larry Jones <address@hidden>
+
+ * commit.c (commit): Complain about obsolete -n option if not in
+ server mode.
+
+2005-11-10 Derek Price <address@hidden>
+
+ * edit.c (mark_up_to_date): Accept update_dir for error messages.
+ Print correct, quoted file name in error message.
+ * edit.h (mark_up_to_date): Update proto to match.
+ * checkin.c, client.c: Update all callers.
+
+2005-11-09 Derek Price <address@hidden>
+
+ * edit.h: Remove unneeded `extern' from function decls.
+
+ * sanity.sh (pserver-4.2): Accept a "no such sytem user" message when
+ a root attempt is made.
+
+2005-11-07 Derek Price <address@hidden>
+
+ * edit.c (watch_onoff): Remove unneeded proto.
+
+2005-11-03 Derek Price <address@hidden>
+
+ * sanity.sh (compression): Elucidate.
+
+2005-10-28 Derek Price <address@hidden>
+
+ * commit.c (check_fileproc): Don't print conflict marker warning in
+ really quiet mode.
+
+ * sanity.sh (rcslib): Rename some misnamed tests.
+
+ [bug #14840]
+ * sanity.sh (compression): New test for problem fixed below.
+
+ [bug #14840]
+ * zlib.c (compress_bufer_input): Don't assume the number of bytes the
+ caller requested will be available from the stream underlying the
+ compression buffer - the data is compressed and should be shorter by
+ definition. Improve comment.
+ (Original report from Rahul Bhargava <address@hidden>.)
+
+2005-10-18 Derek Price <address@hidden>
+
+ Include "wait.h" only as needed.
+ * cvs.h: Remove #include of "wait.h".
+ * run.c, server.c: Add #include "wait.h".
+
+2005-10-16 Brian Murphy <address@hidden>
+
+ * server.c (check_pam_password): set PAM_RHOST to remote host ip.
+
+2005-10-11 Derek Price <address@hidden>
+
+ * client.h (SEND_BUILD_DIRS, SEND_FORCE, SEND_NO_CONTENTS,
+ BACKUP_MODIFIED_FILES): Tidy.
+
+2005-10-04 Derek Price <address@hidden>
+
+ * sanity.sh (diff_u_test, diff_recursive_test): New functions.
+ (directory_cmp): Use GNU diff -ur when possible.
+ (find_tool): Catch stderr output from tests. Count MARGINAL test
+ results and prefer tools with more PASSes.
+ (*): Replace use of cmp to $diff_u where possbile.
+
+2005-10-04 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (watch6-0): Avoid use of GNU grep -qE extensions for
+ anchored search.
+
+2005-10-04 Derek Price <address@hidden>
+
+ * sanity.sh (find_tool): Accept tool name for error messages. Change
+ all callers.
+
+ * run.c: Assume unistd.h.
+
+2005-10-03 Derek Price <address@hidden>
+
+ * sanity.sh (sshstdio-6): Use diff -u instead of cmp so that errors
+ show up in the automated nightly testing emails.
+
+ * run.c (piped_child): Close original dup'd descriptors. Add comments.
+
+ * server.c: #include "setenv.h" to eliminate a Solaris warning.
+
+2005-09-30 Larry Jones <address@hidden>
+
+ * expand_path.c (expand_path): Fix memory leaks.
+
+2005-09-29 Mark D. Baushke <address@hidden>
+
+ * main.c (main): Simplify commitid creation.
+
+ * main.c (main): Not all hosts support O_NOCTTY for open.
+ Try to use more random data when time() returns -1.
+
+2005-09-29 Derek Price <address@hidden>
+
+ * main.c (main): Check for error return from time().
+
+2005-09-29 Paul Eggert <address@hidden>
+ Derek Price <address@hidden>
+
+ * client.c (handle_m, handle_e): Remove incomplete workaround for
+ O_NONBLOCK problem; no longer needed because of the fix below.
+ * cvs.h (piped_child): New bool argument saying whether O_NONBLOCK
+ fix is needed. All uses changed.
+ * rsh-client.c (start_rsh_server): We need the O_NONBLOCK fix,
+ so pass 'true' to piped_child to enable the workaround.
+ * run.c (work_around_openssh_glitch): New function.
+ (piped_child): Use it if the fix is requested. Avoid call call to
+ vfork with undefined behavior.
+
+2005-09-29 Mark D. Baushke <address@hidden>
+
+ * main.c (main): Rename N as COMMITID_RAW_SIZE.
+ Use base62 encoding for non-urandom case.
+
+2005-09-29 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_input), client.c (handle_m, handle_e): Use new
+ fd_select in place of select.
+
+ * subr.c: Remove select includes - the sleep functions are now in
+ lib.
+
+2005-09-28 Mark D. Baushke <address@hidden>
+
+ * main.c (N, RANDOM_BYTES): New constants for base62 conversion.
+ (divide_by): Used in base62 conversion.
+ (convert): Ditto.
+ (main): Set global_session_id using more random data and the
+ current time in base62 if possible, otherwise fallback to the old
+ method.
+
+2005-09-25 Conrad T. Pino <address@hidden>
+
+ * rcs.c: Use "#ifdef HAVE_FSYNC" just like every where else.
+
+2005-09-25 Conrad T. Pino <address@hidden>
+
+ * buffer.c client.h socket-client.h: Remove HAVE_WINSOCK_H macro &
+ change <winsock.h> to <sys/socket.h> include.
+
+ * server.c: Remove HAVE_WINSOCK_H and <winsock.h> include.
+
+2005-09-25 Derek Price <address@hidden>
+
+ * rcs.c (rcs_internal_unlockfile): Fsync files before renaming them.
+ Patch from Rahul Bhargava <address@hidden>.
+
+ * cvs.h, filesubr.c, main.c, server.c:
+ s/push_cvs_tmp_dir/push_cvs_temp_dir/.
+
+ * main.c (get_cvs_temp_dir): Use new get_system_temp_dir function.
+ (push_env_temp_dir): Move to...
+ * filesubr.c (push_env_temp_dir): ...here to split from Windows.
+ (get_system_temp_dir): New function.
+ * cvs.h (get_cvs_temp_dir, push_env_temp_dir): Add protos.
+
+ * buffer.c (fd_buffer_input): Fix spelling in header comment block.
+
+2005-09-24 Derek Price <address@hidden>
+
+ * socket-client.c: Check HAVE_CONFIG_H, not HAVE_CONFIG.
+
+ * socket-client.c, socket-client.h: Minor cleanup.
+
+ * Makefile.am (cvs_SOURCES): Move socket-client.[ch] to...
+ (EXTRA_cvs_SOURCES): ...here to avoid compiling them.
+ * socket-client.c (init_sockaddr), socket-client.h (init_sockaddr):
+ Move to...
+ * client.c (init_sockaddr), client.c (init_sockaddr): ...here.
+
+ * socket-client.c (socket_buffer_input): Avoid overflow problems.
+
+ * socket-client.c: General cleanup.
+
+2005-09-23 Larry Jones <address@hidden>
+
+ * checkout.c (export_usage): Note that -r requires a tag.
+
+2005-09-22 Derek Price <address@hidden>
+
+ * classify.c (Classify_File): Correct comment.
+
+2005-09-22 Larry Jones <address@hidden>
+
+ * patch.c (patch_usage): Document -k option.
+
+2005-09-22 Derek Price <address@hidden>
+
+ * classify.c (Classify_File): If a file had a conflict and the
+ timestamp hasn't changed, it still has a conflict. Add comment about
+ how T_MODIFIED could later turn out to have conflict markers and why
+ it should not be checked in this function.
+ * client.c (send_fileproc): Don't send contents for files known to have
+ conflicts unless this is for `cvs diff'.
+ * commit.c (check_fileproc): T_CONFLICT should be handled like
+ T_MODIFIED, since force could be requested. Simplify logic since
+ T_CONFLICT can now be trusted.
+ * rcs.c (RCS_Checkout): Comment noexec behavior in header block.
+ * server.c (serve_unchanged, serve_is_modified): Handle conflicts.
+ * status.c (status_fileproc): Trust T_CONFLICT to simplify.
+ * subr.c (file_has_conflict): Removed.
+ * subr.h (file_has_conflict): Remove proto.
+ * update.c (update_fileproc): Trust T_CONFLICT.
+ (RegisterMerge): New function factored from...
+ (merge_file, join_file): ...these two functions.
+ * vers_ts.c (time_stamp_server): Handle = conflict timestamps in server
+ entdata.
+ * sanity.sh (files-12): Account for slight behavior improvement.
+ (status, conflicts, mwrap): Account for corrected behavior.
+ (join-readonly-conflict-10): Correct comment.
+ (binfiles-con1b): New test for correct behavior.
+
+ * classify.c (Classify_File): Consolidate redundant conditionals.
+
+2005-09-21 Derek Price <address@hidden>
+
+ * entries.c: Remove obsolete comment.
+
+2005-09-20 Derek Price <address@hidden>
+
+ * myndbm.c: #include getdelim.h for Windows.
+
+ * main.c: #include setenv.h for Windows.
+
+2005-09-19 Derek Price <address@hidden>
+
+ * sanity.sh (modules5-8): Rename...
+ (modules5-8r): ...to this and comment Mac OS X failure.
+ Comment Solaris 9 failure below with a `FIXME?' tag.
+
+ * sanity.sh: Remove previous hack in favor of setting TESTDIR on
+ Solaris (and Mac OS X) until problem is solved correctly.
+
+2005-09-15 Derek Price <address@hidden>
+
+ * sanity.sh: Use /bin/pwd to verify current dir since Solaris 9 is
+ sometimes resolving symlinked paths.
+
+2005-09-14 Derek Price <address@hidden>
+
+ * edit.c (edit_usage, unedit_usage, editors_usage), watch.c
+ (watch_usage, watchers_usage): Add quotes and reword for clarity and
+ consistency.
+
+ * edit.c (edit_usage): Add missing syntax. Reword description for
+ clarity. Mention default behavior.
+
+2005-09-13 Derek Price <address@hidden>
+
+ * sanity.sh ($anyusername): Reduce charset to that allowed on Linux.
+
+ * sanity.sh ($anyusername): Make more robust and expand comments.
+ Reported by Mark D. Baushke <address@hidden>.
+
+ * sanity.sh: Split $username into $username & $username8. Rename
+ $author as $anyusername.
+
+2005-09-12 Derek Price <address@hidden>
+
+ * sanity.sh (username): Cut $username down to 8 characters when longer,
+ since that is all that appears in output.
+
+2005-09-11 Derek Price <address@hidden>
+
+ [bug #14504]
+ * main.c (main): Set server_hostname before it is used by
+ gserver_authenticate_connection.
+ (Report from Serguei E. Leontiev <address@hidden>.)
+
+2005-09-08 Larry Jones <address@hidden>
+
+ * server.c (parseServerOptions): getopt() returns int, not char.
+
+2005-09-07 Derek Price <address@hidden>
+
+ * rcs.c (RCS_parse): Minor reformatting.
+
+ Close <http://savannah.nongnu.org/bugs/?func=detailitem&item_id=14462>.
+ * rcs.c (RCS_parse): Free variable *after* using it for the last time.
+
+2005-09-06 Derek Price <address@hidden>
+
+ * sanity.sh (tempfile): All CVS temp files start with "cvs".
+
+ * sanity.sh (tempfile): New var.
+ (tempname): Set to $TMPDIR/$tempfile after $TMPDIR is set.
+ (info): s#$TMPDIR/##.
+
+ * rcs.c (RCS_putdtree): Remove unused variable.
+
+2005-09-06 Mark D. Baushke <address@hidden>
+
+ Close <https://savannah.nongnu.org/bugs/?func=detailitem&item_id=14435>.
+ * rcs.c (RCS_putdtree): Avoid stack overflow which may be
+ possible with excessive recursive calls to RCS_putdtree().
+ (Patch from Serg Masyutin.)
+
+2005-09-06 Derek Price <address@hidden>
+
+ Close <http://savannah.nongnu.org/bugs/?func=detailitem&item_id=14448>.
+ * kerberos-client.c (start_kerberos4_server): Pass new root arg to
+ make_bufs_from_fds.
+ (Patch from <Manuel.Guijarro.AT.cern.ch>.)
+
+ * server.c: Reorganize includes slightly and alphabetize sections.
+
+ * server.c: #include canon-host.h.
+ (gserver_authenticate_connection): Print out canon-host errors.
+ * subr.c: #include canon-host.h.
+ (isThisHost): Print out canon-host errors.
+
+ * parseinfo.c (parse_config): Don't check hostname without client
+ or server support.
+
+ * main.c (main): Set cvs_cmd_name before calling parseServerOptions.
+
+2005-09-05 Derek Price <address@hidden>
+
+ * cvs.h (Tmpdir): Remove global decl.
+ (get_cvs_tmp_dir, push_env_tmp_dir): New protos.
+ * filesubr.c (cvs_temp_file): Use get_cvs_tmp_dir.
+ * main.c (Tmpdir): Remove global.
+ (tmpdir_cmdline, tmpdir_env): New globals.
+ (get_cvs_tmp_dir, push_env_tmp_dir): New functions.
+ (main): Discard most tmpdir overhead in favor of new globals and
+ functions.
+ * parseinfo.c (parse_config): Parse TmpDir.
+ * sanity.sh (config4): New tests.
+ * server.c (serve_root): Set env TMPDIR after parsing config. Create
+ tmpdir here instead of...
+ (serve_co, server): ...either of thse locations.
+ (server_cleanup): Use new function in favor of obsolete Tmpdir.
+
+ * sanity.sh (config2): Don't overwrite potentially significant config.
+
+ * sanity.sh (info): Use $tempfile instead of reproducing the regex.
+
+ * sanity.sh (info): Accept `.' in temp file names.
+
+ * main (main): Set the actual PID, not a pointer to a string.
+
+2005-09-04 Derek Price <address@hidden>
+
+ * sanity.sh (config3): Generalize.
+
+ * main.c (main), server.c (serve_root, switch_to_user): Replace putenv
+ with GNULIB setenv.
+ * sanity.config.sh.in (HAVE_PUTENV): Remove.
+ * sanity.sh (env): Remove reference to $HAVE_PUTENV.
+
+ * sanity.sh (tests): Add config2 & config3.
+
+ * server.c (isSamePath, isThisHost): Move to...
+ * subr.c (isSamePath, isThisHost): ...here.
+ * subr.h (isSamePath, isThisHost): Add protos.
+ * parseinfo.c (parse_config): Handle [rootspec] syntax.
+ * main.c (main): Always set server_hostname.
+ * sanity.sh (config3): New tests for same.
+
+ * server.c (gserver_authenticate_connection): Output hostname in
+ error message.
+
+ * server.c (isThisHost): Fix typo.
+
+ * server.c (isThisHost, gserver_authenticate_connection): Simplify
+ using canon_host().
+
+ * root.c (free_cvsroot_t): Update header comment.
+
+ * root.c (new_cvsroot_t): directory is not client-specific.
+ (free_cvsroot_t): Declare static.
+ * root.h (free_cvsroot_t): Remove proto.
+ * server.c (server_init): Don't free cvsroot_t when finished with it.
+
+2005-09-03 Derek Price <address@hidden>
+
+ * root.h (cvsroot_t->isremote): Update comment.
+
+ * history.c (read_hrecs_file): Suppress signed/unsigned char warning.
+
+ * root.h (cvsroot_t->isremote): Declare bool.
+ * root.c (new_cvsroot_t): Initialize isremote to false instead of 0.
+
+ * add.c (add_usage): Standardize usage message somewhat.
+
+2005-09-02 Larry Jones <address@hidden>
+
+ * commit.c (checkaddfile): Improve error messages for lock_RCS failure.
+ * release.c (release): Improve error message for pclose failure.
+
+ * root.h (struct cvsroot_s): Always declare isremote to simplify
+ other code. Simplify referencing code.
+ * root.c (new_cvsroot_t): Always initialize isremote.
+ * server.h: Always declare server_active to simplify other code.
+ Simplify referencing code.
+ * server.c: Always define server_active.
+
+2005-09-02 Larry Jones <address@hidden>
+
+ * parseinfo.c (parse_config): Variable declarations must precede
+ executable code for pre-C99 compilers. Pass correct line number
+ variable to expand_path.
+
+2005-09-01 Derek Price <address@hidden>
+
+ * recurse.c: Update bug report email address.
+
+2005-08-31 Derek Price <address@hidden>
+
+ * cvs.h (expand_path): Update proto.
+ * expand_path.c (expand_variable): Accept and use cvsroot arg inplace
+ of global.
+ (expand_path): Accept and pass through cvsroot arg.
+ * main.c (main): Prescan args for config path before config options are
+ used. Pass config path on as needed. Update comment.
+ * modules.c (do_module): Update expand_path call.
+ * parseinfo.ci (Parse_Info): Ditto.
+ (allowed_config_prefixes): New global.
+ (parse_config): Accept configPath arg, update expand_path calls, and
+ expand LockDir path.
+ * parseinfo.h (parse_config): Update proto.
+ * root.c (root_allow_add, get_root_allow_config): Accept new configPath
+ arg and pass through to parse_config.
+ * root.h (root_allow_add, get_root_allow_config): Update protos.
+ * server.c (gConfigPath, server_usage): New globals.
+ (parseServerOptions): New function.
+ (server): Use new usage var.
+ (pserver_authenticate_connection): Update get_root_allow_config call.
+ * server.h (parseServerOptions): New proto.
+ * wrapper.c (wrap_add): Update expand_path calls.
+ * sanity.sh (server): New tests for setting config file path.
+
+2005-08-31 Derek Price <address@hidden>
+
+ * sanity.sh (close-stdout): Remove archive dir when done.
+
+2005-08-31 Larry Jones <address@hidden>
+
+ * import.c (import_descend): Lock repository directory during import.
+
+2005-08-31 Derek Price <address@hidden>
+
+ * server.c (isSamePath): Compare args rather than assuming values.
+
+2005-08-29 Derek Price <address@hidden>
+
+ Add %{sV} format string to verifymsg script.
+ * logmsg.c (do_verify): Accept change list arg, pass through to...
+ (verifymsg_proc): ...here, to pass through to format_cmdline.
+ * commit.c (commit_fileproc, commit_direntproc), import.c (update):
+ Update all callers of do_verify.
+ * cvs.h (do_verify): Update proto.
+ * sanity.sh (info): Test new verifymsg format strings.
+
+2005-08-09 Derek Price <address@hidden>
+
+ * sanity.sh: Remove debugging echo.
+ (watch6, watch6-0): Clean up properly.
+
+2005-08-03 Jim Hyslop <address@hidden>
+
+ * edit.c, watch.c, watch.h, sanity.sh: fixed problems with watch not
+ setting default attributes, when directory specified.
+
+2005-07-20 Derek Price <address@hidden>
+
+ * main.c: s/cvshome.org/nongnu.org.etc.../.
+
+2005-07-12 Derek Price <address@hidden>
+
+ * buffer.c, buffer.h, client.h, expand_path.c, history.c, myndbm.h,
+ release.c: Add copyright notices.
+
+2005-07-12 Derek Price <address@hidden>
+
+ * client.c: Update fwrite usage to use size & count in the standard
+ order.
+
+2005-07-11 Derek Price <address@hidden>
+
+ * buffer.c, buffer.h, client.h, expand_path.c, history.c, myndbm.h,
+ release.c: Update license notices.
+
+2005-06-28 Derek Price <address@hidden>
+
+ * server.c (serve_co): Remove obsolete support for Repository request.
+
+2005-06-10 Derek Price <address@hidden>
+
+ * filesubr.c, ignore.c, import.c, vers_ts.c: Include "lstat.h".
+
+2005-06-10 Derek Price <address@hidden>
+
+ * logmsg.c (logmsg_list_to_args_proc): Add format character for
+ destination tag.
+ (Original patch from Todd Vierling <address@hidden>).
+
+ * tag.c (pretag_list_to_args_proc): Likewise.
+ (check_fileproc): Set destination tag name.
+ (tag_delproc): Delete destination tag name.
+ * sanity.sh (info, taginfo): Test new format strings.
+
+2005-06-08 Derek Price <address@hidden>
+
+ * parseinfo.c: Restore comparison to NULL in assignment within
+ conditional to placate non-GNU compilers. Eliminate assignments in
+ conditionals where possible by GNU coding standards. Eliminate other
+ comparisons to NULL where possible.
+ (Parse_Info): Make int a true bool.
+
+2005-06-03 Derek Price <address@hidden>
+
+ * client.c (force_gzip): New static global.
+ (handle_force_gzip): New function.
+ (responses): Add `Force-gzip'.
+ (start_server): Turn on encryption and compression before potentially
+ sending other rooted requests. Turn on compression when requested by
+ the user or the server.
+ * main.c (opt_usage): Note that -z<level> *requests* compression
+ <level> from the server.
+ * parseinfo.c (new_config): Initialize MaxCompressionLevel.
+ (parse_config): Parse MinCompressionLevel & MaxCompressionLevel.
+ * parseinfo.h (struct config): Add MinCompressionLevel &
+ MaxCompressionLevel.
+ * server.c (pending_warning_text): New static global.
+ (print_pending_error): Print pending warnings too.
+ (warning_pending): New macro.
+ (alloc_pending_internal): New function with much content...
+ (alloc_pending): ...previously from here.
+ (alloc_pending_warning): New function.
+ (server_root, serve_gzip_contents, gzip_stream): Force gzip_level into
+ configured restrictions.
+ (serve_command_prep): Print pending errors.
+ (requests): Make `Gzip-stream', `gzip-file-contents',
+ `Kerberos-encrypt', `Gssapi-encrypt', & `Gssapi-authenticate' requests
+ rootless to allow them before compression starts.
+ (serve_valid_requests): Send `Force-gzip' response when needed.
+ (server): Abort if a rootless compression request forced compression
+ outside restricted levels.
+ * zlib.c (struct compress_buffer, compress_buffer_initialize): Store
+ compression level.
+ (compress_buffer_output): Reset compression level when global
+ gzip_level has changed.
+ * sanity.sh (config2): New tests for compression restrictions.
+
+2005-06-03 Derek Price <address@hidden>
+
+ * zlib.c (compress_buffer_input): Update comment.
+
+2005-06-03 Derek Price <address@hidden>
+
+ * error.c (error): Correct spelling and grammar in comment.
+
+2005-06-03 Derek Price <address@hidden>
+
+ * modules.c (my_module), wrappers.c (wrap_add): Use new expand_path
+ API.
+
+2005-06-03 Derek Price <address@hidden>
+
+ * cvs.h (expand_path): Rearrange args and use bool for formatsafe flag.
+ * expand_path.c: Globally: Remove init of globals to NULL by C89,
+ reformat to CVS conventions, remove unnecessary comparisons to NULL and
+ 0, & remove unnecessary typecasts.
+ (expand_variable): Remove proto and move function above first use.
+ Make return value const.
+ (expand_path): Don't refer to var when contents are known. Rearrange
+ args per cvs.h changes. Improve header comment block.
+ * parseinfo.c (Parse_Info): Use new expand_path API.
+
+2005-06-02 Derek Price <address@hidden>
+
+ * client.c: Don't set NULL for globals by C89. Globally remove
+ comparisons to NULL or replace with !. Similarly remove or replace
+ comparisons of strcmp and strncmp return value to 0. Remove some
+ unneeded braces around single-element blocks.
+ (handle_*): Remove unecessary protos.
+ (handle_notified, notified_a_file): Move up before first use.
+
+2005-06-02 Derek Price <address@hidden>
+
+ * sanity.sh (config): Simplify cleanup.
+
+2005-06-02 Derek Price <address@hidden>
+
+ * zlib.c (compress_buffer_input): Don't request more bytes from the
+ underlying buffer than asked for.
+ (compress_buffer_shutdown_input): Don't attempt to read EOF from the
+ client during shutdown. It might never be sent.
+ * sanity.sh (abspath2): Test for this.
+
+2005-05-31 Derek Price <address@hidden>
+
+ * rcscmds.c (call_diff_argc_allocated): Rename to...
+ (call_diff_arg_allocated): ...to match similar usage in other files.
+
+2005-05-31 Derek Price <address@hidden>
+ for Alexander Taler <address@hidden>
+
+ * rcscmds.c: Change type of call_diff_argc_allocated from int to
+ size_t, to match the prototype of run_add_arg_p(). This fixes a
+ bus error in OpenBSD 3.6 sparc64.
+
+2005-05-27 Derek Price <address@hidden>
+
+ * client.c (send_arg): Make arg const. Remove unnecessary copy to
+ buffer.
+ (send_option_string): Rename to...
+ (send_options): ...this and accept argc/argv in place of string.
+ * client.h: Update protos to match the changes to client.c.
+ * cvs.h (RCS_exec_rcsdiff, diff_exec): Update protos.
+ (run_add_arg_p, run_arg_free_p): New protos.
+ * diff.c (opts, opts_allocated): Replace with...
+ (diff_argv, diff_argc, diff_arg_allocated): ...these.
+ (add_diff_args): New convenience function.
+ (diff): Use new constructs and APIs.
+ * patch.c (patch_fileproc, RCS_checkin, RCS_delete_revs), rcscmds.c
+ (call_diff_add_arg, call_diff_setup, RCS_merge, RCS_exec_rcsdiff,
+ diff_exec, RCS_output_diff_options), update.c (patch_file): Use new
+ APIs.
+ * run.c (run_add_arg_p, run_arg_free_p): New functions.
+ (run_argc_allocated): Make size_t.
+ (run_setup, run_add_arg): Use new functions.
+ * sanity.sh: Accomodate above changes.
+ (rcslib-diffrgx-3): Slip in test for space splitting.
+
+2005-05-26 Derek Price <address@hidden>
+
+ * subr.c (isabsolute), subr.h (isabsolute): Remove this function.
+ * root.c: Likewise, plus some reformatting.
+ * checkout.c, client.c, find_names.c, import.c, modules.c, parseinfo.c,
+ repos.c, root.c, server.c, subr.c: s/isabsolute/ISABSOLUTE/.
+
+2005-05-26 Derek Price <address@hidden>
+
+ * cvs.h: Move "system.h" include before GNULIB includes. Move some
+ GNULIB includes from "system.h".
+
+2005-05-26 Conrad T. Pino <address@hidden>
+
+ * buffer.c, buffer.h: Add & use typedefs for function pointer arguments
+ and struct buffer function pointers. New typedefs are useful in casts.
+ * socket-client.c: Function pointers passed to buf_initialize use size_t
+ not int arguments. buf_initialize warnings are gone from Windows build.
+
+2005-05-24 Derek Price <address@hidden>
+
+ * client.c, entries.c, filesubr.c, hardlink.c, ignore.c, import.c,
+ lock.c, logmsg.c, mkmodules.c, rcs.c, rcscmds.c, server.c, subr.c,
+ update.c, vers_ts.c: s/CVS_STAT/stat/ & s/CVS_LSTAT/lstat/.
+
+2005-05-23 Derek Price <address@hidden>
+
+ * filesubr.c (xresolvepath): Move to...
+ * subr.c (xcanonicalize_file_name): ...here and rename. Use new
+ GNULIB canonicalize module.
+ * cvs.h (xresolvepath): Move proto...
+ * subr.h (xcanonicalize_file_name): ...here.
+ * checkout.c (safe_location), server.c (isSamePath): Use new function
+ name.
+
+2005-05-23 Derek Price <address@hidden>
+
+ * sanity.sh (rcslib-symlink-10): Accept empty result due to broken glob
+ in glibc 2.3.5.
+
+2005-05-18 Derek Price <address@hidden>
+
+ * sanity.sh (config-9): Split to local/remote tests to avoid unportable
+ expr characters.
+
+2005-05-17 Derek Price <address@hidden>
+
+ * sanity.sh: Use a predictable umask.
+
+2005-05-13 Derek Price <address@hidden>
+
+ * login.c (password_entry_parseline): Placate gcc -Wall.
+
+2005-05-11 Derek Price <address@hidden>
+
+ * cvs.h (find_files): New proto.
+ * find_names.c (find_files, strip_rcsext): New functions.
+ (find_rcs): Make arg const. Use new find_files. Improve header
+ comment block.
+ * history.c (histfile): Remove global.
+ (get_history_log_name): New function.
+ (history, history_write): Use new functions.
+ (read_hrecs_file): New function containing most content from...
+ (read_hrecs): ...this function, which now accepts a file list and calls
+ read_hrecs_file once for each file.
+ * parseinfo.c (parse_config): Parse HistoryLogPath & HistorySearchPath.
+ * parseinfo.h (struct config): Add HistoryLogPath & HistorySearchPath.
+ * sanity.sh (basic2-64): Remove obsolete comment.
+ (config): Test new history options.
+ (crerepos): Cleanup.
+
+2005-05-09 Derek Price <address@hidden>
+
+ * error.c (error): Avoid unportable calls to vsyslog.
+
+2005-05-09 Derek Price <address@hidden>
+
+ * history.c (history_write): Add FIXME.
+
+2005-05-09 Derek Price <address@hidden>
+
+ * hash.c (removenode, mergelists): New function.
+ (delnode): Use removenode.
+ * hash.h (mergelists): New proto.
+
+2005-05-04 Derek Price <address@hidden>
+
+ * error.c (error): Avoid recursion and syslog the problem.
+
+2005-05-03 Derek Price <address@hidden>
+
+ * tag.c (is_in_val_tags): Remove unnecessary existance checking for the
+ val-tags file and just rely on open() to create it when necessary.
+
+2005-05-03 Derek Price <address@hidden>
+
+ * tag.c (tag_check_valid): Don't verify the_val_args.found when it is
+ not initialized.
+
+2005-05-03 Derek Price <address@hidden>
+
+ * add.c: Update comment to include the -k option. This resolves issue
+ #226 on cvshome.org.
+
+2005-05-02 Derek Price <address@hidden>
+
+ Remove unnecessary level of indirection.
+ * lock.c (L_HISTORY_LOCK, L_VAL_TAGS_LOCK): Remove macros.
+ (internal_lock, internal_clear_lock): Accept lock as argument.
+ (history_lock, clear_history_lock, val_tags_lock, clear_val_tags_lock):
+ Replace old macro arg with an actual lock pointer.
+
+2005-05-02 Derek Price <address@hidden>
+
+ * lock.c (internal_lock, internal_clear_lock): Add protos.
+ (history_lock, val_tags_lock): Return the chartered true/false status.
+
+2005-05-02 Derek Price <address@hidden>
+
+ * cvs.h (CVSHISTLCK): Rename macro to...
+ (CVSHISTORYLCK): ...this.
+ (CVSVALTAGSLCK): New macro.
+ (val_tags_lock, clear_val_tags_lock): New functions.
+ * lock.c (global_val_tags_lock): New global.
+ (Lock_Cleanup): Clean up after val-tags lock if necessary.
+ (L_HISTORY_LOCK, L_VAL_TAGS_LOCK): New local macros.
+ (internal_lock, val_tags_lock, clear_val_tags_lock): New functions.
+ (history_lock): Use new internal function.
+ * tag.c (is_in_val_tags, add_to_val_tags): New functions using the
+ write lock for val-tags and factored from...
+ (tag_check_valid): ...this function.
+ * sanity.sh (lockfiles-22): Add val-tags lock test.
+
+2005-04-30 Mark D. Baushke <address@hidden>
+
+ * lock.c (global_readlock, global_writelock, global_history_lock):
+ Add missing alternatives for non-LOCK_COMPATIBILITY initialization.
+
+2005-04-28 Derek Price <address@hidden>
+
+ * cvs.h (history_lock, clear_history_lock): New protos.
+ * lock.c (struct lock): Add lockdirname.
+ (global_history_lock): New global.
+ (global_read_lock): Initialize.
+ (lock_name): Handle const args.
+ (lock_simple_remove): Factor out code in favor of clear_lock call.
+ (set_lock): Handle variable lockdirname.
+ (lock_filesdoneproc): Set new lockdirname.
+ (history_lock, clear_history_lock): New functions.
+ (clear_lock): Avoid segfault on missing lock.
+ (Lock_Cleanup): Clean up history locks when necessary.
+ * history.c (history_write): Use new lock.
+ * sanity.sh (lockfiles-20): Test new lock.
+
+2005-04-28 Derek Price <address@hidden>
+
+ * sanity.sh (lockfiles): Port some locking tests over from 1.12.x.
+
+2005-04-28 Derek Price <address@hidden>
+
+ * lock.c (clear_lock): Improve comment.
+
+2005-04-28 Derek Price <address@hidden>
+
+ * lock.c (struct lock): Store lockdir name.
+ (masterlock): Remove global.
+ (remove_lock_files, clear_lock, set_lock): Update to compensate.
+
+2005-04-25 Mark D. Baushke <address@hidden>
+
+ * server.c: Add support for <pam/pam_appl.h> to allow
+ --enable-pam to work on MacOSX 10.2 and newer.
+ (Pach from Moriyoshi Koizumi <address@hidden>.)
+
+2005-04-25 Derek Price <address@hidden>
+
+ * mkmodules.c (mkmodules): Remove `#if 0' and reformat comment.
+
+2005-04-22 Mark D. Baushke <address@hidden>
+
+ * expand_path.c (expand_variable): Add SESSIONID and COMMITID
+ internal variables. Both return the unique global session id of
+ the CVS process. Passing this information to administrative
+ triggers seems reasonable. (The same feature exists in CVSNT and
+ the names were chosen to be the same as the CVSNT names.)
+
+ * sanity.sh (info): Add a test for $COMMITID and $SESSIONID.
+
+2005-04-20 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (rcs4): Fix a typo.
+
+2005-04-20 Derek Price <address@hidden>
+
+ * sanity.sh (rcs5): Minor cosmetic change.
+
+2005-04-20 Derek Price <address@hidden>
+
+ * sanity.sh (tests): Add rcs4.
+ (rcs5): Add comments.
+
+2005-04-20 Derek Price <address@hidden>
+
+ * rcs.c (expand_keywords): Avoid buffer overflow.
+ (Original patch from Stewart Brodie <address@hidden>.)
+
+ * sanity.sh (rcs5): New tests for the above.
+
+2005-04-08 Derek Price <address@hidden>
+
+ * edit.c (edit_fileproc, unedit_fileproc): s/GMT/-0000/.
+ * rcs.c (RCS_getrevtime): Ditto, & replace a sprintf into a static
+ buffer with an Xasprintf which creates a dynamic one.
+ * sanity.sh: Update tests to compensate.
+ (Original bug report from Ian Abbott <address@hidden>.)
+
+2005-03-26 Mark D. Baushke <address@hidden>
+
+ * checkout.c (checkout_proc): Use Xasprintf/xstrdup instead of
+ xmalloc/strcpy+strcat.
+
+2005-03-22 Mark D. Baushke <address@hidden>
+
+ * Makefile.in: Regenerated.
+
+2005-03-22 Mark D. Baushke <address@hidden>
+
+ * rcs.c (linevector_add): Use xnrealloc.
+ * server.c (serve_argument): Ditto.
+
+2005-03-22 Mark D. Baushke <address@hidden>
+
+ * rcs.c (linevector_copy): Use xtimes in argument.
+
+ * patch.c (patch): Remove dead code.
+
+ * lock.c (set_readlock_name): Use Xasprintf instead of
+ xmalloc/sprintf.
+ (lock_exists, set_promotable_lock, lock_wait): Ditto.
+ (lock_obtained, lock_dir_for_write): Ditto.
+ * log.c (rlog_proc): Ditto.
+
+ * ignore.c (ign_dir_add): Use xnrealloc.
+ * modules.c (save_d): Ditto.
+ * rcs.c (linevector_copy): Ditto.
+
+ * add.c (add): Use xstrdup instead of xmalloc/strcpy.
+ * client.c (client_process_import_file): Ditto.
+ * kerberos4-client.c (start_kerberos4_server): Ditto.
+ * logmsg.c (verifymsg_proc): Ditto.
+ * log.c (log_expand_revlist): Ditto.
+ * patch.c (patch_fileproc): Ditto.
+ * rcs.c (RCS_tag2rev, RCS_nodeisbranch, RCS_getbranch): Ditto.
+ (RCS_getdatebranch, expand_keywords, RCS_addbranch): Ditto.
+ (RCS_checkin): Ditto.
+ * remove.c (remove_fileproc): Ditto.
+
+2005-03-18 Derek Price <address@hidden>
+
+ * server.c: Reindent pragmas.
+ (become_proxy): Add parentheses for -Wall.
+
+2005-03-18 Derek Price <address@hidden>
+
+ * error.c (error): Simplify using vasnprintf.
+
+2005-03-17 Mark D. Baushke <address@hidden>
+
+ * admin.c (postadmin_proc): Cast NULL when it is an argument to
+ stdarg function to ensure it is the correct type.
+ * commit.c (precommit_proc): Ditto.
+ * edit.c (notify_proc): Ditto.
+ * fileattr.c (postwatch_proc): Ditto.
+ * logmsg.c (logfile_write, verifymsg_proc): Ditto.
+ * server.c (prepost_proxy_proc): Ditto.
+ * subr.c (cmdlineescape): Ditto.
+ * tag.c (posttag_proc): Ditto.
+ (Thanks to a report from Derek Price <address@hidden>.)
+
+2005-03-17 Derek Price <address@hidden>
+
+ * rcs.h (RCSNode): Improve comment.
+ * rcs.c (RCS_head): Ditto, plus gratuitous reformatting.
+
+2005-03-17 Derek Price <address@hidden>
+
+ * rcs.c (RCS_deltas): Use rcs->print_path.
+
+2005-03-17 Derek Price <address@hidden>
+
+ * login.c (password_entry_parseline): Avoid using uninitialized
+ variable.
+ * rcs.c (RCS_deltas): Avoid buffer overflow.
+ (RCS_checkout): Avoid using uninitialized loglen.
+ * patch.c (patch_fileproc): Free original pointer, not one that may
+ have been incremented.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-17 Derek Price <address@hidden>
+
+ * commit.c (checkaddfile): Avoid dereferencing a NULL pointer in
+ response to a rare error.
+ * admin.c (admin_fileproc), log.c (log_expand_revlist), mkmodules.c
+ (checkout_file), rcs.c (RCS_getdate, RCS_deltas, RCS_findlock_or_tip,
+ RCS_tag2rev): Avoid dereferencing NULL pointer.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-17 Derek Price <address@hidden>
+
+ * rcs.c (RCS_reparsercsfile): Avoid memory leak.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-17 Derek Price <address@hidden>
+
+ * log.c (log_expand_revlist): Suppress message and not error handling
+ when really_quiet.
+
+2005-03-16 Mark D. Baushke <address@hidden>
+
+ * buffer.c (fd_buffer_shutdown): Replace (int *) 0 with NULL.
+ * server.c (do_cvs_command): Ditto.
+
+ * client.c (update_entries): Use xnmalloc.
+
+ * checkin.c (Checkin): Replace (char *) 0 with NULL.
+ * patch.c (patch_fileproc): Ditto.
+ * update.c (update_fileproc): Ditto.
+ * no_diff.c (No_Difference): Ditto.
+ * patch.c (patch_fileproc): Ditto.
+ * rcscmds.c (call_diff_setup, call_diff_add_arg): Ditto.
+
+ * update.c (join_file): Replace (RCSCHECKOUTPROC)0 with NULL.
+ * rcs.c (RCS_checkin, RCS_cmp_file, RCS_delete_revs)
+ (RCS_delete_revs): Ditto.
+ * rcscmds.c (RCS_merge, RCS_exec_rcsdiff): Ditto.
+
+ * annotate.c, checkin.c, classify.c, fileattr.c, find_names.c,
+ hash.c, lock.c, login.c, logmsg.c, main.c, modules.c, myndbm.c,
+ no_diff.c, patch.c, rcs.c, rcscmds.c, remove.c, server.c,
+ status.c, subr.c, tag.c, update.c, vers_ts.c: Avoid casting NULL.
+
+2005-03-17 Derek Price <address@hidden>
+
+ * client.c (call_in_directory): Put function call after var decls.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * client.c (call_in_directory), commit.c (commit_filesdoneproc), log.c
+ (log_expand_revlist, log_version), logmsg.c (logfile_write), modules
+ (my_module), no_diff.c (No_Difference), parseinfo.c (Parse_Info), rcs.c
+ (RCS_deltas, RCS_checkin, RCS_addbranch, do_locks, do_symbols),
+ rcscmds.c (RCS_merge), root.c (parse_cvsroot, normalize_cvsroot),
+ update.c (merge_file): Verify assumptions via assertions.
+ (Thanks to (probably) incorrect reports from Alen Zukich
+ <address@hidden>.)
+
+2005-03-16 Mark D. Baushke <address@hidden>
+
+ * rcs.c (RCS_check_kflag): Use Xasprintf instead of
+ sprintf/xstrdup.
+
+ * mkmodules.c (checkout_file): Use Xasprintf instead of
+ xmalloc/strcpy+strcat.
+ * wrapper.c (wrap_unparse_rcs_options): Ditto.
+ (wrap_rcsoption): Ditto.
+
+ * subr.c (getcaller): Use Xasprintf instead of sprintf/xstrdup.
+
+ * history.c (history): Use Xasprintf instead of xmalloc/sprintf.
+ * lock.c (lock_name, set_lockers_name): Ditto.
+ * main.c (cmd_synonyms, main): Ditto.
+ * mkmodules.c (rename_rcsfile, init): Ditto
+ * modules.c (cat_module): Ditto.
+ * parseinfo.c (Parse_Info): Ditto.
+ * rcscmds.c (diff_exec, RCS_output_diff_options): Ditto.
+ * recurse.c (start_recursion, do_dir_proc): Ditto.
+ * remove.c (remove_fileproc): Ditto.
+ * repos.c (Name_Repository): Ditto.
+ * root.c (Name_Root, Create_Root): Ditto.
+ * status.c (status_fileproc, tag_list_proc): Ditto.
+ * wrapper.c (wrap_setup, wrap_tocvs_process_file): Ditto.
+
+ * hash.c (sortlist): Use xnmalloc.
+ * main.c (cmd_synonyms): Ditto.
+ * server.c (cvs_pam_conv): Ditto.
+
+ * create_adm.c (Create_Admin): Clean up use of Xasprintf/xstrdup.
+ * entries.c (WriteTag, base_walk): Ditto.
+ * modules.c (my_module): Ditto.
+
+ * wrapper.c (wrap_fromcvs_process_file): Use Xasprintf instead of
+ xmalloc/sprintf and clean up control flow.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * history.c (history_write): Remove test that always evaluates to
+ false.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * server.c (become_proxy): Close pipe to primary when pipe from it
+ closes.
+
+2005-03-16 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (debug-log-nonfatal): Only set CVS_CLIENT_LOG to the
+ old value if it was previously set.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * server.c (create_adm_p, serve_entry), tag.c (rtag_proc): Avoid memory
+ leaks.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-16 Derek Price <address@hidden>
+
+ * logmsg.c (do_verify): Don't check for NULL return from xfopen().
+
+2005-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (debug-log-nonfatal): New test.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy-ssh-noredirect): Don't unconditionally create
+ a primary debug log.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * cvs.h (open_file): Move proto...
+ * subr.h (xfopen): ...here and rename.
+ * filesubr.c (open_file): Move function...
+ * subr.c (xfopen): ...here, with additional commenting and minor
+ cosmetic changes.
+ * add.c, checkout.c, client.c, cvsrc.c, edit.c, entries.c, filesubr.c,
+ logmsg.c, mkmodules.c, modules,c, patch.c, root.c, subr.c:
+ s/open_file/xfopen/.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * log-buffer.c (setup_logfiles): Failure to open a debug log file
+ should be non-fatal.
+
+2005-03-16 Derek Price <address@hidden>
+
+ * server.c (cvs_outerr): Quote error text in syslog messages.
+
+2005-03-15 Mark D. Baushke <address@hidden>
+
+ * history.c (select_hrec): Avoid possible memory leak.
+
+2005-03-15 Derek Price <address@hidden>
+
+ * patch.c (patch_proc): Avoid memory leak.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-12 Mark D. Baushke <address@hidden>
+
+ * server.c (receive_partial_file): Use ssize_t to deal with < 0
+ return value from write().
+
+2005-03-12 Mark D. Baushke <address@hidden>
+
+ * history.c (save_file): Bug fix from last change.
+
+2005-03-11 Mark D. Baushke <address@hidden>
+
+ * update.c (get_linkinfo_proc): Use Xasprintf instead of
+ xmalloc/sprintf. Gratuitous reformatting.
+ (update_ignproc, update_dirent_proc, checkout_file): Ditto.
+ (patch_file, merge_file, join_file): Ditto.
+ * modules.c (open_module): Ditto.
+
+ * modules (my_module): Use Xasprintf instead of xmalloc/sprintf
+ and avoid possibility of trying to free(NULL).
+
+ * subr.c (backup_file): Use Xasprintf instead of xmalloc/sprintf.
+ (cmdlinequote): Use Xasprintf instead of xmalloc/strcat+strlen.
+ * modules.c (my_module): Ditto.
+
+ * client.c (handle_module_expansion): Use xnmalloc, xnrealloc,
+ and xstrdup in place of xmalloc, xrealloc, and xmalloc/strcpy.
+ (start_server): Do not assume gzip_level precision encoding.
+ (send_modified): Do not assume 1024 bytes is sufficient for
+ a temporary filename.
+
+ * modules.c (my_module): Use xnmalloc and xnrealloc. Gratuitous
+ reformatting.
+ * subr.c (line2argv): Ditto.
+
+2005-03-11 Mark D. Baushke <address@hidden>
+
+ * modules.c (my_module): Protect against free (NULL) code path.
+
+2005-03-11 Derek Price <address@hidden>
+
+ * annotate.c (rannotate_proc), fileattr.c (fileattr_write), rcs.c
+ (RCS_deltas), server.c (check_repository_password), update.c (update):
+ Avoid memory leaks.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+2005-03-10 Derek Price <address@hidden>
+
+ * diff.c (diff_fileproc): Remove unnecessary check for NULL.
+
+2005-03-10 Mark D. Baushke <address@hidden>
+
+ * commit.c: Gratuitous reformatting.
+ * entries.c: Ditto.
+ * import.c (import, import_descend): Ditto.
+
+ * commit.c (commit): Use xnmalloc. Gratuitous reformatting.
+ * history.c (save_user, save_file, save_module, read_hrecs): Ditto.
+ * ignore.c (ign_add): Ditto.
+
+ * hardlink.c (lookup_file_by_inode): Use Xasprintf instead of
+ xmalloc/sprintf. Gratuitous reformatting.
+ (update_hardlink_info, list_linked_files_on_disk): Ditto.
+ (find_checkedout_proc): Ditto.
+ * history.c (history, history_write, save_file): Ditto.
+ (select_hrec): Ditto.
+ * ignore.c (ign_setup, ignore_files): Ditto.
+ * import.c (process_import_file, import_descend_dir): Ditto.
+
+ * import.c (import_descend_dir): Use Xasprintf instead of
+ xmalloc/strcpy+strcat+strcat.
+
+ * history.c (read_hrecs): Make function argument a const.
+
+2005-03-10 Derek Price <address@hidden>
+
+ * fileattr.c (fileattr_read): Only eat a newline when it really is a
+ newline.
+
+2005-03-10 Mark D. Baushke <address@hidden>
+
+ * zlib.c: Include "pagealign_alloc.h".
+
+2005-03-09 Derek Price <address@hidden>
+
+ * add.c (add, add_directory), buffer.c (allocate_buffer_datas),
+ client.c (update_entries), commit.c (checkaddfile), entries.c
+ (Entries_Open), fileattr.c (fileattr_read), ignore.c (ign_add),
+ import.c (import), main.c (main), parseinfo.c (parse_config), rcs.c
+ (RCS_reparsercsfile, RCS_getbranchpoint, RCS_checkout,
+ RCS_delete_revs, apply_rcs_changes): Avoid memory leaks.
+ (Thanks to report from Alen Zukich <address@hidden>.)
+
+ * hardlink.c, hardlink.h: Avoid compiling entire contents of these
+ files w/o preserve permissions support.
+
+2005-03-09 Mark D. Baushke <address@hidden>
+
+ * history.c (history, save_file): Cleanup the API to match the
+ comments.
+
+2005-03-08 Derek Price <address@hidden>
+
+ * rcs.c: Define MAP_FILE & MAP_FAILED when necessary.
+
+2005-03-08 Derek Price <address@hidden>
+
+ * zlib.c (compress_buffer_input): Use pagealign_xalloc when allocating
+ buffer datas.
+ (compress_buffer_output, compress_buffer_flush,
+ compress_buffer_shutdown_output): Don't assume that BUFFER_DATA_SIZE is
+ a constant.
+ (Thanks to report from Larry Jones <address@hidden>.)
+
+2005-03-08 Larry Jones <address@hidden>
+
+ * release.c (release): Remove unneeded code.
+
+2005-03-07 Conrad T. Pino <address@hidden>
+
+ * buffer.h (buf_free_data): Compile with proxy disabled.
+ (Thanks to report from Larry Jones <address@hidden>.)
+
+2005-03-07 Mark D. Baushke <address@hidden>
+
+ * add.c (add_directory): Xasprintf instead of xmalloc/sprintf+strcat.
+ * admin.c (arg_add): Ditto.
+ (admin_fileproc, postadmin_proc, admin_fileproc): Gratuitous
+ reformatting.
+
+ * client.c (mode_to_string): Use Xasprintf.
+
+ * checkout.c (checkout): Xasprintf instead of xmalloc/sprintf.
+ Gratuitous reformatting.
+ * entries.c (WriteTag): Ditto
+ (subdir_record, base_walk): Ditto.
+ * fileattr.c (fileattr_read, fileattr_set, fileattr_write): Ditto.
+ * filesubr.c (deep_remove_dir, cvs_temp_file): Ditto.
+ (strcat_filename_onto_homedir): Ditto.
+ * find_names.c (Find_Names): Ditto.
+
+ * filesubr.c (expand_wild): Use xnmalloc instead of xmalloc.
+
+2005-03-07 Derek Price <address@hidden>
+
+ * buffer.c (buf_free_data): Compile with proxy disabled.
+ (Thanks to report from Larry Jones <address@hidden>.)
+
+2005-03-07 Derek Price <address@hidden>
+
+ * buffer.c (packetizing_buffer_input, packetizing_buffer_output): Don't
+ assume BUFFER_DATA_SIZE is a constant.
+ (Thanks to report from Larry Jones <address@hidden>.)
+
+2005-03-03 Derek Price <address@hidden>
+
+ Use new pagealign_alloc() and pagealign_free() functions in
+ lieu of maintaining pointers for reuse. On most systems this should be
+ faster.
+ * buffer.c: Include "pagealign_alloc.h".
+ (free_buffer_data): Remove this global.
+ (allocate_buffer_datas): Remove this function.
+ (buf_free, buf_send_output): Call buf_free_data().
+ (get_buffer_data): Use pagealign_alloc().
+ (buf_free_datas): New function.
+ (buf_send_output, buf_free_data, buf_read_file, buf_read_file_to_eof,
+ buf_read_short_line, buf_read_data, buf_copy_counted): Call
+ buf_free_datas().
+ * buffer.h: Include "getpagesize.h".
+ (BUFFER_DATA_SIZE): Default to getpagesize ();
+ * rcs.c: Lean on m4/mmap-anon.m4 to simplify mmap setup.
+
+2005-03-02 Jim Meyering <address@hidden>
+
+ Detect and report write failure for e.g., cvs up -p FILE > /dev/full
+ * main.c: Include "closeout.h".
+ (main): Arrange to close standard output upon exit.
+ * sanity.sh (close-stdout): New test for this fix.
+
+2005-03-01 Mark D. Baushke <address@hidden>
+
+ * checkout.c (emptydir_name): Xasprintf instead of
+ xmalloc/sprintf.
+
+ * add.c (add): Xasprintf instead of xmalloc/sprintf.
+ Gratuitous reformatting.
+ (add_directory, build_entry): Ditto.
+ * annotate.c (rannotate_proc): Ditto.
+ * checkout.c (checkout_proc): Ditto.
+ * client.c (call_in_directory): Ditto.
+ (template, send_dirent_proc): Ditto.
+ (client_process_import_file): Ditto.
+ * commit.c (check_fileproc, precommit_proc): Ditto.
+ (commit_fileproc, finaladd): Ditto.
+ (checkaddfile): Ditto.
+ * create_adm.c (Create_Admin): Ditto.
+ * edit.c (notify_do): Ditto.
+
+2005-03-01 Derek Price <address@hidden>
+
+ * subr.c (get_date): Minor reformatting.
+
+2005-03-01 Derek Price <address@hidden>
+
+ * subr.c (get_date): Replace obsolete timeb cruft in this stub.
+
+2005-02-28 Mark D. Baushke <address@hidden>
+
+ * admin.c (arg_add): Use xnmalloc and xnrealloc.
+ (admin): Use xnmalloc. Minor reformatting.
+ Xasprintf instead of xmalloc/strcpy+strcat.
+ * client.c (client_expand_modules): Use xnmalloc.
+ Minor reformatting.
+ * cvsrc.c (read_cvsrc): Use xnmalloc and xnrealloc.
+ Minor reformatting.
+ * history.c (read_hrecs): Use xnrealloc. Minor reformatting.
+ * ignore.c (ign_add): Use xnrealloc. Minor reformatting.
+ * rcscmds.c (call_diff_add_arg): Use xnrealloc.
+ * wrapper.c (wrap_add_entry): Use xnrealloc. Gratuitous
+ reformatting.
+
+2005-02-28 Derek Price <address@hidden>
+
+ * edit.c (notify_proc), logmsg.c (logfile_write): Minor reformatting.
+
+2005-02-28 Derek Price <address@hidden>
+
+ * root.c (parse_root): Trigger a later error message rather than
+ maintaining two copies.
+
+2005-02-27 Mark D. Baushke <address@hidden>
+
+ * client.c (connect_to_pserver): Use TRACE_FUNCTION, not 1 in
+ TRACE() calls.
+ (connect_to_pserver,send_modified): Ditto.
+ * create_adm.c (Create_Admin): Ditto.
+ * entries.c (Register, Scratch_Entry, WriteTemplate): Ditto.
+ * filesubr.c (copy_file, xchmod, rename_file): Ditto.
+ * history.c (history_write): Ditto.
+ * kerberos4-client.c (start_kerberos4_server): Ditto.
+ * no_diff.c (No_Difference): Ditto.
+ * parseinfo.c (Parse_Info): Ditto.
+ * rcs.c (RCS_checkout): Ditto.
+ * server.c (server_register): Ditto.
+ * update.c (join_file): Ditto.
+
+ * no_diff.c (No_Difference): Gratuitous reformatting.
+ * kerberos4-client.c (start_kerberos4_server): Ditto.
+
+2005-02-27 Jim Meyering <address@hidden>
+
+ * login.c (password_entry_operation): Exit nonzero when
+ failing to close a just-appended-to .cvspass file.
+
+2005-02-26 Derek Price <address@hidden>
+
+ * root.c: Gratuitous reformatting.
+
+2005-02-26 Mark D. Baushke <address@hidden>
+
+ * root.c (parse_cvsroot): Handle another Bad CVSROOT.
+ * sanity.sh (parseroot-8r): Test for it.
+ (Problem report from Hiroyuki Ikezoe <address@hidden>.)
+
+2005-02-26 Derek Price <address@hidden>
+
+ * server.c: Include netdb.h with server support. Other reformatting.
+
+2005-02-25 Derek Price <address@hidden>
+
+ * sanity.sh (multiroot2-9a): Correct for new TRACE message.
+
+2005-02-25 Derek Price <address@hidden>
+
+ * cvs.h (tag_check_valid): Declare NAME arg const.
+ * tag.c (tag_check_valid): Ditto, update to account for this.
+ Xasprintf instead of xmalloc/sprintf.
+
+2005-02-25 Derek Price <address@hidden>
+
+ * client.c (send_modified): Suppress a -Wall warning.
+
+2005-02-25 Derek Price <address@hidden>
+
+ * cvs.h (global_session_id): New global declaration.
+ * import.c (add_rcs_file), rcs.c (RCS_checkin): Save commitid.
+ * log.c (log_version), status.c (status_fileproc): Output commitid.
+ * main.c (global_session_id): Define new global.
+ (main): Create session ID.
+ * sanity.sh: Update to compensate.
+ (Original patch from Frank Hemer <address@hidden>.)
+
+2005-02-24 Derek Price <address@hidden>
+
+ * subr.h (cvs_trace, TRACE*): Move to...
+ * server.h: ...here.
+ * subr.c: Ditto, but to...
+ * server.c: ...here, and print out 'P' instead of 'S' for traces from
+ secondary (proxy) servers.
+
+2005-02-24 Derek Price <address@hidden>
+
+ * admin.c (admin): Suppress warning with -Wall and --disable-client.
+ * server.c: Don't declare functions that won't be defined when
+ --disable-server.
+
+2005-02-24 Derek Price <address@hidden>
+
+ * sanity.sh (primary-wrapper): Rename CVS_SERVER_LOG for the primary in
+ writeproxy mode to avoid overwriting.
+
+2005-02-24 Derek Price <address@hidden>
+
+ * client.c (open_connection_to_server): Fail with an expressive error
+ message when connection is attempted via an unsupported protocol since
+ this is no longer caught in parse_root().
+ * edit.c (edit_fileproc), import.c (import): Don't verify
+ current_parsed_root->isremote without client support.
+ * parseinfo.c (parse_config): Parse PrimaryServer without proxy
+ support. Postpone method verification until the connect phase.
+ * parseinfo.h (struct config): Always include PrimaryServer.
+ * root.c (primary_root_translate, primary_root_inverse_translate):
+ Don't declare vars without PROXY_SUPPORT when they won't be used.
+ (parse_cvsroot): Parse remote connection methods with server support
+ for PrimaryServer/Redirects. Delay method support verification until
+ the connect phase.
+ * root.h (cvsroot_t): Include remote elements with SERVER_SUPPORT.
+ * server.c (isProxyServer): Delay method verification until the connect
+ phase.
+
+2005-02-23 Derek Price <address@hidden>
+
+ * tag.c (tag): Handle -r<tag>:<date>.
+ * sanity.sh (tagdate-13.*): New tests.
+
+2005-02-23 Derek Price <address@hidden>
+
+ * annotate.c (annotate), ls.c (ls): Handle -r<tag>:<date>.
+
+2005-02-23 Derek Price <address@hidden>
+
+ * diff.c: Some reformatting.
+ (diff): Handle -r<tag>:<date>.
+ * sanity.sh (tagdate-13b): New test.
+
+2005-02-23 Derek Price <address@hidden>
+
+ * checkout.c (checkout): Use parse_tagdate.
+ * tag.c (tag_check_valid_join): Remove this function.
+ * cvs.h (tag_check_valid_join): Ditto for the proto.
+ (parse_tagdate): New proto.
+ (Make_Date): Make arg const.
+ * main.c (Make_Date): Ditto.
+ (parse_tagdate): New function.
+ * update.c (date_rev1, date_rev2): Rename these globals...
+ (join_date1, join_date2): ...to this.
+ (update): Use parse_tagdate.
+ (do_update): Use new API.
+ * update.h (do_update): Update proto.
+ * sanity.sh: Misc reformatting.
+ (tagdate-12): This test now passes.
+ (tagdate-12b): New test.
+ (multiroot-9a): Handle new TRACE.
+
+2005-02-23 Derek Price <address@hidden>
+
+ * rsh-client.c (start_rsh_server): Update comment. Replace
+ malloc/sprintf combo with a call to Xasprintf.
+
+2005-02-22 Derek Price <address@hidden>
+
+ * error.c (error): Handle unsigned int format char.
+ * parseinfo.c (parse_error): New function.
+ (parse_config): Keep track of line number and output it in error
+ messages.
+ * rcs.c (RCS_setlocalid): Accept file path and line number for error
+ messages. Add header comment block.
+ * rcs.h (RCS_setlocalid): Update prototype to match.
+ * parseinfo.h (parse_error): Declare new function.
+ * sanity.sh: Accept new --noredirect argument.
+ (checklongoptarg): Accept longoptmode as an argument
+ rather than via an environment variable.
+ (notnoredirect): New function.
+ (newroot): Handle root options.
+ (keywordexpand, config): Skip in noredirect mode. Update for new
+ config error messages.
+ (multiroot): Some reformatting.
+ (writeproxy, writeproxy-noredirect): Skip in noredirect mode.
+ (commit, writeproxy-noredirect): Quote CVSROOT arguments since they
+ might contain semicolons.
+
+2005-02-21 Mark D. Baushke <address@hidden>
+
+ * import.c (import): Avoid using assert with side effects it may
+ be configured away using NDEBUG.
+ (Patch from Frank Hemer <address@hidden>.)
+
+2005-02-20 Mark D. Baushke <address@hidden>
+
+ * main.c (main): Check the results from xgethostname(). Print a
+ message with the errno and use "localhost" if NULL is returned.
+
+2005-02-20 Derek Price <address@hidden>
+
+ * cvs.h (run_arg): Rename to...
+ (run_add_arg): New function.
+ * run.c (run_arg): Remove.
+ (run_add_arg): Remove static declaration.
+ (run_piped): New function.
+ * logmsg.c, modules.c: s/run_arg/run_add_arg/.
+ * release.c (release): Replace use of piped_child with a call
+ to run_piped to avoid quoting issues.
+ * sanity.sh (info-cleanup-0): Expect success.
+
+2005-02-19 Derek Price <address@hidden>
+
+ * edit.c (unedit_fileproc, mark_up_to_date): Replace xmallc/strcat
+ sequence with single call to Xasprintf.
+
+2005-02-08 Derek Price <address@hidden>
+
+ * rsh-client.c: Some reformatting.
+
+2005-02-04 Derek Price <address@hidden>
+
+ * zlib.c (compress_buffer_input): Don't return EOF when there is data
+ pending.
+
+2005-02-01 Derek Price <address@hidden>
+
+ * main.c: Update year in copyright notice to match GNU standards.
+ * sanity.sh (version-1): Update to match.
+
+2005-02-01 Larry Jones <address@hidden>
+
+ * log.c (log_fileproc, log_expand_revlist): Add support for BASE tag.
+ * sanity.sh (log): New tests for above.
+
+2005-01-31 Derek Price <address@hidden>
+
+ * main.c: Rephrase --version message.
+ * sanity.sh (version-1): Update to match.
+
+2005-01-31 Derek Price <address@hidden>
+
+ * Makefile.am, add.c, admin.c, annotate.c, checkin.c, checkout.c,
+ classify.c, commit.c, create_adm.c, cvs.h, cvsrc.c, diff.c, entries.c,
+ find_names.c, hash.c, hash.h, history.h, 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, rcs.h, rcscmds.c, recurse.c, remove.c,
+ repos.c, root.c, root.h, server.h, stack.c, stack.h, status.c, subr.c,
+ tag.c, update.c, vers_ts.c, version.c: Update copyright notices.
+
+2005-01-29 Derek Price <address@hidden>
+
+ * log.c (log_usage): Add note about using -S with revision info
+ supression and selection.
+ (Suggestion from Dan Peterson <address@hidden>.)
+
+2005-01-29 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy-ssh-noredirect): Remove some commented out
+ code.
+
+2005-01-25 Larry Jones <address@hidden>
+
+ * expand_path.c (expand_path): Rewrite using offsets instead of
+ pointers to simplify and avoid reallocation bugs.
+ (Inspired by Jeremy Bopp <address@hidden>.)
+
+2005-01-20 Brian Murphy <address@hidden>
+
+ * server.c fixing the style of the pam function calls and if
+ statements
+
+2005-01-19 Brian Murphy <address@hidden>
+
+ * server.c (pam_username, pam_password) new global static
+ variables to hold the username and pasword for cvs_pam_conv.
+ (cvs_pam_conv) using pam_username and pam_password.
+ (check_pam_password) set pam_username, pam_password before
+ authentication and clear them when authentication is finished.
+ (server, switch_to_user) Check for pamh being set before using
+ pam functionality, NULL indicating that this user was authenticated
+ using the repository password file.
+
+2004-12-09 Derek Price <address@hidden>
+
+ * log-buffer.c (buf_count_mem): Compile this for PROXY_SUPPORT.
+ (Report from Brad L. Chisholm <address@hidden>.)
+
+2004-12-09 Derek Price <address@hidden>
+
+ * sanity.sh (modules7): New test group.
+ (Based on a patch from Mark D. Baushke <address@hidden>, based on a
+ report from Richard Verhoeven <address@hidden>.)
+
+2004-12-09 Derek Price <address@hidden>
+
+ * client.c (start_server): Avoid advertising the Redirect response when
+ the user asked us not to.
+ * root.h (cvsroot_t): Add redirect field.
+ * root.c (new_cvsroot_t): Init redirect field.
+ (parse_cvsroot_t): Parse Redirect method option.
+ * server.c (serve_command_prep): Don't throw proxy_log away when
+ Redirect isn't supported and it might be needed later.
+ * sanity.sh (parseroot): Improve comment, add a few new tests.
+ (writeproxy-ssh): Specify Redirect=yes explicitly.
+ (writeproxy-ssh-noredirect): New test group.
+
+2004-12-09 Mark D. Baushke <address@hidden>
+
+ * main.c (usg): Remove Dr. Pascal Molli's CVS URL from the
+ documentation.
+
+2004-12-08 Derek Price <address@hidden>
+
+ * root.c (Name_Root): s/TRACE_FUNCTION/TRACE_FLOW/.
+
+2004-12-08 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Suppress -Wall warning.
+
+2004-12-08 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): findnode() compares LIST to NULL w/o help.
+
+2004-12-08 Derek Price <address@hidden>
+
+ * root.c (Name_root), server.c (serve_referrer): Don't free cvsroot_t.
+ * server.sh (reposmv): Reaccount for multiple potential warnings and
+ comment.
+
+2004-12-07 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Cache parsed values for efficiency and to
+ avoid printing warnings about non-fatal parsing errors multiple times.
+ * client.c,, login.c, main.c, recurse.c: Don't dispose of parsed roots,
+ parse_cvsroot() has control.
+ * sanity.sh (reposmv): Stop accounting for multiple warnings.
+
+2004-12-03 Mark D. Baushke <address@hidden>
+
+ * root.h (cvsroot_t): Add cvs_rsh and cvs_server for bookkeeping
+ purposes.
+ * root.c (new_cvsroot_t, free_cvsroot_t): Add support for case
+ insensitive options CVS_RSH and CVS_SERVER.
+ (parse_cvsroot): Ditto and make all keywords case insensitive.
+ * client.c (connect_to_forked_server): Add support for the new
+ "CVS_SERVER" option to CVSROOT.
+ * rsh-client.c (connect_to_forked_server): Ditto
+ (start_rsh_server): Ditto and add support for the new "CVS_RSH"
+ option to CVSROOT.
+ * sanity.sh (parseroot3): New tests for the new options to
+ CVSROOT.
+
+2004-11-30 Larry Jones <address@hidden>
+
+ * mkmodules.c (config_contents): Add LocalKeyword and KeywordExpand,
+ misc. cleanup.
+
+2004-11-30 Derek Price <address@hidden>
+
+ * parseinfo.c (readBool): Update quotes in error message for
+ consistency. Move function to...
+ * subr.c (readBool): ...here.
+ (*): Gratuitous reformatting.
+ * cvs.h: Move all subr.c function prototypes to...
+ * subr.h: ...this new file.
+ * Makefile.am (cvs_SOURCES): Add subr.h.
+
+2004-11-30 Derek Price <address@hidden>
+
+ * parseinfo.c (readBool): Reorder arguments to error() and improve
+ header comment.
+
+2004-11-30 Derek Price <address@hidden>
+
+ * client.c (handle_referrer): New function.
+ (handle_redirect): Handle possibility that CLIENT_REFERRER was set via
+ a response.
+ * server.c (serve_command_prep): Send a normalized Referrer response
+ before a Redirect when the client supports it.
+
+2004-11-29 Mark D. Baushke <address@hidden>
+
+ * rcs.c (RCS_setlocalid): Do more configuration validation.
+ Include some gratuitous reformatting.
+ * sanity.sh (keywordexpand): Add tests for new validation code.
+
+2004-11-24 Derek Price <address@hidden>
+
+ * server.c (become_proxy): Note assumptions about syncronized primary
+ and secondary versions with `FIXME?' note.
+
+2004-11-22 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (info): Ignore comments in verifymsg test.
+
+2004-11-22 Mark D. Baushke <address@hidden>
+
+ * mkmodules.c (loginfo_contents, verifymsg_contents,
+ commitinfo_contents, taginfo_contents, preproxy_contents,
+ postadmin_contents, postproxy_contents, posttag_contents,
+ postwatch_contents, notify_contents): Add comments regarding the
+ additional format strings that are available. Include some
+ gratuitous reformatting.
+
+2004-11-19 Derek Price <address@hidden>
+
+ * subr.c (Xasnprintf): Improve header comment block.
+
+2004-11-19 Derek Price <address@hidden>
+
+ * root.c (normalize_cvsroot): Improve header comment block.
+
+2004-11-19 Derek Price <address@hidden>
+
+ * server.c: Misc reformatting and comment corrections.
+
+2004-11-19 Derek Price <address@hidden>
+
+ * cvs.h (hostname): Redeclare as pointer rather than array.
+ * main.c: Include xgethostname.h. Declare server_hostname.
+ (main): Use xgethostname(). Set server_hostname.
+ * server.c (MAXHOSTNAMELEN): Remove this macro.
+ (isThisHost): Reference global HOSTNAME rather than looking it up.
+ Improve header comment block.
+ (gserver_authenticate_connection): Likewise.
+ (serve_command_prep): Correct comment.
+ (serve_hostname): Don't bother to validate hostname length.
+
+2004-11-19 Derek Price <address@hidden>
+
+ * server.c (isThisHost): strcasecmp before consulting the DNS as an
+ optimization.
+
+2004-11-18 Mark D. Baushke <address@hidden>
+
+ * checkout.c (checkout_proc): Passing the repository to
+ tag_check_valid seems to stop the assertion failure in recurse.c
+ do_recursion.
+ * sanity.sh (basic2-21a): Removed.
+ (basic2-21c): Fixed.
+
+2004-11-18 Derek Price <address@hidden>
+
+ * sanity.sh (skip_always, notproxy): New functions.
+ (skip, remoteonly, sshstdio, client): Use new functions.
+
+2004-11-17 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (basic2-21a): The val-tags file should have
+ at least 'rtagged-by-head y' in it. Test from cvs 1.11.18 that
+ shows fixed result.
+ (basic2-21b, basic2-21c): New tests showing a cvs bug when
+ val-tags is not present.
+ (Report from "John Elgin" <address@hidden>.)
+
+ * sanity.sh (basicb-21): POSIX 1003.2 specifies 'illegal option'
+ while lots of getopt implementations still use 'invalid option'.
+ Allow either form for this test.
+
+2004-11-17 Mark D. Baushke <address@hidden>
+
+ * buffer.c (fd_buffer_block): Deal with BSD and BSDI problems to
+ set block/nonblock on /dev/null.
+
+2004-11-17 Derek Price <address@hidden>
+
+ * Makefile.am (distclean-local): Clean check.plog~.
+
+2004-11-11 Derek Price <address@hidden>
+
+ * sanity.sh: s/cp -r/cp -R/ to meet POSIX specification.
+ (Thanks to report from Paul Eggert <address@hidden>.)
+
+2004-11-10 Mark D. Baushke <address@hidden>
+
+ * ms-buffer.c (ms_buffer_input): Avoid UNICOS cc error where
+ 'Both sides of the assignment operator are not compatible.'
+
+ * sanity.sh (importc, rcs, rcs4, tagdate): Use TZ=UTC0 not TZ=UTC
+ to get proper POSIX behavior on MacOS X.
+
+2004-11-10 Derek Price <address@hidden>
+
+ * sanity.sh: Actually parse -e option like we claim to.
+
+2004-11-10 Derek Price <address@hidden>
+
+ * sanity.sh: Maintain pass/skip/warn status and output at end.
+ (usage): Note new functionality of -e.
+ (warn): New function.
+ (verify_tmp_empty): Warn instead of failing. Delete turds if warn()
+ doesn't exit.
+
+2004-11-10 Derek Price <address@hidden>
+
+ * sanity.sh (verify_tmp_empty): New function.
+ (dotest_internal_*): Call verify_tmp_empty as needed.
+
+2004-11-09 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (expr_tooltest3): Bugfix. Use $expr rather than $EXPR.
+
+2004-11-08 Derek Price <address@hidden>
+
+ * sanity.sh (version_test): Echo good version data to the log, even
+ when it went to stderr. Don't echo bad version data.
+
+2004-11-08 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (tool_find): Rewrite. API changed to allow a list of
+ tests to be used against a list of possible command names found on
+ the SEARCHPATH.
+ (version_test): Obtain the version of tools under test if
+ possible.
+ (id_tool_test): Check that 'id -u' and 'id -un' work.
+ (expr_tooltest1): Check for NextStep 3.3 expr bug.
+ (expr_tooltest2): Check for SunOS expr multi-line pattern bug.
+ (expr_create_bar): Create a test file for expr testing.
+ (expr_tooltest3): Use it and test for big multi-line identity
+ matches.
+ (expr_set_ENDANCHOR): Find and set the right value for ENDANCHOR.
+ (expr_set_DOTSTAR): Find and set the right value for DOTSTAR.
+ (expr_tooltest_DOTSTAR): Ensure that DOTSTAR works with big
+ matches.
+ (tr_tooltest1): Verify that tr handles NUL bytes.
+ (ls_tooltest): See if /bin/ls returns true even if wildcard does
+ not match any files.
+ (awk_tooltest1): Verify that awk the BEGIN clause works properly.
+ (awk_tooltest2): Verify that print %c format item works properly.
+
+2004-11-08 Derek Price <address@hidden>
+
+ * sanity.sh (verify_tmp_empty): New function.
+ (dotest_internal_*): Call verify_tmp_empty as needed.
+
+2004-11-08 Derek Price <address@hidden>
+
+ * sanity.sh (run_filter): Add function header comment block.
+
+2004-11-07 Larry Jones <address@hidden>
+
+ * sanity.sh: Remove trailing / from cp -r commands.
+
+2004-11-04 Derek Price <address@hidden>
+
+ * gssapi-client.c (connect_to_gserver): Silence gcc -Wall.
+
+2004-11-04 Derek Price <address@hidden>
+
+ * sanity.sh (set_bad_tool): Remove unnecessary quotes.
+
+2004-11-04 Derek Price <address@hidden>
+
+ * sanity.sh: s/depends_on_/require/.
+
+2004-11-04 Derek Price <address@hidden>
+
+ * sanity.sh (find_tool): Eliminate variable with single reference.
+
+2004-11-04 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (SEARCHPATH): Unify the PATHs that are to be searched.
+ (Which,find_tool): Use SEARCHPATH.
+ (LS): Use default $SEARCHPATH for Which.
+ (depend_on_rsync): Use default $SEARCHPATH for Which.
+
+2004-11-04 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Do not echo rsync information to
+ stdout. Look for rsync in more directories.
+
+2004-11-04 Derek Price <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Minor simplifications. Make sure that
+ an rsync that doesn't understand `--version' sends its error message to
+ the log file too.
+
+2004-11-04 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (set_bad_tool, is_bad_tool): Avoid printing errors
+ about the same tool multiple times.
+
+2004-11-03 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Deal with missing rsync.
+
+2004-11-03 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Include rsync version information
+ in output.
+
+2004-11-03 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (depends_on_rsync): More rigorous tests for rsync
+ 2.3.1 problems with --delete --include dir --exclude '*/.
+
+2004-11-03 Derek Price <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Verify that `rsync --delete' actually
+ does what it is supposed ot do.
+
+2004-11-03 Derek Price <address@hidden>
+
+ * subr.c (Xreadlink): Remove MAXSIZE macro. Minor reformatting.
+
+2004-11-03 Mark D. Baushke <address@hidden>
+
+ * filesubr.c (xreadlink): Remove and use GNULIB version.
+ (islink): Return ssize_t instead of bool.
+ (copy_file): Use Xreadlink().
+ * cvs.h: Add include "xreadlink.h"
+ (islink): Return ssize_t instead of bool.
+ * import.c (preserve_initial_permissions): Use Xreadlink.
+ * rcs.c (RCS_checkin): Ditto.
+ * update.c (special_file_mismatch): Ditto.
+ * subr.c (get_file): Ditto.
+ (resolve_symlink): Ditto.
+ (Xreadlink): New interface to xreadlink(), never return NULL.
+
+2004-11-02 Mark D. Baushke <address@hidden>
+
+ * filesubr.c (MAXSIZE): New macro.
+ (xreadlink): Ensure initial buffer size does not exceed MAXSIZE.
+ Avoid cast. If readlink fails with buffer size just under MAXSIZE,
+ try again with MAXSIZE.
+
+2004-11-02 Mark D. Baushke <address@hidden>
+
+ * filesubr.c (xreadlink): AIX and HP-UX readlink() returns ERANGE
+ when there is not enough room in the buffer.
+
+2004-11-01 Derek Price <address@hidden>
+
+ * cvs.h: Remove getdate proto in favor of including getdate.h.
+ * client.c (handle_mod_time), history.c (history), main.c (Make_Date,
+ format_date_alloc), rcs.c (RCS_getrevtime), server.c
+ (serve_checkin_time): Use new get_date API.
+
+2004-11-01 Derek Price <address@hidden>
+
+ * sanity.sh (rcslib): Fix typo in path.
+
+2004-11-01 Derek Price <address@hidden>
+
+ * sanity.sh (rcslib): Test a link to a path longer than 128
+ characters.
+
+2004-10-30 Mark D. Baushke <address@hidden>
+
+ * patch.c (patch_cleanup): Add signal argument and use it.
+
+2004-10-30 Mark D. Baushke <address@hidden>
+
+ * client.c: Adjust include files to avoid problems with incomplete
+ types under --disable-client.
+ * msg-buffer.c: Deal with --disable-client --disable-server
+ implicitly meaning --disable-proxy.
+ * server.c: Adjust include files to avoid problems with incomplete
+ types under --disable-server.
+
+2004-10-30 Mark D. Baushke <address@hidden>
+
+ * server.c (isThisHost): Deal with possibility of a missing
+ hstrerror() function.
+
+2004-10-29 Derek Price <address@hidden>
+
+ * server.c (server_root, move_file_offset, replace_file_offset):
+ Remove dead code.
+
+2004-10-29 Derek Price <address@hidden>
+
+ * filesubr.c (xreadlink): Make sure allocation is tried once at the
+ maximum buffer size. Protect against overflow.
+
+2004-10-29 Derek Price <address@hidden>
+
+ * server.c (isSameHost): Handle gethostname & gethostbyname errors.
+
+2004-10-29 Derek Price <address@hidden>
+
+ * server.c (isSameHost): Use strcasecmp to compare host names.
+
+2004-10-29 Mark D. Baushke <address@hidden>
+
+ * server.c: Need to #include <netdb.h> for either PROXY_SUPPORT or
+ HAVE_GSSAPI to get gethostbyname() declarations for
+
+2004-10-29 Mark D. Baushke <address@hidden>
+
+ * filesubr.c (SIZE_MAX, SSIZE_MAX): Use #include "xsize.h" instead.
+ (xreadlink): Use xrealloc instead of xmalloc/free.
+
+2004-10-29 Mark D. Baushke <address@hidden>
+
+ * filesubr.c (SIZE_MAX, SSIZE_MAX): New constants.
+ (xreadlink): Deal with symlinks longer than 127 bytes.
+ (Problem reported as issue 190 by Gottfried Ganssauge
+ <address@hidden>.)
+
+2004-10-29 Derek Price <address@hidden>
+
+ * server.c (isSameHost): New fuction.
+ (same_path): Rename to...
+ (isSamePath): ...this.
+ (isProxyServer): Use new functions/names.
+
+2004-10-29 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (writeproxy): Use DOTSTAR to avoid problems with
+ a missing Gssapi-authenticate in Valid-requests.
+
+2004-10-28 Derek Price <address@hidden>
+
+ * server.c (same_path): New function.
+ (isProxyServer): Use new function.
+ * sanity.sh (writeproxy): Test that server resolves symlinks when
+ deciding whether it is a primary server.
+
+2004-10-28 Derek Price <address@hidden>
+
+ * cvs.h (isdir, isfile, islink, isdevice, isreadable, iswritable,
+ isaccessible, isabsolute): Return boolean rather than int.
+ * filesubr.c (isdir, isfile, islink, isdevice, isreadable, iswritable,
+ isaccessible), subr.c (isabsolute): Ditto. Some reformatting.
+ * filesubr.c (xresolvepath): Use save_cwd in place of xgetwd. Some
+ reformatting.
+
+2004-10-28 Derek Price <address@hidden>
+
+ * client.c (handle_redirect): Detect redirect loops.
+ * sanity.sh (client-20): Test that client detects redirect loops.
+
+2004-10-28 Mark D. Baushke <address@hidden>
+
+ * release.c (release): Allow builds of cvs with --disable-server
+ --disable-client both used for local installation configuration.
+ * root.c (Name_Root): Ditto.
+ * update.c (checkout_file): Ditto.
+ * edit.c (edit_fileproc): Ditto.
+ * import.c (import): Ditto.
+ (Problem reported by Jean Olivier Caron <address@hidden>.)
+
+2004-10-27 Mark D. Baushke <address@hidden>
+
+ * cvs.h (RCS_FLAGS_USETIME): New flag.
+ * rcs.c (RCS_checkin): Add citime argument.
+ * rcs.h (RCS_checkin): Ditto.
+ * checkin.c (Checkin): Pass new RCS_checkin argument.
+ * commit.c (remove_file, checkaddfile): Ditto.
+ * import.c (add_rev): Ditto.
+
+ * sanity.sh (tagdate): Delete tagdate-19b as an incorrect test.
+
+2004-10-27 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (tagdate): Provide more output.
+
+2004-10-26 Mark D. Baushke <address@hidden>
+
+ * commit.c (checkaddfile): Create a dead version for a new file
+ added to a branch. Fixes FIXCVS for tagdate tests.
+ * sanity.sh (tagdate): Update to expect correct results.
+ (death2, branch-after-import, join, ignore-on-branch): Ditto.
+
+2004-10-26 Mark D. Baushke <address@hidden>
+
+ * server.c (isProxyServer): Fix hostname setup.
+
+2004-10-26 Derek Price <address@hidden>
+
+ Call all exit handlers via atexit() & exit(). Signal handlers exit().
+ Eliminates a compiler warning.
+
+ * exithandle.c (cleanup_register): Don't register a signal handler.
+ Register a function to block signals before the exit handler is called.
+ * lock.c (Lock_Cleanup): Remove never_run_again cruft and assoc. cmts.
+ * rcs.c (rcs_cleanup), server.c (server_cleanup): Ditto, plus assume
+ signals are blocked.
+ * main.c (main_cleanup): Declare noreturn attribute.
+
+2004-10-26 Derek Price <address@hidden>
+
+ * gssapi-client.c (connect_to_gserver): Avoid truncating error messages
+ from the GSSAPI server.
+ (Report from Dan Peterson <address@hidden>.)
+
+2004-10-26 Derek Price <address@hidden>
+
+ * sanity.sh (import-quirks): Test an even branch number.
+
+2004-10-25 Derek Price <address@hidden>
+
+ * import.c (import): Repair regex for regressions introduced in last
+ commit.
+ * sanity.sh (import-quirks): Test a few branch numbers import shouldn't
+ have a problem with.
+
+2004-10-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (modes3): Quiet rsync messages in proxy mode when
+ permissions are removed. They are expected and not a problem here.
+
+2004-10-25 Derek Price <address@hidden>
+
+ * import.c (import): Anchor and simplify branch verification regex.
+ * sanity.sh (import-quirks): Test another pattern that should fail.
+
+2004-10-25 Derek Price <address@hidden>
+
+ * cvs.h (yesno): Remove prototype.
+ * edit.c, release.c: Include yesno.h. Flush output before calling
+ yesno().
+
+2004-10-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (tagdate): Added some additional tests and FIXCVS
+ comments for dealing properly with a 'cvs add' of a file to
+ a branch that already exists on the mainline.
+ (Problem reported by Renny Barrett <address@hidden>.)
+
+ * sanity.sh (getrlogdate): New shell function.
+ (tagdate-{13,14,16}): Use it to avoid 'sleep 60' by using
+ the exact 1.1.4.1 timestamp for tagdate-14 and tagdate-16.
+
+2004-10-25 Derek Price <address@hidden>
+
+ * sanity.sh (depends_on_rsync): Redirect rsync output to /dev/null when
+ just testing.
+
+2004-10-23 Mark D. Baushke <address@hidden>
+
+ * socket-client.c (socket_buffer_initialize): Drop obsolete
+ arguments to buf_initialize().
+
+2004-10-22 Mark D. Baushke <address@hidden>
+
+ * client.c (handle_m, handle_e): Winsock is returning
+ SOCK_ERRNO == WSAENOTSOCK for select() problems and not
+ setting errno. Do not bother with printing an error from a
+ select() that is not returning an non-zero errno.
+ (Report from Conrad T. Pino <address@hidden>.)
+
+2004-10-22 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (proxy): proxycheck depends on rsync, but skip all if
+ no rsync is found rather than generate an error return.
+
+2004-10-22 Mark D. Baushke <address@hidden>
+
+ * Makefile.am (proxycheck): New test target.
+ * sanity.sh: Keep one more level of check.log backup.
+ * Makefile.in: Regenerated.
+
+2004-10-22 Derek Price <address@hidden>
+
+ * client.c, update.c: Use new MD5 interface.
+
+2004-10-22 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (find_tool): Search /usr/pkg/bin for NetBSD tools
+ like rsync.
+
+2004-10-22 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (tagdate): Fix typo.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * subr.c: #include vasnprintf.h to avoid compiler warning.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * cvs.h (error_use_protocol): Move decl here from lib/error.h so that
+ we may use an unforked error.h from GNULIB.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * main.c: #include strftime.h.
+ (format_time_t, gmformat_time_t): Supply missing args to my_strftime.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * buffer.c: Don't maintain last_index & last_count for buffers.
+ * buffer.h (struct buffer): Update struct.
+ (buf_initialize): Update proto.
+ * log-buffer.c, ms-buffer.c, zlib.c: Update all callers.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * sanity.sh: Fail gracefully in proxy mode when rsync is defective.
+ (depends_on_rsync): New function.
+ (writeproxy, writeproxy-noredirect, writeproxy-ssh): Skip these tests
+ gracefully when rsync is missing.
+
+2004-10-21 Mark D. Baushke <address@hidden>
+
+ * add.c (add): Pay attention to cvswrite mode when resurrecting a
+ file that was not yet committed.
+ (Report from Frank Hemer <address@hidden>.)
+ * sanity.sh (resurrection): Add new tests to deal with read-only
+ mode on a cvs add durring a resurrection. Verify that -r is not
+ honored when the resurrected file must be committed to be seen.
+
+2004-10-21 Derek Price <address@hidden>
+
+ * ls.c (ls_fileproc): Deal with files specified on the command line.
+ (ls_delproc): Move to a more accessible location.
+
+2004-10-20 Derek Price <address@hidden>
+
+ * add.c (add): Avoid unnecessary typecast.
+
+2004-10-20 Mark D. Baushke <address@hidden>
+
+ * Makefile.in: Regenerate for new configure.in.
+
+2004-10-19 Mark D. Baushke <address@hidden>
+
+ * add.c (add): Backout last typecasts cleanup.
+
+2004-10-19 Derek Price <address@hidden>
+
+ * add.c (add): Avoid attempting to resurrect a dead rev 1.1.
+ * sanity.sh (resurrection): Add test for the above.
+ (Report from Dan Peterson <address@hidden>.)
+
+2004-10-19 Derek Price <address@hidden>
+
+ * add.c (add): Avoid unnecessary typecasts.
+
+2004-10-19 Derek Price <address@hidden>
+
+ * cvs.h: Prototype new function.
+ * subr.c (Xasnprintf): New function.
+ * root.c (primary_root_translate, primary_root_inverse_translate):
+ s/asnprintf/Xasnprintf/.
+ * client.c (connect_to_pserver): Store line length for efficiency.
+
+2004-10-19 Derek Price <address@hidden>
+
+ * history.c: Remove unecessary typecasts. Some reformatting.
+
+2004-10-18 Derek Price <address@hidden>
+
+ * server.c (serve_modified): Eliminate >= 0 check since size_t may not
+ be negative.
+ (Originally reported by Martin Neitzel <address@hidden>.)
+
+2004-10-18 Derek Price <address@hidden>
+
+ * cvs.h (DEVNULL): This is system dependant. Move it to lib/system.h.
+ * client.c (copy_a_file): Consolidate USE_VMS_FILENAMES stuff under a
+ single #ifdef.
+
+2004-10-15 Derek Price <address@hidden>
+
+ * cvs.h: Don't include vasnprintf.h.
+ (Xasprintf): New prototype.
+ * client.c, edit.c, history.c, import.c, ls.c, main.c, parseinfo.c,
+ recurse.c, release.c, repos.c, root.c, server.c, status.c, subr.c,
+ vers_ts.c: s/asnprintf/Xasprintf/.
+ * root.c: Include vasnprintf.h.
+ (primary_root_translate, primary_root_inverse_translate): Use
+ asnprintf() properly.
+ * subr.c: Include vasprintf.h.
+ (Xasprintf): New function.
+
+2004-10-14 Derek Price <address@hidden>
+
+ * import.c (import): Remove an unecessary level of nesting. Simplify
+ xmalloc/sprintf with asnprintf. Remove useless comment.
+
+2004-10-14 Derek Price <address@hidden>
+
+ * import.c (import): Verify branch specifications more thoroughly.
+ * sanity.sh (importb): Adapt to new error message.
+ (import-quirks): New test.
+
+2004-10-14 Mark D. Baushke <address@hidden>
+
+ * server.c (server_pause_check, do_cvs_command, server_cleanup):
+ Avoid typecasts.
+
+2004-10-14 Derek Price <address@hidden>
+
+ * gssapi-client.c: Use new size_t buffer APIs.
+
+2004-10-11 Mark D. Baushke <address@hidden>
+
+ * buffer.h, buffer.c (buf_output, buf_input_data, buf_read_line,
+ buf_read_data, struct packetizing_buffer,
+ packetizing_buffer_initialize) Use size_t instead of int.
+ Silences warnings in buffer.c, server.c, and zlib.c on OpenBSD
+ sparc64 where sizeof(int) is not the same as sizeof(size_t).
+ * client.c (read_line_via, read_line, try_read_from_server,
+ get_server_response, handle_ok, handle_error,
+ handle_valid_requests, handle_checked_in, handle_new_entry,
+ handle_checksum, handle_copy_file, handle_updated, handle_merged,
+ handle_patched, handle_rcs_diff, handle_removed,
+ handle_remove_entry, handle_set_static_directory,
+ handle_clear_static_directory, handle_set_sticky,
+ handle_clear_sticky, handle_clear_template,
+ handle_module_expansion, handle_wrapper_rcs_option, handle_m,
+ handle_e, handle_f, handle_notified): Ditto
+ * client.h (struct response): Ditto.
+ * server.c (receive_partial_file, receive_file, serve_modified,
+ do_cvs_command): Ditto.
+ * zlib.c (compress_buffer_input, compress_buffer_shutdown_input):
+ Ditto.
+ (Patch from Alexander Taler <address@hidden>.)
+
+2004-10-08 Derek Price <address@hidden>
+
+ * client.c (send_args): Carry through a const to silence gcc -Wall.
+ * hardlink.c (delhardlist): New function.
+ (lookup_file_by_inode): Use new function as delproc since it should
+ work. Carry through a const to silence gcc -Wall.
+ * ms-buffer.c (ms_buffer_initialize): Use a working delproc.
+
+2004-10-08 Derek Price <address@hidden>
+
+ * client.c (open_connection_to_server, close_connection_to_server,
+ handle_redirect): Add traces.
+
+2004-10-07 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add parseinfo.h.
+
+2004-10-07 Mark D. Baushke <address@hidden>
+
+ * vers_ts.c (entries_time): Use size_t pointers. sizeof(int)
+ may not be the same as sizeof(size_t) on OpenBSD sparc64.
+ (Patch from Alexander Taler <address@hidden>.)
+
+ * wrapper.c (wrap_clean_fmt_str): Make static.
+
+2004-10-07 Mark D. Baushke <address@hidden>
+
+ * client.h (struct response): Make name a const.
+ * cvs.h (RETSIGTYPE): Use a full prototype.
+ * hash.h (struct node): Use a full prototype for delproc.
+
+ * client.c (send_arg, send_option_string, option_with_arg): Make
+ args of function const.
+ * client.h (send_arg, send_option_string, option_with_arg): Ditto.
+
+ * checkout.c (checkout): Make valid_options const.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * release.h: Silence gcc -Wall.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * cvs.h: Move include of getopt.h to system.h with similar headers.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * login.c: Include getpass.h.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * cvs.h: Include strcase.h.
+ (cvs_casecmp): remove proto.
+ * subr.c (cvs_casecmp): Remove function.
+ * parseinfo.c: s/cvs_casecmp/strcasecmp/.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * cvs.h: Move getopt.h and regex.h into the GNULIB include section.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * main.c (cmds): Release calls unedit, to it modifies the repository.
+ (struct cmd): Add const and full prototype where needed.
+ * sanity.sh (edit-check): Use modify_repo where needed.
+ (release): No more special case for release in proxy mode.
+ (Patch from Mark D. Baushke <address@hidden>.)
+
+ * release.c (release): Simplify login in if statement.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * client.c (send_file_names): Back out broken portion of previous
+ change.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * client.c (call_in_directory): Make args of function arg const.
+ * client.h, client.c, edit.h, edit.c, server.h, zlib.c: Carry change
+ through to called functions and functions passed in as args.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * checkout.c, commit.c, edit.c, rcs.c: Avoid typecasts. Some
+ reformatting.
+
+2004-10-06 Derek Price <address@hidden>
+
+ * client.c: Avoid more typecasts. Some reformatting.
+
+2004-10-06 Mark D. Baushke <address@hidden>
+
+ * client.c (send_file_names): Use new save-cwd API.
+ (connect_to_pserver): Use a union to avoid incompatible pointer
+ type warnings.
+
+2004-10-05 Derek Price <address@hidden>
+
+ * cvs.h (Xstrdup): New proto mapped via define to xstrdup.
+ * subr.c (Xstrdup): New function.
+
+2004-10-05 Derek Price <address@hidden>
+
+ * add.c, client.c, history.c, import.c, mkmodules.c, modules.c,
+ recurse.c, release.c, tag.c, update.c: Use new save-cwd API.
+
+2004-10-05 Derek Price <address@hidden>
+
+ * checkout.c, client.c, commit.c, create_adm.c, cvs.h, filesubr.c,
+ hardlink.c, history.c, logmsg.c, main.c, recurse.c, update.c:
+ s/xgetwd/xgetcwd/.
+
+2004-10-04 Derek Price <address@hidden>
+
+ * client.c (responses): Add "Edit-file".
+ (handle_edit_file): New function.
+ * commit.c (usage): Add -c option.
+ (check_fileproc): Check for edit when requested.
+ (commit_fileproc): Use new notify_do API.
+ * edit.c (check_edited): New global.
+ (setting_tedit, setting_tunedit, setting_tcommit): s/int/bool/.
+ (ncheck_fileproc): Use new notify_do API.
+ (send_notifications): Remove redundant proto. Remove unnecessary
+ repository lock.
+ (editors_output, find_editors_and_output, edit_file): New functions.
+ edit_file() factored from...
+ (edit_fileproc): ...here. Skip files with existing editors when
+ requested.
+ (usage): Add -c and -f.
+ (edit): Handle new -c and -f options.
+ (notify_do): Accept update_dir as an argument for user messages.
+ (editors_fileproc): Factor most content to find_editors_and_output()
+ and edit_file().
+ * edit.h (notify_do): Proto new API.
+ (editors_output, edit_file): New functions.
+ * rcs.c (RCS_unlock): Use new notify_do() API.
+ * sanity.sh: Update assorted tests to compensate for new output.
+ (edit-check): New tests.
+ * server.c (gupdate_dir): New global.
+ (struct notify_note): Keep track of update_dir.
+ (serve_notify): Use update_dir.
+ (serve_hostname, serve_localdir, serve_edit, server_edit_file): New
+ functions.
+ (server_notify): Use new notify_do() API.
+ (requests): Add Hostname, LocalDir, and edit.
+ * server.h (server_edit_file): New proto.
+ (Note: Original design of new advisory lock behavior came from Noel Yap
+ <address@hidden>'s original advisory locks patch, originally ported
+ forward and enhanced by Matthew Ogilvie <address@hidden>.)
+
+2004-10-04 Derek Price <address@hidden>
+
+ * admin.c (postadmin_proc), edit.c (notify_proc), fileattr.c
+ (postwatch_proc), logmsg.c (logfile_write, verifymsg_proc), server.c
+ (prepost_proxy_proc), tag.c (pretag_proc, posttag_proc): Pass referrer
+ to called scripts when possible.
+ * client.c (handle_redirect): Save the original server.
+ (start_server): Send referrer to the new server if possible.
+ * sever.c (referrer): New global.
+ (serve_referrer): Save referrer.
+ (requests): Add "Referrer" response.
+ * server.h (referrer): Add extern decl.
+ * sanity.sh (ssh-wrapper): Use in remote mode too.
+ (writeproxy-ssh): New tests.
+
+2004-10-04 Derek Price <address@hidden>
+
+ * cvs.h (CVSROOT_DFLT): Undef rather than defining to NULL.
+ * main.c (main): Untangle parsing of CVSROOT, eliminating several
+ variables in the process. Simplify xmalloc/sprintf with asnprintf.
+
+2004-10-04 Derek Price <address@hidden>
+
+ * root.c (primary_root_translate, primary_root_inverse_translate,
+ get_local_root_dir, local_cvsroot): Simplify logic without proxy
+ support.
+
+2004-10-02 Mark D. Baushke <address@hidden>
+
+ * root.c (primary_root_translate, primary_root_inverse_translate,
+ get_local_root_dir): Protect PrimaryServer with #ifdef
+ PROXY_SUPPORT
+
+2004-10-01 Mark D. Baushke <address@hidden>
+
+ * main.c (main): Initialize CVSroot before it is used.
+ (Report and patch by Martin Neitzel <address@hidden>.)
+ * sanity.sh (status): Test it.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * checkout.c (checkout_proc), fileattr.c (fileattr_read), find_names.c
+ (Find_Names), myndbm.c (mydbm_open, mydbm_load_file), parseinfo.c
+ (Parse_Info), rcs.c (RCS_parsercsfile_i, rcsbuf_getkey,
+ rcsbuf_getrevnum, rcsbuf_valword): Root translation functions no longer
+ allocate.
+ * commit.c (commit): Use send_a_repository rather than reimplementing.
+ * main.c (main): Remove --primary-root option.
+ * root.c (primary_root_add): Remove this function.
+ (primary_root_in, primary_root_out): Remove globals.
+ (primary_root_translate, primary_root_inverse_translate): Return const.
+ Don't allocate return value. Rely on parsed root and confg's
+ PrimarServer rather than separately maintained globals.
+ (get_local_root_dir): New function.
+ (local_cvsroot): Translate requests for primary roots when necessary.
+ * root.h (primary_root_add, primary_root_translate,
+ primary_root_inverse_translate): Update protos to match.
+ * sanity.sh (top level, writeproxy): Simplify $proxy setup.
+ (client): Don't execute $proxy. Simplify skips and reindent.
+ (writeproxy-noredirect): Use --allow-root in place of --primary-root.
+ Save and restore $PRIMARY_CVSROOT*.
+ * server.c (serve_root): Rely on local_cvsroot() to do any necessary
+ root translation.
+ (serve_directory): Root translation functions no longer allocate.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * client.c (arg_should_not_be_sent_to_server), create_adm.c
+ (Create_Admin), recurse.c (start_recursion, do_recursion, do_dir_proc),
+ release.c (release), repos.c (Name_Repository, Short_Repository),
+ update.c (update_filesdone_proc):
+ s/original_root/original_parsed_root->MEMBER/.
+ * main.c (set_root_directory): Set original_parsed_root when setting
+ current_parsed_root.
+ (main): s/original_root/original_parsed_root->MEMBER/.
+ * root.c (original_root): Make cvsroot_t and rename to...
+ (original_parsed_root): ...this.
+ * root.h: Update extern decl to match.
+ * server.c (serve_root): Set original_parsed_root.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * server.c (serve_questionable): Use pending errors per API.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * repos.c (Name_Repository): asnprintf, not sprintf.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * sanity.sh (ignore-11r): Rename second occurance to...
+ (ignore-11ar): ...this.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * sanity.sh (1): Rename to...
+ (init-1): ...this.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * repos.c (Name_Repository): Simplify string construction with
+ asnprintf(). Improve comment. Some reformatting.
+
+2004-10-01 Derek Price <address@hidden>
+
+ * release.c (release): Simplify string construction with asnprintf().
+
+2004-10-01 Derek Price <address@hidden>
+
+ * recurse.c (do_file_proc): Improve header comment. Replace
+ xmalloc()/strcat() combination with asnprintf().
+
+2004-09-29 Derek Price <address@hidden>
+
+ * sanity.sh (config): Handle $SECONDRY_ROOT_DIRNAME output in $proxy
+ mode.
+
+2004-09-29 Mark D. Baushke <address@hidden>
+
+ * sanity.sh: Workaround MacOS X '/bin/ls' is not returning false
+ when no files are listed. Label some old tests.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * cvs.h: Include parseinfo.h.
+ (top_level_admin, UseNewInfoFmtStrings,
+ ImportNewFilesToVendorBranchOnly, PrimaryServer, MaxProxyBufferSize,
+ MaxCommentLeaderLength, UseArchiveCommentLeader, RereadLogAfterVerify,
+ lock_dir, UserAdminOptions): Move extern decls for global config opts
+ to config struct in parseinfo.h.
+ (config): New global.
+ (parse_config): Move proto to...
+ * parseinfo.h: This new file.
+ * parseinfo.c (parse_config): Return struct config.
+ (new_config): New function.
+ * admin.c: Remove global UserAdminOptions.
+ (admin): Don't check config on client.
+ * checkin.c (Checkin), checkout.c (checkout_proc), filesubr.c (xchmod),
+ import.c (import, add_rcs_file, expand_and_copy_contents):
+ Use config instead
+ of globals. Some reformatting.
+ * history.c (logHistory): Move to struct config.
+ (history_write): Use config instead of globals.
+ * lock.c (lock_dir): Move global to struct config.
+ (lock_name): Use config instead of globals.
+ * logmsg.c (RereadLogAfterVerify): Move global to struct config.
+ (do_verify, logfile_write, verifymsg_proc): Prefer config to globals.
+ * main.c (top_level_admin, UseNewInfoFmtStrings, PrimaryServer,
+ MaxProxyBufferSize, MaxCommentLeaderLength, UseArchiveCommentLeader,
+ ImportNewFilesToVendorBranchOnly): Move globals to struct config.
+ (config): New global.
+ (main): Use new parse_config API.
+ * rcs.c (preserve_perms, keywords): Move globals to struct config.
+ (keyword_local): Move to struct rcs_keyword.
+ (new_keywords, free_keywords): New functions.
+ (expand_keywords, RCS_setlocalid, RCS_setincexc): Prefer config to
+ globals.
+ * rcs.h (free_keywords): New proto.
+ (RCS_setincexc, RCS_setlocalid): Accept opaque keywords element.
+ * root.c (delconfig, get_root_allow_config): New functions.
+ * root.h (get_root_allow_config): New proto.
+ * server.c (system_auth): Move global to struct config.
+ (isProxyServer, become_proxy, serve_command_prep): Prefer config to
+ globals.
+ (serve_root): Ditto. Set config.
+ * server.h (system_auth): Move extern decl for global moved to config.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * history.h: Protect against multiple include.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * root.c (root_allow_count, root_allow_vector, root_allow_size):
+ Replace with...
+ (root_allow): ...this single List *.
+ (root_allow_add, root_allow_free, root_allow_ok): Use new List API.
+ Make args const. Return bool rather than int when necessary.
+ * root.h (root_allow_add, root_allow_ok): Update protos to match.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * admin.c (admin): s/int/bool/ as appropriate. Some reformatting.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * history.c (history_write): Use asnprintf(). Some reformatting.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * import.c (killnew): s/int/bool/.
+ (add_rcs_file): Ditto for do_killnew.
+ * rcs.h (add_rcs_file): Change proto to match.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * client.c (start_server): Use bool in place of int.
+
+2004-09-29 Derek Price <address@hidden>
+
+ * subr.c (cvs_trace): Correct header comment. Some reformatting.
+
+2004-09-28 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (ssh-wrapper-env): New script to avoid the assumption
+ that the remote end of a $CVS_RSH is running a bourne shell.
+ (ssh-wrapper): Use it.
+
+2004-09-28 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy): Remove some setup obsoleted by redirects.
+
+2004-09-27 Derek Price <address@hidden>
+
+ Use original_root for client comparisons with CVS/Root since
+ current_parsed_root may be the product of a redirect.
+
+ * client.c (handle_redirect): Don't free current_parsed_root. Validate
+ new roots.
+ (args_should_not_be_sent_to_server):
+ s/current_parsed_root->original/original_root/.
+ * create_adm.c (Create_Admin): Likewise.
+ * cvs.h (current_parsed_root): Move extern decl...
+ * root.h: ...here. Decl original_root extern.
+ * main.c (set_root_directory): Store original_root.
+ (main): s/current_parsed_root->original/original_root/.
+ * recurse.c, release.c, update.c: Likewise.
+ * root.c: Declare original_root.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * sanity.sh (parseroot2): Correct two test names. Restore CVSROOT.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Use TRACE_FLOW, not TRACE_FUNCTION since this
+ routine is called repeatedly by the recursion routines.
+ * sanity.sh (multiroot2): Adjust to compensate.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * sanity.sh (parseroot2): Use remoteonly.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * sanity.sh (tests): Add parseroot2.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * recurse.c (do_recursion, do_dir_proc): Make process_this_directory a
+ boolean.
+
+2004-09-24 Derek Price <address@hidden>
+
+ * sanity.sh (parseroot2): New test for root parsing consistency.
+ (Original patch from Alexander Taler <address@hidden>.)
+
+ * cvs.h (Name_Root, free_cvsroot_t, parse_cvsroot, local_cvsroot,
+ Create_Root, root_allow_add, root_allow_free, root_allow_ok): Move
+ these protos to...
+ * root.h: ...here.
+ * client.c (arg_should_not_be_sent_to_server), recurse.c
+ (start_recusrion, do_recursion): Use new Name_Root API.
+ * main.c (current_root): Remove global.
+ (set_root_directory): Set current_parsed_root directly.
+ (main): Use new Name_Root API. Restore deletion of root directories
+ list.
+ * root.c (Name_Root): Return a parsed cvsroot_t rather than a string.
+
+2004-09-24 Mark D. Baushke <address@hidden>
+
+ * buffer.c (buf_append_buffer): Fix typo in comment.
+
+2004-09-23 Mark D. Baushke <address@hidden>
+
+ * buffer.c (buf_read_file): Fix typo in comment.
+ (buf_read_file_to_eof): Ditto.
+ * server.c (serve_command_prep): Ditto.
+
+2004-09-23 Derek Price <address@hidden>
+
+ * sanity.sh (depends_on_ssh, sshstdio): Don't use skip() to skip
+ remote-only tests.
+
+2004-09-23 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos, sshstdio): Minor modifications to make use of
+ the new depends_on_?sh API.
+
+2004-09-23 Derek Price <address@hidden>
+
+ * sanity.sh: Accept new -e option to interpret non-fatal calls to skip
+ as errors.
+ (skip, depends_on_rsh, depends_on_ssh): New functions.
+
+2004-09-23 Mark D. Baushke <address@hidden>
+
+ * server.c (cvs_output, cvs_output_binary): fflush (stderr)
+ here to avoid problems with 'cvs status 2>&1'.
+ (Report by Frank Hemer <address@hidden>.)
+
+2004-09-17 Derek Price <address@hidden>
+
+ * buffer.c, buffer.h, log-buffer.c, log-buffer.h, main.c, mkmodules.c,
+ parseinfo.c, server.c: Remove TRUST_OS_FILE_CACHE.
+
+2004-09-17 Derek Price <address@hidden>
+
+ * buffer.c (fd_set_block): Ignore FreeBSD /dev/null problem.
+
+2004-09-17 Derek Price <address@hidden>
+
+ * cvs.h (strip_trailing_slashes): Remove proto in favor of including
+ dirname.h from GNULIB.
+ * sever.c (dir_name): Rename to...
+ (gDirname): ...this to avoid conflicts with the GNULIB function.
+
+2004-09-16 Derek Price <address@hidden>
+
+ * expand_path.c (expand_path): Silence `gcc -Wall'. Reformat some
+ comments to fit in 80 characters.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * sanity.sh (sync-secondary, writeproxy, writeproxy-noredirect): Remove
+ redundant checks for $RSYNC readability.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * sanity.sh: Only find $RSYNC once.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy, writeproxy-noredirect): Find $RSYNC in a
+ portable manner.
+
+2004-09-15 Mark D. Baushke <address@hidden>
+
+ * sanity.sh: Find $RSYNC in a portable manner.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * parseinfo.c (parse_config): Complete parsing of the remainder of the
+ config file when errors are encountered. Accept and ignore
+ UseNewInfoFmtStrings=yes when !SUPPORT_OLD_INFO_FMT_STRINGS.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * buffer.c (buf_copy_data): Pass args to buf_append_data correctly.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * sanity.sh: Set $RSYNC in proxy mode.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * main.c: Fix typo in comment.
+
+2004-09-15 Derek Price <address@hidden>
+
+ * rcs.c: s/abort/assert/. Reformat function headers. Remove
+ unnecessary typecasts & prototypes.
+
+2004-09-14 Mark D. Baushke <address@hidden>
+
+ * parseinfo.c (readBool): Remove dead code.
+
+ * filesubr.c (cvs_casecmp): Move to...
+ * subr.c (cvs_casecmp): ...here.
+ * cvs.h (cvs_casecmp): No longer ifdef under SERVER_SUPPORT
+
+ * parseinfo.c (readBool): Return false when there was no boolean
+ found.
+
+2004-09-14 Derek Price <address@hidden>
+
+ * cvs.h (top_level_admin, ImportNewFilesToVendorBranchOnly):
+ s/int/bool/.
+ (MaxCommentLeaderLength, UseArchiveCommentLeader): New vars.
+ * main.c: Ditto, for all four vars above.
+ * mkmodules.c (config_contents): Add default info for
+ MaxCommentLeaderLength & UseArchiveCommentLeader.
+ * parseinfo.c (readBool, readSizeT): New functions.
+ (parse_info): Use new functions. Parse MaxCommentLeaderLength &
+ UseArchiveCommentLeader.
+ * rcs.c (expand_keywords): Limit the size of the comment leader to
+ MaxCommentLeaderLength & fall back to the comment leader specified in
+ the RCS archive when requested.
+ (preserve_perms): s/int/bool/.
+ * rcs.h (preserve_perms), server.c (system_auth), server.h
+ (system_auth): Likewise.
+ * sanity.sh (keywordlog): Add new tests for the above.
+
+2004-09-12 Mark D. Baushke <address@hidden>
+
+ * rcs.c (RCS_checkout): Allow noexec to do checkouts when
+ server_active is true.
+ * sanity.sh (join7): Test above change (fixes a FIXCVS).
+
+2004-09-09 Derek Price <address@hidden>
+
+ * buffer.c (stuct packetizing_buffer): Use size_t & bool as appropriate
+ in preference to int.
+ (packetizing_buffer_output): s/int/size_t/ as appropriate.
+
+2004-09-09 Derek Price <address@hidden>
+
+ * buffer.c (packetizing_buffer_input): s/int/size_t/ as appropriate.
+
+2004-09-09 Mark D. Baushke <address@hidden>
+
+ * root.c (primary_root_inverse_translate): No longer inline.
+
+2004-09-09 Conrad T. Pino <address@hidden>
+
+ * server.c: Add comment before #if at line 5580 to move it further
+ into the file which seems to work around an apparent buffer managment
+ bug in Microsoft Visual C++ 6.0 compiler.
+
+2004-09-08 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (join7): Fix if-then-else conditional.
+
+ * sanity.sh (join7): Re-order join7-5 and join7-6 tests.
+
+2004-09-08 Conrad T. Pino <address@hidden>
+
+ * server.c: Remove extra token in conditional compile line 5580
+ causing error in Windows Visual C++ 6.0 compile.
+
+2004-09-08 Mark D. Baushke <address@hidden>
+
+ * buffer.c (fd_buffer_block): Protect fcntl calls when F_GETFL,
+ O_NONBLOCK and F_SETFL are not available.
+ * server.c (move_file_offset): Ditto.
+ (set_nonblock_fd): Ditto.
+
+ * buffer.c (packetizing_buffer_input): Use size_t rather than int.
+ (struct packetizing_buffer): Ditto.
+
+2004-09-07 Mark D. Baushke <address@hidden>
+
+ * server.c (server_updated): Deal with cvs -n update -jt1 -jt2
+ "protocol error: uncounted data discarded" problem.
+ * sanity.sh (join7): New test for this case.
+
+2004-09-04 Derek Price <address@hidden>
+
+ * socket-client.c (socket_client_initialize): Pass new args to
+ buf_initialize.
+
+2004-09-04 Derek Price <address@hidden>
+
+ Silence `gcc -Wall'.
+
+ * buffer.c (buf_initialize): Remove unnecessary typecasts by using
+ size_t instead of int or bool as args.
+ * buffer.c, import.c, log-buffer.c, ms-buffer.c, zlib.c: Change all
+ callers.
+ * buffer.h, cvs.h: Update protos.
+ * client.c, ls.c, main.c, rcs.c, root.c, server.c: Remove unused vars,
+ add parens, as requested by `gcc -Wall'.
+
+2004-09-04 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (writeproxy-noredirect): Between "Set" and
+ "expand-modules" are the optional Kerberos-encrypt, Gssapi-encrypt
+ and Gssapi-authenticate entries. Use ${DOTSTAR} to deal with
+ these optionally configured requests.
+
+2004-09-04 Mark D. Baushke <address@hidden>
+
+ * import.c (expand_at_signs): Typecasting for fwrite results.
+ * log-buffer.c (log_buffer_input, log_buffer_output,
+ log_buffer_initialize): Ditto.
+
+ * log-buffer.c (log_buffer_initialize): Protect reference to
+ fatal_errors.
+
+ * parseinfo.c (parse_config): Protect reference to PrimaryServer
+ using #ifdef PROXY_SUPPORT.
+
+ * ls.c (ls_fileproc): Remove unused variables.
+ * subr.c (increment_revnum): Ditto.
+ * vers_ts.c (time_stamp): Ditto.
+
+2004-09-03 Derek Price <address@hidden>
+
+ * rcs.c (RCS_checkin), commit.c (remove_file): Accept UPDATE_DIR
+ argument and use it to output full relative path on commit.
+ * rcs.h (RCS_checkin): Update prototype.
+ * checkin.c, commit.c, import.c: Change all callers.
+ * sanity.sh: Adjust to compensate.
+
+2004-09-03 Derek Price <address@hidden>
+
+ * client.c (call_in_directory): Change passed in function to accept
+ void * to avoid typecasting. Change all functions using this API.
+
+2004-09-03 Derek Price <address@hidden>
+
+ * sanity.sh (secondary-wrapper, writeproxy-secondary-wrapper): Improve
+ comments. Use exec to launch server.
+ (writeproxy-noredirect): New tests for writeproxy functionality in
+ conjunction with clients that cannot handle the `Redirect' response.
+
+2004-09-03 Derek Price <address@hidden>
+
+ * log-buffer.c, rsh-client.c, client.c: Reformat function headers.
+ Remove unnecessary typecasts and prototypes.
+ * client.h: Remove unnecessary extern declarations on protos.
+
+2004-09-03 Derek Price <address@hidden>
+
+ * sanity.sh (skip): New function.
+ (sshstdio): Use new function.
+ (writeproxy): Skip test when rsync isn't found.
+
+2004-09-02 Mark D. Baushke <address@hidden>
+
+ * server.c (serve_directory): C89 compilers do not like mixed
+ declarations and code.
+
+2004-08-19 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (sync-secondary): 'dirname -b' fails during the
+ spacefiles-5 test on FreeBSD, so use 'dirname -- "\$dir"' for now
+ and look to AS_DIRNAME at some future date.
+
+2004-08-19 Derek Price <address@hidden>
+
+ * sanity.sh (ssh-wrapper): Create for $proxy mode too & forward CVS_PID
+ for crerepos.
+
+2004-08-18 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (writeproxy): Use CVS_PID instead of PPID as the
+ former environment variable is set by cvs and the latter is
+ NOT set by all bourne shells.
+
+2004-08-18 Mark D. Baushke <address@hidden>
+
+ * log-buffer.c (log_buffer_rewind): Avoid FreeBSD compilation
+ error for dereferencing a void * pointer tmp as well as using it
+ as a buffer pointer under some conditions.
+
+2004-08-17 Derek Price <address@hidden>
+
+ * server.c (isProxyServer): Always compile. Cache hostname lookup.
+ (serve_notify): Ignore notifications in conjunction with redirects.
+ (do_cvs_command): Send an error when !PROXY_SUPPORT and a client does
+ not support redirects. Only close proxy logs when they exist.
+ (serve_command_prep): New function.
+ * sanity.sh (secondary-wrapper, writeproxy): Dynamically switch the
+ servers proxy/primary status for testing.
+ (basica): Gratuitous reformatting.
+ (devcom3, config, release): Handle some new proxy/redirect error
+ messages.
+
+2004-08-17 Derek Price <address@hidden>
+
+ * main.c (PrimaryServer): Include without PROXY_SUPPORT to support
+ redirects.
+ (main): Handle --primary-root without PROXY_SUPPORT.
+ * mkmodules.c (PrimaryServer), parseinfo.c (parse_config), root.c
+ (primary_root_add, primary_root_translate,
+ primary_root_inverse_translate): Likewise.
+
+2004-08-17 Derek Price <address@hidden>
+
+ * client.c: Misc reformatting.
+ (handle_redirect, close_connection_to_server): New functions.
+ (failure_exit,*): s/int/bool/.
+ (responses): Add `Redirect'.
+ (get_server_responses): Handle response_type_redirect.
+ (get_responses_and_close): Use close_connection_to_server().
+ (supported_request): Change API to use bool & const.
+ (start_server): Handle response_type_redirect.
+ * client.h (supported_request): Update proto.
+ (type): Add response_type_redirect.
+ * main.c (lookup_command_attribute): Declare arg const.
+ * cvs.h (lookup_command_attribute): Ditto.
+ * sever.c (requests): Create dummy `Command-prep' request.
+
+2004-08-12 Derek Price <address@hidden>
+
+ * main.c (main): Don't process --primary-root without PROXY_SUPPORT.
+ * root.c (primary_root_translate, primary_root_inverse_translate):
+ Declare inline.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * ms-buffer.h, ms-buffer.c: Disable contents without PROXY_SUPPORT.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * server.c (isProxyServer): Declare inline.
+ (reprocess_proxy_log): Rename to...
+ (rewind_buf_from_net): ...this and change all callers.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * sanity.sh (sync-secondary): Don't bother to log sync activity.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * buffer.c (buf_copy_data), buffer.h (buf_copy_data), log-buffer.c
+ (log_buffer_initialize, log_buffer_input, log_buffer_output,
+ log_buffer_rewind, log_buffer_closelog), log-buffer.h
+ (log_buffer_initialize), main.c (MaxProxyBufferSize), mkmodules.c
+ (config_contents), parseinfo.c (parse_config), server.c (server):
+ Switch eariler support for logs in memory buffers on
+ TRUST_OS_FILE_CACHE.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * buffer.c (buf_free_data, buf_copy_data): Only compile with proxy
+ support.
+ * buffer.h: Ditto for including the protos.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_input): Bracket misguided attempt at improved
+ I/O efficiency with TRUST_OS_FILE_CACHE pragmas.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * cvs.h, main.c, mkmodules.c, parseinfo.c, server.c:
+ s/MaxSecondaryBufferSize/MaxProxyBufferSize/.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * sever.c (secondary_log, secondary_log_out): Rename globals everywhere
+ to...
+ (proxy_log, proxy_log_out): ...these.
+ (isSecondaryServer): Rename function...
+ (isProxyServer): ...to this.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * log-buffer.c: #ifdef PROXY_SUPPORT in appropriate places.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * cvs.h, log-buffer.h, main.c, mkmodules.c, parseinfo.c, server.c:
+ s/SECONDARY_SUPPORT/PROXY_SUPPORT/.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * server.c (isSecondaryServer): Declare static.
+ * server.h (isSecondaryServer): Remove proto.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * log-buffer.h (log_buffer_rewind, log_buffer_closelog): Don't define
+ protos without SECONDARY_SUPPORT.
+ * server.c (*): #ifdef correctly for !SECONDARY_SUPPORT.
+ * version.c (version): Remove unused code.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * server.c (server_cleanup): Remove unused variable.
+
+2004-08-10 Derek Price <address@hidden>
+
+ * server.c (buf_from_net_save): Remove obsolete variable.
+ (server_cleanup): Don't close BUF_FROM_NET_SAVE.
+
+2004-08-09 Derek Price <address@hidden>
+
+ * sanity.sh (sync-secondary, *info): Sync only the updated directories
+ rather than the entire repository after a write for a minor efficiency
+ improvement.
+ (info, taginfo): Hack *info to sync all dirs for these tests rather
+ than rewriting the sync-secondary script to handle old-style *info
+ format strings.
+
+2004-08-05 Derek Price <address@hidden>
+
+ Beginnings of support for turning off writeproxy support (still broke.)
+ * cvs.h (PrimaryServer, MaxSecondaryBufferSize), main.c (PrimaryServer,
+ MaxSecondaryBufferSize), mkmodules.c (config_contents), parseinfo.c
+ (parse_config): Switch off when writeproxy support disabled.
+ * server.c (replace_file_offset, move_file_offset): Comment out
+ temporarily.
+
+2004-08-05 Derek Price <address@hidden>
+
+ * filesubr.c (copy_file): Don't fsync. It's slow.
+
+2004-08-05 Derek Price <address@hidden>
+
+ * buffer.c (buf_copy_data): New function.
+ * buffer.h: Proto new function.
+ * cvs.h (MaxSecondaryBufferSize): Declare new config global.
+ * log-buffer.c: Allow file-backed memory buffers for "speed".
+ (struct log_buffer): Add new fields.
+ (log_buffer_force_file): New function.
+ (log_buffer_initialize): Initialize new fields.
+ (log_buffer_input, log_buffer_output): Handle logging to memory when
+ asked.
+ (log_buffer_disable): Remove function, moving much functionality...
+ (log_buffer_rewind): ...to this new function and expanding.
+ (log_buffer_closelog): Handle new fields and structs.
+ (log_buffer_get_fd): Remove function.
+ (setup_logfiles): Use new _initialize API.
+ * log-buffer.h: Update protos to match.
+ * main.c (MaxSecondaryBufferSize): Init new config global.
+ * mkmodules.c (config_contents): Add comments 4 MaxSecondaryBufferSize.
+ * parseinfo.c (parse_config): Parse MaxSecondaryBufferSize..
+ * server.c (secondary_log_name, secondary_log_out_name): Remove unused
+ globals.
+ (read_secondary_log): Remove function.
+ (reprocess_secondary_log): Use log_buffer_rewind() instead of the above.
+ (become_proxy): Ditto.
+ (server_cleanup): No need to clean up logfiles any longer.
+ (server): Use new log_buffer_initialize API.
+
+2004-08-04 Derek Price <address@hidden>
+
+ * buffer.c (buf_read_data): s/abort/assert/.
+
+2004-08-04 Derek Price <address@hidden>
+
+ * server.c (loop_over_inputs): Remove function, moving contents back...
+ (server): ...to here.
+
+2004-08-03 Derek Price <address@hidden>
+
+ Checking in IO changes intended to improve speed for posterity since
+ they actually increase CPU usage by about .2% in remote mode and 5% in
+ writeproxy mode.
+ * Makefile.am (cvs_SOURCES): Add ms-buffer.c & ms-buffer.h.
+ * buffer.c (buf_free_data): New function.
+ (buf_read_data): s/abort/assert/.
+ (fd_buffer_input): Try to improve efficiency of blocking read.
+ * buffer.h (buf_free_data): New proto.
+ * server.c (reprocess_secondary_log): Only reopen log and attach to
+ BUF_FROM_NET - don't actually loop over inputs.
+ (become_proxy, serve_co, do_cvs_command): Use new log from above.
+ * ms-buffer.c, ms-buffer.h: New files.
+
+2004-08-03 Derek Price <address@hidden>
+
+ * sanity.sh (TIMING): Make this work when not in $remotehost mode.
+ (reserved-13b): Use sorted output.
+
+2004-08-03 Derek Price <address@hidden>
+
+ * log-buffer.c: Improve comments.
+
+2004-08-02 Derek Price <address@hidden>
+
+ * sanity.sh (ssh-wrapper): Export CVSUMASK to remote host.
+ (*): Remove some hacks that were needed because CVSUMASK was not
+ exported to the remote host. Misc cleanup.
+
+2004-07-28 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (pserver-auth-no-dos): ENOMEM on Solaris 7, 8, 9, and
+ AIX 4.3 all use the text "Not enough space" instead of the text
+ "Cannot allocate memory" as is printed on GNU/Linux, NetBSD, and
+ FreeBSD systems.
+
+2004-07-28 Derek Price <address@hidden>
+
+ * sanity.sh (ssh-wrapper): Export CVS_RSH on remote host.
+
+2004-07-28 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (secondary-wrapper): Not all $TESTSHELL shells
+ are able to set a variable and export it at the same time.
+ Separate the value assignment from the export statement.
+ (writeproxy-secondary-wrapper): Ditto.
+
+2004-07-28 Derek Price <address@hidden>
+
+ * server.c (isSecondaryServer): Fix array out of bounds problem.
+ * sanity.sh (ssh-wrapper): Wrap any rsh implementation we are handed to
+ forward variables relevant to testing. Add new $TIMING variable to
+ allow timing of processes on a remote host.
+ (*): Gratuitous reformatting.
+
+2004-07-26 Derek Price <address@hidden>
+
+ * server.c (read_secondary_log): Minor comment corrections.
+
+2004-07-23 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_output): Don't fsync. It is unneeded and slow.
+ * server.c (move_file_offset, replace_file_offset): Ditto.
+ * log-buffer.c (log_buffer_flush_log): Remove function.
+
+2004-07-19 Derek Price <address@hidden>
+
+ * log-buffer.c (log_buffer_disable): Return log FILE *. Don't close
+ log. Fix header comment.
+ (log_buffer_closelog): New function.
+ * log-buffer.h: Update protos to match.
+ * server.c (read_secondary_log): Rewind file pointer rather than
+ closing and reopening file for speed.
+ (serve_root): Close secondary log rather than just disabling when not
+ running in secondary mode.
+
+2004-07-19 Derek Price <address@hidden>
+
+ * buffer.c (buf_flush): Replace abort w/assert.
+ * log-buffer.c (log_buffer_flush_log): New function for syncing a log.
+ (log_buffer_flush, log_buffer_flush): Don't sync log for speed.
+ * log-buffer.h (log_buffer_flush_log): New proto.
+ * sanity.sh: Tidy.
+ (run_filter): Accept file name to filter as argument.
+ (dotest_*): pass new arg to run_filter.
+ * server.c (read_secondary_log): Sync log before reopening.
+
+2004-07-16 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_input): Back out previous two changes due to
+ incompatibility with current state of write proxies.
+ * log-buffer.c (log_buffer_initialize): Handle new buffers which
+ already have some data in them.
+ (log_buffer_input): Don't fsync here. It is slow.
+ * server.c (serve_root): Disable the secondary output log too.
+ (serve_noop, pserver_authenticate_connection): Misc cleanup.
+ * sanity.sh: Misc gratuitous cleanup.
+
+2004-07-16 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_input): Don't overwrite the input buffer the
+ second time through the blocking read loop.
+
+2004-07-15 Derek Price <address@hidden>
+
+ * buffer.c (fd_buffer_input): Improve efficiency.
+ * sanity.sh (modify_repo): Move timestamp race avoidance to...
+ (sync-secondary): This script.
+ (big): Misc cleanup.
+
+2004-07-15 Derek Price <address@hidden>
+
+ * sanity.sh: Misc gratuitous cleanup.
+ (modify_repo): Sleep 1 before rsync to avoid timestamp comparison
+ issues.
+
+2004-07-15 Derek Price <address@hidden>
+
+ * log-buffer.c (log_buffer_output): Remove extremely slow fsync call.
+ (log_buffer_disable): Move to here so log files are sync'd before
+ close.
+ * sanity.sh (run_filter): New function to allow for filtering of cruft
+ output by Rational Quantify or other profilers.
+ (dotest_*): Call new function.
+
+2004-07-13 Derek Price <address@hidden>
+
+ * server.c (prepost_proxy_proc): Add the CVSROOT string for the primary
+ server, as documented.
+
+2004-07-13 Derek Price <address@hidden>
+
+ * tag.c (tag_filesdoneproc): Don't track posttag errors.
+ (cvstag): Move addition of successful tags to val-tags to...
+ (tag_fileproc): ...here and...
+ (rtag_fileproc): ...here. Consolidate returns at single location.
+ (*): Misc reformatting.
+ * sanity.sh (sync-secondary): Include more data in the update-log.
+
+2004-07-13 Derek Price <address@hidden>
+
+ * .cvsignore: Ignore coverage data generated by GCC.
+
+2004-07-12 Derek Price <address@hidden>
+
+ * sanity.sh: Watch $servercvs and other minor fixes.
+
+2004-07-12 Derek Price <address@hidden>
+
+ * server.c: Gratuitous reformatting.
+ * sanity.sh: Misc write proxy accommodations.
+
+2004-07-11 Derek Price <address@hidden>
+
+ * log.c (log_fileproc, log_expand_revlist, log_fix_singledate,
+ log_count_print, log_tree, log_abranch, log_version), parseinfo.c
+ (Parse_Info, parse_config), rcs.c (RCS_fully_parse, rcsbuf_getkey,
+ rcsbuf_getrevnum, rcsbuf_valword, RCS_getbranchpoint, RCS_getdate,
+ RCS_getrevtime, RCS_checkout, RCS_findlock_or_tip, RCS_addbranch,
+ RCS_cmp_file, RCS_lock, RCS_unlock, RCS_delete_revs, RCS_deltas,
+ RCS_getdeltatext, RCS_putdtree): Print primary path.
+ * server.c (serve_kopt): Handle secondary log.
+ * sanity.sh: Misc accommodations.
+
+2004-07-11 Derek Price <address@hidden>
+
+ * checkin.c (checkout_proc): Correct vi induced typo.
+
+2004-07-11 Derek Price <address@hidden>
+
+ * admin.c (postadmin_proc), commit.c (precommit_proc), edit.c
+ (notify_proc), fileattr.c (postwatch_proc), logmsg.c (logfile_write),
+ server.c (prepost_proxy_proc), tag.c (posttag_proc, pretag_proc): Add
+ default %c format string.
+ * client.c, edit.c, lock.c, fileattr.c, mkmodules.c, myndbm.c,
+ parseinfo.c, recurse.c: Misc gratuitous cleanup.
+ * commit.c (commit_filesdoneproc): Move loginfo call to after CVSROOT
+ sync.
+ * checkout.c (checkout_proc), fileattr.c (fileattr_read), myndbm.c
+ (mydbm_open, mydbm_load_file): Print primary path.
+ * server.c (serve_checkin_time): Handle secondary log.
+ (prepost_proxy_proc): Move before first call.
+ (become_proxy): Move before first call.
+ (serve_notify): Become proxy for this request.
+ * sanity.sh: Misc accommodations.
+
+2004-07-10 Derek Price <address@hidden>
+
+ * server.c (serve_notify): Handle secondary_log.
+
+2004-07-08 Derek Price <address@hidden>
+
+ Intermediate checkin. Adds some new server requests and test fixes.
+ * sanity.sh: Update tests to handle proxies and new hooks.
+ * cvs.h (CVSROOTADM_PREPROXY): Alphebetize & sub correct name.
+ * mkmodules.c (filelist): Alphabetize.
+ * server.c (serve_max_dotdot, serve_static_directory, serve_argumentx):
+ Handle secondary_log.
+ * tag.c (*): Misc gratuitous cleanup.
+
+2004-07-08 Derek Price <address@hidden>
+
+ Intermediate checkin. Adds preproxy, & postproxy hooks and some test
+ cruft to test everything.
+
+ * cvs.h (CVSROOTADM_*): Add new hook config files.
+ * log.c (log_fileproc): Print primary path.
+ * mkmodules.c (*_content): Add init content for new files.
+ (filelist): Add new files.
+ * rcs.c (RCS_parsercsfile_i): Set print_path. Misc gratuitous cleanup.
+ (freercsnode): Free print_path.
+ * rcs.h (struct rcsnode): Add print_path.
+ * rcscmds.c (RCS_merge, RCS_exec_rcsdiff): Print primary path.
+ * server.c (isSecondaryServer): No longer static.
+ (serve_sticky, serve_argumentx): Handle secondary logging.
+ (prepost_proxy_proc): New function.
+ (become_proxy): Call pre & post proxy hooks. Handle IO closing better.
+ * server.h (isSecondaryServer): No longer static.
+ * status.c (status_fileproc): Print primary path.
+ * sanity.sh: Accept --proxy argument and run in write proxy mode when
+ seen. Misc fixes to account for other changes. Misc gratuitous
+ cleanup.
+
+2004-07-02 Derek Price <address@hidden>
+
+ Woo-hoo! Write proxies work!
+ * client.c (open_connection_to_server): Set up log files...
+ (start_server): ...here.
+ * server.c (secondary_log_out_name, secondary_log_out): New globals.
+ (argument_cound, argument_vector, argument_vector_size): Move before...
+ (reprocess_secondary_log): ...referencing here. Assume secondary_log.
+ (read_secondary_log): Accept buf & name args.
+ (serve_modified, serve_unchanged, serve_is_modified, serve_entry):
+ Handle logging pass.
+ (become_proxy): Use new output-to-client-log. Verify buffers still
+ exist before using.
+ (output_dir): Translate paths to the client on the secondary.
+ (serve_co): Only reprocess the secondary log when one exists.
+ (server_cleanup): Free buf after shutdown. Dispose of client output
+ log.
+ (server): Create client output log.
+ (*): Misc reformatting & detypecasting.
+ * log-buffer.c (log_buffer_output): Handle fatal_errors.
+ (log_buffer_flush): Don't operate on NULL streams.
+ (log_buffer_disable): Reformat so as not to confuse the optimizer.
+ * root.c (primary_root_translate): Remove unused variable and redundant
+ slash.
+ (primary_root_inverse_translate): New function.
+ * root.h: Add new proto.
+ * sanity.sh (writeproxy): Wrap server executables to set args and
+ server variables properly. Fill in some test gaps. Correct `cd' args.
+ Clean up wrappers.
+
+2004-06-30 Derek Price <address@hidden>
+
+ * root.h (primary_root_add, primary_root_translate): New protos.
+ * root.c (primary_root_add, primary_root_translate): New functions.
+ * main.c (long_options): Add --primary-root. Handle --primary-root
+ and --allow-root only with SERVER_SUPPORT.
+ (main): Ditto.
+ * server.c (move_file_offset): Set block before fsync. Report
+ ftruncate errors. Force sync after rearranging data.
+ (replace_file_offset): Force sync after replacing data.
+ (serve_directory): Translate roots based on --primary-root arg.
+ (serve_root): Likewise & don't rewrite the log file.
+ (become_proxy): Increment select's N arg because it is required.
+ (do_cvs_command): Use MAX macro appropriately.
+ * sanity.sh (writeproxy): Wrap the secondary server in such a way that
+ it gets the --primary-root option and the primary does not. Move the
+ primary root out of the way for the read operations to prove only the
+ secondary was accessed.
+
+2004-06-30 Derek Price <address@hidden>
+
+ * log-buffer.c (log_buffer_input, log_buffer_output): Flush logs as
+ soon as they are written to better diagnose hangs.
+
+2004-06-30 Derek Price <address@hidden>
+
+ * client.c (connect_to_forked_server): Compile for SERVER_SUPPORT.
+
+2004-06-30 Derek Price <address@hidden>
+
+ * buffer.c (buf_append_buffer): Handle NULL from->data.
+
+2004-06-28 Derek Price <address@hidden>
+
+ * log-buffer.c (log_buffer_flush): Sync all, not just data.
+ * buffer.c (fd_buffer_flush): Ditto. Ignore problems synchronizing
+ unsynchronizable descriptors.
+
+2004-06-28 Derek Price <address@hidden>
+
+ Intermediate checkin on the way to enabling the write proxy.
+
+ * server.c (isSecondaryServer): Handle forked primary.
+ (read_secondary_log, move_file_offset, replace_file_offset,
+ become_proxy): New functions.
+ (reprocess_secondary_log): Use new read_secondary_log().
+ (serve_root): Replace `Root' request with new version for primary.
+ (do_cvs_command): Use new become_proxy() function.
+ (*): Gratuitous reformatting.
+ (server): Open new logs and avoid opening pipes to pserver twice.
+ * buffer.c (buf_initialize): Handle new LAST_INDEX & LAST_COUNT
+ initializers.
+ (*): Remove unnecessary typecasts. Gratuitous reformatting. Use
+ assert() rather than if()/abort().
+ (buf_append_buffer, buf_read_data, buf_copy_lines, buf_copy_counted):
+ Track LAST_INDEX & LAST_COUNT.
+ (buf_read_short_line): Track LAST_INDEX & LAST_COUNT.
+ * buffer.h (struct buffer): Add LAST_INDEX & LAST_COUNT.
+ * cvs.h: Include minmax.h.
+ * root.h (enum CVSmethod): Force null_method to 0.
+ * zlib.c: Remove unnecessary typecasts. Gratuitous reformatting. Use
+ assert() rather than if()/abort().
+
+2004-06-23 Derek Price <address@hidden>
+
+ Checkout and probably other read-only commands now work.
+
+ * server.c (serve_expand_modules): Discard arguments even when
+ reprocessing.
+ (serve_argument): Always process arguments.
+ (serve_wrapper_sendme_rcs_options): Process in first pass.
+
+2004-06-23 Derek Price <address@hidden>
+
+ Operate correctly in non-write proxy mode, delaying processing of most
+ commands until after the `Root' request is received.
+
+ * server.c (buf_from_net_save): New global variable to store the input
+ buffer from the client while the secondary log is being reprocessed.
+ (reprocessing): Global to track whether we are reprocessing.
+ (various redundant prototypes): Removed.
+ (fd_buffer_*): Remove unneeded typecasts.
+ (serve_valid_responses, serve_global_option, serve_set,
+ serve_valid_requests): Avoid processing twice.
+ (command_pid, outbuf_memory_error, input_memory_error): Moved above new
+ references.
+ (server): Factor loop over the client inputs to...
+ (loop_over_inputs): ...this new function.
+ (serve_root): Loop over secondary log of client inputs when we
+ discover we are not the secondary.
+ (*): Add !secondary_log assertions to verify that certain code paths
+ are not yet taken.
+ (do_cvs_command, serve_init): Add switch and pseudo-code comments about
+ what will need to be done on secondary servers.
+ (seve_noop): Print errors & "ok" in the first pass, skip "ok" but
+ restore handling of entries and notification in the second.
+ (serve_co): Note what will need to be done on secondaries.
+ (server_cleanup): Deal with buf_from_net_save when necessary.
+
+2004-06-22 Derek Price <address@hidden>
+
+ * server.c (server): Move previously fatal error on failure to open a
+ secondary log to...
+ (serve_root): ...here, and only when we discover we are actually a
+ secondary server.
+ (server): Ensure an interrupt cannot dump core.
+
+2004-06-22 Derek Price <address@hidden>
+
+ Enable the writeproxy log and turn it off when we determine we are not
+ running as a secondary server.
+
+ * log-buffer.c: Compile log buffer routines in server mode for write
+ proxy.
+ (struct log_buffer): Add fatal_error member.
+ (log_buffer_initialize, log_buffer_input): Allow for fatal write errors
+ for writeproxy.
+ (log_buffer_disable): Turn off the log when asked.
+ (log_buffer_shutdown): Close log via log_buffer_disable.
+ (log_buffer_input, log_buffer_output, log_buffer_flush): Don't operate
+ on the log when it doesn't exist.
+ (*): Misc gratuitous cleanup.
+ (setup_logfiles): Use new log_buffer_initialize API.
+ * log-buffer.h (log_buffer_initialize, log_buffer_disable): New
+ prototypes.
+ (log_buffer_initialize): Update prototype.
+ * server.c: Include log-buffer.h. Gratuitous reformatting of pragmas.
+ (secondary_log_name, secondary_log): New globals.
+ (server): Set up recording for writeproxy.
+ (serve_root): Turn off recording when we determine that we are not a
+ secondary.
+
+2004-06-21 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy): Verify that secondary is updated after a
+ commit. Comment test that verifies that commit took place on primary.
+
+2004-06-10 Derek Price <address@hidden>
+
+ * sanity.sh (writeproxy): Test response to a failing rsync.
+
+2004-06-09 Derek Price <address@hidden>
+
+ * server.c (isSecondaryServer): New function.
+ (MAXHOSTNAMELEN): Move to top of file and improve comment.
+
+2004-06-09 Derek Price <address@hidden>
+
+ * parseinfo.c (parse_config): Get my enum references correct.
+
+2004-06-09 Derek Price <address@hidden>
+
+ * parseinfo.c (parse_config): Verify that the ProxyServer connection
+ method is valid.
+
+2004-09-02 Derek Price <address@hidden>
+
+ * server.c (do_cvs_command): Pass new args to fd_buffer_initialize().
+ (server): Don't initialize BUF_TO_NET & BUF_FROM_NET when
+ pserver_authenticate already did.
+ (pserver_read_line): New function to access pserver auth dialogue via
+ buffers.
+ (pserver_authenticate_connection): Init buffers to/from net and access
+ via pserver_read_line() and the buffer output functions.
+ (fd_buffer_*): Move to...
+ * buffer.c (fd_buffer_*): ...here. Handle blocking input more
+ efficiently.
+ (buf_initialize): Handle get_fd() argument.
+ (buf_nonio_initialize, packetizing_buffer_initialize): Pass new
+ get_fd() argument.
+ (buf_copy_data, buf_free_data, buf_read_short_line, buf_get_fd,
+ packetizing_buffer_get_fd): New functions.
+ (bufread_line): Wrap buf_read_short_line().
+ (stdio_buffer_*): Remove these functions.
+ (*): Some reformatting of function headers.
+ * buffer.h (struct buffer, buf_initialize): Add get_fd().
+ (buf_read_short_line, buf_get_fd, buf_copy_data, buf_free_data,
+ fd_buffer_initialize): New prototypes.
+ * client.c (get_port_number, get_cvs_port_number,
+ get_proxy_port_number): Compile with SERVER_SUPPORT.
+ (make_bufs_from_fds): Likewise, and accept new ROOT arg and pass on to
+ fd_buffer_initialize().
+ (connect_to_pserver, connect_to_forked_server): Pass ROOT to
+ make_bufs_from_fds().
+ (start_server): Factor much functionality into...
+ (open_connection_to_server): ...this new function.
+ * client.h (make_bufs_from_fds): Update proto.
+ (open_connection_to_server): New proto.
+ * log-buffer.c (log_buffer_initialize): Handle get_gd().
+ (log_buffer_get_fd): New function.
+ * zlib.c (compress_buffer_initialize, compress_buffer_get_fd): Ditto
+ twice.
+ * rsh-client.c (start_rsh_server): Pass ROOT to make_bufs_from_fds().
+ * sanity.sh (pserver): Expect new error messages.
+
+2004-09-01 Derek Price <address@hidden>
+
+ * run.c: Remove unneeded typecasts. Reformat function headers. Fix
+ trace.
+
+2004-08-31 Derek Price <address@hidden>
+
+ * subr.c (format_cmdline), cvs.h (format_cmdline): Accept bool rather
+ than int.
+ * admin.c, commit.c, edit.c, fileattr.c, logmsg.c, tag.c: Change all
+ callers.
+ * main.c (UseNewInfoFmtStrings), cvs.h (UseNewInfoFmtStrings):
+ s/int/bool/.
+ * parseinfo.c: Change all references.
+
+2004-08-31 Derek Price <address@hidden>
+
+ * checkout.c: Reformat function headers. Remove unnecessary typecasts
+ and prototypes. Some other reformatting for legibility.
+
+2004-08-31 Derek Price <address@hidden>
+
+ * buffer.c: Gratuitous reformatting of header comments.
+ s/abort/assert/. Remove unnecessary typecasts.
+ * buffer.h: Remove unnecessary "extern" decls. Some reformatting.
+
+2004-08-24 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Don't shorten //. to / (use //).
+
+2004-08-24 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Strip trailing CWD indirections on
+ repository.
+ * sanity.sh (rstar-toplevel): Update to account for new behavior.
+ (Report from Dan Peterson <address@hidden>.)
+
+2004-08-24 Mark D. Baushke <address@hidden>
+
+ * recurse.c (do_recursion): Correct test for calling
+ server_pause_check to occur when locktype != CVS_LOCK_WRITE.
+ (Patch suggested by Ian Lance Taylor <address@hidden>
+ in bug#198).
+
+2004-08-24 Derek Price <address@hidden>
+
+ * sanity.sh: Update a few tests to account for the recent error message
+ changes.
+
+2004-08-24 Derek Price <address@hidden>
+
+ * rcs.c (RCS_valid_rev): Declare arg const.
+ * rcs.h: Likewise.
+
+2004-08-24 Derek Price <address@hidden>
+
+ * rcs.c (translate_symtag): Prevent infinite loop.
+ * tag.c (tag_check_valid): Check tag syntax before searching for tags.
+ * sanity.sh (tag-space): Some tests for the above.
+ (Report from Dan Peterson <address@hidden>.)
+
+2004-08-24 Derek Price <address@hidden>
+
+ * tag.c (tag_check_valid): Use RCS_valid_rev() rather than duplicating
+ code. Misc error message improvements.
+
+2004-08-24 Mark D. Baushke <address@hidden>
+
+ * ignore.c (ignore_directory): Include the terminating NUL
+ character in the directory name comparison to avoid matching
+ substrings of directories by accident.
+ (Report and suggested fix from James E Wilson
+ <address@hidden>.)
+ * sanity.sh (modules4): Add some more tests testing the above
+ change.
+
+2004-08-20 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (binfiles): Cleanup the 2a temporary directory.
+
+2004-08-20 Derek Price <address@hidden>
+
+ Cache tags in val-tags on successful creation to avoid problems with
+ write proxies. Merged from `writeproxy2' branch.
+
+ * tag.c (tag_filesdoneproc): Don't track posttag errors.
+ (cvstag): Move addition of successful tags to val-tags to...
+ (tag_fileproc): ...here and...
+ (rtag_fileproc): ...here. Consolidate returns at single location.
+ (*): Misc reformatting.
+
+ * tag.c (add_val_tag): New function with content factored from...
+ (tag_check_valid): ...here.
+ (cvstag): Call add_val_tag() when needed.
+ * annotate.c, checkout.c, commit.c, diff.c, ls.c, patch.c, recurse.c,
+ tag.c, update.c: Pass new args to tag_check_valid.
+
+ Merge of postadmin, posttag, and postwatch functionality from
+ `writeproxy2' branch.
+
+ * admin.c (postadmin_proc), commit.c (precommit_proc), edit.c
+ (notify_proc), fileattr.c (postwatch_proc), logmsg.c (logfile_write),
+ server.c (prepost_proxy_proc), tag.c (posttag_proc, pretag_proc): Add
+ default %c format string.
+
+ * cvs.h (CVSROOTADM_POSTWATCH): New macro.
+ * fileattr.c (*): Misc cleanup.
+ (postwatch_proc): New function.
+ (fileattr_write): Call watch proc when done writing fileattr.
+ * mkmodules.c (postwatch_contents): New var.
+ (filelist): Add postwatch.
+ * watch.c (addremove_filesdoneproc): Remove function.
+ (watch_addremove): Don't call above function.
+ (*): Misc cleanup.
+ * watch.h: Remove some unnecessary "extern" decls.
+
+ * admin.c (postadmin_proc, admin_filesdoneproc): New functions.
+ (admin): Pass admin_filesdoneproc() to start_recursion().
+ (*): Misc gratuitous cleanup.
+ * cvs.h (CVSROOTADM_*): Alphabetize, add new hook config files.
+ (format_cmdline): Fix proto to match change below.
+ * mkmodules.c (*_content): Add init content for new files. Misc
+ cleanup.
+ (filelist): Add new files.
+ * tag.c (struct pretag_proc_data): Move before first use.
+ (posttag_proc, tag_filesdoneproc): New functions.
+ (rtag_proc): Pass new procs to start_recursion().
+ (*): Misc gratuitous cleanup.
+ * sanity.sh: Misc accommodations.
+
+2004-08-19 Mark D. Baushke <address@hidden>
+
+ * log-buffer.c (log_buffer_output): Protect call to fsync()
+ with #ifdef HAVE_FSYNC.
+
+2004-08-18 Mark D. Baushke <address@hidden>
+
+ * log-buffer.c (log_buffer_input): Protect call to fsync()
+ with #ifdef HAVE_FSYNC.
+
+2004-08-17 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (sshstdio): Fix comment typo plus gratuitous
+ reformatting.
+
+ * client.c (handle_m): Workaround to deal with stdio getting put
+ into non-blocking via redirection of stderr and interaction with
+ ssh on some platforms. On those boxes, stdio can put stdout
+ unexpectedly into non-blocking mode which may lead to fwrite() or
+ fflush() failing with EAGAIN, but cvs not checking for the error.
+ (Patch suggested by Frank Hemer <address@hidden>.)
+
+ * client.c (handle_e): Similar fix for stderr.
+ * sanity.sh (sshstdio): New test for non-blocking stdio via ssh.
+
+2004-08-11 Derek Price <address@hidden>
+
+ * sanity.sh (basicc): Work around a problem in Linux 2.2 & Bash 2.05b
+ which prevents a `cd ..' from a deleted directory from working.
+ (Original patch from Matthew Ogilvie <address@hidden>.)
+
+2004-07-18 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (newb-123j0): Use DOTSTAR at end of response.
+
+2004-07-17 Mark D. Baushke <address@hidden>
+
+ * main.c (ImportNewFilesToVendorBranchOnly): New variable.
+ * cvs.h (ImportNewFilesToVendorBranchOnly): Declare new variable.
+ * import.c (import): Respect setting of
+ ImportNewFilesToVendorBranchOnly.
+ * mkmodules.c (config_contents): Document the default
+ ImportNewFilesToVendorBranchOnly=no option in newly generated
+ config files.
+ * parseinfo.c (parse_config): Parse
+ ImportNewFilesToVendorBranchOnly option.
+ * sanity.sh (importX2): New test, to test
+ ImportNewFilesToVendorBranchOnly config file option.
+ (New feature from Chris Demetriou <address@hidden>.)
+
+2004-07-17 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (basic2-14): Use DOTSTAR to be more portable.
+
+ * status.c (status_fileproc): Print datetimes using output_cvs_tagged.
+ * sanity.sh (basic2-14): Allow for an extra blank line at the end.
+
+2004-07-16 Derek Price <address@hidden>
+
+ * server.c (pamh): New global static to hold the PAM handle.
+ (server): Close the PAM session so that logging works properly.
+ (switch_to_user): Opens a PAM session and gets credentials from PAM so
+ that PAM modules can change group permissions. Get the username from
+ PAM so that PAM modules can modify the final local username.
+ (cvs_pam_conv): Changed the assertions to allow PAM to output text to
+ the user.
+ (check_system_password): Renamed to...
+ (check_pam_password): ...this. Changed argument type for username so
+ that PAM modules can modify the username under authentication. Setting
+ a terminal so some PAM modules which expect it to be set work, it is
+ set to the pam servicename which defaults to the binary name. Set the
+ username from PAM after authentication if a module has changed it.
+ (check_password): Calls check_pam_password if PAM is enabled otherwiseu
+ calls check_system_password.
+ (Patch from Brian Murphy <address@hidden>.)
+
+2004-07-15 Derek Price <address@hidden>
+
+ * sanity.sh (run_filter): New function to allow for filtering of cruft
+ output by Rational Quantify or other profilers.
+ (dotest_*): Call new function.
+
+2004-07-13 Derek Price <address@hidden>
+
+ * .cvsignore: Ignore GCC profiling data.
+
+2004-07-12 Derek Price <address@hidden>
+
+ * client.c: Misc reformatting.
+
+2004-07-12 Derek Price <address@hidden>
+
+ * main.c: fix format_time_t to call localtime
+ (Patch from Bart Robinson <address@hidden>.)
+
+2004-07-02 Derek Price <address@hidden>
+
+ * vers_ts.c: Gratuitous reformatting & detypecasting.
+
+2004-06-30 Derek Price <address@hidden>
+
+ * log-buffer.c (log_buffer_input, log_buffer_output): Flush logs as
+ soon as they are written to better diagnose hangs.
+
+2004-06-29 Derek R. Price <address@hidden>
+
+ * sanity.sh (toplevel-12): Handle new error output.
+
+2004-06-29 Derek R. Price <address@hidden>
+
+ * subr.c (xrealloc_and_strcat): Use bool in place of short.
+
+2004-06-29 Derek R. Price <address@hidden>
+
+ * client.c: Gratuitous reformatting.
+ (send_repository): Send relative Directory when server reports it is
+ able to handle it.
+ * server.c (serve_directory): Handle relative directories.
+ (output_dir): Send relative directories.
+ (requests): Add `Relative-directory' request.
+
+2004-06-26 Mark D. Baushke <address@hidden>
+
+ * import.c (import_usage): Add new -X flag.
+ (import): Handle new -X flag.
+ (process_import_file): Handle new -X flag.
+ (killnew): New static flag variable to indicate use of -X flag.
+ (preserve_initial_permissions): New function, extracted from
+ add_rcs_file().
+ (expand_and_copy_contents): Likewise.
+ (add_rcs_file): New argument, do_killnew, to cause "import -X" flag
+ processing. Implement -X flag, and use newly abstracted functions.
+ * rcs.h (add_rcs_file): Update prototype for do_killnew argument.
+ * commit.c (checkaddfile): Update for add_rcs_file function change.
+ * mkmodules.c (init): Likewise.
+ * client.c (handle_mt): Handle an importmergecmd tag without
+ any conflicts (for 'import -X' support).
+ * sanity.sh (importX): New test.
+ (New feature from Chris Demetriou <address@hidden>.)
+
+2004-06-22 Derek Price <address@hidden>
+
+ * wrapper.c: Add explicit "void" return type to "wrap_clean_fmt_str"
+ definition.
+ (Patch from Conrad T. Pino <address@hidden>.)
+
+2004-06-09 Derek Price <address@hidden>
+
+ * server.c (entries, serve_is_modified): Reorder to remove prototypes.
+ (supported_response): Remove prototype.
+
+2004-06-09 Derek Price <address@hidden>
+
+ * commit.c, filesubr.c, history.c, server.c, wrapper.c: Various
+ security fixes.
+ (Original patch from Stefan Essler <address@hidden> & Sebastian
+ Krahmer <address@hidden>.)
+
+ * cvs.h: Include xsize.h.
+
+2004-06-09 Derek Price <address@hidden>
+
+ * server.c (serve_entry, serve_is_modified, serve_unchanged): Protect
+ against malformed entries.
+ * sanity.sh (server): Tests for same.
+
+2004-06-07 Larry Jones <address@hidden>
+
+ * sanity.sh (basica): More tests for string-based revision inc.
+
+2004-06-04 Larry Jones <address@hidden>
+
+ * subr.c (increment_revnum): Rewrite ala RCS to work directly on
+ the string rather than converting to int to avoid overflow.
+ * sanity.sh (basica): New tests for above, update others to match.
+
+2004-06-04 Derek Price <address@hidden>
+
+ Preliminary writeproxy functionality.
+ * main.c: Declare PrimaryServer.
+ * cvs.h: Likewise, but extern.
+ * mkmodules.c: Add PrimaryServer to default CVSROOT/config content.
+ * parseinfo.c: Handle PrimaryServer line.
+ * sanity.sh: (Failing) tests for writeproxy functionality.
+
+2004-05-28 Derek Price <address@hidden>
+
+ * main.c (format_time_t, gm_format_time_t): Use my_strftime from
+ GNULIB rather than the system-dependant strftime.
+
+2004-05-20 Derek Price <address@hidden>
+
+ * sanity.sh: s/GMT/UTC/ where appropriate.
+
+2004-05-20 Derek Price <address@hidden>
+
+ * server.c (cvs_output_tagged): Move new server code inside a
+ SERVER_SUPPORT block.
+
+2004-05-19 Derek Price <address@hidden>
+
+ * cvs.h (gmformat_time_t, entries_time, unix_time_stamp): New protos.
+ * ls.c (struct long_format_data): New structure.
+ (ls_print): Print datetimes using cvs_output_tagged.
+ (long_format_data_delproc): New function.
+ (ls_fileproc, ls_direntproc): Keep track of long_format_data.
+ * main.c (Make_Date): Use standard quotes.
+ (format_time_t, gmformat_time_t): New functions.
+ (format_date_alloc): Use new functions. Improve comments.
+ * server.c (cvs_output_tagged): Only output in localtime in local mode.
+ * vers_ts.c (entries_time, unix_time_stamp): New functions.
+ (time_stamp): Use new functions.
+ * sanity.sh (ls, branches2): Use $ISO8601DATE where applicable.
+
+2004-05-19 Derek Price <address@hidden>
+
+ Output `cvs log' times in the local timezone.
+
+ * client.c (handle_mt): Handle the new "date" MT response
+ * server.c (cvs_output_tagged): Likewise
+ * cvs.h: Proto for format_date_alloc
+ * main.c (format_date_alloc, tm_diff): Added.
+ * log.c (log_version): Use MT response to tag date output.
+ (Original patch from Bart Robinson <address@hidden>.)
+
+ * sanity.sh (importc, rcs, rcs4): Use TZ=GMT for the duration of these
+ tests to obtain consistent times in output.
+ (ISO8601DATE, ISO8601DATE1971, ISO8601DATE2034): Use more precise
+ regex.
+
+2004-05-19 Derek Price <address@hidden>
+
+ * server.c (serve_unchanged, serve_is_modified): Overwrite existing
+ data in timefields. Fixes CAN-2004-0396.
+
+2004-05-15 Derek Price <address@hidden>
+
+ * lock.c (Lock_Cleanup), rcs.c (rcs_cleanup), server.c
+ (server_cleanup): Clean up inchoherent comment.
+
+2004-05-15 Derek Price <address@hidden>
+
+ * cvs.h, client.c, history.c, main.c, rcs.c, sanity.sh, server.c:
+ Back out get_date() changes from 2004-04-28.
+
+2004-05-14 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (trailingslashes): During cleanup remove topfile,v to
+ avoid problems in later tests (editor-1).
+
+2004-05-13 Derek Price <address@hidden>
+
+ * sanity.sh (trailingslashes): Note TODO item #205 in the comment.
+
+2004-05-13 Derek Price <address@hidden>
+
+ * sanity.sh (trailingslashes): New tests to expose a bug in CVS when
+ paths are specified with trailing slashes. This relates to TODO #205.
+
+2004-05-13 Mark D. Baushke <address@hidden>
+
+ * ls.c (ls): Use client_senddate() so the server is able to parse
+ the date/time.
+
+2004-05-12 Derek Price <address@hidden>
+
+ * entries.c, subr.c: Gratuitous reformatting.
+
+2004-05-12 Derek Price <address@hidden>
+
+ * subr.c (file_has_conflict), vers_ts.c (time_stamp_server): Only
+ special case "=" when it is the only character in a timestamp field.
+ Gratuitous reformatting.
+ * vers_ts.c (time_stamp_server): Check for NULL in a consistent manner.
+ Gratuitous reformatting.
+
+2004-05-12 Derek Price <address@hidden>
+
+ * sanity.sh (ls): Add some new tests of the ls command with dates
+ specified and show an assertion error when an existing file is
+ specifically requested.
+ (Original patch from Alexander Taler <address@hidden>.)
+
+2004-05-11 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (RCSKEYDATE): New regular expression to match the rcs
+ keyword date format.
+ (keyword,keywordlog): Use it.
+ (RCSDELTADATE): New regular expression to match the internal rcs
+ file format delta.
+ (admin): Use it.
+ (RCSDATE): Deleted.
+ (ISO8601DATE}: A more exact regular expression for cvs log date
+ output than the previous RCSDATE variable.
+ (basica,basic2,branches,branches3,multibranch,import,importb,importc,
+ join,modules,editor,binfiles,log,log2,keyword,multibranch2,admin,
+ reserved,recase,multiroot,trace):
+ Use ${ISO8601DATE} for cvs log output date patterns.
+ (TOUCH1971,ISO8601DATE1971): New variables for test importc.
+ (TOUCH2034,ISO8601DATE2034): Ditto.
+ (importc): Use them. Isolate the touch commands in a sub-shell
+ with TZ=GMT to make the time more predictable.
+ (RAWRCSDATE2000A,RAWRCSDATE1996A,RAWRCSDATE1996B): New date variables.
+ (ISO8601DATE2000A,ISO8601DATE1996A,ISO8601DATE1996B): Regexps to match.
+ (rcs): Use them.
+ (rcs4): Put the touch commands into sub-shells for temporary
+ TZ=GMT use.
+
+2004-05-11 Derek Price <address@hidden>
+
+ * checkin.c (Checkin), commit.c (commit_filesdoneproc, remove_file,
+ checkaddfile), rcs.c (RCS_checkin): Remove redundant commit messages.
+ Suppress output when really_quiet.
+ * sanity.sh: Update to match.
+
+2004-05-10 Derek Price <address@hidden>
+
+ * sanity.sh (top-level): Rename to...
+ (rstar-toplevel): ...this for clarity.
+
+2004-05-10 Derek Price <address@hidden>
+
+ * sanity.sh (dirs2-10ar): Remove unnecessary empty argument.
+
+2004-05-08 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): Suppress warnings if really_quiet.
+
+2004-05-08 Derek Price <address@hidden>
+
+ * server.c: Gratuitous reformatting. Remove unnecessary prototype &
+ unnecessary type cast.
+
+2004-05-07 Derek Price <address@hidden>
+
+ * sanity.sh (basica): Remove unnecessary empty arguments.
+
+2004-05-07 Derek Price <address@hidden>
+
+ * cvs.h (fopen_case): Remove obsolescent prototype.
+
+2004-05-05 Derek Price <address@hidden>
+
+ * sanity.sh: Wait a second and retry if cvs-serv* directories are
+ discovered to avoid race conditions on some systems.
+ (Patch from Pavel Roskin <address@hidden>.)
+
+2004-05-05 Derek Price <address@hidden>
+
+ * commit.c: Some gratuitous reformatting.
+
+2004-05-04 Derek Price <address@hidden>
+
+ * update.c: Some gratuitous reformatting.
+
+2004-05-04 Derek Price <address@hidden>
+
+ * add.c (add): Remove obsolete FIXME comment.
+ (*): Some gratuitous reformatting.
+
+2004-05-03 Derek Price <address@hidden>
+
+ * src/sanity.sh (branches2-14-ls-4): Change expectations due to new -d
+ flag.
+
+2004-05-02 Derek Price <address@hidden>
+
+ * sanity.sh (ls): Add some new tests of ls -d flag.
+ (Original patch from Alexander Taler <address@hidden>.)
+
+ * ls.c (ls): Accept -d to show dead files.
+ (ls_proc): Add W_ATTIC to start_recursion flags when user requests dead
+ files.
+ (ls_fileproc): Don't show dead files with -d. Print "dead" in long
+ listings for dead files.
+
+2004-05-02 Derek Price <address@hidden>
+
+ * ls.c (ls_dirleaveproc): Return err.
+ (Original patch from Mark D. Baushke <address@hidden>.)
+
+2004-05-02 Derek Price <address@hidden>
+
+ * ls.c (ls_print): Return 0.
+ (Patch from Mark D. Baushke <address@hidden>.)
+
+2004-04-30 Derek Price <address@hidden>
+
+ * tag.c (tag_check_valid): Treat a NULL repository the same as an empty
+ one.
+ * sanity.sh (branches2-ls-7): Verify absence of assertion failure.
+
+2004-04-29 Derek Price <address@hidden>
+
+ * sanity.sh (branches2-rls-1): Reformat comment.
+
+2004-04-28 Derek Price <address@hidden>
+
+ * sanity.sh (rcs2-5): Update to cope with new getdate.y.
+
+2004-04-28 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_LIBADD): Use libs for nanosleep & clock_gettime when
+ necessary.
+ * cvs.h: Remove get_date() proto and #include getdate.h.
+ * client.c, history.c, main.c, rcs.c, server.c: Use new form of
+ get_date().
+ * Makefile.in: Regenerated.
+
+2004-04-28 Derek Price <address@hidden>
+
+ * lock.c (set_lock), subr.c (sleep_past): Assume we have nanosleep.
+
+2004-04-27 Derek Price <address@hidden>
+
+ * root.c (normalize_cvsroot): Use asnprintf in preference to other
+ indirections.
+
+2004-04-27 Derek Price <address@hidden>
+
+ Add dirname module from GNULIB.
+
+ * add.c, client.c, commit.c, find_names.c, import.c, lock.c, ls.c,
+ repos.c, server.c, subr.c: s/ISDIRSEP/ISSLASH/.
+
+2004-04-27 Derek Price <address@hidden>
+
+ * commit.c, create_adm.c, entries.c, filesubr.c, hash.c, update.c:
+ Gratuitious reformatting.
+
+2004-04-27 Derek Price <address@hidden>
+
+ * ls.c (ls_direntproc): Remove unneeded assertion.
+
+2004-04-27 Derek Price <address@hidden>
+
+ * ls.c (ls): Set client_prune_dirs in order to delete any directories
+ created by the server.
+ (ls_dirleaveproc): Always delete directories created by
+ ls_direntproc().
+ * sanity.sh (ls): Update to match.
+
+2004-04-27 Derek Price <address@hidden>
+
+ * ls.c: Remove unneeded prototypes. Add `prune' option.
+ (dircount): Remove static global.
+ (set_tag, created_dir, ls_prune_dirs): New static globals.
+ (ls): Handle new prune option.
+ (ls_print_dir): Don't count directories, just remember not to print a
+ blank line in front of the first one. Don't list empty directories
+ when prune is specified.
+ (ls_delproc): New function to dispose of dirlist.
+ (ls_direntproc): Reorganize to assume a directory without a parent must
+ be listed. Create missing directories a la update and checkout so that
+ they may be processed. Use new delproc when creating new list nodes.
+ (ls_dirleave_proc): New function to remove directories created by
+ ls_direntproc.
+ (ls_proc): Call start_recursion() once for each argument so that
+ ls_direntproc() may assume that any directory without a parent in the
+ dirlist must be listed and others must not unless recursing.
+ * sanity.sh (ls): New tests.
+ (Thanks to a report from Mark D. Baushke <address@hidden>.)
+
+2004-04-26 Derek Price <address@hidden>
+
+ * rsh-client.c (start_rsh_server): Don't rely on GNU argument
+ processing capabilities in the RSH command.
+ (Report from Mark Andrews <address@hidden>.)
+
+2004-04-26 Derek Price <address@hidden>
+
+ * ls.c (dircount): s/long long/long/ for Windows.
+
+2004-04-23 Derek Price <address@hidden>
+
+ * ls.c (usage): Sync with manual.
+
+2004-04-23 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add ls.c.
+ * client.c, subr.c: Move #include vasnprintf.h to...
+ * cvs.h: ...here.
+ (ls): Add prototype.
+ * ls.c: New file.
+ * main.c (cmds): Add ls & rls entries.
+ * server.c (serve_ls, serve_rls): New functions.
+ (requests): Add list, ls, rlist, & global-list-quiet.
+ * sanity.sh (branches2): Test new cvs ls & rls commands.
+ * Makefile.in: Regenerated.
+ (Thanks for patches from Alexander Taler <address@hidden>
+ and Mark D. Baushke <address@hidden>.)
+
+2004-04-23 Derek Price <address@hidden>
+
+ * Makefile.am (AM_CPPFLAGS): No, really, $(top_builddir)/lib.
+ * Makefile.in: Regenerated.
+
+2004-04-23 Derek Price <address@hidden>
+
+ * Makefile.am (AM_CPPFLAGS): Add the builddir/lib directory for
+ generated header files.
+ * Makefile.in: Regenerated.
+
+2004-04-22 Derek Price <address@hidden>
+
+ * cvs.h: Move include of fnmatch.h into lib/system.h with the other
+ GNULIB headers.
+
+2004-04-22 Derek Price <address@hidden>
+
+ * tag.c: Use bool where appropriate. Some gratuitous reformatting.
+
+2004-04-19 Derek Price <address@hidden>
+
+ * ignore.c: Gratuitous reformatting.
+
+2004-04-16 Derek Price <address@hidden>
+
+ * tag.c: Gratuitous reformatting.
+
+2004-04-16 Derek Price <address@hidden>
+
+ * client.c (connect_to_pserver): Use size_t instead of int as argument
+ to asnprintf. Some gratuitous reformatting.
+ (Report from Mark <address@hidden>.)
+
+2004-04-15 Derek Price <address@hidden>
+
+ * client.c, commit.c, server.c: Gratuitous reformatting.
+
+2004-04-11 Derek Price <address@hidden>
+
+ * client.c (call_in_directory): Check paths the server sends us to make
+ sure they are within a sandbox the user requested be updated.
+ (is_valid_client_path, path_list_prefixed): New functions.
+
+2004-04-11 Derek Price <address@hidden>
+
+ * modules.c (do_module): Don't allow up-level references in paths to
+ step out of the repository.
+ * sanity.sh (multiroot3): Update tests and add a few more.
+
+2004-04-11 Derek Price <address@hidden>
+
+ * client.c (get_proxy_port_number): Use CVS_PROXY_PORT as the default
+ proxy port rather than CVS_AUTH_PORT.
+
+2004-04-10 Mark D. Baushke <address@hidden>
+
+ * client.c (get_cvs_port_number): Use CVS_AUTH_PORT as the default
+ for "cvspserver" rather than the CVS_PROXY_PORT.
+ (Fixes parseroot-3r on machines without "cvspserver" in
+ their /etc/services file.)
+
+2004-04-07 Derek Price <address@hidden>
+
+ * sanity.sh (parseroot): s/oberon/$username/.
+
+2004-04-06 Derek Price <address@hidden>
+
+ Import Jim Kingdon's old, old patch which allows CVS to work via a
+ web proxy server.
+ * client.c (*): Some gratuitous restyling.
+ (get_proxy_port_number): New function.
+ (connect_to_pserver): Connect via proxy.
+ * client.h (CVS_PROXY_PORT): Define.
+ * root.c (parse_cvsroot): Parse method options.
+ * sanity.sh (parseroot): Add new tests.
+
+2004-04-06 Derek Price <address@hidden>
+
+ * root.h (cvsroot_t): Move username, password, hostname, port inside
+ CLIENT_SUPPORT ifdefs.
+ * buffer.c, gssapi-client.c, root.c, sever.c: Add #ifdefs as necessary
+ so that this will compile without client support and the root.h change.
+ Some gratuitous restyling.
+
+2004-04-06 Derek Price <address@hidden>
+
+ * log.c, tag.c: Gratuitous restyling.
+
+2004-04-04 Derek Price <address@hidden>
+
+ * filesubr.c (isabsolute): Move...
+ * subr.c: ...here and use new ISABSOLUTE macro.
+
+2004-04-04 Derek Price <address@hidden>
+
+ * client.c (send_file_names): Cast out an unneeded const to avoid a
+ warning.
+
+2004-04-03 Larry Jones <address@hidden>
+
+ * cvsrc.c: Remove unused declarations.
+ * run.c: Ditto.
+ * server.h (gunzip_and_write): Declare.
+
+ * client.c (send_file_names): Remove unused variables.
+
+2004-04-02 Derek Price <address@hidden>
+
+ * sanity.sh (client): Honor $keep.
+
+2004-04-02 Derek Price <address@hidden>
+
+ * log.c, patch.c, rcs.c: Gratuitous restyling.
+
+2004-04-02 Derek Price <address@hidden>
+
+ * import.c (import): Use ISDIRSEP rather than testing paths against `/'
+ directly. Some gratuitos reformatting.
+
+2004-04-02 Derek Price <address@hidden>
+
+ * sanity.sh: Note the effectiveness of `tail -f check.log' in providing
+ running status.
+
+2004-04-02 Derek Price <address@hidden>
+
+ * client.c (send_file_names): Move code which calculates and sends
+ Max-dotdot...
+ (send_max_dotdot): ...to this new function.
+ (send_files): Call send_max_dotdot.
+ * sanity.sh (files-14): Expect .. in paths to work now.
+ (status): Add a few new tests using `..'.
+
+2004-04-01 Derek Price <address@hidden>
+
+ * lock.c: Gratuitous restyling.
+
+2004-04-01 Derek Price <address@hidden>
+
+ * commit.c, cvs.h, server.c: Gratuitous restyling.
+ * run.c (run_exec): Ditto, plus call cvs_flush{out,err}() instead of
+ flushing stderr & stdout directly.
+
+2004-03-29 Derek Price <address@hidden>
+
+ * login.c, server.c: Gratuitous restyling.
+
+2004-03-25 Derek Price <address@hidden>
+
+ * client.c (send_file_names): Initialize string to NULL rather than to
+ a single byte string containing only a null byte.
+ * subr.c (xrealloc_and_strcat): If the destination string is NULL,
+ simply allocate a new string.
+ (Original report from Chris Bohn <address@hidden>.)
+
+2004-03-24 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (ISODATE): Fix ISO 8601 format comment.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh (toplevel): Remove FIXME type comment and unneeded
+ Emtptydir removal.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * update.c: Some minor style cleanup.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * update.c: Some minor style cleanup.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh (top-level): Don't match most of the assertion since this
+ string is often system dependent.
+ (Thanks to Larry Jones <address@hidden>.)
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh (top-level): Don't match the assertion's line number.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh (top-level): New test to confirm assertion failure.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh: Only verify argument to -f when -f was passed. Check for
+ $TMPDIR/cvsXXXXXX temp files after each test.
+
+2004-03-22 Derek Price <address@hidden>
+
+ * sanity.sh: Verify that the argument to -f is really a test.
+
+2004-03-20 Larry Jones <address@hidden>
+
+ * cvs.h: Change command_name to cvs_command_name to avoid conflict
+ on HP-UX (incredibly, it declares a global command_name in prot.h,
+ which is included from shadow.h, which we include in server.c).
+ Change all references.
+
+ * subr.c (previous_rev): Fix == vs = typo.
+
+ * buffer.h: Add prototype for buf_empty.
+
+ * add.c (add): Remove unused variable.
+
+2004-03-20 Derek Price <address@hidden>
+
+ * add.c (add, add_directory, build_entry), admin.c (admin_dirproc),
+ checkin.c (Checkin), checkout.c (safe_location, build_dirs_and_chdir),
+ client.c (add_prune_candidate, send_repository, send_a_repository,
+ send_to_server, start_rsh_server, send_arg, send_modified,
+ send_ignproc, send_filesdone_proc, send_dirent_proc,
+ send_dirleave_proc, client_notify), commit.c (check_direntproc,
+ check_filesdoneproc, checkaddfile, commit_direntproc,
+ commit_dirleaveproc, lock_RCS, precommit_proc, find_data,
+ find_dirent_proc, find_ignproc, find_filesdoneproc), create_adm.c
+ (Create_Admin), cvsrc.c (read_cvsrc), diff.c (diff_dirproc,
+ diff_filesdoneproc, diff_dirleaveproc), edit.c (onoff_filesdoneproc,
+ mark_up_to_date, editor_set, notify_proc_args, notify_proc, notify_do,
+ notify_check), entries.c (Scratch_Entry, Register, WriteTag),
+ expand_path.c (expand_variable, expand_path), fileattr.c
+ (fileattr_startdir), filesubr.c (mkdir_if_needed, xchmod,
+ last_component), history.c (history_write), ignore.c (ignore_directory,
+ ignore_files), import.c (get_comment, add_rcs_file, expand_at_signs),xi
+ lock.c (lock_filesdoneproc), log.c (log_dirproc), logmsg.c
+ (logfile_write, rcsinfo_proc, update_logfile_proc, editinfo_proc,
+ verifymsg_proc, do_editor, do_verify, Update_Logfile), main.c (main
+ program_name, program_path, command_name), parseinfo.c (Parse_Info),
+ patch.c (patch_dirproc), rcs.c (RCS_getdatebranch, rcs_lockfilename,
+ RCS_parse, RCS_setattic, RCS_getversion, RCS_gettag, RCS_getbranch,
+ RCS_getdate, RCS_datecmp, RCS_getrevtime, RCS_setexpand,
+ expand_keywords, RCS_checkout, RCS_addbranch, RCS_checkin, RCS_lock,
+ RCS_cmp_file, RCS_deltas, rcs_lockfilename, make_file_label),
+ rcscmds.c (RCS_output_diff_options, call_diff, RCS_merge,
+ RCS_exec_rcsdiff, diff_exec), recurse.c (start_recursion, do_recursion,
+ do_file_proc), remove.c (remove_dirproc), repos.c (Name_Repository,
+ Short_Repository), root.c (Name_Root, Create_Root), run.c
+ (piped_child), server.c (output_dir, server_register,
+ server_checked_in, server_update_entries, server_copy_file,
+ server_set_entstat, server_clear_entstat, server_set_sticky,
+ server_template, cvs_output_tagged), status.c (status_dirproc), subr.c
+ (make_message_rcslegal), tag.c (pretag_proc, tag_dirproc,
+ check_fileproc, check_filesdoneproc, tag_fileproc, val_direntproc),
+ update.c (update_dirent_proc, update_dirleave_proc, update_ignproc,
+ update_filesdone_proc, isemptydir), vers_ts.c (time_stamp_server,
+ time_stamp), watch.c (watch_modify_watchers, addremove_filesdoneproc),
+ zlib.c (read_and_gzip): Make most string args const, mainly in the
+ interest of preserving repository & updatedir but including some
+ collateral damage. Update a few functions to comply with new
+ requirement. Some style fixes.
+ * client.h, cvs.h, edit.h, fileattr.h, rcs.h, server.h, update.h,
+ watch.h: Update prototypes to match.
+
+2004-03-20 Derek Price <address@hidden>
+
+ * sanity.sh (conflicts2): s/cvs/$testcvs/.
+
+2004-03-20 Derek Price <address@hidden>
+
+ * add.c (add): Correct longstanding resurrection bugs. Remove FIXME
+ comment to this effect. Set mode and Entries timestamps of resurrected
+ files correctly.
+ * sanity.sh (basica, binfiles, conflicts2, recase, resurrection,
+ update-p): Update tests to compensate. Remove FIXCVS comments.
+
+2004-03-19 Mark D. Baushke <address@hidden>
+
+ * server.c (gserver_authenticate_connection): Handle large
+ GSSAPI packets dynamically.
+ (Bug report from Douglas Engert <address@hidden>)
+
+2004-03-19 Derek Price <address@hidden>
+
+ * cvs.h (pathname_levels, previous_rev): Remove leading underscore from
+ prototype arguments to avoid potential conflicts with implementations.
+
+2004-03-18 Derek Price <address@hidden>
+
+ * cvs.h (pathname_levels): Make string argument const.
+ * subr.c (pathname_levels): Simplify function.
+
+2004-03-17 Derek Price <address@hidden>
+
+ * commit.c (precommit_list_to_args_proc), logmsg.c (do_editor), subr.c
+ (format_cmdline), tag.c (pretag_list_to_args_proc):
+ s/atribute/attribute/.
+ (Report from Matthew Doar <address@hidden>.)
+
+2004-03-17 Derek Price <address@hidden>
+
+ * subr.c (pathname_levels): Get it right this time.
+
+2004-03-17 Derek Price <address@hidden>
+
+ * subr.c (pathname_levels): Remove incorrect assertion and just
+ return 0 when pathname is NULL.
+
+2004-03-17 Derek Price <address@hidden>
+
+ * subr.c (pathname_levels): Use ISDIRSEP() instead of strchr('/')
+ and remove FIXME comment to that effect.
+
+2004-03-16 Derek Price <address@hidden>
+
+ * main.c (main): Update the --version Copyright (c) string to
+ include 2004.
+
+2004-03-15 Mark D. Baushke <address@hidden>
+
+ * release.c (release): Add missing xmalloc of update_cmd.
+
+2004-03-15 Derek Price <address@hidden>
+
+ * release.c (release): Enable authentication and encryption for a child
+ update process when necessary.
+ (Original patch from Dan Russell <address@hidden> via Hal Mahaffey
+ <address@hidden>.)
+
+2004-03-14 Derek Price <address@hidden>
+
+ * add.c (add): Only call server_updated() when we actual have a new
+ resurrected file for the client.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * cvs.h (previous_rev, write_letter): New prototypes.
+ (struct file_info): Move to before the write_letter prototype.
+ * add.c (add): Allow resurrection of files which used to exist on a
+ branch.
+ * subr.c (previous_rev): New function.
+ * update.c: Consolidate like pragmas.
+ (write_letter): Remove prototype. Remove static declaration.
+ * sanity.sh (resurrection): New tests.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * commit.c (remove_file): Print the actual previous revision instead of
+ a branch number.
+ * sanity.sh: Update to match.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * rcs.c (RCS_cmp_file): Print the actual name of the file we failed to
+ open in the error message.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * diff.c (diff_fileproc): Allow diffing of new files against arbitrary
+ revisions instead of assuming that there is no RCS archive file.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * sanity.sh: Update serveral tests to use $CPROG & $SPROG correctly.
+
+2004-03-14 Derek Price <address@hidden>
+
+ * add.c (add): Update file resurrection messages for consistency.
+ * sanity.sh: Update to match.
+
+2004-03-13 Derek Price <address@hidden>
+
+ * server.c (server_updated): Fix header comment.
+
+2004-03-13 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Remove error.h since it is now included in
+ lib.
+ * Makefile.in: Regenerated.
+
+2004-03-09 Larry Jones <address@hidden>
+
+ * run.c: Move #includes required for cmdline_escape and friends...
+ * subr.c: ...to here.
+
+2004-03-07 Derek Price <address@hidden>
+
+ * run.c (struct cmdline_bindings, cmdline_bindings_hash_node_delete,
+ cmdline_escape, cmdline_quote, format_cmdline): Move...
+ * subr.c: ...here so they will compile under Windows.
+
+2004-03-03 Derek Price <address@hidden>
+
+ * import.c (import): Check that the module name specified by the user
+ does not contain `CVS' as a directory name.
+ * ignore.c (ign_add): Never cease ignoring "CVS" - it is a reserved
+ name.
+ (Original patch from Dan Peterson <address@hidden>.)
+
+ * sanity.sh (import-CVS): New tests for the above.
+
+2004-02-29 Larry Jones <address@hidden>
+
+ * import.c (expand_at_signs): Change type of len to size_t.
+ * subr.c (resolve_symlink): Move declaration of newname inside
+ #ifdef, clean up coding style.
+ * zlib.c (gunzip_and_write): Fix up potential overlow problems.
+ (read_and_gzip): Add explicit casts to placate paranoid compilers.
+
+2004-02-28 Larry Jones <address@hidden>
+
+ * sanity.sh (join6): New tests for previous fix.
+
+ * update.c (join_file): One more fix to avoid dereferencing NULL.
+ (Reported by Steve McIntyre <address@hidden>.)
+ * sanity.sh (join6): New tests for above.
+
+2004-02-25 Larry Jones <address@hidden>
+
+ * update.c (join_file): Fix optimization to avoid dereferencing NULL.
+ (Reported by Steve McIntyre <address@hidden>.)
+
+2004-02-25 Derek Price <address@hidden>
+
+ No longer require directories specified to `checkout -d' to exist.
+
+ * checkout.c (safe_location): Only confirm that destination is a safe
+ location for directories that already exist since we can assume that
+ creating directories under such a safe directory is acceptable.
+ (dir_to_build): Remove just_chdir member.
+ (checkout_proc): Add trace. No longer set dir_to_build->just_chdir.
+ Minor reformatting.
+ (build_dirs_and_chdir): Don't create or register directories that are
+ not created.
+ * sanity.sh (*): Update tests to account for new behavior.
+
+2004-02-25 Derek Price <address@hidden>
+
+ * buffer.c (buf_empty): New function.
+ * server.c (server): Check for unread data in buffer before closing.
+
+2004-02-25 Derek Price <address@hidden>
+
+ * release.c (release): Restore the initial directory before and after
+ calling various sections of code that expect it to prevent corruption
+ of CVS/Entries files on release of a subdir and tell unedit() what to
+ release.
+ * sanity.sh: Add test case for release.c fix.
+ (Original patch from Matthew Ogilvie <address@hidden>.)
+
+ * client.c (last_entries): Move global variable...
+ (call_in_directory): ...here (now a local variable). Remove test that
+ always evaluates to true.
+ (last_dir_name): Remove unused global variable.
+
+2004-02-24 Larry Jones <address@hidden>
+
+ * filesubr.c (xresolvepath): Fix crash in error case.
+ (Reported by Reinhard Zierke <address@hidden>.)
+
+2004-02-24 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos): Fix it so that it ignores the user's
+ .cvsrc file (.cvsrc "checkout -r" used to cause the "rm -r 1"
+ command to print warnings and wait for input).
+ (Original patch from Matthew Ogilvie <address@hidden>.)
+
+ * sanity.sh (reposmv, parseroot, devcom3, binwrap3):
+ s/_SAVED\>/_save/ for consistency.
+
+2004-02-24 Derek Price <address@hidden>
+
+ * sanity.sh (taginfo-newfmt-examine-2): Correct expected results.
+
+2004-02-23 Larry Jones <address@hidden>
+
+ * sanity.sh (taginfo-examine-1): Correct expected results.
+
+2004-02-20 Derek Price <address@hidden>
+
+ * subr.c (expand_string): Use x2realloc() from GNULIB for core
+ functionality.
+
+2004-02-20 Derek Price <address@hidden>
+
+ * subr.c (set_nonblock_fd): Move back to...
+ * server.c: ...here.
+ * cvs.h: Remove protos for the above two functions.
+ * buffer.c (stdio_buffer_shutdown): Remove unexessary and possibly
+ dangerous check for unread data on a pipe with a nonblock read.
+
+2004-02-20 Larry Jones <address@hidden>
+
+ * tag.c (check_fileproc): Remove unused variable.
+
+2004-02-20 Derek Price <address@hidden>
+
+ * ChangeLog, commit.c, filesubr.c, rcs.c, root.c, sanity.sh, subr.c,
+ update.c: Remove VIM editor commands.
+
+2004-02-20 Derek Price <address@hidden>
+
+ Import xalloc module from GNULIB, as well as its remaining unimported
+ dependency, the exitfail module.
+
+ * cvs.h: #include "xalloc.h".
+ * subr.c (xmalloc, xrealloc, xstrdup): Remove these functions.
+
+2004-02-20 Larry Jones <address@hidden>
+
+ * hash.h (struct node): Change data from char * to void *, change
+ all callers.
+
+ * run.c (cmdlinequote, cmdlineescape): Use prototype in definition
+ to match cvs.h.
+ (format_cmdline): UNIQUE_*_TYPE_* macros imply HAVE_*, so no need
+ to check for both. Remove duplicate code for size_t and ptrdiff_t.
+
+ * tag.c (check_fileproc): Remove spurious invalid cast, clean up
+ coding style.
+
+ * commit.c (precommit_list_proc): Remove vestigial prototype.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * run.c (format_cmdline): Don't accept printf `L' modifier at all when
+ long doubles are not available. Allow UNIQUE_FLOAT_TYPE_LONG_DOUBLE to
+ imply HAVE_LONG_DOUBLE.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * run.c: Remove include of stddef.h - it is already included via
+ lib/system.h.
+
+2004-02-19 Larry Jones <address@hidden>
+
+ * run.c: Include the appropriate headers for wchar_t and wint_t,
+ create a typedef for convproc and use it (required for va_arg),
+ add explicit comparisons to keep gcc -Wall happy, remove unused
+ variables.
+ (format_cmdline): Fix bugs and portability problems.
+ * tag.c: Remove unused global variable.
+ (pretag_proc): Correct call to format_cmdline: %hhc is not a valid
+ printf format (should just be %c) and NULL must be cast to the correct
+ type in a varargs call.
+ (pretag_list_to_args_proc): Initialize arg to keep gcc -Wall happy,
+ remove unused variable, add auxiliary variable to simplify code,
+ clean up coding style.
+
+ * commit.c (precommit_list_to_args_proc): Initialize arg to keep
+ gcc -Wall happy, remove unused variable, clean up coding style.
+ (precommit_proc): Cast NULL to the correct type in varargs call.
+ * edit.c (notify_proc): Remove unused variables.
+ * expand_path.c (expand_path): Initialize inquotes, add explicit
+ comparison to keep gcc -Wall happy.
+ * logmsg.c: Remove unused global variables and function decl.
+ (do_verify): Add explicit comparison to keep gcc -Wall happy,
+ clean up coding style.
+ (logmsg_list_to_args_proc): Initialize arg to keep gcc -Wall happy,
+ remove unused variable.
+ (logfile_write): Cast NULL to the correct type in varargs call.
+ (verifymsg_proc): Cast NULL to the correct type in varargs call.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * run.c (format_cmdline): Correct some typos and cut/paste errors.
+ Switch on HAVE_WINT_T for Solaris. Switch on HAVE_INTMAX_T for other
+ pre-C99 platforms.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos): Correct comment.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * logmsg.c (verifymsg_proc), run.c (format_cmdline): Plug memory leak.
+ (Report from Mark D. Baushke <address@hidden>.)
+
+ * run.c (format_cmdline): Simplify two expressions.
+
+2004-02-19 Larry Jones <address@hidden>
+
+ * login.c (password_entry_operation): Initialize line.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh (tests): Add errmsg3.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh (errmsg3): Don't create directories named `tmp' in
+ $TESTDIR to avoid conflicts with the default value of $TMPDIR.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos): Don't create directories named `tmp' in
+ $TESTDIR to avoid conflicts with the default value of $TMPDIR.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh (directory_cmp): Use $TESTDIR for temporary files, like the
+ dotest functions.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * sanity.sh: No longer allow user override of $tmp. Set $TMPDIR to a
+ directory under $TESTDIR, as for $HOME, but still allowing for user
+ override. Check for cvs-serv* directories under $TMPDIR rather than
+ $tmp at the end of the script.
+
+2004-02-19 Derek Price <address@hidden>
+
+ * run.c (cmdline_quote): Plug memory leak.
+ (format_cmdline): Don't bother with freeing memory before a call to
+ error() which will exit. Plug memory leak.
+ (Report from Mark D. Baushke <address@hidden>.)
+
+2004-02-18 Derek Price <address@hidden>
+
+ * run.c (format_cmdline): Move variable declaration to a position more
+ acceptable to the ANSI C standards.
+
+2004-02-17 Derek Price <address@hidden>
+
+ * commit.c (precommit_proc): Installed new format_cmdline() instead
+ of the old, nonuniform info file interpreter. Support for new user
+ data field in Parse_Info() callbacks. Use cmdlinequote() instead of c.
+ 10 lines of quote processing.
+ * cvs.h (expand_path): Update proto.
+ (UseNewInfoFmtStrings): New global variable to keep track of the config
+ option of the same name.
+ (format_cmdline): Added function prototypes and type definitions to
+ support this new function.
+ * edit.c (notify_proc): Added the formatsafe flag to an
+ expand_path() call. Installed new format_cmdline() instead of the old,
+ nonuniform info file interpreter. Support for new user data field in
+ Parse_Info() callbacks.
+ * expand_path.c (expand_path): Added the formatsafe flag to and some
+ code to double up '%'s in variable subs if formatsafe is set on the
+ presumption that the variables that expand_path() subs into the string
+ are already working paths to files or whatever. It should be quoting
+ too but I haven't done that yet.
+ * logmsg.c (title_proc, logmsg_list_to_args_proc, update_logfile_proc,
+ rcsinfo_proc, editinfo_proc, verifymsg_proc, logfile_write): Installed
+ new format_cmdline() instead of the old, nonuniform info file
+ interpreter. Support for new user data field in Parse_Info()
+ callbacks.
+ * main.c: Added global UseNewInfoFmtStrings variable to keep track
+ of the config file option of the same name (see above).
+ * mkmodules.c (commitinfo_contents, editinfo_contents,
+ taginfo_contents, verifymsg_contents, loginfo_contents: Document new
+ format string features.
+ (config_contents): Added new UseNewInfoFmtStrings option with default
+ of yes.
+ * modules.c (do_module): Added the formatsafe flag to an
+ expand_path call.
+ * parseinfo.c (Parse_Info, parse_config): Added the formatsafe flag
+ to an expand_path() call. Added handling for UseNewInfoFmtStrings
+ option in the config file. Set to no, modifications needed to set to
+ yes are harmless. Set to yes, modifications needed for full
+ compatibility cause deprecation warnings. Eliminating deprecation
+ warnings should allow executable to be compile with
+ --disable-old-info-format-support passed into configure. Added user
+ data field (closure) which is passed through into callproc to
+ Parse_Info().
+ * run.c (format_cmdline): New function to provide uniform handling of
+ *info file scripting hook lines.
+ (cmdline_bindings_hash_node_delete,
+ (cmdlineescape, cmdlinequote): New functions to "quote" and "escape"
+ strings like you would to get them past a command line parser as an
+ argument.
+ (run_setup): Made quote aware
+ * sanity.sh (info, taginfo, config, pserver, lockfiles, reserved):
+ New tests for the new format string functionality as well as the
+ support of backwards compatibility. Had to alter a few of the tests to
+ not care which version of some of the (shared) config files they'd just
+ checked in so that the tests can be run in any order.
+ (taginfo): Add tests for file names with spaces in them.
+ * server.c (template_proc): Support for new user data field in
+ Parse_Info() callbacks.
+ * tag.c (pretag_proc): Installed new format_cmdline() instead of the
+ old, nonuniform info file interpreter. Add support for new user data
+ field in Parse_Info() callbacks.
+ (check_fileproc): Add proper tag_info struct as node data rather than a
+ single revision number in a string and pass tlist properly rather than
+ in a global variable.
+ (check_filesdone_proc): Altered to use non-global tlist.
+ (tag_delproc): Handle new tag_info struct data members.
+ (pretag_list_to_args_proc): Ditto.
+ (pretag_list_proc): Deleted.
+ * wrapper.c (Parse_Info): Added the formatsafe flag to an expand_path()
+ call.
+
+2004-02-17 Derek Price <address@hidden>
+
+ * sanity.sh: Check for $PWD != $TESTDIR after each set of tests rather
+ than once at the end. Check that there are no cvs-serv* directories in
+ $tmp after each set of remote tests.
+
+2004-02-17 Derek Price <address@hidden>
+
+ * sanity.sh: Don't check for an empty $TESTDIR - if $TESTDIR was empty
+ then the preceding call to mkdir would have failed anyhow.
+
+2004-02-17 Larry Jones <address@hidden>
+
+ * log.c (rlog_proc): Fix (harmless) uninitialized variable.
+
+ * sanity.sh (basicc): Add tests pointing out defective handling
+ of the Entries file.
+
+2004-02-17 Derek Price <address@hidden>
+
+ * checkout.c (build_dir_and_chdir): Expand header comment.
+
+2004-02-15 Mark D. Baushke <address@hidden>
+
+ * annotate.c (rannotate_proc): Plug a memory leak.
+ * log.c (log_fileproc): Ditto.
+ * tag.c (tag_fileproc): Ditto.
+ * update.c (checkout_file): Ditto.
+ * server.c (server_updated): Do not buf_free (filebuf) here.
+
+2004-02-15 Derek Price <address@hidden>
+
+ Make error() accessible to the GNULIB functions.
+
+ * error.h: Move into the ../lib directory.
+
+2004-02-13 Derek Price <address@hidden>
+
+ * lock.c (Reader_Lock, lock_dir_for_write): Plug memory leaks.
+ (Report from Mark D. Baushke <address@hidden>.)
+
+2004-02-13 Derek Price <address@hidden>
+
+ * lock.c (remove_lock_files): Minor refactoring.
+
+2004-02-12 Mark D. Baushke <address@hidden>
+
+ * lock.c (lock_exists): Plug a memory leak.
+
+2004-02-12 Derek Price <address@hidden>
+
+ * modules.c: Reformat comment and line to fit in 80 chars.
+
+2004-02-12 Mark D. Baushke <address@hidden>
+
+ * server.c (do_cvs_command): Plug a memory leak.
+ (Use buf_free() rather than free().)
+
+2004-02-12 Derek Price <address@hidden>
+
+ * sanity.sh (lockfiles): Reformat a comment for 80 characters. Fix a
+ few minor issues that caused tests to fail in remote mode. Clean up
+ after test.
+
+2004-02-12 Derek Price <address@hidden>
+
+ * lock.c (unlock_proc): Don't expect an int in closure as it is not
+ condoned in the C standard. For now, just assume zero since this
+ function is currently only called from one location.
+ (remove_locks): Pass NULL for the closure argument to unlock_proc().
+
+2004-02-12 Derek Price <address@hidden>
+
+ * lock.c (unlock_proc): Use closure as true/false free_repository
+ free_repository argument to remove_lock_files. Move to a position
+ above remove_locks and delete prototype.
+ (remove_locks): Don't free storage, as specified in our header comment,
+ in order to avoid a seg fault that could otherwise occur after waiting
+ on a lock.
+ * sanity.sh (lockfiles): Add tests for all operation (read, read
+ promotably, or write) and lock combinations.
+ (Original report from Mark <address@hidden>.)
+
+2004-02-12 Larry Jones <address@hidden>
+
+ * modules.c (_do_module): Rename to my_module to avoid reserved name.
+ * stack.c (_push, _pop, _unshift, _shift): Rename to do_*.
+
+2004-02-12 Mark D. Baushke <address@hidden>
+
+ * commit.c (find_fileproc): Plug a memory leak.
+
+2004-02-12 Larry Jones <address@hidden>
+
+ * lock.c (_lock_simple_remove): Rename to remove_lock_files to avoid
+ reserved name. Fix typo (free => free_repository).
+ (lock_simple_remove): Delete; use remove_lock_files directly.
+ (lock_simple_remove_and_free): Ditto.
+ (_lock_exists): Rename to lock_exists.
+
+2004-02-11 Larry Jones <address@hidden>
+
+ * root.c (parse_cvsroot): Set hostname in fork mode for error messages.
+ * buffer.c (stdio_buffer_shutdown): Undo previous change.
+
+2004-02-11 Mark D. Baushke <address@hidden>
+
+ * buffer.c (buf_free): Plug a memory leak.
+ * commit.c (checkaddfile): Ditto.
+
+ * server.c (fd_buffer_shutdown): Avoid a double free().
+
+ * parseinfo.c (parse_config): Fix comments.
+
+2004-02-11 Derek Price <address@hidden>
+
+ * buffer.c (stdio_buffer_shutdown): Add logic to avoid attempting to
+ print current_parsed_root->hostname when using the fork method.
+
+2004-02-11 Derek Price <address@hidden>
+
+ * server.c (do_cvs_command): Simplify stream & pipe closing.
+ (Suggestion from Eric Siegerman <address@hidden>.)
+
+ * cvs.h, subr.c (set_block_fd): Remove this unnecessary function.
+
+2004-02-11 Derek Price <address@hidden>
+
+ * checkout.c (checkout_proc): Remove unneeded variable and enclosing
+ block.
+ * modules.c (_do_modules): Minor whitespace change.
+
+2004-02-10 Derek Price <address@hidden>
+
+ * lock.c (lock_simple_remove): Move core functionality...
+ (_lock_simple_remove): ...here.
+ (lock_simple_remove_and_free): New function.
+ (set_promotable_lock): Use new function to avoid freeing data that will
be
+ reused.
+
+2004-02-10 Derek Price <address@hidden>
+
+ * server.c (do_cvs_command): s/FIXCVS/FIXME/ in comment.
+ (set_block_fd, set_nonblock_fd): Move to...
+ * subr.c: ...here.
+ * cvs.h: Add protos for the above two functions.
+ * buffer.c (stdio_buffer_shutdown): Replace fgetc() which checked for
+ unread data on a pipe with a nonblock read.
+
+2004-02-10 Derek Price <address@hidden>
+
+ * server.c (do_cvs_command): Have the server child close all the pipes
+ but the flow control pipe and wait on an EOF on the flow control pipe
+ from the parent when done to avoid a race condition that could
+ otherwise generate a SIGPIPE for the parent before the SIGCHILD when
+ the other pipes were so full after a child exited that the parent
+ attempted to write a stop byte to the flow control pipe.
+ (Original report from <address@hidden>.)
+
+2004-02-10 Derek Price <address@hidden>
+
+ * buffer.c (stdio_buffer_shutdown): Add a helpful comment.
+
+2004-02-09 Derek Price <address@hidden>
+
+ * lock.c (lock_simple_remove): Add comments. Use critical sections to
+ set some variables that might otherwise cause trouble.
+ (struct lock): Correct comment.
+
+2004-02-09 Derek Price <address@hidden>
+
+ Attempt to improve readability of code.
+
+ * lock.c (promotable_lock): Rename to...
+ (set_promotable_lock): ...this.
+ (set_promotablelock_proc): Expand header comment.
+ (Promotable_Lock): Rename to...
+ (lock_tree_promotably): ...this.
+
+2004-02-09 Derek Price <address@hidden>
+
+ * lock.c (set_writelock_proc): Remove unused prototype.
+ (promotable_lock): Remove unneded whitespace.
+ (Promotable_Lock): Correct comments.
+
+2004-02-09 Derek Price <address@hidden>
+
+ * sanity.sh (co-d): Update comments and tests to reflect the current
+ state of my side of my discussion with Larry Jones on how these
+ commands should behave.
+
+2004-02-09 Derek Price <address@hidden>
+
+ * sanity.sh (emptydir): Add two new tests for how modules -d behaves
+ when a directory already exists in the user's workspace.
+ (emptydir): Add --keep functionality.
+
+2004-02-09 Derek Price <address@hidden>
+
+ * sanity.sh (co-d): New test to prove `co -d' failure case.
+
+2004-02-05 Derek Price <address@hidden>
+
+ * sanity.sh (recase): Fix typo that creeped in somehow between my last
+ test run and my commit.
+
+2004-02-04 Derek Price <address@hidden>
+
+ * modules.c (do_modules): Move content to and make this function a
+ wrapper for...
+ (_do_modules): ...this new function which can watch for infinite loops
+ in alias modules.
+ * stack.c (_push, _pop, _unshift, _shift, push_string, pop_string,
+ unshift_string, shift_string): New
+ functions.
+ * stack.h (push_string, pop_string, unshift_string, shift_string: New
+ prototypes.
+ * sanity.sh (modules): Add check for nested alias loops.
+
+2004-02-04 Derek Price <address@hidden>
+
+ * sanity.sh (recase): Update test names and comments for clarity and
+ consistency.
+
+2004-02-04 Derek Price <address@hidden>
+
+ * sanity.sh (recase): Restore some cruft necessary when clients know
+ they are on case insensitive systems.
+
+2004-02-03 Derek Price <address@hidden>
+
+ Preserve the case of checked out directories in a path as well as file
+ names for client communication with the server.
+
+ * Makefile.am (cvs_SOURCES): Add stack.c & stack.h.
+ * stack.c, stack.h: New files.
+ * cvs.h: Include stack.h.
+ * client.c (send_file_names): Preserve the case of directories in a
+ path as well as file names for communication with the server.
+
+ * Makefile.in: Regenerated.
+
+2004-02-04 Derek Price <address@hidden>
+
+ * checkout.c (find_fileproc): Update error message for consistency.
+ * sanity.sh (basica): Update to compensate.
+
+2004-02-04 Derek Price <address@hidden>
+
+ * classify.c (Classify_File): Update header comment block and reformat
+ prototype for readability in 80 character widths.
+
+2004-02-02 Derek Price <address@hidden>
+
+ * sanity.sh (*): Update tests for the new status message from update.c.
+
+2004-02-02 Derek Price <address@hidden>
+
+ * sanity.sh (join-rm): New test for issue #104 & #159.
+
+2004-02-02 Derek Price <address@hidden>
+
+ * update.c (join_file): Correct status message for consistency.
+
+2004-02-02 Derek Price <address@hidden>
+
+ Continue removal from server of handling of case insensitive clients.
+
+ * cvs.h: Remove extern declaration of ign_case.
+ * ignore.c (ign_case): Remove declaration.
+ (ign_name): Remove support for ign_case.
+ * server.c (serve_case): Ditto.
+ (requests): No longer support the "Case" request.
+ * rcs.c (locate_rcs): Remove reference to GLOBAL in function header
+ comment.
+
+2004-02-02 Derek Price <address@hidden>
+
+ * client.c (send_file_names): Restore prescribed client handling of the
+ FILENAMES_CASE_INSENSITIVE switch.
+
+2004-01-25 Derek Price <address@hidden>
+
+ * server.c (kserver_authenticate_connection): Fix call to
+ switch_to_user().
+ (Original patch from Alexey Mahotkin <address@hidden>.)
+
+2004-01-22 Derek Price <address@hidden>
+
+ * modules.c (do_module): Strip trailing slashes before checking for
+ infinite alias loops.
+ * sanity.sh (modules): Tests for response to infinite alias loops.
+
+2004-01-17 Mark D. Baushke <address@hidden>
+
+ * logmsg.c (do_verify): Eliminate double-free bug.
+ (Original patch from Gerald Combs.)
+
+2004-01-12 Mark D. Baushke <address@hidden>
+
+ * lock.c (lock_name): Deal with potentially NULL string pointers
+ in calls to TRACE.
+ (promotable_lock): Ditto.
+ (set_lock): Ditto.
+ * sanity.sh (trace): Update to latest patterns.
+
+2004-01-07 Larry Jones <address@hidden>
+
+ * checkout.c (safe_location): Remove unused variable(s).
+ * lock.c (lock_tree_for_write): Ditto.
+ * rcs.c (RCS_checkin): Ditto.
+ * subr.c (compare_revnums): Ditto.
+ * tag.c (tag_check_valid): Ditto.
+ * mkmodules.c (init): Initialize err and return it rather than 0.
+ * server.c (do_cvs_command): Only define and set max_command_fd if
+ we're actually going to use it.
+
+2004-01-06 Mark D. Baushke <address@hidden>
+
+ * socket-client.c (socket_buffer_initialize): Fix argument
+ declaration for VMS compiler.
+ (Patch submitted from Michael Lemke
+ <address@hidden>.)
+
+2004-01-01 Larry Jones <address@hidden>
+
+ * zlib.c (read_and_gzip, gunzip_and_write): Fix potential buffer
+ overruns, use names for magic numbers.
+ (Original patch from Jeff Downs <address@hidden>.)
+
+2003-12-17 Larry Jones <address@hidden>
+
+ * main.c (main): Don't reference isremote without CLIENT_SUPPORT.
+ (Patch from Jim Salter <address@hidden>.)
+
+2003-12-18 Derek Price <address@hidden>
+
+ * server.c (switch_to_user): SysLog attempts to root from pserver.
+
+2003-12-18 Derek Price <address@hidden>
+
+ * server.c (switch_to_user): Don't allow CVS to run as root in pserver
+ mode.
+ (Original patch from Wichert Akkerman via Bradley M Kuhn
+ <address@hidden>.)
+ * sanity.sh (pserver): Check for bad root error message.
+
+2003-12-17 Larry Jones <address@hidden>
+
+ * run.c (close_on_exec): fcntl is not documented to return 0 for
+ success (and QNX doesn't), only -1 for error.
+ (Patch from George Refseth <address@hidden>.)
+
+2003-12-10 Larry Jones <address@hidden>
+
+ * rcs.c: Cleanup HAVE_MMAP code in preparation for falling back to
+ stdio if mmap fails on large files.
+
+2003-12-10 Mark D. Baushke <address@hidden>
+
+ * tag.c (tag_check_valid): Fix typo.
+ (Patch from Rob Clevenger <address@hidden>.)
+
+2003-12-09 Derek Price <address@hidden>
+
+ Rewrite code to use promotable write locks to avoid locking more than a
+ directory at a time for a full write.
+
+ * cvs.h (lock_tree_for_write): Rename to...
+ (lock_tree_promotably): ...this.
+ (Simple_Lock_Cleanup): New prototype.
+ * lock.c: Remove some unneeded prototypes. Some function
+ reorganization.
+ (struct lock): Make repository const. Add file1 & file2 to
+ track lock names. Keep track of when repository needs to be freed.
+ (promotablelock, global_writelock): New globals.
+ (locked_dir, locked_list): Remove unneeded globals.
+ (lock_name): Take const args.
+ (remove_locks): Update comment. Move readlock cleanup...
+ (Simple_Lock_Cleanup): ...to this new function which also cleans up
+ writelocks.
+ (Lock_Cleanup): No need to clean up after the obsolete locked_dir &
+ locked_list.
+ (lock_simple_remove): Use new lock name cache. Set lock->repository to
+ NULL to show locks are removed. Free repository name when necessary.
+ (Reader_Lock): Copy xrepository argument so we do not need to trust the
+ caller not to dispose of it. Use lock name cache. Factor code to set
+ the global readlock variable...
+ (set_readlock_name): ...to this new function so it can be used by
+ promotable_lock too.
+ (readers_exist): Factor common code and make wrapper for...
+ (_lock_exists): ...this new function.
+ (promotable_exists): Wrapper for _lock_exists().
+ (write_lock): Rename to...
+ (promotable_lock): ...this and tweak for new behavior.
+ (set_writelock_proc): Rename to...
+ (set_promotablelock_proc): ...this and tweak for new functionality.
+ (Write_Lock): Rename to...
+ (Promotable_Lock): ...this and tweak for new functionality.
+ (set_lock): Add trace.
+ (lock_tree_for_write): Rename to...
+ (lock_tree_promotably): ...this and tweak for new functionality.
+ (lock_dir_for_write): Lock only one directory at a time in a promotable
+ lock aware fashion using new interfaces.
+ * admin.c, commit.c, edit.c, watch.c:
+ s/lock_tree_for_write/lock_tree_promotably/.
+ s/CVS_LOCK_NONE/CVS_LOCK_WRITE/ in calls to start_recursion that used
+ to rely on lock_tree_for_write() to obtain their write locks. Remove
+ some unnecessary typecasting.
+ * recurse.c (do_recursion): Use Simple_Lock_Cleanup() rather than
+ Lock_Cleanup to avoid removing promotable locks.
+ * server.c (do_cvs_command): Add traces.
+ (server): Add new way to sleep the parent server process for connecting
+ a debugger.
+ * sanity.sh (lockfiles, multiroot2): Update tests to accomodate minor
+ trace inconsistencies.
+
+2003-12-09 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (trace): Remove trace from the set of tests to run.
+ The regexps used take an excessive amount of time and need to
+ be simplified.
+
+2003-12-08 Mark D. Baushke <address@hidden>
+
+ * rcs.c (rcsbuf_ftell): Rename to...
+ (rcsbuf_ftello): this.
+ (rcsbuf_cache_open): Use off_t rather than long as the pos
+ argument. Use fseeko rather than fseek and ftello rather than
+ ftell.
+ * rcs.h (struct rcsnode): delta_pos is now an off_t type.
+
+2003-12-03 Derek Price <address@hidden>
+
+ * sanity.sh (recase): Add some clarifying comments.
+
+2003-12-03 Larry Jones <address@hidden>
+
+ * expand_path.c (expand_variable): Expand ${CVSROOT} to just the
+ directory like it's supposed to be.
+ (Reported by Michael S. Tsirkin <address@hidden>.)
+
+2003-11-26 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (recase-17sscs): Use ${CVSROOT_DIRNAME} in pattern.
+
+2003-11-26 Derek Price <address@hidden>
+
+ Remove server support for case insensitive clients. Includes some
+ merges from 1.11.x.
+
+ * add.c, client.c, cvs.h, rcs.c, subr.c: Remove unnecessary support for
+ case insensitive clients.
+ * sanity.sh (tests): Add recase.
+ (recase): New test of cases that might be expected to cause problems
+ with a heterogeneous mix of case sensitive and case insensitive clients
+ and servers.
+
+2003-11-26 Derek Price <address@hidden>
+
+ * sanity.sh (modules3-2): Simplify syntax that may have given Cygwin
+ intermittent conniptions.
+
+2003-11-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (binfiles2): Correct yet another Cygwin difficulty.
+
+2003-11-25 Derek Price <address@hidden>
+
+ * sanity.sh (release): Perform forgotten cleanup.
+
+2003-11-25 Derek Price <address@hidden>
+
+ * sanity.sh (env): Enable to work with $remotehost.
+
+2003-11-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (trace-19): Separate stdout and stderr to workaround
+ problems on SGI IRIX.
+ (crecrepos): Use 'unset DISPLAY' to avoid problems with ssh
+ X11Forwarding configurations.
+
+2003-11-25 Derek Price <address@hidden>
+
+ * sanity.sh (pserver, server, server2): Save $servercvs and use the
+ local $testcvs for these tests.
+
+2003-11-25 Derek Price <address@hidden>
+
+ * commit.c (commit_fileproc): Reword comment.
+
+2003-11-25 Derek Price <address@hidden>
+
+ * sanity.sh (devcom3-9ar): Ignore the stderr output since it varies
+ considerably between platforms.
+
+2003-11-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (CVS_RSH): Read config file sooner to pickup RSH_DFLT
+ for use in setting CVS_RSH variable.
+ * sanity.config.sh.in (RSH_DFLT): Use new substitution variable.
+ * Makefile.am (localcheck, remotecheck): Depend on sanity.config.sh.
+ * Makefile.in: Regenerate for new Makefile.am.
+
+ * update.c (join_file): Deal with rev1 == NULL due to rev1 merge tag
+ being missing from the current file.
+ * sanity.sh (join6): New tests for this case.
+ (trace): Renumber test cases.
+
+2003-11-24 Larry Jones <address@hidden>
+
+ * diff.c (diff_file_nodiff): use_rev1 does *not* imply that diff_rev1
+ is not null, diff_date1 could be set instead (ditto for use_rev2).
+ (Reported by <address@hidden>.)
+
+2003-11-24 Mark D. Baushke <address@hidden>
+
+ * client.c (connect_to_forked_server): Avoid passing NULL strings
+ to TRACE. Calling printf("%s",NULL) is not defined and may
+ segfault on some systems.
+ * diff.c (diff_file_nodiff): Ditto.
+ * main.c (main): Ditto.
+ * modules.c (do_module): Ditto.
+ * patch.c (patch_proc): Ditto.
+ * rcs.c (RCS_cmp_file): Ditto.
+ * recurse.c (start_recursion): Ditto.
+ * root.c (parse_cvsroot): Ditto.
+ * server.c (dirswitch, server_pathname_check, dirswitch,
+ serve_directory): Ditto.
+ * tag.c (rtag_proc, check_fileproc, tag_check_valid): Ditto.
+ * sanity.sh (trace): New testcase to test the -t option.
+ (RCSDATE, ISODATE, PFMT): New variables in aid of the trace tests.
+ (dotest_fail_sort): New function in aid of the trace tests.
+ (template): Fix cleanup.
+
+2003-11-24 Derek Price <address@hidden>
+
+ * sanity.sh (modes3): Skip modes3-5 entirely under Cygwin since
+ permisions are broken there. This change removes most of the earlier
+ Cygwin differentiation in this test ($cygwin_hack & $cygwin_hack2) in
+ favor of skipping the test entirely.
+
+2003-11-24 Derek Price <address@hidden>
+
+ * sanity.sh: Add `-h <hostname>' option to enable testing across a
+ :ext: connection to another host. Warn when `-h' is specified without
+ $TESTDIR. Leave $TESTDIR intact when it looks absolute since it may
+ contain symlinks. Allow $CVS_SERVER to be overridden via the
+ environment for `-h'. Default $CVS_RSH to `ssh'.
+ (*): Use $CVS_RSH to perform certain commands on the remote host (esp.
+ `ln -s' and `chmod') when `-h' is specified to work around
+ incompatibilities with CygWin & Samba. Add a few other minor
+ workarounds for Cygwin bugs.
+
+ (newroot): New function.
+ (*): Use newroot when appropriate.
+
+2003-11-21 Larry Jones <address@hidden>
+
+ * hash.c (printnode, printlist): Cast %p arguments to void * as
+ required by the C standard.
+
+2003-11-21 Larry Jones <address@hidden>
+
+ * recurse.c (start_recursion, do_recursion): Cast %p arguments to
+ void * as required by the C standard.
+
+2003-11-19 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_getrevtime): Add error checking; cleanup.
+
+2003-11-18 Derek Price <address@hidden>
+
+ * socket-client.c (socket_buffer_initialize): Rename poorly named `n'
+ to a slightly more descriptive `sbuf'.
+ (Suggestion from Larry Jones <address@hidden>.)
+
+2003-11-18 Derek Price <address@hidden>
+
+ * socket-client.c (socket_buffer_initialize): Pass in the socket
+ closure we allocate.
+ (Report from Larry Jones <address@hidden>.)
+
+2003-11-18 Derek Price <address@hidden>
+
+ * modules.c (do_module): Reject absolute paths.
+ (Report and suggested fix from Tony Hoyle <address@hidden>.)
+
+ * sanity.sh (abspath2): Check for the above.
+ (spacefiles): Remove tests that expect absolute paths to files in the
+ top level repository directory to work.
+ (tests): Add abspath2.
+
+2003-11-18 Derek Price <address@hidden>
+
+ * socket-client.c (socket_buffer_initialize): Correct a typo that
+ happened to compile.
+ (Report and suggested fix from Patrick Brown <address@hidden>.)
+
+2003-11-13 Derek Price <address@hidden>
+
+ * rcs.c (RCS_delete_revs): It's `&&', not `and'.
+
+2003-11-13 Derek Price <address@hidden>
+
+ * sanity.sh: Create the empty log to make it easier to tail immediately
+ after the script is started.
+
+2003-11-13 Derek Price <address@hidden>
+
+ * sanity.sh (exit_help): Correct help to specify `-H' and not `-h' as
+ the help option.
+
+2003-11-13 Derek Price <address@hidden>
+
+ * rcs.c (RCS_delete_revs): Don't use the WOE32 kludge which refuses to
+ delete revisions from bvinary files on Cygwin. I'm not sure what the
+ kludge was trying to avoid, but commenting it out causes the test suite
+ to pass.
+
+2003-11-12 Derek Price <address@hidden>
+
+ * main.c (main): Remove a trailing newline from the version string.
+ Replace multiple calls to fputs to a single call reformated to C89
+ specifications. Remove some typecasts unecessary under C89.
+ * sanity.sh (version): Remove trailing newline from the version string.
+
+2003-11-12 Derek Price <address@hidden>
+
+ * add.c (add): Allocate more space for the string I added characters
+ to.
+ (Report from Mark D. Baushke <address@hidden>.)
+
+2003-11-11 Derek Price <address@hidden>
+
+ * add.c (add), classify.c (Classify_File), client.c (update_entries),
+ repos.c (Name_Repository): Use consistent quoting in error messages.
+ Misc reformatting.
+ * sanity.sh: Update to match.
+
+2003-11-10 Derek Price <address@hidden>
+
+ * commit.c (find_fileproc, check_fileproc): Refuse to remove files
+ when the file exists in the sandbox. This used to cause data loss.
+ (Report from Andreas Reifschneider <address@hidden>.)
+
+ * sanity.sh (rmadd3): Update to match. Expand comments.
+
+2003-11-10 Derek Price <address@hidden>
+
+ * sanity.sh (rmadd3): Test the behavior of commit after the
+ add/replace.
+ (Report from Andreas Reifschneider <address@hidden>.)
+
+2003-11-10 Derek Price <address@hidden>
+
+ * sanity.sh (rmadd3): Fix another typo.
+
+2003-11-10 Mark D. Baushke <address@hidden>
+
+ * recurse.c (do_dir_proc): Set xframe.repository to NULL after a
+ call to free().
+
+2003-11-10 Derek Price <address@hidden>
+
+ * sanity.sh (rmadd3): Fix typo.
+
+2003-11-10 Derek Price <address@hidden>
+
+ * sanity.sh (rmadd3): New tests that confirms that CVS refuses to
+ delete a file it thinks was already removed.
+ (Report and test from Andreas Reifschneider
+ <address@hidden>.)
+
+2003-11-03 Derek Price <address@hidden>
+
+ * sanity.sh (server): Test that the global `-l' option is ignored
+ nonfatally.
+
+2003-11-03 Derek Price <address@hidden>
+
+ * server.c (serve_global_option): Warn that -l is being ignored rather
+ than exiting fatally due to backwards compatibility complaints from
+ administrators.
+
+2003-11-01 Larry Jones <address@hidden>
+
+ * filesubr.c (xcmp): Make sure S_ISLNK exists before calling it.
+ (Reported by Paul Edwards <address@hidden>.)
+
+2003-10-31 Derek Price <address@hidden>
+
+ * sanity.sh: s/${TESTDIR}/cvsroot/${CVSROOT_DIRNAME}/.
+
+2003-10-28 Derek Price <address@hidden>
+
+ * sanity.sh (devcom): Renumber tests and use dotest function.
+
+2003-10-28 Derek Price <address@hidden>
+
+ * sever.h: Add the standard copyright notice.
+
+2003-10-28 Derek Price <address@hidden>
+
+ * lock.c: Remove some suggestions which have already been implemented
+ or which have become obsolete from the header comment.
+
+2003-10-26 Derek Price <address@hidden>
+
+ * sanity.sh (join6): Fix a few typos in the last test and remove a
+ misplaced test.
+
+2003-10-25 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (parseroot): Use dokeep function.
+
+ * sanity.sh (parseroot): Perform this test in a subdirectory.
+ It should avoid problems on case-insensitive systems where
+ CVSROOT and cvsroot are the same directory (eg, MacOS X).
+
+2003-10-24 Derek Price <address@hidden>
+
+ * update.c (join_file): Restore the optimization Mark recently removed,
+ but fix it. Move one other optimization up since it needs to be
+ checked for first. Add bew status messages like merge_file produces
+ when the requested diff has already been applied to the destination.
+ Expand header comment.
+ * sanity.sh (join6): Add tests for the new error messages.
+ (import-113, join-admin-2, diffmerge1): Fix collateral damage.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * update.c (merge_file): Optimize & eliminate code.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * recurse.c (do_recursion): Assert that ignoring the return value of
+ Name_Repository is not a memory leak.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * repos.c (Name_Repository): Replace a FIXME with the improved error
+ message it requested.
+ * sanity.sh (errmsg3): New test for the above.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * patch.c (patch_proc): Avoid memory leak.
+ (Patch from Mark D. Baushke <address@hidden>.)
+ (patch_proc): Reformat a few long lines for readability.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * vers_ts.c (Version_TS): Move variable declaration inside the only
+ block where it is used and remove uneeded reinitialization.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * server.h: s/^extern// off of function declarations per HACKING.
+ Reformat protos for readability.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * vers_ts.c (Version_TS): Reformat declaration and expand header
+ comment.
+
+2003-10-24 Derek Price <address@hidden>
+
+ * update.c (merge_file): Remove code that hasn't been used since CVS
+ used an external RCS (1.9.something).
+
+2003-10-23 Mark D. Baushke <address@hidden>
+
+ * update.c (join_file): Do the -jrev1 -jrev2 merge even when
+ the file is already at rev2.
+ * sanity.sh (join6): New testcase for above.
+ (Suggested by Paul Edwards, from somewhere in Australia.)
+ (import): Fix collateral damage.
+
+2003-10-23 Derek Price <address@hidden>
+
+ * sanity.sh (fail): Refer the user to the `TESTS' and `check.log' files
+ on failure.
+
+2003-10-22 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Reformat function declaration and
+ expand comments.
+ (Original patch from Terrence Enger <address@hidden>.)
+
+2003-10-22 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_LDADD): Add $(LIBINTL) for gettext.
+ * Makefile.in: Regenerated.
+
+2003-10-19 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (admin-31): Fix more typos.
+
+2003-10-18 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (admin): Fix a typo.
+
+ * admin.c (admin_fileproc): Restore the ':' character in the
+ -mtag:message admin argument even if the tag does not exist so
+ that other files with the tag will be found. Also, be more
+ paranoid that a symbolic tag actually points to a version that
+ exists.
+ (Reported by Rodolfo Schulz de Lima <address@hidden>.)
+ * sanity.sh (admin): Test these changes.
+
+2003-10-17 Mark D. Baushke <address@hidden>
+
+ * admin.c (admin_fileproc): Force tag match on admin
+ -mversion:message rather than altering the wrong log message.
+ (Patch from "Rodolfo Schulz de Lima" <address@hidden>.)
+ * sanity.sh (admin): Test case for it.
+
+2003-10-15 Larry Jones <address@hidden>
+
+ * commit.c (commit_fileproc, finaladd): Don't call fixaddfile()
+ if the RCS file didn't get created at all.
+ (Reported by David Wood <address@hidden>.)
+
+2003-10-14 Derek Price <address@hidden>
+
+ Port to pedantic POSIX 1003.1-2001 hosts, such as Debian GNU/Linux
+ testing with _POSIX2_VERSION=200112 in the environment.
+
+ * sanity.sh: Use 'sed 1q', not 'head -1'.
+ (Patch from Paul Eggert <address@hidden>.)
+
+2003-10-10 Derek Price <address@hidden>
+
+ * lock.c (set_lock): Clarify comment.
+
+2003-10-11 Larry Jones <address@hidden>
+
+ * server.c (server_cleanup): Replace CVS_CHDIR call: some systems
+ won't allow you to delete a directory tree containg your working
+ directory.
+
+2003-10-10 Derek Price <address@hidden>
+
+ * server.c (cvs_output, cvs_outerr): Protect calls to syslog()
+ with the usual preprocessor condition: HAVE_SYSLOG_H.
+ (Original patch from Terrence Enger <address@hidden>.)
+
+2003-10-09 Derek Price <address@hidden>
+
+ * cvs.h: s/^extern// off of function declarations per HACKING.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add history.h.
+ * history.c: Include history.h. Add the `P' record types to more
+ comments. s/ALL_REC_TYPES/ALL_HISTORY_REC_TYPES/.
+ (usage): Reference ALL_HISTORY_REC_TYPES rather than using a separate
+ string literal.
+ (report_hrecs): Handle `P' record type.
+ (ALL_REC_TYPES): Rename and move...
+ * history.h (ALL_HISTORY_REC_TYPES): ...here.
+ * mkmodules.c: Include history.h.
+ (config_contents): Update contents of and references to LogHistory
+ records to use ALL_HISTORY_REC_TYPES.
+ * sanity.sh (basic2-64): Update to include history records of type `P'.
+
+ * Makefile.in: Regenerated.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * update.c (patch_file): Correct spelling and punctuation in comment.
+ Update some lines to fit in 80 characters.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * lock.c (remove_locks): Copy global struct and set status variable to
+ NULL before calling disposal functions that might try to access it
+ during calls to error(1,...).
+
+2003-10-08 Larry Jones <address@hidden>
+
+ * history.c (history): Don't conflate -e with -x since the client's
+ idea of what -e means may not match the server's.
+ (Reported by Frank Hemer <address@hidden>.)
+
+2003-10-08 Derek Price <address@hidden>
+
+ * lock.c (Lock_Cleanup), rcs.c (rcs_cleanup), server.c
+ (server_cleanup): Expand comments about the never_run_again variable
+ and its interoperation with critical sections, exit, and interrupts.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * lock.c (remove_locks): Reduce TRACE level since this function is
+ rarely called and we can usually rely on tracing higher level
+ functions.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * lock.c (lock_name, lock_simple_remove),
+ server.c (server_pathname_check, dirswitch): Add TRACE.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * main.c: Reformat header comment to fit in 80 chars.
+
+2003-10-08 Larry Jones <address@hidden>
+
+ * sanity.sh: Use dotest_fail instead of dotest_status for diff tests
+ since CVS only returns success/fail rather than 0/1/2 like diff does.
+
+2003-10-08 Derek Price <address@hidden>
+
+ Fix a client/server bug introduced via the data loss fix of 2003-03-17.
+ Basically, the server was reporting ambiguous filename requests when it
+ should have been trusting the user to type the intended case or using
+ the case the client preserved in CVS/Entries before it tried to look
+ anything up in case insensitive mode.
+
+ * rcs.c (locate_rcs): Use the filename exactly as cased before
+ investigating a case insensitive lookup, per the client/server protocol
+ specification. Expand comments.
+ * subr.c (locate_file_in_dir): This function only needs to locate files
+ case insensitively. Expand comments.
+ * cvs.h (locate_file_in_dir): Only prototype when servers which need to
+ handle case insensitivity are being compiled.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * rcs.c (locate_rcs): Declare static. Move to an earlier location in
+ file to avoid prototyping.
+ * rcs.h (locate_rcs): Remove proto.
+
+2003-10-08 Derek Price <address@hidden>
+
+ * lock.c (lock_filesdoneproc): Reformat long function prototype.
+
+2003-10-07 Larry Jones <address@hidden>
+
+ * server.c (server_cleanup): Remove old code that was commented out
+ with //, which isn't valid in C.
+
+2003-10-04 Derek Price <address@hidden>
+
+ * exithandle.c: New file.
+ * Makefile.am (cvs_SOURCES): Add exithandle.c.
+ * cvs.h (cleanup_register, signals_register): New prototypes.
+ * lock.c (Lock_Cleanup, remove_locks), rcs.c (rcs_cleanup),
+ server.c (server_cleanup): Avoid calling twice when called from a
+ signal handler and then exit. Avoid being interrupted while globals
+ that the signal handler might touch are in inconsistent states. Expand
+ comments.
+ * rcs.c (rcs_internal_lockfile): Ditto. Use cleanup_register rather
+ than calling atexit() directly.
+ * main.c (main): Consolidate signal stuff into a call to
+ signals_register(). Call cleanup_register to register cleanup
+ functions rather than calling atexit() directly.
+
+ * Makefile.in: Regenerated.
+
+2003-10-04 Derek Price <address@hidden>
+
+ * error.c, error.h: Remove error_exit() function.
+ * add.c, client.c, history.c, import.c, main.c, mkmodules.c, modules.c,
+ rcscmds.c, recurse.c, release.c, root.c, server.c, socket-client.c,
+ tag.c, update.c: s/\<error_exit *();$/exit (EXIT_FAILURE);/. Remove
+ related comments.
+
+2003-10-04 Derek Price <address@hidden>
+
+ * buffer.c: Reformat a few long function prototypes and lines.
+
+2003-10-04 Derek Price <address@hidden>
+
+ * hash.c (dellist): Immediately set input pointers to NULL in case they
+ are references to global variables which might be accessed by interrupt
+ handlers.
+
+2003-10-04 Derek Price <address@hidden>
+
+ * rcs.c (rcs_cleanup): Declare static.
+ * rcs.h (rcs_cleanup): Remove prototype.
+
+2003-10-03 Derek Price <address@hidden>
+
+ Move calls to Lock_Cleanup to the atexit handler.
+
+ * commit.c (commit): Don't call Lock_Cleanup on error exit.
+ * error.c (error_exit): Don't call Lock_Cleanup.
+ * lock.c (Lock_Cleanup): Don't worry about recursive calls now that we
+ are using atexit for calls on exit. Dispose locklist storage after the
+ locks are removed. Expand comments.
+ * main.c (main): Move Lock_Cleanup call into atexit(Lock_Cleanup).
+ * server.c (server_notify): Add TRACE.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Remove unnecessary typecasts.
+ (do_recursion): Ditto. Add TRACE. Expand comments. Remove unneeded
+ parens.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * main.c (main): Dispose of old Tmpdir and Editor when specified
+ multiple times between the command line & the ~/.cvsrc file.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * lock.c (remove_locks): Eliminate unecessary typecasting.
+
+2003-10-03 Derek Price <address@hidden>
+
+ Move calls to rcs_cleanup to the atexit handler.
+
+ * error.c (error_exit): Don't call rcs_cleanup.
+ * rcs.c: Initialize global RCS_LOCKFILE to NULL.
+ (rcs_internal_lockfile): Use atexit (rcs_cleanup) rather than
+ setting up signal handlers.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * modules.c (do_module): Format prototype.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * server.c (server_cleanup): Skip BUF_TO_NET checks as an optimization
+ when ERROR_USE_PROTOCOL is set.
+
+2003-10-03 Derek Price <address@hidden>
+
+ * modules.c (do_module): Use TRACE.
+
+2003-10-02 Derek Price <address@hidden>
+
+ * main.c (main): Don't free globals that might be needed by the cleanup
+ functions.
+ * server.c (server_cleanup): Only clean up when called in the parent
+ process. Set buffers to NULL before shutting down and freeing in case
+ we are interrupted. Improve comments. Add TRACE.
+
+2003-10-01 Derek Price <address@hidden>
+
+ * main.c (main): Use symbolic name for trace level.
+
+2003-10-01 Derek Price <address@hidden>
+
+ * client.c (connect_to_forked_server): Use TRACE macro rather than the
+ old style.
+
+2003-10-01 Derek Price <address@hidden>
+
+ * server.c (protocol): Initialize the protocol buffer to NULL so that
+ use before initialization may be detected.
+ (cvs_output, cvs_outerr): Syslog messages when the appropriate buffers
+ are not available.
+ (server_cleanup): Reorganize for a single exit point and to eliminate
+ duplicated code. Set buf_to_net to NULL before calling the buffer
+ shutdown functions in order to force error messages into the syslog.
+ * buffer.c (stdio_buffer_close): Remove FIXME comment re syslog since
+ the calls to error should go through the cvs_outerr function anyhow.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * entries.c (WriteTemplate): TRACE on entrance to a function, not exit.
+ Don't worry about checking noexec without server support since this
+ function will then do nothing.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * update.c (do_update): Reformat function decl. Move and merge
+ comment.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * client.h (buf_output, buf_outerr): Check that our buffers exist
+ before sending them data.
+ (buf_output_binary): Assert that the output buffer is not NULL.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * client.h, rcs.c, rcs.h, server.h: Assume __STDC__ since it is
+ defined as part of the C89 spec.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * commit.c (commit): Optimize function towards a single exit point.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * error.c (error_exit): Remove call to SYSTEM_CLEANUP.
+ * main.c (main): Set up atexit(SYSTEM_CLEANUP) rather than calling it
+ explicitly before exit.
+ * server.c (pserver_authenticate_connection): Don't call SYSTEM_CLEANUP
+ before exiting.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * error.c (exit_error): Remove call to server_cleanup.
+ * main.c (main): Call atexit(server_cleanup). Let server_cleanup turn
+ server_active off.
+ * server.c (server_cleanup): Don't require an argument. Fill out
+ header comment. Unset server_active when done.
+ (server): Don't call server_cleanup - let it be called via the atexit
+ handler.
+ * server.h (server_cleanup): Update proto.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * server.c (buf_output): Don't check that the buffers exist before
+ using them since cvs_outerr does this without problems.
+
+2003-09-30 Derek Price <address@hidden>
+
+ * server.c: Remove some unecessary function prototypes.
+
+2003-09-29 Derek Price <address@hidden>
+
+ * rcs.c (make_file_label): Make a failure to stat a file a fatal error
+ since it signals that a later read will also fail.
+
+2003-09-29 Derek Price <address@hidden>
+
+ * diff.c (diff_fileproc): Optimize the check for labels set by the
+ user.
+
+2003-09-26 Derek Price <address@hidden>
+
+ * diff.c (diff): Add a FIXME re spaces in diff arguments.
+
+2003-09-25 Mark D. Baushke <address@hidden>
+
+ * rcs.c (make_file_label): Do not return an uninitialized label.
+ (Reported by "Todd C. Miller" <address@hidden>)
+
+2003-09-24 Derek Price <address@hidden>
+
+ * lock.c (lock_name): Remove useless prototype.
+
+2003-09-12 Derek Price <address@hidden>
+
+ * sanity.sh (mkmodules): Correct comments.
+
+2003-09-12 Derek Price <address@hidden>
+
+ * mkmodules.c (mkmodules): Do not pass a string which came from the
+ checkoutlist file directly to error as a format string since we don't
+ want to trust any user with access to checkoutlist with creating printf
+ format strings. I already claimed I did this in the NEWS file.
+ (Thanks to Larry Jones for spotting my mistake.)
+ * sanity.sh (mkmodules): Test for the above.
+
+2003-09-12 Derek Price <address@hidden>
+
+ * mkmodules.c (checkoutlist_contents): Document the optional portions
+ of this file format more accurately.
+ (mkmodules): Ditto, in comments. Fix bug that always failed to ignore
+ whitespace before error messages.
+ * sanity.sh (mkmodules-temp-file-removal): Rename to...
+ (mkmodules): ...this and add a test of the checkoutlist error message.
+ Add cleanup step to restore checkoutlist.
+
+2003-09-08 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): Replace a chmod 0600, which shouldn't
+ really be necessary and which provided a false sense of security, with
+ an informative comment.
+ (Thanks to Paul Eggert <address@hidden> for his educational
+ advice.)
+
+2003-08-29 Derek Price <address@hidden>
+
+ * cvs.h: Delete reference to CVSADMROOT_EDITINFO.
+ * logmsg.c (editinfo_proc): Delete function and proto.
+ (do_editor): Don't look for editinfo script.
+ * mkmodules.c (editinfo_contents): Delete.
+ * sanity.sh (*): Remove references to editinfo in updates of the
+ CVSROOT module.
+
+2003-08-29 Derek Price <address@hidden>
+
+ * remove.c (cvsremove): Update quotes for consistency.
+
+2003-08-27 Larry Jones <address@hidden>
+
+ * history.c: 'P' is a valid record type and has been for a long time.
+ Add it to the comments, usage message, and, most important,
+ ALL_REC_TYPES so it gets recorded by default.
+ * server.c (do_cvs_command): Set global command_name to the real
+ command name rather than leaving it set to "server".
+ * sanity.sh: Update to match.
+ (Reported by Dmitry Ryzhkov <address@hidden>.)
+
+2003-08-19 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): Expand comments. Check for glibc version
+ before compiling chmod command. Remove FIXME to this effect.
+
+2003-08-19 Derek Price <address@hidden>
+
+ * logmsg.c (do_editor): Use cvs_temp_file rather than cvs_temp_name to
+ create and open the temporary file. Remove FIXME to this effect.
+
+2003-08-19 Derek Price <address@hidden>
+
+ * logmsg.c (do_editor): Move editinfo processing to before creation of
+ the temp file so that it may be skipped when no editor is defined.
+ Remove related FIXME. Add comments. Remove some processing of
+ editinfo_editor rendered obsolete when editinfo_editor ceased to be a
+ global.
+
+2003-08-19 Derek Price <address@hidden>
+
+ * (*.c): Move some includes to lib/system.h.
+
+2003-08-18 Derek Price <address@hidden>
+
+ * add.c (add): Use consistent quoting style in user messages.
+ * sanity.sh (*): Ditto.
+
+2003-08-13 Larry Jones <address@hidden>
+
+ * server.c (server_cleanup): Don't shutdown buf_from_net if it's
+ null.
+ (Reported by Scott Mitchell <address@hidden>.)
+
+2003-08-03 Mark D. Baushke <address@hidden>
+
+ * lock.c: Do not include xtime.h (already included via system.h).
+ * subr.c: Ditto.
+ (Original patch from Rainer Orth <address@hidden>
+ to fix IRIX 5.3 problem.)
+
+2003-08-01 Derek Price <address@hidden>
+
+ * sanity.sh (join5): Use $SPROG rather than $PROG.
+
+2003-08-01 Derek Price <address@hidden>
+
+ * sanity.sh (join5): Use $PROG consistently and escape a `.'.
+
+2003-08-01 Derek Price <address@hidden>
+
+ * sanity.sh (join5): Use `[a-z]*' as opposed to `update'.
+
+2003-07-31 Derek Price <address@hidden>
+
+ * rcscmds.c (RCS_merge): Pass `--' before the filename arguments to
+ diff so that filenames starting with `-' can be merged.
+ * sanity.sh (join5): New test for same.
+
+2003-07-31 Derek Price <address@hidden>
+
+ * add.c (add_directory): Don't print status information in really_quiet
+ mode.
+
+2003-07-29 Derek Price <address@hidden>
+
+ * commit.c (checkaddfile): Simplify the logic here, using assumptions
+ already made later in the function to remove calls to locate_rcs and
+ some conditionals. Use same assumptions to remove some variables.
+
+2003-07-29 Derek Price <address@hidden>
+
+ * login.c: Remove GETPASS & HAVE_GETPASSPHRASE cruft in favor of always
+ using the GNULIB getpass since the system getpass was removed from the
+ POSIX.2 specification.
+
+2003-07-28 Derek Price <address@hidden>
+
+ * subr.c (strip_trailiing_newlines): Use size_t rather than int to
+ count string length.
+ (Suggestion from Paul Edwards, who provides a broken return email
+ address in Tonga. I believe he is actually from Australia.)
+
+2003-07-28 Derek Price <address@hidden>
+
+ * checkout.c (checkout): Remove out-of-date comment about Checkin.prog
+ and Update.prog.
+
+2003-07-25 Derek Price <address@hidden>
+
+ * rcs.c (RCS_parsercsfile): Declare rcsfile argument as const.
+ * rcs.h (RCS_parsercsfile): Update prototype to match.
+ * commit.c (fixaddfile): Accept a single path to an rcs file as an
+ argument rather than trying to look it up again when it is not
+ necessary.
+
+2003-07-25 Derek Price <address@hidden>
+
+ * commit.c (finaladd): But don't free variables we no longer allocate.
+
+2003-07-25 Derek Price <address@hidden>
+
+ * checkin.c (Checkin): The rcs argument is unecessary since we know
+ that the parsed RCS data always exists as part of finfo by the time
+ this function gets called.
+ * commit.c (commit_fileproc, finaladd): Use new Checkin() API.
+ * cvs.h (Checkin): Update prototype.
+
+2003-07-25 Derek Price <address@hidden>
+
+ * subr.c (strip_trailing_newlines): Check len b4 str[len] to avoid
+ exceeding the array bounds when the string length == 0.
+ (Report from John Tytgat <address@hidden>.)
+
+2003-07-25 Derek Price <address@hidden>
+
+ * subr.c (strip_trailing_newlines): Generalize this function to watch
+ len so that it cannot walk past the beginning of the string passed in.
+ (Report from John Tytgat <address@hidden>.)
+
+2003-07-25 Derek Price <address@hidden>
+
+ * subr.c (strip_trailing_newlines): Leave the K&R function decl on this
+ branch.
+
+2003-07-25 Derek Price <address@hidden>
+
+ * cvs.h (strip_trailing_newlines): Update prototype.
+ * subr.c (strip_trailing_newlines): Return true when newlines are
+ removed.
+ * server.c (pserver_authenticate_connection): Don't give a DOS attack a
+ chance to authenticate accidentally because I like to be paranoid.
+ * sanity.sh (pserver): New test for same.
+
+2003-07-24 Mark D. Baushke <address@hidden>
+
+ * server.c (check_system_password): Cleanup pam_* return code
+ checking. (Original patch from Brian Murphy <address@hidden>.)
+
+2003-07-24 Derek Price <address@hidden>
+
+ * main.c: But the GNULIB gethostname accepts an int not ssize_t.
+
+2003-07-24 Derek Price <address@hidden>
+
+ * main.c: Don't declare gethostname when we already have it to avoid
+ decl conflicts.
+
+2003-07-24 Derek Price <address@hidden>
+
+ * server.c (server_directory): Add a TRACE for OS X debugging.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * client.c: Avoid some warning from gcc -Wall.
+ * lock.c: Ditto.
+ * login.c: Ditto.
+ * modules.c: Ditto.
+ * server.c: Ditto.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * filesubr.c (isaccessible): Correct some double const warnings from
+ protoize.
+ * login.c (password_entry_parseline): Ditto.
+ * server.c (kserver_authenticate_connection): Remove a multi-line
+ string along with the warning from GCC.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * *.{c,h}: Back out the indent run.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * cvs.h: Move some includes into lib/system.h.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * *.{c,h}: Run these through GNU indent as per the NEWS file to fix
+ some of the long function decls which came out of protoize.
+
+2003-07-23 Derek Price <address@hidden>
+
+ * *.c: Run these through GCC's protoize to convert the pre-ANSI C
+ function decls to C89 compliance.
+
+2003-07-22 Derek Price <address@hidden>
+
+ * cvs.h: Remove support for the PTR macro, since we can assume void *
+ under C89. It also was not being made use of in very many places so
+ even most K&R compilers must have supported it, or nobody was using
+ K&R compilers. We can also assume <stdarg.h> under C89, but move the
+ include...
+ * error.c: ...here, de-macro VA_START, and...
+ * subr.c: ...put a copy here too, as well as de-macroing VA_START.
+ * history.c: s/PTR /void */g;
+ * modules.c: Ditto.
+
+2003-07-22 Derek Price <address@hidden>
+
+ * cvs.h: Include GNULIB exit.h.
+
+2003-07-20 Derek Price <address@hidden>
+
+ * server.c: Add PAM support.
+ (cvs_pam_userinfo): New data type for PAM conversations.
+ (cvs_pam_conv): New function.
+ (check_password): Add PAM support.
+ (Original patch from Brian Murphy <address@hidden>.)
+
+2003-07-20 Derek Price <address@hidden>
+
+ * wrapper.c: Remove mention of obsolete -f and -t wrapper options from
+ a comment.
+
+2003-07-18 Derek Price <address@hidden>
+
+ * sanity.sh (release): Add new tests for release with unrecognized
+ recognized directories.
+
+2003-07-18 Derek Price <address@hidden>
+
+ * vers_ts (Version_TS): Don't allow command line keyword expansion
+ modes to override binary mode.
+ * sanity.sh (): Tests for the above.
+ (Original patch from Dieter Maurer <address@hidden>.)
+
+2003-07-16 Derek Price <address@hidden>
+
+ * add.c: s/PROTO//.
+ * admin.c: Ditto.
+ * annotate.c: Ditto.
+ * buffer.c: Ditto.
+ * buffer.h: Ditto.
+ * checkout.c: Ditto.
+ * classify.c: Ditto.
+ * client.c: Ditto.
+ * client.h: Ditto.
+ * commit.c: Ditto.
+ * cvs.h: Ditto.
+ * diff.c: Ditto.
+ * edit.c: Ditto.
+ * edit.h: Ditto.
+ * entries.c: Ditto.
+ * error.c: Ditto.
+ * error.h: Ditto.
+ * expand_path.c: Ditto.
+ * fileattr.c: Ditto.
+ * fileattr.h: Ditto.
+ * filesubr.c: Ditto.
+ * find_names.c: Ditto.
+ * gssapi-client.c: Ditto.
+ * gssapi-client.h: Ditto.
+ * hardlink.h: Ditto.
+ * hash.c: Ditto.
+ * hash.h: Ditto.
+ * history.c: Ditto.
+ * import.c: Ditto.
+ * kerberos4-client.h: Ditto.
+ * lock.c: Ditto.
+ * log-buffer.c: Ditto.
+ * log-buffer.h: Ditto.
+ * log.c: Ditto.
+ * login.c: Ditto.
+ * logmsg.c: Ditto.
+ * mkmodules.c: Ditto.
+ * modules.c: Ditto.
+ * myndbm.c: Ditto.
+ * myndbm.h: Ditto.
+ * patch.c: Ditto.
+ * rcs.c: Ditto.
+ * rcs.h: Ditto.
+ * rcscmds.c: Ditto.
+ * recurse.c: Ditto.
+ * release.c: Ditto.
+ * remove.c: Ditto.
+ * root.c: Ditto.
+ * rsh-client.h: Ditto.
+ * run.c: Ditto.
+ * server.c: Ditto.
+ * server.h: Ditto.
+ * socket-client.c: Ditto.
+ * socket-client.h: Ditto.
+ * status.c: Ditto.
+ * subr.c: Ditto.
+ * tag.c: Ditto.
+ * update.c: Ditto.
+ * update.h: Ditto.
+ * vers_ts.c: Ditto.
+ * watch.c: Ditto.
+ * watch.h: Ditto.
+ * wrapper.c: Ditto.
+ * zlib.c: Ditto.
+
+2003-07-16 Derek Price <address@hidden>
+
+ * cvs.h: Include pathmax.h.
+
+2003-07-16 Derek Price <address@hidden>
+
+ * myndbm.c: Use the GNU getdelim function rather than our package
+ getstr.
+ * server.c: Use the (hopefully) GNULIB and more appropriately named
+ getnline function rather than our getline_safe function.
+
+2003-07-12 Larry Jones <address@hidden>
+
+ * sanity.sh (diffnl): New tests for diff on files with no newline
+ at end.
+ (Patch from Andrew Moise <address@hidden>.)
+
+2003-07-11 Larry Jones <address@hidden>
+
+ * Makefile.am (cvs_DEPENDENCIES): Include the libraries.
+ * Makefile.in: Regenerated.
+
+ * diff.c (diff_file_nodiff): Fix -Wall complaints.
+ * log.c (rlog_proc): Ditto.
+ * rcs.c (RCS_setlocalid): Ditto.
+ * recurse.c (start_recursion): Handle null repository_in in TRACE.
+
+2003-07-09 Larry Jones <address@hidden>
+
+ * sanity.sh: Use ${CPROG} instead of ${PROG} so that changes merged
+ from cvs1-11-x-branch without updating won't appear to work.
+
+ * sanity.sh (keywordexpand): Use ${SPROG} instead of ${PROG} as
+ required.
+
+ * add.c (add): Update "re-adding" message to have quotes around
+ the file name like all the other similar messages.
+ * sanity.sh: Update to match.
+
+ * update.c (join_file): Handle locally removed but not yet committed
+ files.
+ (Reported by Larry Lords <address@hidden>.)
+ * sanity.sh (join, join4): New tests for above.
+
+2003-06-28 Larry Jones <address@hidden>
+
+ * commit.c (fixaddfile): Bail out if locate_rcs() fails. Make
+ parameters const.
+
+ * add.c (add): Fix -Wall complaints.
+ * diff.c (diff_file_nodiff): Ditto.
+ * filesubr.c (cvs_casecmp): Ditto.
+ * patch.c (patch_fileproc): Ditto.
+ * rcs.c (RCS_cmp_file): Ditto.
+ * root.c (parse_cvsroot): Ditto.
+ * subr.c (locate_file_in_dir): Ditto.
+ * cvs.h (cvs_casecmp, locate_file_in_dir): Update prototypes.
+
+2003-06-27 Larry Jones <address@hidden>
+
+ * lock.c (readers_exist): Use LockDir rather than always looking
+ in the repository.
+ (Original patch from Robert Ambalu <address@hidden>.)
+ Remove vestigial lock promotion code.
+
+2003-06-27 Derek Price <address@hidden>
+
+ * checkout.c (safe_location): Don't try and print from a NULL pointer.
+ (Report and original patch from Sampo Kellomaki <address@hidden>.)
+
+2003-06-26 Larry Jones <address@hidden>
+
+ * hash.c (sortlist): Avoid crash when list is null.
+
+2003-06-23 Derek Price <address@hidden>
+
+ * patch.c (patch_fileproc): Output revision number of the original
+ revision in the removed case.
+ (Idea from Paul Edwards <address@hidden>.)
+
+ * sanity.sh (rdiff-add-remove-nodiff): Rename to...
+ (rdiff-short): ...this. Test for the above changes. Add some tests
+ for when rev2 defaults to the trunk. Expand comments.
+
+2003-06-23 Derek Price <address@hidden>
+
+ * client.c: Reapply Alexey's changes to client.c from three commits
+ back they were left out of the diff.
+
+2003-06-23 Derek Price <address@hidden>
+
+ * add.c (add): Fix xmalloc's strlen() of wrong variable.
+ * checkout.c (safe_location): leak: reused where_location without free.
+ * log.c (rlog_proc): leak: free where before exit.
+ * logmsg.c (do_verify): leak: free verifymsg_script before exit.
+ (Original patch from Kenneth Lorber <address@hidden>.)
+
+2003-06-23 Derek Price <address@hidden>
+
+ * client.c: Remove silly comment.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-06-23 Derek Price <address@hidden>
+
+ * kerberos4-client.h, kerberos4-client.c, client.c: Rename
+ start_tcp_server() to start_kerberos4_server().
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-06-20 Derek Price <address@hidden>
+
+ * kerberos-client.c, kerberos-client.h, client.c: Split out
+ Kerberos 4 code to separate files.
+
+ * Makefile.am: Mention new files.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-06-16 Derek Price <address@hidden>
+
+ * cvs.h: Comment an #endif.
+
+2003-06-13 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_name): Remove portability cruft obsoleted by the
+ import of GNULIB's mkstemp().
+
+2003-06-13 Derek Price <address@hidden>
+
+ * subr.c (file_has_conflict): Fix comment.
+ (Patch from Paul Edwards <address@hidden>.)
+
+2003-06-13 Derek Price <address@hidden>
+
+ * subr.c (xrealloc): Trivial comment fix.
+ (Patch from Kenneth Lorber <address@hidden>.)
+
+2003-06-13 Derek Price <address@hidden>
+
+ * diff.c (diff_fileproc): Fix memory leak.
+ (Patch from Kenneth Lorber <address@hidden>.)
+
+2003-06-12 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot, local_cvsroot): Parse trailing '/'s off the
+ end of cvsroots. Make arguments const.
+ * cvs.h: Update prototypes to match.
+ (Idea from Miles Zarathustra <address@hidden>.)
+
+2003-06-12 Derek Price <address@hidden>
+
+ * checkout.c (safe_location): Fix memory leak.
+ (Patch from Kenneth Lorber <address@hidden>.)
+
+2003-06-11 Larry Jones <address@hidden>
+
+ * filesubr.c (xresolvepath): Fix memory leak.
+ (Original patch from Kenneth Lorber <address@hidden>.)
+
+2003-06-11 Derek Price <address@hidden>
+
+ * commit.c: Change Parse_Info calling convention to include void *
+ suggested in HACKING file and generalize all argument to opt.
+ * cvs.h: update defs for Parse_Info and its callproc.
+ * edit.c: Change Parse_Info calls for new calling convention.
+ * logmsg.c: Ditto.
+ * parseinfo.c: Change Parse_Info for new calling convention.
+ * server.c: Change Parse_Info calls for new calling convention.
+ * tag.c: Ditto.
+ (Original patch from Ken Lorber <address@hidden>.)
+
+2003-06-11 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerate for new configure.in.
+
+2003-06-11 Larry Jones <address@hidden>
+
+ * sanity.sh: Change warning messages to note that defective tools
+ can result in defective results, both pass and fail. Also change
+ "which" to "that" for errant grammar pedants.
+
+2003-06-10 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Avoid unneeded allocation.
+
+2003-06-10 Mark D. Baushke <address@hidden>
+
+ * rcs.c (RCS_setlocalid,RCS_setincexc): New functions to support
+ LocalKeyword and KeywordExpand config keywords.
+
+ * rcs.h (RCS_setlocalid,RCS_setincexc): New prototypes.
+
+ * parseinfo.c (parse_config): Added LocalKeyword
+ and KeywordExpand keywords.
+
+ * sanity.sh (keywordexpand): New CVSROOT/config tests for
+ LocalKeyword and KeywordExpand options.
+
+2003-06-09 Derek Price <address@hidden>
+
+ * rcs.c (RCS_delete_revs): Reference WOE32 rather than WIN32 in
+ accordance with the GNU convention to avoid implying that we consider
+ the Microsoft Windows Operating Environment any sort of "win".
+
+2003-06-09 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): Tidy a comment.
+
+2003-06-09 Derek Price <address@hidden>
+
+ * patch.c (patch_fileproc): Don't assume the content of files is
+ different just because the revision number is different.
+ * sanity.sh (rdiff-add-remove-nodiff): New tests for the above.
+ (Report & original patches from Paul Edwards <address@hidden>.)
+
+2003-06-04 Derek Price <address@hidden>
+
+ * cvs.h (locate_file_in_dir): New proto.
+ (locate_rcs): Move proto...
+ * rcs.h: ...here.
+ * filesubr.c (locate_rcs): Move function...
+ * rcs.c: ...here for Windows.
+ * filesubr.c (locate_file_in_dir): Move function...
+ * subr.c: ...here for Windows.
+
+2003-06-02 Derek Price <address@hidden>
+
+ * sanity.sh: Add comments re portability of test -x & test -e. Don't
+ bother with quotes in arguments to test when we have laready checked
+ the variables for empty content.
+
+2003-06-02 Derek Price <address@hidden>
+
+ * sanity.sh: Don't use `test -x' since BSD 4.3 doesn't like it. Minor
+ reorganization for clarity. Don't check for $server = false after we
+ set its default. Use </dev/null with calls to $prog --version when we
+ don't know what $prog does for sure.
+
+2003-06-02 Derek Price <address@hidden>
+
+ * diff.c (diff_file_nodiff): Don't assume that because two specified
+ revision numbers are different, the contents are different.
+ (Original report & patch from Paul Edwards <address@hidden>.)
+
+ * diff.c (diff_file_nodiff): Pass through rev1_cache to be filled in
+ by RCS_cmp_file when it needs to check out revision 1 into a file. Add
+ some more informative error messages. Cleanup for efficiency &
+ readability.
+ (diff_fileproc): Pass the cached revision to RCS_exec_diff(). Clean up
+ the error exit code. Remove code killed by the changes to
+ diff_file_nodiff().
+ * rcscmds.c (RCS_exec_rcsdiff): Accept and use new cached revision text
+ if present.
+ * rcs.c (RCS_cmp_file): Accept a second revision number and cache the
+ first revision if it needs to be checked out.
+
+ * checkin.c (Checkin): Use new RCS_cmp_file().
+ * import.c (update_rcs_file): Ditto.
+ * no_diff.c (No_Difference): Ditto.
+
+ * cvs.h (RCS_exec_rcsdiff): New proto to match above changes.
+ * rcs.h (RCS_cmp_file): Ditto.
+
+ * sanity.sh: Minor corrections to handle the above changes.
+
+2003-05-31 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Refuse :fork: only in client mode, not
+ server.
+ * client.c (connect_to_forked_server): Die without SERVER_SUPPORT when
+ CVSSERVER isn't supplied in the environment.
+ * sanity.sh: Default $servercvs to $testcvs. Add SPROG so that testing
+ a client and server with different names works in order to test the
+ above changes. s/PROG/SPROG/ almost everywhere. Misc corrections to
+ tests when ${PROG} is required not to use [a-z]*. Misc uniqifications
+ of test names. Misc replacement of CVS_SERVER=${testcvs} with
+ CVS_SERVER=${servercvs}. Confirm ${testcvs} & ${servercvs} exist and
+ are executable. Set testcvs_server_support=true if the ${testcvs}
+ executable has server support. Misc comment corrections.
+ (pserver): s/\$\{testcvs\}/${servercvs}/ for invocations of pserver.
+ (server): Ditto for invocations of `cvs server'.
+ (fork): Accept the `was not compiled with server support' error
+ message.
+
+2003-05-29 Derek Price <address@hidden>
+
+ * client.c (start_server): Don't send -l to server.
+ * history.c (history_write): Fix comment.
+ * main.c (main): Don't process -l.
+ * server.c (serve_global_option): Ditto.
+ (Suggestion from Rob Lanphier <address@hidden>.)
+
+2003-05-29 Derek Price <address@hidden>
+
+ * log-buffer.c, rsh-client.c, socket-client.c: Allow compilation
+ with --disable-client.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-29 Derek Price <address@hidden>
+
+ * gssapi-client.h: Move contents of lib/xgssapi.h here.
+ * server.c: xgssapi.h is no more.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-28 Derek Price <address@hidden>
+
+ * server.c: Use standard PROTOTYPES symbol instead of non-standard
+ USE_PROTOTYPES.
+ * error.h, cvs.h: Use PROTO.h.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-23 Larry Jones <address@hidden>
+
+ * sanity.sh (info-cleanup-verifymsg): Avoid race in output.
+
+ * sanity.sh (template): Fix unintended duplicate DEFAULT lines,
+ duplicate test names.
+
+2003-05-22 Larry Jones <address@hidden>
+
+ * commit.c (commit): Fix leading zero stripping code to not strip
+ unless there's a following digit.
+
+ * parseinfo.c (Parse_Info): Warn if multiple DEFAULT lines found.
+ * sanity.sh (info): New test for above.
+
+2003-05-21 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerate with Automake version 1.7.5.
+
+2003-05-20 Larry Jones <address@hidden>
+
+ * parseinfo.c (Parse_Info): Fix stupid memory management error.
+
+ * logmsg.c (do_verify): Treate Parse_Info errors as failure.
+ * parseinfo.c (Parse_Info): Don't call expand_path until executing
+ the command so that errors in unexecuted commands aren't reported.
+ * sanity.sh (info): New tests for above.
+
+2003-05-20 Derek Price <address@hidden>
+
+ * mkmodules.c (config_contents): Add missing newline.
+ (Patch from Kenneth Lorber <address@hidden>.)
+
+2003-05-20 Derek Price <address@hidden>
+
+ * Makefile.am: Macro subsitution for zlib include path and library
+ location
+ * zlib.c: #ifdef inclusion of <zlib.h> versus "zlib.h"
+ (Original patch from Anthon Pang <address@hidden>.)
+
+ * Makefile.in: Regenerated
+
+2003-05-20 Derek Price <address@hidden>
+
+ * cvs.h: Move the standard includes into lib/system.h.
+ * subr.c: s/malloc/CVS_MALLOC/;s/realloc/CVS_REALLOC/.
+
+2003-05-19 Derek Price <address@hidden>
+
+ * filesubr.c: s/\bstat\b/CVS_STAT/g;s/\blstat\b/CVS_LSTAT/g
+ * hardlink.c: Ditto.
+ * ignore.c: Ditto.
+ * rcs.c: Ditto.
+ * update.c: Ditto.
+
+2003-05-18 Larry Jones <address@hidden>
+
+ * checkout.c (safe_location): Remove unused variable.
+ * hash.c (walklist, printnode, printlist): Use %p to print pointers
+ if available, convert to unsigned long if not.
+ * recurse.c (start_recursion, do_recursion): Ditto.
+ * tag.c (rtag_proc, tag_check_valid): Ditto.
+
+2003-05-18 Mark D. Baushke <address@hidden>
+
+ * Makefile.am (localcheck,remotecheck): Use cvs$(EXEEXT) not cvs.
+ * Makefile.in: Regenerated.
+ * sanity.sh (status-init-7): Use ${PROG} not cvs in tests.
+ (branch-after-import-5): Ditto.
+ (keywordname-update-11): Ditto.
+
+2003-05-18 Larry Jones <address@hidden>
+
+ * server.h (kserver_authenticate_connection,
+ pserver_authenticate_connection): Add prototypes.
+
+ * client.c (update_entries): Set file's access time to the current
+ time rather than the same as the modification time.
+ * vers_ts.c (Version_TS): Ditto.
+
+2003-05-09 Derek Price <address@hidden>
+
+ * client.c: #ifdef inclusion of gssapi-client.h.
+ * server.c: Ditto.
+ (Reported by Boyd Lynn Gerber <address@hidden>.)
+
+2003-05-09 Derek Price <address@hidden>
+
+ * client.h: Move some of the GSSAPI stuff...
+ * gssapi-client.h: ...to this new file.
+ * client.c (start_server): Use new initialize_gssapi_buffers().
+ (*): Move most of the GSSAPI stuff from here and...
+ * server.c (*): ...here...
+ * gssapi-client.c: ...to this new file.
+ * Makefile.am (EXTRA_cvs_SOURCES, cvs_DEPENDENCIES, cvs_LDADD): Support
+ $(cvs_client_objects).
+ (Original patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-05-09 Derek Price <address@hidden>
+
+ * buffer.c: Reindent some compiler directives in order to make nesting
+ clearer.
+
+2003-05-08 Derek Price <address@hidden>
+
+ * client.c (log_buffer*): Move...
+ * log-buffer.c (log_buffer*): ...to this new file.
+ * log-buffer.h (setup_logfiles): New file to share prototype.
+ * Makefile.am: Add log-buffer.c & log-buffer.h.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-05-08 Derek Price <address@hidden>
+
+ * client.c (init_sockaddr): Move...
+ * socket-client.c (init_sockaddr): ...here.
+ * socket-client.h (init_sockaddr): Prototype.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-08 Derek Price <address@hidden>
+
+ * client.c (send_to_server): Move most of the functionality to and
+ wrap...
+ (send_to_server_via): ...this new function which accepts the buffer
+ pointer as an argument.
+ (read_line): Ditto, but to...
+ (read_line_via): ...here.
+ (auth_server): Rename lto_server & lfrom_server to s/^l//. Remove
+ ugly code which sets the global versions of these variables
+ temporarily for function calls.
+ s/send_to_server(/send_to_server_via(to_server,/g,
+ s/read_line(/read_line(from_server,/g,
+ Remove emotional FIXME comment to the effect that all this is
+ necessary.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-07 Derek Price <address@hidden>
+
+ * client.c (from_server, to_server): Rename these global buffer
+ pointers to...
+ (global_from_server, global_to_server): ...this in order to avoid
+ confusion.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-05-07 Derek Price <address@hidden>
+
+ * client.c (make_bufs_from_fds, connect_to_forked_server,
+ start_tcp_server): Rename struct buffer ** arguments to s/$/_p/ in an
+ attempt to denote their pointerness more clearly.
+ * rsh-client.c (start_rsh_client): Ditto.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+ * client.h (make_bufs_from_fds): Sanitize prototype so that the
+ argument name change doesn't clash.
+
+2003-05-07 Derek Price <address@hidden>
+
+ * client.h (make_bufs_from_fd): Prototype in order to make available to
+ rsh-client.c.
+ * client.c (start_rsh_server): Moved most of the RSH (:ext:) client
+ stuff to...
+ * rsh-client.h: ...here...
+ * rsh-client.c: ...and here.
+ * Makefile.am (cvs_SOURCES): Add new source files.
+ (Original patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-05-06 Derek Price <address@hidden>
+
+ * client.c (socket_buffer_*): Moved most of the socket stuff to...
+ * socket-client.h: ...here...
+ * socket-client.c: ...and here.
+ * Makefile.am (cvs_SOURCES): Add new source files.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-05-05 Derek Price <address@hidden>
+
+ * hash.c (findnode): Document behavior of this function when its list
+ argument is NULL and document this behavior. Remove FIXME comment to
+ the effect that this is necessary.
+
+2003-05-01 Derek Price <address@hidden>
+
+ * main.c (main): Ignore -z when CLIENT_SUPPORT is not defined.
+ (Report from Jim Salter <address@hidden>.)
+
+2003-05-01 Derek Price <address@hidden>
+
+ * repos.c (Sanitize_Repository_Name): Remove some old comments about
+ the defunct RELATIVE_REPOS macro.
+ * server.c (outside_root): Ditto.
+
+2003-04-30 Derek Price <address@hidden>
+
+ * vers_ts.c (Version_TS): Minor optimization.
+
+2003-04-30 Derek Price <address@hidden>
+
+ * add.c (add): Fix a possible, if unlikely, memory out of bounds error.
+
+2003-04-30 Derek Price <address@hidden>
+
+ * commit.c: Free vers in single place
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2003-04-30 Derek Price <address@hidden>
+
+ * Makefile.am: Get rid of $includeopt, using $CPPFLAGS as intended by
+ the Autoconf folk.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.in: Regenerated.
+
+2003-04-30 Derek Price <address@hidden>
+
+ * add.c (add): Fix a possible, if unlikely, memory out of bounds error.
+
+2003-04-28 Derek Price <address@hidden>
+
+ * client.c (save_prog): Remove unneeded struct.
+ (checkin_progs, update_progs): Remove these unneeded globals.
+ (handle_set_checkin_prog, handle_set_update_prog, do_deferred_progs):
+ Remove these functions.
+ (send_repository): Remove checkin and update prog support.
+ (responses): Remove Set-checkin-prog and Set-update-prog.
+ (get_responses_and_close): Don't call do_deferred_prog().
+ * commit.c (commit_usage): Remove reference to -n.
+ (commit): Don't set and send run_module_prog via -n. Don't run
+ Checkin.prog or Checkout.prog in local mode.
+ * modules.c (CVSMODULE_OPTS): Remove -i and -u.
+ (do_module): Don't process -i and -u options to set checkin and update
+ progs, respectively.
+ * server.c (server_prog, serve_checkin_prog, server_update_prog):
+ Remove unused functions.
+ (requests): Remove Checkin-prog and Update-prog.
+ * update.c (update_dirleave_proc): Remove update prog functionality.
+
+ * cvs.h (CVSADM_CIPROG, CVSADM_UPROG): Remove unneeded defines.
+ * server.h (server_prog): Remove proto.
+ (progs): Remove enum.
+
+ * sanity.sh (modules5): Remove tests for checkin and update programs.
+
+2003-04-15 Derek Price <address@hidden>
+
+ * sanity.sh (*): Shrink the yucky case statement using sed's transform
+ functionality.
+ (getlongoptarg): Convert this function to only confirm the arg and move
+ it...
+ (checklongoptarg): ...here.
+
+2003-04-10 Larry Jones <address@hidden>
+
+ * Makefile.in: Regenerated.
+
+2003-04-04 Larry Jones <address@hidden>
+
+ * sanity.sh (branches4-15): New test.
+
+ * error.h: Avoid __pure__ for GCC versions < 2.96 & __malloc__ for GCC
+ versions < 3.0.
+
+2003-04-03 Derek Price <address@hidden>
+
+ * Makefile.am (DISTCLEAN_FILES): Move the contents of this variable...
+ (distclean-local): ...to this target now that Automake supports it.
+
+2003-04-03 Derek Price <address@hidden>
+
+ * cvs.h: Avoid __pure__ for GCC versions < 2.96 & __malloc__ for GCC
+ versions < 3.0.
+
+2003-04-02 Larry Jones <address@hidden>
+
+ * update.c (update, update_fileproc, update_filesdone_proc,
+ update_dirent_proc, update_dirleave_proc): Keep track of whether
+ a tag is both a revision tag and a branch tag and warn the user.
+ * sanity.sh (branches4): New tests for above.
+
+2003-04-02 Derek Price <address@hidden>
+
+ * recurse.c (do_recursion): Use strstr("/./") rather than strchr('.')
+ to catch only indirections in paths and not directory names with dots
+ in them.
+ (Report from Pavel Roskin <address@hidden>.)
+
+ * sanity.sh (multiroot): Put a dot in the CVSROOT_DIRNAMEs.
+ (dottedroot): New test.
+ (Based on a script from Pavel Roskin <address@hidden>.)
+
+2003-04-01 Derek Price <address@hidden>
+
+ * sanity.sh (multiroot2-9): Add newly TRACEd parse_cvsroot() to
+ expected output.
+
+2003-03-31 Derek Price <address@hidden>
+
+ * rcs.c (freercsnode): Revert an accidental change from the previous
+ commit.
+
+2003-03-31 Derek Price <address@hidden>
+
+ * recurse.c (start_recursion): Accept new repository argument so that
+ the working directory may be tracked by do_recursion without using
+ xgetwd(), which returned a value different from the one the user
+ requested when symlinks were in use. Add TRACE. Pass repository_in
+ to do_recursion() as part of the recursion frame.
+ (do_recursion): Default srepository to NULL and only set when we set
+ repository. Keep track of repository using xframe.repository for the
+ r* commands rather than xgetwd(), which used to break when CVSROOT was
+ a symlink to a real root.
+
+ * cvs.h (xreadlink): #ifdef HAVE_READLINK proto.
+ (xresolvepath): New proto.
+ (start_recursion): Add repository to proto.
+ (*): Define some more abstract TRACE levels.
+ * update.h (do_update): Add repository argument to proto.
+
+ * checkout.c (safe_location): Add more complete header comment. Add
+ TRACE. Use new xresolvepath() function. Always return true in
+ client mode since checking our destination path against the CVSROOT
+ path is usually meaningless in client/server mode.
+ (checkout_proc): Pass repository to do_update() for later use with
+ start_recursion().
+ (*): s/<chdir>/CVS_CHDIR/.
+ * filesubr.c (xreadlink): #ifdef HAVE_READLINK this function. Add more
+ complete header comment.
+ (xresolvepath): New function.
+ * hash.c (walklist): Add TRACE.
+ * main.c (main): Don't copy and dispose of CVSRoot_cmdline twice.
+ * patch.c (patch_proc): Add TRACE. Pass repository to
+ tag_check_valid() for r* commands.
+ * root.c (parse_cvsroot): Add TRACE.
+ * tag.c (rtag_proc): Add TRACE.
+ (check_fileproc): Ditto.
+ (tag_check_valid): Ditto.
+ * update.c (do_update): Accept new repository argument for co.
+ (update): Pass NULL repository to do_update().
+
+ * admin.c (*): Use new definition of start_recursion().
+ * annotate.c (*): Ditto.
+ * client.c (*): Ditto.
+ * commit.c (*): Ditto.
+ * diff.c (*): Ditto.
+ * edit.c (*): Ditto.
+ * lock.c (*): Ditto.
+ * log.c (*): Ditto.
+ * patch.c (*): Ditto.
+ * remove.c (*): Ditto.
+ * status.c (*): Ditto.
+ * tag.c (*): Ditto.
+ * update.c (*): Ditto.
+ * watch.c (*): Ditto.
+
+ * sanity.sh: Add new -l option to test symlinked roots.
+ (abspath-3.2): Use [a-z]* rather than "checkout".
+ (check_repository-1): Add server error messages about absolute paths
+ since the client now skips destination validity checks.
+ (check_repository-2): Process client error messages about CVSROOT
+ files being in the way since the client skips destination validity
+ checks since it should be rare that a client is running in
+ client/server mode on the server and CVS has no current way to check if
+ it is runnning on the server.
+
+2003-03-27 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (rdiff2): Add new test case for SEGV problem reported
+ against cvs 1.11.5.
+ (Report from James Cribb)
+
+2003-03-26 Derek Price <address@hidden>
+
+ * client.c: Fix, reorganize, and comment ifdefs for AUTH_CLIENT_SUPPORT
+ and HAVE_GSSAPI.
+ * client.h: Ditto. Remove some unecessary server function prototypes.
+
+2003-03-26 Derek Price <address@hidden>
+
+ * client.c: Include the net headers for HAVE_GSSAPI.
+ (Report from Jim Salter <address@hidden>.)
+
+2003-03-26 Derek Price <address@hidden>
+
+ * main.c (main): Verify the argument to -z when running without
+ CLIENT_SUPPORT since Eric Siegerman complained about being bit
+ by a run of `cvs -z -n up' which parsed the -n as the argument to
+ -z.
+ * sanity.sh (opterrmsg): New tests for -z argument checking.
+
+2003-03-26 Larry Jones <address@hidden>
+
+ * main.c (main): Use strtol() instead of atoi() when parsing -z
+ to detect errors.
+ (Reported by Eric Siegerman <address@hidden>.)
+
+2003-03-25 Derek Price <address@hidden>
+
+ * cvs.h: Disable GNU attributes as part of PROTO behavior.
+ * error.h: Mirror GNU attribute definitions from cvs.h.
+
+2003-03-25 Derek Price <address@hidden>
+
+ * subr.c (cvs_trace): #ifdef use of server_active.
+ (Report from Jim Salter <address@hidden>.)
+
+ * cvs.h (xmalloc, xrealloc, xstrdup, parse_cvsroot,
+ local_cvsroot, normalize_cvsroot): Use GNU attributes.
+ * error.h (error_exit): Fix PROTO/__attribute__ specification.
+ * root.c (new_cvsroot_t): Add GNU attribute.
+
+2003-03-24 Derek Price <address@hidden>
+
+ * Makefile.am: Update copyright notice.
+
+ * Makefile.in: Regenerated.
+
+2003-03-24 Larry Jones <address@hidden>
+
+ * server.h (server_clear_template): Add declaration.
+ (server_template): Add parameter names to prototype.
+
+2003-03-20 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (env): Try more than one ps command if the first one
+ fails. This may let the test succeed on more platforms. Also,
+ keep the ps output that was processed for the error report.
+
+2003-03-19 Mark D. Baushke <address@hidden>
+
+ * sanity.sh (env): Use 'ps -el' rather than 'ps -l' so that
+ crontab jobs that might be running these tests without a
+ controlling terminal work properly.
+
+ * client.c (start_rsh_server): Use new definition of RSH_DFLT to
+ allow "rsh" to be configured to default to "ssh" or some other
+ local remote transport program.
+
+ * Makefile.in: Regenerated.
+
+2003-03-19 Larry Jones <address@hidden>
+
+ * filesubr.c (mkdir_if_needed): Save errno since isdir() can clobber.
+ (Patch from Brian Poole <address@hidden>.)
+ * sanity.sh (abspath-4): Update to match.
+
+ * filesubr.c (locate_rcs): Fix gcc warning.
+
+2003-03-19 Derek Price <address@hidden>
+
+ * add.c (add_directory): Only call WriteTemplate when the server is
+ active.
+ * create_adm.c (Create_Admin): Don't call WriteTemplate here since
+ Create_Admin is only called from the client.
+ * commit.c (commit_dirleaveproc): Don't call WriteTemplate here. I'm
+ a little confused as to why since update_direntproc works for update.c,
+ but I can't come up with a test case that fails when this call is
+ missing. Nor can I come up with a test case that passes when this call
+ is present and the one in commit_filesdoneproc is removed.
+ * sanity.sh (template): Tidy, minimize, and add some extra tests.
+
+2003-03-19 Mark D. Baushke <address@hidden>
+
+ * .cvsignore: Added sanity.config.sh, a new auto-generated file.
+
+ * cvs.h (CVS_PID_ENV): New environment variable CVS_PID has the
+ pid of the parent cvs process.
+ * main.c (main): Initialize it.
+ * sanity.sh (env): Test it.
+
+2003-03-19 Derek Price <address@hidden>
+
+ * sanity.config.sh.in: New file.
+ * sanity.sh: Source new config file when available. Accept alternate
+ config file as an argument to a -c option. Accept long options with
+ arguments.
+ (getlongoptarg): New function.
+
+ * Makefile.in: Regenerated.
+
+2003-03-18 Derek Price <address@hidden>
+
+ * root.c (parse_root): Add some more comments and expand
+ #ifdef CLIENT_SUPPORT pragmas. Rearrange sanity checks slightly.
+
+2003-03-18 Derek Price <address@hidden>
+
+ * main.c (main): Output -R warning in quiet mode and correct spelling
+ in this warning message.
+ * sanity.sh (commit-readonlyfs-2r4): Correct cascaded spelling mistake
+ in test.
+
+2003-03-17 Derek Price <address@hidden>
+
+ * add.c: Correct comment.
+ * client.c: Ditto.
+ * checkin.c (Checkin): Pass work file name to RCS_checkin so that this
+ function works properly in the case insensitive mode.
+ * commit.c (checkaddfile): Fix and factor add logic so that the
+ correct files and directories are created in the case insensitive mode.
+ Reuse code in RCS_parse() below. This avoids a problem that could
+ cause corrupted RCS files to be created on an add from a case
+ insensitive system. Corrupted RCS files could cause later assertion
+ failures for everyone.
+ (locate_rcs): Move this function...
+ * filesubr.c (locate_rcs): ...here and rewrite it.
+ (fopen_case): Remove this function.
+ (locate_file_in_dir): New function.
+ * cvs.h (locate_rcs): Prototype new function.
+ * rcs.c (RCS_parse): Factor out file location into locate_rcs.
+
+2003-03-17 Mark D. Baushke <address@hidden>
+
+ * main.c (main): Issue a warning about readonlyfs options unless
+ quiet or really_quiet is set.
+ * lock.c (Writer_Lock): Modify readonlyfs error message text.
+ * sanity.sh (commit-readonlyfs): Adjust test for new messages.
+
+2003-03-17 Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Add syslog calls for setgid/setuid
+ failure.
+
+2003-03-16 Mark D. Baushke <address@hidden>
+
+ * cvs.h (CVSREADONLYFS_ENV): New macro to support new environment
+ variable "CVSREADONLYFS" for read-only file-system repository mode.
+ * lock.c (Reader_Lock, Writer_Lock): Add support for new read-only
+ file-system repository mode.
+ * main.c (main, opt_usage): Ditto.
+ * root.c (parse_cvsroot): Ditto.
+ * sanity.sh (commit-readonlyfs): Test new read-only file-system
+ repository mode.
+
+2003-03-14 Mark D. Baushke <address@hidden>
+
+ * server.c (template_proc): Fix broken Template protocol code.
+ Must call send buf_send_counted() for Template files to avoid
+ "Protocol error: uncounted data discarded" messages in some
+ circumstances.
+ * sanity.sh (template): Test case to verify fix this fix.
+
+2003-03-10 Mark D. Baushke <address@hidden>
+
+ * cvs.h (WriteTemplate): Add missing prototype.
+
+2003-03-07 Mark D Baushke <address@hidden>
+
+ * sanity.sh: Fix broken setting of the servercvs variable.
+
+2003-03-07 Derek Price <address@hidden>
+
+ * sanity.sh (help): Add explanation of CVS-TO-TEST and edit for
+ consistency.
+
+2003-03-07 Derek Price <address@hidden>
+
+ * sanity.sh (usage): Show users long --help rather than less
+ informative -h.
+
+2003-03-07 Derek Price <address@hidden>
+
+ * sanity.sh: Add support for long options.
+ (exit_usage): Move the actual generation of usage text to...
+ (usage): ...this new function and improve the usage message.
+ (exit_help): New function.
+
+2003-03-07 Mark D. Baushke <address@hidden>
+
+ * sanity.sh: Drop the clientcvs option. Add usage info for
+ the -s servercvs option.
+
+2003-03-07 Larry Jones <address@hidden>
+
+ * commit.c (check_fileproc): Remove unused variables.
+
+ * patch.c (patch): Pass local to do_module so that -l actually works.
+ (Reported by John Coers <address@hidden>.)
+ (patch_fileproc): Fix uninitialized variables.
+ * sanity.sh: Define a DATE pattern for rdiff and use it.
+ (basic2-24a): New test for above.
+
+2003-03-07 Mark D. Baushke <address@hidden>
+
+ * entries.c (WriteTemplate): New function to control updates to
+ the CVS/Template file or its removal.
+ * create_adm.c (Create_Admin): Use the new WriteTemplate function.
+ * add.c (add_directory): Add a WriteTemplate() call
+ when a new directory is added to the repository.
+ * commit.c (commit_filesdoneproc): Ensure that the CVS/Template is
+ updated at the end of a commit -- mostly to remove it if it is not
+ relevant.
+ (commit_dirleaveproc): Ensure that the CVS/Template gets updated
+ when the directory is left.
+ * update.c (update_dirent_proc): Update CVS/Template file.
+ * server.c (server_clear_template): New protocol response to
+ remove existing CVS/Template files.
+ * client.c (clear_template): New function to remove or truncate a
+ CVS/Template file.
+ (handle_clear_template): New function. Handle Clear-template
+ protocol response message.
+ (save_prog): Add new Clear-template response line.
+ * sanity.sh (template): Test the CVS/Template creation with
+ the remote protocol. Nothing gets created locally.
+ (multiroot2): Fix for minor changes to trace output.
+ (getopts): Allow tests to be run with specified client and server
+ cvs commands to allow for interoperatbility testing.
+ (check_keep): New shell function for --keep processing.
+
+2003-03-06 Derek Price <address@hidden>
+
+ * subr.c (file_has_conflict): New file.
+ * commit.c (check_fileproc): Factor code into new file_has_conflict()
+ function.
+ * update.c (update_fileproc): Ditto.
+ * status.c (status_fileproc): Use new file_has_conflict() function.
+ (Report from Bernd Kuemmerlen <address@hidden>.)
+
+ * sanity.sh (status): New test for same.
+
+2003-03-06 Larry Jones <address@hidden>
+
+ * sanity.sh (branches3-4): Set and export CVS_LOCAL_BRANCH_NUM
+ to be sure it really gets into the environment, then unset it
+ when finished (ala CVSWRAPPERS et al).
+
+2003-03-03 Mark D. Baushke <address@hidden>
+
+ * sanith.sh (branches3): Localize the setting of
+ the CVS_LOCAL_BRANCH_NUM environment variable.
+
+ * rcs.c (RCS_magicrev): CVS_LOCAL_BRANCH_NUM feature.
+ Port of the FreeBSD hack for setting the next magic branch number
+ to be used. The original patch was written by Peter Wemm
+ <address@hidden> and may be found by visiting the URL:
+
http://www.freebsd.org/cgi/cvsweb.cgi/src/contrib/cvs/src/rcs.c.diff?r1=1.1&r2=1.2
+ Implement a horrible (but simple) hack to allow some control over the
+ branch number that is assigned. This is specifically to support the
+ local commit feature of cvsup. If one sets $CVS_LOCAL_BRANCH_NUM to
+ (say) 1000 then branches the local repository, the revision numbers
+ will look like 1.66.1000.xx. This is almost a dead-set certainty that
+ there will be no conflicts with version numbers.
+ (This needs to be something more than an option to 'cvs tag' or 'cvs
+ rtag' as various parts of cvs "know" how to automatically branch files
+ (eg: cvs add). Trying to remember state is getting "Too Hard (TM)")
+ * sanity.sh (branches3): Test the CVS_LOCAL_BRANCH_NUM feature.
+
+2003-03-04 Derek Price <address@hidden>
+
+ * history.c (history_write): Remove unneeded O_CREAT in the call to
+ open() since we abort a few lines earlier if the file doesn't exist.
+ Add a comment to the effect that this is not the optimal method of
+ doing things and needs fixed.
+
+2003-02-28 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Set no_password for :gserver: and :kserver:
+ as tokens should already be obtained via external sources.
+ * update.c (update_fileproc): Remove redundant code.
+
+2003-02-28 Larry Jones <address@hidden>
+
+ * lock.c (set_lock): If possible, try a short wait with no message
+ before calling lock_wait() to optimize master lock contention.
+
+2003-02-26 Larry Jones <address@hidden>
+
+ * checkout.c (checkout): Send "--" before file names.
+ * sanity.sh (spacefiles): Remote now works just like local.
+
+2003-02-25 Derek Price <address@hidden>
+
+ * sanity.sh (rcs4): Use UTC to work across timezones.
+
+2003-02-25 Derek Price <address@hidden>
+
+ * rcs.c (RCS_getdate): Fix a bug that shows up when checking out
+ files by date with the "-D date" command line option. There is
+ code in the original to handle a special case. If the date search
+ finds revision 1.1 it is supposed to check whether revision
+ 1.1.1.1 has the same date stamp, which would indicate that the
+ file was originally brought in with "cvs import". In that case it
+ is supposed to return the vendor branch version 1.1.1.1.
+
+ However, there was a bug in the code. It actually compares the date
+ of revision 1.1 for equality with the date given on the command
+ line -- clearly wrong. This commit fixes the coding bug.
+
+ Note: There is an additional bug which is _not_ fixed in this
+ commit. The date comparison should not be a strict equality test.
+ It should allow a fudge factor of, say, 2-3 seconds. Old versions
+ of CVS created the two revisions with two separate invocations of
+ the RCS "ci" command. We have many old files in the tree in which
+ the dates of revisions 1.1 and 1.1.1.1 differ by 1 second.
+
+ This bug was discovered and fixed for FreeBSD cvs. See v 1.21 of
+ <http://www.freebsd.org/cgi/cvsweb.cgi/src/contrib/cvs/src/rcs.c.diff>
+ for more information.
+
+ * sanity.sh (rcs4): Tests for same.
+ (Patch from Mark D. Baushke <address@hidden>.)
+
+2003-02-25 Derek Price <address@hidden>
+
+ * logmsg.c (logfile_write): Do not pass a NULL pointer to
+ fprintf() when we have an empty log message.
+ * sanity.sh (editor): Add new tests to verify correct behavior of
+ empty log messages.
+ (Patch from Mark D. Baushke <address@hidden>, original report from
+ Piotr KUCHARSKI <address@hidden>.)
+
+2003-02-25 Derek Price <address@hidden>
+
+ * cvs.h (user_admin_options): Rename to...
+ (UserAdminOptions): ...this to match the convention set by
+ of RereadLogAfterVerify.
+ * admin.c (admin): Ditto.
+ * parseinfo.c (parse_config): Ditto.
+
+ * mkmodules.c (config_contents): Update with UserAdminOptions
+ information.
+
+2003-02-25 Derek Price <address@hidden>
+
+ * cvsbug.in: Import use of mktemp function from RedHat 8.0's
+ CVS 1.11.2 RPM. Use new MKTEMP configure variable. Use new
+ SENDMAIL from configure.
+
+ * Makefile.in: Regenerated.
+
+2003-02-25 Derek Price <address@hidden>
+
+ * cvs.h (user_admin_options): New global config option.
+ * admin.c (admin): Handle user_admin_options.
+ * parseinfo.c (parse_config): Handle UserAdminOptions.
+ (Original patch from Dan Peterson <address@hidden>.)
+
+2003-02-25 Derek Price <address@hidden>
+
+ * watch.c (watch_usage): Use {} rather than () for literals.
+
+2003-02-21 Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Update comment, change error message
+ so it's not an exact duplicate of the one in check_password.
+ (check_repository_password): Add syslog call for password mismatches.
+ (check_password): Add syslog call for password mismatches, rearrange
+ code to simplify and eliminate redundancy.
+ (pserver_authenticate_connection): Remove syslog call, now done by
+ lower-level routines.
+
+2003-02-19 Larry Jones <address@hidden>
+
+ * sanity.sh (admin-10): Add test for repository files not in
+ working directory.
+
+ * admin.c (admin_fileproc): Fix crash when no rcs file, return
+ failure status for bogus files.
+ * sanity.sh (admin-4a): Test for above.
+ (Original patch submitted by Mark D. Baushke <address@hidden>).
+
+2003-02-14 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): Fix crashes in error cases.
+ (Reported by Bart Santy <address@hidden>.)
+ * sanity.sh (log): New tests for above.
+
+2003-02-14 Derek Price <address@hidden>
+
+ * rcs.h (RCSNode): Add a field for the original path to print with
+ error messages.
+ * rcs.c (RCS_parsercsfile_i): Keep track of the original path for error
+ messages.
+ (freercsnode): Free the origpath.
+
+2003-02-14 Derek Price <address@hidden>
+
+ * watch.c (watch_usage): Make the repeatability of -a part of the
+ usage spec.
+
+2003-02-14 Derek Price <address@hidden>
+
+ * watch.c (watch_usage): Mention default for -a. Mention multiple
+ invocations of -a. Mention -R as default. Use required () rather than
+ optional [] around watch subcommand list in invocation spec. Use
+ `path' instead of `file'. Put variable <> around `action' and `path'.
+
+2003-02-12 Derek Price <address@hidden>
+
+ * main.c (main): Update copyright message to 2003.
+
+2003-02-08 Derek Price <address@hidden>
+
+ * rcs.c (RCS_checkout): Supply the full function name in the TRACE
+ output.
+ * update.c (checkout_file, join_file): Supply tag properly to
+ RCS_checkout more often.
+ (patch_file): Ditto. Fill out comments.
+ * sanity.sh (keyword, keywordname): Some changes to accomodate the fact
+ that the above changes cause patches generated by patch_file to fail
+ occassionally.
+
+2003-02-07 Derek Price <address@hidden>
+
+ * sanity.sh (*): Don't keep running after a test when --keep has been
+ supplied. That was kind of silly, wasn't it?
+
+2003-02-07 Derek Price <address@hidden>
+
+ * rcscmds.c (RCS_merge): Add a FIXME.
+
+2003-02-07 Derek Price <address@hidden>
+
+ * commit.c (checkaddfile): Do not lose the vendor branch when
+ adding files to a new branch. Avoids extranious conflicts for
+ future vendor imports. This was found and fixed in FreeBSD cvs.
+ See http://www.freebsd.org/cgi/query-pr.cgi?pr=4033 for details.
+ * sanity.sh (branch-after-import): New test.
+ (Thanks to Mark D Baushke <address@hidden> for forwarding the
+ patch and writing the test cases!)
+
+ * sanity.sh (branch-after-import): Misc portablility and standard
+ changes.
+
+2003-02-07 Derek Price <address@hidden>
+
+ * add.c: Exercise the pet peeve Karl Fogel, I think, infected me with
+ about using the word invalid rather than illegal and reserving illegal
+ for use when actually discussing laws and governmentally enforced
+ restrictions:
+ s/illegal/invalid/g;s/legality/validity/g;s/legal/valid/g;
+ * admin.c: Ditto.
+ * cvs.h: Ditto.
+ * expand_path.c: Ditto.
+ * log.c: Ditto.
+ * modules.c: Ditto.
+ * rcs.c: Ditto.
+ * rcs.h: Ditto.
+ * repos.c: Ditto.
+ * root.c: Ditto.
+ * sanity.sh: Ditto.
+ * scramble.c: Ditto.
+ * server.c: Ditto.
+ * subr.c: Ditto.
+
+2003-02-06 Derek Price <address@hidden>
+
+ * rcs.c (RCS_getdatebranch): Update header comment to reflect the state
+ of the docs and the code's operation.
+
+2003-02-06 Derek Price <address@hidden>
+
+ * client.c: Use the complete path to the CVSADM_TEMPLATE file in
+ error messages. Remove related FIXME.
+
+2003-02-04 Derek Price <address@hidden>
+
+ * status.c (status_fileproc): Add a FIXME comment.
+
+2003-02-04 Derek Price <address@hidden>
+
+ * sanity.sh (conflicts2- c. 142d): New test for double add and two
+ attempted commits of files with the same name. Fill out some comments
+ and change one FIXME to a FIXCVS THEN FIXME.
+
+2003-02-03 Derek Price <address@hidden>
+
+ * client.c (start_Server): Send multiple trace options when
+ necessary.
+ * server.c (server): Update trace option processing to accept multiple
+ -t arguments.
+ * *: Use new TRACE macro.
+
+2003-02-02 Larry Jones <address@hidden>
+
+ * error.c: Update to match error.h.
+
+ * cvs.h (cvs_trace): Add attribute for GNU printf format checking.
+ * error.h: Use same check for prototypes as cvs.h. Use PROTO
+ macro rather than #ifdef for error and error_exit.
+
+2003-02-01 Larry Jones <address@hidden>
+
+ * buffer.c (stdio_buffer_shutdown): Handle EINTR from waitpid.
+ (Patch from Johannes Grødem <address@hidden>.)
+
+2003-02-01 Derek Price <address@hidden>
+
+ * lock.c: Remove extra line feed on TRACE output.
+
+2003-01-31 Derek Price <address@hidden>
+
+ * cvs.h: Move header includes in from...
+ * error.c: ...here. Remove checks for definition of vprintf().
+ Since our error() function was making assumptions about the definition
+ of VA_START, we must not have been compiling on platforms without
+ vprintf for quite awhile and I've heard no complaints.
+ (fperrmsg): Assume vprintf().
+ * subr.c (cvs_trace): Don't assume ANSI C function declarations.
+
+2003-01-31 Derek Price <address@hidden>
+
+ * main.c (main): Allow multiple -t options.
+ (opt_usage): Correct usage.
+ * cvs.h (TRACE): New macro.
+ * subr.c (cvs_trace): New function.
+ (Thanks to the team at the CVSNT project.)
+
+ * lock.c (*): Use new TRACE macro.
+
+2003-01-31 Derek Price <address@hidden>
+
+ * sanity.sh (keywordname): Change a "FIXME" comment to "FIXCVS".
+
+2003-01-30 Derek Price <address@hidden>
+
+ * sanity.sh (keywordname): New test.
+
+2003-01-23 Larry Jones <address@hidden>
+
+ * diff.c (diff_fileproc): Restructure code to simplify and eliminate
+ redundant tests.
+
+ * server.c (do_cvs_command): Use WCOREDUMP macro rather than hard
+ coding test for core file.
+
+2003-01-21 Larry Jones <address@hidden>
+
+ * root.c (method_name): Redefine as a 2D array.
+ * root.h (method_name): Ditto.
+
+2003-01-21 Jim Meyering <address@hidden>
+
+ * add.c (add): Rename local-shadowing `i' to `j'.
+
+ * root.c (method_names): Declare to be a const array of const strings.
+ (Name_Root): Save errno so it doesn't get clobbered
+ by the intervening error call.
+ Use getline's return value, mainly to save a call to strrchr.
+
+2003-01-20 Larry Jones <address@hidden>
+
+ * myndbm.c (O_ACCMODE): Parenthesize the replacement string so that
+ it parses correctly.
+ (Reported by Andres Bertens <address@hidden>.)
+
+2003-01-15 Karl Fogel <address@hidden>
+
+ * server.c (dirswitch): Don't free dir_name until right before
+ allocating it again. This removes a potential double-free
+ problem, whereby this function could free dir_name and then
+ immediately return due to invalid directory syntax (without ever
+ reassigning dir_name), then reenter and free dir_name again.
+
+ Thanks to Stefan Esser <address@hidden> for the fix.
+
+2003-01-08 Larry Jones <address@hidden>
+
+ * client.c (update_entries): Only "0" is a special version number;
+ other numbers starting with 0 (like 0.1) are normal version numbers.
+ * commit.c (find_fileproc): Ditto. Also reorganize the code to
+ simplify the conditions.
+ (Reported by Michele Zamparelli <address@hidden>.)
+
+2003-01-02 Larry Jones <address@hidden>
+
+ * rcs.c (getdelta): Use RCSDEAD rather than literal "dead".
+
+2002-12-27 Derek Price <address@hidden>
+
+ * admin.c: s/LOCK_(NONE|WRITE|READ)/CVS_$&/g; since the definition of
+ LOCK_WRITE clashes with a definition in objidl.h on Windoze platforms.
+ * annotate.c: Ditto.
+ * client.c: Ditto.
+ * commit.c: Ditto.
+ * cvs.h: Ditto.
+ * diff.c: Ditto.
+ * edit.c: Ditto.
+ * lock.c: Ditto.
+ * log.c: Ditto.
+ * patch.c: Ditto.
+ * recurse.c: Ditto.
+ * remove.c: Ditto.
+ * status.c: Ditto.
+ * tag.c: Ditto.
+ * update.c: Ditto.
+ * watch.c: Ditto.
+ * myndbm.c: Ditto & define O_ACCMODE when it isn't defined, as under
+ Windoze.
+ (Thanks to Stephane Rouleau <address@hidden>,
+ Cristopher Seawood <address@hidden>, and
+ Frederico Costa <address@hidden> for all their hints,
+ tips, and patches for this problem.)
+
+2002-12-20 Derek Price <address@hidden>
+
+ * client.c (send_a_repository): Suppress a warning under Windoze.
+
+2002-12-19 Derek Price <address@hidden>
+
+ * Makefile.am: Remove reference to options.h.
+ * cvs.h: Ditto.
+ * options.h: Remove this obsolete file.
+ * sanity.sh: Remove comment about external diffs causing tests to fail
+ since CVS hasn't used external diffs in years.
+
+ * Makefile.in: Regenerated.
+
+2002-12-16 Derek Price <address@hidden>
+
+ * admin.c: Disable cvsadmin group checking on the client.
+ (Reported by Dan Peterson <address@hidden>.)
+
+2002-12-06 Derek Price <address@hidden>
+
+ * buffer.c: Replace calls to malloc with calls to xmalloc and calls to
+ realloc with calls to xrealloc.
+ * parseinfo.c: Ditto.
+ * root.c: Ditto.
+ * server.c: Ditto.
+ * zlib.c: Ditto.
+ * scramble.c: Change some comments to refer to xmalloc rather than
+ malloc.
+ (Reported by Dan Peterson <address@hidden>.)
+
+2002-12-04 Derek Price <address@hidden>
+
+ * options.h: Remove CVS_ADMIN_GROUP.
+
+2002-12-02 Larry Jones <address@hidden>
+
+ * commit.c (commit): Strip leading zeros from numeric revision
+ in addition to trailing dots.
+ (Reported by Peter Meszaros <address@hidden>.)
+
+2002-11-22 Larry Jones <address@hidden>
+
+ * sanity.sh: Note that the tests run for a long time.
+
+ * checkout.c (safe_location): Use xstrdup, not strdup.
+ (Reported by Terrence Enger <address@hidden>.)
+
+2002-11-19 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): Fix cross-branch correction code.
+
+ * sanity.sh: Set $LANG for systems that ignore $LC_ALL.
+ (rcs2-7): Change date offset from 100 months to 96 months to reduce
+ periodic problems with invalid dates.
+
+2002-11-12 Derek Price <address@hidden>
+
+ * sanity.sh (rcslib-symlink): Use rm -f rather than a simple rm when
+ removing links because under some configurations of RH Linux 8.0 the
+ script pauses to ask for removal approval.
+
+2002-11-08 Derek Price <address@hidden>
+
+ * sanity.sh (importc): Update the use of the touch command to be
+ compliant with POSIX 1003.1-2001, SUS2, and SUS3 now that GNU touch
+ supports this. If this breaks any test platforms we should test
+ the behavior of touch like we do for other tools.
+
+2002-11-03 Derek Price <address@hidden>
+
+ * sanity.sh (rcs2-7): Notate with a wild untested hypothesis.
+
+2002-11-03 Derek Price <address@hidden>
+
+ * sanity.sh (rcs2-7): Notate with three more failure dates.
+
+2002-10-25 Derek Price <address@hidden>
+
+ * root.c: Change some calls to SYSTEM_CLEANUP() and then exit() to
+ more appropriate calls to error_exit().
+ * server.c: Ditto.
+ * tag.c: Ditto.
+
+2002-10-24 Derek Price <address@hidden>
+
+ * buffer.c (stdio_buffer_shutdown): Remove the getc() call used to
+ detect spurious output from clients since getc() would sometimes
+ block and hang indefinately if the client kept the conection open but
+ sent no data. Bug reports state that this hapened frequently with
+ older clients connecting to 1.11.2 servers, especially when
+ compression is enabled.
+ (Original report from Mark D. Baushke <address@hidden>.
+ Original patch from Ralf S. Engelschall <address@hidden>
+ via Peter Wemm <address@hidden>.)
+
+2002-10-05 Larry Jones <address@hidden>
+
+ * recurse.c (start_recursion, do_recursion): Allow write locking
+ in addition to read locking. Change all callers.
+ * cvs.h: Change prototype to match, add lock types.
+ * tag.c (rtag_proc, rtag_fileproc, tag_fileproc): Have start_recursion
+ use write locks rather than calling lock_dir_for_write to avoid deadly
+ embrace.
+
+2002-10-04 Larry Jones <address@hidden>
+
+ * client.c (get_responses_and_close, connect_to_pserver): Set
+ to_server and from_server to NULL after freeing.
+ * main.c (main): Clear server_active when finished. Also neaten
+ up the SERVER_SUPPORT ifdef's.
+ * server.c (do_cvs_command): Set protocol_inbuf, stderrbuf, and
+ stdoutbuf to NULL after freeing.
+ (server_cleanup): Free buf_from_net and buf_to_set and set to NULL.
+ Also reset error_use_protocol.
+ (server): Don't SIG_register server_cleanup. main_cleanup (which
+ is already registered) outputs a fatal error which causes it to
+ be called; registering it directly results in it being called twice.
+ (cvs_output): Don't try to use buf_to_net or protocol if they're NULL.
+
+2002-10-03 Larry Jones <address@hidden>
+
+ * lock.c (readers_exist): Ignore our own read lock, if any, to
+ allow upgrading an existing read lock to a write lock.
+ * tag.c (rtag_proc, rtag_fileproc, tag_fileproc): Rather than
+ locking the entire tree, have start_recursion establish read
+ locks and then upgrade the read lock to a write lock (so only
+ one directory is locked at a time).
+
+2002-09-27 Larry Jones <address@hidden>
+
+ * add.c (add): Send "--" before file names.
+ * admin.c (admin): Ditto.
+ * annotate.c (annotate): Ditto.
+ * commit.c (commit): Ditto.
+ * diff.c (diff): Ditto.
+ * edit.c (watch_onoff, editors): Ditto.
+ * log.c (cvslog): Ditto.
+ * remove.c (cvsremove): Ditto.
+ * status.c (cvsstatus): Ditto.
+ * tag.c (cvstag): Ditto.
+ * update.c (update): Ditto.
+ * watch.c (watch_addremove, watchers): Ditto.
+
+ * sanity.sh (client-9): Update to match.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Remove prototype of STDC exit() function. If this breaks
+ a build, this should be detected in configure.in somehow rather than
+ restoring the line to this file.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Move definition of AUTH_CLIENT_SUPPORT into configure.in.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Move definition of FORCE_USE_EDITOR into configure.in.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Move definition of UMASK_DFLT into configure.in.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated using Automake 1.6.3.
+
+2002-09-24 Larry Jones <address@hidden>
+
+ * filesubr.c, history.c, import.c, rcs.c, update.c: Use
+ HAVE_STRUCT_STAT_ST_BLKSIZE and HAVE_STRUCT_STAT_ST_RDEV instead of
+ the obsolete HAVE_ST_BLKSIZE and HAVE_ST_RDEV.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Move definition of TMPDIR_DFLT into configure.in.
+
+2002-09-24 Derek Price <address@hidden>
+
+ * options.h: Move defininition of EDITOR_DFLT into configure.in.
+
+ * Makefile.in: Regenerated.
+
+2002-09-23 Jim Meyering <address@hidden>
+
+ If `cvs -d REPO commit ...' was used to override CVS/Root,
+ then modified files in the directory from which cvs is invoked
+ would not be committed.
+ * client.c (arg_should_not_be_sent_to_server): The above would happen
+ because this function would throw out a file name when CVS/Root
+ did not match the current server. Fix by allowing the command-line-
+ specified repository to take precedence over the value returned
+ by Name_Root. Patch by Simon Walton <address@hidden>.
+ * sanity.sh (commit-d): New tests for the above.
+ Patch by Simon Walton <address@hidden>.
+
+2002-09-20 Derek Price <address@hidden>
+
+ * options.h: Move definition of SERVER_FLOWCONTROL, SERVER_HI_WATER,
+ and SERVER_LO_WATER into configure.in.
+
+2002-09-20 Derek Price <address@hidden>
+
+ * options.h: Move definition of PATCH_PROGRAM to configure.in.
+
+2002-09-18 Larry Jones <address@hidden>
+
+ * client.c (call_in_directory): Don't create admin directory when
+ exporting into an existing directory.
+ (Reported by Jens Engel <address@hidden>.)
+ * sanity.sh (basic2): New tests for above.
+
+2002-09-16 Jim Meyering <address@hidden>
+
+ * server.c (do_cvs_command): Move declarations of locals, timeout and
+ timeout_ptr, `up', out of enclosing `#ifdef SERVER_FLOWCONTROL' block.
+ Otherwise, this file would not compile with SERVER_FLOWCONTROL
+ turned off. Patch by Ed Santiago <address@hidden>.
+
+2002-09-15 Larry Jones <address@hidden>
+
+ * myndbm.c (mydbm_open): Open the file read/write rather than read-
+ only if that's what the user asked for to ensure that the later open
+ for write will succeed.
+ (Patch submitted by Josh Lehan <address@hidden>.)
+
+2002-08-28 Larry Jones <address@hidden>
+
+ * logmsg.c (do_editor): Fix bug which prevented reusing log messages.
+ (Reported by Eric Siegerman <address@hidden>.)
+
+2002-08-16 Derek Price <address@hidden>
+
+ * create_adm.c (Create_Admin): Assume RELATIVE_REPOS is set.
+ * server.c (outside_root): Add comment.
+ * options.h: Remove RELATIVE_REPOS & CVS_BADROOT.
+ * sanity.sh: Remove a lot of !RELATIVE_REPOS cruft from tests.
+
+2002-08-14 Derek Price <address@hidden>
+
+ * server.c (server): Dispose of the correct pointer. Tidy comment.
+
+2002-08-13 Derek Price <address@hidden>
+
+ * client.c (get_cvs_port_number): Fix typo in comment. Add comments.
+ * server.c (server): Fix a FIXME. Remove an errant "const" directive.
+ Remove some redundant memory allocation and error handling code.
+
+2002-08-08 Derek Price <address@hidden>
+
+ * import.c (import): Surrounded `server_active' with
+ #ifdef SERVER_SUPPORT/#endif.
+ * commit.c (commit_fileproc, commit_direntproc): Likewise.
+ (Patch from John Tytgat <address@hidden>.)
+
+2002-07-31 Derek Price <address@hidden>
+
+ * filesubr.c: Add a line so VIM can determine tab stops and shift
widths.
+ * root.c: Ditto.
+ * (parse_cvsroot): Add comments and tidy slightly.
+
+2002-07-31 Derek Price <address@hidden>
+
+ * sanity.sh: Add another date to the comment about rcs2-7 failing.
+
+2002-07-26 Jim Meyering <address@hidden>
+
+ * commit.c (find_fileproc): When committing in client mode,
+ arrange to fail if a `cvs add'ed file no longer exists in the
+ working directory.
+ * sanity.sh (commit-add-missing): New test for above.
+
+2002-07-25 Larry Jones <address@hidden>
+
+ * sanity.sh: Set $TMPDIR if it's not already set and use it rather
+ than /tmp for the expected server temp directory path.
+
+2002-07-09 Larry Jones <address@hidden>
+
+ * vers_ts.c (time_stamp_server, time_stamp): Eliminate unneeded
+ struct_tm copying.
+
+ * lock.c (lock_wait, lock_obtained): Display time in UTC if possible
+ to reduce confusion in client/server mode.
+ (Original patch from Eduardo Perez Ureta <address@hidden>.)
+
+2002-06-26 Larry Jones <address@hidden>
+
+ * tag.c (check_fileproc): When checking up-to-date, T_REMOVE_ENTRY
+ is also a valid status.
+ (Reported by David Everly <address@hidden>.)
+ * sanity.sh (tagc): New tests for above.
+
+2002-06-18 Larry Jones <address@hidden>
+
+ * update.c (patch_file): Don't patch if diff bigger than file.
+ Don't bother adjusting the permission on the diff output if
+ we're not going to use it.
+
+
+2002-06-18 Derek Price <address@hidden>
+
+ * server.c: Handle HPUX password expiration fields in the passwd
+ string in case we are set up on a server with NIS passwords served
+ from HPUX.
+ (Original patch from John Cavanaugh <address@hidden>.)
+
+2002-06-17 Larry Jones <address@hidden>
+ and Jonathan Kamens <address@hidden>
+
+ * commit.c (commit_fileproc, commit_direntproc): Don't try to call
+ an editor to get the log message if running as a server. Instead,
+ just use an empty log message.
+ * import.c (import): Ditto.
+
+ * import.c (import): In client mode, always send a message to the
+ server, even if it's empty (this parallels a change made by Larry
+ Jones to commit.c on May 7).
+
+2002-05-31 Larry Jones <address@hidden>
+
+ * rcs.c: Conditionally define MAP_FAILED for old systems that don't
+ have it in <mman.h>.
+ (Reported by jeremy brand <address@hidden>.)
+
+2002-05-24 Larry Jones <address@hidden>
+
+ * rcscmds.c (diff_exec): Add a -- before the first file name just
+ in case it looks like an option.
+ (Reported by Zooko <address@hidden>.)
+
+ * rcscmds.c (diff_execv): Remove -- same as diff_exec. Change
+ only caller.
+ * cvs.h: Ditto.
+
+2002-05-23 Larry Jones <address@hidden>
+
+ * cvs.h (strcat_filename_onto_homedir): Make arguments const.
+ * filesubr.c (strcat_filename_onto_homedir): Make arguments const,
+ move more code here from callers, change all callers.
+
+2002-05-22 Derek Price <address@hidden>
+
+ * cvs.h: Add prototype for this...
+ * filesubr.c (strcat_filename_onto_homedir): new function.
+ * login.c (): Use new function.
+
+ * cvsrc.c (read_cvsrc): Use new function due to problems on VMS.
+ * ignore.c (ign_setup): Ditto.
+ * wrapper.c (wrap_setup): Ditto.
+ (Original patch from Karsten Spang <address@hidden>.)
+
+2002-05-21 Larry Jones <address@hidden>
+
+ * rcs.c (rcsbuf_getkey): Correct off-by-one error in ptr assertion
+ and add a similar assertion for ptrend.
+ (Reported by Rebecca Young <address@hidden>.)
+ (rcsbuf_fill): Remove redundant code.
+
+2002-05-20 Derek Price <address@hidden>
+
+ * buffer.h: New prototype for...
+ * buffer.c (stdio_buffer_get_file): this new function to abstract
+ access to a buffer's file descriptor.
+ * client.c (auth_server): Use the new function.
+ (Original patch from Jonathan Kamens <address@hidden>.)
+
+2002-05-20 Derek Price <address@hidden>
+
+ * main.c (main): Add 2002 to the copyright years output with the
+ version string.
+
+2002-05-15 Larry Jones <address@hidden>
+
+ * log.c (log_parse_list): Fix off-by-one error which caused
+ incorrect handling of 'cvs log -wuser1,user2 foo.c' command.
+ (Patch from Alexey Mahotkin <address@hidden>,
+ reported by Alex Morozov <address@hidden>.)
+
+2002-05-09 Larry Jones <address@hidden>
+
+ * login.c (password_entry_operation): Get cvsroot_canonical before
+ trying to read the user's password file so we have it even if the
+ file doesn't exist.
+ (Reported by Sarah Thompson <address@hidden>.)
+
+2002-05-08 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add options.h explicitly - since we
+ stopped generating it dynamically, Automake stopped noticing it and
+ including it in dists. See TODO item #214 for notes.
+
+2002-05-08 Derek Price <address@hidden>
+
+ * cvs.h: Use the HAVE_CONFIG_H define.
+
+2002-05-07 Larry Jones <address@hidden>
+
+ * filesubr.c (isaccessible): Set errno before returning failure
+ in the SETXID_SUPPORT code.
+
+ * logmsg (do_verify): Avoid even more work if there's no verifymsg
+ script to run.
+
+ * logmsg: Use fputs/putc rather than fprintf where appropriate.
+ (do_verify): Run the verifymsg script even if there's no log
+ message. (Reported by Andy Baker <address@hidden>.)
+ Don't reread the log message unless a verifymsg script was run.
+
+ * commit.c (commit): Always send -m to the server, even if there's
+ no message.
+
+ * create_adm.c (Create_Admin): Add dotemplate parameter to trace.
+ Remove unreachable code.
+
+2002-05-03 Larry Jones <address@hidden>
+
+ * server.c (serve_watch_on, serve_watch_off, serve_watch_add,
+ serve_watch_remove): Just pass "watch" as the command name
+ to do_cvs_command to avoid unknown command errors.
+ (Reported by Gary Hennigan <address@hidden>.)
+
+ * rcs.c (RCS_checkin): Fix bad call to error () in buggy
+ PRESERVE_PERMISSIONS code.
+ (rcs_internal_unlockfile): Include current value of errno in error
+ message even though it may well be irrelevant (it's still better
+ than nothing).
+
+2002-05-02 Derek Price <address@hidden>
+
+ * .cvsignore: Remove lines for files obsoleted by new autotools.
+
+2002-05-02 Derek Price <address@hidden>
+
+ * stamp-h2.in: Remove this uneeded file.
+
+2002-05-01 Derek Price <address@hidden>
+
+ * options.h.in: Move to...
+ * options.h: here.
+
+2002-04-30 Derek Price <address@hidden>
+
+ * version.h.in: Remove this file.
+ * version.h: Ditto.
+
+ * Makefile.am: Remove references to version.h.
+ * cvs.h: Use <> rather than "" around the config.h #include. I didn't
+ quite bother to understand why, but autoconf recommends it.
+ * cvsbug.in: Use PACKAGE_BUGREPORT defined by configure for the bug
+ report email address.
+ * version.c (version): Use PACKAGE_STRING defined in config.h instead
+ of the version_string that used to be defined in version.h.
+
+ * Makefile.in: Regenerated with automake 1.6.
+
+2002-04-28 Derek Price <address@hidden>
+
+ * cvs.h: Use `"'s around includes when we mean a local file.
+
+2002-04-28 Derek Price <address@hidden>
+
+ * cvs.h: #define new names for functions and variables when they
+ might conflict with system definitions (namely on Mac OS X 10.1 with
+ the most recent dev packages - This should be removable after the Mac
+ dev packages are fixed.).
+
+2002-04-26 Larry Jones <address@hidden>
+
+ * logmsg.c (do_editor): Fix assertion when CLIENT_SUPPORT not defined.
+ (Reported by Matthias Andree <address@hidden>.)
+
+2002-04-19 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): First cut at code to allow logging
+ between a revision and *any* ancestor, not just one explicitly on
+ the same branch (e.g., from 1.1 to 4.1.2.3.6.1).
+
+ * subr.c (gca): Simplify and optimize.
+
+2002-04-19 Jim Meyering <address@hidden>
+ and Ed Santiago <address@hidden>
+
+ * classify.c (Classify_File): Fix it so that `cvs update -p -r...'
+ works, even under some slightly unusual (though perfectly legitimate)
+ circumstances.
+ * sanity.sh (update-p): New tests for this.
+
+2002-04-18 Derek Price <address@hidden>
+
+ * sanity.sh: Move test for regex metacharacters in username until
+ after we're sure we found the version of expr that we're going to use.
+
+2002-04-18 Larry Jones <address@hidden>
+
+ * admin.c (admin_fileproc): Allow admin to be used on RCS files with
+ no local version (e.g., removed files) like most other subcommands.
+
+ * wrapper.c (wrap_add): Update URL of -t/-f wrapper discussion.
+
+2002-04-18 Derek Price <address@hidden>
+
+ * version.h: Regenerated for 1.11.2.1 version update.
+
+2002-04-17 Derek Price <address@hidden>
+
+ * version.h: Regenerated for 1.11.2.
+
+2002-04-03 Derek Price <address@hidden>
+
+ * stamp-h2.in: Regenerate with recent version of Autoconf.
+
+2002-04-03 Derek Price <address@hidden>
+
+ * sanity.sh (TR): Send the stderr of one of the tool setup (tr) tests
+ to /dev/null to avoid spurious output on some operating systems
+ (notably Mac OS X).
+
+2002-03-22 Larry Jones <address@hidden>
+
+ * sanity.sh (rcslib): Correct new tests to use ${testcvs} instead
+ of cvs.
+
+2002-03-21 Derek Price <address@hidden>
+
+ * vers_ts.c (time_stamp): Return the timestamp for the newer of the
+ link and the link's source when the file is a link.
+ (Patch from RedHat cvs-1.11.1p1-7 SRPM.)
+
+ * sanity.sh (rcslib): Test for same.
+
+2002-03-17 Larry Jones <address@hidden>
+
+ * log.c (cvslog, log_fileproc): Add -S option to suppress head or
+ file name if no revisions selected.
+ * sanity.sh (log): New tests for above.
+
+2002-03-13 Derek Price <address@hidden>
+
+ * main.c (usg): Correct a spelling mistake in a comment.
+ (Thanks to Matt Kraai <address@hidden>.)
+
+2002-03-09 Larry Jones <address@hidden>
+
+ * import.c (import): Change the suggested merge message to use
+ rev tags instead of the branch tag with a date.
+ * sanity.sh (import, importb): Change to match.
+
+ * remove.c (remove_fileproc): Disallow removing files with sticky
+ dates for the same reason we already disallow sticky numeric tags.
+ * sanity.sh (sticky): New test for above.
+
+2002-02-27 Larry Jones <address@hidden>
+
+ * diff.c (diff_fileproc): Treat dead revisions as nonexistent.
+
+2002-02-26 Larry Jones <address@hidden>
+
+ * diff.c (diff): Remove -V and --paginate options: they aren't valid.
+ (diff_usage): Document all the diff options.
+
+2002-02-13 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_gettag): Do not interpret an empty tag as HEAD (nothing
+ else does and I don't see any documentation that says it should).
+ (translate_symtag): Break out of loop at end of symbols to prevent
+ looping forever when tag is "".
+ (Reported by Alain ENOUT <address@hidden>
+ via Eric Gillespie <address@hidden>.)
+
+2002-02-11 Larry Jones <address@hidden>
+
+ * server.c (server_cleanup): Set buf_to_net back to blocking mode
+ and flush it (in case there are any error messages pending) before
+ shutting down buf_from_net and again right before shutting it down.
+
+2002-02-08 Larry Jones <address@hidden>
+
+ * main.c (lookup_command_attribute): Throw a fatal error if the
+ command is not found.
+ * server.c (server_tag): Use the correct command name.
+
+2002-01-30 Larry Jones <address@hidden>
+
+ * error.h (error_exit): Remove unintended prototype.
+
+ * server.c (serve_root): Remove check for impossible condition.
+ (serve_init): Save and restore current_parsed_root.
+
+2002-01-29 Larry Jones <address@hidden>
+
+ * error.h (error_exit): Declare __noreturn__ to avoid spurious
+ warnings.
+
+ * server.c (serve_root): If the specified root doesn't match the
+ pserver root, return before changing current_parsed_root to prevent
+ subsequent commands from accessing an unchecked root directory.
+ (server_init): Check specified root against the pserver root and
+ complain if they don't match. Also, if there are pending errors,
+ print them and return before changing current_parsed_root to prevent
+ subsequent commands from accessing an unchecked root directory.
+ * sanity.sh (pserver): New tests for above.
+
+2002-01-10 Larry Jones <address@hidden>
+
+ * log.c (log_version_requested): Change :: in revision spec to be
+ exclusive just on the low end (so -r tag1::tag2 gives revisions
+ after tag1 but up to and including tag2), which is much more useful
+ than the previous (exclusive at both ends) behavior.
+ (log_usage): Update to match.
+ * sanity.sh (log): Update to match.
+
+2002-01-02 Larry Jones <address@hidden>
+
+ * server.c (LOG_DAEMON): Define if needed.
+ (Patch from John David Anglin <address@hidden>.)
+
+ * server.c (pserver_authenticate_connection): Add a specific error
+ message for EOF at protocol start and syslog if available.
+ * sanity.sh (pserver-bufinit): Update to match.
+
+2001-12-10 Larry Jones <address@hidden>
+
+ * log.c (log_usage): Note that -r and -d take lists, not just a
+ single specification.
+ (log_expand_revlist): Don't dereference null pointers when one end
+ of a revision range is a non-existent tag.
+
+2001-12-03 Larry Jones <address@hidden>
+
+ * annotate.c (annotate, annotate_fileproc): Don't annotate binary
+ files unless new -F option given.
+ * sanity.sh (basica, ann, ann-id, rcs, keywordlog, tagdate): Update
+ to match.
+
+2001-11-30 Larry Jones <address@hidden>
+
+ * admin.c (admin): Allow unrestricted usage of -q in addition to -k.
+
+2001-10-25 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): Make erroneous or inconsistent revision
+ specs select no revisions rather than all revisions.
+
+2001-10-23 Larry Jones <address@hidden>
+
+ * import.c (add_rcs_file): Don't put an expand entry into the file
+ for the default expansion mode (kv).
+ * wrapper.c (wrap_send, wrap_unparse_rcs_options): Process entries
+ with default expansion mode since they may be needed to avoid matching
+ a more general entry later.
+ (wrap_add): Set rcsOption to NULL for default (kv).
+ (wrap_add_entry): Use structure assignment to copy entries rather
+ that copying members by hand.
+ * sanity.sh (binwrap3): Revise to test wrapper entries that don't
+ specify any non-default options but just prevent matching later,
+ more general entries.
+
+2001-10-02 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_fully_parse): Add revision number to more error messages.
+
+2001-09-27 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_fully_parse, RCS_getdeltatext): Add the missing revision
+ number to the "mismatch" error message.
+
+ * sanity.sh (multiroot2-9a): Update to match changes to lock.c.
+
+2001-09-26 Larry Jones <address@hidden>
+
+ * lock.c (Lock_Cleanup, Reader_Lock, write_lock): Add trace messages.
+
+2001-09-24 Derek Price <address@hidden>
+
+ * find_names.c (add_entries_proc): Leave closure specified as such in
the
+ function definition for clarity.
+
+ * find_names.c (Find_Names): Use 'closure' feature of walklist()
+ to eliminate the static variable.
+ (add_entries_proc): Expect closure to be the file list.
+ (Patch from Alexey Mahotkin <address@hidden>.)
+
+2001-09-19 Derek Price <address@hidden>
+
+ * rcs.c (rcsbuf_valpolish_internal): Restore one of the
+ "if ( ... ) abort();" sequences since it seems to check the validity of
+ the RCS file rather than for a programming error. Also added a FIXME
+ comment to the effect that we should explain the RCS file error to the
+ user as such if it is such.
+ (Thanks to Larry Jones <address@hidden>.)
+
+2001-09-19 Derek Price <address@hidden>
+
+ * rcs.c (rcsbuf_getkey, rcsbuf_valpolish_internal): Replace some code
+ of the form "if ( ... ) abort();" with equivalent calls to assert().
+
+2001-09-17 Derek Price <address@hidden>
+
+ * myndbm.c (mydbm_load_file): Fix buffer overflow error and make error
+ messages more informative.
+ * sanity.sh (modules6): New test.
+ (Original report from Taska <address@hidden> and others.)
+
+2001-09-14 Derek Price <address@hidden>
+
+ * logmsg.c (do_verify): Dispose memory when finished with it.
+
+2001-09-07 Larry Jones <address@hidden>
+
+ * mkmodules.c (notify_contents): In the example, move the %s to
+ the end since many, if not most, versions of mail insist on
+ options coming before addresses.
+
+2001-09-06 Derek Price <address@hidden>
+
+ * login.c (login): Deal with NULL return value from getpass.
+
+2001-09-04 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated with automake 1.5.
+ * stamp-h2.in: Ditto.
+
+2001-09-04 Derek Price <address@hidden>
+
+ * main.c (main): Fix empty CVSROOT message to specify `valid' instead
+ of `legal'.
+
+2001-09-04 Derek Price <address@hidden>
+
+ * server.c (pserver_authenticate_connection): Back out changes from the
+ 30th and...
+ * getline.c (getstr): init the buffer instead.
+
+2001-08-31 Derek Price <address@hidden>
+
+ * Makefile.in: Backed out accidental commit from yesterday.
+
+2001-08-30 Derek Price <address@hidden>
+
+ * server.c (pserver_authenticate_connection): Don't print from the
+ NULL pointer in the error message string in the case where the client
+ didn't send any data.
+ * sanity.sh (pserver): Test for this case.
+ (Report from Mark Welch <address@hidden>).
+
+2001-08-24 Derek Price <address@hidden>
+
+ * logmsg.c (do_editor): Add comment and assertion.
+ * import.c (import): Don't call do_editor with a repository argument
+ in client mode.
+ (Report and original patch from darkness <address@hidden>.)
+
+2001-08-24 Larry Jones <address@hidden>
+
+ * log.c (log_expand_revlist): Arrange for nil revision specs to
+ select nothing instead of everything.
+ * sanity.sh (log): New tests for above.
+
+2001-08-24 Derek Price <address@hidden>
+
+ * parseinfo.c (Parse_Info): Change the function name in the trace
+ and add the client/server string.
+
+2001-08-24 Derek Price <address@hidden>
+
+ * Implement RereadLogAfterVerify CVSROOT/config option to control
+ FreeBSD read-write of log messages in the verification script.
+ * logmsg.c: RereadLogAfterVerify defaults to LOGMSG_REREAD_NEVER
+ to preserve the status quo.
+ * parseinfo.c (parse_config): Add parsing for RereadLogAfterVerify
+ option. Possible values are: no | never | yes | always | stat
+ * cvs.h: Add extern for RereadLogAfterVerify and new value macros
+ LOGMSG_REREAD_NEVER, LOGMSG_REREAD_ALWAYS, LOGMSG_REREAD_STAT for
+ its values.
+ (Patch from Mark D. Baushke <address@hidden>.)
+
+ * Apply changes from FreeBSD cvs sources to implement a read-write
+ user-defined verification script.
+ * logmsg.c (do_verify): Update do_verify to expect a pointer
+ to the saved message. The log file passed to the verifymsg_script
+ should be re-read after the user-defined verification script has
+ been run. The user-defined verification script is allowed to
+ modify the message. This allows the script to add extra
+ information to the log message or to remove template lines that
+ are not needed.
+ * cvs.h: Update prototype for do_verify prototype to expect a
+ pointer to the saved_message.
+ * commit.c (commit, commit_fileproc, commit_direntproc): Update
+ calls to do_verify as the saved_message arg is now read-write.
+ * import.c (import): Update calls to do_verify as the
+ saved_message arg is now read-write.
+ * sanity.sh (info-v4-[12]): Rename the old info-v4 test to info-v5
+ and add a new info-v4 test case have the verification script
+ modify the log message to test the above changes.
+ (Patch from Mark D. Baushke <address@hidden>.)
+
+ * logmsg.c: Change RereadLogAfterVerify default to always.
+ (do_verify): Reformat and make minor fixes to Mark's patch.
+ * mkmodules.c (config_constants): Add comment about
+ RereadLogAfterVerify.
+ * sanity.sh (info-rereadlog): Rename the tests from Mark's patch and
+ reformat them a bit.
+
+2001-08-23 Derek Price <address@hidden>
+
+ * sanity.sh (info): Demonstrate that the verifymsg scripts can
+ sometimes, but not always, retreive information on which directory is
+ being committed to.
+
+2001-08-22 Derek Price <address@hidden>
+
+ * logmsg.c: Back out the last change - the repository which is passed
+ in is actually the directory and changes with each call to do_verify.
+ If a verifymsg script is using `pwd`, this could change the operation.
+ * cvs.h: Ditto.
+ * commit.c: Ditto.
+ * import.c: Ditto.
+
+2001-08-22 Derek Price <address@hidden>
+
+ * logmsg.c (do_editor): Return reused_message.
+ (do_verify): Don't verify the same log message more than once.
+ * cvs.h: Update prototypes for do_verify and do_editor.
+ * commit.c (commit_fileproc, commit_direntproc): Use the new
functionality.
+ * import.c (import): Ditto.
+
+2001-08-22 Derek Price <address@hidden>
+
+ * logmsg.c (do_verify): Remove an unecessary "else" clause following an
+ exit and unindent the former contents.
+
+2001-08-22 Derek Price <address@hidden>
+
+ * commit.c (commit): Don't call do_verify in client mode since we know
+ do_verify will just return anyhow.
+
+2001-08-20 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add version.c and version.h.
+ (BUILT_SOURCES): Add version.h.
+ (Maintainer Targets): Remove version.h.
+ * version.c: Remove @VERSION@ dependant bits.
+ * version.c.in: Removed.
+ * version.h.in: New file.
+ (Original patch from Alexey Mahotkin <address@hidden>.)
+
+ * Makefile.am: Various modifications to make Automake, make dist, and
+ windows targets work like they are supposed to.
+ * version.h: New (generated) file.
+
+ * Makefile.in: Regenerated.
+
+2001-08-09 Derek Price <address@hidden>
+
+ * client.c (socket_buffer_shutdown): Use recv instead of read and
+ return 0 on success.
+ (Patch from "Manfred Klug" <address@hidden>.)
+
+2001-08-09 Derek Price <address@hidden>
+
+ * buffer.c (stdio_buffer_shutdown): Assume the buffer is not a socket
+ when NO_SOCKET_TO_FD is defined.
+ * client.c (make_bufs_from_fds): Add is_sock argument and remove fstat
+ call and reference to S_ISSOCK since these functions aren't available
+ under Windows.
+ (connect_to_forked_server, connect_to_pserver, start_tcp_server,
+ start_server, start_rsh_server): Use new argument.
+ (Patch from "Manfred Klug" <address@hidden>.)
+
+ * buffer.c (stdio_buffer_shutdown): Various reformattings, fix bug
+ where rsh pipes weren't being closed.
+
+2001-08-09 Derek Price <address@hidden>
+
+ * sanity.sh (rmadd, rm-update-message, join-two-branch,
+ ignore-on-branch): Change a few references to `cvs' to `$PROG'.
+
+2001-08-07 Derek Price <address@hidden>
+
+ * build_src.com: Add annotate.c/annotate.obj,verify, correct zlib name.
+ * patch.c: VMS time_t appears to be unsigned. Add a cast when testing
+ for (time_t)-1.
+ * subr.c: #else,#endif for no symlinks should be moved.
+ (Patch from Mike Marciniszyn <address@hidden>.)
+
+2001-08-06 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated.
+
+2001-08-01 Derek Price <address@hidden>
+
+ * diff.c (diff): Send long option for side-by-side diffs to the server
+ rather than '-y', for backwards compatibility with old servers.
+ (Original patch from Peter Mathiasson <address@hidden>.)
+
+2001-07-19 Larry Jones <address@hidden>
+
+ * mkmodules.c (cvswrappers_contents): Remove -t/-f since they're
+ disabled in wrapper.c.
+
+ * checkout.c (checkout): Don't complain about checking out into the
+ repository when piping output.
+ (Reported by der Mouse <address@hidden>.)
+ * sanity.sh (checkout_repository): New tests for above.
+
+2001-07-10 Larry Jones <address@hidden>
+
+ * sanity.sh (importc-7): Now works correctly in local mode.
+
+ * commit.c (commit_dirleaveproc): We're still in the directory when
+ this is called, so the first argument to Name_Repository needs to
+ be NULL, not dir.
+ * sanity.sh (rmadd): New tests for above.
+
+ * commit.c (commit): Reword error messages for committing as root.
+
+2001-07-08 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_checkout): Correct scanf format to allow for trailing
+ NUL terminator.
+ * update.c (special_file_mismatch): Ditto.
+ (Reported by Pekka Savola <address@hidden>.)
+
+2001-07-05 Larry Jones <address@hidden>
+
+ * client.c, root.c: Fix -Wall warnings.
+
+ * buffer.c: #include socket header to declare shutdown().
+
+ * rcs.c (rcsbuf_open): Use getpagesize() instead of sysconf() for
+ portability.
+ (RCS_copydeltas, rcsbuf_fill): Fix -Wall warnings.
+
+2001-07-04 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated with new Automake release candidate 1.4h.
+
+2001-07-03 Derek Price <address@hidden>
+
+ * rcs.c (rcsbuf_open): Reduce memory consumption still further by not
+ mmapping the entire file when pos is specified.
+ (rcsbuf_cache_open): Add FIXME comment wrt read-only mmaps and rcsbuf
+ caching.
+
+2001-07-03 Derek Price <address@hidden>
+
+ * rcs.c (rcsbuf_open): Use mmap when possible to reduce memory
+ consumption, especially with large (e.g. binary) files.
+ (rcsbuf_close): Call munmap.
+ (rcsbuf_getkey): Remove the buffer fill code when using mmap.
+ (rcsbuf_getrevnum): Ditto.
+ (rcsbuf_fill): Remove this function when using mmap.
+ (rcsbuf_cache_open): Mostly don't use this function with mmap.
+ (RCS_copydeltas): Don't depend on the file pointer with mmap.
+
+ * stamp-h2.in: Regenerated.
+
+2001-07-03 Derek Price <address@hidden>
+
+ * update.c: Indent compiler directives.
+
+2001-07-02 Larry Jones <address@hidden>
+
+ * import.c (update_rcs_file): Use -kb instead of -ko when comparing
+ binary files.
+ (Reported by Gyula Faller <address@hidden>.)
+
+2001-06-28 Larry Jones <address@hidden>
+
+ * checkout.c (checkout): Explicitly initialize all the static options
+ so that multiple calls work right. Also fix potential memory leaks.
+ (Reported by Dr. Dieter Maurer <address@hidden>.)
+
+2001-06-28 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated with new version of Automake.
+
+2001-06-28 Larry Jones <address@hidden>
+
+ * checkout.c (checkout): Set history_name for export as well as
+ checkout.
+ (checkout_proc): Use it.
+
+ * checkout.c (safe_location): Add missing argument in error message.
+
+2001-06-26 Larry Jones <address@hidden>
+
+ * recurse.c (start_recursion): Use strip_trailing_slashes instead
+ of doing it by hand.
+
+ * server.c (pserver_authenticate_connection): Don't clear out
+ descrambled_password until *after* it's (potentially) logged.
+ (Reported by Eric Hanchrow <address@hidden>.)
+
+2001-06-25 Larry Jones <address@hidden>
+
+ * recurse.c (start_recursion): Deal with at least some of the cases
+ where trailing slashes cause confusion.
+ (Reported by Malcolm Fernandes <address@hidden>.)
+ * sanity.sh (basica, basicb): Tweak existing tests to check this.
+
+2001-06-22 Larry Jones <address@hidden>
+
+ * sanity.sh (modules5): New tests with -d on command line.
+
+2001-06-21 Larry Jones <address@hidden>
+
+ * modules.c (do_module): Use run_module_prog and server_active to
+ determine when to call server_prog instead of using server_expanding
+ so that we get the right paths in the replies as long as we take
+ mwhere into account in addition to where.
+ (Reported by Pascal Bourguignon <address@hidden>.)
+ * server.c (server_prog): Use protocol pipe instead of buf_to_net.
+ * sanity.sh (modules5): Remove FIXCVS comment and update to match.
+ * server.c, server.h: Remove server_expanding since now unused.
+
+2001-06-21 Larry Jones <address@hidden>
+ for Stephen Rasku <address@hidden>
+
+ * admin.c: Corrected spelling mistakes in help.
+
+2001-06-20 Derek Price <address@hidden>
+
+ * client.c (socket_buffer_shutdown): Fix untested typos.
+ (Reported by "Jerzy Kaczorowski" <address@hidden>.)
+
+ * buffer.c (stdio_buffer_shutdown): Put the call to SHUTDOWN_SERVER in
+ the correct place.
+
+2001-06-20 Derek Price <address@hidden>
+
+ * logmsg.c (do_editor): Abort in the case that the file has only
+ comment lines.
+ (Original patch from Mark Valentine <address@hidden>.)
+
+ * logmsg.c (do_editor): Fix rare memory leak.
+ * sanity.sh (editor): Add tests for aborted log messages.
+
+2001-06-20 Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Only set $CVS_USER if
+ AUTH_SERVER_SUPPORT is defined.
+ (Reported by Nalin Dahyabhai <address@hidden>.)
+
+2001-06-13 Derek Price <address@hidden>
+
+ * client.c: Fix incorrect fixed-size buffer usage in
+ connect_to_gserver().
+ (Minor changes to a patch from Alexey Mahotkin <address@hidden>.)
+
+2001-06-11 Derek Price <address@hidden>
+
+ * main.c (main): Always print $CVSROOT when parse_cvsroot fails.
+ * root.c (parse_cvsroot): Tidy error messages and provide more
+ consistent behavior.
+ * sanity.sh (crerepos): Adapt to new error messages.
+ (Suggested by Alexey Mahotkin <address@hidden>.)
+
+2001-06-08 Derek Price <address@hidden>
+
+ * sanity.sh (tagf-28): Use $CVSROOT_DIRNAME.
+
+2001-06-07 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_unlock): Reverse kj's change of 1999-10-18: a bare -u
+ should never break locks, you have to specify a specific revision
+ to do that. Also add an informative message for a bare -u when
+ the user doesn't hold any locks.
+ * commit.c (unlockrcs): Make RCS_unlock quiet, like RCS_lock.
+ * sanity.sh (rmadd-24): Update to match.
+
+ * sanity.sh (crerepos-6a): Set CVS_RSH for ${testcvs}, not for
+ dotest_fail. Allow for "broken pipe" rather than "end of file".
+
+2001-06-07 Derek Price <address@hidden>
+
+ * sanity.sh (tagf): Use $CVSROOT_DIRNAME rather than
+ /tmp/cvs-sanity/cvsroot.
+
+2001-06-06 Derek Price <address@hidden>
+
+ (Reformatting, bug fixes, tests, and comments to a
+ patch from Stephen Cameron <address@hidden>.)
+
+ * tag.c: (rtag_fileproc, rtag_delete, tag_fileproc)
+ Changed behavior of "cvs tag -F", "cvs tag -d", "cvs rtag -F"
+ and "cvs rtag -d" so that they will not disturb existing
+ branch tags unless a new "-B" option is given.
+ * sanity.sh (tagf-16 - tagf-33): Added tests for new -B option
+ to "cvs tag" and "cvs rtag"
+
+2001-06-06 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos-6a): Set CVS_RSH=false and only for the actual
+ test call at Larry's suggestion. Also, test the error message since
+ it's fixed now.
+
+2001-06-05 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_unlock): Note when breaking someone else's lock.
+ (Reported by MURVAI-BUZOGANY Laszlo
+ <address@hidden>.)
+ * sanity.sh (reserved-14): Update to match.
+
+2001-06-05 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos-6a): Set CVS_RSH=/bin/false... this is a local
+ mode only test anyhow.
+ (Thanks to Larry Jones and Morgan Burke <address@hidden>.)
+
+2001-05-31 Derek Price <address@hidden>
+
+ * sanity.sh (rcs2-7): Add today to the list of failure dates for rcs2-7
+ in the hopes that the data will eventually prove useful to someone
+ motivated enough to fix the problem.
+
+2001-05-30 Derek Price <address@hidden>
+
+ * stamp-h2.in: Regenerated.
+
+2001-05-30 Derek Price <address@hidden>
+
+ * *: Various bug fixes and comments for the following
+ patch from Donald Sharp <address@hidden>:
+
+ * checkout.c (safe_location): cvs co -d <directory> still had
+ failure modes from the way the -d option works.
+ * sanity.sh: Misc error message resynching.
+
+2001-05-29 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Add root.h.
+
+ * Makefile.in: Regenerated.
+ * stamp-h2.in: Regenerated.
+
+2001-05-29 Derek Price <address@hidden>
+
+ * checkout.c (safe_location): Correct formatting.
+
+2001-05-29 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): Fix a comment.
+
+2001-05-26 Larry Jones <address@hidden>
+
+ * checkout.c (safe_location): Use old-style definition to keep
+ non-ANSI compilers happy.
+
+ * sanity.sh (check_respository): Use ${CVSROOT_DIRNAME} instead
+ of /tmp/cvs-sanity/cvsroot.
+
+2001-05-25 Larry Jones <address@hidden>
+
+ * sanity.sh (modules5): Add sleep to script to help avoid out of
+ order messages.
+
+ * filesubr.c (mkdir_if_needed): Return 1 if the directory exists
+ reguardless of what errno is set to.
+ (Reported by "Robinson, Greg" <address@hidden>.)
+
+2001-05-25 Derek Price <address@hidden>
+ for Donald Sharp <address@hidden>
+
+ * checkout.c: Modified safe_location() to refuse checkout if
+ the -d option to co specifies inside of the repository.
+ * import.c: New parameter to safe_location needed to be added.
+ * cvs.h: New parameter to safe_location needed to be added.
+ * sanity.sh: Test case to test for failure mode.
+
+2001-05-23 Larry Jones <address@hidden>
+
+ * checkout.c (checkout_proc): Don't build top_level_admin directory
+ when exporting.
+ (Reported by Tony Byrne <address@hidden>.)
+
+2001-05-21 Derek Price <address@hidden>
+
+ * client.c: Fix a mispelling in a comment.
+ (Patch from Alexey Mahotkin <address@hidden>).
+
+2001-05-05 Larry Jones <address@hidden>
+
+ * login.c (password_entry_operation): Only warn if unable to open
+ .cvspass for reading: may be initial login and it doesn't exist yet.
+
+2001-05-15 Derek Price <address@hidden>
+
+ * client.c (start_tcp_server): Use the struct sockaddr_in declared in
+ the function.
+ (Reported by Emil Isberg <address@hidden>.)
+
+2001-05-05 Larry Jones <address@hidden>
+
+ * annotate.c (annotate): Pass local to do_module and rannotate_proc
+ so that -l actually works.
+ * log.c (cvslog): Ditto.
+ * patch.c (patch): Ditto; make local local instead of global.
+ (patch_proc): Use local_specified parameter instead of global.
+ * tag.c (cvstag, rtag_proc): Ditto.
+
+2001-05-05 Larry Jones <address@hidden>
+
+ * client.h: Declare "struct buffer" outside prototype for __STDC__
+ compilers.
+
+2001-05-04 Derek Price <address@hidden>
+
+ * client.c: General refactoring. Removed several global variables in
+ favor of passing locals and/or dynamic evaluation.
+ (recv_line): Removed this function.
+ (make_bufs_from_fds): New function with factored code.
+ (connect_to_forked_server): New prototype. Use new functions.
+ (connect_to_pserver): New prototype. Use new functions.
+ (connect_to_gserver): New prototype. Use new API.
+ (auth_server): Factored this portion of the pserver code so it can be
+ shared. Rewrote to use buffers rather than depending on a socket.
+ (start_rsh_server): New prototype. Use new API.
+ (start_tcp_server): New prototype. Use new API.
+ (start_server): Factor some code. Use new API.
+ * client.h: New prototypes.
+ * cvs.h: Gratuitous reformatting. Use new root.h.
+ * login.c (login): Use new connect_to_pserver API.
+ * root.h: New file. Contains some code that used to be in cvs.h.
+
+2001-05-04 Derek Price <address@hidden>
+
+ * client.c: Gratuitous reformatting.
+ * client.h: Ditto.
+
+2001-05-04 Derek Price <address@hidden>
+
+ * zlib.c (compress_buffer_shutdown_input): Use new buffer shutdown
+ prototype.
+ (compress_buffer_shutdown_output): Ditto.
+ (Thanks to Pavel Roskin <address@hidden>.)
+
+2001-05-03 Derek Price <address@hidden>
+
+ * buffer.c (struct stdio_buffer_closure): New structure to hold a
+ FILE * and the child's PID when necessary.
+ (stdio_buffer_initialize): Change proto to accept PID. Set up new
+ closure. Pass new stdio_buffer_shutdown to buf_initialize.
+ (stdio_buffer_input): Use new closure.
+ (stdio_buffer_output): Ditto.
+ (stdio_buffer_flush): Ditto.
+ (stdio_buffer_shutdown): New function. Teach buffer to close itself.
+ (packetizing_buffer_shutdown): Use new buffer shutdown proto.
+ * buffer.h (struct buffer): New buffer shutdown proto.
+ (stdio_buffer_initialize): New proto.
+ * client.c (log_buffer_shutdown): Use new proto.
+ (socket_buffer_initialize): Pass shutdown func.
+ (socket_buffer_shutdown): New function.
+ * server.c (get_responses_and_close): Remove most of the guts. Rely
+ on the buffer shutdown function from now on.
+ (start_rsh_server): Return child PID.
+
+2001-05-03 Larry Jones <address@hidden>
+
+ * history.c (history_write): Handle the case where the user's home
+ directory doesn't exist gracefully instead of erroring out.
+ (Reported by David Hoover <address@hidden>.)
+
+2001-05-03 Derek Price <address@hidden>
+
+ * cvs.h: s/allocate_and_strcat/xrealloc_and_strcat/ since that is what
+ I wrote in the ChangeLog, oh, so long ago.
+ * diff.c (diff): Ditto.
+ * subr.c (allocate_and_strcat, xrealloc_and_strcat): Ditto.
+
+2001-05-02 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_getdate): Handle the (unusual!) case where we
+ can't find any revisions at all.
+ (Reported by Ryan Grow <address@hidden>.)
+
+2001-04-30 Larry Jones <address@hidden>
+
+ * sanity.sh (multiroot2-9a): Rename (from multiroot2-9) to avoid
+ duplicate names; fix to work without SERVER_SUPPORT defined.
+ (Reported by Pavel Roskin <address@hidden>.)
+
+2001-04-29 Derek Price <address@hidden>
+
+ * Makefile.am (check-local): Make dependent on localcheck and
+ remotecheck and move old check target...
+ (localcheck): here.
+
+ * Makefile.in: Regenerated.
+
+2001-04-27 Larry Jones <address@hidden>
+
+ * sanity.sh (pserver): Add tests for readers and writers.
+
+2001-04-27 Derek Price <address@hidden>
+
+ * sanity.sh (version-2r): Update to handle patch releases in version
+ numbers.
+
+2001-04-27 Derek Price <address@hidden>
+
+ * version.c: Regenerated.
+
+2001-04-27 Derek Price <address@hidden>
+
+ * version.c: Regenerated.
+
+2001-04-27 Larry Jones <address@hidden>
+
+ * main.c (lookup_command_attribute): Lookup specified command, not
+ whatever's in the global command_name.
+
+2001-04-25 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated using AM 1.4e as of today at 18:10 -0400.
+ * version.c: Regenerated.
+
+2001-04-22 Larry Jones <address@hidden>
+
+ * tag.c (tag_check_valid): Make an unwritable val-tags file a
+ warning instead of a fatal error.
+
+2001-04-20 Larry Jones <address@hidden>
+
+ * annotate.c (annotate_usage): -r and -D are not mutually exclusive.
+ * main.c (cmd_usage): Add missing version subcommand.
+ * update.c (update_usage): Add missing -C option.
+
+ * sanity.sh (death2): New tests for previous change.
+
+ * classify.c (Classify_File): Treat a dead revision like the RCS
+ file doesn't exist.
+ * sanity.sh: Update to match.
+
+2001-04-16 Larry Jones <address@hidden>
+
+ * checkout.c, update.c: Fix usage messages: -r and -D are not
+ mutually exclusive.
+ (Suggested by David L. Martin <address@hidden>.)
+
+ * logmsg.c (do_editor): Don't add a blank line to the message.
+ * sanity.sh (editor-log-file*): Update to match.
+
+ * checkout.c, update.c: Note in usage message that -k is sticky.
+
+ * server.c: (server_cleanup, wait_sig): Remove ancient SunOS kludge.
+ (Suggested by Rob Saccoccio <address@hidden>.)
+
+2001-04-04 Larry Jones <address@hidden>
+
+ * sanity.sh (dotest, dotest_lit, dotest_fail, dotest_status,
+ dotest_sort): Don't count on $? being set in then or else clauses.
+
+ * ignore.c (ignore_files): Collect unignored files into a list and
+ sort it before calling PROC to avoid order dependencies. Rewrite
+ the while loop to allow normal continues instead of goto.
+
+2001-04-04 Derek Price <address@hidden>
+
+ * sanity.sh (ignore-on-branch-3): Fix in the remote case.
+
+2001-04-03 Larry Jones <address@hidden>
+
+ * update.c (update_fileproc): Remove unused variable (resurrecting).
+
+2001-04-03 Derek Price <address@hidden>
+ Larry Jones <address@hidden>
+ reported by Jakob Bøhm <address@hidden>
+
+ * update.c (update_fileproc): Don't store a file with T_UNKNOWN status
+ in ignlist if present in the sandbox.
+ * sanity.sh (ignore-on-branch): New test.
+ (ignore): Tidy this test.
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh: Make sure the test for `id' fails when a nonstandard `id'
+ is used and the user is root. Fix some quoting in error messages.
+ (fork): Take `cvs' out of the PATH.
+ (TODO): Add note about the test suite not working with user names over
+ eight characters in length.
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh (fork): New test for CVS_SERVER default.
+ (TODO): Note about eventually removing most of the references to
+ CVS_SERVER.
+
+2001-04-02 Larry Jones <address@hidden>
+
+ * client.c (connect_to_forked_server): Use program_path as the default
+ server instead of "cvs".
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh: Use less obfuscated English in my comment about sanity
+ checking sanity.sh.
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh (rm-update-message): Create a test directory again but
+ change back to the correct directory upon completion this time.
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh: Change last two '[.*]'s to 'test's for
+ consistency and remove...
+ (TODO): the note from the TODO list.
+
+2001-04-02 Derek Price <address@hidden>
+
+ * sanity.sh: Add test for PWD before successful exit.
+
+2001-03-30 Larry Jones <address@hidden>
+
+ * sanity.sh (rm-update-message): Remove duplicate code.
+
+2001-03-30 Derek Price <address@hidden>
+
+ * sanity.sh (rm-update-message): New test for local/client-server
+ warning message discrepency.
+
+2001-03-30 Larry Jones <address@hidden>
+
+ * annotate.c: Move annotate() here from rcs.c, support rannotate.
+ * Makefile.am, Makefile.in: Add annotate.c.
+ * main.c (cmds[], cmd_usage[]): Add rannotate.
+ * rcs.c: Move declarations of rcs_delta_op and RCS_deltas to...
+ * rcs.h: ... here.
+ * server.c (serve_rannotate): New.
+ (requests[]): Add rannotate.
+ * sanity.sh (ann): New tests for rannotate.
+
+ * log.c (rlog_proc): Remove dead code.
+
+2001-03-30 Derek Price <address@hidden>
+
+ * sanity.sh (join-readonly-conflict): Run more of this through dotest.
+
+2001-03-30 Larry Jones <address@hidden>
+
+ * log.c (log_fileproc): Don't output working file for rlog.
+ * sanity.sh (log): New tests for rlog.
+
+ * cvs.h (mtype): Add MISC type.
+ * log.c (cvslog): Support rlog as well as log.
+ (rlog_proc): New.
+ * main.c (cmds[], cmd_usage[]): Add rlog.
+ (main): Remove old rlog warning message.
+ * server.c (serve_rlog): New.
+ (requests[]): Add rlog.
+
+2001-03-29 Derek Price <address@hidden>
+
+ * sanity.sh: cd to $TESTDIR once after it is normalized. Make TODO
+ on history and symlinks more specific. Tested properly this time.
+
+2001-03-29 Larry Jones <address@hidden>
+
+ * main.c (cmds[], lookup_command_attribute, main): Include the
+ command attributes in the global command table instead of inferring
+ them from the command names. Change the sense of the
+ CVS_CMD_IGNORE_ADMROOT attribute to match its name.
+
+2001-03-29 Derek Price <address@hidden>
+
+ * sanity.sh (*, basic2-64): Remove references to TMPPWD. Fix FIXME
+ at end of script now that $TESTDIR can't be relative.
+
+2001-03-29 Derek Price <address@hidden>
+
+ * sanity.sh: Normalize TESTDIR even when the user set it.
+
+2001-03-29 Larry Jones <address@hidden>
+
+ * client.c (connect_to_pserver, start_tcp_server): Add IP address
+ to connect failed message.
+ (connect_to_forked_server, connect_to_pserver, start_tcp_server): Add
+ trace messages ala start_rsh_server.
+ (start_rsh_server): Include entire command in trace message for
+ START_RSH_WITH_POPEN_RW like ! START_RSH_WITH_POPEN_RW does.
+
+2001-03-29 Derek Price <address@hidden>
+
+ * sanity.sh: Global search & replace ${TESTDIR}/cvsroot with
+ ${CVSROOT_DIRNAME} for consistency.
+
+2001-03-29 Derek Price <address@hidden>
+
+ * sanity.sh (conflicts-12[68].5): Remove sanity hack which has allowed
+ for a CVS bug since May 96/97. Not sure when the bug went bye-bye, but
+ the tests broke when $TESTDIR != $TMPPWD.
+
+2001-03-26 Larry Jones <address@hidden>
+
+ * classify.c (Classify_File): Don't report a conflict for a removed
+ file when piping. Also simplify the code structure.
+ (Reported by Milos Kleint <address@hidden>.)
+ * sanity.sh (rmadd2-14[abc]): New tests for above.
+
+2001-03-24 Noel Cragg <address@hidden>
+
+ * diff.c: mods to allow `-T' and `-y' options to be passed through
+ to the diff library. This wasn't allowed earlier because of a
+ similarly named options that got passed through to the old rcs
+ programs. We've long since stopped sending `-T' to any rcs
+ utility and have never used `-y'. Any users of moldly CVS
+ versions which used to support `-T' have (hopefully) upgraded to
+ one where that option isn't supported. It seems reasonable to
+ enable them again and pass them through. (sanity.sh still works
+ anyways...)
+ (longopts): add short option equivalents for --initial-tab and
+ --side-by-side.
+ (diff): add new short options to getopt string and switch
+ statement.
+
+2001-03-22 Larry Jones <address@hidden>
+
+ * sanity.sh: Add check for ${DOTSTAR} with large matches.
+
+2001-03-23 Derek Price <address@hidden>
+
+ * sanity.sh: Do the same as below for $keep.
+
+2001-03-23 Derek Price <address@hidden>
+
+ * sanity.sh: Replace 'remote=(yes|no)' with 'remote=(:|false)' since
+ often 'false' and more often ':' are shell builtins. This makes the
+ succinct, 'if $remote; then' faster than 'if test $remote = yes; then'.
+ Alter tests in the rest of the script to match the new usage. Added
+ a suffix of 'r' to remote test names when it was appropriate and I
+ remembered. Some reformatting.
+
+2001-03-22 Larry Jones <address@hidden>
+
+ * sanity.sh (diffmerge1_yours, diffmerge1_mine): Check for exact
+ output instead of using wildcards to avoid buffer overflows in some
+ versions of expr.
+
+2001-03-21 Derek Price <address@hidden>
+
+ * sanity.sh: cd to '/tmp' again rather than $HOME since HOME was set to
+ a value inside ${TESTDIR} by the script.
+
+2001-03-20 Derek Price <address@hidden>
+
+ * sanity.sh (diffmerge1): Minor formatting and syntax changes.
+
+ for Jacob Burckhardt <address@hidden>
+
+ * sanity.sh (diffmerge1): More merging behavior tests. Specifically,
+ test some cases which broke before in Karl Tomlinson's diff fix was
+ checked in today.
+
+2001-03-20 Derek Price <address@hidden>
+
+ * sanity.sh: Don't use unescaped parens in sh outside of quotes.
+
+2001-03-20 Derek Price <address@hidden>
+
+ * sanity.sh: Don't remove ${TESTDIR} when -k (keep) set.
+
+2001-03-20 Derek Price <address@hidden>
+
+ * sanity.sh: Change usage to match the new getopts format and comment.
+
+2001-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (modules2-nestedrename): New test. Verifies behavior of
+ renames nested under an ampersand module.
+ (modules2-ampertag): New test. Verifies an error condition which
+ prevents some ampersand modules from being checked out when a tag
+ is specified.
+
+2001-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (modules2): Additional test for ampersand module behavior
+ with '-d'.
+
+ for Greg Klanderman <address@hidden>
+
+ * checkout.c (build_one_dir): Fix typo where clauses of two
+ conditionals were reversed in call to Create_Admin. This caused
+ the CVS/Tag file to be removed in cases where it should have been
+ set, and vice-versa. It only surfaced in rare cases as this code
+ is only invoked when using the -d option to build the path to
+ check out in. Further, the bug would only matter when checking
+ out a module containing ampersand modules within it, via
+ client/server CVS.
+
+2001-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (admin-28-5): Confirm that a missing tag during an
+ 'admin -n' operation is not a fatal error.
+
+2001-03-16 Derek Price <address@hidden>
+
+ * admin.c (admin_data): Remove 'quiet' member.
+ (admin_fileproc): Use global 'really_quiet' rather than
+ admin_data->quiet.
+
+2001-03-16 Derek Price <address@hidden>
+
+ * sanity.sh (admin): Replace hardcoded testdir path with the variable.
+
+2001-03-15 Derek Price <address@hidden>
+
+ * sanity.sh (basica, binfiles, head, admin): Adjust for new messages.
+ * admin.c (admin_fileproc): Only print messages when not in
+ really_quiet mode.
+
+ for Stephen Rasku <address@hidden>
+
+ * rcs.c (RCS_tag2rev): Make a missing tag a survivable error.
+
+2001-03-15 Larry Jones <address@hidden>
+
+ * subr.c (sleep_past): Fix various bugs that would result in a
+ negative sleep time if it weren't unsigned; since it is, it would
+ result in a very large sleep time. Ensure that us is always less
+ than 1000000. Don't try to sleep for more 1 sec with usleep.
+ Cast NULL select arguments to correct type just in case.
+
+2001-03-14 Derek Price <address@hidden>
+
+ * subr.c (sleep_past): New function.
+ * client.c (get_responses_and_close): Use new function.
+ * commit.c (commit): Ditto.
+ * update.c (do_update): Ditto.
+ * cvs.h: Prototype new function.
+
+ * stamp-h2.in: Regenerated.
+
+2001-03-14 Derek Price <address@hidden>
+
+ * Makefile.in: Regenerated.
+ * stamp-h2.in: Ditto.
+
+2001-03-14 Larry Jones <address@hidden>
+
+ * commit.c (check_fileproc): Allow adding on the trunk when there's
+ an existing non-Attic RCS file as long as the head revision is dead.
+ This can happen due to an aborted resurrection.
+ (commit_fileproc): When resurrecting, consider the dead revision
+ along with the other files' revisions.
+ (findmaxrev): Avoid unnecessary work.
+ (checkaddfile): Only warn if file isn't in Attic as expected.
+ (Reported by Ross Burton <address@hidden>.)
+ * sanity.sh (basica-r*): New tests for above.
+ (basica-o4): Update to match.
+
+2001-03-09 Larry Jones <address@hidden>
+
+ * edit.c (edit_fileproc, unedit_fileproc): Some implementations of
+ asctime/ctime apparently use a leading zero on the date instead
+ of the space required by the C Standard. Correct for this so that
+ shared working directories work without hassle.
+ (Reported by David L. Martin <address@hidden>.)
+ * entries.c (fgetentent): Ditto.
+ * vers_ts.c (time_stamp_server, time_stamp) Ditto.
+
+2001-03-07 Larry Jones <address@hidden>
+
+ * sanity.sh (basica, binfiles2, head, admin): Update to match
+ change to admin.c.
+
+2001-03-06 Larry Jones <address@hidden>
+
+ * client.c (recv_bytes): Handle EOF as in recv_line().
+ (Reported by Pavel Roskin <address@hidden>.)
+
+ * admin.c (admin_fileproc): Change final error message to clarify
+ that CVS refused to modify the RCS file rather than being unable to.
+
+2001-02-28 Jim Meyering <address@hidden>
+
+ * commit.c (commit_usage): Use `-F logfile' (rather than -F file') in
+ the description of that option, to be consistent with the `-F logfile'
+ in the Usage: line. Use spaces instead of TAB characters, and realign.
+
+2001-03-02 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos): Make failed ${CVS_RSH-rsh} attempt print the
+ name of the command it actually used rather than 'rsh'.
+
+2001-02-27 Derek Price <address@hidden>
+
+ * sanity.sh (modules2-ampermod-*): Added these tests to make sure the
+ top level directory is created in an ampermodule when '-n' is passed to
+ checkout.
+
+ original bug report from
+ Wolfgang Haefelinger <address@hidden>
+
+2001-02-27 Derek Price <address@hidden>
+
+ * sanity.sh (version-[12]): replace ' (client/server)' with .* in these
+ two tests so that 'make check' works with whatever client/server
+ options the executable was compiled with.
+
+2001-02-23 Derek Price <address@hidden>
+
+ * main.c (main): Only check a cvsroot_t's isremote member when client
+ support is enabled.
+ * server.c: Include GSSAPI headers with client support as well as
+ server support.
+
+2001-02-21 Larry Jones <address@hidden>
+
+ * modules.c, cvs.h (do_module): Add build_dirs argument and use it
+ instead of run_module_prog. Change all callers.
+ * tag.c (cvstag): For rtag, don't build directories.
+ * sanity.sh (modules3): Update to match.
+
+2001-02-20 Derek Price <address@hidden>
+
+ * client.c: Use xgssapi.h.
+ * server.c: Ditto.
+
+2001-02-15 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Correct error from yesterday.
+ * Makefile.in: Regenerated.
+
+2001-02-14 Derek Price <address@hidden>
+
+ * server.c: Include xselect.h.
+ * update.c (do_update): Use best available sleep function.
+
+2001-02-14 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Alphabetize and split to one/line.
+ (cvs_LDADD): Alphabetize and split to one/line.
+ * Makefile.in: Regenerated.
+
+2001-02-14 Larry Jones <address@hidden>
+
+ * build_src.com: Remove references to rtag.c & rtag.obj.
+
+2001-02-13 Derek Price <address@hidden>
+
+ * main.c (date_to_tm): New function to convert an RCS date string to a
+ struct tm.
+ (tm_to_internet): New function to convert a struct tm to a date string
+ as specified by RFC822 and amended by RFC 1123.
+ (date_to_internet): Use the above two functions and a struct tm
+ intermediary for conversion.
+ * patch.c (patch_fileproc): Answer somebody's comment and use the new
+ diff_exec API.
+ * rcs.c (RCS_checkin): Use new diff_exec API.
+ (RCS_delete_revs): Use new diff_exec API.
+ (make_file_label): If the file name is DEVNULL, date it the Epoch for
+ compatibility with the POSIX.2 spec and Larry Wall's patch
+ implementation.
+ * rcscmds.c (diff_exec): Accept new label arguments.
+ * sanity.sh (death2): Update some diff tests to accept the new format.
+ * update.c (patch_file): Use new diff_exec API.
+ * diff.c (diff_fileproc): Create header labels appropriate for
+ compatibility with the Larry Wall version of patch.
+ (diff): Rename calls to strcat_and_allocate.
+ (strcat_and_allocate): Rename and move...
+ * subr.c (xrealloc_and_strcat): here.
+ * cvs.h: Update prototypes to match.
+
+2001-02-13 Derek Price <address@hidden>
+
+ * Makefile.am (cvs_SOURCES): Remove rtag.c.
+
+2001-02-07 Larry Jones <address@hidden>
+
+ * sanity.sh (directory_cmp): Return status rather than setting ISDIFF.
+ (basic2): Rewrite using dotest.
+
+2001-02-06 Larry Jones <address@hidden>
+
+ * tag.c, rtag.c: Merge with tag.c being the surviving file.
+ * Makefile.in: Update to match.
+ * main.c (cmds): rtag() => cvstag().
+ * server.c (serve_rtag): Ditto, and set command name.
+
+2001-02-06 Derek Price <address@hidden>
+ Rex Jolliff <address@hidden>
+ Shawn Smith <address@hidden>
+
+ * add.c: Replace opendir, closedir, & readdir calls with CVS_OPENDIR,
+ CVS_CLOSEDIR, & CVS_READDIR in support of changes to handle VMS DEC C
+ 5.7 {open,read,close}dir problems. Check today's entry in the vms
+ subdir for more.
+ * filesubr.c: ditto
+ * find_names.c: ditto
+ * ignore.c: ditto
+ * import.c: ditto
+ * lock.c: ditto
+ * update.c: ditto
+
+2001-02-02 Larry Jones <address@hidden>
+
+ * error.h: Changed include guard macro from _error_h_ to ERROR_H;
+ names beginning with underscore are reserved.
+ * login.c (password_entry_parseline, password_entry_operation,
+ password_entry_operation_e, password_entry_operation_t): Removed
+ leading underscore(s).
+ (password_entry_parseline): Corrected error messages.
+ (password_entry_operation): Fixed uninitialized variable (password).
+ (login): Removed unused variable (found_password).
+
+ * rtag.c (rtag_proc): Call lock_tree_for_write() before calling
+ start_recursion. This fixes a serious problem where do_recursion
+ was reading and caching RCS files without any locks in place and
+ that information was subsequently being used to rewrite the file
+ causing any intermediate changes to be lost.
+ (rtag_filesdoneproc): Defunct.
+ (Reported by Karl Tomlinson <address@hidden>.)
+ * tag.c (cvstag, tag_filesdoneproc): Ditto.
+ * lock.c (lock_tree_for_write): Add which argument, change all
+ callers to pass W_LOCAL.
+ * rcs.h: Ditto.
+
+2001-01-29 Derek Price <address@hidden>
+
+ * client.c (get_cvs_port_number): change the prototype to accept a
+ const cvsroot_t * as input and add a FIXME comment
+ * cvs.h: new prototypes for get_cvs_port_number & normalize_cvsroot
+ * login.c (_password_entry_operation): consolidate all the ~/.cvspass
+ access into a single new function which reads ~/.cvspass in a backwards
+ compatible manner
+ (logout): use the new _password_entry_operation function
+ (login): ditto
+ (get_cvs_password): ditto
+ * root.c (normalize_cvsroot): move knowledge of default port & username
+ values inside
+
+2001-01-29 Larry Jones <address@hidden>
+
+ * subr.c (shell_escape): New function.
+ * cvs.h: Declare it.
+ * logmsg.c (logfile_write): Use it to avoid problems with filenames
+ containing "'".
+ (Reported by Gerhard Ahuis <address@hidden>.)
+
+ * server.c (outbuf_memory_error, pserver_authenticate_connection,
+ kserver_authenticate_connection): If available, use syslog() to
+ record some errors.
+
+2001-01-25 Larry Jones <address@hidden>
+
+ * server.c (do_cvs_command): If there's a partial output line left
+ over and the client doesn't support MT, go ahead and send it in an
+ M response instead of just dropping it.
+ (Reported by Milos Kleint <address@hidden>.)
+
+ * update.c (update_fileproc): Handle toss_local_changes in the
+ T_NEEDS_MERGE case.
+ (Inspired by Noel L Yap <address@hidden>.)
+ * sanity.sh (clean): New tests for above.
+
+2001-01-23 Derek Price <address@hidden>
+
+ * run.c (run_exec): flush, if used, stderr and stdout before exit
+ * server.c (cvs_flusherr): flush stderr & send a stderr flush command
+ on the protocol pipe
+ (cvs_flushout): like above, for stdout
+ (do_cvs_command): handle flushes properly
+ * sanity.sh (reserved): make the commitinfo script echo errors to
+ stderr rather than stdin
+
+2001-01-18 Larry Jones <address@hidden>
+
+ * log.c (option_revlist, revlist, log_usage, cvslog,
+ log_parse_revlist, log_expand_revlist, log_version_requested): Add
+ support for :: for exclusive ranges.
+ * admin.c (admin_usage): Reorder -o to be parallel to log -r.
+ * sanity.sh (log): New tests for above.
+
+2001-01-18 Derek Price <address@hidden>
+
+ * main.c: Add '2001' to the range of copyright years listed by the
+ --version option
+ * version.c.in (version): check current_parsed_root before its isremote
+ member to avoid a core dump
+ * sanity.sh (version): add a test for the version command
+
+ * version.c: regenerated
+
+2001-01-12 Larry Jones <address@hidden>
+
+ * rcs.c, rcs.h (RCS_lock, RCS_unlock): Use RCS_gettag to find the
+ correct revision so that symbolic tags work correctly. (This
+ requires removing the "const" from the rev parameter since it's
+ passed to RCS_gettag which might modify it.)
+ (Reported by irina sturm <address@hidden>.)
+
+2001-01-11 Larry Jones <address@hidden>
+
+ * run.c (close_on_exec): Remove check for FD_CLOEXEC. As far as I
+ can see, it's *never* been defined, which defeats the whole point.
+ If F_SETFD is defined, it's probably safe to use it.
+
+ * server.c (do_cvs_command): Call close_on_exec on the protocol and
+ flow control pipes in the child process so they don't get inherited
+ by any subsidiary processes.
+ (Reported by Tristan Gingold <address@hidden>.)
+
+ * cvs.h (free_cvsroot_t): Spell correctly (was free_CVSroot_t).
+
+2001-01-10 Derek Price <address@hidden>
+ Rex Jolliff <address@hidden>
+
+ * build_src.com: VMS changes
+ * filesubr.c: replace calls to unlink() with CVS_UNLINK() for VMS
+ * rcs.c: ditto
+
+2001-01-10 Derek Price <address@hidden>
+
+ * main.c (current_root): explicitly list this as a static global
+
+2001-01-10 Derek Price <address@hidden>
+
+ * cvs.h (get_cvs_port_number): change name & prototype from
+ get_port_number
+ * client.c (get_cvs_port_number): new function which returns a port
+ number based on a cvsroot_t rather than requiring all possible sources
+ passed in
+ (connect_to_pserver): use new get_cvs_port_number function
+ (connect_to_server): ditto
+ * login.c (get_password): use new get_cvs_port_number function
+ (login): ditto
+ (logout): ditto
+
+2001-01-10 Derek Price <address@hidden>
+
+ * Makefile.am ($(srcdir)/version.c): specify $(srcdir) for all subparts
+ of the build since some systems don't allow mv's across partitions
+ * Makefile.in: regenerated
+
+2001-01-10 Derek Price <address@hidden>
+
+ * Makefile.am (version.c): specify $(srcdir) explicitly in target rule
+ so version.c gets built properly for all makes.
+ (version.o): specify $(srcdir)/version.c explicitly so dependency is
+ found and built properly
+ * Makefile.in: regenerated
+
+2001-01-09 Derek Price <address@hidden>
+
+ * version.c: updated timestamp
+
+2001-01-09 Larry Jones <address@hidden>
+
+ * server.c (server): Change to server_temp_dir immediately after
+ creating it so that any stray files that happen to be created go
+ there instead of in the server's initial directory, wherever that
+ may be.
+ * sanity.sh (modules5-15): Update to match.
+
+ * version.c.in: Update to match Derek's change to version.c.
+
+2001-01-09 Derek Price <address@hidden>
+
+ * cvs.h: Remove the various CVSroot_* bits and replace them with a
+ single structure of type cvsroot_t (current_parsed_root)
+
+ * root.c (parse_cvsroot): return pointer to a new cvsroot_t rather than
+ altering global variables
+ (local_cvsroot): return a pointer to a new cvsroot_t rather than
+ setting globals. changed the name of this function from
+ set_local_cvsroot to better explain new functionality
+ (new_cvsroot_t): new initializer function
+ (free_cvsroot_t): new function
+ (others): use current_parsed_root rather than the old CVSroot_* globals
+
+ * add.c: use current_parsed_root rather than the old CVSroot_* globals
+ * admin.c: ditto
+ * checkout.c: ditto
+ * client.c: ditto
+ * commit.c: ditto
+ * create_adm.c: ditto
+ * diff.c: ditto
+ * edit.c: ditto
+ * expand_path.c: ditto
+ * find_names.c: ditto
+ * history.c: ditto
+ * ignore.c: ditto
+ * import.c: ditto
+ * lock.c: ditto
+ * log.c: ditto
+ * login.c: ditto
+ * logmsg.c: ditto
+ * main.c: ditto
+ * mkmodules.c: ditto
+ * modules.c: ditto
+ * parseinfo.c: ditto
+ * patch.c: ditto
+ * rcs.c: ditto
+ * recurse.c: ditto
+ * release.c: ditto
+ * remove.c: ditto
+ * repos.c: ditto
+ * rtag.c: ditto
+ * server.c: ditto
+ * status.c: ditto
+ * tag.c: ditto
+ * update.c: ditto
+ * version.c: ditto
+ * watch.c: ditto
+ * wrapper.c: ditto
+
+2001-01-05 Derek Price <address@hidden>
+
+ * cvs.h (enum CVSmethod): add null_method
+ * root.c (method_names): correlate null_method & "undefined"
+ (parse_cvsroot): make two error cases non fatal
+ * sanity.sh (crerepos-6b): account for new error message, re above
+
+2001-01-05 Derek Price <address@hidden>
+
+ * src/Makefile.am (cvsbug, cvsbug_EXTRA_DIST, EXTRA_DIST): move cvsbug
+ target to configure.in - see ../ChangeLog for more
+ * src/cvsbug.in: Rename from cvsbug.sh
+ * src/cvsbug.sh: Rename to cvsbug.in
+
+2001-01-04 Larry Jones <address@hidden>
+
+ * Makefile.am (cvsbug): Explicitly list input file ($< is only
+ valid in inference rules).
+ * Makefile.in: Ditto.
+
+2001-01-04 Derek Price <address@hidden>
+
+ * sanity.sh: use getopts rather than getopt for portability reasons
+
+2001-01-03 Derek Price <address@hidden>
+
+ * Makefile.am (remotecheck): depend on 'all'
+ * Makefile.in: regenerated
+
+2000-12-29 Derek Price <address@hidden>
+
+ * sanity.sh: remove explicit "$@" from last checkin and move the 'do'
+ to the line following the 'for'. Apparently this is more portable.
+
+2000-12-29 Derek Price <address@hidden>
+
+ * sanity.sh: make "$@" explicit in 'for' statement since Solaris 5.6's
+ implementation of Bourne shell doesn't seem to implement this default
+ behavior.
+
+2000-12-27 Derek Price <address@hidden>
+
+ * sanity.sh: add a -f option for continuing from a particular test
+ and shorten --keep to -k so we can use the getopt function.
+
+2000-12-27 Derek Price <address@hidden>
+
+ * Makefile.am (remotecheck): Make remotecheck dependant on all
+ * Makefile.in: regenerated
+
+2000-12-26 Derek Price <address@hidden>
+
+ * Makefile.in: update timestamp
+ * stamp-h2.in: ditto
+ * version.c: ditto
+
+2000-12-26 Derek Price <address@hidden>
+
+ * Makefile.am: new target for version.c
+ (EXTRA_DIST): add version.c.in & version.c so builds work when
+ configure doesn't
+ * Makefile.in: Regenerated
+ * stamp-h2.in: update timestamp
+ * version.c: ditto
+
+2000-12-26 Derek Price <address@hidden>
+
+ * Makefile.am (INCLUDES): add zlib
+ * Makefile.in: Regenerated
+
+2000-12-22 Derek Price <address@hidden>
+
+ * Makefile.am (DISTCLEANFILES): added a few files
+ (INCLUDES): commented
+ * Makefile.in: Regenerated
+
+2000-12-21 Derek Price <address@hidden>
+
+ * .cvsignore: Added .deps directory and a new stamp file
+ * Makefile.am: New file needed by Automake
+ * Makefile.in: Regenerated
+ * stamp-h2.in: New stamp file created by Automake
+ * version.c.in: use configure to generate version.c
+
+2000-12-16 Derek Price <address@hidden>
+
+ * server.c (server_update): Keep the vers structure up to date after
+ sending a Remove or Remove-entry command to the client
+ * update.c (update): remove call to server_updated() after
+ scratch_file()
+ (scratch_file): in server mode, call server_updated(), otherwise keep
+ the vers structure up to date
+ (join_file): add a trace, save the revision to Register() on a remove
+ before calling server_scratch & server_updated
+ * sanity.sh (join): Add test for a remove/add caused by an update
+ to a new branch and a join in the same step.
+
+2000-12-15 Larry Jones <address@hidden>
+
+ * error.c (error): Add %ld and %lu.
+
+ * history.c: Change hrec.idx from int to long, reformat NEXT_BAR
+ for readability, add hrec_idx.
+ (fill_hrec): Change initialization to be portable and always set
+ idx so it can be used as a line number in error messages; improve
+ parsing and error checking.
+ (read_hrecs): Initialize hrec_idx, handle embedded NULs, warn about
+ no newline at end of file.
+ (select_hrec): Add basic validity checking.
+
+2000-12-07 Larry Jones <address@hidden>
+
+ * history.c (history): Allow multiple -m options as documented.
+
+2000-11-29 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): back out yesterday's redundant changes
+ * main.c (main): fix CVSROOT trace message to look like other trace
+ messages
+ * sanity.sh (multiroot2-9): expect new trace message
+
+2000-11-28 Derek Price <address@hidden>
+
+ * root.c (parse_cvsroot): add trace on this function
+ * client.c (get_port_number): make trace print look like others
+
+2000-11-16 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): back out the previous change in the
+ interests of portability, add an assertion, and fix the header comment
+
+2000-11-16 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): refine the exit behavior to notice if
+ the out param was passed in NULL and, if so, avoid setting it and delete
+ the temp file for later
+
+2000-11-16 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): fixed a garble or two, added some
+ additional error checking, and added a comment
+
+2000-11-15 Derek Price <address@hidden>
+
+ * filesubr.c (cvs_temp_file): added cvs_temp_file
+ function to use mkstemp rather than one of the other temp file
+ generators as gcc keeps complaining I should.
+ (cvs_temp_name): altered this function to simply wrap cvs_temp_file
+ and deprecated it
+ * cvs.h: added prototype for cvs_temp_file
+ * commit.c (commit): use the new function instead of the old and plug
+ an old (though related) memory leak.
+ * import.c (import): use the new function
+ * login.c (login): Ditto
+ * logmsg.c (do_editor, do_verify): Ditto
+ * patch.c (patch_fileproc): Ditto
+
+2000-11-14 Larry Jones <address@hidden>
+
+ * update.c, update.h (do_update): Add xdotemplate parameter.
+ Change all callers.
+ (update_dirent_proc): Use dotemplate for Create_Admin, not 1.
+ * checkout.c (checkout_proc): Don't create CVS/Template if
+ exporting.
+ (Reported by Andrey Podkolzin <address@hidden>.)
+
+2000-11-08 Larry Jones <address@hidden>
+
+ * admin.c (admin): Use getgroups() to check for membership in
+ CVS_ADMIN_GROUP if it exists. In any event, check the user's
+ primary group in addition to any additional groups.
+ (Reported by Thomas Okken <address@hidden>.)
+
+2000-11-06 Jim Meyering <address@hidden>
+
+ Compile with gcc's -Wformat and fix the exposed problems.
+ * root.c (parse_cvsroot) [! HAVE_KERBEROS]: Provide an argument
+ for the %s error format spec.
+ [! HAVE_GSSAPI]: Likewise.
+ (normalize_cvsroot): Put comment delimiters around token after `#endif'.
+
+2000-11-03 Larry Jones <address@hidden>
+
+ * sanity.sh: Some versions of sed require a space between -e and
+ the value.
+
+2000-10-27 Larry Jones <address@hidden>
+
+ * checkout.c (checkout): Don't check for a safe location if just
+ cat'ing the module database.
+ (Reported by Ilya Martynov <address@hidden>.)
+ Have -s set cat as well as status; it simplifies the code.
+
+2000-10-26 Larry Jones <address@hidden>
+
+ * sanity.sh (join-admin-2): Check output from all commands instead
+ of (mostly) discarding. (Some of the tests used to produce stray
+ output in remote mode.)
+
+ * sanity.sh (dotest_line_by_line): Handle empty lines in pattern
+ (expr doesn't distingish between successfully matching nothing
+ and failing to match anything).
+
+ * sanity.sh (dotest_internal): Rearrange and use elif to simplify.
+
+2000-10-24 Jim Meyering <address@hidden>
+
+ Fix a bug, introduced with my fix of 2000-07-10, whereby -kk would
+ sometimes be ignored for some of the files involved in an update.
+
+ * update.c (join_file): Restore the original value of `options'
+ right after calling checkout_file.
+ * sanity.sh (join-admin-2): New test for this.
+
+2000-10-23 Derek Price <address@hidden>
+ James Youngman <address@hidden>
+
+ * sanity.sh: it's /gnu/bin, not /gun/bin. Thanks go to James Youngman
+ <address@hidden> for the bug report and patch.
+
+2000-10-20 Jim Kingdon <http://sourceforge.net/users/kingdon/>
+
+ * server.c (switch_to_user): Set CVS_USER. Patch from Sudish
+ Joseph and popularized by dozens (e.g. mozilla.org, also others).
+
+2000-10-20 Derek Price <address@hidden>
+ KOIE Hidetaka <address@hidden>
+
+ * root.c (normalize_cvsroot): plug a memory leak. Thanks to
+ KOIE Hidetaka <address@hidden>
+
+2000-10-18 Derek Price <address@hidden>
+
+ * client.c (connect_to_pserver): added a close brace the lack of which
+ was preventing compilation when gssapi was enabled. Removed a
+ redundant check for HAVE_KERBEROS.
+
+2000-10-18 Derek Price <address@hidden>
+
+ * root.c (normalize_cvsroot): removed references to free_port_s and the
+ now useless call to free now that port_s is on the stack. Thanks to
+ Jon Miner.
+
+2000-10-18 Derek Price <address@hidden>
+
+ * root.c (normalize_cvsroot): remove calls to snprintf for
+ compatibility with M$ Windoze.
+
+2000-10-18 Derek Price <address@hidden>
+
+ * sanity.sh (crerepos-6a, crerepos-6a-r): fix a "?" in a regex & pipe
+ the output of a test to /dev/null since we don't know what error
+ messages specific rsh implementations will output.
+
+2000-10-17 Derek Price <address@hidden>
+
+ * cvs.h: added CVSroot_password variable. Provided prototypes for
+ get_port_number & normalize_cvsroot.
+ * client.c (get_port_number): Fixed an ANSI prototype I had included
+ for get_port_number.
+ * login.c (login, logout): Removed two checks for a non-null
+ CVSroot_username since parse_cvsroot now supplies a default in pserver
+ mode. allow for a password in CVSROOT
+ (get_cvs_passsword): return CVSroot_password if it was supplied
+ in the CVSROOT.
+ * root.c (parse_cvsroot): Changed CVSROOT spec from
+ :method:address@hidden/port:/cvsroot to
+ :method:[[user][:address@hidden:[port]]/cvsroot
+ Removed the xstrdup function since we'd rather have the error checking
+ from the version in subr.c anyhow. Moved some error messages which
+ looked like they would print the wrong error message after a failed
+ connect_to_gserver call.
+ (normalize_cvsroot): return a normalized CVSROOT for use in the
+ .cvspass file.
+ * sanity.sh (crerepos-6): fix a test which was expecting an old error
+ message.
+
+ * client.c (connect_to_pserver): Moved some error messages which looked
like they
+ would print the wrong error message after a failed connect_to_gserver
+ call.
+
+ * login.c (login): Paranoiacly zero a password in memory.
+
+2000-10-12 Derek Price <address@hidden>
+
+ * client.c (auth_server_port_number -> get_port_number, start_pserver,
+ start_tcp_server): use a port specified in CVSROOT instead of the
+ default port. Failing that, use the CVS_CLIENT_PORT environment
+ variable.
+ * cvs.h: Added global CVSroot_port & renamed auth_server_port_number.
+ * root.c (parse_cvsroot): Parse the new CVSROOT format properly.
+ Incidentally reformated some error messages for uniformity and
+ readability.
+ * sanity.sh (crerepos): fix two tests which were now expecting the
+ wrong error message.
+
+2000-10-11 Larry Jones <address@hidden>
+
+ * server.c (pserver_authenticate_connection): Fix stupid mistake
+ in previous change.
+
+2000-10-11 Derek Price <address@hidden>
+
+ * main.c (main): Dispose old CVSroot when parsing a '-d' option if
+ free_CVSroot is set.
+ * root.c (parse_cvsroot): remove references to 'cvsroot_parsed', a
+ static boolean I expect hasn't been used since CVS learned to handle
+ multiple CVSROOTs.
+
+2000-10-10 Larry Jones <address@hidden>
+
+ * server.c (print_error): Make up a message if strerror fails.
+
+ * server.c (pserver_authenticate_connection): Give a real error
+ message for an invalid repository.
+
+2000-10-06 Derek Price <address@hidden>
+
+ * add.c (add): Made quiet mode affect some warning messages as seemed
+ appropriate. Specifically, some of the messages which a user might
+ want to ignore so they don't have to be quite so specific on the
+ command line: files added twice, files already in the repository and
+ check out properly (i.e. but picked up by 'cvs add *'), & files which
+ are readded in place of a dead revision or onto a branch. '-q' will
+ not change the non-zero exit code for the cases where at least one
+ passed in file name was already in the Entries file. There seems to
+ be a precedent in remove.c.
+ * remove.c (cvsremove): switched the "use cvs ci to make these changes
+ permanent message" to only print w/o '-Q' to match the new behavior of
+ add. This seems appropriate as '-Q' is defined to restrict messages
+ to critical errors.
+ * sanity.sh (adderrmsg): Added some tests for the above behavior.
+
+2000-10-05 Larry Jones <address@hidden>
+
+ * client.c (call_in_directory): Create CVSADM directory if it doesn't
+ exist in the directory. This makes client/server work more like
+ standalone when checking out into an existing (non-CVS) directory.
+ * sanity.sh (dirs2, conflicts3, toplevel): Update to match.
+
+2000-10-03 Larry Jones <address@hidden>
+
+ * filesubr.c (get_homedir): Ignore $HOME when running in server mode.
+
+2000-10-02 Larry Jones <address@hidden>
+
+ * cvs.h: Define (and use) T_PATCH as a valid file classification
+ even when SERVER_SUPPORT isn't defined -- it simplifies the code.
+ * classify.c (Classify_File): Ditto.
+ * commit.c (check_fileproc): Ditto.
+ * status.c (status_fileproc): Ditto.
+ * update.c (update_fileproc): Ditto.
+ * tag.c (check_fileproc): Accept T_PATCH in addition to T_CHECKOUT.
+ * sanity.sh (tagc-10): Update to match.
+
+2000-09-29 Larry Jones <address@hidden>
+
+ * client.c (get_responses_and_close): Reset server_fd to -1 after
+ shutting down.
+ (Reported by Joerg Thoennes <address@hidden>.)
+
+2000-09-27 Larry Jones <address@hidden>
+
+ * commit.c (commit): Don't sleep before returning in server mode,
+ just let the client do it.
+ * update.c (do_update): Ditto.
+
+ * sanity.sh (find_tool): Correct method of checking for GNU tools.
+
+ * checkout.c (checkout_proc): Match up user directories with
+ repository directories instead of using Emptydir.
+ * sanity.sh (cvsadm, emptydir): Update to match.
+
+2000-09-19 Larry Jones <address@hidden>
+
+ * version.c: Push version number to 1.11.0.1.
+
+ * version.c: Version 1.11.
+
+2000-09-07 Larry Jones <address@hidden>
+
+ * Makefile.in: Use @bindir@, @libdir@, @infodir@, and @mandir@
+ from autoconf.
+
+2000-08-23 Larry Jones <address@hidden>
+
+ * mkmodules.c (init): Create an empty val-tags file if it doesn't
+ already exist to avoid problems with users not having sufficient
+ permissions to create it later.
+
+2000-09-06 Jim Kingdon <address@hidden>
+
+ * main.c (lookup_command_attribute): Add "release" to commands
+ which can be done by a read-only user.
+
+2000-08-23 Larry Jones <address@hidden>
+
+ * repos.c (Name_Repository): Use pathname_levels to detect attempts
+ to get above the repository instead of checking for leading ..
+ which isn't reliable.
+ * sanity.sh (multiroot3-12 to multiroot3-15): New tests for above.
+
+2000-08-21 Larry Jones <address@hidden>
+
+ * rcs.c (expand_keywords): Handle the unusual case of log == NULL.
+ (Reported by Craig Metz <address@hidden>.)
+
+2000-08-01 Larry Jones <address@hidden>
+
+ * subr.c (pathname_levels): Fix bug that miscounts adjacent
+ slashes.
+ (Patch submitted by Tanaka Akira <address@hidden>.)
+
+ * loginc.c (login): If available, use getpassphrase instead of
+ getpass to support long passwords on Solaris.
+
+2000-07-28 Larry Jones <address@hidden>
+
+ * server.c (server_noop): Avoid do_cvs_command() overhead.
+ (requests): Make noop RQ_ROOTLESS.
+
+2000-07-27 Noel Cragg <address@hidden>
+
+ * root.c (parse_cvsroot): change fork method to behave like other
+ remote methods -- let the server check that the repository
+ directory is an absolute pathname.
+
+2000-07-27 Larry Jones <address@hidden>
+
+ * lock.c (set_lock): Include actual lock directory in error message.
+ * sanity.sh (multiroot3-10): Change to match.
+
+ * sanity.sh (client-3): Allow for a potential "broken pipe".
+
+2000-07-26 Larry Jones <address@hidden>
+
+ * commit.c (commit_filesdoneproc): Flush stdout before running script.
+ * modules.c (do_module): Ditto.
+ * update.c (update_dirleave_proc): Ditto.
+ * server.c (do_cvs_command): Give input from the protocol pipe
+ precedence over input from stdout/stderr. There's no particularly
+ good justification for this other than helping to avoid out-of-order
+ messages in sanity.sh.
+
+ * admin.c (admin_usage): Add the supported options.
+
+ * sanity.sh (info): Try to avoid out-of-order messages.
+
+ * sanity.sh (info): Fix problems when running twice in a row.
+
+2000-07-17 Larry Jones <address@hidden>
+
+ * sanity.sh (modules5-7, cvsadm-1e, emptydir-2): Allow for a nil
+ commit (can happen if the test is run twice in a row).
+
+2000-07-19 Pavel Roskin <address@hidden>
+ and Larry Jones <address@hidden>
+
+ * mkmodules.c (config_contents): Add a commented out example for
+ LockDir. Don't suggest PreservePermissions unless it's enabled.
+
+2000-07-17 Larry Jones <address@hidden>
+
+ * login.c (get_cvs_password): Handle malformed ~/.cvspass more
+ gracefully.
+
+2000-07-12 Larry Jones <address@hidden>
+
+ * sanity.sh (modules5): New tests for module programs.
+
+2000-07-11 Larry Jones <address@hidden>
+
+ * filesubr.c (copy_file, xcmp): Handle systems (like Plan 9) that
+ don't support mknod() and/or st_rdev.
+ * import.c (add_rcs_file): Ditto.
+ * rcs.c (RCS_checkout, RCS_checkin): Ditto.
+ * update.c (special_file_mismatch): Ditto.
+
+2000-07-10 Larry Jones <address@hidden>
+
+ * zlib.c (gunzip_and_write): Fix type clashes.
+
+ * main.c (main): Remove unused variables.
+
+2000-07-10 Jim Meyering <address@hidden>
+
+ When a command like `cvs update -kk -jT1 -jT2' creates a new file
+ (because it had the T2 tag, but not T1), the subsequent commit of
+ that just-added file would effectively set the admin `-kk' option
+ for that file in the repository.
+
+ * update.c (join_file): Rename global-shadowing local `options'
+ to `t_options'.
+ Set file-scoped global `options' to NULL just before
+ check-out.
+ * sanity.sh (join-admin): New test for this.
+
+2000-07-08 Larry Jones <address@hidden>
+
+ * version.c, cvs.h (version): New function.
+ * main.c (cmds[]): Add version command to invoke it.
+ (main): Also use it in -v.
+ * server.c (serve_version): New function.
+ (requests[]): Add version command to invoke it.
+
+2000-07-06 Karl Fogel <address@hidden>
+
+ * sanity.sh (pserver-14): remove this test for portability
+ reasons (it was only recently added for the 2000-07-04 change).
+
+2000-07-06 Larry Jones <address@hidden>
+
+ sanity.sh (modules-148): Don't test for specific revisions.
+
+ * main.c (main): Catch SIGABRT to try to clean up after assertion
+ failures. Don't bother SIG_register'ing Lock_Cleanup because
+ main_cleanup calls it indirectly anyway.
+ * patch.c (patch): Catch SIGABRT.
+ * rcs.c (rcs_internal_lockfile): Ditto.
+ * server.c (server): Ditto.
+
+ * fileattr.c (fileattr_write): Don't delete the unrecog_head list
+ when writing...
+ (fileattr_free): Delete it when freeing!
+
+2000-07-05 Larry Jones <address@hidden>
+
+ * admin.c (admin): Handle -t in client so reading from files works
+ correctly in client/server mode.
+ * sanity.sh (log2): Update to match.
+
+2000-07-04 Karl Fogel <address@hidden>
+
+ * server.c (pserver_authenticate_connection): use new
+ getline_safe() during authentication phase, to avoid a
+ denial-of-service attack in which client sends arbitrary
+ amounts of data with no newlines.
+ (Reported by <address@hidden>.)
+
+ * sanity.sh: new test pserver-14 for above.
+
+ * myndbm.c: #include getline.h.
+ (mydbm_load_file): pass new GETLINE_NO_LIMIT flag to getstr().
+
+2000-07-03 Larry Jones <address@hidden>
+
+ * sanity.sh (modules): Rewrite using dotest. Add "modules-"
+ prefix to test names.
+
+2000-06-28 Larry Jones <address@hidden>
+
+ * error.c (error_exit): Call rcs_cleanup () to release any rcs locks.
+ * rcs.c, rcs.h (rcs_cleanup): Make public, close file before trying
+ to remove (some systems won't remove open files).
+ (RCS_putdtree): Don't worry about cleaning up before call error
+ since it now does it for us.
+ (rcs_internal_lockfile, rcs_internal_unlockfile): Keep track of
+ lock file fd for rcs_cleanup ().
+
+ * client.c (handle_set_checkin_prog, handle_set_update_prog):
+ Just ignore the request when exporting.
+
+2000-06-27 Larry Jones <address@hidden>
+
+ * create_adm.c, cvs.h (Create_Admin): Add dotemplate argument.
+ Change all callers.
+ * checkout.c (checkout_proc): Don't create CVS/Template if
+ exporting.
+
+2000-06-26 Pavel Roskin <address@hidden>
+ and Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Only set CVS_Username if
+ AUTH_SERVER_SUPPORT is defined.
+
+2000-06-23 Larry Jones <address@hidden>
+
+ * client.c (send_dirent_proc): Don't allocate ignlist if you're
+ going to skip the directory (plugs memory leak).
+ (send_dirleave_proc): New function.
+ (send_files): Use it (plugs memory leak).
+ * root.c (root_allow_free): Plug memory leaks.
+ * server.c (serve_directory, serve_notify, check_password,
+ pserver_authenticate_connection): Ditto.
+ * update.c (update): Ditto.
+
+ This completes the memory leak shoot-out -- the Purify'ed version
+ of CVS now runs the entire test suite, both local and remote (except
+ for remote crerepos, which causes Purify to choke) with *no* memory
+ leaks.
+
+ * server.c (pserver_authenticate_connection): Don't free null pointer.
+
+2000-06-21 Larry Jones <address@hidden>
+
+ * client.c (update_entries, get_responses_and_close): Plug memory leaks.
+ * commit.c (find_fileproc, commit): Ditto.
+ * import.c (import): Ditto.
+ * log.c (cvslog): Ditto.
+ * recurse.c (start_recursion): Ditto.
+ * remove.c (cvsremove): Ditto.
+ * server.c (fd_buffer_initialize, server_notify, do_cvs_command): Ditto.
+ (fd_buffer_shutdown): New function.
+
+2000-06-20 Larry Jones <address@hidden>
+
+ * root.c (parse_cvsroot): Put the terminating NUL byte into the
+ string *before* copying it, not after. :-(
+
+2000-06-19 Larry Jones <address@hidden>
+
+ * main.c (main): Plug memory leaks.
+ * root.c (parse_cvsroot, set_local_cvsroot): Ditto.
+ * server.c (serve_root): Ditto.
+
+2000-06-16 Larry Jones <address@hidden>
+
+ * fileattr.c (fileattr_read): Plug memory leak.
+ * rcs.c (RCS_whatbranch): Ditto.
+ * update.c (update_dirleave_proc): Ditto.
+
+ * ignore.c (ign_dir_add): Duplicate string so caller can free.
+
+ * modules.c (do_module): Don't write into dbm's memory!
+
+2000-06-15 Larry Jones <address@hidden>
+
+ * checkout.c (checkout_proc): Fix non-ANSI code in call to
+ findslash(), minor cleanups.
+
+2000-06-14 Larry Jones <address@hidden>
+
+ * tag.c (val_direntproc): Return R_PROCESS instead of 0.
+
+ * client.c (update_entries): Fix type clash calling gunzip_and_write().
+ * server.c (receive_file): Fix type clash calling gunzip_and_write().
+ (server_updated): Fix type clash calling buf_output().
+ * error.c (error): Make buf char instead of unsigned char to avoid
+ type clashes.
+
+ * modules.c (do_module): Change callback_proc to pass argc by
+ value instead of by reference: callback procs shouldn't be
+ messing with the callers argc/argv, it makes correct memory
+ management impossible. Plug memory leaks.
+ * cvs.h: Change to match.
+ * checkout.c (checkout_proc): Ditto; use a local argv array instead
+ of messing with caller's.
+ * modules.c (callback_proc): Ditto.
+ * patch.c (patch_proc): Ditto; use a local argv array instead
+ of messing with caller's.
+ * rtag.c (rtag_proc): Ditto; use a local argv array instead
+ of messing with caller's.
+ * server.c (expand_proc): Ditto.
+ * subr.c (line2argv): Change initial argv_allocated back to 1.
+
+ * checkout.c (findslash): Fix non-ANSI code.
+
+ * sanity.sh (modes3): Fix test names.
+
+2000-06-13 Larry Jones <address@hidden>
+
+ * add.c (add): Plug memory leaks.
+ * admin.c (admin_fileproc): Ditto.
+ * checkout.c (build_dirs_and_chdir): Ditto.
+ * edit.c (editors_fileproc): Ditto.
+ * log.c (cvslog, log_parse_revlist, log_parse_date): Ditto.
+ * rcs.c (RCS_addaccess): Ditto.
+ * tag.c (check_fileproc): Ditto.
+ * vers_ts.c (Version_TS): Ditto.
+ * watch.c (watchers_fileproc): Ditto.
+
+2000-06-12 Larry Jones <address@hidden>
+
+ * rcs.c (rcsbuf_valword): Set rcsbuf->vlen to keep rcsbuf_valcopy()
+ from allocating more memory than needed for @ strings. Don't declare
+ unless PRESERVE_PERMISSIONS_SUPPORT (since not defined).
+
+ * rcs.c (RCS_abandon): New function to abandon changes.
+ * rcs.h: Declare it.
+ * admin.c (admin_fileproc): Use it instead of RCS_reparsercsfile.
+
+ * commit.c (commit_fileproc): Fix memory leaks.
+ * patch.c (patch_fileproc): Ditto.
+ * rcs.c (RCS_nodeisbranch, RCS_copydeltas): Ditto.
+ * tag.c (tag_fileproc): Ditto.
+ * update.c (update): Ditto.
+
+2000-06-09 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_reparsercsfile, RCS_fully_parse, getdelta,
+ RCS_getdeltatext): Handle newphrases with composite values.
+ (rcsbuf_getkey): Don't remove @s in composite values -- it makes
+ it impossible to parse the value! Set special flag to indicate
+ a composite value.
+ (rcsbuf_valcopy, rcsbuf_valpolish_internal): Handle composite values.
+ (putrcsfield): Write composite values.
+ (RCS_checkin): Set node types in other_delta list.
+ * hash.h: Add RCSCMPFLD.
+ * hash.c (nodetypestring): Ditto.
+
+ * rcs.c (getdelta): Never allocate space for value, just return
+ pointer into rcsbuf (fixes memory leaks). Use rcsbuf_getkey to
+ read a key and value and then parse the value if needed rather
+ than trying to read it in bits and pieces with rcsbuf_getid,
+ rcsbuf_getstring, and rcsbuf_getword.
+ (RCS_reparsercsfile): Change callers to compensate.
+ (rcsbuf_valcmp, rcsbuf_valword): New functions.
+ (rcsbuf_getid, rcsbuf_getstring, rcsbuf_getword): Deleted.
+ * sanity.sh (rcs3-1): Now get slightly different error message.
+
+2000-06-08 Larry Jones <address@hidden>
+
+ * main.c (usg): Update CVS home page URL.
+
+ * main.c (main): Provide an actual error message for an unknown
+ command in addition to the usage message.
+
+2000-06-07 Larry Jones <address@hidden>
+
+ * server.c (serve_root, dirswitch, serve_repository,
+ serve_static_directory, serve_sticky, receive_partial_file,
+ receive_file, serve_modified, server_write_entries, serve_notify,
+ serve_checkin_prog, serve_update_prog, server): Don't set
+ pending_error before calling alloc_pending, it makes it fail;
+ use alloc_pending instead of malloc when reasonable; be sure to
+ save errno before calling functions that might change it.
+ (Patch submitted by Dietmar Petras <address@hidden>.)
+
+2000-06-03 Larry Jones <address@hidden>
+
+ * commit.c (checkaddfile): Plug memory leak.
+ * rcs.c (RCS_checkin): Plug memory leaks.
+ * server.c (do_cvs_command): Plug file descriptor leaks.
+ * tag.c (check_fileproc): Plug memory leak.
+
+2000-05-26 Larry Jones <address@hidden>
+
+ * recurse.c (unroll_files_proc): Plug memory leak.
+
+ * recurse.c (addfile): Fix nonportable pointer cast.
+
+ * rcs.c (rcsbuf_getstring, rcsbuf_getword, getdelta): Plug memory
+ leaks.
+
+2000-05-25 Larry Jones <address@hidden>
+
+ * checkout.c (checkout, build_one_dir, checkout_proc): Move m_type
+ to file scope and use it instead of continually doing strcmp on
+ command_name.
+ (build_one_dir, checkout_proc): Don't allow export if CVSADM
+ directory already exists.
+
+2000-05-23 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_checkin, RCS_cmp_file): Plug memory leaks. (Patch
+ submitted by Chris G. Demetriou <address@hidden>.)
+
+2000-05-20 Ian Lance Taylor <address@hidden>
+
+ * client.c (connect_to_gserver): Handle server error messages
+ reasonably.
+
+2000-05-19 Larry Jones <address@hidden>
+
+ * server.c (requests): Make Global_option RQ_ROOTLESS so it can be
+ used with init.
+
+2000-05-18 Larry Jones <address@hidden>
+
+ * client.c (start_server): Don't do encryption, authentication,
+ compression, or case insensitivity when doing init because init
+ is ROOTLESS and they're not.
+
+ * client.c (connect_to_pserver): Include repository and username in
+ authorization failed message -- if a directory tree crosses multiple
+ repositories, it can be quite difficult for the user to figure out
+ which one is the problem.
+
+2000-05-17 Larry Jones <address@hidden>
+
+ * main.c (main): Use full set of options when looking for -f to
+ avoid misparsing options that take values (previously, -sVAR=foo
+ was incorrectly parsed as though it were -s -V -A -R -= -f -o -o
+ because it didn't know that -s takes a value).
+ * sanity.sh (info-6b): New test for above.
+
+ * sanity.sh (conflicts-status): Fix tests so they work remotely, too.
+
+2000-05-17 Jim Meyering <address@hidden>
+
+ * sanity.sh (TESTDIR): Fix braino in last change:
+ cd to /tmp before invoking pwd.
+
+ * sanity.sh: Set TESTDIR so that `make check' passes even when /tmp
+ is a symlink.
+ (join-36): Use $TESTDIR rather than hard-coding `/tmp/cvs-sanity'.
+ (conflicts-132): Remove unnecessary `rm aa'.
+
+2000-05-16 Jim Kingdon <address@hidden>
+
+ * cvs.h, checkout.c (safe_location): Make extern.
+ * import.c (import): Call it rather than reimplementing
+ (incompletely) the same check.
+
+2000-05-16 Larry Jones <address@hidden>
+
+ * rcs.h, subr.c (file_has_markers): Check for any of the three
+ conflict marker lines, not just one.
+ * sanity.sh (conflicts-status): New tests for above.
+ * sanity.sh: Revise to avoid tripping the above check when merging
+ changes into sanity.sh itself.
+
+2000-05-15 Larry Jones <address@hidden>
+
+ * update.c (join_file): When registering the result of the merge,
+ make sure that the version number is valid (vers->vn_rcs may be
+ null if the file doesn't exist on the branch yet). (Patch submitted
+ by Robert de Vries <address@hidden>.)
+ * update.c (join_file): Correct diagnostics (previous change was not
+ correct -- the file *does* exist in the specified revision, it just
+ doesn't exist in the sandbox).
+ * sanity.sh (import-113, join): New tests and changes for above.
+
+2000-05-04 Larry Jones <address@hidden>
+
+ * sanity.sh: Look for a useful id program. Since we're getting
+ the real username for some tests anyway, use it for all the
+ tests instead of a generic regular expression that may or may
+ not match the actual username.
+
+2000-05-04 Larry Jones <address@hidden>
+
+ * server.c: More error messages.
+
+2000-05-02 Donald Sharp <address@hidden>
+ and Larry Jones <address@hidden>
+
+ * history.c (report_hrecs): Added code to print out year instead of
+ just month/day.
+ * sanity.sh (basic2-64, history): Update to match.
+
+2000-04-19 Larry Jones <address@hidden>
+
+ * server.c (dirswitch): Set pending_error_text in addition to
+ pending_error to aid in problem determination.
+
+2000-03-23 Larry Jones <address@hidden>
+
+ * mkmodules.c (mkmodules): Return without doing anything if noexec
+ is set to avoid trashing existing files.
+
+2000-03-23 Larry Jones <address@hidden>
+
+ * main.c: Alphabetize cmds[] and cmd_usage[] and add server
+ commands to cmd_usage[].
+
+2000-03-21 Larry Jones <address@hidden>
+
+ * sanity.sh (client-1): May get "Broken pipe" message from the
+ "server" in addition to the expected output.
+
+2000-03-17 Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Set CVS_Username if it hasn't already
+ been set elsewhere. (Patch submitted by Gordon Matzigkeit
+ <address@hidden>).
+
+2000-03-13 Larry Jones <address@hidden>
+
+ * parseinfo.c: Add extern to logHistory declaration. (Reported by
+ <address@hidden>.)
+ (parse_config): Reformat logHistory code.
+
+2000-03-10 Larry Jones <address@hidden>
+
+ * add.c (add): Don't try to set cvsroot_len until after checking
+ for help only -- CVSroot_directory isn't set in that case.
+
+2000-03-03 Larry Jones <address@hidden>
+
+ * mkmodules.c (init): Use mkdir_if_needed to create CVSROOT/Emptydir
+ so we don't fail if run multiple times. (Reported by KOIE Hidetaka
+ <address@hidden>.)
+ * sanity.sh (1a): New test for above.
+
+2000-03-02 Larry Jones <address@hidden>
+
+ * main.c: Use identical #if's in the command table and the code
+ for pserver and kserver to prevent "peculiar" configurations from
+ having really perverse behavior because the command table entries
+ are present but the related code isn't.
+
+2000-03-01 Larry Jones <address@hidden>
+
+ * import.c (import): Don't allow importing the repository.
+ * sanity.sh (errmsg2-20, errmsg2-21): New tests for above.
+
+2000-03-01 Larry Jones <address@hidden>
+
+ * main.c (main): Update year in copyright message.
+
+2000-03-01 Larry Jones <address@hidden>
+
+ * logmsg.c (do_editor): Correct previous change.
+
+2000-02-29 Larry Jones <address@hidden>
+
+ * logmsg.c (do_editor): When reading temp file, check that message
+ buffer is large enough to hold the next line and expand if needed.
+
+2000-02-28 Larry Jones <address@hidden>
+
+ * commit.c (commit): Use get_file() to read log file correctly
+ and in text mode rather than binary mode.
+
+ * subr.c (get_file): Ignore bufsize if buf is NULL. Include
+ terminating NUL byte when estimating required buffer size.
+
+2000-02-28 Larry Jones <address@hidden>
+
+ * sanity.sh (find_tool): New function to replace duplicated code.
+
+2000-02-25 Larry Jones <address@hidden>
+
+ * import.c (add_rcs_file): Don't abort just because lstat fails.
+
+2000-02-16 Jim Meyering <address@hidden>
+
+ Avoid race condition whereby a catchable signal could
+ end up corrupting the repository.
+ * commit.c (checkaddfile): Put a critical section around the code
+ that handles the first commit on the trunk of a file that's already
+ been committed on a branch.
+ * cvs.h (Sig_inCrSect): Declare new function.
+
+2000-02-21 Karl Fogel <address@hidden>
+
+ * main.c (main): still check for repository, but not history file
+ (correction to 2000-02-18 change -- that's what I get for
+ believing the comment rather than the code).
+
+2000-02-21 K.J. Paradise <address@hidden>
+
+ * history.c mkmodules.c parseinfo.c: control which actions
+ get logged to the cvs history file via CVSROOT/config file
+ and LogHistory keyword. (John P Cavanaugh <address@hidden>)
+
+2000-02-18 Karl Fogel <address@hidden>
+
+ * history.c (history_write): don't die if history file not
+ writable, just warn (unless `really_quiet') and skip out.
+
+ * main.c (main): don't bother checking if history file is
+ writable.
+
+ * server.c (serve_root): same.
+
+2000-02-17 Larry Jones <address@hidden>
+
+ * sanity.sh (perms symlinks symlinks2 hardlinks): Don't run by
+ default since PreservePermissions code is now disabled.
+
+2000-02-17 Larry Jones <address@hidden>
+
+ * sanity.sh (import-113): Revise to match Jim Meyering's fix.
+
+2000-02-16 Larry Jones <address@hidden>
+
+ * add.c (add): Don't allow adding files or directories to Emptydir.
+ (Patch submitted by Chris Cameron <address@hidden>.)
+ * sanity.sh (emptydir): Revise (emptydir-7 and emptydir-8) for this.
+
+2000-02-16 Jim Meyering <address@hidden>
+
+ * update.c (join_file): Correct typo in diagnostic:
+ change `file %s is present...' to `file %s is not present...'.
+
+2000-02-10 Larry Jones <address@hidden>
+
+ * parseinfo.c (Parse_Info): Treat matching lines with bad expansions
+ as errors rather than just ignoring.
+
+2000-02-10 Larry Jones <address@hidden>
+
+ * edit.c (edit): Check for invalid characters in hostname and CurDir.
+ (Reported by "Andrew S. Townley" <address@hidden>.)
+ * sanity.sh (devcom2): New tests for above.
+
+2000-02-10 Larry Jones <address@hidden>
+
+ * cvs.h: Always #include "server.h" to prevent compile errors when
+ neither CLIENT_SUPPORT nor SERVER_SUPPORT is defined.
+ (Reported by "Crow, Ian" <address@hidden>.)
+ * log.c (send_one, send_arg_list): Only define when CLIENT_SUPPORT
+ is defined to prevent link errors.
+
+ * server.c (server): Always create a new temporary directory, don't
+ try to reuse an existing one since we might not have correct
+ permissions. Also, include directory name in error messages.
+
+2000-01-29 Jim Kingdon <http://developer.redhat.com/>
+
+ * ignore.c (ignore_files): Correctly set errno to 0 when we go
+ back to the top of the loop. Fixes spurious errors like "cvs
+ update: error reading current directory: No such file or
+ directory".
+
+2000-01-26 Larry Jones <address@hidden>
+
+ * run.c (run_exec): Conditionalize K.J.'s change so that it only
+ applies when SETXID_SUPPORT is defined since some platforms don't
+ have setegid().
+
+2000-01-26 Larry Jones <address@hidden>
+
+ * sanity.sh: Make TESTDIR earlier then use it to check for versions
+ of expr that don't work right with long expressions.
+
+ * sanity.sh (dotest_line_by_line): Have wc read from stdin so it
+ doesn't output the file name and confuse expr. Make the output a
+ bit less verbose and easier to read.
+
+2000-01-24 K.J. Paradise <address@hidden>
+
+ * run.c :> prevents a user from creating a privileged shell from the
+ text editor when the SETXID_SUPPORT option is selected. This came from
+ Bob Colle <address@hidden>, and is his completely.
+
+2000-01-22 Jim Kingdon <http://developer.redhat.com/>
+
+ * sanity.sh (emptydir): Add a case in which one might hope for a
+ non-Emptydir result, but which result?
+
+2000-01-18 Larry Jones <address@hidden>
+
+ * main.c (main): Allow -z0 to disable gzip compression.
+
+2000-01-17 Larry Jones <address@hidden> for
+ K.J. Paradise (address@hidden)
+
+ * version.c: Push version number to 1.10.8.1.
+
+ * version.c: Version 1.10.8.
+
+2000-01-17 Larry Jones <address@hidden>
+
+ * mkmodules.c (init): Create CVSROOT/Emptydir to avoid problems
+ with users not having sufficient permissions to create it later.
+
+2000-01-04 Larry Jones <address@hidden>
+
+ * client.c (get_responses_and_close): Simplify time-stamp race
+ avoidance code.
+ * commit.c (commit): Ditto.
+ * update.c (do_update): Ditto.
+ (Prompted by patch submitted by Pavel Roskin
+ <address@hidden>.)
+
+ * hardlink.c: sizeof (char) is 1, by definition.
+ * logmsg.c: Ditto.
+ * rcs.c: Ditto.
+
+2000-01-03 Karl Fogel <address@hidden>
+
+ * filesubr.c, subr.c (backup_file): moved this function from
+ filesubr.c to subr.c, at JimK's suggestion.
+
+2000-01-03 Jim Kingdon <http://developer.redhat.com/>
+
+ * sanity.sh (clean): Test the contents of the .#cleanme.txt.1.1
+ file, not just its existence.
+
+2000-01-03 Karl Fogel <address@hidden>
+
+ * cvs.h, filesubr.c (backup_file): use `const' for suffix too;
+ correct suffix length calculation and appending behavior; discard
+ unnecessary `void' cast. Thanks to Jim Meyering for noticing.
+
+2000-01-03 Larry Jones <address@hidden>
+
+ * sanity.sh (clean): Fix up expected output.
+
+2000-01-02 John P Cavanaugh <address@hidden>
+ and Karl Fogel <address@hidden>
+
+ New -C option to update: overwrites local changes with clean
+ copies from the repository. (This is an unreversion of the
+ 1999-12-10 change, further modified to work remotely.)
+
+ * client.h (BACKUP_MODIFIED_FILES): new #define.
+
+ * client.c (struct send_data): new element `backup_modified'.
+ (send_files): set above element if BACKUP_MODIFIED_FILES flag is
+ present.
+
+ * filesubr.c (backup_file): new function.
+
+ * cvs.h: prototype for new function `backup_file'.
+
+ * update.c (toss_local_changes): new file-scoped global.
+ (update): set toss_local_changes if -C flag seen. If
+ client_active, send "-C" to server, and set SEND_NO_CONTENTS and
+ BACKUP_MODIFIED_FILES flags before calling send_files().
+
+ (update_fileproc): if file is modified and toss_local_changes is
+ set, then back the file up and then check out a fresh copy from
+ the repository. Also, fixed indentation and formatting for a
+ particularly bad stretch of code near (but unrelated to) these
+ changes.
+
+ * sanity.sh: new test `clean', for update -C option.
+
+1999-12-29 Jim Kingdon <http://developer.redhat.com/>
+
+ * history.c (read_hrecs): st_blksize is unsigned long, not int.
+ This isn't just cosmetic - getting it wrong will cause coredumps
+ and such on 64 bit machines.
+
+ * import.c (import_descend), ignore.c (ignore_files): Placate gcc
+ -Wall by parenthesizing foo || (bar && baz).
+
+1999-12-24 Larry Jones <address@hidden>
+
+ * release.c (release): Use fputs to echo lines from update instead
+ of printf to avoid problems with lines containing "%". (Reported
+ by Jean-Luc Simard <address@hidden>.)
+
+ * history.c (read_hrecs): Allocate a single 2-block buffer instead
+ of allocating and freeing a buffer for each block.
+ (fill_hrec): Remove redundant code.
+ (select_hrec): Plug memory leak.
+
+1999-12-22 Larry Jones <address@hidden>
+
+ * history.c (history): For "modified" or "checkout", sort on
+ file if user specified -l, even if user also specified a date-
+ oriented flag.
+ * sanity.sh (history): Update to match; add new tests.
+
+1999-12-15 Pavel Roskin <address@hidden>
+ and Larry Jones <address@hidden>
+
+ * lock.c (lock_name): fixed assertion failure for the
+ top-level CVS directory when LockDir is used
+ * sanity.sh (lockfiles-9): new test for this case
+
+1999-12-11 Karl Fogel <address@hidden>
+
+ * Revert previous change -- it doesn't work remotely yet.
+
+1999-12-10 John P Cavanaugh <address@hidden>
+ and Karl Fogel <address@hidden>
+
+ * update.c: new -C option to update, overwrites local changes with
+ clean copies from the repository.
+ Also, fixed indentation and formatting for a particularly bad
+ stretch of code near these changes in update_fileproc().
+
+ * sanity.sh: test new update -C option.
+
+1999-12-10 Larry Jones <address@hidden>
+
+ * commit.c (remove_file): Call history_write with update_dir NULL
+ like Checkin() does for add and modify.
+ * sanity.sh (basic2-64): Update to match, add "R" records to expected
+ remote output.
+
+1999-12-09 K.J. Paradise (address@hidden)
+
+ * history.c, commit.c, sanity.sh: found (I think) final
+ cause of seg fault in history command. Also, added the "R"
+ history functionality. Fixed basic2-64 so it looks correct for
+ the change.
+
+1999-11-30 K.J. Paradise (address@hidden)
+
+ * history.c: fixed seg fault caused by 11-03 changes.
+ off by one in block memory allocations.
+
+1999-11-29 Karl Fogel <address@hidden>
+
+ * login.c (logout): free `tmp_name' when done.
+ Correct a comment.
+
+1999-11-29 Larry Jones <address@hidden>
+
+ * cvs.h, error.c, import.c: Rename fperror to avoid name clash
+ on LynxOS. (Reported by Markus Braun <address@hidden>.)
+
+1999-11-23 Larry Jones <address@hidden>
+
+ * checkout.c (checkout_proc): Split declaration and initialization
+ of rp to placate neurotic compilers that gripe about jumping past
+ an initialization, even when the variable is not subsequently used.
+
+1999-11-19 Larry Jones <address@hidden>
+
+ * server.c (switch_to_user): Correct setgid error messages.
+
+1999-11-19 Karl Fogel <address@hidden>
+
+ * edit.c (unedit_usage, unedit): new struct, use it. Now "cvs
+ unedit" prints an accurate usage message (formerly it printed the
+ message for "cvs edit", even though the two commands do not have
+ identical usages).
+
+1999-11-19 Larry Jones <address@hidden>
+
+ * history.c: Move -e documentation from Flags to Reports.
+ (history): Add -e to list of report types in error message.
+
+ * history.c (history): Process file arguments before client/server
+ processing so they get sent to the server.
+ * sanity.sh (history): New tests for above. (Also remove comments
+ about variable spacing -- history output is in variable-width
+ columns with exactly one space between.)
+
+1999-11-19 Larry Jones <address@hidden>
+
+ * sanity.sh: Reestablish check for running as root (using ``id -u''
+ instead of ``whoami'').
+
+ * sanity.sh(dotest, dotest_lit, dotest_fail, dotest_status,
+ dotest_sort): Eval the command so quoting and pipes work right.
+ (spacefiles, dirs, rcslib, modules, unedit-without-baserev,
+ ignore, rcs, rcs2, history, tagdate, pserver, server, server2)
+ Simplify various tests based on above.
+
+1999-11-19 Karl Fogel <address@hidden>
+
+ * mkmodules.c (init): make history file world-writeable after
+ creating it, since it needs to be writeable for virtually any
+ CVS operation.
+
+1999-11-10 Jim Kingdon <http://developer.redhat.com/>
+
+ * admin.c: Revert change to add -H command option. The help
+ invocation is "cvs -H admin" not "cvs admin -H" (see cvs.texinfo,
+ basicb-21 in sanity.sh; fix to cvs.1)
+
+1999-11-08 Jim Kingdon <http://developer.redhat.com/>
+
+ * log.c (cvslog): If client_active, send options to the server
+ based on our parsed options rather than trying to send the exact
+ strings specified (using canonical forms, like RFC822/1123
+ dates, in the protocol is just cleaner).
+ (send_one, send_arg_list): New functions, helpers for above.
+ * sanity.sh (logopt-6a): New test, for this fix.
+
+1999-11-09 K.J. Paradise <address@hidden>
+
+ * admin.c: made the -H option do what it is documented to
+ do. a
+
+1999-11-08 Tom Tromey <address@hidden>
+
+ * client.c (connect_to_gserver): Print more error text if gssapi
+ initialization fails. From Assar Westerlund <address@hidden>.
+
+1999-11-06 Larry Jones <address@hidden>
+
+ *sanity.sh(rcs3-5): Remote output can be out-of-order, so need a
+ more general pattern to match the assertion failure.
+
+1999-11-05 K.J. Paradise (address@hidden)
+
+ * history.c: Added a trap to verify that if a
+ read(file, buffer,blocksize) returns less than blocksize,
+ that we really are at the end of the file. I can't easily
+ come up with a test case where this code gets touched, so
+ it may cause problems. All sanity tests still pass though.
+
+1999-11-05 Jim Kingdon <http://developer.redhat.com/>
+
+ * sanity.sh (logopt): New test, for Larry's fix.
+ * sanity.sh (log-18a, rcs-15 to rcs-19): New tests, to test -d
+ and -r more thoroughly.
+
+1999-11-05 Larry Jones <address@hidden>
+
+ * log.c (cvslog): Fix -s and -d with spaces on client side.
+ (log_usage): Revert Karl's change once again.
+ sanity.sh(rcs3-5): No longer get different results from local
+ and client/server.
+
+1999-11-04 Karl Fogel <address@hidden>
+
+ * log.c (log_usage): Revert Jim Kingdon's reversion of my change
+ of 1999-11-03. Allowing a space between option and argument
+ results in lossage; here is a reproduction recipe: run this from
+ the top of a remote copy of the cvs source tree
+
+ cvs log -d '>1999-03-01' > log-out.with-space
+
+ and then run this (note there's no space after -d now):
+
+ cvs log -d'>1999-03-01' > log-out.no-space
+
+ The resulting files differ; furthermore, a glance at the output of
+ cvs shows that the first command failed to recurse into
+ subdirectories. Until this misbehavior can be fixed in the source
+ code, the documentation should reflect the true state of affairs:
+ if one simply omits the space, everything works fine.
+
+1999-11-04 Jim Kingdon <http://developer.redhat.com/>
+
+ * log.c (log_usage): Revert Karl's change regarding -d and
+ -s. A space is allowed (see sanity.sh for example).
+
+1999-11-03 K.J. Paradise (address@hidden>
+
+ * history.c: cleaned up my prior change a bit, per Larry Jones'
+ comments, and John O'Conner's additional comments about bits of
+ non MS-Visual C++ compliancy of my code.
+
+1999-11-04 Larry Jones <address@hidden>
+
+ * sanity.sh: Check that tr that correctly handles NULs; if not, try
+ to find a version that does; if none can be found, warn user.
+ Also fix warnings for defective expr.
+
+1999-11-04 Karl Fogel <address@hidden>
+
+ Changes for empty/random passwords in anon pserver access:
+
+ * server.c (check_repository_password): if password empty, grant
+ access no matter what password is received; this is so anon CVS no
+ longer requires a password but remains backwards-compatible with
+ all those clients out there.
+
+ * client.c (connect_to_pserver): proceed with login even if
+ password not found in .cvspass file -- just use empty string as
+ password. And if such a login fails, print a descriptive error.
+
+ * login.c (get_cvs_password): don't complain if file or password
+ not found. That condition is no longer a showstopper, now that
+ empty passwords are permissible.
+ Cleaned up conditional chaining a bit, too.
+
+ * sanity.sh (pserver-9, pserver-10, pserver-11, pserver-12,
+ pserver-13): new tests, about empty-password pserver access.
+
+1999-11-03 K.J. Paradise (address@hidden>
+
+ * history.c: modify parsing routines to parse the history
+ file a block at a time, rather than all at once. This allows
+ people with large history files and small amount of memory
+ to still get some functionality out of the history file.
+
+1999-11-03 Karl Fogel <address@hidden>
+
+ * log.c (log_usage): correct usage message for -d and -s options.
+ Because the space between the option letter and its argument has
+ been eliminated, I capitalized the argument portion to distinguish
+ it from the option letter. This makes it slightly inconsistent
+ with other such usage summaries, but at least it is now both
+ correct and readable.
+
+1999-10-22 Larry Jones <address@hidden>
+
+ * sanity.sh (dotest_sort): Old versions of tr don't understand \t
+ so use a literal tab instead.
+
+1999-10-21 Larry Jones <address@hidden>
+
+ * sanity.sh (dotest_sort): Convert any tabs in the output into spaces
+ before sorting to avoid POSIX.2 sort weirdness.
+ (import-106, importb-2): Change expected output per above.
+
+1999-10-18 K.J. Paradise <address@hidden>
+
+ Bug: users 'stan' and 'cartman' both have full read/write access
+ to the cvs repository. 'cartman' does a 'cvs admin -l foo.c'.
+ 'stan' then does a 'cvs admin -u foo.c'. The lock wouldn't be
+ removed, and no warning/error would be given. This is now fixed.
+ * rcs.c:(c.6157) remove caller/user check on the multiple lock
+ detection routines. Sanity.sh runs with no errors after this fix.
+
+1999-10-14 Larry Jones <address@hidden>
+
+ Make "cvs admin -e" (with no list of users) work:
+ * admin.c (admin): Remove error message.
+ (admin_fileproc): If no args for -e, call RCS_delaccess with NULL user.
+ * rcs.c (RCS_delaccess): Interpret NULL user as request to delete
+ entire access list.
+ * sanity.sh (admin-19a-*): Test.
+
+1999-09-29 Larry Jones <address@hidden>
+
+ * entries.c (Subdirs_Known): Use entfilename when opening CVSADM_ENTLOG
+ like everywhere else. Although this isn't strictly necessary (since
+ we immediately close it again), it keeps the code consistent and fixes
+ a bug where an open error reported the wrong file name.
+
+1999-09-16 Larry Jones <address@hidden>
+
+ * log.c (log_parse_revlist): Handle peculiar revision specs like
+ "-r.", "-r:", and "-r," correctly. (Thanks to Pavel Roskin
+ <address@hidden> for submitting a patch, this fix is
+ somewhat different.)
+ * sanity.sh (log): New tests for above.
+
+1999-09-15 Larry Jones <address@hidden>
+
+ * sanity.sh (basica-8b1): New test to check fix for bad diff options
+ causing cvs to crash.
+
+1999-09-02 Larry Jones <address@hidden>
+
+ * modules.c (do_module): Handle case where module definition has
+ options and special options but no directory; fix potential problems
+ running off beginning of string while stripping trailing blanks.
+ * sanity.sh (modules2): New tests for above.
+
+1999-08-26 Larry Jones <address@hidden>
+
+ * lock.c (lock_name): Remove side-effects from assert() expression
+ since they won't occur if NDEBUG is defined (not that that's a good
+ thing to do). (Reported by KOIE Hidetaka <address@hidden>.)
+
+1999-08-25 Larry Jones <address@hidden>
+
+ * sanity.sh: Use "${AWK}" instead of "awk" to make it easier for
+ people to use nawk/gawk/etc.; use an explicit "-print" with find
+ since some older version don't assume it; rename tests to avoid
+ duplicate importc-8. (Changes along these lines suggested by
+ Chris Cameron <address@hidden>.)
+
+1999-08-24 Larry Jones <address@hidden>
+
+ * commit.c (check_fileproc): Don't crash when a file has no
+ repository, just treat it as unknown. (Reported by Stefaan
+ Diericx <address@hidden>.)
+ * sanity.sh (errmsg2): New tests, for this fix.
+
+1999-08-18 Larry Jones <address@hidden>
+
+ * update.c (special_file_mismatch): Initialize *_hardlinks to
+ avoid trying to free garbage later on. (Reported by Jan
+ Scheffczyk <address@hidden>.)
+
+1999-08-17 Larry Jones <address@hidden>
+
+ * sanity.sh (basicc-11): Older versions of sh don't understand
+ ``if ! test...''. (Patch submitted by David J N Begley
+ <address@hidden>.)
+
+1999-08-17 Larry Jones <address@hidden>
+
+ * client.c, hardlink.c, hash.c, hash.h, main.c, recurse.c: Change
+ enum constant UNKNOWN to avoid conflicts on HPUX 11.0. (Reported
+ by Laurent Duperval <address@hidden>.)
+
+1999-08-16 Larry Jones <address@hidden>
+
+ client.c: Eliminate redundant #if. (Patch submitted by Assar
+ Westerlund <address@hidden>.)
+
+1999-07-30 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_checkin): Terminate cleanly if RCS_addbranch fails
+ rather than blithely continuing on and crashing.
+ * sanity.sh (basica): New tests, for this fix.
+
+1999-07-29 Larry Jones <address@hidden>
+
+ * import.c (add_rcs_file): change "cannot lstat" message to include
+ userfile (the actual file causing the problem) instead of user
+ (which may or may not be the same).
+
+1999-07-29 Eric Sink <address@hidden>
+
+ * version.c: Push version number to 1.10.7.1.
+
+ * version.c: Version 1.10.7.
+
+1999-07-28 Eric Sink <address@hidden>
+
+ * sanity.sh: before running basicc-11, we need to see if
+ the cwd has been deleted (by basicc-8). If so, we
+ recreate it to allow basicc-11 to proceed. This may be
+ something that only happens under the Linux 2.2 kernel.
+
+1999-07-18 Karl Fogel <address@hidden>
+
+ * edit.c (notify_do): chop newline, if any, from the value
+ obtained from CVSROOT/users. Otherwise it just gets passed along
+ in the argument to the notification program (usually mail), which
+ will misinterpret it as signifying the end of the command.
+
+1999-07-19 Larry Jones <address@hidden>
+
+ * rcs.c (RCS_delete_revs): In the WIN32 kludge, be sure that the result
+ of RCS_getexpand is not NULL before trying to use what it points to.
+ (Patch submitted by Timothy L. Taylor <address@hidden>.)
+
+1999-07-16 Tom Tromey <address@hidden>
+
+ * admin.c (admin): Allow `-k' options to be used unrestricted.
+
+1999-06-23 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (symlinks2): New test, for symlinks in working
+ directory without PreservePermissions. This test (modulo a few
+ details not relevant to testing whether we are following symlinks)
+ worked remote as of now, or either remote or local for CVS 1.9.
+ * subr.c (get_file): Revert 1998-02-15 change to special-case
+ symlinks. This makes the above test work local too.
+ * rcs.c (RCS_checkin): Move the logic to handle special-case
+ symlinks (and other files other than regular files) here, and make
+ it only happen if PreservePermissions is on.
+
+1999-06-18 Larry Jones <address@hidden>
+
+ * sanity.sh (devcom3-9a): Be less specific about the expected
+ error message (BSD/OS 4.0 has a bug that can cause exec* to fail
+ with EACCES instead of ENOENT).
+
+1999-06-08 Larry Jones <address@hidden>
+
+ * sanity.sh (diff-4, dirs2-10, tagf-13, importc-7, conflicts2-142b8):
+ Use ${PROG} instead of "cvs".
+
+1999-06-05 Jim Kingdon <http://www.cyclic.com>
+
+ * recurse.c (do_recursion, do_dir_proc): Make the SERVER_ACTIVE
+ #ifdef be only around the check for server_active. Modulo a few
+ cosmetic tweaks, same as a patch submitted by Johannes Stezenbach
+ of propack-data.de.
+
+1999-06-01 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh: Add comment about rcs2-7 failures on certain days.
+
+ Make "cvs status -v" on a removed file work:
+ * status.c (cvsstatus): Reindent the client code.
+ (status_fileproc): Don't need a CVS/Entries listing to show the
+ tags.
+ * sanity.sh (rmadd2): New test rmadd2-16 tests the existing
+ behavior with "cvs log"; new test rmadd2-17 tests the new behavior
+ with "cvs status".
+
+ * sanity.sh (basicc): To match no output in dotest, put the empty
+ regexp first. Remove tests which check that first-dir exists,
+ since that isn't true in the case where the OS let us delete it.
+ (dotest_internal): Fix so that things work with two regexps, with
+ an empty one first.
+
+1999-05-28 Larry Jones <address@hidden>
+
+ * sanity.sh (server-4): Replace bogus directory with real one since
+ the server now checks it.
+
+1999-05-27 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (spacefiles): Clean up -c, top, and -b at end.
+ (spacefiles, files): Fix bad references to CVSROOT_DIRNAME.
+
+ Fix two problems pointed out by Olaf Kirch of swb.de/caldera.de:
+ * server.c (outside_root): New function, contains expanded version
+ of code from serve_directory.
+ (serve_directory): Call outside_root.
+ (outside_dir): New function
+ (serve_modified, serve_is_modified, serve_notify,
+ serve_questionable, serve_unchanged): Call outside_dir.
+ * sanity.sh (server2): New tests, for these fixes.
+
+1999-05-26 Jim Kingdon <http://www.cyclic.com>
+
+ * cvs.h, subr.c (xmalloc): Return void* not char*, like xrealloc
+ has done for some time.
+ * modules.c (do_module): If we find the module as a directory/file
+ (rather than in the modules file), skip a bunch of processing
+ which was unnecessary and also broken in most of the cases
+ now tested for by the spacefiles sanity.sh test.
+ * sanity.sh (spacefiles): New test, for specifying filenames
+ (containing spaces, or starting with '-', or starting with '/') to
+ "cvs co".
+
+1999-05-25 Jim Kingdon <http://www.cyclic.com>
+
+ * client.c (update_entries): Make the old DONT_USE_PATCH code the
+ only code. This means that if people are still on CVS 1.9
+ servers, then CVS will fall back to transferring entire files.
+ This is better than looking for an external "patch" program which
+ causes no end of troubles (especially on Windows, but someone just
+ posted to info-cvs about a problem with the Solaris patch). (This
+ change was run by devel-cvs and feedback was positive).
+
+ * subr.c (xmalloc, xrealloc): The new error.c does not support
+ %lu; use sprintf instead.
+
+1999-05-25 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * sanity.sh (server): Escaped a few more newlines in
+ another awk script. Solaris awk still don't like 'em.
+
+1999-05-25 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+ and Jim Kingdon
+
+ * log.c: Remove comment which said "you can delete [this line]"
+ and which stuck around for over 3 years.
+ * sanity.sh (errmsg2 & tagdate): Added tests to prove the
+ current functionality with respect to combining -r and -D.
+
+1999-05-20 Larry Jones <address@hidden>
+
+ * server.c (pserver_authenticate_connection): Previous changes
+ broke verify_and_exit (reported by Robert Fitzsimons, thanks).
+ * sanity.sh (pserver): New tests pserver-7 and pserver-8 for this.
+
+1999-05-18 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * sanity.sh (keyword2): Escaped a newline in an awk script.
+ Apparently Solaris awk don't like 'em.
+
+1999-05-18 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (basicc): Allow the behavior whereby unlink(".")
+ succeeds. Reported by Jeremy Buhler and Pavel Roskin.
+
+1999-05-17 Steve Cameron of Compaq
+
+ * sanity.sh: Modified to no longer use "test -e" for existence
+ test as it has turned out to be not portable enough. Instead use
+ "test -f", "test -d", etc.
+ [SCO Unixware 7 apparently doesn't always support it -kingdon]
+
+1999-05-17 Jim Kingdon <http://www.cyclic.com>
+
+ * version.c: Push version number to 1.10.6.1.
+
+ * version.c: Version 1.10.6.
+
+1999-05-16 Jim Kingdon <http://www.cyclic.com>
+
+ * update.c (patch_file): When we are passing vn_rcs to
+ RCS_checkout, pass vn_tag as well.
+ * sanity.sh (keyword): In test keyword-22, test for the fixed
+ behavior rather than the buggy behavior. Adjust keyword-23. Add
+ test keyword-24, to see whether keyword-23 really worked.
+
+1999-05-12 Larry Jones <address@hidden>
+
+ * sanity.sh (pserver-4, pserver-5): Bogus error messages from
+ non-root initgroups on some 4.4BSD derived systems now show up
+ in different places in the output.
+
+1999-05-12 Jim Kingdon <http://www.cyclic.com>
+
+ * import.c (import): Don't allow the user to supply a repository
+ directory which takes us out of the cvsroot.
+ * sanity.sh (importc): New tests importc-10 to importc-12, for this.
+
+1999-05-11 Larry Jones <address@hidden>
+
+ * server.c (serve_notify): Allocate enough memory to hold the
+ "misformed Notify request" message in pending_error_text.
+
+1999-05-11 Jim Kingdon <http://www.cyclic.com>
+
+ * server.c (switch_to_user): Ignore EPERM from initgroups. Fixes
+ pserver-4 in testsuite.
+ (pserver_authenticate_connection): Only print "I LOVE YOU" after
+ switch_to_user has come back successfully.
+
+ * server.c (pserver_authenticate_connection): Call error_exit
+ rather than reinventing the wheel ourselves.
+ (switch_to_user): Check for errors from setuid, setgid, and
+ initgroups. Fix the #ifdef's (the previous code would skip the
+ setuid call if SETXID_SUPPORT).
+
+1999-05-10 Jim Kingdon <http://www.cyclic.com>
+
+ * server.c (serve_notify), edit.c (notify_do): Check for
+ and reject characters which will get confused with delimiters.
+ * sanity.sh (server): New tests server-7 through server-15 test
+ for this and for other notify behaviors.
+
+ * rcs.c (RCS_tag2rev): Also look for a physical branch with
+ RCS_getversion.
+ * sanity.sh (tagf): Adjust tagf-12 and following tests to test for
+ the fixed behavior rather than the broken behavior.
+
+1999-05-07 Jim Kingdon <http://www.cyclic.com>
+
+ * server.c (server_notify): Also set last_node to NULL.
+ * sanity.sh (server): New tests server-6 and server-7, for this.
+
+1999-05-05 Jim Kingdon <http://www.cyclic.com>
+
+ * rcs.c (rcs_internal_lockfile): Remove unused variable lockfile.
+
+ * add.c (add): Look for directories with the same name in a
+ different case where appropriate (analogous to fopen_case).
+ In client code, add comment about how this doesn't do quite
+ everything.
+
+1999-05-03 Jim Meyering <address@hidden>
+
+ Remove rcs-style ,file, lock files upon signal.
+ * rcs.c (rcs_lockfile): New file-scoped global.
+ (rcs_cleanup): New function (similar to patch_cleanup).
+ (rcs_internal_lockfile): Register rcs_cleanup the first time this
+ function is called. Rename uses of local `lockfile' to refer to new
+ global, `rcs_lockfile'. Don't free the lock file name string, now
+ that it's global.
+ (rcs_internal_unlockfile): Rename `lockfile', as above, and carefully
+ free and NULL-out the global, rcs_lockfile.
+
+1999-04-30 Jim Kingdon <http://www.cyclic.com>
+
+ * rcs.c (annotate_fileproc): Don't cast NULL in passing it to
+ RCS_deltas. Because there is a prototype in scope the cast is
+ unnecessary (per HACKING's ANSI C or SunOS4 rule), and in fact it
+ was causing failures on UNICOS because it cast to size_t instead
+ of size_t*. (Thanks to Dean Kopesky for reporting this).
+
+1999-04-29 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh: If invoked without any arguments, print a usage
+ message (thanks to Pavel Roskin for a report/patch).
+
+ * run.c (piped_child): Make the error messages more verbose.
+ (close_on_exec): Reindent.
+ * sanity.sh (devcom3): Several errors are possible in devcom3-9a.
+ Adjust for change to piped_child error message.
+
+1999-04-28 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (devcom3): Add some tests of the CVS/Notify file and
+ disconnected "cvs edit".
+
+ * main.c (opt_usage): Remove -b.
+
+1999-04-20 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * rcs.c (RCS_delete_revs): RCS_delete_revs uses an
+ RCS_checkout call to get a new copy of a revision to be
+ used internally after old revisions were deleted and it was
+ performing keyword substitutions. This munged all the
+ the revisions of the file on the branch containing the
+ deleted revisions and its sub-branches, as the original they
+ were being patched from was incorrect. Corrected this by
+ passing in "-ko" as an option to RCS_checkout.
+ * sanity.sh (keywordlog): modified this test to verify the
+ correct behavior of 'cvs admin -o'.
+ [Fixed use of \$ in keywordlog test; added code in RCS_delete_revs
+ to abort on binary file on Windows -kingdon]
+
+1999-04-21 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+ and Jim Kingdon
+
+ * tag.c (tag_check_valid): A bug was causing CVS to spin
+ indefinately when -j:<date> was specified. CVS now returns
+ an error.
+ * sanity.sh: Added a test (tagdate-12) to test this.
+
+1999-04-19 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (backuprecover): Clean up the repository at the end.
+
+1999-04-18 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * sanity.sh added a test (backuprecover) to test cvs behavior
+ with a repository that is out of date relative to the
+ developer's workspaces.
+ [Fix --keep code; move test to "Repository Storage" section since
+ it doesn't really exercise the diff/diff3 library. -kingdon]
+
+1999-04-13 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * sanity.sh (diff): Tests to verify correct operation of
+ the --ifdef parameter to cvs diff.
+ [indentation fixed -kingdon].
+
+1999-04-13 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+ for Noah Friedman <address@hidden>
+
+ * diff.c (diff): Put "--ifdef=" in opts string, not "-D"; the
+ latter is confused by pserver for a date spec.
+
+1999-04-14 Jim Kingdon <http://www.cyclic.com>
+
+ * fileattr.h: Adjust comments to reflect the official version of
+ the fileattr format now being in cvs.texinfo.
+
+1999-04-05 Jim Kingdon
+
+ * sanity.sh (watch5): Remove nonstandard --keep code. Don't pass
+ -f to rm when cleaning up (that tends to mask bugs). Add watch5
+ to list of tests at start. Add comment explaining why we consider
+ the behavior we test for the right one. Rename a few tests which
+ had been erroneously named watch6* instead of watch5*.
+ * client.c (update_entries): Add comment with brief discussion of
+ whether there is a better way.
+
+1999-04-05 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * client.c (update_entries): Only call mark_up_to_date
+ (which deletes the CVS/Base/<filename> file for watched
+ and edited files) on commit.
+ * sanity.sh: Make sure the CVS/Base/<filename> file for
+ a watched and edited file is not removed on a status or
+ update of a touched/unmodfied file.
+
+1999-03-30 Larry Jones <address@hidden>
+
+ * client.c (get_responses_and_close), commit.c (commit),
+ update.c (do_update): If the sleep(1) call returns prematurely
+ (due to the way wakeup is scheduled or receiving a signal), do
+ it again.
+
+1999-03-26 Jim Kingdon <http://www.cyclic.com>
+
+ * server.c (server): Add comment about Gzip-stream vs. RQ_ROOTLESS.
+
+ * sanity.sh (modules3-11b): Adjust exact text of error message to
+ reflect 1999-03-24 change to dirswitch.
+
+1999-03-25 Jim Kingdon <http://www.cyclic.com>
+
+ * admin.c (admin): Make argument to -e optional, to match the
+ documentation.
+ * sanity.sh (admin-19a-2): Test for this.
+
+ * server.c (serve_root): Update comment about checking for missing
+ Root request.
+
+1999-03-24 Jim Kingdon <http://www.cyclic.com>
+
+ * server.c (dirswitch): Also check dir here, similar to
+ what server_pathname_check does for other cases.
+ * sanity.sh (files): Adjust files-14 to test for this.
+
+1999-03-24 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+ and Jim Kingdon
+
+ * sanity.sh: added a test (files-13) to test .. indirection
+ in a path and another (files-14) to make sure we still fail
+ out when the '..' indirection takes us into the $CVSROOT
+ directory or beyond.
+
+1999-03-24 Larry Jones <address@hidden>
+
+ * rcs.c: Change enum constants ADD and DELETE to something less
+ likely to run into conflicts.
+
+1999-03-21 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (tagf): New test, tests for moving a branch tag to a
+ non-branch tag and trying to recover.
+
+1999-03-12 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (branches): Tweak test branches-5 to test the case in
+ which one modifies a file and then branches it.
+
+1999-03-09 John Bley of duke.edu
+
+ * mkmodules.c (filelist): Missed a NULL in this struct (should
+ have 3 members, only had 2).
+
+1999-03-07 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (Index): Rename new test from rm_CVS/Root to rmroot
+ (we don't have a formal rule about funky punctuation in test names
+ but both underscore and a slash is too funky for me :-)).
+ Reindent a few tests which were off.
+
+ * root.c: Remove the sentence which had the improper English;
+ there isn't really a need for that sentence and it isn't
+ particularly accurate any more.
+
+1999-02-27 Derek Price
+ <http://www-personal.engin.umich.edu/~oberon/resume.html>
+
+ * sanity.sh: Added rm_CVS/Root test to test that CVS uses
+ $CVSROOT rather than dumping core when running remotely and
+ the admin file CVS/Root is deleted from the workspace.
+
+ Also, altered a few 'cvs commit' 's in regular expressions to
+ fit the .${PROG} commit. portability syntax.
+
+ * recurse.c: Stopped CVS from dumping core in the case tested
+ above.
+
+ * root.c: Fixed somebody's improper english.
+
+1999-02-25 Larry Jones <address@hidden>
+
+ * sanity.sh (keyword2-12): Use ${QUESTION} instead of ? in the
+ expected result.
+
+1999-02-24 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (keyword2): Restore the original \\\$ instead of $.
+ The latter ends up working due to various kludgy semantics in the
+ shell and regular expressions, but the former is cleaner.
+
+ * sanity.sh (keyword2): Protect keywords against accidental
+ expansion in sanity.sh itself (most occurrences had this, but not
+ all).
+
+1999-02-23 Derek Price <http://www.cyclic.com>
+ and Jim Kingdon.
+
+ * sanity.sh (keyword2): New test, tests for merging with -kk.
+
+1999-02-22 Jim Kingdon <http://www.cyclic.com>
+
+ * version.c: Ease version number to 1.10.5.1.
+
+ * version.c: Version 1.10.5.
+
+1999-02-18 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (files): New test, for a relatively obscure spurious
+ "Up-to-date check failed" in client/server.
+
+ * main.c (lookup_command_attribute): Don't check for "history"
+ twice.
+
+1999-02-17 Jim Kingdon <http://www.cyclic.com>
+ and Hallvard B Furuseth
+
+ * root.c (parse_cvsroot): Rearrange ifdefs to squelch possible
+ warnings about statement not reached.
+
+1999-02-16 Jim Kingdon <http://www.cyclic.com>
+
+ * recurse.c (start_recursion): If we are skipping the current
+ directory (due to it being from the wrong repository), also adjust
+ the arguments we send to the server accordingly (like we already
+ do for the case in which there is no CVS directory).
+ * sanity.sh (multiroot4): New test, for this. All these tests had
+ passed locally, but remote multiroot4-12 tests for this fix.
+ (multiroot): Adjust multiroot-diff-1, multiroot-update-2,
+ multiroot-tag-1, multiroot-status-1, multiroot-update-3, and
+ multiroot-log-1 to reflect the cosmetic change this produces (one
+ less "Diffing ." message).
+ (multiroot2): multiroot2-8 likewise.
+
+1999-02-10 Jim Kingdon <http://www.cyclic.com>
+
+ * tag.c (cvstag): Don't pass SEND_NO_CONTENTS if -c specified.
+ * sanity.sh (tagc): New test, for various tag -c behaviors.
+ Test tagc-6 tests for this fix.
+
+1999-02-09 Jim Kingdon <http://www.cyclic.com>
+
+ * error.c (error): Rewrite to no longer use vasprintf (see
+ ../lib/ChangeLog for rationale). Note the slight change in
+ interface - callers which want %8.8s or similar formats need to
+ call sprintf.
+ * lock.c (lock_wait, lock_obtained): Use sprintf.
+
+1999-02-08 Jim Kingdon <http://www.cyclic.com>
+
+ * rcs.c (RCS_delete_revs): Pass -a to diff_exec.
+ * sanity.sh (binfiles3): New tests binfiles3-9 through
+ binfiles3-13 test for this fix.
+ * sanity.sh (binfiles): New tests binfiles-o4 and binfiles-o5
+ (which don't test this bug, just on general principles).
+
+1999-02-04 Jim Kingdon <http://www.cyclic.com>
+
+ * lock.c (lock_name): Permissions of directories in LockDir
+ shouldn't depend on the umask.
+ * sanity.sh (lockfiles): Set umask and CVSUMASK, to test for this.
+
+1999-02-01 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (keywordlog): New tests keywordlog-22 and
+ keywordlog-23 test keyword expansion and $Log. Adjust other tests
+ so that revisions differ more from each other, so this is a
+ better test.
+
+1999-01-29 Jim Kingdon <http://www.cyclic.com>
+
+ * commit.c (checkaddfile): If options is "", treat it the same as
+ NULL. Centralize this check, and the one for it starting with
+ "-k", at the start of the function.
+
+ * rcs.c, rcs.h (RCS_setexpand): New function.
+ * admin.c (admin_fileproc): Access keyword expansion field via
+ RCS_getexpand and RCS_setexpand, rather than directly.
+ * commit.c (checkaddfile): When resurrecting, set the keyword
+ expansion mode.
+ * sanity.sh (binfiles3): Adjust tests binfiles3-7 and binfiles3-8
+ for the new behavior.
+
+1999-01-27 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (multiroot3): Add new variant of multiroot3-10 test
+ for RELATIVE_REPOS. Move multiroot3-11 test out of the
+ conditionals; it works the same for remote or local,
+ RELATIVE_REPOS or no.
+
+ * options.h.in: Make RELATIVE_REPOS the default, as has been
+ announced as a future direction since 1997-10-11.
+ * sanity.sh (multiroot): Tweak multiroot-update-1a and
+ multiroot-update-1b tests to work with either RELATIVE_REPOS or
+ non-RELATIVE_REPOS.
+
+ * sanity.sh (client-9): Don't assume the time zone.
+
+1999-01-26 Jim Kingdon <http://www.cyclic.com>
+
+ Fix one facet of the "cvs add -kb" re-adding problem (the other
+ known facet is tested for by binfiles3-8).
+ * add.c (add): When re-adding a file, set the keyword expansion
+ as we normally would.
+ * sanity.sh (binfiles3): New test binfiles3-6a tests for this.
+
+1999-01-22 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (rmadd2): New tests, for undoing a commit.
+
+1999-01-21 Eric Mumpower <address@hidden>
+
+ * sanity.sh (reposmv): Actually modify CVSROOT in current
+ environment when calling functions, rather than trying to achieve
+ the same effect with "CVSROOT=foo functionname". (Many common
+ bourne shells, including those in SunOS and Solaris 2.4-2.7,
+ do not properly handle "ENVVAR=foo command" when "command" is
+ a user-defined shell function rather than an actual executable.)
+
+1999-01-15 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (rcs3): Redirect awk's stdin to /dev/null like all the
+ other awk invocations. GNU awk seems not to read stdin in this
+ case, but that behavior is hard to reconcile with the Single Unix
+ Spec and some awks don't do it.
+
+ * sanity.sh (binfiles, binfiles2, binfiles3, server): Use the same
+ tr trick as in rcs3. People don't seem to have been complaining,
+ and this should fix server-4 for HPUX.
+
+1999-01-14 Jim Kingdon <http://www.cyclic.com>
+
+ * client.c (recv_line): If the line we are reading contains a
+ character which would sign-extend to EOF, don't treat it as end of
+ file. recv() doesn't report end of file this way and this might
+ fix bugs with 0xff characters.
+
+1999-01-14 Larry Jones <address@hidden>
+
+ * client.c (recv_line): Handle EOF from server.
+
+ * sanity.sh (importc-8, importc-9): Accept anything in the seconds
+ fields of the timestamps since touch doesn't set it reliably.
+ (This isn't great, but it's better than nothing.)
+
+1999-01-14 Jim Kingdon <http://www.cyclic.com>
+
+ * run.c (run_exec): Adjust comment about vfork; this isn't the place
+ to get into a treatise about fork performance vs. vfork
+ performance but it isn't quite as simple as whether one has
+ copy-on-write.
+
+1999-01-13 Larry Jones <address@hidden>
+
+ * sanity.sh (dotest_fail): Handle spurrious output from assert better.
+
+ * sanity.sh (rcs3-4, rcs3-5a): Handle even more variants of the
+ assertion failure message.
+
+1999-01-12 Larry Jones <address@hidden>
+
+ * sanity.sh (mtfr-3): ls behavior varies wildly on nonexistant files,
+ just use echo instead.
+
+1999-01-11 Jim Meyering <address@hidden>
+
+ * sanity.sh (mkmodules-temp-file-removal): New test, for this.
+ * mkmodules.c (mkmodules): Remove each `CVSROOT/.#[0-9]*' temporary
+ file that's used to check out files listed in CVSROOT/checkoutlist.
+ Remove extra semicolon at end of line.
+
+1999-01-11 Larry Jones <address@hidden>
+
+ * sanity.sh (rcs3-5a): Allow for multiple lines of output before the
+ assertion failure message.
+
+ * sanity.sh (lockfiles-6, client-8): Work around bug in HP-UX chmod
+ (doesn't allow anything to follow omitted permissions).
+
+1999-01-09 Jim Kingdon <http://www.cyclic.com>
+
+ * client.c (set_sticky): Nonfatal error if we can't write it.
+ * sanity.sh (dirs2-8 through dirs2-14): New tests, for this.
+
+ * sanity.sh (rcs3): Write NUL character with tr not awk, in
+ accordance with Single Unix Specification. Hopefully will fix
+ rcs3-7 for HPUX. Will not work on SunOS4, but then again neither
+ did the old syntax.
+
+1999-01-05 Jim Kingdon <http://www.cyclic.com>
+
+ * client.c, update.c: Rename MD5* functions to cvs_MD5* per
+ corresponding change to ../lib/md5.h.
+
+1999-01-03 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (client): Give file1 a predictable mode so that the
+ output in client-9 will not depend on the umask of the user
+ running the tests.
+
+1998-12-29 Jim Kingdon <http://www.cyclic.com>
+
+ * client.c (client_senddate): Use date_to_internet rather than
+ using our own "5/26/1997 13:01:40 GMT" date format.
+ * main.c (date_to_internet): Check for errors from sscanf. Always
+ send a four digit year. Send hours, minutes, and seconds as two
+ digits per RFC822.
+ * sanity.sh (client): New tests client-8 and client-9 test for this.
+
+ * sanity.sh (rcs2): New tests rcs2-6 through rcs2-8 test for fix
+ to lib/getdate.y (before the fix, "100 months" or "8 years" would
+ tend to mean the year 1969, thus the tests would give "cvs update:
+ file1 is no longer in the repository").
+
+1998-12-28 Larry Jones <address@hidden>
+
+ * entries.c (Register): Return if unable to open log file to avoid
+ referencing the invalid file pointer.
+ * sanity.sh (dirs2-7): With above change, no longer fails.
+ * sanity.sh (rcs3-5a): Another assertion failure message.
+ * sanity.sh (pserver-4, pserver-5): Some 4.4BSD derived systems spit
+ out bogus error messages when initgroups is called as non-root.
+
+1998-12-23 Larry Jones <address@hidden>
+
+ * sanity.sh (rcs3, dotest_fail): The assertion failure message varies
+ wildly between different systems and the resulting abort call can
+ even result in spurrious output. Fix the regexp to accept nearly
+ anything containing some kind of assertion failure and ensure that
+ any spurrious output ends up in the output file instead of on the
+ terminal.
+
+1998-12-23 Jim Kingdon <http://www.cyclic.com>
+
+ * admin.c, checkout.c, commit.c, cvsrc.c, expand_path.c,
+ history.c, ignore.c, import.c, log.c, mkmodules.c, modules.c,
+ myndbm.c, parseinfo.c, rcs.c, remove.c, rtag.c, status.c, subr.c,
+ tag.c, wrapper.c: Cast all char's to unsigned char before passing
+ them to ctype.h functions (isalpha, isgraph, isalnum, isspace,
+ isdigit, isprint, isupper). Whether using ctype.h is the right
+ thing at all is unclear to me (having the server depend on locale
+ seems wrong, as we don't necessarily have any good way to set the
+ right locale, if there even is such a concept as 'right' locale in
+ this context), but as long as we use ctype.h we might as use it
+ according to the standards (this affects systems where plain char
+ is signed but users supply characters with the 8th bit set).
+ Thanks to Paul Eggert for suggesting this.
+
+1998-12-22 Jim Kingdon <http://www.cyclic.com>
+
+ * sanity.sh (rcs3): Oops, the earlier fix for srcdir only fixed
+ the non-remote case, not the remote case. Fix the other occurrence.
+
+1998-12-22 Jim Kingdon
+
+ * sanity.sh (rcs3): The assertion failure message varies slightly
+ depending on whether CVS was built with srcdir != ".". Fix regexp.
+
+1998-12-21 Jim Kingdon
+
+ * rcs.c (RCS_getdate): Reindent Jim Meyering's change; remove
+ unused variable x_vers.
+
+ * rcs.c: When printing an unexpected character we found in the RCS
+ file, print it in hex rather than as a character (see comment for
+ rationale).
+ * sanity.sh (rcs3): Adjust rcs3-2 and rcs3-7 tests accordingly.
+
+ * sanity.sh (rcs3): New test, for some error handling cases
+ involving parsing RCS files.
+
+1998-12-16 Jim Meyering <address@hidden>
+
+ * rcs.c (RCS_getdate): Handle the case in which a file is first
+ imported after its initial version has been created.
+ * sanity.sh (import-after-initial): New test for that.
+
+1998-12-17 Jim Kingdon
+
+ * server.c (serve_root): Pserver_Repos only exists if
+ AUTH_SERVER_SUPPORT is defined.
+
+1998-12-12 Jim Kingdon, and Derek R. Price of Stortek.
+
+ * sanity.sh (multiroot): Change + to ${PLUS}.
+
+1998-12-12 Jim Kingdon, and Gary Young of Motorola
+
+ * sanity.sh (admin): In tests admin-13, admin-25, and admin-29,
+ allow 4 digit year in addition to 2 digit year.
+
+1998-12-12 Jim Kingdon
+
+ * sanity.sh (log): New tests log-14a and log-14b test for -rHEAD
+ and for HEAD as (nonexistent) file name.
+
+1998-12-02 Jim Kingdon
+
+ * version.c: Squish version number to 1.10.4.1.
+
+ * version.c: Version 1.10.4.
+
+1998-11-24 Jim Kingdon
+
+ * recurse.c (do_file_proc): Check for errors from RCS_parse.
+ * sanity.sh (rcslib-symlink-7 through rcslib-symlink-10): New
+ tests, test for this.
+
+ * sanity.sh (reposmv-2): Adjust for 22-Nov change to Find_Names.
+
+ * entries.c (Register): If we can't write Entries.Log, make it a
+ nonfatal error.
+ * sanity.sh (dirs2): Test for this fix.
+
+ * sanity.sh (dirs2): Clean up working directory at end of test.
+
+1998-11-23 Jim Kingdon
+
+ * sanity.sh (dirs2): New test, for some more cases involving
+ deleting directories and such.
+
+ * sanity.sh (dirs): Update for yesterday's change in Find_Names
+ error handling. The error in dirs-4 is fairly different now; in
+ dirs-3 and dirs-3a it is the obvious change.
+
+1998-11-22 Jim Kingdon
+
+ * sanity.sh (release): Move the commments listing "cvs release"
+ tests from modules2-6 to here.
+ * release.c (release): Update comment to reflect "? foo" case.
+
+ * find_names.c (Find_Names): If we can't read the repository, make
+ it a nonfatal error. Tell the caller whether this happened.
+ (find_rcs): Add comment regarding this behavior.
+ * recurse.c (do_recursion): If Find_Names gives an error, skip
+ the directory and print a message saying so.
+ * sanity.sh (modes3): New test, for this.
+
+1998-11-18 Jim Kingdon
+
+ * rtag.c (rtag_usage), tag.c (tag_usage): Use "-r rev"
+ consistently.
+
+ * sanity.sh (conflicts3): Tests conflicts3-24 through
+ conflicts3-28 test for another case similar to conflicts3-22.
+
+1998-11-14 Jim Kingdon
+
+ * sanity.sh (diff): New test, for now just tests for the "I know
+ nothing" message.
+
+ * sanity.sh (conflicts2-142b7 through conflicts2-142b11): New
+ tests; resurrecting doesn't work from one level up.
+
+ * sanity.sh (mwrap-7): Remote prints the messages in a different
+ order.
+
+1998-11-13 Jim Kingdon
+
+ * tag.c (check_fileproc): Log tag deletions.
+ * rtag.c (check_fileproc): Likewise.
+ * sanity.sh (taginfo-14 through taginfo-18): New tests, for
+ these behaviors.
+
+1998-11-12 Jim Kingdon
+
+ * sanity.sh (mwrap-7): Update for the noexec fix.
+
+ * server.c (server_copy_file): Add comment about noexec.
+
+ * update.c (checkout_file): Handle noexec case involving revbuf
+ and modes.
+ (update_fileproc): In case T_NEEDS_MERGE, let merge_file take care
+ of noexec, so it can tell the user if there would be conflicts.
+ (merge_file): Print "conflicts found in FILE" message
+ regardless of noexec. Add comment about checking for whether the
+ file already contained the changes, and noexec.
+ * sanity.sh (conflicts-192a): New test, for this.
+
+1998-10-20 Jim Kingdon
+
+ Use the gzip library on the server. Probably doesn't speed things
+ up as currently implemented, but does avoid hassles in terms of
+ finding an external gzip program.
+ * zlib.c, server.h (gunzip_and_write, read_and_gzip): Now returns
+ whether a fatal error occurred, rather than expecting error (1,
+ ...) to work.
+ * client.c (update_entries, send_modified): Change callers.
+ * server.c (receive_file): Rewrite gzip code to use
+ gunzip_and_write rather than filter_through_gunzip.
+ (server_updated): Likewise, use read_and_gzip rather than
+ filter_through_gzip.
+ * client.c, client.h (filter_through_gzip, filter_through_gunzip),
+ run.c, cvs.h (filter_stream_through_program): Removed; no longer used.
+ * sanity.sh (server): New tests server-4 and server-5 test
+ this feature (note that CVS 1.10 also passes these tests; the
+ behavior is supposed to be unchanged).
+
+1998-10-19 Jim Kingdon
+
+ * sanity.sh (multiroot3): New test, tests for a few more
+ multiroot cases.
+
+ * lock.c (lock_name): Set the permissions on each directory we
+ create to that of the parent directory.
+ * sanity.sh (lockfiles): New chmod and tests lockfiles-7a and
+ lockfiles-7b test for this. Adjust lockfiles-5 for new text of
+ error message.
+
+1998-10-15 Jim Kingdon
+
+ * server.c (requests): Set RQ_ROOTLESS for "Set".
+ * sanity.sh (info): Also clean up $HOME/.cvsrc.
+ (server): Test that we can send Set before Root (had been tested
+ by crerepos-6b, but only if you ran the info test first). Tests
+ for this fix.
+
+1998-10-14 Jim Kingdon
+
+ * subr.c (expand_string): Tweak the algorithm so that the size
+ that it allocates is generally a power of two.
+
+1998-10-14 Eivind Eklund and Jim Kingdon
+
+ * commit.c (commit): For the client, don't worry about whether we
+ are root.
+
+1998-10-13 Jim Kingdon
+
+ * server.h (struct request): Change status field to flags and add
+ RQ_ROOTLESS.
+ * client.c (handle_valid_requests, supported_request): Change
+ status to flags.
+ * server.c (requests): Change status to flags. Add RQ_ROOTLESS.
+ * server.c (server): If not RQ_ROOTLESS, and we haven't gotten a
+ Root request, give an error.
+
+1998-10-12 Jim Kingdon
+
+ * version.c: Slide version number to 1.10.3.1.
+
+ * Version 1.10.3.
+
+ * sanity.sh (modules2-17): Update for 9 Oct 1998 change to
+ update_dirent_proc.
+
+1998-10-11 Jim Kingdon
+
+ * commit.c (checkaddfile, commit_fileproc): A numeric value for
+ 'tag' does not mean that we are adding on a branch.
+ * sanity.sh (keywordlog): Adjust this test, to test for this
+ (replaces comment saying we should be doing it).
+ (rmadd): Likewise.
+
+ * sanity.sh (rmadd): New test, tests for various existing
+ behaviors with "cvs ci -r".
+
+1998-10-09 Jim Kingdon
+
+ * update.c (update_dirent_proc): For local CVS, if the directory
+ does not exist in the working directory nor in the repository,
+ just skip it.
+ * sanity.sh (dirs): New tests dirs-3a, dirs-7 and dirs-8 test for
+ this and related behaviors. Note that the new behavior was also
+ the previous behavior for remote; we are only changing it for local.
+
+ * wrapper.c, cvsrc.c, ignore.c: Add comments about ignoring .cvsrc
+ and friends if we can't find a home directory.
+ * expand_path.c (expand_path): If we can't find the home
+ directory, give an error rather than a coredump (or worse).
+ * login.c (construct_cvspass_filename): Don't use errno in error
+ message; get_homedir doesn't set it. Add comment about this
+ message.
+
+1998-10-07 Jim Kingdon <address@hidden>
+
+ * diff.c (diff): Set variables to NULL at the start, and free
+ memory at the end.
+ * sanity.sh (multiroot2): Add tests for this (before the fix,
+ multiroot2-12 would abort with "no more than two revisions/dates
+ can be specified").
+
+1998-10-06 Jim Kingdon <address@hidden>
+
+ * Makefile.in (installcheck check): Remove references to RCSBIN;
+ they don't do anything now that RCSBIN is ignored.
+
+ * client.c: Clean up horrible confusion about whether stored_mode
+ or stored_mode_valid (or nothing :-)) indicates whether
+ stored_mode is allocated. Should fix crashes (for example, on NT
+ when the server has renamed multiple files from uppercase to
+ lowercase).
+
+ * sanity.sh (dirs): New tests, tests for some cases involving
+ admins who do surgery on the repository.
+
+1998-10-03 Johannes Stezenbach <address@hidden>
+
+ * vers_ts.c (Version_TS): If UTIME_EXPECTS_WRITABLE, if
+ necessary change the file to be writable temporarily to set its
+ modification time.
+
+1998-10-03 Jim Kingdon <address@hidden>
+
+ * client.c (handle_error): Add comment about indicating which
+ errors are from the server.
+
+1998-10-01 Jim Kingdon <address@hidden>
+
+ * sanity.sh (devcom-180): Allow one digit day.
+
+1998-09-30 Jim Kingdon <address@hidden>
+
+ * main.c (main): Don't call Name_Root if -d specified.
+ * recurse.c (do_recursion, do_dir_proc): Don't check CVS/Root
+ if -d was specified.
+ * import.c (import): Indentation fix.
+ * sanity.sh (multiroot): Update for this change.
+ (reposmv): New test, tests for this.
+
+1998-09-28 Jim Kingdon <address@hidden>
+
+ * sanity.sh (multiroot2): New test, tests some nested directory
+ cases.
+
+1998-09-25 Jim Kingdon <address@hidden>
+
+ * sanity.sh (multiroot): Change a few comments which said modules
+ when they meant directories.
+
+1998-09-25 Jim Meyering <address@hidden>
+
+ * sanity.sh (devcom-180): Add 0-9 to the range of characters allowed
+ in hostname regexp.
+
+1998-09-25 Jim Kingdon <address@hidden>
+
+ * sanity.sh (log2): New test log2-7a tests for one error handling
+ case. Add a comment about another.
+
+1998-09-24 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Change crerepos test back to :ext: (for several
+ reasons; see comments).
+
+1998-09-24 Noel Cragg <address@hidden>
+
+ * sanity.sh (rcslib-symlink-5, rcslib-symlink-6): new tests to
+ check the operation of "tag" when there are symlinks in the
+ repository.
+
+ * rcs.c (RCS_checkin): remove old code that resolved the symlink
+ and call resolve_symlink instead.
+ (RCS_rewrite): call resolve_symlink before doing anything else to
+ make sure we're operating on the file and not the symlink.
+
+ * subr.c (resolve_symlink): new routine -- resolves a symbolic
+ link chain to its destination.
+ * cvs.h: add prototype.
+
+ * sanity.sh (basica-6.2, basica-6.3): changed match expressions to
+ reflect new diff output.
+
+ * rcs.c (make_file_label): generate labels for files that include
+ the pathname so that output from "cvs diff" is useable by patch.
+ Looks like I came up with the mods as Andy Piper
+ <address@hidden>; his patch was on the Cyclic unofficial
+ patches page.
+
+ * sanity.sh: change remote access method from ext to fork. This
+ results in a significant speed improvement when running the
+ testsuite. The ext method on my machine (i586 120MHz Linux 2.0.35
+ with TCP wrappers installed) runs in 450% of the time of the local
+ method while the fork method runs in only 150% of the time of the
+ local method! Yow! Am I SWAPPING yet?!
+ (crerepos-6a, crerepos-6b): change to reflect different error
+ messages for fork method.
+ (modes-15): same.
+
+ * client.c (connect_to_forked_server): new routine.
+ (start_server): call the above when method is fork_method.
+
+ * root.c: add a new method named "fork". This method uses the
+ remote protocol, but does so by forking a "cvs server" process
+ directly rather than doing "rsh host cvs server" (for example).
+ This new method has few advantages for day-to-day use, but has
+ three important benefits for debugging:
+
+ 1) Most secure installations these days don't allow rsh access.
+ With this new method, we can still test the remote protocol on
+ these machines because we don't need to be able to make a local
+ TCP connection.
+
+ 2) Even if installations allow rsh access, they almost always
+ have TCP wrappers to check permissions by IP/hostname. This
+ causes a short delay for every connection. For invocations from
+ the command line, this doesn't matter much, but it adds up to a
+ significant amount of time when running the testsuite.
+
+ 3) On machines that can't (or do not usually) provide rshd
+ access (I'm thinking of WNT/W95 in particular), we can now run
+ tests of the remote protocol using this method. Indeed, we can
+ run remote protocol tests on any machine that has an
+ implementation of piped_child().
+
+ (parse_cvsroot): handle new method.
+ (error_exit, xstrdup, isabsolute): new stub functions to use when
+ compiling root.c with the DEBUG option.
+ (main): fix a few typos.
+ * cvs.h (CVSmethod): add fork_method.
+
+ * server.c (create_adm_p): use Emptydir as the placeholder
+ directory instead of "." to avoid problems with "cvs update -d" et
+ al.
+
+1998-09-22 Noel Cragg <address@hidden>
+
+ * sanity.sh (devcom-180): fixed typo in regexp.
+
+ * main.c (main): remove need_to_create_root and related code
+ (including CVS_IGNORE_REMOTE_ROOT environment variable). The
+ current implementation (just removed) of rewriting the contents of
+ the CVS/Root file isn't desirable for a number of reasons:
+
+ 1) Only the top-level CVS/Root directory is updated. If we're
+ really interested in pointing our WD at another CVSROOT, we
+ should have a separate command.
+
+ 2) With the new multiroot mods, we don't ever want to rewrite
+ CVS/Root files in the way the removed code did. Consider:
+
+ cvs -d repository1 co a
+ cd a
+ cvs -d repository2 co b
+ cvs -d repository2 update b
+
+ The update command would rewrite the contents of a/CVS/Root to
+ the incorrect value. Bad. We then wouldn't be talking to the
+ correct repository for files in a.
+
+ 3) The removed code seems to be a quick hack to support working
+ directories checked out from multiple repositories. With the
+ CVS_IGNORE_REMOTE_ROOT variable set, one could perform commands
+ as in example 2, above, without worring about updating CVS/Root
+ files. While in pre-1.10.1 recursive commands wouldn't handle
+ that working directory hierarchy, one could use commands like
+ "cvs foo -l" instead. While not great, this allows you (with a
+ lot of manual interaction) to have a multiroot WD. Since we now
+ have multiroot mods checked in, we don't need this code.
+
+ (lookup_command_attribute): while we don't need the
+ CVS_CMD_USES_WORK_DIR flag anymore (since it only was supporting
+ the need_to_create_root code), I'm leaving it in. It may come in
+ handy at some later date.
+
+1998-09-18 Jim Kingdon <address@hidden>
+
+ * version.c: Advance version number to 1.10.2.1.
+
+ * Version 1.10.2.
+
+1998-09-13 Jim Kingdon <address@hidden>
+
+ * client.c: Refuse to Copy-file to another directory
+ * sanity.sh (client): New test, tests for this.
+
+ * edit.c (editors_fileproc), watch.c (watchers_fileproc): Use
+ cvs_output rather than writing to stdout.
+ * sanity.sh (devcom): Use dotest for tests 178, 180, and 183
+ (tests that we preserve existing behavior on "cvs editors").
+
+ * commit.c (check_fileproc): Don't allow commits in Emptydir.
+ * sanity.sh (emptydir-8): Test for this change in behavior.
+
+ * sanity.sh: Add some compatibility tests to TODO comments at end.
+
+1998-09-10 Jim Kingdon <address@hidden>
+
+ * wrapper.c (wrap_add): Remove obsolete comment about -m.
+
+ * server.c (server_updated): Check for error from CVS_UNLINK.
+
+1998-09-09 Jim Kingdon <address@hidden>
+
+ * server.c (serve_root): Allocate with malloc, not xmalloc.
+
+ * root.c (set_local_cvsroot): Move memory allocation from here...
+ * server.c (serve_root): ...to here. Fixes error handling.
+
+ * root.c (parse_cvsroot): Don't call check_root_consistent;
+ parse_cvsroot is only used for local and client.
+ * root.c (set_local_cvsroot): Move check_root_consistent
+ functionality from here...
+ * server.c (serve_root): ...to here. Fixes error handling. Also
+ made the error more explicit, while I am at it.
+ * server.c (Pserver_Repos): Now static.
+ * cvs.h: Don't declare it.
+ * root.c (check_root_consistent): Removed; no longer needed.
+ * sanity.sh (pserver): New test, tests for this behavior and some
+ other basic pserver stuff.
+
+ * update.c (merge_file): Use cvs_output for "already contains the
+ differences" message. Found this one when I actually observed the
+ out-of-order bug in Real Life(TM).
+
+1998-09-09 Jim Kingdon
+
+ * find_names.c (find_dirs): Make sure to zero errno before
+ going around the loop again.
+ * find_names.c (find_rcs): Make sure to set save_errno.
+ (thanks to Alexandre Parenteau for reporting both problems).
+
+1998-09-09 Jim Kingdon <address@hidden> and Michael Pakovic
+
+ * edit.c (notify_do): Only free line if it is not NULL.
+
+1998-09-07 Jim Kingdon <address@hidden>
+
+ * cvs.h: dirs_sent_to_server should not be inside
+ AUTH_SERVER_SUPPORT (reported by both Richard Levitte and Murray
+ Bishop, thanks).
+
+ * lock.c, cvs.h: New variable lock_dir.
+ * parseinfo.c (parse_config): New option LockDir.
+ * lock.c (lock_name): New function, abstracts out lock file naming
+ and also supports LockDir.
+ * lock.c (lock_simple_remove, Reader_Lock, write_lock, set_lock):
+ Call it (6 places, to create/remove read/write/master locks).
+ (Lock_Cleanup): Refuse to reenter this function.
+ * sanity.sh (lockfiles): New test, tests for this feature.
+
+1998-09-03 Jim Kingdon <address@hidden>
+
+ * sanity.sh (multiroot): Expect ${TESTDIR} in output instead of
+ assuming it is /tmp/cvs-sanity (thanks to Mark D. Baushke of Cisco).
+ Clean up working directory when done (fixes apparent thinko).
+
+ * server.c (create_adm_p): Fix one "return" which didn't return a
+ value.
+ (dirswitch): Check for errors from create_adm_p.
+
+ * sanity.sh: Set LC_ALL rather than just LC_COLLATE.
+
+Wed Sep 2 02:30:22 1998 Jim Kingdon <address@hidden>
+
+ * version.c: Bump version number to 1.10.1.1.
+
+ * Version 1.10.1.
+
+1998-09-01 Jim Kingdon <address@hidden>
+
+ Administrative note regarding Noel's changes to allow one to
+ switch from one CVS root to another in a single command: The
+ ChangeLog entries for the changes which Noel just checked in
+ appear for 1998-09-01, 1998-08-28, 1998-08-25, 1998-08-19, and
+ 1998-08-18, rather than being all together.
+
+ * main.c (set_root_directory): Fix whitespace.
+ (main): Nuke new -m option and just have that message controlled
+ by -t.
+ * server.c (server): Revert the CVS_SERVER_SLEEP code back the way
+ it was in CVS 1.10. Attaching to the parent process is relatively
+ boring (you can just run "cvs server" under a debugger instead),
+ but connecting to the child process is what the old code was for.
+ * recurse.c, server.c: Remove DEBUG_NJC code.
+
+1998-09-01 Noel Cragg <address@hidden>
+
+ * server.c (do_cvs_command): add another environment variable,
+ CVS_SERVER_SLEEP2, after forking to pause the program so one can
+ attach a debugger.
+
+ * sanity.sh (crerepos): clean up crerepos-18 now that multiroot
+ works in this case.
+ (multiroot): finalize tests for local vs. remote operation.
+
+ * recurse.c (start_recursion): near the beginning, save the list
+ of directories to spoof as command-line arguments, if necessary.
+ Use that list near the end and call send_file_names to send those
+ arguments to the server.
+ (do_argument_proc): removed, since we call send_file_names now.
+
+ * main.c (main): re-initialize dirs_sent_to_server on each pass
+ through the loop for each CVSROOT.
+
+ * cvs.h: add proto for global variable which keeps track of which
+ directories have been sent to the server when in client mode.
+
+ * client.c (is_arg_a_parent_or_listed_dir): new function.
+ (arg_should_not_be_sent_to_server): new function. Tries to decide
+ whether the given argument should be sent to the server, based on
+ the current CVSROOT and the list of directories sent to the
+ server.
+ (send_repository): add the directory name to the list of
+ directories sent to the server.
+ (send_file_names): call arg_should_not_be_sent_to_server.
+
+ * add.c (add): switch the order of send_files and send_file_names
+ to make multiple repository support possible. send_files needs to
+ create a list of directories being requested so that
+ send_file_names can decide which command-line arguments to send to
+ the server for the given current CVSROOT.
+ * admin.c (admin): same.
+ * commit.c (commit): same.
+ * diff.c (diff): same.
+ * edit.c (editors): same.
+ * log.c (cvslog): same.
+ * rcs.c (annotate): same.
+ * remove.c (cvsremove): same.
+ * status.c (cvsstatus): same.
+ * tag.c (cvstag): same.
+ * update.c (update): same.
+ * watch.c (watch_addremove): same.
+ (watchers): same.
+
+1998-08-31 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Remove "debug" function; it was apparently checked
+ in accidentally by Norbert Kiesel's change.
+
+1998-08-31 Norbert Kiesel <address@hidden>
+
+ * release.c (release): modify last patch to release so that
+ save_cwd is called only once and restore_cwd is always called when
+ neccessary. Also fixed a tiny memory leak.
+
+ * sanity.sh (release): added some more tests for "cvs release"
+ including a test with two dirs and a "no" for the first one (which
+ fails without the above patch).
+
+1998-08-28 Noel Cragg <address@hidden>
+
+ * sanity.sh (crerepos-18): add new comment and change test
+ slightly to support multiroot.
+ (multiroot): add more tests.
+
+ * server.c (create_adm_p): new function.
+ (dirswitch): call create_adm_p. Modify the code to always write a
+ new CVSADM_REP file, since create_adm_p might have put a
+ placeholder there and our value is guaranteed to be correct.
+ (server): move the CVS_SERVER_SLEEP check here so we can debug
+ things at an earlier stage.
+
+ * recurse.c (start_recursion): add large comment about the ideal
+ solution to the "Argument xxx" problem.
+
+ * main.c (main): move position of debugging comment for -m flag.
+
+ * diff.c (diff): clear a static variable.
+
+ * client.c (send_file_names): check to see if we should send this
+ argument to the server based on the contents of the appropriate
+ CVSADM directory. This avoids "nothing known about foo" messages
+ and problems with duplicate modules names in multiple
+ repositories.
+ (send_a_repository): change method of calculating toplevel_repos
+ to support multiple CVSROOTs.
+ (start_server): clear some static variables.
+
+1998-08-28 Jim Meyering <address@hidden>
+
+ * sanity.sh (basicc-8, basicc-11): Use `.*' instead of explicit
+ `Operation not permitted'. Solaris2.5.1 gets a different error:
+ `Invalid argument'.
+
+1998-08-26 Eric M. Hopper
+
+ * sanity.sh: Set LC_COLLATE to "C".
+
+1998-08-25 Noel Cragg <address@hidden>
+
+ * sanity.sh (multiroot): new set of tests to check the behavior of
+ multiroot.
+
+ * diff.c (diff): set options value to NULL after freeing to reset
+ the state for the next time around.
+
+1998-08-25 Jim Kingdon <address@hidden>
+
+ Fix problems with trying to rename an open file:
+ * rcs.c, rcs.h (RCS_setattic): New function.
+ * commit.c (remove_file, checkaddfile): Call it.
+
+1998-08-24 Jim Kingdon <address@hidden>
+
+ * release.c (release): Use save_cwd and restore_cwd to get back to
+ where we started, rather than hoping that CVS_CHDIR ("..") will do
+ something useful. This removes the need for most of
+ release_delete, so remove that function and inline what is left.
+ * sanity.sh (basicc): Adjust tests for this fix, also some tests
+ with multiple arguments to "cvs release" (in the non-"-d"-case, it
+ would seem like the old code would CVS_CHDIR into directories and not
+ CVS_CHDIR back, but I'm not going to investigate this and it
+ should be a moot point with this fix.).
+
+ * sanity.sh (basicc): Add tests for a serious bug in "cvs release
+ -d .".
+
+ More error handling fixes:
+ * ignore.c (ignore_files): Check for errors from opendir and
+ readdir.
+ * find_names.c (Find_Names): Check for errors from find_rcs.
+ (find_rcs, find_dirs): Comment error handling better; also return
+ an error if we got one from readdir.
+ * filesubr.c (deep_remove_dir): Also check for errors from readdir.
+ * import.c (import_descend): Print message on error from opendir
+ or readdir.
+ * commit.c (remove_file): Check for errors from CVS_MKDIR and
+ CVS_RENAME.
+ (remove_file): No need to remove the file in the temporary
+ directory; server.c now informs time_stamp_server of what is going
+ on via CVS/Entries rather than a file with a kludged up timestamp.
+ * client.c, entries.c, login.c, logmsg.c, mkmodules.c, patch.c,
+ remove.c, update.c: Check for errors from unlink_file.
+ * mkmodules.c (write_dbmfile, rename_dbfile, rename_rcsfile):
+ Check for errors from fclose, CVS_RENAME, and CVS_STAT.
+ * mkmodules.c (checkout_file): Clarify error handling convention.
+ * mkmodules.c (mkmodules): Call checkout_file accordingly.
+ * entries.c (Entries_Open): Check for errors from fclose.
+
+1998-08-21 Ian Lance Taylor <address@hidden>
+
+ * import.c (import): Output suggested merge command using
+ cvs_output_tagged rather than just cvs_output. Don't put
+ CVSroot_cmdline in the log file.
+ * client.c (importmergecmd): New static struct.
+ (handle_mt): Handle +importmergecmd tag.
+ * sanity.sh (import): Use an explicit -d in importb-2, to test
+ whether it is reported in the suggested merge command.
+
+1998-08-20 Ian Lance Taylor <address@hidden>
+
+ * sanity.sh (import): Rewrite tests to use dotest.
+
+1998-08-20 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Add comments about binary files and cvs import.
+
+1998-08-19 Jim Kingdon <address@hidden>
+
+ * sanity.sh (importc): Use ${username} in one place where I had
+ missed it.
+
+ Make import -d work client/server:
+ * client.c, client.h (client_process_import_file): Take new
+ argument, for whether -d is specified, and send Checkin-time
+ request if it is set.
+ * import.c (import_descend): Pass it.
+ * main.c, cvs.h (date_to_internet): New function.
+ * server.c (server_modtime): Call date_to_internet.
+ * server.c (serve_checkin_time): New function.
+ (requests): Add "Checkin-time" request.
+ (serve_modified): If it was sent, set the timestamp in the
+ temporary directory.
+ * import.c (import): If the client sends a -d option, complain.
+ (import): For the server, always use the timestamps from the temp
+ directory.
+ (import): Don't send a -d option to the server.
+ * sanity.sh (importc): Add tests for import -d.
+
+Wed Aug 19 15:19:13 1998 Larry Jones <address@hidden>
+
+ * sanity.sh (unedit-without-baserev-5): use ${DOTSTAR} instead
+ of .* since we expect to match multiple lines.
+
+1998-08-19 Ian Lance Taylor <address@hidden>
+
+ * cvs.h (CVSroot_cmdline): Declare.
+ * root.c (CVSroot_cmdline): Define.
+ * main.c (main): Set CVSroot_cmdline if the -d option is used.
+ * import.c (import): If CVSroot_cmdline is not NULL, then mention
+ an explicit -d option in the suggested merge command line.
+
+Wed Aug 19 00:28:50 1998 Noel Cragg <address@hidden>
+
+ * recurse.c (do_dir_proc): don't muck with CVS/Root directories
+ when running in server mode.
+ (do_recursion): same.
+
+ * main.c (main): add the command-line option `m' to help debug the
+ multiroot environment; it prints out the value of CVSROOT for each
+ iteration through the main loop. Also, changed the main loop so
+ that it gets executed only once when running in server mode (the
+ server will only deal with a single CVSROOT).
+
+ * recurse.c (do_recursion): change default for
+ PROCESS_THIS_DIRECTORY to true; we should always process a
+ directory's contents unless there's an existing CVS/Root file with
+ a different root than the current root to tell us otherwise.
+ (do_dir_proc): same.
+
+Tue Aug 18 14:30:59 1998 Noel Cragg <address@hidden>
+
+ * recurse.c (do_recursion): check the current value of CVS/Root
+ and add it to our list of CVSROOTs if it doesn't exist. Decide
+ whether or not to process files in this directory based based on
+ the value of CURRENT_ROOT.
+ (do_dir_proc): same.
+
+ * main.c: add two new globals -- root_directories and current_root
+ -- which keep track of the values of CVSROOT we've seen and which
+ value of CVSROOT we're currently processing.
+ (main): put the main loop for stepping through cvsroot values
+ here, since we might need to send command-specific arguments for
+ every unique non-local cvsroot. Moved blocks of code around so
+ that one-time initializations happen first (outside the loop) and
+ the other stuff happens inside the loop.
+ (set_root_directory): helper function.
+
+ * cvs.h: add prototypes for root_directories and current_root, two
+ new globals for keeping track of multiple CVSROOT information.
+
+1998-08-18 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Don't assume that the shell leaves $^ unexpanded in
+ an unquoted here-document (suggested by Bart Schaefer to help when
+ zsh is the shell).
+
+1998-08-17 Ian Lance Taylor <address@hidden>
+
+ * commit.c (checkaddfile): Don't call fix_rcs_modes.
+ (fix_rcs_modes): Remove.
+
+1998-08-16 Jim Kingdon <address@hidden>
+
+ * create_adm.c (Create_Admin): Don't condition traces on
+ SERVER_SUPPORT; SERVER_SUPPORT shouldn't do (much of) anything
+ independent of server_active.
+
+ * sanity.sh (binfiles3): New test, for yet another binary file
+ bug (sigh). Thanks to Jason Aten for reporting this one.
+
+1998-08-15 Jim Kingdon <address@hidden>
+
+ * rcscmds.c (call_diff_write_output): Update to reflect new
+ calling convention for the write_output callback.
+
+1998-08-15 Jim Meyering <address@hidden>
+
+ * update.c (merge_file): Warn about failed unlink when not due
+ to ENOENT.
+
+ * server.h (CLIENT_SERVER_STR): New macro
+ * create_adm.c (Create_Admin): Use it.
+ * entries.c (Scratch_Entry, Register): Use it.
+ * filesubr.c (copy_file, xchmod, rename_file, unlink_file): Use it.
+ * history.c (history_write): Use it.
+ * modules.c (do_module): Use it.
+ * no_diff.c (No_Difference): Use it.
+ * run.c (run_popen): Use it.
+ * server.c (server_register): Use it.
+
+1998-08-14 Jim Meyering <address@hidden>
+
+ * hardlink.c (lookup_file_by_inode): Use existence_error rather than
+ comparing errno to ENOENT directly.
+
+ * client.c (copy_a_file): Unlink destination before doing copy.
+ * sanity.sh (join-readonly-conflict): New test for this -- it would
+ fail only in client/server mode.
+
+ * sanity.sh (rcsmerge-symlink-4): Don't use `test -L', it's not
+ portable. Instead, match against the output of `ls -l'.
+ (dotest tag8k-16): Simplify tag-construction code and at the same
+ time, avoid using expr's `length' and `substr' operators. Not
+ all versions of expr support those.
+
+1998-08-14 Jim Kingdon <address@hidden>
+
+ * version.c: Bump version number to 1.10.0.1.
+
+Thu Aug 13 11:15:24 1998 Noel Cragg <address@hidden>
+
+ * version.c: Change version number to 1.10 and name to `Halibut'.
+
+ * sanity.sh (rcslib): new tests to check behavior of symlinks in
+ the repository.
+
+Wed Aug 12 15:39:38 1998 Noel Cragg <address@hidden>
+
+ * main.c (lookup_command_attribute): the `annotate' command
+ shouldn't require access to the repository. Add comment about
+ commands that do not use the working directory.
+
+Mon Aug 10 10:26:38 1998 Noel Cragg <address@hidden>
+
+ * version.c: Change version number to 1.9.30.
+
+Thu Aug 6 17:44:50 1998 Noel Cragg <address@hidden>
+
+ * server.c (serve_rdiff): change the name of the command (for
+ error reporting, etc.) from "patch" to "rdiff."
+ (serve_remove): rename from "cvsremove" to "remove."
+
+ * main.c (lookup_command_attribute): the `rdiff' command shouldn't
+ require write access to the repository.
+
+1998-08-06 David Masterson of kla-tencor.com
+ and Jim Kingdon
+
+ * commit.c (commit_filesdoneproc): Don't call strlen ("CVSROOT")
+ from within the assert statement. Apparently HP's cc compiler on
+ HPUX 10.20 has trouble with that.
+
+1998-08-06 Jim Kingdon <address@hidden>
+
+ * rcs.c (RCS_checkin): When adding branch, if there is a lock on
+ the branchpoint owned by someone else, leave it alone. This
+ restores CVS 1.9 (RCS 5.7) behavior, fixing a core dump.
+ * sanity.sh (reserved): New tests reserved-16 through reserved-19
+ test for this fix.
+
+1998-08-05 Jim Kingdon <address@hidden>
+
+ * sanity.sh (unedit-without-baserev): Use ${QUESTION} not "?".
+ This makes it work with GNU expr 1.12 as well as 1.16.
+
+Sun Aug 2 20:27:44 1998 Noel Cragg <address@hidden>
+
+ * mkmodules.c: add comment about TopLevelAdmin for the initial
+ contents of CVSROOT/config.
+
+1998-07-29 Jim Kingdon <address@hidden>
+
+ * rcs.c (RCS_checkin): Only try to call xreadlink if HAVE_READLINK
+ is defined.
+
+Tue Jul 28 19:33:08 1998 Noel Cragg <address@hidden>
+
+ * version.c: Change version number to 1.9.29.
+
+ * rcs.c (RCS_checkin): add code to follow symbolic links in the
+ repository.
+
+Sun Jul 26 05:14:41 1998 Noel Cragg <address@hidden>
+
+ * This set of changes reverts the code to pre-1.9.2 behavior and
+ does not create CVS directories at top-level (except for the
+ obvious "cvs co ."). Added a new configuration option to switch
+ between 1.9 and 1.9.2 behavior.
+
+ * recurse.c (do_argument_proc): new function.
+ (start_recursion): in the case that we've done a command from
+ top-level but have no CVS directory there, the behavior should be
+ the same as "cvs <cmd> dir1 dir2 dir3...". Make sure that the
+ appropriate "Argument" commands are sent to the server by calling
+ walklist with do_argument_proc.
+
+ * client.c (call_in_directory): only create the top-level CVS
+ directory when we're checking out "." explicitly. The server will
+ force creation of this directory in all other cases.
+
+ * checkout.c (checkout_proc): only generate the top-level
+ directory when the TopLevelAdmin=yes. Also send a message to the
+ client to do the same.
+
+ * parseinfo.c (parse_config): handle TopLevelAdmin option. Set
+ top_level_admin.
+
+ * main.c: add new variable top_level_admin.
+ * cvs.h: add extern definition for above.
+
+ * sanity.sh: since we're reverting to pre 1.9.2 behavior for
+ top-level CVS directories, I needed to make changes to a bunch of
+ tests that made assumptions about said directories.
+ (preamble): make sure to add read and execute access to everything
+ in TMPDIR before removing, since some tests make things read-only.
+ (basicb-1a, basicb-1b, basicb-9a, basicb-9b): use dotest_fail
+ because these tests check for the non-existant top-level CVS
+ directory.
+ (basicc-3, emptydir-6, emptydir-7, crerepos-6): use "rm -rf" so it
+ won't complain when trying to remove the non-existant top-level
+ CVS directory.
+ (106.5): remove imported-f2-orig.tmp.
+ (modules2-10, emptydir-4, abspath-1ba, abspath-1bb): cd into the
+ directory where files exist before using the "add" command so cvs
+ can find CVSROOT in CVS/Root.
+ (cvsadm-2): look at a different CVS/Repository file, since the
+ top-level one doesn't exist.
+ (taginfo-3): create the directory in the repository directly
+ rather than relying on the fact that the top-level CVS directory
+ was created in a previous test.
+ (serverpatch-6): update first-dir explicity, rather than relying
+ on the non-existant top-level CVS/Entries file.
+ (crerepos-18): look at CVS/Repository in a subdirectory rather
+ than in the non-existant top-level CVS directory.
+ (toplevel): add code to set TopLevelAdmin=yes.
+ (toplevel2): new tests -- same as toplevel, but TopLevelAdmin=no.
+
+1998-07-21 Jim Meyering <address@hidden>
+
+ * rcs.c (RCS_checkout): Hoist frees of rev and value.
+ Warn and return 1 in several cases rather than exiting via
+ `error (1, ...'. The latter could abort a multi-file commit
+ in mid-stream, leaving stale locks in the repository.
+
+1998-07-16 Jim Kingdon <address@hidden>
+
+ * build_src.com (rcscmds.c): Also look for include files in
+ [-.diff], just like Ian's 1998-06-18 change to Makefile.in
+
+1998-07-14 Jim Kingdon <address@hidden>
+
+ * tag.c (pretag_proc), rtag.c (pretag_proc): Don't pass RUN_REALLY
+ to run_exec. This means that taginfo does not get executed if the
+ global -n option is specified. Which makes it like loginfo, -i,
+ -e, -o, -t, -u in modules, editinfo, and verifymsg and unlike
+ commitinfo. The old behavior was pretty bad in the sense that it
+ doesn't provide any way to log only the tags which actually
+ happen.
+ * sanity.sh (taginfo): New tests taginfo-11 to taginfo-13, for this.
+
+1998-07-12 Jim Kingdon <address@hidden>
+
+ * sanity.sh (ann-id): Write the test so that it tests for the
+ current (buggy) behavior.
+
+ * sanity.sh (taginfo): Also clean up cvsroot/first-dir.
+
+1998-07-12 Jim Meyering <address@hidden>
+
+ * sanity.sh (ann-id): New (currently failing) test for bug in how
+ rcs keywords are expanded in the output of `cvs annotate'.
+
+1998-07-12 Jim Kingdon <address@hidden>
+
+ * sanity.sh (taginfo): Write the TESTDIR into the script rather
+ than having the script look at the environment. This means that
+ it will work if TESTDIR is set by sanity.sh as well as if
+ sanity.sh finds TESTDIR in the environment.
+
+1998-07-11 Jim Kingdon <address@hidden>
+
+ * tag.c (check_fileproc): Calculate the revision to be tagged the
+ same way that tag_fileproc does.
+ * sanity.sh (taginfo): New tests, test for this (before this fix,
+ brtag had said 1.1 not 1.1.2.1).
+
+1998-07-10 Jim Kingdon <address@hidden>
+
+ * sanity.sh (unedit-without-baserev): Also clean up "2" directory.
+
+1998-07-08 Jim Kingdon <address@hidden>
+
+ * edit.c (unedit_fileproc): If the Baserev file is missing, don't
+ get the working file from CVS/Base. The previous code could get
+ you version 1.1 of the working file and put 1.2 in CVS/Entries.
+ * sanity.sh (unedit-without-baserev): New tests test for this.
+
+1998-07-02 Jim Kingdon <address@hidden>
+
+ * sanity.sh (unedit-without-baserev): Move the test itself to be
+ in the same order as in the "tests" variable.
+
+1998-07-02 Ian Lance Taylor <address@hidden>
+
+ * rcscmds.c: Don't include <stdarg.h> or <vasprintf.h>. Don't
+ declare vasprintf.
+ (call_diff_printf_output): Remove.
+ (call_diff_stdout_callbacks): Don't initialize printf_output
+ field--it has been removed from the interface.
+ (call_diff_file_callbacks): Likewise.
+
+1998-07-01 Jim Meyering <address@hidden>
+
+ * edit.c (unedit_fileproc): Handle the case in which base_get
+ returns a NULL baserev. That happens when a file being `unedit'ed
+ exists in the CVS/Base directory, but isn't listed in the CVS/Baserev
+ file. The one case I've seen had no Baserev file at all. The symptom
+ (if you're lucky) is a segmentation fault upon unedit. If you use
+ SunOS4.1.4 for which printf prints NULL pointers as `(null)', your
+ unedit command will complete normally, but it will have corrupted
+ your CVS/Entries file and a subsequent update may result in an
+ assertion failure, a core dump, and a stale lock in the repository.
+ * sanity.sh (unedit-without-baserev): New test for this.
+
+1998-07-01 Andy Mortimer of aeat.co.uk
+ and Jim Kingdon <address@hidden>
+
+ * server.c (server_updated): Use a prototype if we are using them
+ for declarations.
+
+1998-06-29 Jim Kingdon <address@hidden>
+
+ * sanity.sh (commit-readonly): Protect keyword against expansion
+ in sanity.sh itself. Keep the keyword in the file which we check
+ in (or else this fails to test for the RCS_checkout change).
+
+1998-06-27 Jim Meyering <address@hidden>
+
+ * rcs.c (RCS_checkout): If opening the local workfile fails due to
+ lack of write access, try to chmod the file and retry the open.
+ Before, a commit could fail part way through merely because the
+ open to rewrite with newly expanded rcs keywords would fail. It's
+ easy to make this happen if you use `cvs -r' or CVSREAD and you
+ apply a patch to one of your read-only source files -- patch
+ preserves the read-only setting for the file and your next commit
+ will fail after committing that file, but before rewriting
+ (checking out) your working copy.
+ * sanity.sh (commit-readonly): New test for this.
+
+1998-06-25 Jim Kingdon <address@hidden>
+
+ * update.c (patch_file): Update comments regarding context diffs
+ to reflect diff library.
+
+1998-06-23 Jim Kingdon <address@hidden>
+
+ * sanity.sh (modules4): Add tests for reversing the order of the
+ "!first-dir/sdir" and "first-dir".
+
+1998-06-23 Jim Kingdon <address@hidden>
+ and Dave address@hidden
+
+ * sanity.sh (modes2): Touch the file before chmod'ing it.
+
+1998-06-21 Ian Lance Taylor <address@hidden>
+
+ * update.c (merge_files): Revert changes of 1998-06-19. Instead,
+ register a merged file with a dummy time stamp. Only set
+ last_register_time if we need to.
+ (join_file): Likewise. Always register a merged file, not just
+ when the merge fails.
+
+1998-06-21 Jim Kingdon <address@hidden>
+
+ * call_diff_write_output, call_diff_printf_output,
+ call_diff_flush_output, call_diff_write_stdout, call_diff_error,
+ call_diff_stdout_callbacks, call_diff_file_callbacks): Re-indent.
+
+1998-06-19 Ian Lance Taylor <address@hidden>
+
+ * update.c (merge_file): Make sure the time stamp of the file is
+ different from the time stamp we register in the Entries file.
+ (join_file): Likewise.
+
+1998-06-18 Ian Lance Taylor <address@hidden>
+
+ * rcscmds.c: Include <stdio.h>. Include either <stdarg.h> or
+ <varargs.h>. Declare vasprintf.
+ (call_diff_write_output): New static function.
+ (call_diff_printf_output): New static function.
+ (call_diff_flush_output): New static function.
+ (call_diff_write_stdout): New static function.
+ (call_diff_error): New static function.
+ (call_diff_stdout_callbacks): New static variable.
+ (call_diff_file_callbacks): New static variable.
+ (call_diff): Don't sleep. Use a callback structure when calling
+ the diff library.
+ (call_diff3): Likewise.
+
+ * rcscmds.c: Include diffrun.h.
+ (call_diff, call_diff3): Pass NULL callback parameter.
+ (diff_run, diff3_run): Don't declare.
+ * Makefile.in (rcscmds.o): New target, to use -I for diff
+ directory.
+ (zlib.o): Depend upon zlib.h.
+
+1998-06-09 Mike address@hidden
+
+ Make it compile with Sun's bundled K&R C compiler:
+ * rcs.c (count_delta_actions): Change to static to match
+ declaration.
+ * client.c (handle_wrapper_rcs_option): Rename error label to
+ handle_error to avoid clash with function name.
+
+1998-06-09 Jim Kingdon <address@hidden>
+
+ * rcs.c (RCS_delete_revs): If we are trying to delete all
+ revisions, give an error rather than assertion failed.
+ * sanity.sh (basicb): New tests basicb-o* test for this.
+
+1998-06-04 Jim Kingdon <address@hidden>
+
+ * add.c (add): Only send "Directory" requests if we need to.
+
+1998-06-02 Assar Westerlund <address@hidden>
+
+ * client.c: Check for HAVE_GSS_C_NT_HOSTBASED_SERVICE rather than
+ assuming that GSS_C_NT_HOSTBASED_SERVICE is a macro.
+ * server.c: Likewise.
+
+1998-06-02 Jim Kingdon <address@hidden>
+
+ * fileattr.c (fileattr_read): Check for NULL return from strchr.
+ * sanity.sh (devcom3): New test devcom3-10 checks for this.
+
+1998-06-01 Assar Westerlund <address@hidden>
+ and Ian Lance Taylor <address@hidden>
+
+ * client.c: If HAVE_GSSAPI_H, include <gssapi.h>. Only include
+ <gssapi/gssapi.h> if HAVE_GSSAPI_GSSAPI_H. Only include
+ <gssapi/gssapi_generic.h> if HAVE_GSSAPI_GSSAPI_GENERIC_H.
+ (GSS_C_NT_HOSTBASED_SERVICE): Define if not defined.
+ (connect_to_gserver): Use GSS_C_NT_HOSTBASED_SERVICE instead of
+ gss_nt_service_name.
+ * server.c: Same header file changes.
+ (GSS_C_NT_HOSTBASED_SERVICE): Define if not defined.
+ (gserver_authenticate_connection): Use GSS_C_NT_HOSTBASED_SERVICE
+ instead of gss_nt_service_name.
+
+1998-06-01 Jim Meyering <address@hidden>
+
+ * sanity.sh (tag8k): Add a test for the 1998-05-02 rcs.c bug fix.
+
+1998-05-26 Jim Kingdon <address@hidden>
+
+ * rcs.c (annotate): Call tag_check_valid like the other functions
+ which have a -r option.
+ * sanity.sh (ann): New test ann-14 tests for this.
+
+1998-05-24 Jim Kingdon <address@hidden>
+
+ * sanity.sh (importc): New tests importc-5 through importc-8 test
+ for a (fairly obscure) regression from CVS 1.9.
+
+1998-05-23 Jim Kingdon <address@hidden>
+
+ * sanity.sh (modules2): Add comment listing cvs release tests.
+ (info): New test info-cleanup-0 tests "cvs -n release".
+
+ * rcs.c (rcsbuf_getid): Remove semicolon at end of #undef. I'm
+ kind of surprised that compilers accepted this at all, but
+ removing it squelches a warning for some compilers.
+
+ * version.c: Change version number to 1.9.28.1.
+
+ * Version 1.9.28.
+
+1998-05-22 Jim Kingdon <address@hidden>
+
+ * rcs.c (RCS_cmp_file): Check for errors from CVS_FOPEN. This
+ restores the CVS 1.9 behavior (fatal error if we can't open the
+ file), and corrects an apparent oversight in Ian's 13 Apr 1997
+ change.
+ * sanity.sh (modes2): New test, tests for this.
+
+1998-05-22 Ian Lance Taylor <address@hidden>
+
+ * server.c (server_updated): Correct test for whether to unlink
+ the file.
+
+1998-05-20 Jim Kingdon <address@hidden>
+
+ * wrapper.c (wrap_add): Disable -t/-f wrappers at least until the
+ serious bug can be fixed.
+
+1998-05-15 Jim Kingdon <address@hidden>
+
+ * checkout.c (checkout): Call server_pathname_check on the
+ argument to "cvs co -d".
+ * server.c (server_pathname_check): Add comment about how we could
+ be handling absolute pathnames.
+ * sanity.sh (abspath): Rewrite the tests which run "cvs co -d /foo"
+ for remote, to reflect this.
+
+ * sanity.sh (abspath): Also do the "cannot rename" work-around for
+ abspath-7d.
+
+1998-05-13 Jim Kingdon <address@hidden>
+
+ * commit.c (commit_filesdoneproc): Free admin_dir when done with it.
+
+1998-05-13 Jim Meyering <address@hidden>
+
+ * sanity.sh (editor): Change bogus sed command, `s/^/x&/g', to `s/^/x/'.
+ The former exercised a bug in GNU sed-3.01-beta3.
+ (emptydir-8): Add `Rebuilding administrative file database' message,
+ since now it does that.
+ * commit.c (commit_filesdoneproc): Pass only the admin directory
+ pathname to mkmodules.
+ Remove #if 0, now that it's fixed.
+
+ * status.c (cvsstatus): Rename from `status' to avoid shadowing
+ lots of locals and parameters by the same name.
+ * server.c (serve_status): Update caller.
+ * main.c (cmds[]): Update table entry.
+ * cvs.h: Update prototype.
+
+ * commit.c (commit_filesdoneproc): Remove trailing blanks.
+ (commit) [CLIENT_SUPPORT]: Remove unnecessary (and local-shadowing)
+ declaration of `err'.
+ Rename global `tag' to `saved_tag' to avoid overshadowing `tag'
+ parameters of three functions.
+ Rename global `message' to `saved_message' to avoid overshadowing
+ `message' parameter of a function.
+ Rename global `ulist' to `saved_ulist' and move dcl up with others.
+
+1998-05-12 Jim Kingdon <address@hidden>
+
+ * commit.c (commit_filesdoneproc): #if 0 the new code until it can
+ be fixed.
+
+ * commit.c (commit_filesdoneproc): Add comment explaining last
+ change.
+
+1998-05-12 Jim Meyering <address@hidden>
+
+ * commit.c (commit_filesdoneproc): Call mkmodules not just when
+ committing a file directly under CVSROOT, but also when committing
+ files in subdirectories of CVSROOT.
+
+1998-05-08 Jim Meyering <address@hidden>
+
+ * filesubr.c (xreadlink): NUL-terminate the symbolic link name.
+ Use a much smaller initial buffer length.
+ Test errno only if readlink fails.
+ Use xstrdup then free the original link name so we don't waste space.
+
+1998-05-02 Jim Meyering <address@hidden>
+
+ * rcs.c (rcsbuf_getword): Fix off-by-one error that would result in
+ an abort (the first one in rcsbuf_getkey) when operating on on some
+ ,v files with over 8192 bytes of tag and branch info.
+
+1998-05-04 Jim Kingdon <address@hidden>
+
+ * sanity.sh (ann): New tests ann-12 and ann-13 test for specifying
+ a numeric branch.
+
+1998-05-02 Jim Kingdon <address@hidden>
+
+ * rcs.c: Add comments about getting rid of rcsbuf_getid,
+ rcsbuf_getword, and rcsbuf_getstring.
+
+ * sanity.sh (abspath): Revise the workarounds to deal with exit
+ status.
+
+1998-04-30 Jim Kingdon <address@hidden>
+
+ * sanity.sh (abspath): Work around the "cannot rename" bug.
+
+1998-04-27 Jim Kingdon <address@hidden>
+
+ * classify.c (Classify_File): Add comments about checking whether
+ command name is "update".
+
+1998-04-22 Jim Kingdon <address@hidden>
+
+ * version.c: Change version number to 1.9.27.1.
+
+ * Version 1.9.27.
+
+1998-04-20 Jim Kingdon <address@hidden>
+
+ (This diff was run by devel-cvs and everyone seemed to like it).
+ * diff.c (diff_file_nodiff): Make HEAD mean the head of the branch
+ which contains the sticky tag, not the sticky tag itself.
+ * rcs.c, rcs.h (RCS_branch_head): New function.
+ * sanity.sh (head): Update for this changed behavior.
+
+1998-04-19 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Move emptydir tests from basicb to new test emptydir.
+ This is because we now need a module definition to create Emptydir;
+ "co -d" doesn't cut it anymore.
+
+1998-04-17 Petri Virkkula
+
+ * server.c (mkdir_p): Ignore EROFS error (like for EACCES).
+
+1998-04-16 Jim Kingdon <address@hidden>
+
+ * checkout.c (checkout_proc): Don't create directories above the
+ last one specified in "co -d".
+ (build_dirs_and_chdir): Revert Noel's change of 17 Feb 1998.
+ (struct dir_to_build): New field just_chdir.
+ (build_dirs_and_chdir): Test it.
+ * sanity.sh (abspath): New tests abspath-7* test for a bug which
+ we fix, in which CVS would create bogus "D/////" entries in
+ CVS/Entries.
+ (abspath): Revise abspath-3* tests to test for the fact that we no
+ longer create directories above the last one specified in "co -d".
+ I checked that CVS 1.9 gives an error on this, so changing this
+ behavior back should be OK.
+ (cvsadm-2d3): Likewise (also checked CVS 1.9 for this case).
+ (cvsadm-2d3d): Likewise (also checked CVS 1.9 for this case).
+ (cvsadm-2d{4,5,6,7,8}, cvsadm-N2d{3,4,5,6,7,8}): Adjust for new
+ behavior (same case as cvsadm-2d3).
+ (cvsadm-2d{4,5,6,7,8}d, cvsadm-N2d{3,4,5,6,7,8}d): Remove test
+ (same case as cvsadm-2d3d).
+ (cvsadm): For remote, skip most these tests.
+ (abspath): When cleaning up, delete mod1 and mod2 rather than mod1
+ twice (longstanding bug, apparently only becomes visible if you
+ run the tests in a certain order).
+
+1998-04-14 Wilfredo Sanchez <address@hidden>
+
+ * rcs.c: variable "lockfile" was being referenced after being
+ free'd. Bad. Moved the free() call down.
+
+1998-04-12 Jim Kingdon <address@hidden>
+
+ * sanity.sh (rcs): Add test for annotate and the year 2000.
+
+ * server.c (do_cvs_command): If there are partial lines left when
+ the child process is done, send them along.
+ * sanity.sh (rcs, rcs2): Enable all tests for remote; tests for
+ this fix.
+
+1998-04-11 Jim Kingdon <address@hidden>
+
+ * client.c (client_senddate): Pass SDATEFORM not DATEFORM to
+ sscanf. This fixes a Y2K bug.
+
+ * history.c (history, select_hrec): Change since_date from time_t
+ to RCS format. Use the usual machinery (in particular, Make_Date
+ and client_senddate) so that it will work on VMS too.
+ * main.c, cvs.h (date_from_time_t): New function.
+ * sanity.sh (history): New test, to test that this didn't break
+ anything (also tests client_senddate fix).
+
+1998-04-11 Norbert Kiesel <address@hidden>
+
+ * server.c (cvs_output_binary): Shut up "gcc -Wall" by removing
+ unnecessary else if test.
+ * server.c (check_password): Fix uninitialized memory read if
+ shadow passwords are used. Also added some comments.
+ * rcs.c (RCS_checkout): Make sure to call chown with -1 for uid or
+ gid if they should not be changed
+
+1998-04-10 Jim Kingdon <address@hidden>
+
+ * sanity.sh (rcs2): New test, tests for various Y2K cases.
+ * rcs.c (getdelta): Value for "state" keyword is optional (bug
+ discovered incidentally in writing rcs2 test).
+
+1998-04-09 Jim Kingdon <address@hidden>
+
+ * filesubr.c, cvs.h (link_file): Remove; no longer used.
+
+1998-04-08 Jim Kingdon <address@hidden>
+
+ * recurse.c (do_dir_proc): Restore update_dir rather than a
+ computation which appears to, but does not necessarily, restore it
+ (reported by various people; this fix is from Greg Hudson).
+ * sanity.sh (importc): New test, tests for this fix.
+
+1998-03-27 Jim Kingdon <address@hidden>
+
+ * rcs.c (RCS_lock): If the revision is already locked, give an
+ error rather than dumping core.
+ * sanity.sh (reserved): New test reserved-13c tests for this.
+
+1998-03-25 Loren J. Rittle
+
+ * import.c (add_rev): Rewrite to use RCS_FLAGS_KEEPFILE option
+ of RCS_checkin() to avoid damage to imported files instead of
+ externally undoing damage after the fact. The side effect is
+ that callers of add_rev() may now incrementally walk the
+ entries of the current directory without seeing gratuitous
+ changes to the directory structure (under at least one file
+ system under at least one OS).
+
+1998-03-18 Jim Kingdon <address@hidden>
+
+ * error.c (error): Save and restore errno. Should fix test case
+ conflicts3-23 on SCO 5.0.2. Reported by Steve Cameron.
+
+ * sanity.sh (admin): Rename admin-26-o* to admin-26-*; the "o"
+ stands for "cvs admin -o". Add comment about length of tests.
+ Use ${PLUS}.
+
+1998-03-05 Dan Wilder <address@hidden>
+
+ * Fix problem with cvs admin in which -ntag:branch
+ option associated tag with the branch's head revision.
+ Should have used branch number. Entailed in this fix,
+ the following.
+
+ * Add new functions "RCS_exist_rev", "RCS_exist_tag",
+ "RCS_tag2rev", and "RCS_valid_rev" to rcs.c. RCS_tag2rev
+ is similar to RCS_gettag, but does less interpretation.
+
+ * Plug a small memory leak.
+
+ * Add tests admin-26 through admin-29 to sanity.sh,
+ to test "cvs admin -n".
+
+1998-03-17 Samuel Tardieu <address@hidden>
+
+ * server.c (server_register): protect dereferencing timestamp in
+ the trace message when it is null, to avoid a segmentation fault.
+
+1998-03-16 Jim Kingdon <address@hidden>
+
+ * options.h.in (MY_NDBM): Rewrite the comment explaining this
+ option. It was not clear to everyone who "my" referred to, for
+ example.
+
+ * hardlink.c (list_linked_files_on_disk): Remove unused variables
+ err and p.
+ (list_linked_files_on_disk): Add comment about memory allocation
+ of return value.
+ * rcs.c (rcsbuf_getword): Shut up gcc -Wall with a "return 0".
+ (RCS_checkin): Remove unused variable fullpath.
+ * sanity.sh (hardlinks): Remove comment about spurious warnings;
+ the warnings are gone.
+
+1998-03-12 Tim Pierce <address@hidden>
+
+ New functions for parsing and writing hardlink fields.
+ * rcs.c [PRESERVE_PERMISSIONS_SUPPORT] (puthardlink_proc): New
+ function.
+ (putdelta) [PRESERVE_PERMISSIONS_SUPPORT]: Use it.
+ (rcsbuf_getid, rcsbuf_getstring, rcsbuf_getword): New functions.
+ (getdelta): Call them, storing `hardlinks' field in vnode->hardlinks.
+ (RCS_reparsercsfile): When setting rdata->desc, xstrdup value
+ rather than rcsbuf_valcopying it (due to changes in how getdelta
+ handles keys and values in newphrases).
+
+ * sanity.sh (hardlinks): Use uglier filenames. Checking out
+ hardlinked files no longer produces the same spurious diagnostics,
+ so fix that test.
+ (hardlinks-2.3): Renamed from hardlinks-2.2 (duplicate test name).
+
+ New infrastructure for managing hardlink lists internally...
+ * hardlink.c, hardlink.h (list_linked_files_on_disk,
+ compare_linkage_lists, find_checkedout_proc): New functions.
+ * rcs.h (struct rcsversnode) [PRESERVE_PERMISSIONS_SUPPORT]: New
+ member `hardlinks'.
+ * update.c (special_file_mismatch): Get hardlinks from
+ vp->hardlinks instead of from vp->other_delta.
+ * rcs.c (free_rcsvers_contents): Comment about freeing hardlinks
+ member.
+ (RCS_checkout) [PRESERVE_PERMISSIONS_SUPPORT]: Get hardlinks from
+ vers->hardlinks list instead of vers->other_delta.
+
+ ... and removed obsolete code from earlier revs.
+ * hardlink.c, hardlink.h (list_files_linked_to,
+ cache_hardlinks_proc, list_files_proc, set_hardlink_field_proc):
+ Removed.
+ * hardlink.h: Removed `links' member from hardlink_info struct.
+ * commit.c (commit): Remove the call to cache_hardlinks_proc.
+ (check_fileproc) [PRESERVE_PERMISSIONS_SUPPORT]: Removed reference
+ to hlinfo->links.
+ * hardlink.c (update_hardlink_info): Same.
+ * update.c (get_linkinfo_proc): Same.
+
+ * rcs.c (RCS_checkout) [PRESERVE_PERMISSIONS_SUPPORT]: Use
+ vp->hardlinks and find_checkedout_proc to find recently-updated
+ files that may be hardlinked.
+ * update.c (special_file_mismatch): Use List * structures and
+ compare_linkage_lists for rev1_hardlinks and rev2_hardlinks.
+
+1998-03-16 Larry Jones <address@hidden>
+
+ * server.c (check_password): If shadow passwords are supported but no
+ entry is found in the shadow file, check the regular password file.
+
+1998-03-07 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Rename permissions test to perms since that is what
+ each of its individual tests are named.
+ * sanity.sh (perms symlinks hardlinks): Change CVSROOT to
+ CVSROOT_DIRNAME where appropriate.
+ (perms symlinks hardlinks): Disable/adjust the meat of the tests for
+ remote.
+ (symlinks): Link to ${TESTDIR}/fumble rather than
+ /fumble/mumble/grumble. We shouldn't be making assumptions about
+ what might exist in random directories outside ${TESTDIR}.
+ * hardlink.c (cache_hardlinks_proc): Add comment about trimming
+ whitespace.
+
+1998-03-07 Tim Pierce <address@hidden>
+
+ * rcs.c (RCS_checkout): Negation bug when checking out symlinks:
+ existence_error should be !existence_error.
+ * sanity.sh (permissions symlinks hardlinks): New tests, for
+ PreservePermissions.
+
+1998-03-04 Jim Kingdon <address@hidden>
+
+ * version.c: Change version number to 1.9.26.1.
+
+ * Version 1.9.26.
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+ and Ian Lance Taylor <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * rcs.c (rcs_internal_unlockfile): Pass errno when calling error
+ because ferror is true.
+
+1998-02-20 Jim Kingdon <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ [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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ [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 <address@hidden>
+ and Ian Lance Taylor <address@hidden>
+
+ 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * 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 <address@hidden>
+
+ * version.c: Change version number to 1.9.25.
+
+ * Version 1.9.24.
+
+ * sanity.sh (multibranch2): File file2 and tests multibranch2-13
+ through multibranch2-15 test a slightly different case than the
+ rest of multibranch2.
+
+ * mkmodules.c (cvswrappers_contents): Rewrite. The text didn't
+ describe -k and had various other problems.
+
+28 Jan 1998 Karl Fogel and Jim Kingdon
+
+ New feature to let server tell client about wrappers.
+ * client.h (struct response): Add comment about args being
+ '\0' terminated when passed to handle_* functions.
+ * client.c (start_server): send "wrapper-sendme-rcsOptions" to
+ server iff supported.
+ (responses): new response "Wrapper-rcsOption"; allows the server
+ to send certain lines from its cvswrappers file.
+ (handle_wrapper_rcs_option): new func, handles "Wrapper-rcsOption"
+ response from server.
+ * server.c (serve_wrapper_sendme_rcs_options): new func, sends
+ server side CVSROOT/cvswrappers rcs option lines to client.
+ (requests): new request "wrapper-sendme-rcsOptions"; if received,
+ we know we can send "Wrapper-rcsOption..." to the client.
+ * wrapper.c (wrap_unparse_rcs_options): new func; repeated calls
+ step down the wrapper list returning rcs option entries, but
+ repackaged as cvswrappers lines.
+ (wrap_setup): new guard variable `wrap_setup_already_done'; if
+ this function has run already, just return having done nothing.
+ Add comment concerning environment variable.
+ * cvs.h: declare wrap_unparse_rcs_options().
+
+Tue Jan 27 18:27:19 1998 Ian Lance Taylor <address@hidden>
+
+ * rtag.c (rtag_dirproc): Call ignore_directory, and skip the
+ directory if it returns true.
+ * sanity.sh (modules4): New set of tests to test some aspects of
+ excluding directories in the modules file, including the above
+ patch.
+
+Thu Jan 22 10:05:55 1998 Jim Kingdon <address@hidden>
+
+ * server.c (serve_kopt): Check for length of arg. Based on
+ inspection of the code, plugs a buffer overrun security hole which
+ was introduced Monday.
+
+ * server.c (serve_is_modified): Don't call xmalloc; we aren't
+ allowed to call error() here. Remove duplicate (and potentially
+ confusing) variable 'p'.
+
+ * log.c (log_fileproc): Look for first character of version
+ '0' AND second character '\0', rather than OR. I didn't try to
+ come up with a test case but this looks like a simple thinko
+ (albeit one which would show up in obscure cases if at all).
+
+Tue Jan 20 19:37:53 1998 Jim Kingdon <address@hidden>
+
+ * client.c (send_dirent_proc): Don't send nonexistent directories
+ unless noexec.
+ * sanity.sh (modules2): New tests modules2-13 through modules2-18
+ test for this fix.
+
+Mon Jan 19 11:17:51 1998 Jim Kingdon <address@hidden>
+
+ * server.c (serve_kopt): New function.
+ (requests): Add "Kopt" request.
+ (kopt): New variable.
+ (serve_is_modified): Write kopts from there into entries.
+ (serve_modified): Call serve_is_modified so we do the same.
+ Declare serve_modified and serve_is_modified.
+ * vers_ts.c (Version_TS): Set ->options even for a dummy ("D"
+ timestamp) entry.
+ * import.c (process_import_file): Check for -k options.
+ * client.c (client_process_import_file): Send Kopt request.
+ (send_fileproc): Likewise, for "cvs add".
+ * sanity.sh: Enable test binwrap3-sub2-add1 for remote.
+ Add -I .cvswrappers to binwrap3-2a; adjust binwrap3-2d
+ accordingly. Tests for this fix.
+
+Mon Jan 19 08:48:59 1998 Larry Jones <address@hidden>
+
+ * sanity.sh (errmsg1): Append test 168 output to log file.
+
+Sat Jan 17 08:01:51 1998 Jim Kingdon <address@hidden>
+
+ * sanity.sh (ann-10, ann-11): Don't make assumptions about the
+ number of characters in the username.
+
+Fri Jan 16 15:34:02 1998 Larry Jones <address@hidden>
+
+ * diff.c (diff_fileproc): Free label1 and label2 when finished.
+
+ * edit.c (editor_set): Don't free edlist until after we're
+ done using it.
+
+ * rcscmds.c (RCS_merge): Free xrev1 and xrev2 when finished.
+
+ * subr.c (make_message_rcslegal): Don't access uninitialized or
+ unallocated memory; only strip trailing blank lines.
+ * sanity.sh (log-3): Enhance to test this fix.
+
+Fri Jan 16 12:41:03 1998 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Add keywordlog to list of tests run by default.
+
+ * rcs.c (RCS_deltas): Don't call cvs_output if length is zero;
+ passing zero length to cvs_output does not mean output zero
+ bytes. The 27 Dec 1997 change to no longer '\0'-terminate the
+ ->text field turned this from a time bomb to a user-visible bug.
+ * sanity.sh (ann): New tests, test for this fix and other annotate
+ behaviors.
+
+Thu Jan 15 23:52:00 1998 Jim Kingdon <address@hidden>
+
+ * root.c (root_allow_ok): If inetd.conf didn't specify an
+ --allow-root options at all, we know we are in trouble. Give a
+ specific error message.
+
+Thu Jan 15 21:24:59 1998 Ian Lance Taylor <address@hidden>
+
+ * sanity.sh (dotest_sort): New variant of dotest which sorts the
+ output, for use when the output depends upon details of the file
+ system, typically when doing an import.
+ (rdiff): Use dotest_sort for rdiff-1.
+ (ignore): Use dotest_sort for 188a, 188b, 189d, 190, and 191.
+
+ * sanity.sh: (TESTSHELL): New variable.
+ (editor, info, reserved): Use TESTSHELL in temporary script.
+
+ * sanity.sh (ignore): Do all tests in subdirectory, to avoid
+ conflict between cvsroot and CVSROOT on Windows.
+ (binwrap3, mwrap, info, config): Likewise.
+
+ * sanity.sh (binfiles2): Correct test name binfile2-7-brmod to
+ binfiles2-7-brmod.
+
+ * release.c (release_delete): If __CYGWIN32__ is defined, don't
+ worry about mismatched inodes. This is a hack, but then I think
+ the test is rather peculiar anyhow.
+
+Thu Jan 15 16:07:36 1998 Larry Jones <address@hidden>
+
+ * sanity.sh (reserved-9): Use ${PROG} instead of "cvs".
+
+Wed Jan 14 15:43:13 1998 Jim Kingdon <address@hidden>
+
+ * Split ChangeLog into ChangeLog-97 and ChangeLog.
+ * Makefile.in (DISTFILES): Add ChangeLog-97.
+
+13 Jan 1998 Jim Kingdon
+
+ * client.c: Declare handle_mt.
+
+Tue Jan 13 22:21:30 1998 Jim Kingdon <address@hidden>
+
+ * sanity.sh: Add comment about how pwd and /bin/pwd often differ
+ in behavior (but are not guaranteed to).
+
+Tue Jan 13 13:49:53 1998 Ian Lance Taylor <address@hidden>
+
+ * sanity.sh: When setting TMPPWD use just pwd, not /bin/pwd.
+
+ * update.c (checkout_file): Don't pass set_time as true to
+ Version_TS if the file is dead.
+ * sanity.sh (modules): Add tests modules-155c6 through
+ modules-155c8 to test for above patch (without the above patch,
+ modules-155c8 will fail when remote).
+
+Tue Jan 13 10:37:02 1998 Larry Jones <address@hidden>
+
+ * client.c (send_modified): Change bufsize and newsize from int
+ to size_t to avoid type clashes in call to read_and_gzip.
+
+Tue Jan 13 10:33:02 1998 Larry Jones <address@hidden>
+
+ * zlib.c (read_and_gzip): Set finish to 0; it was uninitialized.
+
+Tue Jan 13 10:26:43 1998 Larry Jones <address@hidden>
+
+ * add.c, rcs.c: Plug memory leaks.
+
+Mon Jan 12 10:45:27 1998 Larry Jones <address@hidden>
+
+ * server.c (mkdir_p): Don't try to create nameless directories
+ (i.e., given "/foo//bar", don't try to create "/foo/",
+ just "/foo" and "/foo//bar") since it isn't necessary and
+ it fails on some systems in unexpected ways.
+
+1998-01-11 enami tsugutomo <address@hidden>
+
+ * rcs.c (linevector_copy): Delete lines before overwriting them.
+
+Sat Jan 10 11:05:40 1998 Jim Kingdon <address@hidden>
+
+ * cvsrc.c, entries.c, login.c, logmsg.c, myndbm.c, patch.c,
+ release.c, server.c: Check for errors from getline, CVS_FOPEN,
+ fprintf, CVS_UNLINK and fclose. Note that the new errors are
+ nonfatal. This is because of conservatism more than because
+ it is always the best thing.
+ * login.c (get_cvs_password): Close the file when done with it.
+ * client.c (notified_a_file): If -1 return from getline, check
+ feof rather than assuming errno is set.
+
+Fri Jan 9 14:38:54 1998 Jim Kingdon <address@hidden>
+
+ * server.c (expand_proc): Also output server_dir in
+ "Module-expansion", not just in output_dir ("Created", &c).
+ * sanity.sh (modules2): New tests modules2-9 through modules2-12
+ test for this.
+
+Thu Jan 8 12:56:55 1998 Yasutoshi Hiroe <address@hidden>
+
+ * import.c (import): Don't strcat on uninitialized memory. Fixes
+ possible SIGSEGV with zero-length message.
+
+Tue Jan 6 22:56:29 1998 Jim Kingdon <address@hidden>
+
+ * sanity.sh (crerepos): Fix mistaken variable name which caused us
+ not to clean up at the end of the test.
+
+Mon Dec 22 01:40:57 1997 Jim Kingdon <address@hidden>
+
+ * add.c (add): Also look for .cvswrappers files.
+ * sanity.sh (binwrap3): New tests binwrap3-2*, binwrap3-sub2-add*
+ test for this.
+
+Tue Jan 6 11:50:38 1998 Jim Kingdon <address@hidden>
+
+ * sanity.sh (crerepos): New tests crerepos-8 through crerepos-18
+ test behaviors when mixing repositories.
+
+Sun Jan 4 17:40:22 1998 Jim Kingdon <address@hidden>
+
+ * version.c: Change version number to 1.9.23.
+
+ * Version 1.9.22.
+
+
+For older changes see ChangeLog-97.
Index: ccvs/src/admin.c
diff -u /dev/null ccvs/src/admin.c:1.112.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/admin.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,1181 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Administration ("cvs admin")
+ *
+ */
+
+#include "cvs.h"
+#ifdef CVS_ADMIN_GROUP
+#include <grp.h>
+#endif
+
+static Dtype admin_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int admin_fileproc (void *callerdat, struct file_info *finfo);
+
+static const char *const admin_usage[] =
+{
+ "Usage: %s %s [options] files...\n",
+ "\t-a users Append (comma-separated) user names to access list.\n",
+ "\t-A file Append another file's access list.\n",
+ "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
+ "\t-c string Set comment leader.\n",
+ "\t-e[users] Remove (comma-separated) user names from access list\n",
+ "\t (all names if omitted).\n",
+ "\t-I Run interactively.\n",
+ "\t-k subst Set keyword substitution mode:\n",
+ "\t kv (Default) Substitute keyword and value.\n",
+ "\t kvl Substitute keyword, value, and locker (if any).\n",
+ "\t k Substitute keyword only.\n",
+ "\t o Preserve original string.\n",
+ "\t b Like o, but mark file as binary.\n",
+ "\t v Substitute value only.\n",
+ "\t-l[rev] Lock revision (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-L Set strict locking.\n",
+ "\t-m rev:msg Replace revision's log message.\n",
+ "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
+ "\t delete the tag; if rev is omitted, tag the latest\n",
+ "\t revision on the default branch.\n",
+ "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
+ "\t-o range Delete (outdate) specified range of revisions:\n",
+ "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
+ "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
+ "\t rev: rev and following revisions on the same branch.\n",
+ "\t rev:: After rev on the same branch.\n",
+ "\t :rev rev and previous revisions on the same branch.\n",
+ "\t ::rev Before rev on the same branch.\n",
+ "\t rev Just rev.\n",
+ "\t-q Run quietly.\n",
+ "\t-s state[:rev] Set revision state (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
+ "\t-t-string Set descriptive text.\n",
+ "\t-u[rev] Unlock the revision (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-U Unset strict locking.\n",
+ "\t--execute Turn on execute bits on repository file.\n",
+ "\t--no-execute Turn off execute bits on repository file.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+/* This structure is used to pass information through start_recursion. */
+struct admin_data
+{
+ /* Set default branch (-b). It is "-b" followed by the value
+ given, or NULL if not specified, or merely "-b" if -b is
+ specified without a value. */
+ char *branch;
+
+ /* Set comment leader (-c). It is "-c" followed by the value
+ given, or NULL if not specified. The comment leader is
+ relevant only for old versions of RCS, but we let people set it
+ anyway. */
+ char *comment;
+
+ /* Set strict locking (-L). */
+ int set_strict;
+
+ /* Set nonstrict locking (-U). */
+ int set_nonstrict;
+
+ /* Delete revisions (-o). It is "-o" followed by the value specified. */
+ char *delete_revs;
+
+ /* Keyword substitution mode (-k), e.g. "-kb". */
+ char *kflag;
+
+ /* Description (-t). */
+ char *desc;
+
+ /* Interactive (-I). Problematic with client/server. */
+ int interactive;
+
+ enum {AVOID = 0, NOEXECUTE, EXECUTE} execute;
+
+ /* This is the cheesy part. It is a vector with the options which
+ we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
+ this presumably will be replaced by other variables which break
+ out the data in a more convenient fashion. AV as well as each of
+ the strings it points to is malloc'd. */
+ int ac;
+ char **av;
+ int av_alloc;
+};
+
+/* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
+ argument to that option, or NULL if omitted (whether NULL can actually
+ happen depends on whether the option was specified as optional to
+ getopt). */
+static void
+arg_add (struct admin_data *dat, int opt, char *arg)
+{
+ char *newelt = Xasprintf ("-%c%s", opt, arg ? arg : "");
+
+ if (dat->av_alloc == 0)
+ {
+ dat->av_alloc = 1;
+ dat->av = xnmalloc (dat->av_alloc, sizeof (*dat->av));
+ }
+ else if (dat->ac >= dat->av_alloc)
+ {
+ dat->av_alloc *= 2;
+ dat->av = xnrealloc (dat->av, dat->av_alloc, sizeof (*dat->av));
+ }
+ dat->av[dat->ac++] = newelt;
+}
+
+
+
+/*
+ * callback proc to run a script when admin finishes.
+ */
+static int
+postadmin_proc (const char *repository, const char *filter, void *closure)
+{
+ char *cmdline;
+ const char *srepos = Short_Repository (repository);
+
+ TRACE (TRACE_FUNCTION, "postadmin_proc (%s, %s)", repository, filter);
+
+ /* %c = cvs_cmd_name
+ * %R = referrer
+ * %p = shortrepos
+ * %r = repository
+ */
+ /*
+ * Cast any NULL arguments as appropriate pointers as this is an
+ * stdarg function and we need to be certain the caller gets what
+ * is expected.
+ */
+ cmdline = format_cmdline (
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ false, srepos,
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ filter,
+ "c", "s", cvs_cmd_name,
+#ifdef SERVER_SUPPORT
+ "R", "s", referrer ? referrer->original : "NONE",
+#endif /* SERVER_SUPPORT */
+ "p", "s", srepos,
+ "r", "s", current_parsed_root->directory,
+ (char *) NULL);
+
+ if (!cmdline || !strlen (cmdline))
+ {
+ if (cmdline) free (cmdline);
+ error (0, 0, "postadmin proc resolved to the empty string!");
+ return 1;
+ }
+
+ run_setup (cmdline);
+
+ free (cmdline);
+
+ /* FIXME - read the comment in verifymsg_proc() about why we use abs()
+ * below() and shouldn't.
+ */
+ return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ RUN_NORMAL | RUN_SIGIGNORE));
+}
+
+
+
+/*
+ * Call any postadmin procs.
+ */
+static int
+admin_filesdoneproc (void *callerdat, int err, const char *repository,
+ const char *update_dir, List *entries)
+{
+ TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository,
+ update_dir);
+ Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL,
+ NULL);
+
+ return err;
+}
+
+
+
+static const char short_options[] =
+ "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:";
+
+enum {OPT_NONE = 0, OPT_EXECUTE, OPT_NOEXECUTE} opt_values;
+static struct option long_options[] =
+{
+ {"execute", 0, NULL, OPT_EXECUTE},
+ {"no-execute", 0, NULL, OPT_NOEXECUTE},
+ {0, 0, NULL, OPT_NONE}
+};
+
+
+
+/* Accept a `;' delimited string and break it into tokens. Allocate a
+ * return string. Copy the first token into the return string
+ * checking to be sure that each character is a valid option character
+ * of the short_options string. For remaining tokens, convert to the
+ * long option VAL (from the global LONG_OPTIONS above) and append
+ * that char to the return value. When long option tokens are
+ * unrecognized, a warning is printed and they are ignored.
+ *
+ * i.e., S will be of the format `[SHORTOPTIONS][;LONGOPTION]...'. It is
+ * perfectly acceptable for SHORTOPTIONS to resolve to the empty string,
+ * an empty LONGOPTION will also be ignored.
+ */
+char *
+make_UserAdminOptions (const char *infopath, unsigned int ln, const char *s)
+{
+ const char *cur_opt, *next_opt;
+ size_t len;
+ char *ns;
+
+ assert (s);
+
+ cur_opt = s;
+
+ next_opt = strchr (cur_opt, ';');
+ if (next_opt)
+ len = next_opt - cur_opt;
+ else
+ len = strlen (cur_opt);
+
+ ns = xmalloc (len + 1);
+ *ns = '\0';
+ if (len > 0)
+ {
+ const char *p;
+ size_t nspos = 0;
+ /* validate short options */
+ for (p = cur_opt; p < (cur_opt + len); p++)
+ {
+ if (*p == '+' || *p == ':' || strchr (short_options, *p) == NULL)
+ error (0, 0,
+ "%s [%u]: Unrecognized short admin option `%c'.",
+ infopath, ln, *p);
+ else
+ ns[nspos++] = *p;
+ }
+ ns[nspos] = '\0';
+ if (nspos > 0)
+ TRACE (TRACE_FUNCTION, "Setting short UserAdminOptions `%s'", ns);
+ }
+
+ /* process long options (if any) */
+ while ((cur_opt = next_opt))
+ {
+ next_opt = strchr (++cur_opt, ';');
+
+ if (next_opt)
+ len = next_opt - cur_opt;
+ else
+ len = strlen (cur_opt);
+
+ /* ignore empty long options (ie, ';;') */
+ if (len > 0)
+ {
+ struct option *found;
+
+ for (found = long_options; found->name; found++)
+ if (len == strlen (found->name)
+ && !strncmp (cur_opt, found->name, len))
+ break;
+
+ if (found->name)
+ {
+ size_t nslen = strlen (ns);
+
+ assert (found->val);
+
+ ns = xrealloc (ns, nslen + 2);
+ ns[nslen++] = found->val;
+ ns[nslen] = '\0';
+ TRACE (TRACE_FUNCTION, "Adding long UserAdminOptions `%s'",
+ found->name);
+ }
+ else
+ {
+ char *tmp = xmalloc (len + 1);
+ strncpy (tmp, cur_opt, len);
+ tmp[len] = '\0';
+ error (0, 0,
+ "%s [%u]: Unrecognized long admin option `%s'.",
+ infopath, ln, tmp);
+ free (tmp);
+ }
+ }
+ }
+
+ return ns;
+}
+
+
+
+int
+admin (int argc, char **argv)
+{
+ int err;
+#ifdef CVS_ADMIN_GROUP
+ struct group *grp;
+ struct group *getgrnam (const char *);
+#endif
+ struct admin_data admin_data;
+ int c;
+ int i;
+ bool only_allowed_options;
+
+ if (argc <= 1)
+ usage (admin_usage);
+
+ wrap_setup ();
+
+ memset (&admin_data, 0, sizeof admin_data);
+
+ /* TODO: get rid of `-' switch notation in admin_data. For
+ example, admin_data->branch should be not `-bfoo' but simply `foo'. */
+
+ optind = 0;
+ only_allowed_options = true;
+ while ((c = getopt_long
+ (argc, argv, short_options, long_options, NULL))
+ != EOF)
+ {
+ if (
+# ifdef CLIENT_SUPPORT
+ !current_parsed_root->isremote &&
+# endif /* CLIENT_SUPPORT */
+ c != 'q' && !strchr (config->UserAdminOptions, c)
+ )
+ only_allowed_options = false;
+
+ switch (c)
+ {
+ case OPT_EXECUTE: /* --execute */
+ admin_data.execute = EXECUTE;
+ break;
+
+ case OPT_NOEXECUTE: /* --no-execute */
+ admin_data.execute = NOEXECUTE;
+ break;
+
+ case 'i':
+ /* This has always been documented as useless in cvs.texinfo
+ and it really is--admin_fileproc silently does nothing
+ if vers->vn_user is NULL. */
+ error (0, 0, "the -i option to admin is not supported");
+ error (0, 0, "run add or import to create an RCS file");
+ goto usage_error;
+
+ case 'b':
+ if (admin_data.branch != NULL)
+ {
+ error (0, 0, "duplicate 'b' option");
+ goto usage_error;
+ }
+ if (optarg == NULL)
+ admin_data.branch = xstrdup ("-b");
+ else
+ admin_data.branch = Xasprintf ("-b%s", optarg);
+ break;
+
+ case 'c':
+ if (admin_data.comment != NULL)
+ {
+ error (0, 0, "duplicate 'c' option");
+ goto usage_error;
+ }
+ admin_data.comment = Xasprintf ("-c%s", optarg);
+ break;
+
+ case 'a':
+ arg_add (&admin_data, 'a', optarg);
+ break;
+
+ case 'A':
+ /* In the client/server case, this is cheesy because
+ we just pass along the name of the RCS file, which
+ then will want to exist on the server. This is
+ accidental; having the client specify a pathname on
+ the server is not a design feature of the protocol. */
+ arg_add (&admin_data, 'A', optarg);
+ break;
+
+ case 'e':
+ arg_add (&admin_data, 'e', optarg);
+ break;
+
+ case 'l':
+ /* Note that multiple -l options are valid. */
+ arg_add (&admin_data, 'l', optarg);
+ break;
+
+ case 'u':
+ /* Note that multiple -u options are valid. */
+ arg_add (&admin_data, 'u', optarg);
+ break;
+
+ case 'L':
+ /* Probably could also complain if -L is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -L. */
+ if (admin_data.set_nonstrict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_strict = 1;
+ break;
+
+ case 'U':
+ /* Probably could also complain if -U is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -U. */
+ if (admin_data.set_strict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_nonstrict = 1;
+ break;
+
+ case 'n':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -n options are
+ valid. */
+ arg_add (&admin_data, 'n', optarg);
+ break;
+
+ case 'N':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -N options are
+ valid. */
+ arg_add (&admin_data, 'N', optarg);
+ break;
+
+ case 'm':
+ /* Change log message. Could also be parsing the syntax
+ of optarg, although for now we just pass it to rcs
+ as-is. Note that multiple -m options are valid. */
+ arg_add (&admin_data, 'm', optarg);
+ break;
+
+ case 'o':
+ /* Delete revisions. Probably should also be parsing the
+ syntax of optarg, so that the client can give errors
+ rather than making the server take care of that.
+ Other than that I'm not sure whether it matters much
+ whether we parse it here or in admin_fileproc.
+
+ Note that multiple -o options are invalid, in RCS
+ as well as here. */
+
+ if (admin_data.delete_revs != NULL)
+ {
+ error (0, 0, "duplicate '-o' option");
+ goto usage_error;
+ }
+ admin_data.delete_revs = Xasprintf ("-o%s", optarg);
+ break;
+
+ case 's':
+ /* Note that multiple -s options are valid. */
+ arg_add (&admin_data, 's', optarg);
+ break;
+
+ case 't':
+ if (admin_data.desc != NULL)
+ {
+ error (0, 0, "duplicate 't' option");
+ goto usage_error;
+ }
+ if (optarg != NULL && optarg[0] == '-')
+ admin_data.desc = xstrdup (optarg + 1);
+ else
+ {
+ size_t bufsize = 0;
+ size_t len;
+
+ get_file (optarg, optarg, "r", &admin_data.desc,
+ &bufsize, &len);
+ }
+ break;
+
+ case 'I':
+ /* At least in RCS this can be specified several times,
+ with the same meaning as being specified once. */
+ admin_data.interactive = 1;
+ break;
+
+ case 'q':
+ /* Silently set the global really_quiet flag. This keeps admin
in
+ * sync with the RCS man page and allows us to silently support
+ * older servers when necessary.
+ *
+ * Some logic says we might want to output a deprecation warning
+ * here, but I'm opting not to in order to stay quietly in sync
+ * with the RCS man page.
+ */
+ really_quiet = 1;
+ break;
+
+ case 'x':
+ error (0, 0, "the -x option has never done anything useful");
+ error (0, 0, "RCS files in CVS always end in ,v");
+ goto usage_error;
+
+ case 'V':
+ /* No longer supported. */
+ error (0, 0, "the `-V' option is obsolete");
+ break;
+
+ case 'k':
+ if (admin_data.kflag != NULL)
+ {
+ error (0, 0, "duplicate '-k' option");
+ goto usage_error;
+ }
+ admin_data.kflag = RCS_check_kflag (optarg);
+ break;
+ default:
+ case '?':
+ /* getopt will have printed an error message. */
+
+ usage_error:
+ /* Don't use cvs_cmd_name; it might be "server". */
+ error (1, 0, "specify %s -H admin for usage information",
+ program_name);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CVS_ADMIN_GROUP
+ /* The use of `cvs admin -k' is unrestricted. However, any other
+ option is restricted if the group CVS_ADMIN_GROUP exists on the
+ server. */
+ /* This is only "secure" on the server, since the user could edit the
+ * RCS file on a local host, but some people like this kind of
+ * check anyhow. The alternative would be to check only when
+ * (server_active) rather than when not on the client.
+ */
+ if (!current_parsed_root->isremote && !only_allowed_options &&
+ (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
+ {
+#ifdef HAVE_GETGROUPS
+ gid_t *grps;
+ int n;
+
+ /* get number of auxiliary groups */
+ n = getgroups (0, NULL);
+ if (n < 0)
+ error (1, errno, "unable to get number of auxiliary groups");
+ grps = xnmalloc (n + 1, sizeof *grps);
+ n = getgroups (n, grps);
+ if (n < 0)
+ error (1, errno, "unable to get list of auxiliary groups");
+ grps[n] = getgid ();
+ for (i = 0; i <= n; i++)
+ if (grps[i] == grp->gr_gid) break;
+ free (grps);
+ if (i > n)
+ error (1, 0, "usage is restricted to members of the group `%s'",
+ CVS_ADMIN_GROUP);
+#else
+ char *me = getcaller ();
+ char **grnam;
+
+ for (grnam = grp->gr_mem; *grnam; grnam++)
+ if (strcmp (*grnam, me) == 0) break;
+ if (!*grnam && getgid () != grp->gr_gid)
+ error (1, 0, "usage is restricted to members of the group %s",
+ CVS_ADMIN_GROUP);
+#endif
+ }
+#endif /* defined CVS_ADMIN_GROUP */
+
+ for (i = 0; i < admin_data.ac; ++i)
+ {
+ assert (admin_data.av[i][0] == '-');
+ switch (admin_data.av[i][1])
+ {
+ case 'm':
+ case 'l':
+ case 'u':
+ check_numeric (&admin_data.av[i][2], argc, argv);
+ break;
+ default:
+ break;
+ }
+ }
+ if (admin_data.branch != NULL)
+ check_numeric (admin_data.branch + 2, argc, argv);
+ if (admin_data.delete_revs != NULL)
+ {
+ char *p;
+
+ check_numeric (admin_data.delete_revs + 2, argc, argv);
+ p = strchr (admin_data.delete_revs + 2, ':');
+ if (p != NULL && isdigit ((unsigned char) p[1]))
+ check_numeric (p + 1, argc, argv);
+ else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
+ check_numeric (p + 2, argc, argv);
+ }
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ /* We're the client side. Fire up the remote server. */
+ start_server ();
+
+ ign_setup ();
+
+ /* Note that option_with_arg does not work for us, because some
+ of the options must be sent without a space between the option
+ and its argument. */
+ if (admin_data.interactive)
+ error (1, 0, "-I option not useful with client/server");
+ if (admin_data.branch != NULL)
+ send_arg (admin_data.branch);
+ if (admin_data.comment != NULL)
+ send_arg (admin_data.comment);
+ if (admin_data.set_strict)
+ send_arg ("-L");
+ if (admin_data.set_nonstrict)
+ send_arg ("-U");
+ if (admin_data.delete_revs != NULL)
+ send_arg (admin_data.delete_revs);
+ if (admin_data.execute == EXECUTE)
+ send_arg ("--execute");
+ else if (admin_data.execute == NOEXECUTE)
+ send_arg ("--no-execute");
+ if (admin_data.desc != NULL)
+ {
+ char *p = admin_data.desc;
+ send_to_server ("Argument -t-", 0);
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ send_to_server ("\012Argumentx ", 0);
+ ++p;
+ }
+ else
+ {
+ char *q = strchr (p, '\n');
+ if (q == NULL) q = p + strlen (p);
+ send_to_server (p, q - p);
+ p = q;
+ }
+ }
+ send_to_server ("\012", 1);
+ }
+ /* Send this for all really_quiets since we know that it will be
silently
+ * ignored when unneeded. This supports old servers.
+ */
+ if (really_quiet)
+ send_arg ("-q");
+ if (admin_data.kflag != NULL)
+ send_arg (admin_data.kflag);
+
+ for (i = 0; i < admin_data.ac; ++i)
+ send_arg (admin_data.av[i]);
+
+ send_arg ("--");
+ send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("admin\012", 0);
+ err = get_responses_and_close ();
+ goto return_it;
+ }
+#endif /* CLIENT_SUPPORT */
+
+ lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
+
+ err = start_recursion
+ (admin_fileproc, admin_filesdoneproc, admin_dirproc,
+ NULL, &admin_data,
+ argc, argv, 0,
+ W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL);
+
+ Lock_Cleanup ();
+
+/* This just suppresses a warning from -Wall. */
+#ifdef CLIENT_SUPPORT
+ return_it:
+#endif /* CLIENT_SUPPORT */
+ if (admin_data.branch != NULL)
+ free (admin_data.branch);
+ if (admin_data.comment != NULL)
+ free (admin_data.comment);
+ if (admin_data.delete_revs != NULL)
+ free (admin_data.delete_revs);
+ if (admin_data.kflag != NULL)
+ free (admin_data.kflag);
+ if (admin_data.desc != NULL)
+ free (admin_data.desc);
+ for (i = 0; i < admin_data.ac; ++i)
+ free (admin_data.av[i]);
+ if (admin_data.av != NULL)
+ free (admin_data.av);
+
+ return err;
+}
+
+
+
+/*
+ * Called to run "rcs" on a particular file.
+ */
+/* ARGSUSED */
+static int
+admin_fileproc (void *callerdat, struct file_info *finfo)
+{
+ struct admin_data *admin_data = callerdat;
+ Vers_TS *vers;
+ char *version;
+ int i;
+ int status = 0;
+ RCSNode *rcs, *rcs2;
+
+ vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
+
+ version = vers->vn_user;
+ if (version != NULL && strcmp (version, "0") == 0)
+ {
+ error (0, 0, "cannot admin newly added file `%s'", finfo->file);
+ status = 1;
+ goto exitfunc;
+ }
+
+ rcs = vers->srcfile;
+ if (rcs == NULL)
+ {
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", finfo->file);
+ status = 1;
+ goto exitfunc;
+ }
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (!really_quiet)
+ {
+ cvs_output ("RCS file: ", 0);
+ cvs_output (rcs->path, 0);
+ cvs_output ("\n", 1);
+ }
+
+ if (admin_data->branch != NULL)
+ {
+ char *branch = &admin_data->branch[2];
+ if (*branch != '\0' && !isdigit ((unsigned char) *branch))
+ {
+ branch = RCS_whatbranch (rcs, admin_data->branch + 2);
+ if (branch == NULL)
+ {
+ error (0, 0, "%s: Symbolic name %s is undefined.",
+ rcs->path, admin_data->branch + 2);
+ status = 1;
+ }
+ }
+ if (status == 0)
+ RCS_setbranch (rcs, branch);
+ if (branch != NULL && branch != &admin_data->branch[2])
+ free (branch);
+ }
+ if (admin_data->comment != NULL)
+ {
+ if (rcs->comment != NULL)
+ free (rcs->comment);
+ rcs->comment = xstrdup (admin_data->comment + 2);
+ }
+ if (admin_data->set_strict)
+ rcs->strict_locks = 1;
+ if (admin_data->set_nonstrict)
+ rcs->strict_locks = 0;
+ if (admin_data->delete_revs != NULL)
+ {
+ char *s, *t, *rev1, *rev2;
+ /* Set for :, clear for ::. */
+ int inclusive;
+ char *t2;
+
+ s = admin_data->delete_revs + 2;
+ inclusive = 1;
+ t = strchr (s, ':');
+ if (t != NULL)
+ {
+ if (t[1] == ':')
+ {
+ inclusive = 0;
+ t2 = t + 2;
+ }
+ else
+ t2 = t + 1;
+ }
+
+ /* Note that we don't support '-' for ranges. RCS considers it
+ obsolete and it is problematic with tags containing '-'. "cvs log"
+ has made the same decision. */
+
+ if (t == NULL)
+ {
+ /* -orev */
+ rev1 = xstrdup (s);
+ rev2 = xstrdup (s);
+ }
+ else if (t == s)
+ {
+ /* -o:rev2 */
+ rev1 = NULL;
+ rev2 = xstrdup (t2);
+ }
+ else
+ {
+ *t = '\0';
+ rev1 = xstrdup (s);
+ *t = ':'; /* probably unnecessary */
+ if (*t2 == '\0')
+ /* -orev1: */
+ rev2 = NULL;
+ else
+ /* -orev1:rev2 */
+ rev2 = xstrdup (t2);
+ }
+
+ if (rev1 == NULL && rev2 == NULL)
+ {
+ /* RCS segfaults if `-o:' is given */
+ error (0, 0, "no valid revisions specified in `%s' option",
+ admin_data->delete_revs);
+ status = 1;
+ }
+ else
+ {
+ status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
+ if (rev1)
+ free (rev1);
+ if (rev2)
+ free (rev2);
+ }
+ }
+ if (admin_data->desc != NULL)
+ {
+ free (rcs->desc);
+ rcs->desc = xstrdup (admin_data->desc);
+ }
+ if (admin_data->kflag != NULL)
+ {
+ char *kflag = admin_data->kflag + 2;
+ char *oldexpand = RCS_getexpand (rcs);
+ if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
+ RCS_setexpand (rcs, kflag);
+ }
+
+ /* Handle miscellaneous options. TODO: decide whether any or all
+ of these should have their own fields in the admin_data
+ structure. */
+ for (i = 0; i < admin_data->ac; ++i)
+ {
+ char *arg;
+ char *p, *rev, *revnum, *tag, *msg;
+ char **users;
+ int argc, u;
+ Node *n;
+ RCSVers *delta;
+
+ arg = admin_data->av[i];
+ switch (arg[1])
+ {
+ case 'a': /* fall through */
+ case 'e':
+ line2argv (&argc, &users, arg + 2, " ,\t\n");
+ if (arg[1] == 'a')
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ else if (argc == 0)
+ RCS_delaccess (rcs, NULL);
+ else
+ for (u = 0; u < argc; ++u)
+ RCS_delaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'A':
+
+ /* See admin-19a-admin and friends in sanity.sh for
+ relative pathnames. It makes sense to think in
+ terms of a syntax which give pathnames relative to
+ the repository or repository corresponding to the
+ current directory or some such (and perhaps don't
+ include ,v), but trying to worry about such things
+ is a little pointless unless you first worry about
+ whether "cvs admin -A" as a whole makes any sense
+ (currently probably not, as access lists don't
+ affect the behavior of CVS). */
+
+ rcs2 = RCS_parsercsfile (arg + 2);
+ if (rcs2 == NULL)
+ error (1, 0, "cannot continue");
+
+ p = xstrdup (RCS_getaccess (rcs2));
+ line2argv (&argc, &users, p, " \t\n");
+ free (p);
+ freercsnode (&rcs2);
+
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'n': /* fall through */
+ case 'N':
+ if (arg[2] == '\0')
+ {
+ cvs_outerr ("missing symbolic name after ", 0);
+ cvs_outerr (arg, 0);
+ cvs_outerr ("\n", 1);
+ break;
+ }
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ if (RCS_deltag (rcs, arg + 2) != 0)
+ {
+ error (0, 0, "%s: Symbolic name %s is undefined.",
+ rcs->path,
+ arg + 2);
+ status = 1;
+ continue;
+ }
+ break;
+ }
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+
+ /* Option `n' signals an error if this tag is already bound. */
+ if (arg[1] == 'n')
+ {
+ n = findnode (RCS_symbols (rcs), tag);
+ if (n != NULL)
+ {
+ error (0, 0,
+ "%s: symbolic name %s already bound to %s",
+ rcs->path,
+ tag, (char *)n->data);
+ status = 1;
+ free (tag);
+ continue;
+ }
+ }
+
+ /* Attempt to perform the requested tagging. */
+
+ if ((*p == 0 && (rev = RCS_head (rcs)))
+ || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
+ {
+ RCS_check_tag (tag); /* exit if not a valid tag */
+ RCS_settag (rcs, tag, rev);
+ free (rev);
+ }
+ else
+ {
+ if (!really_quiet)
+ error (0, 0,
+ "%s: Symbolic name or revision %s is undefined.",
+ rcs->path, p);
+ status = 1;
+ }
+ free (tag);
+ break;
+ case 's':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ tag = xstrdup (arg + 2);
+ rev = RCS_head (rcs);
+ if (!rev)
+ {
+ error (0, 0, "No head revision in archive file `%s'.",
+ rcs->path);
+ status = 1;
+ continue;
+ }
+ }
+ else
+ {
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+ rev = xstrdup (p);
+ }
+ revnum = RCS_gettag (rcs, rev, 0, NULL);
+ if (revnum != NULL)
+ {
+ n = findnode (rcs->versions, revnum);
+ free (revnum);
+ }
+ else
+ n = NULL;
+ if (n == NULL)
+ {
+ error (0, 0,
+ "%s: can't set state of nonexisting revision %s",
+ rcs->path,
+ rev);
+ free (rev);
+ status = 1;
+ continue;
+ }
+ free (rev);
+ delta = n->data;
+ free (delta->state);
+ delta->state = tag;
+ break;
+
+ case 'm':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ error (0, 0, "%s: -m option lacks revision number",
+ rcs->path);
+ status = 1;
+ continue;
+ }
+ *p = '\0'; /* temporarily make arg+2 its own string */
+ rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
+ if (rev == NULL)
+ {
+ error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
+ status = 1;
+ *p = ':'; /* restore the full text of the -m argument */
+ continue;
+ }
+ msg = p+1;
+
+ n = findnode (rcs->versions, rev);
+ /* tags may exist against non-existing versions */
+ if (n == NULL)
+ {
+ error (0, 0, "%s: no such revision %s: %s",
+ rcs->path, arg+2, rev);
+ status = 1;
+ *p = ':'; /* restore the full text of the -m argument */
+ free (rev);
+ continue;
+ }
+ *p = ':'; /* restore the full text of the -m argument */
+ free (rev);
+
+ delta = n->data;
+ if (delta->text == NULL)
+ {
+ delta->text = xmalloc (sizeof (Deltatext));
+ memset (delta->text, 0, sizeof (Deltatext));
+ }
+ delta->text->version = xstrdup (delta->version);
+ delta->text->log = make_message_rcsvalid (msg);
+ break;
+
+ case 'l':
+ status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ case 'u':
+ status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ default: assert(0); /* can't happen */
+ }
+ }
+
+ if (status == 0)
+ {
+ RCS_rewrite (rcs, NULL, NULL);
+
+ /*
+ * Update the execute bit for the file if requested.
+ */
+ if (admin_data->execute != AVOID)
+ {
+ struct stat sb;
+
+ if (stat (rcs->path, &sb) < 0)
+ error (0, errno, "cannot stat `%s'", rcs->path);
+ else
+ {
+ mode_t mode;
+
+ if (admin_data->execute == EXECUTE)
+ mode = (sb.st_mode
+ | (((sb.st_mode & S_IRUSR) ? S_IXUSR : 0)
+ | ((sb.st_mode & S_IRGRP) ? S_IXGRP : 0)
+ | ((sb.st_mode & S_IROTH) ? S_IXOTH : 0)));
+ else /* admin_data->execute == NOEXECUTE */
+ mode = (sb.st_mode
+ & ~(S_IEXEC | S_IXGRP | S_IXOTH));
+
+ if (mode == sb.st_mode)
+ error (0, 0, "%s: already has mode=0%o",
+ rcs->path, (unsigned int) mode);
+ else
+ {
+ TRACE (TRACE_FLOW, "chmod(%s,0%o)", rcs->path,
+ (unsigned int) mode);
+
+ if (!noexec)
+ {
+ if (chmod (rcs->path, mode) < 0)
+ error (0, errno,
+ "cannot change mode of file `%s'",
+ rcs->path);
+ }
+ }
+ }
+ }
+
+ if (!really_quiet)
+ cvs_output ("done\n", 5);
+ }
+ else
+ {
+ /* Note that this message should only occur after another
+ message has given a more specific error. The point of this
+ additional message is to make it clear that the previous problems
+ caused CVS to forget about the idea of modifying the RCS file. */
+ if (!really_quiet)
+ error (0, 0, "RCS file for `%s' not modified.", finfo->file);
+ RCS_abandon (rcs);
+ }
+
+ exitfunc:
+ freevers_ts (&vers);
+ return status;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+admin_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!quiet)
+ error (0, 0, "Administrating %s", update_dir);
+ return R_PROCESS;
+}
Index: ccvs/src/annotate.c
diff -u /dev/null ccvs/src/annotate.c:1.21.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/annotate.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Show last revision where each line modified
+ *
+ * Prints the specified files with each line annotated with the revision
+ * number where it was last modified. With no argument, annotates all
+ * all the files in the directory (recursive by default).
+ */
+
+#include "cvs.h"
+
+/* Options from the command line. */
+
+static int force_tag_match = 1;
+static int force_binary = 0;
+static char *tag = NULL;
+static int tag_validated;
+static char *date = NULL;
+int annotate_width = 8; /* Used in RCS_deltas() */
+
+static int is_rannotate;
+
+static int annotate_fileproc (void *callerdat, struct file_info *);
+static int rannotate_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local, char *mname, char *msg);
+
+static const char *const annotate_usage[] =
+{
+ "Usage: %s %s [-lRfF] [-r rev] [-D date] [files...]\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-f\tUse head revision if tag/date not found.\n",
+ "\t-F\tAnnotate binary files.\n",
+ "\t-r rev\tAnnotate file as of specified revision/tag.\n",
+ "\t-D date\tAnnotate file as of specified date.\n",
+ "\t-w width\tModify width of username field (default 8, 0 < width <
80).\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+/* Command to show the revision, date, and author where each line of a
+ file was modified. */
+
+int
+annotate (int argc, char **argv)
+{
+ int local = 0;
+ int err = 0;
+ char *widthstr = NULL;
+ int c;
+
+ is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0);
+
+ if (argc == -1)
+ usage (annotate_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+lr:D:fFRw:")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'r':
+ parse_tagdate (&tag, &date, optarg);
+ break;
+ case 'D':
+ if (date) free (date);
+ date = Make_Date (optarg);
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'F':
+ force_binary = 1;
+ break;
+ case 'w':
+ {
+ int w = atoi(optarg);
+ /* check bounds */
+ if (0 < w && w < 80)
+ {
+ widthstr = optarg;
+ annotate_width = w;
+ }
+ else
+ error (1, 0, "-w %d is invalid, must be > 0 && < 80",
+ w);
+ }
+ break;
+ case '?':
+ default:
+ usage (annotate_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ if (is_rannotate && !supported_request ("rannotate"))
+ error (1, 0, "server does not support rannotate");
+
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ if (!force_tag_match)
+ send_arg ("-f");
+ if (force_binary)
+ send_arg ("-F");
+ option_with_arg ("-r", tag);
+ if (date)
+ client_senddate (date);
+ if (widthstr)
+ option_with_arg ("-w", widthstr);
+ send_arg ("--");
+ if (is_rannotate)
+ {
+ int i;
+ for (i = 0; i < argc; i++)
+ send_arg (argv[i]);
+ send_to_server ("rannotate\012", 0);
+ }
+ else
+ {
+ send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("annotate\012", 0);
+ }
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ if (is_rannotate)
+ {
+ DBM *db;
+ int i;
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ {
+ err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc,
+ NULL, 0, local, 0, 0, NULL);
+ }
+ close_module (db);
+ }
+ else
+ {
+ err = rannotate_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0,
+ local, NULL, NULL);
+ }
+
+ return err;
+}
+
+
+static int
+rannotate_proc (int argc, char **argv, char *xwhere, char *mwhere,
+ char *mfile, int shorten, int local, char *mname, char *msg)
+{
+ /* Begin section which is identical to patch_proc--should this
+ be abstracted out somehow? */
+ char *myargv[2];
+ int err = 0;
+ int which;
+ char *repository;
+ char *where;
+
+ if (is_rannotate)
+ {
+ repository = xmalloc (strlen (current_parsed_root->directory) + strlen
(argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
+ (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
argv[0]);
+ where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile)
+ 1)
+ + 1);
+ (void) strcpy (where, argv[0]);
+
+ /* if mfile isn't null, we need to set up to do only part of the module
*/
+ if (mfile != NULL)
+ {
+ char *cp;
+ char *path;
+
+ /* if the portion of the module is a path, put the dir part on
repos */
+ if ((cp = strrchr (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ path = Xasprintf ("%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void) strcpy (repository, path);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ }
+ else
+ {
+ myargv[0] = argv[0];
+ myargv[1] = mfile;
+ argc = 2;
+ argv = myargv;
+ }
+ free (path);
+ }
+
+ /* cd to the starting repository */
+ if (CVS_CHDIR (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (repository);
+ free (where);
+ return 1;
+ }
+ /* End section which is identical to patch_proc. */
+
+ if (force_tag_match && tag != NULL)
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+ }
+ else
+ {
+ where = NULL;
+ which = W_LOCAL;
+ repository = "";
+ }
+
+ if (tag != NULL && !tag_validated)
+ {
+ tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository, false);
+ tag_validated = 1;
+ }
+
+ err = start_recursion (annotate_fileproc, NULL, NULL, NULL, NULL,
+ argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
+ where, 1, repository);
+ if (which & W_REPOS)
+ free (repository);
+ if (where != NULL)
+ free (where);
+ return err;
+}
+
+
+static int
+annotate_fileproc (void *callerdat, struct file_info *finfo)
+{
+ char *expand;
+ char *version = NULL;
+
+ if (finfo->rcs == NULL)
+ return 1;
+
+ if (finfo->rcs->flags & PARTIAL)
+ RCS_reparsercsfile (finfo->rcs, NULL, NULL);
+
+ expand = RCS_getexpand (finfo->rcs);
+
+ /* bogus annotate behavior needs to be kept for backward compatibility */
+ if (!tag)
+ version = Xasprintf (".%s", TAG_TRUNK);
+ else if (RCS_is_relative (tag))
+ {
+ version = Version_resolve_relTag (finfo, tag, !is_rannotate);
+ if (!version)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.", tag);
+ return 0;
+ }
+ }
+ else
+ version = xstrdup (tag);
+
+ char *tmp = version;
+ version = RCS_getversion (finfo->rcs, version, date, force_tag_match,
NULL);
+ free (tmp);
+
+ if (!version)
+ return 0;
+
+ /* Distinguish output for various files if we are processing
+ several files. */
+ cvs_outerr ("\nAnnotations for ", 0);
+ cvs_outerr (finfo->fullname, 0);
+ cvs_outerr ("\n***************\n", 0);
+
+ if (!force_binary && expand && expand[0] == 'b')
+ {
+ cvs_outerr ("Skipping binary file -- -F not specified.\n", 0);
+ }
+ else
+ {
+ RCS_deltas (finfo->rcs, NULL, NULL,
+ version, RCS_ANNOTATE, NULL, NULL, NULL, NULL);
+ }
+
+ free (version);
+ return 0;
+}
Index: ccvs/src/commit.c
diff -u /dev/null ccvs/src/commit.c:1.258.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/commit.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,2479 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Commit Files
+ *
+ * "commit" commits the present version to the RCS repository, AFTER
+ * having done a test on conflicts.
+ *
+ * The call is: cvs commit [options] files...
+ *
+ */
+
+#include "cvs.h"
+#include "getline.h"
+#include "edit.h"
+#include "fileattr.h"
+#include "hardlink.h"
+
+static Dtype check_direntproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int check_fileproc (void *callerdat, struct file_info *finfo);
+static int check_filesdoneproc (void *callerdat, int err, const char *repos,
+ const char *update_dir, List *entries);
+static int checkaddfile (const char *file, const char *repository,
+ const char *tag, const char *options,
+ RCSNode **rcsnode);
+static Dtype commit_direntproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int commit_dirleaveproc (void *callerdat, const char *dir, int err,
+ const char *update_dir, List *entries);
+static int commit_fileproc (void *callerdat, struct file_info *finfo);
+static int commit_filesdoneproc (void *callerdat, int err,
+ const char *repository,
+ const char *update_dir, List *entries);
+static int finaladd (struct file_info *finfo, char *revision, char *tag,
+ char *options);
+static int findmaxrev (Node * p, void *closure);
+static int lock_RCS (const char *user, RCSNode *rcs, const char *rev,
+ const char *repository);
+static int precommit_list_to_args_proc (Node * p, void *closure);
+static int precommit_proc (const char *repository, const char *filter,
+ void *closure);
+static int remove_file (struct file_info *finfo, char *tag,
+ char *message);
+static void fixaddfile (const char *rcs);
+static void fixbranch (RCSNode *, char *branch);
+static void unlockrcs (RCSNode *rcs);
+static void ci_delproc (Node *p);
+static void masterlist_delproc (Node *p);
+
+struct commit_info
+{
+ Ctype status; /* as returned from Classify_File() */
+ char *rev; /* a numeric rev, if we know it */
+ char *tag; /* any sticky tag, or -r option */
+ char *options; /* Any sticky -k option */
+};
+struct master_lists
+{
+ List *ulist; /* list for Update_Logfile */
+ List *cilist; /* list with commit_info structs */
+};
+
+static int check_valid_edit = 0;
+static int force_ci = 0;
+static int got_message;
+static int aflag;
+static char *saved_tag;
+static char *write_dirtag;
+static int write_dirnonbranch;
+static char *logfile;
+static List *mulist;
+static char *saved_message;
+static time_t last_register_time;
+
+static const char *const commit_usage[] =
+{
+ "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n",
+ " -c Check for valid edits before committing.\n",
+ " -R Process directories recursively.\n",
+ " -l Local directory only (not recursive).\n",
+ " -f Force the file to be committed; disables recursion.\n",
+ " -F logfile Read the log message from file.\n",
+ " -m msg Log message.\n",
+ " -r rev Commit to this branch or trunk revision.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+#ifdef CLIENT_SUPPORT
+/* Identify a file which needs "? foo" or a Questionable request. */
+struct question
+{
+ /* The two fields for the Directory request. */
+ char *dir;
+ char *repos;
+
+ /* The file name. */
+ char *file;
+
+ struct question *next;
+};
+
+struct find_data
+{
+ List *ulist;
+ int argc;
+ char **argv;
+
+ /* This is used from dirent to filesdone time, for each directory,
+ to make a list of files we have already seen. */
+ List *ignlist;
+
+ /* Linked list of files which need "? foo" or a Questionable request. */
+ struct question *questionables;
+
+ /* Only good within functions called from the filesdoneproc. Stores
+ the repository (pointer into storage managed by the recursion
+ processor. */
+ const char *repository;
+
+ /* Non-zero if we should force the commit. This is enabled by
+ either -f or -r options, unlike force_ci which is just -f. */
+ int force;
+};
+
+
+
+static Dtype
+find_dirent_proc (void *callerdat, const char *dir, const char *repository,
+ const char *update_dir, List *entries)
+{
+ struct find_data *find_data = callerdat;
+
+ /* This check seems to slowly be creeping throughout CVS (update
+ and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess
+ is that it (or some variant thereof) should go in all the
+ dirent procs. Unless someone has some better idea... */
+ if (!isdir (dir))
+ return R_SKIP_ALL;
+
+ /* initialize the ignore list for this directory */
+ find_data->ignlist = getlist ();
+
+ /* Print the same warm fuzzy as in check_direntproc, since that
+ code will never be run during client/server operation and we
+ want the messages to match. */
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+
+ return R_PROCESS;
+}
+
+
+
+/* Here as a static until we get around to fixing ignore_files to pass
+ it along as an argument. */
+static struct find_data *find_data_static;
+
+
+
+static void
+find_ignproc (const char *file, const char *dir)
+{
+ struct question *p;
+
+ p = xmalloc (sizeof (struct question));
+ p->dir = xstrdup (dir);
+ p->repos = xstrdup (find_data_static->repository);
+ p->file = xstrdup (file);
+ p->next = find_data_static->questionables;
+ find_data_static->questionables = p;
+}
+
+
+
+static int
+find_filesdoneproc (void *callerdat, int err, const char *repository,
+ const char *update_dir, List *entries)
+{
+ struct find_data *find_data = callerdat;
+ find_data->repository = repository;
+
+ /* if this directory has an ignore list, process it then free it */
+ if (find_data->ignlist)
+ {
+ find_data_static = find_data;
+ ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
+ dellist (&find_data->ignlist);
+ }
+
+ find_data->repository = NULL;
+
+ return err;
+}
+
+
+
+/* Machinery to find out what is modified, added, and removed. It is
+ possible this should be broken out into a new client_classify function;
+ merging it with classify_file is almost sure to be a mess, though,
+ because classify_file has all kinds of repository processing. */
+static int
+find_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Vers_TS *vers;
+ enum classify_type status;
+ Node *node;
+ struct find_data *args = callerdat;
+ struct logfile_info *data;
+ struct file_info xfinfo;
+
+ /* if this directory has an ignore list, add this file to it */
+ if (args->ignlist)
+ {
+ Node *p;
+
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (finfo->file);
+ if (addnode (args->ignlist, p) != 0)
+ freenode (p);
+ }
+
+ xfinfo = *finfo;
+ xfinfo.repository = NULL;
+ xfinfo.rcs = NULL;
+
+ vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0);
+ if (vers->vn_user == NULL)
+ {
+ if (vers->ts_user == NULL)
+ error (0, 0, "nothing known about `%s'", finfo->fullname);
+ else
+ error (0, 0, "use `%s add' to create an entry for `%s'",
+ program_name, finfo->fullname);
+ freevers_ts (&vers);
+ return 1;
+ }
+ if (vers->vn_user[0] == '-')
+ {
+ if (vers->ts_user != NULL)
+ {
+ error (0, 0,
+ "`%s' should be removed and is still there (or is back"
+ " again)", finfo->fullname);
+ freevers_ts (&vers);
+ return 1;
+ }
+ /* else */
+ status = T_REMOVED;
+ }
+ else if (strcmp (vers->vn_user, "0") == 0)
+ {
+ if (vers->ts_user == NULL)
+ {
+ /* This happens when one has `cvs add'ed a file, but it no
+ longer exists in the working directory at commit time.
+ FIXME: What classify_file does in this case is print
+ "new-born %s has disappeared" and removes the entry.
+ We probably should do the same. */
+ if (!really_quiet)
+ error (0, 0, "warning: new-born %s has disappeared",
+ finfo->fullname);
+ status = T_REMOVE_ENTRY;
+ }
+ else
+ status = T_ADDED;
+ }
+ else if (vers->ts_user == NULL)
+ {
+ /* FIXME: What classify_file does in this case is print
+ "%s was lost". We probably should do the same. */
+ freevers_ts (&vers);
+ return 0;
+ }
+ else if (vers->ts_rcs != NULL
+ && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0))
+ /* If we are forcing commits, pretend that the file is
+ modified. */
+ status = T_MODIFIED;
+ else
+ {
+ /* This covers unmodified files, as well as a variety of other
+ cases. FIXME: we probably should be printing a message and
+ returning 1 for many of those cases (but I'm not sure
+ exactly which ones). */
+ freevers_ts (&vers);
+ return 0;
+ }
+
+ node = getnode ();
+ node->key = xstrdup (finfo->fullname);
+
+ data = xmalloc (sizeof (struct logfile_info));
+ data->type = status;
+ data->tag = xstrdup (vers->tag);
+ data->rev_old = data->rev_new = NULL;
+
+ node->type = UPDATE;
+ node->delproc = update_delproc;
+ node->data = data;
+ (void)addnode (args->ulist, node);
+
+ ++args->argc;
+
+ freevers_ts (&vers);
+ return 0;
+}
+
+
+
+static int
+copy_ulist (Node *node, void *data)
+{
+ struct find_data *args = data;
+ args->argv[args->argc++] = node->key;
+ return 0;
+}
+#endif /* CLIENT_SUPPORT */
+
+
+
+#ifdef SERVER_SUPPORT
+# define COMMIT_OPTIONS "+cnlRm:fF:r:"
+#else /* !SERVER_SUPPORT */
+# define COMMIT_OPTIONS "+clRm:fF:r:"
+#endif /* SERVER_SUPPORT */
+int
+commit (int argc, char **argv)
+{
+ int c;
+ int err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (commit_usage);
+
+#ifdef CVS_BADROOT
+ /*
+ * For log purposes, do not allow "root" to commit files. If you look
+ * like root, but are really logged in as a non-root user, it's OK.
+ */
+ /* FIXME: Shouldn't this check be much more closely related to the
+ readonly user stuff (CVSROOT/readers, &c). That is, why should
+ root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */
+ /* Who we are on the client side doesn't affect logging. */
+ if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote)
+ {
+ struct passwd *pw;
+
+ if ((pw = getpwnam (getcaller ())) == NULL)
+ error (1, 0,
+ "your apparent username (%s) is unknown to this system",
+ getcaller ());
+ if (pw->pw_uid == (uid_t) 0)
+ error (1, 0, "'root' is not allowed to commit files");
+ }
+#endif /* CVS_BADROOT */
+
+ optind = 0;
+ while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1)
+ {
+ switch (c)
+ {
+ case 'c':
+ check_valid_edit = 1;
+ break;
+#ifdef SERVER_SUPPORT
+ case 'n':
+ /* Silently ignore -n for compatibility with old
+ * clients.
+ */
+ if (!server_active) error(0, 0, "the `-n' option is obsolete");
+ break;
+#endif /* SERVER_SUPPORT */
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = 1;
+#else
+ use_editor = 0;
+#endif
+ if (saved_message)
+ {
+ free (saved_message);
+ saved_message = NULL;
+ }
+
+ saved_message = xstrdup (optarg);
+ break;
+ case 'r':
+ if (saved_tag)
+ free (saved_tag);
+ saved_tag = xstrdup (optarg);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'f':
+ force_ci = 1;
+ check_valid_edit = 0;
+ local = 1; /* also disable recursion */
+ break;
+ case 'F':
+#ifdef FORCE_USE_EDITOR
+ use_editor = 1;
+#else
+ use_editor = 0;
+#endif
+ logfile = optarg;
+ break;
+ case '?':
+ default:
+ usage (commit_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* numeric specified revision means we ignore sticky tags... */
+ if (saved_tag && isdigit ((unsigned char) *saved_tag))
+ {
+ char *p = saved_tag + strlen (saved_tag);
+ aflag = 1;
+ /* strip trailing dots and leading zeros */
+ while (*--p == '.') ;
+ p[1] = '\0';
+ while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1]))
+ ++saved_tag;
+ }
+
+ /* some checks related to the "-F logfile" option */
+ if (logfile)
+ {
+ size_t size = 0, len;
+
+ if (saved_message)
+ error (1, 0, "cannot specify both a message and a log file");
+
+ get_file (logfile, logfile, "r", &saved_message, &size, &len);
+ }
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ struct find_data find_args;
+
+ ign_setup ();
+
+ find_args.ulist = getlist ();
+ find_args.argc = 0;
+ find_args.questionables = NULL;
+ find_args.ignlist = NULL;
+ find_args.repository = NULL;
+
+ /* It is possible that only a numeric tag should set this.
+ I haven't really thought about it much.
+ Anyway, I suspect that setting it unnecessarily only causes
+ a little unneeded network traffic. */
+ find_args.force = force_ci || saved_tag != NULL;
+
+ err = start_recursion
+ (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL,
+ &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
+ NULL, 0, NULL );
+ if (err)
+ error (1, 0, "correct above errors first!");
+
+ if (find_args.argc == 0)
+ {
+ /* Nothing to commit. Exit now without contacting the
+ server (note that this means that we won't print "?
+ foo" for files which merit it, because we don't know
+ what is in the CVSROOT/cvsignore file). */
+ dellist (&find_args.ulist);
+ return 0;
+ }
+
+ /* Now we keep track of which files we actually are going to
+ operate on, and only work with those files in the future.
+ This saves time--we don't want to search the file system
+ of the working directory twice. */
+ if (size_overflow_p (xtimes (find_args.argc, sizeof (char **))))
+ {
+ find_args.argc = 0;
+ return 0;
+ }
+ find_args.argv = xnmalloc (find_args.argc, sizeof (char **));
+ find_args.argc = 0;
+ walklist (find_args.ulist, copy_ulist, &find_args);
+
+ /* Do this before calling do_editor; don't ask for a log
+ message if we can't talk to the server. But do it after we
+ have made the checks that we can locally (to more quickly
+ catch syntax errors, the case where no files are modified,
+ added or removed, etc.).
+
+ On the other hand, calling start_server before do_editor
+ means that we chew up server resources the whole time that
+ the user has the editor open (hours or days if the user
+ forgets about it), which seems dubious. */
+ start_server ();
+
+ /*
+ * We do this once, not once for each directory as in normal CVS.
+ * The protocol is designed this way. This is a feature.
+ */
+ if (use_editor)
+ do_editor (".", &saved_message, NULL, find_args.ulist);
+
+ /* We always send some sort of message, even if empty. */
+ option_with_arg ("-m", saved_message ? saved_message : "");
+
+ /* OK, now process all the questionable files we have been saving
+ up. */
+ {
+ struct question *p;
+ struct question *q;
+
+ p = find_args.questionables;
+ while (p != NULL)
+ {
+ if (ign_inhibit_server || !supported_request ("Questionable"))
+ {
+ cvs_output ("? ", 2);
+ if (p->dir[0] != '\0')
+ {
+ cvs_output (p->dir, 0);
+ cvs_output ("/", 1);
+ }
+ cvs_output (p->file, 0);
+ cvs_output ("\n", 1);
+ }
+ else
+ {
+ /* This used to send the Directory line of its own accord,
+ * but skipped some of the other processing like checking
+ * for whether the server would accept "Relative-directory"
+ * requests. Relying on send_a_repository() to do this
+ * picks up these checks but also:
+ *
+ * 1. Causes the "Directory" request to be sent only once
+ * per directory.
+ * 2. Causes the global TOPLEVEL_REPOS to be set.
+ * 3. Causes "Static-directory" and "Sticky" requests
+ * to sometimes be sent.
+ *
+ * (1) is almost certainly a plus. (2) & (3) may or may
+ * not be useful sometimes, and will ocassionally cause a
+ * little extra network traffic. The additional network
+ * traffic is probably already saved several times over and
+ * certainly cancelled out via the multiple "Directory"
+ * request suppression of (1).
+ */
+ send_a_repository (p->dir, p->repos, p->dir);
+
+ send_to_server ("Questionable ", 0);
+ send_to_server (p->file, 0);
+ send_to_server ("\012", 1);
+ }
+ free (p->dir);
+ free (p->repos);
+ free (p->file);
+ q = p->next;
+ free (p);
+ p = q;
+ }
+ }
+
+ if (local)
+ send_arg ("-l");
+ if (check_valid_edit)
+ send_arg ("-c");
+ if (force_ci)
+ send_arg ("-f");
+ option_with_arg ("-r", saved_tag);
+ send_arg ("--");
+
+ /* FIXME: This whole find_args.force/SEND_FORCE business is a
+ kludge. It would seem to be a server bug that we have to
+ say that files are modified when they are not. This makes
+ "cvs commit -r 2" across a whole bunch of files a very slow
+ operation (and it isn't documented in cvsclient.texi). I
+ haven't looked at the server code carefully enough to be
+ _sure_ why this is needed, but if it is because the "ci"
+ program, which we used to call, wanted the file to exist,
+ then it would be relatively simple to fix in the server. */
+ send_files (find_args.argc, find_args.argv, local, 0,
+ find_args.force ? SEND_FORCE : 0);
+
+ /* Sending only the names of the files which were modified, added,
+ or removed means that the server will only do an up-to-date
+ check on those files. This is different from local CVS and
+ previous versions of client/server CVS, but it probably is a Good
+ Thing, or at least Not Such A Bad Thing. */
+ send_file_names (find_args.argc, find_args.argv, 0);
+ free (find_args.argv);
+ dellist (&find_args.ulist);
+
+ send_to_server ("ci\012", 0);
+ err = get_responses_and_close ();
+ if (err != 0 && use_editor && saved_message != NULL)
+ {
+ /* If there was an error, don't nuke the user's carefully
+ constructed prose. This is something of a kludge; a better
+ solution is probably more along the lines of #150 in TODO
+ (doing a second up-to-date check before accepting the
+ log message has also been suggested, but that seems kind of
+ iffy because the real up-to-date check could still fail,
+ another error could occur, &c. Also, a second check would
+ slow things down). */
+
+ char *fname;
+ FILE *fp;
+
+ fp = cvs_temp_file (&fname);
+ if (fp == NULL)
+ error (1, 0, "cannot create temporary file %s", fname);
+ if (fwrite (saved_message, 1, strlen (saved_message), fp)
+ != strlen (saved_message))
+ error (1, errno, "cannot write temporary file %s", fname);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close temporary file %s", fname);
+ error (0, 0, "saving log message in %s", fname);
+ free (fname);
+ }
+ return err;
+ }
+#endif
+
+ if (saved_tag != NULL)
+ tag_check_valid (saved_tag, argc, argv, local, aflag, "", false);
+
+ /* XXX - this is not the perfect check for this */
+ if (argc <= 0)
+ write_dirtag = saved_tag;
+
+ wrap_setup ();
+
+ lock_tree_promotably (argc, argv, local, W_LOCAL, aflag);
+
+ /*
+ * Set up the master update list and hard link list
+ */
+ mulist = getlist ();
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms)
+ {
+ hardlist = getlist ();
+
+ /*
+ * We need to save the working directory so that
+ * check_fileproc can construct a full pathname for each file.
+ */
+ working_dir = xgetcwd ();
+ }
+#endif
+
+ /*
+ * Run the recursion processor to verify the files are all up-to-date
+ */
+ err = start_recursion (check_fileproc, check_filesdoneproc,
+ check_direntproc, NULL, NULL, argc, argv, local,
+ W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL);
+ if (err)
+ error (1, 0, "correct above errors first!");
+
+ /*
+ * Run the recursion processor to commit the files
+ */
+ write_dirnonbranch = 0;
+ if (noexec == 0)
+ err = start_recursion (commit_fileproc, commit_filesdoneproc,
+ commit_direntproc, commit_dirleaveproc, NULL,
+ argc, argv, local, W_LOCAL, aflag,
+ CVS_LOCK_WRITE, NULL, 1, NULL);
+
+ /*
+ * Unlock all the dirs and clean up
+ */
+ Lock_Cleanup ();
+ dellist (&mulist);
+
+ /* add the commitid to val-tags
+ */
+ char *commitid = Xasprintf ("@%s", global_session_id);
+ tag_check_valid (commitid, argc, argv, local, aflag, "", true);
+ free (commitid);
+
+ /* see if we need to sleep before returning to avoid time-stamp races */
+ if (!server_active && last_register_time)
+ {
+ sleep_past (last_register_time);
+ }
+
+ return err;
+}
+
+
+
+/* This routine determines the status of a given file and retrieves
+ the version information that is associated with that file. */
+
+static
+Ctype
+classify_file_internal (struct file_info *finfo, Vers_TS **vers)
+{
+ int save_noexec, save_quiet, save_really_quiet;
+ Ctype status;
+
+ /* FIXME: Do we need to save quiet as well as really_quiet? Last
+ time I glanced at Classify_File I only saw it looking at really_quiet
+ not quiet. */
+ save_noexec = noexec;
+ save_quiet = quiet;
+ save_really_quiet = really_quiet;
+ noexec = quiet = really_quiet = 1;
+
+ /* handle specified numeric revision specially */
+ if (saved_tag && isdigit ((unsigned char) *saved_tag))
+ {
+ /* If the tag is for the trunk, make sure we're at the head */
+ if (numdots (saved_tag) < 2)
+ {
+ status = Classify_File (finfo, NULL, NULL,
+ NULL, 1, aflag, vers, 0);
+ if (status == T_UPTODATE || status == T_MODIFIED ||
+ status == T_ADDED)
+ {
+ Ctype xstatus;
+
+ freevers_ts (vers);
+ xstatus = Classify_File (finfo, saved_tag, NULL,
+ NULL, 1, aflag, vers, 0);
+ if (xstatus == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ else if (status == T_MODIFIED && xstatus == T_CONFLICT)
+ status = T_MODIFIED;
+ else
+ status = xstatus;
+ }
+ }
+ else
+ {
+ char *xtag, *cp;
+
+ /*
+ * The revision is off the main trunk; make sure we're
+ * up-to-date with the head of the specified branch.
+ */
+ xtag = xstrdup (saved_tag);
+ if ((numdots (xtag) & 1) != 0)
+ {
+ cp = strrchr (xtag, '.');
+ *cp = '\0';
+ }
+ status = Classify_File (finfo, xtag, NULL,
+ NULL, 1, aflag, vers, 0);
+ if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
+ && (cp = strrchr (xtag, '.')) != NULL)
+ {
+ /* pluck one more dot off the revision */
+ *cp = '\0';
+ freevers_ts (vers);
+ status = Classify_File (finfo, xtag, NULL,
+ NULL, 1, aflag, vers, 0);
+ if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ }
+ /* now, muck with vers to make the tag correct */
+ free ((*vers)->tag);
+ (*vers)->tag = xstrdup (saved_tag);
+ free (xtag);
+ }
+ }
+ else
+ status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0);
+ noexec = save_noexec;
+ quiet = save_quiet;
+ really_quiet = save_really_quiet;
+
+ return status;
+}
+
+
+
+/*
+ * Check to see if a file is ok to commit and make sure all files are
+ * up-to-date
+ */
+/* ARGSUSED */
+static int
+check_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Ctype status;
+ const char *xdir;
+ Node *p;
+ List *ulist, *cilist;
+ Vers_TS *vers;
+ struct commit_info *ci;
+ struct logfile_info *li;
+ int retval = 1;
+
+ size_t cvsroot_len = strlen (current_parsed_root->directory);
+
+ if (!finfo->repository)
+ {
+ error (0, 0, "nothing known about `%s'", finfo->fullname);
+ return 1;
+ }
+
+ if (strncmp (finfo->repository, current_parsed_root->directory,
+ cvsroot_len) == 0
+ && ISSLASH (finfo->repository[cvsroot_len])
+ && strncmp (finfo->repository + cvsroot_len + 1,
+ CVSROOTADM,
+ sizeof (CVSROOTADM) - 1) == 0
+ && ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
+ && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
+ CVSNULLREPOS) == 0
+ )
+ error (1, 0, "cannot check in to %s", finfo->repository);
+
+ status = classify_file_internal (finfo, &vers);
+
+ /*
+ * If the force-commit option is enabled, and the file in question
+ * appears to be up-to-date, just make it look modified so that
+ * it will be committed.
+ */
+ if (force_ci && status == T_UPTODATE)
+ status = T_MODIFIED;
+
+ switch (status)
+ {
+ case T_CHECKOUT:
+ case T_PATCH:
+ case T_NEEDS_MERGE:
+ case T_REMOVE_ENTRY:
+ error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
+ goto out;
+ case T_CONFLICT:
+ case T_MODIFIED:
+ case T_ADDED:
+ case T_REMOVED:
+ {
+ char *editor;
+
+ /*
+ * some quick sanity checks; if no numeric -r option specified:
+ * - can't have a sticky date
+ * - can't have a sticky tag that is not a branch
+ * Also,
+ * - if status is T_REMOVED, file must not exist and its entry
+ * can't have a numeric sticky tag.
+ * - if status is T_ADDED, rcs file must not exist unless on
+ * a branch or head is dead
+ * - if status is T_ADDED, can't have a non-trunk numeric rev
+ * - if status is T_MODIFIED and a Conflict marker exists, don't
+ * allow the commit if timestamp is identical or if we find
+ * an RCS_MERGE_PAT in the file.
+ */
+ if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
+ {
+ if (vers->date)
+ {
+ error (0, 0,
+ "cannot commit with sticky date for file `%s'",
+ finfo->fullname);
+ goto out;
+ }
+ if (status == T_MODIFIED && vers->tag &&
+ !RCS_isbranch (finfo->rcs, vers->tag))
+ {
+ error (0, 0,
+ "sticky tag `%s' for file `%s' is not a branch",
+ vers->tag, finfo->fullname);
+ goto out;
+ }
+ }
+ if (status == T_CONFLICT && !force_ci)
+ {
+ error (0, 0,
+ "file `%s' had a conflict and has not been modified",
+ finfo->fullname);
+ goto out;
+ }
+ if (status == T_MODIFIED && !force_ci && !really_quiet
+ && file_has_markers (finfo))
+ {
+ /* Make this a warning, not an error, because we have
+ no way of knowing whether the "conflict indicators"
+ are really from a conflict or whether they are part
+ of the document itself (cvs.texinfo and sanity.sh in
+ CVS itself, for example, tend to want to have strings
+ like ">>>>>>>" at the start of a line). Making people
+ kludge this the way they need to kludge keyword
+ expansion seems undesirable. And it is worse than
+ keyword expansion, because there is no -ko
+ analogue. */
+ error (0, 0,
+ "\
+warning: file `%s' seems to still contain conflict indicators",
+ finfo->fullname);
+ }
+
+ if (status == T_REMOVED)
+ {
+ if (vers->ts_user != NULL)
+ {
+ error (0, 0,
+ "`%s' should be removed and is still there (or is"
+ " back again)", finfo->fullname);
+ goto out;
+ }
+
+ if (vers->tag && isdigit ((unsigned char) *vers->tag))
+ {
+ /* Remove also tries to forbid this, but we should check
+ here. I'm only _sure_ about somewhat obscure cases
+ (hacking the Entries file, using an old version of
+ CVS for the remove and a new one for the commit), but
+ there might be other cases. */
+ error (0, 0,
+ "cannot remove file `%s' which has a numeric sticky"
+ " tag of `%s'", finfo->fullname, vers->tag);
+ freevers_ts (&vers);
+ goto out;
+ }
+ }
+ if (status == T_ADDED)
+ {
+ if (vers->tag == NULL)
+ {
+ if (finfo->rcs != NULL &&
+ !RCS_isdead (finfo->rcs, finfo->rcs->head))
+ {
+ error (0, 0,
+ "cannot add file `%s' when RCS file `%s' already exists",
+ finfo->fullname, finfo->rcs->path);
+ goto out;
+ }
+ }
+ else if (isdigit ((unsigned char) *vers->tag) &&
+ numdots (vers->tag) > 1)
+ {
+ error (0, 0,
+ "cannot add file `%s' with revision `%s'; must be on trunk",
+ finfo->fullname, vers->tag);
+ goto out;
+ }
+ }
+
+ /* done with consistency checks; now, to get on with the commit */
+ if (finfo->update_dir[0] == '\0')
+ xdir = ".";
+ else
+ xdir = finfo->update_dir;
+ if ((p = findnode (mulist, xdir)) != NULL)
+ {
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+ }
+ else
+ {
+ struct master_lists *ml;
+
+ ml = xmalloc (sizeof (struct master_lists));
+ ulist = ml->ulist = getlist ();
+ cilist = ml->cilist = getlist ();
+
+ p = getnode ();
+ p->key = xstrdup (xdir);
+ p->type = UPDATE;
+ p->data = ml;
+ p->delproc = masterlist_delproc;
+ (void) addnode (mulist, p);
+ }
+
+ /* first do ulist, then cilist */
+ p = getnode ();
+ p->key = xstrdup (finfo->file);
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ li = xmalloc (sizeof (struct logfile_info));
+ li->type = status;
+
+ if (check_valid_edit)
+ {
+ char *editors = NULL;
+
+ editor = NULL;
+ editors = fileattr_get0 (finfo->file, "_editors");
+ if (editors != NULL)
+ {
+ char *caller = getcaller ();
+ char *p = NULL;
+ char *p0 = NULL;
+
+ p = editors;
+ p0 = p;
+ while (*p != '\0')
+ {
+ p = strchr (p, '>');
+ if (p == NULL)
+ {
+ break;
+ }
+ *p = '\0';
+ if (strcmp (caller, p0) == 0)
+ {
+ break;
+ }
+ p = strchr (p + 1, ',');
+ if (p == NULL)
+ {
+ break;
+ }
+ ++p;
+ p0 = p;
+ }
+
+ if (strcmp (caller, p0) == 0)
+ {
+ editor = caller;
+ }
+
+ free (editors);
+ }
+ }
+
+ if (check_valid_edit && editor == NULL)
+ {
+ error (0, 0, "Valid edit does not exist for %s",
+ finfo->fullname);
+ freevers_ts (&vers);
+ return 1;
+ }
+
+ li->tag = xstrdup (vers->tag);
+ li->rev_old = xstrdup (vers->vn_rcs);
+ li->rev_new = NULL;
+ p->data = li;
+ (void) addnode (ulist, p);
+
+ p = getnode ();
+ p->key = xstrdup (finfo->file);
+ p->type = UPDATE;
+ p->delproc = ci_delproc;
+ ci = xmalloc (sizeof (struct commit_info));
+ ci->status = status;
+ if (vers->tag)
+ if (isdigit ((unsigned char) *vers->tag))
+ ci->rev = xstrdup (vers->tag);
+ else
+ ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
+ else
+ ci->rev = NULL;
+ ci->tag = xstrdup (vers->tag);
+ ci->options = xstrdup (vers->options);
+ p->data = ci;
+ (void) addnode (cilist, p);
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms)
+ {
+ /* Add this file to hardlist, indexed on its inode. When
+ we are done, we can find out what files are hardlinked
+ to a given file by looking up its inode in hardlist. */
+ char *fullpath;
+ Node *linkp;
+ struct hardlink_info *hlinfo;
+
+ /* Get the full pathname of the current file. */
+ fullpath = Xasprintf ("%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 = xmalloc (sizeof (struct hardlink_info));
+ hlinfo->status = status;
+ linkp->data = hlinfo;
+ }
+ }
+#endif
+
+ break;
+ }
+
+ case T_UNKNOWN:
+ error (0, 0, "nothing known about `%s'", finfo->fullname);
+ goto out;
+ case T_UPTODATE:
+ break;
+ default:
+ error (0, 0, "CVS internal error: unknown status %d", status);
+ break;
+ }
+
+ retval = 0;
+
+ out:
+
+ freevers_ts (&vers);
+ return retval;
+}
+
+
+
+/*
+ * By default, return the code that tells do_recursion to examine all
+ * directories
+ */
+/* ARGSUSED */
+static Dtype
+check_direntproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!isdir (dir))
+ return R_SKIP_ALL;
+
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+
+ return R_PROCESS;
+}
+
+
+
+/*
+ * Walklist proc to generate an arg list from the line in commitinfo
+ */
+static int
+precommit_list_to_args_proc (p, closure)
+ Node *p;
+ void *closure;
+{
+ struct format_cmdline_walklist_closure *c = closure;
+ struct logfile_info *li;
+ char *arg = NULL;
+ const char *f;
+ char *d;
+ size_t doff;
+
+ if (p->data == NULL) return 1;
+
+ f = c->format;
+ d = *c->d;
+ /* foreach requested attribute */
+ while (*f)
+ {
+ switch (*f++)
+ {
+ case 's':
+ li = p->data;
+ if (li->type == T_ADDED
+ || li->type == T_MODIFIED
+ || li->type == T_REMOVED)
+ {
+ arg = p->key;
+ }
+ break;
+ default:
+ error (1, 0,
+ "Unknown format character or not a list attribute: %c",
+ f[-1]);
+ /* NOTREACHED */
+ break;
+ }
+ /* copy the attribute into an argument */
+ if (c->quotes)
+ {
+ arg = cmdlineescape (c->quotes, arg);
+ }
+ else
+ {
+ arg = cmdlinequote ('"', arg);
+ }
+ doff = d - *c->buf;
+ expand_string (c->buf, c->length, doff + strlen (arg));
+ d = *c->buf + doff;
+ strncpy (d, arg, strlen (arg));
+ d += strlen (arg);
+ free (arg);
+
+ /* and always put the extra space on. we'll have to back up a char
+ * when we're done, but that seems most efficient
+ */
+ doff = d - *c->buf;
+ expand_string (c->buf, c->length, doff + 1);
+ d = *c->buf + doff;
+ *d++ = ' ';
+ }
+ /* correct our original pointer into the buff */
+ *c->d = d;
+ return 0;
+}
+
+
+
+/*
+ * Callback proc for pre-commit checking
+ */
+static int
+precommit_proc (const char *repository, const char *filter, void *closure)
+{
+ char *newfilter = NULL;
+ char *cmdline;
+ const char *srepos = Short_Repository (repository);
+ List *ulist = closure;
+
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ if (!strchr (filter, '%'))
+ {
+ error (0, 0,
+ "warning: commitinfo line contains no format strings:\n"
+ " \"%s\"\n"
+ "Appending defaults (\" %%r/%%p %%s\"), but please be aware
that this usage is\n"
+ "deprecated.", filter);
+ newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
+ filter = newfilter;
+ }
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+
+ /*
+ * Cast any NULL arguments as appropriate pointers as this is an
+ * stdarg function and we need to be certain the caller gets what
+ * is expected.
+ */
+ cmdline = format_cmdline (
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ false, srepos,
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ filter,
+ "c", "s", cvs_cmd_name,
+#ifdef SERVER_SUPPORT
+ "R", "s", referrer ? referrer->original : "NONE",
+#endif /* SERVER_SUPPORT */
+ "p", "s", srepos,
+ "r", "s", current_parsed_root->directory,
+ "s", ",", ulist, precommit_list_to_args_proc,
+ (void *) NULL,
+ (char *) NULL);
+
+ if (newfilter) free (newfilter);
+
+ if (!cmdline || !strlen (cmdline))
+ {
+ if (cmdline) free (cmdline);
+ error (0, 0, "precommit proc resolved to the empty string!");
+ return 1;
+ }
+
+ run_setup (cmdline);
+ free (cmdline);
+
+ return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_REALLY);
+}
+
+
+
+/*
+ * Run the pre-commit checks for the dir
+ */
+/* ARGSUSED */
+static int
+check_filesdoneproc (void *callerdat, int err, const char *repos,
+ const char *update_dir, List *entries)
+{
+ int n;
+ Node *p;
+ List *saved_ulist;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ saved_ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ saved_ulist = NULL;
+
+ /* skip the checks if there's nothing to do */
+ if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
+ return err;
+
+ /* run any pre-commit checks */
+ n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
+ saved_ulist);
+ if (n > 0)
+ {
+ error (0, 0, "Pre-commit check failed");
+ err += n;
+ }
+
+ return err;
+}
+
+
+
+/*
+ * Do the work of committing a file
+ */
+static int maxrev;
+static char *sbranch;
+
+/* ARGSUSED */
+static int
+commit_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Node *p;
+ int err = 0;
+ List *ulist, *cilist;
+ struct commit_info *ci;
+
+ /* Keep track of whether write_dirtag is a branch tag.
+ Note that if it is a branch tag in some files and a nonbranch tag
+ in others, treat it as a nonbranch tag. It is possible that case
+ should elicit a warning or an error. */
+ if (write_dirtag != NULL
+ && finfo->rcs != NULL)
+ {
+ char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
+ if (rev != NULL
+ && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
+ write_dirnonbranch = 1;
+ if (rev != NULL)
+ free (rev);
+ }
+
+ if (finfo->update_dir[0] == '\0')
+ p = findnode (mulist, ".");
+ else
+ p = findnode (mulist, finfo->update_dir);
+
+ /*
+ * if p is null, there were file type command line args which were
+ * all up-to-date so nothing really needs to be done
+ */
+ if (p == NULL)
+ return 0;
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+
+ /*
+ * At this point, we should have the commit message unless we were called
+ * with files as args from the command line. In that latter case, we
+ * need to get the commit message ourselves
+ */
+ if (!got_message)
+ {
+ got_message = 1;
+ if (!server_active && use_editor)
+ do_editor (finfo->update_dir, &saved_message,
+ finfo->repository, ulist);
+ do_verify (&saved_message, finfo->repository, ulist);
+ }
+
+ p = findnode (cilist, finfo->file);
+ if (p == NULL)
+ return 0;
+
+ ci = p->data;
+ if (ci->status == T_MODIFIED)
+ {
+ if (finfo->rcs == NULL)
+ error (1, 0, "internal error: no parsed RCS file");
+ if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
+ finfo->repository) != 0)
+ {
+ unlockrcs (finfo->rcs);
+ err = 1;
+ goto out;
+ }
+ }
+ else if (ci->status == T_ADDED)
+ {
+ if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
+ &finfo->rcs) != 0)
+ {
+ if (finfo->rcs != NULL)
+ fixaddfile (finfo->rcs->path);
+ err = 1;
+ goto out;
+ }
+
+ /* adding files with a tag, now means adding them on a branch.
+ Since the branch test was done in check_fileproc for
+ modified files, we need to stub it in again here. */
+
+ if (ci->tag
+
+ /* If numeric, it is on the trunk; check_fileproc enforced
+ this. */
+ && !isdigit ((unsigned char) ci->tag[0]))
+ {
+ if (finfo->rcs == NULL)
+ error (1, 0, "internal error: no parsed RCS file");
+ if (ci->rev)
+ free (ci->rev);
+ ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
+ err = Checkin ('A', finfo, ci->rev,
+ ci->tag, ci->options, saved_message);
+ if (err != 0)
+ {
+ unlockrcs (finfo->rcs);
+ fixbranch (finfo->rcs, sbranch);
+ }
+
+ (void) time (&last_register_time);
+
+ ci->status = T_UPTODATE;
+ }
+ }
+
+ /*
+ * Add the file for real
+ */
+ if (ci->status == T_ADDED)
+ {
+ char *xrev = NULL;
+
+ if (ci->rev == NULL)
+ {
+ /* find the max major rev number in this directory */
+ maxrev = 0;
+ (void) walklist (finfo->entries, findmaxrev, NULL);
+ if (finfo->rcs->head)
+ {
+ /* resurrecting: include dead revision */
+ int thisrev = atoi (finfo->rcs->head);
+ if (thisrev > maxrev)
+ maxrev = thisrev;
+ }
+ if (maxrev == 0)
+ maxrev = 1;
+ xrev = Xasprintf ("%d", maxrev);
+ }
+
+ /* XXX - an added file with symbolic -r should add tag as well */
+ err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
+ if (xrev)
+ free (xrev);
+ }
+ else if (ci->status == T_MODIFIED)
+ {
+ err = Checkin ('M', finfo, ci->rev, ci->tag,
+ ci->options, saved_message);
+
+ (void) time (&last_register_time);
+
+ if (err != 0)
+ {
+ unlockrcs (finfo->rcs);
+ fixbranch (finfo->rcs, sbranch);
+ }
+ }
+ else if (ci->status == T_REMOVED)
+ {
+ err = remove_file (finfo, ci->tag, saved_message);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ server_scratch_entry_only ();
+ server_updated (finfo,
+ NULL,
+
+ /* Doesn't matter, it won't get checked. */
+ SERVER_UPDATED,
+
+ (mode_t) -1,
+ NULL,
+ NULL);
+ }
+#endif
+ }
+
+ /* Clearly this is right for T_MODIFIED. I haven't thought so much
+ about T_ADDED or T_REMOVED. */
+ notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
+ finfo->repository);
+
+out:
+ if (err != 0)
+ {
+ /* on failure, remove the file from ulist */
+ p = findnode (ulist, finfo->file);
+ if (p)
+ delnode (p);
+ }
+ else
+ {
+ /* On success, retrieve the new version number of the file and
+ copy it into the log information (see logmsg.c
+ (logfile_write) for more details). We should only update
+ the version number for files that have been added or
+ modified but not removed since classify_file_internal
+ will return the version number of a file even after it has
+ been removed from the archive, which is not the behavior we
+ want for our commitlog messages; we want the old version
+ number and then "NONE." */
+
+ if (ci->status != T_REMOVED)
+ {
+ p = findnode (ulist, finfo->file);
+ if (p)
+ {
+ Vers_TS *vers;
+ struct logfile_info *li;
+
+ (void) classify_file_internal (finfo, &vers);
+ li = p->data;
+ li->rev_new = xstrdup (vers->vn_rcs);
+ freevers_ts (&vers);
+ }
+ }
+ }
+ if (SIG_inCrSect ())
+ SIG_endCrSect ();
+
+ return err;
+}
+
+
+
+/*
+ * Log the commit and clean up the update list
+ */
+/* ARGSUSED */
+static int
+commit_filesdoneproc (void *callerdat, int err, const char *repository,
+ const char *update_dir, List *entries)
+{
+ Node *p;
+ List *ulist;
+
+ assert (repository);
+
+ p = findnode (mulist, update_dir);
+ if (p == NULL)
+ return err;
+
+ ulist = ((struct master_lists *) p->data)->ulist;
+
+ got_message = 0;
+
+ /* Build the administrative files if necessary. */
+ {
+ const char *p;
+
+ if (strncmp (current_parsed_root->directory, repository,
+ strlen (current_parsed_root->directory)) != 0)
+ error (0, 0,
+ "internal error: repository (%s) doesn't begin with root (%s)",
+ repository, current_parsed_root->directory);
+ p = repository + strlen (current_parsed_root->directory);
+ if (*p == '/')
+ ++p;
+ if (strcmp ("CVSROOT", p) == 0
+ /* Check for subdirectories because people may want to create
+ subdirectories and list files therein in checkoutlist. */
+ || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
+ )
+ {
+ /* "Database" might a little bit grandiose and/or vague,
+ but "checked-out copies of administrative files, unless
+ in the case of modules and you are using ndbm in which
+ case modules.{pag,dir,db}" is verbose and excessively
+ focused on how the database is implemented. */
+
+ /* mkmodules requires the absolute name of the CVSROOT directory.
+ Remove anything after the `CVSROOT' component -- this is
+ necessary when committing in a subdirectory of CVSROOT. */
+ char *admin_dir = xstrdup (repository);
+ int cvsrootlen = strlen ("CVSROOT");
+ assert (admin_dir[p - repository + cvsrootlen] == '\0'
+ || admin_dir[p - repository + cvsrootlen] == '/');
+ admin_dir[p - repository + cvsrootlen] = '\0';
+
+ if (!really_quiet)
+ {
+ cvs_output (program_name, 0);
+ cvs_output (" ", 1);
+ cvs_output (cvs_cmd_name, 0);
+ cvs_output (": Rebuilding administrative file database\n", 0);
+ }
+ mkmodules (admin_dir);
+ free (admin_dir);
+ WriteTemplate (".", 1, repository);
+ }
+ }
+
+ /* FIXME: This used to be above the block above. The advantage of being
+ * here is that it is not called until after all possible writes from this
+ * process are complete. The disadvantage is that a fatal error during
+ * update of CVSROOT can prevent the loginfo script from being called.
+ *
+ * A more general solution I have been considering is calling a generic
+ * "postwrite" hook from the remove write lock routine.
+ */
+ Update_Logfile (repository, saved_message, NULL, ulist);
+
+ return err;
+}
+
+
+
+/*
+ * Get the log message for a dir
+ */
+/* ARGSUSED */
+static Dtype
+commit_direntproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ Node *p;
+ List *ulist;
+ char *real_repos;
+
+ if (!isdir (dir))
+ return R_SKIP_ALL;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ ulist = NULL;
+
+ /* skip the files as an optimization */
+ if (ulist == NULL || ulist->list->next == ulist->list)
+ return R_SKIP_FILES;
+
+ /* get commit message */
+ got_message = 1;
+ real_repos = Name_Repository (dir, update_dir);
+ if (!server_active && use_editor)
+ do_editor (update_dir, &saved_message, real_repos, ulist);
+ do_verify (&saved_message, real_repos, ulist);
+ free (real_repos);
+ return R_PROCESS;
+}
+
+
+
+/*
+ * Process the post-commit proc if necessary
+ */
+/* ARGSUSED */
+static int
+commit_dirleaveproc (void *callerdat, const char *dir, int err,
+ const char *update_dir, List *entries)
+{
+ /* update the per-directory tag info */
+ /* FIXME? Why? The "commit examples" node of cvs.texinfo briefly
+ mentions commit -r being sticky, but apparently in the context of
+ this being a confusing feature! */
+ if (err == 0 && write_dirtag != NULL)
+ {
+ char *repos = Name_Repository (NULL, update_dir);
+ WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
+ update_dir, repos);
+ free (repos);
+ }
+
+ return err;
+}
+
+
+
+/*
+ * find the maximum major rev number in an entries file
+ */
+static int
+findmaxrev (Node *p, void *closure)
+{
+ int thisrev;
+ Entnode *entdata = p->data;
+
+ if (entdata->type != ENT_FILE)
+ return 0;
+ thisrev = atoi (entdata->version);
+ if (thisrev > maxrev)
+ maxrev = thisrev;
+ return 0;
+}
+
+/*
+ * Actually remove a file by moving it to the attic
+ * XXX - if removing a ,v file that is a relative symbolic link to
+ * another ,v file, we probably should add a ".." component to the
+ * link to keep it relative after we move it into the attic.
+
+ Return value is 0 on success, or >0 on error (in which case we have
+ printed an error message). */
+static int
+remove_file (struct file_info *finfo, char *tag, char *message)
+{
+ int retcode;
+
+ int branch;
+ int lockflag;
+ char *corev;
+ char *rev;
+ char *prev_rev;
+ char *old_path;
+
+ corev = NULL;
+ rev = NULL;
+ prev_rev = NULL;
+
+ retcode = 0;
+
+ if (finfo->rcs == NULL)
+ error (1, 0, "internal error: no parsed RCS file");
+
+ branch = 0;
+ if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
+ {
+ /* a symbolic tag is specified; just remove the tag from the file */
+ if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", tag,
+ finfo->fullname);
+ return 1;
+ }
+ RCS_rewrite (finfo->rcs, NULL, NULL);
+ Scratch_Entry (finfo->entries, finfo->file);
+ return 0;
+ }
+
+ /* we are removing the file from either the head or a branch */
+ /* commit a new, dead revision. */
+
+ rev = NULL;
+ lockflag = 1;
+ if (branch)
+ {
+ char *branchname;
+
+ rev = RCS_whatbranch (finfo->rcs, tag);
+ if (rev == NULL)
+ {
+ error (0, 0, "cannot find branch \"%s\".", tag);
+ return 1;
+ }
+
+ branchname = RCS_getbranch (finfo->rcs, rev, 1);
+ if (branchname == NULL)
+ {
+ /* no revision exists on this branch. use the previous
+ revision but do not lock. */
+ corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
+ prev_rev = xstrdup (corev);
+ lockflag = 0;
+ } else
+ {
+ corev = xstrdup (rev);
+ prev_rev = xstrdup (branchname);
+ free (branchname);
+ }
+
+ } else /* Not a branch */
+ {
+ /* Get current head revision of file. */
+ prev_rev = RCS_head (finfo->rcs);
+ }
+
+ /* if removing without a tag or a branch, then make sure the default
+ branch is the trunk. */
+ if (!tag && !branch)
+ {
+ if (RCS_setbranch (finfo->rcs, NULL) != 0)
+ {
+ error (0, 0, "cannot change branch to default for %s",
+ finfo->fullname);
+ return 1;
+ }
+ RCS_rewrite (finfo->rcs, NULL, NULL);
+ }
+
+ /* check something out. Generally this is the head. If we have a
+ particular rev, then name it. */
+ retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
+ NULL, NULL, RUN_TTY, NULL, NULL);
+ if (retcode != 0)
+ {
+ error (0, 0,
+ "failed to check out `%s'", finfo->fullname);
+ return 1;
+ }
+
+ /* Except when we are creating a branch, lock the revision so that
+ we can check in the new revision. */
+ if (lockflag)
+ {
+ if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
+ RCS_rewrite (finfo->rcs, NULL, NULL);
+ }
+
+ if (corev != NULL)
+ free (corev);
+
+ retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
+ rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
+ if (retcode != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to commit dead revision for `%s'", finfo->fullname);
+ return 1;
+ }
+ /* At this point, the file has been committed as removed. We should
+ probably tell the history file about it */
+ corev = rev ? RCS_getbranch (finfo->rcs, rev, 1) : RCS_head (finfo->rcs);
+ history_write ('R', NULL, corev, finfo->file, finfo->repository);
+ free (corev);
+
+ if (rev != NULL)
+ free (rev);
+
+ old_path = xstrdup (finfo->rcs->path);
+ if (!branch)
+ RCS_setattic (finfo->rcs, 1);
+
+ /* Print message that file was removed. */
+ if (!really_quiet)
+ {
+ cvs_output (old_path, 0);
+ cvs_output (" <-- ", 0);
+ if (finfo->update_dir && strlen (finfo->update_dir))
+ {
+ cvs_output (finfo->update_dir, 0);
+ cvs_output ("/", 1);
+ }
+ cvs_output (finfo->file, 0);
+ cvs_output ("\nnew revision: delete; previous revision: ", 0);
+ cvs_output (prev_rev, 0);
+ cvs_output ("\n", 0);
+ }
+
+ free (prev_rev);
+
+ free (old_path);
+
+ Scratch_Entry (finfo->entries, finfo->file);
+ return 0;
+}
+
+
+
+/*
+ * Do the actual checkin for added files
+ */
+static int
+finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
+{
+ int ret;
+
+ ret = Checkin ('A', finfo, rev, tag, options, saved_message);
+ if (ret == 0)
+ {
+ char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
+ if (unlink_file (tmp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmp);
+ free (tmp);
+ }
+ else if (finfo->rcs != NULL)
+ fixaddfile (finfo->rcs->path);
+
+ (void) time (&last_register_time);
+
+ return ret;
+}
+
+
+
+/*
+ * Unlock an rcs file
+ */
+static void
+unlockrcs (RCSNode *rcs)
+{
+ int retcode;
+
+ if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not unlock %s", rcs->path);
+ else
+ RCS_rewrite (rcs, NULL, NULL);
+}
+
+
+
+/*
+ * remove a partially added file. if we can parse it, leave it alone.
+ *
+ * FIXME: Every caller that calls this function can access finfo->rcs (the
+ * parsed RCSNode data), so we should be able to detect that the file needs
+ * to be removed without reparsing the file as we do below.
+ */
+static void
+fixaddfile (const char *rcs)
+{
+ RCSNode *rcsfile;
+ int save_really_quiet;
+
+ save_really_quiet = really_quiet;
+ really_quiet = 1;
+ if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
+ {
+ if (unlink_file (rcs) < 0)
+ error (0, errno, "cannot remove %s", rcs);
+ }
+ else
+ freercsnode (&rcsfile);
+ really_quiet = save_really_quiet;
+}
+
+
+
+/*
+ * put the branch back on an rcs file
+ */
+static void
+fixbranch (RCSNode *rcs, char *branch)
+{
+ int retcode;
+
+ if (branch != NULL)
+ {
+ if ((retcode = RCS_setbranch (rcs, branch)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "cannot restore branch to %s for %s", branch, rcs->path);
+ RCS_rewrite (rcs, NULL, NULL);
+ }
+}
+
+
+
+/*
+ * do the initial part of a file add for the named file. if adding
+ * with a tag, put the file in the Attic and point the symbolic tag
+ * at the committed revision.
+ *
+ * INPUTS
+ * file The name of the file in the workspace.
+ * repository The repository directory to expect to find FILE,v in.
+ * tag The name or rev num of the branch being added to, if any.
+ * options Any RCS keyword expansion options specified by the user.
+ * rcsnode A pointer to the pre-parsed RCSNode for this file, if the file
+ * exists in the repository. If this is NULL, assume the file
+ * does not yet exist.
+ *
+ * RETURNS
+ * 0 on success.
+ * 1 on errors, after printing any appropriate error messages.
+ *
+ * ERRORS
+ * This function will return an error when any of the following functions do:
+ * add_rcs_file
+ * RCS_setattic
+ * lock_RCS
+ * RCS_checkin
+ * RCS_parse (called to verify the newly created archive file)
+ * RCS_settag
+ */
+
+static int
+checkaddfile (const char *file, const char *repository, const char *tag,
+ const char *options, RCSNode **rcsnode)
+{
+ RCSNode *rcs;
+ char *fname;
+ int newfile = 0; /* Set to 1 if we created a new RCS archive. */
+ int retval = 1;
+ int adding_on_branch;
+
+ assert (rcsnode != NULL);
+
+ /* Callers expect to be able to use either "" or NULL to mean the
+ default keyword expansion. */
+ if (options != NULL && options[0] == '\0')
+ options = NULL;
+ if (options != NULL)
+ assert (options[0] == '-' && options[1] == 'k');
+
+ /* If numeric, it is on the trunk; check_fileproc enforced
+ this. */
+ adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
+
+ if (*rcsnode == NULL)
+ {
+ char *rcsname;
+ char *desc = NULL;
+ size_t descalloc = 0;
+ size_t desclen = 0;
+ const char *opt;
+
+ if (adding_on_branch)
+ {
+ mode_t omask;
+ rcsname = xmalloc (strlen (repository)
+ + sizeof (CVSATTIC)
+ + strlen (file)
+ + sizeof (RCSEXT)
+ + 3);
+ (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
+ omask = umask (cvsumask);
+ if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
+ error (1, errno, "cannot make directory `%s'", rcsname);
+ (void) umask (omask);
+ (void) sprintf (rcsname,
+ "%s/%s/%s%s",
+ repository,
+ CVSATTIC,
+ file,
+ RCSEXT);
+ }
+ else
+ rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
+
+ /* this is the first time we have ever seen this file; create
+ an RCS file. */
+ fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ /* If the file does not exist, no big deal. In particular, the
+ server does not (yet at least) create CVSEXT_LOG files. */
+ if (isfile (fname))
+ /* FIXME: Should be including update_dir in the appropriate
+ place here. */
+ get_file (fname, fname, "r", &desc, &descalloc, &desclen);
+ free (fname);
+
+ /* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
+ end of the log message if the message is nonempty.
+ Do it. RCS also deletes certain whitespace, in cleanlogmsg,
+ which we don't try to do here. */
+ if (desclen > 0)
+ {
+ expand_string (&desc, &descalloc, desclen + 1);
+ desc[desclen++] = '\012';
+ }
+
+ /* Set RCS keyword expansion options. */
+ if (options != NULL)
+ opt = options + 2;
+ else
+ opt = NULL;
+
+ if (add_rcs_file (NULL, rcsname, file, NULL, opt,
+ NULL, NULL, 0, NULL,
+ desc, desclen, NULL, 0) != 0)
+ {
+ if (rcsname != NULL)
+ free (rcsname);
+ goto out;
+ }
+ rcs = RCS_parsercsfile (rcsname);
+ newfile = 1;
+ if (rcsname != NULL)
+ free (rcsname);
+ if (desc != NULL)
+ free (desc);
+ *rcsnode = rcs;
+ }
+ else
+ {
+ /* file has existed in the past. Prepare to resurrect. */
+ char *rev;
+ char *oldexpand;
+
+ rcs = *rcsnode;
+
+ oldexpand = RCS_getexpand (rcs);
+ if ((oldexpand != NULL
+ && options != NULL
+ && strcmp (options + 2, oldexpand) != 0)
+ || (oldexpand == NULL && options != NULL))
+ {
+ /* We tell the user about this, because it means that the
+ old revisions will no longer retrieve the way that they
+ used to. */
+ error (0, 0, "changing keyword expansion mode to %s", options);
+ RCS_setexpand (rcs, options + 2);
+ }
+
+ if (!adding_on_branch)
+ {
+ /* We are adding on the trunk, so move the file out of the
+ Attic. */
+ if (!(rcs->flags & INATTIC))
+ {
+ error (0, 0, "warning: expected %s to be in Attic",
+ rcs->path);
+ }
+
+ /* Begin a critical section around the code that spans the
+ first commit on the trunk of a file that's already been
+ committed on a branch. */
+ SIG_beginCrSect ();
+
+ if (RCS_setattic (rcs, 0))
+ {
+ goto out;
+ }
+ }
+
+ rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
+ /* and lock it */
+ if (lock_RCS (file, rcs, rev, repository))
+ {
+ error (0, 0, "cannot lock revision %s in `%s'.",
+ rev ? rev : tag ? tag : "HEAD", rcs->path);
+ if (rev != NULL)
+ free (rev);
+ goto out;
+ }
+
+ if (rev != NULL)
+ free (rev);
+ }
+
+ /* when adding a file for the first time, and using a tag, we need
+ to create a dead revision on the trunk. */
+ if (adding_on_branch)
+ {
+ if (newfile)
+ {
+ char *tmp;
+ FILE *fp;
+ int retcode;
+
+ /* move the new file out of the way. */
+ fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
+ rename_file (file, fname);
+
+ /* Create empty FILE. Can't use copy_file with a DEVNULL
+ argument -- copy_file now ignores device files. */
+ fp = fopen (file, "w");
+ if (fp == NULL)
+ error (1, errno, "cannot open %s for writing", file);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", file);
+
+ tmp = Xasprintf ("file %s was initially added on branch %s.",
+ file, tag);
+ /* commit a dead revision. */
+ retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
+ RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
+ free (tmp);
+ if (retcode != 0)
+ {
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not create initial dead revision %s", rcs->path);
+ free (fname);
+ goto out;
+ }
+
+ /* put the new file back where it was */
+ rename_file (fname, file);
+ free (fname);
+
+ /* double-check that the file was written correctly */
+ freercsnode (&rcs);
+ rcs = RCS_parse (file, repository);
+ if (rcs == NULL)
+ {
+ error (0, 0, "could not read %s", rcs->path);
+ goto out;
+ }
+ *rcsnode = rcs;
+
+ /* and lock it once again. */
+ if (lock_RCS (file, rcs, NULL, repository))
+ {
+ error (0, 0, "cannot lock initial revision in `%s'.",
+ rcs->path);
+ goto out;
+ }
+ }
+
+ /* when adding with a tag, we need to stub a branch, if it
+ doesn't already exist. */
+ if (!RCS_nodeisbranch (rcs, tag))
+ {
+ /* branch does not exist. Stub it. */
+ char *head;
+ char *magicrev;
+ int retcode;
+ time_t headtime = -1;
+ char *revnum, *tmp;
+ FILE *fp;
+ time_t t = -1;
+ struct tm *ct;
+
+ fixbranch (rcs, sbranch);
+
+ head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
+ if (!head)
+ error (1, 0, "No head revision in archive file `%s'.",
+ rcs->print_path);
+ magicrev = RCS_magicrev (rcs, head);
+
+ /* If this is not a new branch, then we will want a dead
+ version created before this one. */
+ if (!newfile)
+ headtime = RCS_getrevtime (rcs, head, 0, 0);
+
+ retcode = RCS_settag (rcs, tag, magicrev);
+ RCS_rewrite (rcs, NULL, NULL);
+
+ free (head);
+ free (magicrev);
+
+ if (retcode != 0)
+ {
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not stub branch %s for %s", tag, rcs->path);
+ goto out;
+ }
+ /* We need to add a dead version here to avoid -rtag -Dtime
+ checkout problems between when the head version was
+ created and now. */
+ if (!newfile && headtime != -1)
+ {
+ /* move the new file out of the way. */
+ fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
+ rename_file (file, fname);
+
+ /* Create empty FILE. Can't use copy_file with a DEVNULL
+ argument -- copy_file now ignores device files. */
+ fp = fopen (file, "w");
+ if (fp == NULL)
+ error (1, errno, "cannot open %s for writing", file);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", file);
+
+ /* As we will be hacking the delta date, put the time
+ this was added into the log message. */
+ t = time (NULL);
+ ct = gmtime (&t);
+ tmp = Xasprintf ("file %s was added on branch %s on
%d-%02d-%02d %02d:%02d:%02d +0000",
+ file, tag,
+ ct->tm_year + (ct->tm_year < 100 ? 0 : 1900),
+ ct->tm_mon + 1, ct->tm_mday,
+ ct->tm_hour, ct->tm_min, ct->tm_sec);
+
+ /* commit a dead revision. */
+ revnum = RCS_whatbranch (rcs, tag);
+ retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
+ RCS_FLAGS_DEAD |
+ RCS_FLAGS_QUIET |
+ RCS_FLAGS_USETIME);
+ free (revnum);
+ free (tmp);
+
+ if (retcode != 0)
+ {
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not created dead stub %s for %s", tag,
+ rcs->path);
+ goto out;
+ }
+
+ /* put the new file back where it was */
+ rename_file (fname, file);
+ free (fname);
+
+ /* double-check that the file was written correctly */
+ freercsnode (&rcs);
+ rcs = RCS_parse (file, repository);
+ if (rcs == NULL)
+ {
+ error (0, 0, "could not read %s", rcs->path);
+ goto out;
+ }
+ *rcsnode = rcs;
+ }
+ }
+ else
+ {
+ /* lock the branch. (stubbed branches need not be locked.) */
+ if (lock_RCS (file, rcs, NULL, repository))
+ {
+ error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
+ goto out;
+ }
+ }
+
+ if (*rcsnode != rcs)
+ {
+ freercsnode (rcsnode);
+ *rcsnode = rcs;
+ }
+ }
+
+ fileattr_newfile (file);
+
+ /* At this point, we used to set the file mode of the RCS file
+ based on the mode of the file in the working directory. If we
+ are creating the RCS file for the first time, add_rcs_file does
+ this already. If we are re-adding the file, then perhaps it is
+ consistent to preserve the old file mode, just as we preserve
+ the old keyword expansion mode.
+
+ If we decide that we should change the modes, then we can't do
+ it here anyhow. At this point, the RCS file may be owned by
+ somebody else, so a chmod will fail. We need to instead do the
+ chmod after rewriting it.
+
+ FIXME: In general, I think the file mode (and the keyword
+ expansion mode) should be associated with a particular revision
+ of the file, so that it is possible to have different revisions
+ of a file have different modes. */
+
+ retval = 0;
+
+ out:
+ if (retval != 0 && SIG_inCrSect ())
+ SIG_endCrSect ();
+ return retval;
+}
+
+
+
+/*
+ * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
+ * couldn't. If the RCS file currently has a branch as the head, we must
+ * move the head back to the trunk before locking the file, and be sure to
+ * put the branch back as the head if there are any errors.
+ */
+static int
+lock_RCS (const char *user, RCSNode *rcs, const char *rev,
+ const char *repository)
+{
+ char *branch = NULL;
+ int err = 0;
+
+ /*
+ * For a specified, numeric revision of the form "1" or "1.1", (or when
+ * no revision is specified ""), definitely move the branch to the trunk
+ * before locking the RCS file.
+ *
+ * The assumption is that if there is more than one revision on the trunk,
+ * the head points to the trunk, not a branch... and as such, it's not
+ * necessary to move the head in this case.
+ */
+ if (rev == NULL
+ || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
+ {
+ branch = xstrdup (rcs->branch);
+ if (branch != NULL)
+ {
+ if (RCS_setbranch (rcs, NULL) != 0)
+ {
+ error (0, 0, "cannot change branch to default for %s",
+ rcs->path);
+ if (branch)
+ free (branch);
+ return 1;
+ }
+ }
+ err = RCS_lock (rcs, NULL, 1);
+ }
+ else
+ {
+ RCS_lock (rcs, rev, 1);
+ }
+
+ /* We used to call RCS_rewrite here, and that might seem
+ appropriate in order to write out the locked revision
+ information. However, such a call would actually serve no
+ purpose. CVS locks will prevent any interference from other
+ CVS processes. The comment above rcs_internal_lockfile
+ explains that it is already unsafe to use RCS and CVS
+ simultaneously. It follows that writing out the locked
+ revision information here would add no additional security.
+
+ If we ever do care about it, the proper fix is to create the
+ RCS lock file before calling this function, and maintain it
+ until the checkin is complete.
+
+ The call to RCS_lock is still required at present, since in
+ some cases RCS_checkin will determine which revision to check
+ in by looking for a lock. FIXME: This is rather roundabout,
+ and a more straightforward approach would probably be easier to
+ understand. */
+
+ if (err == 0)
+ {
+ if (sbranch != NULL)
+ free (sbranch);
+ sbranch = branch;
+ return 0;
+ }
+
+ /* try to restore the branch if we can on error */
+ if (branch != NULL)
+ fixbranch (rcs, branch);
+
+ if (branch)
+ free (branch);
+ return 1;
+}
+
+
+
+/*
+ * free an UPDATE node's data
+ */
+void
+update_delproc (Node *p)
+{
+ struct logfile_info *li = p->data;
+
+ if (li->tag)
+ free (li->tag);
+ if (li->rev_old)
+ free (li->rev_old);
+ if (li->rev_new)
+ free (li->rev_new);
+ free (li);
+}
+
+/*
+ * Free the commit_info structure in p.
+ */
+static void
+ci_delproc (Node *p)
+{
+ struct commit_info *ci = p->data;
+
+ if (ci->rev)
+ free (ci->rev);
+ if (ci->tag)
+ free (ci->tag);
+ if (ci->options)
+ free (ci->options);
+ free (ci);
+}
+
+/*
+ * Free the commit_info structure in p.
+ */
+static void
+masterlist_delproc (Node *p)
+{
+ struct master_lists *ml = p->data;
+
+ dellist (&ml->ulist);
+ dellist (&ml->cilist);
+ free (ml);
+}
Index: ccvs/src/cvs.h
diff -u /dev/null ccvs/src/cvs.h:1.346.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/cvs.h Tue Jan 17 15:41:23 2006
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS kit.
+ */
+
+/*
+ * basic information used in all source files
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h> /* this is stuff found via autoconf */
+#endif /* CONFIG_H */
+
+/* Add GNU attribute suppport. */
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# else
+# if __GNUC__ == 2 && __GNUC_MINOR__ < 96
+# define __pure__ /* empty */
+# endif
+# if __GNUC__ < 3
+# define __malloc__ /* empty */
+# endif
+# endif
+/* The __-protected variants of `format' and `printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __const__ const
+# define __format__ format
+# define __noreturn__ noreturn
+# define __printf__ printf
+# endif
+#endif /* __attribute__ */
+
+/* Some GNULIB headers require that we include system headers first. */
+#include "system.h"
+
+/* begin GNULIB headers */
+#include "dirname.h"
+#include "exit.h"
+#include "getdate.h"
+#include "minmax.h"
+#include "regex.h"
+#include "strcase.h"
+#include "stat-macros.h"
+#include "timespec.h"
+#include "unlocked-io.h"
+#include "xalloc.h"
+#include "xgetcwd.h"
+#include "xreadlink.h"
+#include "xsize.h"
+/* end GNULIB headers */
+
+#if ! STDC_HEADERS
+char *getenv();
+#endif /* ! STDC_HEADERS */
+
+/* Under OS/2, <stdio.h> doesn't define popen()/pclose(). */
+#ifdef USE_OWN_POPEN
+#include "popen.h"
+#endif
+
+#ifdef SERVER_SUPPORT
+/* If the system doesn't provide strerror, it won't be declared in
+ string.h. */
+char *strerror (int);
+#endif
+
+#include "hash.h"
+#include "stack.h"
+
+#include "root.h"
+
+#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
+# include "client.h"
+#endif
+
+#ifdef MY_NDBM
+#include "myndbm.h"
+#else
+#include <ndbm.h>
+#endif /* MY_NDBM */
+
+#include "rcs.h"
+
+
+
+/* Note that the _ONLY_ reason for PATH_MAX is if various system calls (getwd,
+ * getcwd, readlink) require/want us to use it. All other parts of CVS
+ * allocate pathname buffers dynamically, and we want to keep it that way.
+ */
+#include "pathmax.h"
+
+
+
+/* Definitions for the CVS Administrative directory and the files it contains.
+ Here as #define's to make changing the names a simple task. */
+
+#ifdef USE_VMS_FILENAMES
+#define CVSADM "CVS"
+#define CVSADM_ENT "CVS/Entries."
+#define CVSADM_ENTBAK "CVS/Entries.Backup"
+#define CVSADM_ENTLOG "CVS/Entries.Log"
+#define CVSADM_ENTSTAT "CVS/Entries.Static"
+#define CVSADM_REP "CVS/Repository."
+#define CVSADM_ROOT "CVS/Root."
+#define CVSADM_TAG "CVS/Tag."
+#define CVSADM_NOTIFY "CVS/Notify."
+#define CVSADM_NOTIFYTMP "CVS/Notify.tmp"
+#define CVSADM_BASE "CVS/Base"
+#define CVSADM_BASEREV "CVS/Baserev."
+#define CVSADM_BASEREVTMP "CVS/Baserev.tmp"
+#define CVSADM_TEMPLATE "CVS/Template."
+#else /* USE_VMS_FILENAMES */
+#define CVSADM "CVS"
+#define CVSADM_ENT "CVS/Entries"
+#define CVSADM_ENTBAK "CVS/Entries.Backup"
+#define CVSADM_ENTLOG "CVS/Entries.Log"
+#define CVSADM_ENTSTAT "CVS/Entries.Static"
+#define CVSADM_REP "CVS/Repository"
+#define CVSADM_ROOT "CVS/Root"
+#define CVSADM_TAG "CVS/Tag"
+#define CVSADM_NOTIFY "CVS/Notify"
+#define CVSADM_NOTIFYTMP "CVS/Notify.tmp"
+/* A directory in which we store base versions of files we currently are
+ editing with "cvs edit". */
+#define CVSADM_BASE "CVS/Base"
+#define CVSADM_BASEREV "CVS/Baserev"
+#define CVSADM_BASEREVTMP "CVS/Baserev.tmp"
+/* File which contains the template for use in log messages. */
+#define CVSADM_TEMPLATE "CVS/Template"
+#endif /* USE_VMS_FILENAMES */
+
+/* This is the special directory which we use to store various extra
+ per-directory information in the repository. It must be the same as
+ CVSADM to avoid creating a new reserved directory name which users cannot
+ use, but is a separate #define because if anyone changes it (which I don't
+ recommend), one needs to deal with old, unconverted, repositories.
+
+ See fileattr.h for details about file attributes, the only thing stored
+ in CVSREP currently. */
+#define CVSREP "CVS"
+
+/*
+ * Definitions for the CVSROOT Administrative directory and the files it
+ * contains. This directory is created as a sub-directory of the $CVSROOT
+ * environment variable, and holds global administration information for the
+ * entire source repository beginning at $CVSROOT.
+ */
+#define CVSROOTADM "CVSROOT"
+#define CVSROOTADM_CHECKOUTLIST "checkoutlist"
+#define CVSROOTADM_COMMITINFO "commitinfo"
+#define CVSROOTADM_CONFIG "config"
+#define CVSROOTADM_HISTORY "history"
+#define CVSROOTADM_IGNORE "cvsignore"
+#define CVSROOTADM_LOGINFO "loginfo"
+#define CVSROOTADM_MODULES "modules"
+#define CVSROOTADM_NOTIFY "notify"
+#define CVSROOTADM_PASSWD "passwd"
+#define CVSROOTADM_POSTADMIN "postadmin"
+#define CVSROOTADM_POSTPROXY "postproxy"
+#define CVSROOTADM_POSTTAG "posttag"
+#define CVSROOTADM_POSTWATCH "postwatch"
+#define CVSROOTADM_PREPROXY "preproxy"
+#define CVSROOTADM_RCSINFO "rcsinfo"
+#define CVSROOTADM_READERS "readers"
+#define CVSROOTADM_TAGINFO "taginfo"
+#define CVSROOTADM_USERS "users"
+#define CVSROOTADM_VALTAGS "val-tags"
+#define CVSROOTADM_VERIFYMSG "verifymsg"
+#define CVSROOTADM_WRAPPER "cvswrappers"
+#define CVSROOTADM_WRITERS "writers"
+
+#define CVSNULLREPOS "Emptydir" /* an empty directory */
+
+/* Other CVS file names */
+
+/* Files go in the attic if the head main branch revision is dead,
+ otherwise they go in the regular repository directories. The whole
+ concept of having an attic is sort of a relic from before death
+ support but on the other hand, it probably does help the speed of
+ some operations (such as main branch checkouts and updates). */
+#define CVSATTIC "Attic"
+
+#define CVSLCK "#cvs.lock"
+#define CVSHISTORYLCK "#cvs.history.lock"
+#define CVSVALTAGSLCK "#cvs.val-tags.lock"
+#define CVSRFL "#cvs.rfl"
+#define CVSPFL "#cvs.pfl"
+#define CVSWFL "#cvs.wfl"
+#define CVSPFLPAT "#cvs.pfl.*" /* wildcard expr to match plocks */
+#define CVSRFLPAT "#cvs.rfl.*" /* wildcard expr to match read locks */
+#define CVSEXT_LOG ",t"
+#define CVSPREFIX ",,"
+#define CVSDOTIGNORE ".cvsignore"
+#define CVSDOTWRAPPER ".cvswrappers"
+
+/* Command attributes -- see function lookup_command_attribute(). */
+#define CVS_CMD_IGNORE_ADMROOT 1
+
+/* Set if CVS needs to create a CVS/Root file upon completion of this
+ command. The name may be slightly confusing, because the flag
+ isn't really as general purpose as it seems (it is not set for cvs
+ release). */
+
+#define CVS_CMD_USES_WORK_DIR 2
+
+#define CVS_CMD_MODIFIES_REPOSITORY 4
+
+/* miscellaneous CVS defines */
+
+/* This is the string which is at the start of the non-log-message lines
+ that we put up for the user when they edit the log message. */
+#define CVSEDITPREFIX "CVS: "
+/* Number of characters in CVSEDITPREFIX to compare when deciding to strip
+ off those lines. We don't check for the space, to accomodate users who
+ have editors which strip trailing spaces. */
+#define CVSEDITPREFIXLEN 4
+
+#define CVSLCKAGE (60*60) /* 1-hour old lock files
cleaned up */
+#define CVSLCKSLEEP 30 /* wait 30 seconds before
retrying */
+#define CVSBRANCH "1.1.1" /* RCS branch used for vendor
srcs */
+
+#ifdef USE_VMS_FILENAMES
+# define BAKPREFIX "_$"
+#else /* USE_VMS_FILENAMES */
+# define BAKPREFIX ".#" /* when rcsmerge'ing */
+#endif /* USE_VMS_FILENAMES */
+
+/*
+ * Special tags. -rHEAD refers to the head of an RCS file, regardless
of any
+ * sticky tags. -rBASE refers to the current revision the user has checked
+ * out This mimics the behaviour of RCS.
+ */
+#define TAG_HEAD "HEAD"
+#define TAG_BASE "BASE"
+#define TAG_DOTHEAD "head"
+#define TAG_DOTBASE "base"
+#define TAG_COMMITID "commitid"
+#define TAG_PREVIOUS "prev"
+#define TAG_TRUNK "trunk"
+#define TAG_ORIGIN "origin"
+#define TAG_ROOT "root"
+#define TAG_NEXT "next"
+
+/* Environment variable used by CVS */
+#define CVSREAD_ENV "CVSREAD" /* make files read-only */
+#define CVSREAD_DFLT 0 /* writable files by default */
+
+#define CVSREADONLYFS_ENV "CVSREADONLYFS" /* repository is read-only */
+
+#define TMPDIR_ENV "TMPDIR" /* Temporary directory */
+#define CVS_PID_ENV "CVS_PID" /* pid of running cvs */
+
+#define EDITOR1_ENV "CVSEDITOR" /* which editor to use */
+#define EDITOR2_ENV "VISUAL" /* which editor to use */
+#define EDITOR3_ENV "EDITOR" /* which editor to use */
+
+#define CVSROOT_ENV "CVSROOT" /* source directory root */
+/* Define CVSROOT_DFLT to a fallback value for CVSROOT.
+ *
+#undef CVSROOT_DFL
+ */
+
+#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */
+#define WRAPPER_ENV "CVSWRAPPERS" /* name of the wrapper file */
+
+#define CVSUMASK_ENV "CVSUMASK" /* Effective umask for
repository */
+
+/*
+ * If the beginning of the Repository matches the following string, strip it
+ * so that the output to the logfile does not contain a full pathname.
+ *
+ * If the CVSROOT environment variable is set, it overrides this define.
+ */
+#define REPOS_STRIP "/master/"
+
+/* Large enough to hold DATEFORM. Not an arbitrary limit as long as
+ it is used for that purpose, and not to hold a string from the
+ command line, the client, etc. */
+#define MAXDATELEN 50
+
+/* The type of an entnode. */
+enum ent_type
+{
+ ENT_FILE, ENT_SUBDIR
+};
+
+/* structure of a entry record */
+struct entnode
+{
+ enum ent_type type;
+ char *user;
+ char *version;
+
+ /* Timestamp, or "" if none (never NULL). */
+ char *timestamp;
+
+ /* Keyword expansion options, or "" if none (never NULL). */
+ char *options;
+
+ char *tag;
+ char *date;
+ char *conflict;
+};
+typedef struct entnode Entnode;
+
+/* The type of request that is being done in do_module() */
+enum mtype
+{
+ CHECKOUT, TAG, PATCH, EXPORT, MISC
+};
+
+/*
+ * structure used for list-private storage by Entries_Open() and
+ * Version_TS() and Find_Directories().
+ */
+struct stickydirtag
+{
+ /* These fields pass sticky tag information from Entries_Open() to
+ Version_TS(). */
+ int aflag;
+ char *tag;
+ char *date;
+ int nonbranch;
+
+ /* This field is set by Entries_Open() if there was subdirectory
+ information; Find_Directories() uses it to see whether it needs
+ to scan the directory itself. */
+ int subdirs;
+};
+
+/* Flags for find_{names,dirs} routines */
+#define W_LOCAL 0x01 /* look for files locally */
+#define W_REPOS 0x02 /* look for files in the
repository */
+#define W_ATTIC 0x04 /* look for files in the attic
*/
+
+/* Flags for return values of direnter procs for the recursion processor */
+enum direnter_type
+{
+ R_PROCESS = 1, /* process files and maybe dirs */
+ R_SKIP_FILES, /* don't process files in this dir */
+ R_SKIP_DIRS, /* don't process sub-dirs */
+ R_SKIP_ALL /* don't process files or dirs */
+};
+#ifdef ENUMS_CAN_BE_TROUBLE
+typedef int Dtype;
+#else
+typedef enum direnter_type Dtype;
+#endif
+
+/* Recursion processor lock types */
+#define CVS_LOCK_NONE 0
+#define CVS_LOCK_READ 1
+#define CVS_LOCK_WRITE 2
+
+/* Option flags for Parse_Info() */
+#define PIOPT_ALL 1 /* accept "all" keyword */
+
+extern const char *program_name, *program_path, *cvs_cmd_name;
+extern char *Editor;
+extern int cvsadmin_root;
+extern char *CurDir;
+extern int really_quiet, quiet;
+extern int use_editor;
+extern int cvswrite;
+extern mode_t cvsumask;
+
+/* Temp dir abstraction. */
+/* From main.c. */
+const char *get_cvs_tmp_dir (void);
+/* From filesubr.c. */
+const char *get_system_temp_dir (void);
+void push_env_temp_dir (void);
+
+
+/* This global variable holds the global -d option. It is NULL if -d
+ was not used, which means that we must get the CVSroot information
+ from the CVSROOT environment variable or from a CVS/Root file. */
+extern char *CVSroot_cmdline;
+
+/* This variable keeps track of all of the CVSROOT directories that
+ * have been seen by the client.
+ */
+extern List *root_directories;
+
+char *emptydir_name (void);
+int safe_location (char *);
+
+extern int trace; /* Show all commands */
+extern int noexec; /* Don't modify disk anywhere */
+extern int readonlyfs; /* fail on all write locks; succeed all read
locks */
+extern int logoff; /* Don't write history entry */
+
+
+
+#define LOGMSG_REREAD_NEVER 0 /* do_verify - never reread message */
+#define LOGMSG_REREAD_ALWAYS 1 /* do_verify - always reread message */
+#define LOGMSG_REREAD_STAT 2 /* do_verify - reread message if changed */
+
+/* This header needs the LOGMSG_* defns above. */
+#include "parseinfo.h"
+
+/* This structure holds the global configuration data. */
+extern struct config *config;
+
+#ifdef CLIENT_SUPPORT
+extern List *dirs_sent_to_server; /* used to decide which "Argument
+ xxx" commands to send to each
+ server in multiroot mode. */
+#endif
+
+extern char *hostname;
+
+/* Externs that are included directly in the CVS sources */
+
+int RCS_merge (RCSNode *, const char *, const char *, const char *,
+ const char *, const char *);
+/* Flags used by RCS_* functions. See the description of the individual
+ functions for which flags mean what for each function. */
+#define RCS_FLAGS_FORCE 1
+#define RCS_FLAGS_DEAD 2
+#define RCS_FLAGS_QUIET 4
+#define RCS_FLAGS_MODTIME 8
+#define RCS_FLAGS_KEEPFILE 16
+#define RCS_FLAGS_USETIME 32
+
+int RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc,
+ char * const *diff_argv, const char *options,
+ const char *rev1, const char *rev1_cache,
+ const char *rev2,
+ const char *label1, const char *label2,
+ const char *workfile);
+int diff_exec (const char *file1, const char *file2,
+ const char *label1, const char *label2,
+ int iargc, char * const *iargv, const char *out);
+
+
+#include "error.h"
+
+/* If non-zero, error will use the CVS protocol to report error
+ * messages. This will only be set in the CVS server parent process;
+ * most other code is run via do_cvs_command, which forks off a child
+ * process and packages up its stderr in the protocol.
+ *
+ * This needs to be here rather than in error.h in order to use an unforked
+ * error.h from GNULIB.
+ */
+extern int error_use_protocol;
+
+
+DBM *open_module (void);
+List *Find_Directories (char *repository, int which, List *entries);
+void Entries_Close (List *entries);
+List *Entries_Open (int aflag, char *update_dir);
+void Subdirs_Known (List *entries);
+void Subdir_Register (List *, const char *, const char *);
+void Subdir_Deregister (List *, const char *, const char *);
+
+void parse_tagdate (char **tag, char **date, const char *input);
+char *Make_Date (const char *rawdate);
+char *date_from_time_t (time_t);
+void date_to_internet (char *, const char *);
+void date_to_tm (struct tm *, const char *);
+void tm_to_internet (char *, const struct tm *);
+char *gmformat_time_t (time_t unixtime);
+char *format_date_alloc (char *text);
+
+char *Name_Repository (const char *dir, const char *update_dir);
+const char *Short_Repository (const char *repository);
+void Sanitize_Repository_Name (char *repository);
+
+char *entries_time (time_t unixtime);
+time_t unix_time_stamp (const char *file);
+char *time_stamp (const char *file);
+
+typedef int (*CALLPROC) (const char *repository, const char *value,
+ void *closure);
+int Parse_Info (const char *infofile, const char *repository,
+ CALLPROC callproc, int opt, void *closure);
+
+typedef RETSIGTYPE (*SIGCLEANUPPROC) (int);
+int SIG_register (int sig, SIGCLEANUPPROC sigcleanup);
+bool isdir (const char *file);
+bool isfile (const char *file);
+ssize_t islink (const char *file);
+bool isdevice (const char *file);
+bool isreadable (const char *file);
+bool iswritable (const char *file);
+bool isaccessible (const char *file, const int mode);
+const char *last_component (const char *path);
+char *get_homedir (void);
+char *strcat_filename_onto_homedir (const char *, const char *);
+char *cvs_temp_name (void);
+FILE *cvs_temp_file (char **filename);
+
+int ls (int argc, char *argv[]);
+int unlink_file (const char *f);
+int unlink_file_dir (const char *f);
+
+/* This is the structure that the recursion processor passes to the
+ fileproc to tell it about a particular file. */
+struct file_info
+{
+ /* Name of the file, without any directory component. */
+ const char *file;
+
+ /* Name of the directory we are in, relative to the directory in
+ which this command was issued. We have cd'd to this directory
+ (either in the working directory or in the repository, depending
+ on which sort of recursion we are doing). If we are in the directory
+ in which the command was issued, this is "". */
+ const char *update_dir;
+
+ /* update_dir and file put together, with a slash between them as
+ necessary. This is the proper way to refer to the file in user
+ messages. */
+ const char *fullname;
+
+ /* Name of the directory corresponding to the repository which contains
+ this file. */
+ const char *repository;
+
+ /* The pre-parsed entries for this directory. */
+ List *entries;
+
+ RCSNode *rcs;
+};
+
+/* This needs to be included after the struct file_info definition since some
+ * of the functions subr.h defines refer to struct file_info.
+ */
+#include "subr.h"
+
+int update (int argc, char *argv[]);
+/* The only place this is currently used outside of update.c is add.c.
+ * Restricting its use to update.c seems to be in the best interest of
+ * modularity, but I can't think of a good way to get an update of a
+ * resurrected file done and print the fact otherwise.
+ */
+void write_letter (struct file_info *finfo, int letter);
+int xcmp (const char *file1, const char *file2);
+
+int Create_Admin (const char *dir, const char *update_dir,
+ const char *repository, const char *tag, const char *date,
+ int nonbranch, int warn, int dotemplate);
+int expand_at_signs (const char *, size_t, FILE *);
+
+/* Locking subsystem (implemented in lock.c). */
+
+int Reader_Lock (char *xrepository);
+void Simple_Lock_Cleanup (void);
+void Lock_Cleanup (void);
+
+/* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL,
+ and AFLAG, anyway. */
+void lock_tree_promotably (int argc, char **argv, int local, int which,
+ int aflag);
+
+/* See lock.c for description. */
+void lock_dir_for_write (const char *);
+
+/* Get a write lock for the history file. */
+int history_lock (const char *);
+void clear_history_lock (void);
+
+/* Get a write lock for the val-tags file. */
+int val_tags_lock (const char *);
+void clear_val_tags_lock (void);
+
+void Scratch_Entry (List * list, const char *fname);
+void ParseTag (char **tagp, char **datep, int *nonbranchp);
+void WriteTag (const char *dir, const char *tag, const char *date,
+ int nonbranch, const char *update_dir, const char *repository);
+void WriteTemplate (const char *update_dir, int dotemplate,
+ const char *repository);
+void cat_module (int status);
+void check_entries (char *dir);
+void close_module (DBM * db);
+void copy_file (const char *from, const char *to);
+void fperrmsg (FILE * fp, int status, int errnum, char *message,...);
+
+int ign_name (char *name);
+void ign_add (char *ign, int hold);
+void ign_add_file (char *file, int hold);
+void ign_setup (void);
+void ign_dir_add (char *name);
+int ignore_directory (const char *name);
+typedef void (*Ignore_proc) (const char *, const char *);
+void ignore_files (List *, List *, const char *, Ignore_proc);
+extern int ign_inhibit_server;
+
+#include "update.h"
+
+void make_directories (const char *name);
+void make_directory (const char *name);
+int mkdir_if_needed (const char *name);
+void rename_file (const char *from, const char *to);
+/* Expand wildcards in each element of (ARGC,ARGV). This is according to the
+ files which exist in the current directory, and accordingly to OS-specific
+ conventions regarding wildcard syntax. It might be desirable to change the
+ former in the future (e.g. "cvs status *.h" including files which don't
exist
+ in the working directory). The result is placed in *PARGC and *PARGV;
+ the *PARGV array itself and all the strings it contains are newly
+ malloc'd. It is OK to call it with PARGC == &ARGC or PARGV == &ARGV. */
+void expand_wild (int argc, char **argv,
+ int *pargc, char ***pargv);
+
+/* exithandle.c */
+void signals_register (RETSIGTYPE (*handler)(int));
+void cleanup_register (void (*handler) (void));
+
+void update_delproc (Node * p);
+void usage (const char *const *cpp);
+void xchmod (const char *fname, int writable);
+List *Find_Names (char *repository, int which, int aflag,
+ List ** optentries);
+void Register (List * list, const char *fname, const char *vn, const char *ts,
+ const char *options, const char *tag, const char *date,
+ const char *ts_conflict);
+void Update_Logfile (const char *repository, const char *xmessage,
+ FILE *xlogfp, List *xchanges);
+void do_editor (const char *dir, char **messagep,
+ const char *repository, List *changes);
+
+void do_verify (char **messagep, const char *repository, List *changes);
+
+typedef int (*CALLBACKPROC) (int argc, char *argv[], char *where,
+ char *mwhere, char *mfile, int shorten, int local_specified,
+ char *omodule, char *msg);
+
+
+typedef int (*FILEPROC) (void *callerdat, struct file_info *finfo);
+typedef int (*FILESDONEPROC) (void *callerdat, int err,
+ const char *repository, const char *update_dir,
+ List *entries);
+typedef Dtype (*DIRENTPROC) (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+typedef int (*DIRLEAVEPROC) (void *callerdat, const char *dir, int err,
+ const char *update_dir, List *entries);
+
+int mkmodules (char *dir);
+int init (int argc, char **argv);
+
+int do_module (DBM * db, char *mname, enum mtype m_type, char *msg,
+ CALLBACKPROC callback_proc, char *where, int shorten,
+ int local_specified, int run_module_prog, int build_dirs,
+ char *extra_arg);
+void history_write (int type, const char *update_dir, const char *revs,
+ const char *name, const char *repository);
+int start_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc,
+ DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc,
+ void *callerdat,
+ int argc, char *argv[], int local, int which,
+ int aflag, int locktype, char *update_preload,
+ int dosrcs, char *repository);
+void SIG_beginCrSect (void);
+void SIG_endCrSect (void);
+int SIG_inCrSect (void);
+void read_cvsrc (int *argc, char ***argv, const char *cmdname);
+
+/* flags for run_exec(), the fast system() for CVS */
+#define RUN_NORMAL 0x0000 /* no special behaviour */
+#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */
+#define RUN_REALLY 0x0002 /* do the exec, even if noexec
is on */
+#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't
truncate */
+#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't
truncate */
+#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for
command */
+#define RUN_TTY (char *)0 /* for the benefit of lint */
+
+void run_add_arg_p (int *, size_t *, char ***, const char *s);
+void run_arg_free_p (int, char **);
+void run_add_arg (const char *s);
+void run_print (FILE * fp);
+void run_setup (const char *prog);
+int run_exec (const char *stin, const char *stout, const char *sterr,
+ int flags);
+int run_piped (int *, int *);
+
+/* other similar-minded stuff from run.c. */
+FILE *run_popen (const char *, const char *);
+int piped_child (char *const *, int *, int *, bool);
+void close_on_exec (int);
+
+pid_t waitpid (pid_t, int *, int);
+
+/*
+ * a struct vers_ts contains all the information about a file including the
+ * user and rcs file names, and the version checked out and the head.
+ *
+ * this is usually obtained from a call to Version_TS which takes a
+ * tag argument for the RCS file if desired
+ */
+struct vers_ts
+{
+ /* rcs version user file derives from, from CVS/Entries.
+ It can have the following special values:
+
+ NULL = file is not mentioned in Entries (this is also used for a
+ directory).
+ "" = INVALID! The comment used to say that it meant "no user file"
+ but as far as I know CVS didn't actually use it that way.
+ Note that according to cvs.texinfo, "" is not valid in the
+ Entries file.
+ 0 = user file is new
+ -vers = user file to be removed. */
+ char *vn_user;
+
+ /* Numeric revision number corresponding to ->vn_tag (->vn_tag
+ will often be symbolic). */
+ char *vn_rcs;
+ /* If ->tag is a simple tag in the RCS file--a tag which really
+ exists which is not a magic revision--and if ->date is NULL,
+ then this is a copy of ->tag. Otherwise, it is a copy of
+ ->vn_rcs. */
+ char *vn_tag;
+
+ /* This is the timestamp from stating the file in the working directory.
+ It is NULL if there is no file in the working directory. It is
+ "Is-modified" if we know the file is modified but don't have its
+ contents. */
+ char *ts_user;
+ /* Timestamp from CVS/Entries. For the server, ts_user and ts_rcs
+ are computed in a slightly different way, but the fact remains that
+ if they are equal the file in the working directory is unmodified
+ and if they differ it is modified. */
+ char *ts_rcs;
+
+ /* Options from CVS/Entries (keyword expansion), malloc'd. If none,
+ then it is an empty string (never NULL). */
+ char *options;
+
+ /* If non-NULL, there was a conflict (or merely a merge? See merge_file)
+ and the time stamp in this field is the time stamp of the working
+ directory file which was created with the conflict markers in it.
+ This is from CVS/Entries. */
+ char *ts_conflict;
+
+ /* Tag specified on the command line, or if none, tag stored in
+ CVS/Entries. */
+ char *tag;
+ /* Date specified on the command line, or if none, date stored in
+ CVS/Entries. */
+ char *date;
+ /* If this is 1, then tag is not a branch tag. If this is 0, then
+ tag may or may not be a branch tag. */
+ int nonbranch;
+
+ /* Pointer to entries file node */
+ Entnode *entdata;
+
+ /* Pointer to parsed src file info */
+ RCSNode *srcfile;
+};
+typedef struct vers_ts Vers_TS;
+
+Vers_TS *Version_TS (struct file_info *finfo, char *options, char *tag,
+ char *date, int force_tag_match,
+ int set_time);
+void freevers_ts (Vers_TS ** versp);
+char *Version_resolve_relTag (struct file_info *, const char *, bool);
+
+
+/* Miscellaneous CVS infrastructure which layers on top of the recursion
+ processor (for example, needs struct file_info). */
+
+int Checkin (int type, struct file_info *finfo, char *rev,
+ char *tag, char *options, char *message);
+int No_Difference (struct file_info *finfo, Vers_TS *vers);
+/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */
+int special_file_mismatch (struct file_info *finfo,
+ char *rev1, char *rev2);
+
+/* CVSADM_BASEREV stuff, from entries.c. */
+char *base_get (struct file_info *);
+void base_register (struct file_info *, char *);
+void base_deregister (struct file_info *);
+
+/*
+ * defines for Classify_File() to determine the current state of a file.
+ * These are also used as types in the data field for the list we make for
+ * Update_Logfile in commit, import, and add.
+ */
+enum classify_type
+{
+ T_UNKNOWN = 1, /* no old-style analog existed */
+ T_CONFLICT, /* C (conflict) list
*/
+ T_NEEDS_MERGE, /* G (needs merging) list */
+ T_MODIFIED, /* M (needs checked in) list
*/
+ T_CHECKOUT, /* O (needs checkout) list
*/
+ T_ADDED, /* A (added file) list */
+ T_REMOVED, /* R (removed file) list */
+ T_REMOVE_ENTRY, /* W (removed entry) list */
+ T_UPTODATE, /* File is up-to-date
*/
+ T_PATCH, /* P Like C, but can patch */
+ T_TITLE /* title for node type */
+};
+typedef enum classify_type Ctype;
+
+Ctype Classify_File (struct file_info *finfo, char *tag, char *date, char
*options,
+ int force_tag_match, int aflag, Vers_TS **versp, int pipeout);
+
+/*
+ * structure used for list nodes passed to Update_Logfile() and
+ * do_editor().
+ */
+struct logfile_info
+{
+ enum classify_type type;
+ char *tag;
+ char *rev_old; /* rev number before a commit/modify,
+ NULL for add or import */
+ char *rev_new; /* rev number after a commit/modify,
+ add, or import, NULL for remove */
+};
+
+/* Wrappers. */
+
+typedef enum { WRAP_MERGE, WRAP_COPY } WrapMergeMethod;
+typedef enum {
+ /* -t and -f wrapper options. Treating directories as single files. */
+ WRAP_TOCVS,
+ WRAP_FROMCVS,
+ /* -k wrapper option. Default keyword expansion options. */
+ WRAP_RCSOPTION
+} WrapMergeHas;
+
+void wrap_setup (void);
+int wrap_name_has (const char *name,WrapMergeHas has);
+char *wrap_rcsoption (const char *fileName, int asFlag);
+char *wrap_tocvs_process_file (const char *fileName);
+int wrap_merge_is_copy (const char *fileName);
+void wrap_fromcvs_process_file (const char *fileName);
+void wrap_add_file (const char *file,int temp);
+void wrap_add (char *line,int temp);
+void wrap_send (void);
+#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
+void wrap_unparse_rcs_options (char **, int);
+#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
+
+/* Pathname expansion */
+char *expand_path (const char *name, const char *cvsroot, bool formatsafe,
+ const char *file, int line);
+
+/* User variables. */
+extern List *variable_list;
+
+void variable_set (char *nameval);
+
+int watch (int argc, char **argv);
+int edit (int argc, char **argv);
+int unedit (int argc, char **argv);
+int editors (int argc, char **argv);
+int watchers (int argc, char **argv);
+int annotate (int argc, char **argv);
+int add (int argc, char **argv);
+int admin (int argc, char **argv);
+int checkout (int argc, char **argv);
+int commit (int argc, char **argv);
+int diff (int argc, char **argv);
+int history (int argc, char **argv);
+int import (int argc, char **argv);
+int cvslog (int argc, char **argv);
+#ifdef AUTH_CLIENT_SUPPORT
+/* Some systems (namely Mac OS X) have conflicting definitions for these
+ * functions. Avoid them.
+ */
+#ifdef HAVE_LOGIN
+# define login cvs_login
+#endif /* HAVE_LOGIN */
+#ifdef HAVE_LOGOUT
+# define logout cvs_logout
+#endif /* HAVE_LOGOUT */
+int login (int argc, char **argv);
+int logout (int argc, char **argv);
+#endif /* AUTH_CLIENT_SUPPORT */
+int patch (int argc, char **argv);
+int release (int argc, char **argv);
+int cvsremove (int argc, char **argv);
+int rtag (int argc, char **argv);
+int cvsstatus (int argc, char **argv);
+int cvstag (int argc, char **argv);
+int version (int argc, char **argv);
+
+unsigned long int lookup_command_attribute (const char *);
+
+#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
+char *scramble (char *str);
+char *descramble (char *str);
+#endif /* AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT */
+
+#ifdef AUTH_CLIENT_SUPPORT
+char *get_cvs_password (void);
+/* get_cvs_port_number() is not pure since the /etc/services file could change
+ * between calls. */
+int get_cvs_port_number (const cvsroot_t *root);
+/* normalize_cvsroot() is not pure since it calls get_cvs_port_number. */
+char *normalize_cvsroot (const cvsroot_t *root)
+ __attribute__ ((__malloc__));
+#endif /* AUTH_CLIENT_SUPPORT */
+
+void tag_check_valid (const char *, int, char **, int, int, char *, bool);
+
+#include "server.h"
+
+/* From server.c and documented there. */
+void cvs_output (const char *, size_t);
+void cvs_output_binary (char *, size_t);
+void cvs_outerr (const char *, size_t);
+void cvs_flusherr (void);
+void cvs_flushout (void);
+void cvs_output_tagged (const char *, const char *);
+
+extern const char *global_session_id;
+
+/* From find_names.c. */
+List *find_files (const char *dir, const char *pat);
Index: ccvs/src/import.c
diff -u /dev/null ccvs/src/import.c:1.175.8.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/import.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,1782 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * "import" checks in the vendor release located in the current directory into
+ * the CVS source repository. The CVS vendor branch support is utilized.
+ *
+ * At least three arguments are expected to follow the options:
+ * repository Where the source belongs relative to the CVSROOT
+ * VendorTag Vendor's major tag
+ * VendorReleTag Tag for this particular release
+ *
+ * Additional arguments specify more Vendor Release Tags.
+ */
+
+#include "cvs.h"
+#include "lstat.h"
+#include "save-cwd.h"
+
+static char *get_comment (const char *user);
+static int add_rev (char *message, RCSNode *rcs, char *vfile,
+ char *vers);
+static int add_tags (RCSNode *rcs, char *vfile, char *vtag, int targc,
+ char *targv[]);
+static int import_descend (char *message, char *vtag, int targc, char
*targv[]);
+static int import_descend_dir (char *message, char *dir, char *vtag,
+ int targc, char *targv[]);
+static int process_import_file (char *message, char *vfile, char *vtag,
+ int targc, char *targv[]);
+static int update_rcs_file (char *message, char *vfile, char *vtag, int targc,
+ char *targv[], int inattic);
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+static int preserve_initial_permissions (FILE *fprcs, const char *userfile,
+ mode_t file_type, struct stat *sbp);
+#endif
+static int expand_and_copy_contents (FILE *fprcs, mode_t file_type,
+ const char *user, FILE *fpuser);
+static void add_log (int ch, char *fname);
+
+static int repos_len;
+static char *vhead;
+static char *vbranch;
+static FILE *logfp;
+static char *repository;
+static int conflicts;
+static int use_file_modtime;
+static char *keyword_opt = NULL;
+static bool killnew;
+
+static const char *const import_usage[] =
+{
+ "Usage: %s %s [-dX] [-k subst] [-I ign] [-m msg] [-b branch]\n",
+ " [-W spec] repository vendor-tag release-tags...\n",
+ "\t-d\tUse the file's modification time as the time of import.\n",
+ "\t-X\tWhen importing new files, mark their trunk revisions as dead.\n",
+ "\t-k sub\tSet default RCS keyword substitution mode.\n",
+ "\t-I ign\tMore files to ignore (! to reset).\n",
+ "\t-b bra\tVendor branch id.\n",
+ "\t-m msg\tLog message.\n",
+ "\t-W spec\tWrappers specification line.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+int
+import (int argc, char **argv)
+{
+ char *message = NULL;
+ char *tmpfile;
+ char *cp;
+ int i, c, msglen, err;
+ List *ulist;
+ Node *p;
+ struct logfile_info *li;
+
+ if (argc == -1)
+ usage (import_usage);
+
+ /* Force -X behaviour or not based on the CVS repository
+ CVSROOT/config setting. */
+#ifdef CLIENT_SUPPORT
+ killnew = !current_parsed_root->isremote
+ && config->ImportNewFilesToVendorBranchOnly;
+#else /* !CLIENT_SUPPORT */
+ killnew = config->ImportNewFilesToVendorBranchOnly;
+#endif /* CLIENT_SUPPORT */
+
+
+ ign_setup ();
+ wrap_setup ();
+
+ vbranch = xstrdup (CVSBRANCH);
+ optind = 0;
+ while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:X")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ case 'q':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_name);
+ break;
+ case 'd':
+ if (server_active)
+ {
+ /* CVS 1.10 and older clients will send this, but it
+ doesn't do any good. So tell the user we can't
+ cope, rather than silently losing. */
+ error (0, 0,
+ "warning: not setting the time of import from the
file");
+ error (0, 0, "due to client limitations");
+ }
+ use_file_modtime = 1;
+ break;
+ case 'b':
+ free (vbranch);
+ vbranch = xstrdup (optarg);
+ break;
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = 1;
+#else
+ use_editor = 0;
+#endif
+ if (message) free (message);
+ message = xstrdup (optarg);
+ break;
+ case 'I':
+ ign_add (optarg, 0);
+ break;
+ case 'k':
+ /* RCS_check_kflag returns strings of the form -kxx. We
+ only use it for validation, so we can free the value
+ as soon as it is returned. */
+ free (RCS_check_kflag (optarg));
+ keyword_opt = optarg;
+ break;
+ case 'W':
+ wrap_add (optarg, 0);
+ break;
+ case 'X':
+ killnew = true;
+ break;
+ case '?':
+ default:
+ usage (import_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 3)
+ usage (import_usage);
+
+ /* This is for handling the Checkin-time request. It might seem a
+ bit odd to enable the use_file_modtime code even in the case
+ where Checkin-time was not sent for a particular file. The
+ effect is that we use the time of upload, rather than the time
+ when we call RCS_checkin. Since those times are both during
+ CVS's run, that seems OK, and it is easier to implement than
+ putting the "was Checkin-time sent" flag in CVS/Entries or some
+ such place. */
+
+ if (server_active)
+ use_file_modtime = 1;
+
+ /* Don't allow "CVS" as any directory in module path.
+ *
+ * Could abstract this to valid_module_path, but I don't think we'll need
+ * to call it from anywhere else.
+ */
+ if ((cp = strstr (argv[0], "CVS")) && /* path contains "CVS" AND ... */
+ ((cp == argv[0]) || ISSLASH (*(cp-1))) && /* /^CVS/ OR m#/CVS# AND ...
*/
+ ((*(cp+3) == '\0') || ISSLASH (*(cp+3))) /* /CVS$/ OR m#CVS/# */
+ )
+ {
+ error (0, 0,
+ "The word `CVS' is reserved by CVS and may not be used");
+ error (1, 0, "as a directory in a path or as a file name.");
+ }
+
+ for (i = 1; i < argc; i++) /* check the tags for validity */
+ {
+ int j;
+
+ RCS_check_tag (argv[i]);
+ for (j = 1; j < i; j++)
+ if (strcmp (argv[j], argv[i]) == 0)
+ error (1, 0, "tag `%s' was specified more than once", argv[i]);
+ }
+
+ if (ISABSOLUTE (argv[0]) || pathname_levels (argv[0]) > 0)
+ /* It is somewhere between a security hole and "unexpected" to
+ let the client start mucking around outside the cvsroot
+ (wouldn't get the right CVSROOT configuration, &c). */
+ error (1, 0, "directory %s not relative within the repository",
+ argv[0]);
+
+ if (current_parsed_root == NULL)
+ {
+ error (0, 0, "missing CVSROOT environment variable\n");
+ error (1, 0, "Set it or specify the '-d' option to %s.",
+ program_name);
+ }
+ repository = Xasprintf ("%s/%s", current_parsed_root->directory, argv[0]);
+ repos_len = strlen (current_parsed_root->directory);
+
+ /*
+ * Consistency checks on the specified vendor branch. It must be
+ * composed of only numbers and dots ('.'). Also, for now we only
+ * support branching to a single level, so the specified vendor branch
+ * must only have two dots in it (like "1.1.1").
+ */
+ {
+ regex_t pat;
+ int ret = regcomp (&pat, "^[1-9][0-9]*\\.[1-9][0-9]*\\.[1-9][0-9]*$",
+ REG_EXTENDED);
+ assert (!ret);
+ if (regexec (&pat, vbranch, 0, NULL, 0))
+ {
+ error (1, 0,
+"Only numeric branch specifications with two dots are\n"
+"supported by import, not `%s'. For example: `1.1.1'.",
+ vbranch);
+ }
+ regfree (&pat);
+ }
+
+ /* Set vhead to the branch's parent. */
+ vhead = xstrdup (vbranch);
+ cp = strrchr (vhead, '.');
+ *cp = '\0';
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ /* For rationale behind calling start_server before do_editor, see
+ commit.c */
+ start_server ();
+ }
+#endif
+
+ if (!server_active && use_editor)
+ {
+ do_editor (NULL, &message,
+ current_parsed_root->isremote ? NULL : repository,
+ NULL);
+ }
+ msglen = message == NULL ? 0 : strlen (message);
+ if (msglen == 0 || message[msglen - 1] != '\n')
+ {
+ char *nm = xmalloc (msglen + 2);
+ *nm = '\0';
+ if (message != NULL)
+ {
+ (void) strcpy (nm, message);
+ free (message);
+ }
+ (void) strcat (nm + msglen, "\n");
+ message = nm;
+ }
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ int err;
+
+ if (vbranch[0] != '\0')
+ option_with_arg ("-b", vbranch);
+ option_with_arg ("-m", message ? message : "");
+ if (keyword_opt != NULL)
+ option_with_arg ("-k", keyword_opt);
+ if (killnew)
+ send_arg ("-X");
+ /* The only ignore processing which takes place on the server side
+ is the CVSROOT/cvsignore file. But if the user specified -I !,
+ the documented behavior is to not process said file. */
+ if (ign_inhibit_server)
+ {
+ send_arg ("-I");
+ send_arg ("!");
+ }
+ wrap_send ();
+
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ }
+
+ logfp = stdin;
+ client_import_setup (repository);
+ err = import_descend (message, argv[1], argc - 2, argv + 2);
+ client_import_done ();
+ if (message)
+ free (message);
+ free (repository);
+ free (vbranch);
+ free (vhead);
+ send_to_server ("import\012", 0);
+ err += get_responses_and_close ();
+ return err;
+ }
+#endif
+
+ if (!safe_location (NULL))
+ {
+ error (1, 0, "attempt to import the repository");
+ }
+
+ ulist = getlist ();
+ p = getnode ();
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->key = xstrdup ("- Imported sources");
+ li = xmalloc (sizeof (struct logfile_info));
+ li->type = T_TITLE;
+ li->tag = xstrdup (vbranch);
+ li->rev_old = li->rev_new = NULL;
+ p->data = li;
+ (void) addnode (ulist, p);
+ do_verify (&message, repository, ulist);
+
+ /*
+ * Make all newly created directories writable. Should really use a more
+ * sophisticated security mechanism here.
+ */
+ (void) umask (cvsumask);
+ make_directories (repository);
+
+ /* Create the logfile that will be logged upon completion */
+ if ((logfp = cvs_temp_file (&tmpfile)) == NULL)
+ error (1, errno, "cannot create temporary file `%s'", tmpfile);
+ /* On systems where we can unlink an open file, do so, so it will go
+ away no matter how we exit. FIXME-maybe: Should be checking for
+ errors but I'm not sure which error(s) we get if we are on a system
+ where one can't unlink open files. */
+ (void) CVS_UNLINK (tmpfile);
+ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
+ (void) fprintf (logfp, "Release Tags:\t");
+ for (i = 2; i < argc; i++)
+ (void) fprintf (logfp, "%s\n\t\t", argv[i]);
+ (void) fprintf (logfp, "\n");
+
+ /* Just Do It. */
+ err = import_descend (message, argv[1], argc - 2, argv + 2);
+ if (conflicts || killnew)
+ {
+ if (!really_quiet)
+ {
+ char buf[20];
+
+ cvs_output_tagged ("+importmergecmd", NULL);
+ cvs_output_tagged ("newline", NULL);
+ if (conflicts)
+ sprintf (buf, "%d", conflicts);
+ else
+ strcpy (buf, "No");
+ cvs_output_tagged ("conflicts", buf);
+ cvs_output_tagged ("text", " conflicts created by this import.");
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("text",
+ "Use the following command to help the merge:");
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("text", "\t");
+ cvs_output_tagged ("text", program_name);
+ if (CVSroot_cmdline != NULL)
+ {
+ cvs_output_tagged ("text", " -d ");
+ cvs_output_tagged ("text", CVSroot_cmdline);
+ }
+ cvs_output_tagged ("text", " checkout -j");
+ cvs_output_tagged ("mergetag1", "<prev_rel_tag>");
+ cvs_output_tagged ("text", " -j");
+ cvs_output_tagged ("mergetag2", argv[2]);
+ cvs_output_tagged ("text", " ");
+ cvs_output_tagged ("repository", argv[0]);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("-importmergecmd", NULL);
+ }
+
+ /* FIXME: I'm not sure whether we need to put this information
+ into the loginfo. If we do, then note that it does not
+ report any required -d option. There is no particularly
+ clean way to tell the server about the -d option used by
+ the client. */
+ if (conflicts)
+ (void) fprintf (logfp, "\n%d", conflicts);
+ else
+ (void) fprintf (logfp, "\nNo");
+ (void) fprintf (logfp, " conflicts created by this import.\n");
+ (void) fprintf (logfp,
+ "Use the following command to help the merge:\n\n");
+ (void) fprintf (logfp, "\t%s checkout ", program_name);
+ (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n",
+ argv[1], argv[1], argv[0]);
+ }
+ else
+ {
+ if (!really_quiet)
+ cvs_output ("\nNo conflicts created by this import\n\n", 0);
+ (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
+ }
+
+ /*
+ * Write out the logfile and clean up.
+ */
+ Update_Logfile (repository, message, logfp, ulist);
+ dellist (&ulist);
+ if (fclose (logfp) < 0)
+ error (0, errno, "error closing %s", tmpfile);
+
+ /* Make sure the temporary file goes away, even on systems that don't let
+ you delete a file that's in use. */
+ if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile);
+ free (tmpfile);
+
+ char *commitid = Xasprintf ("@%s", global_session_id);
+ tag_check_valid (commitid, argc, argv, 0, 1, "", true);
+ free (commitid);
+
+ if (message)
+ free (message);
+ free (repository);
+ free (vbranch);
+ free (vhead);
+
+ return err;
+}
+
+/* Process all the files in ".", then descend into other directories.
+ Returns 0 for success, or >0 on error (in which case a message
+ will have been printed). */
+static int
+import_descend (char *message, char *vtag, int targc, char **targv)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ int err = 0;
+ List *dirlist = NULL;
+
+ /* first, load up any per-directory ignore lists */
+ ign_add_file (CVSDOTIGNORE, 1);
+ wrap_add_file (CVSDOTWRAPPER, 1);
+
+ if (!current_parsed_root->isremote)
+ lock_dir_for_write (repository);
+
+ if ((dirp = CVS_OPENDIR (".")) == NULL)
+ {
+ error (0, errno, "cannot open directory");
+ err++;
+ }
+ else
+ {
+ errno = 0;
+ while ((dp = CVS_READDIR (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
+ goto one_more_time_boys;
+
+ /* CVS directories are created in the temp directory by
+ server.c because it doesn't special-case import. So
+ don't print a message about them, regardless of -I!. */
+ if (server_active && strcmp (dp->d_name, CVSADM) == 0)
+ goto one_more_time_boys;
+
+ if (ign_name (dp->d_name))
+ {
+ add_log ('I', dp->d_name);
+ goto one_more_time_boys;
+ }
+
+ if (
+#ifdef DT_DIR
+ (dp->d_type == DT_DIR
+ || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name)))
+#else
+ isdir (dp->d_name)
+#endif
+ && !wrap_name_has (dp->d_name, WRAP_TOCVS)
+ )
+ {
+ Node *n;
+
+ if (dirlist == NULL)
+ dirlist = getlist ();
+
+ n = getnode ();
+ n->key = xstrdup (dp->d_name);
+ addnode (dirlist, n);
+ }
+ else if (
+#ifdef DT_DIR
+ dp->d_type == DT_LNK
+ || (dp->d_type == DT_UNKNOWN && islink (dp->d_name))
+#else
+ islink (dp->d_name)
+#endif
+ )
+ {
+ add_log ('L', dp->d_name);
+ err++;
+ }
+ else
+ {
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ err += client_process_import_file (message, dp->d_name,
+ vtag, targc, targv,
+ repository,
+ keyword_opt != NULL &&
+ keyword_opt[0] == 'b',
+ use_file_modtime);
+ else
+#endif
+ err += process_import_file (message, dp->d_name,
+ vtag, targc, targv);
+ }
+ one_more_time_boys:
+ errno = 0;
+ }
+ if (errno != 0)
+ {
+ error (0, errno, "cannot read directory");
+ ++err;
+ }
+ (void) CVS_CLOSEDIR (dirp);
+ }
+
+ if (!current_parsed_root->isremote)
+ Simple_Lock_Cleanup ();
+
+ if (dirlist != NULL)
+ {
+ Node *head, *p;
+
+ head = dirlist->list;
+ for (p = head->next; p != head; p = p->next)
+ {
+ err += import_descend_dir (message, p->key, vtag, targc, targv);
+ }
+
+ dellist (&dirlist);
+ }
+
+ return err;
+}
+
+/*
+ * Process the argument import file.
+ */
+static int
+process_import_file (char *message, char *vfile, char *vtag, int targc,
+ char **targv)
+{
+ char *rcs;
+ int inattic = 0;
+
+ rcs = Xasprintf ("%s/%s%s", repository, vfile, RCSEXT);
+ if (!isfile (rcs))
+ {
+ char *attic_name;
+
+ attic_name = xmalloc (strlen (repository) + strlen (vfile) +
+ sizeof (CVSATTIC) + sizeof (RCSEXT) + 10);
+ (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
+ vfile, RCSEXT);
+ if (!isfile (attic_name))
+ {
+ int retval;
+ char *free_opt = NULL;
+ char *our_opt = keyword_opt;
+
+ /* If marking newly-imported files as dead, they must be
+ created in the attic! */
+ if (!killnew)
+ free (attic_name);
+ else
+ {
+ free (rcs);
+ rcs = attic_name;
+
+ /* Attempt to make the Attic directory, in case it
+ does not exist. */
+ (void) sprintf (rcs, "%s/%s", repository, CVSATTIC);
+ if (CVS_MKDIR (rcs, 0777 ) != 0 && errno != EEXIST)
+ error (1, errno, "cannot make directory `%s'", rcs);
+
+ /* Note that the above clobbered the path name, so we
+ recreate it here. */
+ (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC,
+ vfile, RCSEXT);
+ }
+
+ /*
+ * A new import source file; it doesn't exist as a ,v within the
+ * repository nor in the Attic -- create it anew.
+ */
+ add_log ('N', vfile);
+
+#ifdef SERVER_SUPPORT
+ /* The most reliable information on whether the file is binary
+ is what the client told us. That is because if the client had
+ the wrong idea about binaryness, it corrupted the file, so
+ we might as well believe the client. */
+ if (server_active)
+ {
+ Node *node;
+ List *entries;
+
+ /* Reading all the entries for each file is fairly silly, and
+ probably slow. But I am too lazy at the moment to do
+ anything else. */
+ entries = Entries_Open (0, NULL);
+ node = findnode_fn (entries, vfile);
+ if (node != NULL)
+ {
+ Entnode *entdata = node->data;
+
+ if (entdata->type == ENT_FILE)
+ {
+ assert (entdata->options[0] == '-'
+ && entdata->options[1] == 'k');
+ our_opt = xstrdup (entdata->options + 2);
+ free_opt = our_opt;
+ }
+ }
+ Entries_Close (entries);
+ }
+#endif
+
+ retval = add_rcs_file (message, rcs, vfile, vhead, our_opt,
+ vbranch, vtag, targc, targv,
+ NULL, 0, logfp, killnew);
+ if (free_opt != NULL)
+ free (free_opt);
+ free (rcs);
+ return retval;
+ }
+ free (attic_name);
+ inattic = 1;
+ }
+
+ free (rcs);
+ /*
+ * an rcs file exists. have to do things the official, slow, way.
+ */
+ return update_rcs_file (message, vfile, vtag, targc, targv, inattic);
+}
+
+/*
+ * The RCS file exists; update it by adding the new import file to the
+ * (possibly already existing) vendor branch.
+ */
+static int
+update_rcs_file (char *message, char *vfile, char *vtag, int targc,
+ char **targv, int inattic)
+{
+ Vers_TS *vers;
+ int letter;
+ char *tocvsPath;
+ char *expand;
+ struct file_info finfo;
+
+ memset (&finfo, 0, sizeof finfo);
+ finfo.file = vfile;
+ /* Not used, so don't worry about it. */
+ finfo.update_dir = NULL;
+ finfo.fullname = finfo.file;
+ finfo.repository = repository;
+ finfo.entries = NULL;
+ finfo.rcs = NULL;
+ vers = Version_TS (&finfo, NULL, vbranch, NULL, 1, 0);
+ if (vers->vn_rcs != NULL
+ && !RCS_isdead (vers->srcfile, vers->vn_rcs))
+ {
+ int different;
+
+ /*
+ * The rcs file does have a revision on the vendor branch. Compare
+ * this revision with the import file; if they match exactly, there
+ * is no need to install the new import file as a new revision to the
+ * branch. Just tag the revision with the new import tags.
+ *
+ * This is to try to cut down the number of "C" conflict messages for
+ * locally modified import source files.
+ */
+ tocvsPath = wrap_tocvs_process_file (vfile);
+ /* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is
+ not NULL? */
+ expand = (vers->srcfile->expand != NULL
+ && vers->srcfile->expand[0] == 'b') ? "-kb" : "-ko";
+ different = RCS_cmp_file (vers->srcfile, vers->vn_rcs, NULL,
+ NULL, expand, vfile);
+ if (tocvsPath)
+ if (unlink_file_dir (tocvsPath) < 0)
+ error (0, errno, "cannot remove %s", tocvsPath);
+
+ if (!different)
+ {
+ int retval = 0;
+
+ /*
+ * The two files are identical. Just update the tags, print the
+ * "U", signifying that the file has changed, but needs no
+ * attention, and we're done.
+ */
+ if (add_tags (vers->srcfile, vfile, vtag, targc, targv))
+ retval = 1;
+ add_log ('U', vfile);
+ freevers_ts (&vers);
+ return retval;
+ }
+ }
+
+ /* We may have failed to parse the RCS file; check just in case */
+ if (vers->srcfile == NULL ||
+ add_rev (message, vers->srcfile, vfile, vers->vn_rcs) ||
+ add_tags (vers->srcfile, vfile, vtag, targc, targv))
+ {
+ freevers_ts (&vers);
+ return 1;
+ }
+
+ if (vers->srcfile->branch == NULL || inattic ||
+ strcmp (vers->srcfile->branch, vbranch) != 0)
+ {
+ conflicts++;
+ letter = 'C';
+ }
+ else
+ letter = 'U';
+ add_log (letter, vfile);
+
+ freevers_ts (&vers);
+ return 0;
+}
+
+/*
+ * Add the revision to the vendor branch
+ */
+static int
+add_rev (char *message, RCSNode *rcs, char *vfile, char *vers)
+{
+ int locked, status, ierrno;
+ char *tocvsPath;
+
+ if (noexec)
+ return 0;
+
+ locked = 0;
+ if (vers != NULL)
+ {
+ /* Before RCS_lock existed, we were directing stdout, as well as
+ stderr, from the RCS command, to DEVNULL. I wouldn't guess that
+ was necessary, but I don't know for sure. */
+ /* Earlier versions of this function printed a `fork failed' error
+ when RCS_lock returned an error code. That's not appropriate
+ now that RCS_lock is librarified, but should the error text be
+ preserved? */
+ if (RCS_lock (rcs, vbranch, 1) != 0)
+ return 1;
+ locked = 1;
+ RCS_rewrite (rcs, NULL, NULL);
+ }
+ tocvsPath = wrap_tocvs_process_file (vfile);
+
+ status = RCS_checkin (rcs, NULL, tocvsPath == NULL ? vfile : tocvsPath,
+ message, vbranch, 0,
+ (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE
+ | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)));
+ ierrno = errno;
+
+ if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0))
+ error (0, errno, "cannot remove %s", tocvsPath);
+
+ if (status)
+ {
+ if (!noexec)
+ {
+ fperrmsg (logfp, 0, status == -1 ? ierrno : 0,
+ "ERROR: Check-in of %s failed", rcs->path);
+ error (0, status == -1 ? ierrno : 0,
+ "ERROR: Check-in of %s failed", rcs->path);
+ }
+ if (locked)
+ {
+ (void) RCS_unlock (rcs, vbranch, 0);
+ RCS_rewrite (rcs, NULL, NULL);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Add the vendor branch tag and all the specified import release tags to the
+ * RCS file. The vendor branch tag goes on the branch root (1.1.1) while the
+ * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
+ * 1.1.1.2, ...).
+ */
+static int
+add_tags (RCSNode *rcs, char *vfile, char *vtag, int targc, char **targv)
+{
+ int i, ierrno;
+ Vers_TS *vers;
+ int retcode = 0;
+ struct file_info finfo;
+
+ if (noexec)
+ return 0;
+
+ if ((retcode = RCS_settag (rcs, vtag, vbranch)) != 0)
+ {
+ ierrno = errno;
+ fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
+ error (0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs->path);
+ return 1;
+ }
+ RCS_rewrite (rcs, NULL, NULL);
+
+ memset (&finfo, 0, sizeof finfo);
+ finfo.file = vfile;
+ /* Not used, so don't worry about it. */
+ finfo.update_dir = NULL;
+ finfo.fullname = finfo.file;
+ finfo.repository = repository;
+ finfo.entries = NULL;
+ finfo.rcs = NULL;
+ vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0);
+ for (i = 0; i < targc; i++)
+ {
+ if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) == 0)
+ RCS_rewrite (rcs, NULL, NULL);
+ else
+ {
+ ierrno = errno;
+ fperrmsg (logfp, 0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i],
+ rcs->path);
+ error (0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i],
+ rcs->path);
+ }
+ }
+ freevers_ts (&vers);
+ return 0;
+}
+
+/*
+ * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
+ */
+struct compair
+{
+ char *suffix, *comlead;
+};
+
+static const struct compair comtable[] =
+{
+
+/*
+ * comtable pairs each filename suffix with a comment leader. The comment
+ * leader is placed before each line generated by the $Log keyword. This
+ * table is used to guess the proper comment leader from the working file's
+ * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
+ * languages without multiline comments; for others they are optional.
+ *
+ * I believe that the comment leader is unused if you are using RCS 5.7, which
+ * decides what leader to use based on the text surrounding the $Log keyword
+ * rather than a specified comment leader.
+ */
+ {"a", "-- "}, /* Ada */
+ {"ada", "-- "},
+ {"adb", "-- "},
+ {"asm", ";; "}, /* assembler (MS-DOS) */
+ {"ads", "-- "}, /* Ada */
+ {"bas", "' "}, /* Visual Basic code */
+ {"bat", ":: "}, /* batch (MS-DOS) */
+ {"body", "-- "}, /* Ada */
+ {"c", " * "}, /* C */
+ {"c++", "// "}, /* C++ in all its infinite guises */
+ {"cc", "// "},
+ {"cpp", "// "},
+ {"cxx", "// "},
+ {"m", "// "}, /* Objective-C */
+ {"cl", ";;; "}, /* Common Lisp */
+ {"cmd", ":: "}, /* command (OS/2) */
+ {"cmf", "c "}, /* CM Fortran */
+ {"cs", " * "}, /* C* */
+ {"csh", "# "}, /* shell */
+ {"dlg", " * "}, /* MS Windows dialog file */
+ {"e", "# "}, /* efl */
+ {"epsf", "% "}, /* encapsulated postscript */
+ {"epsi", "% "}, /* encapsulated postscript */
+ {"el", "; "}, /* Emacs Lisp */
+ {"f", "c "}, /* Fortran */
+ {"for", "c "},
+ {"frm", "' "}, /* Visual Basic form */
+ {"h", " * "}, /* C-header */
+ {"hh", "// "}, /* C++ header */
+ {"hpp", "// "},
+ {"hxx", "// "},
+ {"in", "# "}, /* for Makefile.in */
+ {"l", " * "}, /* lex (conflict between lex and
+ * franzlisp) */
+ {"mac", ";; "}, /* macro (DEC-10, MS-DOS, PDP-11,
+ * VMS, etc) */
+ {"mak", "# "}, /* makefile, e.g. Visual C++ */
+ {"me", ".\\\" "}, /* me-macros t/nroff */
+ {"ml", "; "}, /* mocklisp */
+ {"mm", ".\\\" "}, /* mm-macros t/nroff */
+ {"ms", ".\\\" "}, /* ms-macros t/nroff */
+ {"man", ".\\\" "}, /* man-macros t/nroff */
+ {"1", ".\\\" "}, /* feeble attempt at man pages... */
+ {"2", ".\\\" "},
+ {"3", ".\\\" "},
+ {"4", ".\\\" "},
+ {"5", ".\\\" "},
+ {"6", ".\\\" "},
+ {"7", ".\\\" "},
+ {"8", ".\\\" "},
+ {"9", ".\\\" "},
+ {"p", " * "}, /* pascal */
+ {"pas", " * "},
+ {"pl", "# "}, /* perl (conflict with Prolog) */
+ {"ps", "% "}, /* postscript */
+ {"psw", "% "}, /* postscript wrap */
+ {"pswm", "% "}, /* postscript wrap */
+ {"r", "# "}, /* ratfor */
+ {"rc", " * "}, /* Microsoft Windows resource file */
+ {"red", "% "}, /* psl/rlisp */
+#ifdef sparc
+ {"s", "! "}, /* assembler */
+#endif
+#ifdef mc68000
+ {"s", "| "}, /* assembler */
+#endif
+#ifdef pdp11
+ {"s", "/ "}, /* assembler */
+#endif
+#ifdef vax
+ {"s", "# "}, /* assembler */
+#endif
+#ifdef __ksr__
+ {"s", "# "}, /* assembler */
+ {"S", "# "}, /* Macro assembler */
+#endif
+ {"sh", "# "}, /* shell */
+ {"sl", "% "}, /* psl */
+ {"spec", "-- "}, /* Ada */
+ {"tex", "% "}, /* tex */
+ {"y", " * "}, /* yacc */
+ {"ye", " * "}, /* yacc-efl */
+ {"yr", " * "}, /* yacc-ratfor */
+ {"", "# "}, /* default for empty suffix
*/
+ {NULL, "# "} /* default for unknown suffix; */
+/* must always be last */
+};
+
+
+
+static char *
+get_comment (const char *user)
+{
+ char *cp, *suffix;
+ char *suffix_path;
+ int i;
+ char *retval;
+
+ suffix_path = xmalloc (strlen (user) + 5);
+ cp = strrchr (user, '.');
+ if (cp != NULL)
+ {
+ cp++;
+
+ /*
+ * Convert to lower-case, since we are not concerned about the
+ * case-ness of the suffix.
+ */
+ (void) strcpy (suffix_path, cp);
+ for (cp = suffix_path; *cp; cp++)
+ if (isupper ((unsigned char) *cp))
+ *cp = tolower (*cp);
+ suffix = suffix_path;
+ }
+ else
+ suffix = ""; /* will use the default */
+ for (i = 0;; i++)
+ {
+ if (comtable[i].suffix == NULL)
+ {
+ /* Default. Note we'll always hit this case before we
+ ever return NULL. */
+ retval = comtable[i].comlead;
+ break;
+ }
+ if (strcmp (suffix, comtable[i].suffix) == 0)
+ {
+ retval = comtable[i].comlead;
+ break;
+ }
+ }
+ free (suffix_path);
+ return retval;
+}
+
+/* Create a new RCS file from scratch.
+ *
+ * This probably should be moved to rcs.c now that it is called from
+ * places outside import.c.
+ *
+ * INPUTS
+ * message Log message for the addition. Not used if add_vhead == NULL.
+ * rcs Filename of the RCS file to create. Note that if 'do_killnew'
+ * is set, this file should be in the Attic directory, and the
+ * Attic directory must already exist.
+ * user Filename of the file to serve as the contents of the initial
+ * revision. Even if add_vhead is NULL, we use this to determine
+ * the modes to give the new RCS file.
+ * add_vhead Revision number of head that we are adding. Normally 1.1 but
+ * could be another revision as long as ADD_VBRANCH is a branch
+ * from it. If NULL, then just add an empty file without any
+ * revisions (similar to the one created by "rcs -i").
+ * key_opt Keyword expansion mode, e.g., "b" for binary. NULL means the
+ * default behavior.
+ * add_vbranch
+ * Vendor branch to import to, or NULL if none. If non-NULL, then
+ * vtag should also be non-NULL.
+ * vtag
+ * targc Number of elements in TARGV.
+ * targv The list of tags to attached to this imported revision.
+ * desctext If non-NULL, description for the file. If NULL, the
+ * description will be empty.
+ * desclen The number of bytes in desctext.
+ * add_logfp Write errors to here as well as via error (), or NULL if we
+ * should use only error ().
+ * do_killnew Mark newly-imported files as being dead on the trunk,
i.e.,
+ * as being imported only to the vendor branch.
+ *
+ * RETURNS
+ * Return value is 0 for success, or nonzero for failure (in which
+ * case an error message will have already been printed).
+ */
+int
+add_rcs_file (const char *message, const char *rcs, const char *user,
+ const char *add_vhead, const char *key_opt,
+ const char *add_vbranch, const char *vtag, int targc,
+ char **targv, const char *desctext, size_t desclen,
+ FILE *add_logfp, bool do_killnew)
+{
+ FILE *fprcs, *fpuser;
+ struct stat sb;
+ struct tm *ftm;
+ time_t now;
+ char altdate1[MAXDATELEN];
+ char *author;
+ int i, ierrno, err = 0;
+ mode_t mode;
+ char *tocvsPath;
+ const char *userfile;
+ char *free_opt = NULL;
+ mode_t file_type;
+ char *dead_revision = NULL;
+
+ if (noexec)
+ return 0;
+
+ if (do_killnew)
+ {
+ char *last_place;
+ int last_number;
+
+ /* If we are marking the newly imported file as dead, we must
+ have a head revision. */
+ if (add_vhead == NULL)
+ error (1, 0, "killing new file attempted when no head revision is
being added");
+
+ /* One extra byte for NUL, plus one for carry generated by adding
+ one to the last number in the add_vhead revision. */
+ dead_revision = xmalloc (strlen (add_vhead) + 2);
+ strcpy (dead_revision, add_vhead);
+
+ /* Find the loacation of the last number, which we will increment
+ and overwrite. Note that this handles single numbers (w/o
+ dots), which is probably unnecessary. */
+ if ((last_place = strrchr (dead_revision, '.')) != NULL)
+ last_place++;
+ else
+ last_place = dead_revision;
+ last_number = atoi (last_place);
+ if (++last_number <= 0)
+ error (1, 0, "invalid revision number %s", add_vhead);
+ sprintf (last_place, "%d", last_number);
+ }
+
+ /* Note that as the code stands now, the -k option overrides any
+ settings in wrappers (whether CVSROOT/cvswrappers, -W, or
+ whatever). Some have suggested this should be the other way
+ around. As far as I know the documentation doesn't say one way
+ or the other. Before making a change of this sort, should think
+ about what is best, document it (in cvs.texinfo and NEWS), &c. */
+
+ if (key_opt == NULL)
+ {
+ if (wrap_name_has (user, WRAP_RCSOPTION))
+ {
+ key_opt = free_opt = wrap_rcsoption (user, 0);
+ }
+ }
+
+ tocvsPath = wrap_tocvs_process_file (user);
+ userfile = (tocvsPath == NULL ? user : tocvsPath);
+
+ /* Opening in text mode is probably never the right thing for the
+ server (because the protocol encodes text files in a fashion
+ which does not depend on what the client or server OS is, as
+ documented in cvsclient.texi), but as long as the server just
+ runs on unix it is a moot point. */
+
+ /* If PreservePermissions is set, then make sure that the file
+ is a plain file before trying to open it. Longstanding (although
+ often unpopular) CVS behavior has been to follow symlinks, so we
+ maintain that behavior if PreservePermissions is not on.
+
+ NOTE: this error message used to be `cannot fstat', but is now
+ `cannot lstat'. I don't see a way around this, since we must
+ stat the file before opening it. -twp */
+
+ if (lstat (userfile, &sb) < 0)
+ {
+ /* not fatal, continue import */
+ if (add_logfp != NULL)
+ fperrmsg (add_logfp, 0, errno,
+ "ERROR: cannot lstat file %s", userfile);
+ error (0, errno, "cannot lstat file %s", userfile);
+ goto read_error;
+ }
+ file_type = sb.st_mode & S_IFMT;
+
+ fpuser = NULL;
+ if (
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ !config->preserve_perms ||
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+ file_type == S_IFREG)
+ {
+ fpuser = CVS_FOPEN (userfile,
+ ((key_opt != NULL && strcmp (key_opt, "b") == 0)
+ ? "rb"
+ : "r")
+ );
+ if (fpuser == NULL)
+ {
+ /* not fatal, continue import */
+ if (add_logfp != NULL)
+ fperrmsg (add_logfp, 0, errno,
+ "ERROR: cannot read file %s", userfile);
+ error (0, errno, "ERROR: cannot read file %s", userfile);
+ goto read_error;
+ }
+ }
+
+ fprcs = CVS_FOPEN (rcs, "w+b");
+ if (fprcs == NULL)
+ {
+ ierrno = errno;
+ goto write_error_noclose;
+ }
+
+ /*
+ * putadmin()
+ */
+ if (add_vhead != NULL)
+ {
+ if (fprintf (fprcs, "head %s;\012",
+ do_killnew ? dead_revision : add_vhead) < 0)
+ goto write_error;
+ }
+ else
+ {
+ if (fprintf (fprcs, "head ;\012") < 0)
+ goto write_error;
+ }
+
+ /* This sets the default branch. If using the 'do_killnew' functionality,
+ where imports don't show up until merged, no default branch should
+ be set. */
+ if (add_vbranch != NULL && ! do_killnew)
+ {
+ if (fprintf (fprcs, "branch %s;\012", add_vbranch) < 0)
+ goto write_error;
+ }
+ if (fprintf (fprcs, "access ;\012") < 0 ||
+ fprintf (fprcs, "symbols ") < 0)
+ {
+ goto write_error;
+ }
+
+ for (i = targc - 1; i >= 0; i--)
+ {
+ /* RCS writes the symbols backwards */
+ assert (add_vbranch != NULL);
+ if (fprintf (fprcs, "%s:%s.1 ", targv[i], add_vbranch) < 0)
+ goto write_error;
+ }
+
+ if (add_vbranch != NULL)
+ {
+ if (fprintf (fprcs, "%s:%s", vtag, add_vbranch) < 0)
+ goto write_error;
+ }
+ if (fprintf (fprcs, ";\012") < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "locks ; strict;\012") < 0 ||
+ /* XXX - make sure @@ processing works in the RCS file */
+ fprintf (fprcs, "comment @%s@;\012", get_comment (user)) < 0)
+ {
+ goto write_error;
+ }
+
+ if (key_opt != NULL && strcmp (key_opt, "kv") != 0)
+ {
+ if (fprintf (fprcs, "expand @%s@;\012", key_opt) < 0)
+ {
+ goto write_error;
+ }
+ }
+
+ if (fprintf (fprcs, "\012") < 0)
+ goto write_error;
+
+ /* Write the revision(s), with the date and author and so on
+ (that is "delta" rather than "deltatext" from rcsfile(5)). */
+
+ if (use_file_modtime)
+ now = sb.st_mtime;
+ else
+ (void) time (&now);
+ ftm = gmtime (&now);
+ (void) sprintf (altdate1, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ author = getcaller ();
+
+ if (do_killnew)
+ {
+ if (fprintf (fprcs, "\012%s\012", dead_revision) < 0 ||
+ fprintf (fprcs, "date %s; author %s; state %s;\012",
+ altdate1, author, RCSDEAD) < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "branches;\012") < 0)
+ goto write_error;
+ if (fprintf (fprcs, "next %s;\012", add_vhead) < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "commitid %s;\012", global_session_id) < 0)
+ goto write_error;
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* Store initial permissions if necessary. */
+ if (config->preserve_perms)
+ {
+ if (preserve_initial_permissions (fprcs, userfile,
+ file_type, sbp))
+ goto write_error;
+ }
+#endif
+ }
+
+ if (add_vhead != NULL)
+ {
+ if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\012",
+ altdate1, author) < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "branches") < 0)
+ goto write_error;
+ if (add_vbranch != NULL)
+ {
+ if (fprintf (fprcs, " %s.1", add_vbranch) < 0)
+ goto write_error;
+ }
+ if (fprintf (fprcs, ";\012") < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "next ;\012") < 0)
+ goto write_error;
+
+ if (fprintf (fprcs, "commitid %s;\012", global_session_id) < 0)
+ goto write_error;
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* Store initial permissions if necessary. */
+ if (config->preserve_perms)
+ {
+ if (preserve_initial_permissions (fprcs, userfile,
+ file_type, sbp))
+ goto write_error;
+ }
+#endif
+
+ if (add_vbranch != NULL)
+ {
+ if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\012",
+ altdate1, author) < 0 ||
+ fprintf (fprcs, "branches ;\012") < 0 ||
+ fprintf (fprcs, "next ;\012") < 0 ||
+ fprintf (fprcs, "commitid %s;\012", global_session_id) <
0)
+ goto write_error;
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* Store initial permissions if necessary. */
+ if (config->preserve_perms)
+ {
+ if (preserve_initial_permissions (fprcs, userfile,
+ file_type, sbp))
+ goto write_error;
+ }
+#endif
+
+ if (fprintf (fprcs, "\012") < 0)
+ goto write_error;
+ }
+ }
+
+ /* Now write the description (possibly empty). */
+ if (fprintf (fprcs, "\012desc\012") < 0 ||
+ fprintf (fprcs, "@") < 0)
+ goto write_error;
+ if (desctext != NULL)
+ {
+ /* The use of off_t not size_t for the second argument is very
+ strange, since we are dealing with something which definitely
+ fits in memory. */
+ if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0)
+ goto write_error;
+ }
+ if (fprintf (fprcs, "@\012\012\012") < 0)
+ goto write_error;
+
+ /* Now write the log messages and contents for the revision(s) (that
+ is, "deltatext" rather than "delta" from rcsfile(5)). */
+
+ if (do_killnew)
+ {
+ if (fprintf (fprcs, "\012%s\012", dead_revision) < 0 ||
+ fprintf (fprcs, "log\012@") < 0)
+ goto write_error;
+ if (fprintf (fprcs, "Revision %s was added on the vendor branch.\012",
+ add_vhead) < 0)
+ goto write_error;
+ if (fprintf (fprcs, "@\012") < 0 ||
+ fprintf (fprcs, "text\012@") < 0)
+ {
+ goto write_error;
+ }
+
+ /* Now copy over the contents of the file, expanding at signs. */
+ if (expand_and_copy_contents (fprcs, file_type, user, fpuser))
+ goto write_error;
+
+ if (fprintf (fprcs, "@\012\012") < 0)
+ goto write_error;
+ }
+
+ if (add_vhead != NULL)
+ {
+ if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
+ fprintf (fprcs, "log\012@") < 0)
+ goto write_error;
+ if (add_vbranch != NULL)
+ {
+ /* We are going to put the log message in the revision on the
+ branch. So putting it here too seems kind of redundant, I
+ guess (and that is what CVS has always done, anyway). */
+ if (fprintf (fprcs, "Initial revision\012") < 0)
+ goto write_error;
+ }
+ else
+ {
+ if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0)
+ goto write_error;
+ }
+ if (fprintf (fprcs, "@\012") < 0 ||
+ fprintf (fprcs, "text\012@") < 0)
+ {
+ goto write_error;
+ }
+
+ /* Now copy over the contents of the file, expanding at signs.
+ * If config->preserve_perms is set, do this only for regular files.
+ */
+ if (!do_killnew)
+ {
+ /* Now copy over the contents of the file, expanding at signs,
+ if not done as part of do_killnew handling above. */
+ if (expand_and_copy_contents (fprcs, file_type, user, fpuser))
+ goto write_error;
+ }
+
+ if (fprintf (fprcs, "@\012\012") < 0)
+ goto write_error;
+
+ if (add_vbranch != NULL)
+ {
+ if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
+ fprintf (fprcs, "log\012@") < 0 ||
+ expand_at_signs (message,
+ (off_t) strlen (message), fprcs) < 0 ||
+ fprintf (fprcs, "@\012text\012") < 0 ||
+ fprintf (fprcs, "@@\012") < 0)
+ goto write_error;
+ }
+ }
+
+ if (fclose (fprcs) == EOF)
+ {
+ ierrno = errno;
+ goto write_error_noclose;
+ }
+ /* Close fpuser only if we opened it to begin with. */
+ if (fpuser != NULL)
+ {
+ if (fclose (fpuser) < 0)
+ error (0, errno, "cannot close %s", user);
+ }
+
+ /*
+ * Fix the modes on the RCS files. The user modes of the original
+ * user file are propagated to the group and other modes as allowed
+ * by the repository umask, except that all write permissions are
+ * turned off.
+ */
+ mode = (sb.st_mode |
+ (sb.st_mode & S_IRWXU) >> 3 |
+ (sb.st_mode & S_IRWXU) >> 6) &
+ ~cvsumask &
+ ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ if (chmod (rcs, mode) < 0)
+ {
+ ierrno = errno;
+ if (add_logfp != NULL)
+ fperrmsg (add_logfp, 0, ierrno,
+ "WARNING: cannot change mode of file %s", rcs);
+ error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
+ err++;
+ }
+ if (tocvsPath)
+ if (unlink_file_dir (tocvsPath) < 0)
+ error (0, errno, "cannot remove %s", tocvsPath);
+ if (free_opt != NULL)
+ free (free_opt);
+ return err;
+
+write_error:
+ ierrno = errno;
+ if (fclose (fprcs) < 0)
+ error (0, errno, "cannot close %s", rcs);
+write_error_noclose:
+ if (fclose (fpuser) < 0)
+ error (0, errno, "cannot close %s", user);
+ if (add_logfp != NULL)
+ fperrmsg (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
+ error (0, ierrno, "ERROR: cannot write file %s", rcs);
+ if (ierrno == ENOSPC)
+ {
+ if (CVS_UNLINK (rcs) < 0)
+ error (0, errno, "cannot remove %s", rcs);
+ if (add_logfp != NULL)
+ fperrmsg (add_logfp, 0, 0, "ERROR: out of space - aborting");
+ error (1, 0, "ERROR: out of space - aborting");
+ }
+read_error:
+ if (tocvsPath)
+ if (unlink_file_dir (tocvsPath) < 0)
+ error (0, errno, "cannot remove %s", tocvsPath);
+
+ if (free_opt != NULL)
+ free (free_opt);
+
+ return err + 1;
+}
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+/* Write file permissions and symlink information for a file being
+ * added into its RCS file.
+ *
+ * INPUTS
+ * fprcs FILE pointer for the (newly-created) RCS file. Permisisons
+ * and symlink information should be written here.
+ * userfile Filename of the file being added. (Used to read symbolic
+ * link contents, for symlinks.)
+ * file_type File type of userfile, extracted from sbp->st_mode.
+ * sbp 'stat' information for userfile.
+ *
+ * RETURNS
+ * Return value is 0 for success, or nonzero for failure (in which case
+ * no error message has yet been printed).
+ */
+static int
+preserve_initial_permissions (fprcs, userfile, file_type, sbp)
+ FILE *fprcs;
+ const char *userfile;
+ mode_t file_type;
+ struct stat *sbp;
+{
+ if (file_type == S_IFLNK)
+ {
+ char *link = Xreadlink (userfile, sbp->st_size);
+ 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", sbp->st_uid) < 0)
+ goto write_error;
+ if (fprintf (fprcs, "group\t%u;\012", sbp->st_gid) < 0)
+ goto write_error;
+ if (fprintf (fprcs, "permissions\t%o;\012",
+ sbp->st_mode & 07777) < 0)
+ goto write_error;
+ switch (file_type)
+ {
+ case S_IFREG: break;
+ case S_IFCHR:
+ case S_IFBLK:
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ if (fprintf (fprcs, "special\t%s %lu;\012",
+ (file_type == S_IFCHR
+ ? "character"
+ : "block"),
+ (unsigned long) sbp->st_rdev) < 0)
+ goto write_error;
+#else
+ error (0, 0,
+"can't import %s: unable to import device files on this system",
+userfile);
+#endif
+ break;
+ default:
+ error (0, 0,
+ "can't import %s: unknown kind of special file",
+ userfile);
+ }
+ }
+ return 0;
+
+write_error:
+ return 1;
+}
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+
+/* Copy file contents into an RCS file, expanding at signs.
+ *
+ * If config->preserve_perms is set, nothing is copied if the source is not
+ * a regular file.
+ *
+ * INPUTS
+ * fprcs FILE pointer for the (newly-created) RCS file. The expanded
+ * contents should be written here.
+ * file_type File type of the data source. No data is copied if
+ * preserve_permissions is set and the source is not a
+ * regular file.
+ * user Filename of the data source (used to print error messages).
+ * fpuser FILE pointer for the data source, whose data is being
+ * copied into the RCS file.
+ *
+ * RETURNS
+ * Return value is 0 for success, or nonzero for failure (in which case
+ * no error message has yet been printed).
+ */
+static int
+expand_and_copy_contents (fprcs, file_type, user, fpuser)
+ FILE *fprcs, *fpuser;
+ mode_t file_type;
+ const char *user;
+{
+ if (
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ !config->preserve_perms ||
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+ file_type == S_IFREG)
+ {
+ char buf[8192];
+ unsigned int len;
+
+ while (1)
+ {
+ len = fread (buf, 1, sizeof buf, fpuser);
+ if (len == 0)
+ {
+ if (ferror (fpuser))
+ error (1, errno, "cannot read file %s for copying",
+ user);
+ break;
+ }
+ if (expand_at_signs (buf, len, fprcs) < 0)
+ goto write_error;
+ }
+ }
+ return 0;
+
+write_error:
+ return 1;
+}
+
+/*
+ * Write SIZE bytes at BUF to FP, expanding @ signs into double @
+ * signs. If an error occurs, return a negative value and set errno
+ * to indicate the error. If not, return a nonnegative value.
+ */
+int
+expand_at_signs (const char *buf, size_t size, FILE *fp)
+{
+ register const char *cp, *next;
+
+ cp = buf;
+ while ((next = memchr (cp, '@', size)) != NULL)
+ {
+ size_t len = ++next - cp;
+ if (fwrite (cp, 1, len, fp) != len)
+ return EOF;
+ if (putc ('@', fp) == EOF)
+ return EOF;
+ cp = next;
+ size -= len;
+ }
+
+ if (fwrite (cp, 1, size, fp) != size)
+ return EOF;
+
+ return 1;
+}
+
+/*
+ * Write an update message to (potentially) the screen and the log file.
+ */
+static void
+add_log (int ch, char *fname)
+{
+ if (!really_quiet) /* write to terminal */
+ {
+ char buf[2];
+ buf[0] = ch;
+ buf[1] = ' ';
+ cvs_output (buf, 2);
+ if (repos_len)
+ {
+ cvs_output (repository + repos_len + 1, 0);
+ cvs_output ("/", 1);
+ }
+ else if (repository[0] != '\0')
+ {
+ cvs_output (repository, 0);
+ cvs_output ("/", 1);
+ }
+ cvs_output (fname, 0);
+ cvs_output ("\n", 1);
+ }
+
+ if (repos_len) /* write to logfile */
+ (void) fprintf (logfp, "%c %s/%s\n", ch,
+ repository + repos_len + 1, fname);
+ else if (repository[0])
+ (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
+ else
+ (void) fprintf (logfp, "%c %s\n", ch, fname);
+}
+
+/*
+ * This is the recursive function that walks the argument directory looking
+ * for sub-directories that have CVS administration files in them and updates
+ * them recursively.
+ *
+ * Note that we do not follow symbolic links here, which is a feature!
+ */
+static int
+import_descend_dir (char *message, char *dir, char *vtag, int targc,
+ char **targv)
+{
+ struct saved_cwd cwd;
+ char *cp;
+ int ierrno, err;
+ char *rcs = NULL;
+
+ if (islink (dir))
+ return 0;
+ if (save_cwd (&cwd))
+ {
+ fperrmsg (logfp, 0, errno, "Failed to save current directory.");
+ return 1;
+ }
+
+ /* Concatenate DIR to the end of REPOSITORY. */
+ if (repository[0] == '\0')
+ {
+ char *new = xstrdup (dir);
+ free (repository);
+ repository = new;
+ }
+ else
+ {
+ char *new = Xasprintf ("%s/%s", repository, dir);
+ free (repository);
+ repository = new;
+ }
+
+ if (!quiet && !current_parsed_root->isremote)
+ error (0, 0, "Importing %s", repository);
+
+ if (CVS_CHDIR (dir) < 0)
+ {
+ ierrno = errno;
+ fperrmsg (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
+ error (0, ierrno, "ERROR: cannot chdir to %s", repository);
+ err = 1;
+ goto out;
+ }
+ if (!current_parsed_root->isremote && !isdir (repository))
+ {
+ rcs = Xasprintf ("%s%s", repository, RCSEXT);
+ if (isfile (repository) || isfile (rcs))
+ {
+ fperrmsg (logfp, 0, 0,
+ "ERROR: %s is a file, should be a directory!",
+ repository);
+ error (0, 0, "ERROR: %s is a file, should be a directory!",
+ repository);
+ err = 1;
+ goto out;
+ }
+ if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0)
+ {
+ ierrno = errno;
+ fperrmsg (logfp, 0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ error (0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ err = 1;
+ goto out;
+ }
+ }
+ err = import_descend (message, vtag, targc, targv);
+ out:
+ if (rcs != NULL)
+ free (rcs);
+ if ((cp = strrchr (repository, '/')) != NULL)
+ *cp = '\0';
+ else
+ repository[0] = '\0';
+ if (restore_cwd (&cwd))
+ error (1, errno, "Failed to restore current directory, `%s'.",
+ cwd.name);
+ free_cwd (&cwd);
+ return err;
+}
Index: ccvs/src/log.c
diff -u /dev/null ccvs/src/log.c:1.103.8.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/log.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,1808 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Print Log Information
+ *
+ * Prints the RCS "log" (rlog) information for the specified files. With no
+ * argument, prints the log information for all the files in the directory
+ * (recursive by default).
+ */
+
+#include "cvs.h"
+#include <assert.h>
+
+/* This structure holds information parsed from the -r option. */
+
+struct option_revlist
+{
+ /* The next -r option. */
+ struct option_revlist *next;
+ /* The first revision to print. This is NULL if the range is
+ :rev, or if no revision is given. */
+ char *first;
+ /* The last revision to print. This is NULL if the range is rev:,
+ or if no revision is given. If there is no colon, first and
+ last are the same. */
+ char *last;
+ /* Nonzero if there was a trailing `.', which means to print only
+ the head revision of a branch. */
+ int branchhead;
+ /* Nonzero if first and last are inclusive. */
+ int inclusive;
+};
+
+/* This structure holds information derived from option_revlist given
+ a particular RCS file. */
+
+struct revlist
+{
+ /* The next pair. */
+ struct revlist *next;
+ /* The first numeric revision to print. */
+ char *first;
+ /* The last numeric revision to print. */
+ char *last;
+ /* The number of fields in these revisions (one more than
+ numdots). */
+ int fields;
+ /* Whether first & last are to be included or excluded. */
+ int inclusive;
+};
+
+/* This structure holds information parsed from the -d option. */
+
+struct datelist
+{
+ /* The next date. */
+ struct datelist *next;
+ /* The starting date. */
+ char *start;
+ /* The ending date. */
+ char *end;
+ /* Nonzero if the range is inclusive rather than exclusive. */
+ int inclusive;
+};
+
+/* This structure is used to pass information through start_recursion. */
+struct log_data
+{
+ /* Nonzero if the -R option was given, meaning that only the name
+ of the RCS file should be printed. */
+ int nameonly;
+ /* Nonzero if the -h option was given, meaning that only header
+ information should be printed. */
+ int header;
+ /* Nonzero if the -t option was given, meaning that only the
+ header and the descriptive text should be printed. */
+ int long_header;
+ /* Nonzero if the -N option was seen, meaning that tag information
+ should not be printed. */
+ int notags;
+ /* Nonzero if the -b option was seen, meaning that only revisions
+ on the default branch should be printed. */
+ int default_branch;
+ /* Nonzero if the -S option was seen, meaning that the header/name
+ should be suppressed if no revisions are selected. */
+ int sup_header;
+ /* If not NULL, the value given for the -r option, which lists
+ sets of revisions to be printed. */
+ struct option_revlist *revlist;
+ /* If not NULL, the date pairs given for the -d option, which
+ select date ranges to print. */
+ struct datelist *datelist;
+ /* If not NULL, the single dates given for the -d option, which
+ select specific revisions to print based on a date. */
+ struct datelist *singledatelist;
+ /* If not NULL, the list of states given for the -s option, which
+ only prints revisions of given states. */
+ List *statelist;
+ /* If not NULL, the list of login names given for the -w option,
+ which only prints revisions checked in by given users. */
+ List *authorlist;
+};
+
+/* This structure is used to pass information through walklist. */
+struct log_data_and_rcs
+{
+ struct log_data *log_data;
+ struct revlist *revlist;
+ RCSNode *rcs;
+};
+
+static int rlog_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+static Dtype log_dirproc (void *callerdat, const char *dir,
+ const char *repository, const char *update_dir,
+ List *entries);
+static int log_fileproc (void *callerdat, struct file_info *finfo);
+static struct option_revlist *log_parse_revlist (const char *);
+static void log_parse_date (struct log_data *, const char *);
+static void log_parse_list (List **, const char *);
+static struct revlist *log_expand_revlist (struct file_info *, char *,
+ struct option_revlist *, int);
+static void log_free_revlist (struct revlist *);
+static int log_version_requested (struct log_data *, struct revlist *,
+ RCSNode *, RCSVers *);
+static int log_symbol (Node *, void *);
+static int log_count (Node *, void *);
+static int log_fix_singledate (Node *, void *);
+static int log_count_print (Node *, void *);
+static void log_tree (struct log_data *, struct revlist *,
+ RCSNode *, const char *);
+static void log_abranch (struct log_data *, struct revlist *,
+ RCSNode *, const char *);
+static void log_version (struct log_data *, struct revlist *,
+ RCSNode *, RCSVers *, int);
+static int log_branch (Node *, void *);
+static int version_compare (const char *, const char *, int);
+
+static struct log_data log_data;
+static int is_rlog;
+
+static const char *const log_usage[] =
+{
+ "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n",
+ " [-w[logins]] [files...]\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ "\t-b\tOnly list revisions on the default branch.\n",
+ "\t-h\tOnly print header.\n",
+ "\t-R\tOnly print name of RCS file.\n",
+ "\t-t\tOnly print header and descriptive text.\n",
+ "\t-N\tDo not list tags.\n",
+ "\t-S\tDo not print name/header if no revisions selected. -d, -r,\n",
+ "\t\t-s, & -w have little effect in conjunction with -b, -h, -R, and\n",
+ "\t\t-t without this option.\n",
+ "\t-r[revisions]\tA comma-separated list of revisions to print:\n",
+ "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
+ "\t rev1::rev2 Between rev1 and rev2, excluding rev1.\n",
+ "\t rev: rev and following revisions on the same branch.\n",
+ "\t rev:: After rev on the same branch.\n",
+ "\t :rev rev and previous revisions on the same branch.\n",
+ "\t ::rev rev and previous revisions on the same branch.\n",
+ "\t rev Just rev.\n",
+ "\t branch All revisions on the branch.\n",
+ "\t branch. The last revision on the branch.\n",
+ "\t-d dates\tA semicolon-separated list of dates\n",
+ "\t \t(D1<D2 for range, D for latest before).\n",
+ "\t-s states\tOnly list revisions with specified states.\n",
+ "\t-w[logins]\tOnly list revisions checked in by specified logins.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+#ifdef CLIENT_SUPPORT
+
+
+
+/* Helper function for send_arg_list. */
+static int
+send_one (Node *node, void *closure)
+{
+ char *option = closure;
+
+ send_to_server ("Argument ", 0);
+ send_to_server (option, 0);
+ if (strcmp (node->key, "@@MYSELF") == 0)
+ /* It is a bare -w option. Note that we must send it as
+ -w rather than messing with getcaller() or something (which on
+ the client will return garbage). */
+ ;
+ else
+ send_to_server (node->key, 0);
+ send_to_server ("\012", 0);
+ return 0;
+}
+
+
+
+/* For each element in ARG, send an argument consisting of OPTION
+ concatenated with that element. */
+static void
+send_arg_list (char *option, List *arg)
+{
+ if (arg == NULL)
+ return;
+ walklist (arg, send_one, option);
+}
+
+#endif
+
+
+
+int
+cvslog (int argc, char **argv)
+{
+ int c;
+ int err = 0;
+ int local = 0;
+ struct option_revlist **prl;
+
+ is_rlog = (strcmp (cvs_cmd_name, "rlog") == 0);
+
+ if (argc == -1)
+ usage (log_usage);
+
+ memset (&log_data, 0, sizeof log_data);
+ prl = &log_data.revlist;
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::")) != -1)
+ {
+ switch (c)
+ {
+ case 'b':
+ log_data.default_branch = 1;
+ break;
+ case 'd':
+ log_parse_date (&log_data, optarg);
+ break;
+ case 'h':
+ log_data.header = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'N':
+ log_data.notags = 1;
+ break;
+ case 'S':
+ log_data.sup_header = 1;
+ break;
+ case 'R':
+ log_data.nameonly = 1;
+ break;
+ case 'r':
+ *prl = log_parse_revlist (optarg);
+ prl = &(*prl)->next;
+ break;
+ case 's':
+ log_parse_list (&log_data.statelist, optarg);
+ break;
+ case 't':
+ log_data.long_header = 1;
+ break;
+ case 'w':
+ if (optarg != NULL)
+ log_parse_list (&log_data.authorlist, optarg);
+ else
+ log_parse_list (&log_data.authorlist, "@@MYSELF");
+ break;
+ case '?':
+ default:
+ usage (log_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ wrap_setup ();
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ struct datelist *p;
+ struct option_revlist *rp;
+ char datetmp[MAXDATELEN];
+
+ /* We're the local client. Fire up the remote server. */
+ start_server ();
+
+ if (is_rlog && !supported_request ("rlog"))
+ error (1, 0, "server does not support rlog");
+
+ ign_setup ();
+
+ if (log_data.default_branch)
+ send_arg ("-b");
+
+ while (log_data.datelist != NULL)
+ {
+ p = log_data.datelist;
+ log_data.datelist = p->next;
+ send_to_server ("Argument -d\012", 0);
+ send_to_server ("Argument ", 0);
+ date_to_internet (datetmp, p->start);
+ send_to_server (datetmp, 0);
+ if (p->inclusive)
+ send_to_server ("<=", 0);
+ else
+ send_to_server ("<", 0);
+ date_to_internet (datetmp, p->end);
+ send_to_server (datetmp, 0);
+ send_to_server ("\012", 0);
+ if (p->start)
+ free (p->start);
+ if (p->end)
+ free (p->end);
+ free (p);
+ }
+ while (log_data.singledatelist != NULL)
+ {
+ p = log_data.singledatelist;
+ log_data.singledatelist = p->next;
+ send_to_server ("Argument -d\012", 0);
+ send_to_server ("Argument ", 0);
+ date_to_internet (datetmp, p->end);
+ send_to_server (datetmp, 0);
+ send_to_server ("\012", 0);
+ if (p->end)
+ free (p->end);
+ free (p);
+ }
+
+ if (log_data.header)
+ send_arg ("-h");
+ if (local)
+ send_arg("-l");
+ if (log_data.notags)
+ send_arg("-N");
+ if (log_data.sup_header)
+ send_arg("-S");
+ if (log_data.nameonly)
+ send_arg("-R");
+ if (log_data.long_header)
+ send_arg("-t");
+
+ while (log_data.revlist != NULL)
+ {
+ rp = log_data.revlist;
+ log_data.revlist = rp->next;
+ send_to_server ("Argument -r", 0);
+ if (rp->branchhead)
+ {
+ if (rp->first != NULL)
+ send_to_server (rp->first, 0);
+ send_to_server (".", 1);
+ }
+ else
+ {
+ if (rp->first != NULL)
+ send_to_server (rp->first, 0);
+ send_to_server (":", 1);
+ if (!rp->inclusive)
+ send_to_server (":", 1);
+ if (rp->last != NULL)
+ send_to_server (rp->last, 0);
+ }
+ send_to_server ("\012", 0);
+ if (rp->first)
+ free (rp->first);
+ if (rp->last)
+ free (rp->last);
+ free (rp);
+ }
+ send_arg_list ("-s", log_data.statelist);
+ dellist (&log_data.statelist);
+ send_arg_list ("-w", log_data.authorlist);
+ dellist (&log_data.authorlist);
+ send_arg ("--");
+
+ if (is_rlog)
+ {
+ int i;
+ for (i = 0; i < argc; i++)
+ send_arg (argv[i]);
+ send_to_server ("rlog\012", 0);
+ }
+ else
+ {
+ send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("log\012", 0);
+ }
+ err = get_responses_and_close ();
+ return err;
+ }
+#endif
+
+ /* OK, now that we know we are local/server, we can resolve @@MYSELF
+ into our user name. */
+ if (findnode (log_data.authorlist, "@@MYSELF") != NULL)
+ log_parse_list (&log_data.authorlist, getcaller ());
+
+ if (is_rlog)
+ {
+ DBM *db;
+ int i;
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ {
+ err += do_module (db, argv[i], MISC, "Logging", rlog_proc,
+ NULL, 0, local, 0, 0, NULL);
+ }
+ close_module (db);
+ }
+ else
+ {
+ err = rlog_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
+ NULL);
+ }
+
+ while (log_data.revlist)
+ {
+ struct option_revlist *rl = log_data.revlist->next;
+ if (log_data.revlist->first)
+ free (log_data.revlist->first);
+ if (log_data.revlist->last)
+ free (log_data.revlist->last);
+ free (log_data.revlist);
+ log_data.revlist = rl;
+ }
+ while (log_data.datelist)
+ {
+ struct datelist *nd = log_data.datelist->next;
+ if (log_data.datelist->start)
+ free (log_data.datelist->start);
+ if (log_data.datelist->end)
+ free (log_data.datelist->end);
+ free (log_data.datelist);
+ log_data.datelist = nd;
+ }
+ while (log_data.singledatelist)
+ {
+ struct datelist *nd = log_data.singledatelist->next;
+ if (log_data.singledatelist->start)
+ free (log_data.singledatelist->start);
+ if (log_data.singledatelist->end)
+ free (log_data.singledatelist->end);
+ free (log_data.singledatelist);
+ log_data.singledatelist = nd;
+ }
+ dellist (&log_data.statelist);
+ dellist (&log_data.authorlist);
+
+ return err;
+}
+
+
+
+static int
+rlog_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
+ int shorten, int local, char *mname, char *msg)
+{
+ /* Begin section which is identical to patch_proc--should this
+ be abstracted out somehow? */
+ char *myargv[2];
+ int err = 0;
+ int which;
+ char *repository = NULL;
+ char *where;
+
+ if (is_rlog)
+ {
+ repository = xmalloc (strlen (current_parsed_root->directory)
+ + strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
+ (void)sprintf (repository, "%s/%s",
+ current_parsed_root->directory, argv[0]);
+ where = xmalloc (strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1)
+ + 1);
+ (void)strcpy (where, argv[0]);
+
+ /* If mfile isn't null, we need to set up to do only part of theu
+ * module.
+ */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char *path;
+
+ /* If the portion of the module is a path, put the dir part on
+ * repos.
+ */
+ if ((cp = strrchr (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void)strcat (repository, "/");
+ (void)strcat (repository, mfile);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ path = Xasprintf ("%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void)strcpy (repository, path);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ }
+ else
+ {
+ myargv[0] = argv[0];
+ myargv[1] = mfile;
+ argc = 2;
+ argv = myargv;
+ }
+ free (path);
+ }
+
+ /* cd to the starting repository */
+ if (CVS_CHDIR (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (repository);
+ free (where);
+ return 1;
+ }
+ /* End section which is identical to patch_proc. */
+
+ which = W_REPOS | W_ATTIC;
+ }
+ else
+ {
+ repository = NULL;
+ where = NULL;
+ which = W_LOCAL | W_REPOS | W_ATTIC;
+ }
+
+ err = start_recursion (log_fileproc, NULL, log_dirproc,
+ NULL, &log_data,
+ argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
+ where, 1, repository);
+
+ if (!(which & W_LOCAL)) free (repository);
+ if (where) free (where);
+
+ return err;
+}
+
+
+
+/*
+ * Parse a revision list specification.
+ */
+static struct option_revlist *
+log_parse_revlist (const char *argstring)
+{
+ char *orig_copy, *copy;
+ struct option_revlist *ret, **pr;
+
+ /* Unfortunately, rlog accepts -r without an argument to mean that
+ latest revision on the default branch, so we must support that
+ for compatibility. */
+ if (argstring == NULL)
+ argstring = "";
+
+ ret = NULL;
+ pr = &ret;
+
+ /* Copy the argument into memory so that we can change it. We
+ don't want to change the argument because, at least as of this
+ writing, we will use it if we send the arguments to the server. */
+ orig_copy = copy = xstrdup (argstring);
+ while (copy != NULL)
+ {
+ char *comma;
+ struct option_revlist *r;
+
+ comma = strchr (copy, ',');
+ if (comma != NULL)
+ *comma++ = '\0';
+
+ r = xmalloc (sizeof *r);
+ r->next = NULL;
+ r->first = copy;
+ r->branchhead = 0;
+ r->last = strchr (copy, ':');
+ if (r->last != NULL)
+ {
+ *r->last++ = '\0';
+ r->inclusive = (*r->last != ':');
+ if (!r->inclusive)
+ r->last++;
+ }
+ else
+ {
+ r->last = r->first;
+ r->inclusive = 1;
+ if (r->first[0] != '\0' && r->first[strlen (r->first) - 1] == '.')
+ {
+ r->branchhead = 1;
+ r->first[strlen (r->first) - 1] = '\0';
+ }
+ }
+
+ if (*r->first == '\0')
+ r->first = NULL;
+ if (*r->last == '\0')
+ r->last = NULL;
+
+ if (r->first != NULL)
+ r->first = xstrdup (r->first);
+ if (r->last != NULL)
+ r->last = xstrdup (r->last);
+
+ *pr = r;
+ pr = &r->next;
+
+ copy = comma;
+ }
+
+ free (orig_copy);
+ return ret;
+}
+
+
+
+/*
+ * Parse a date specification.
+ */
+static void
+log_parse_date (struct log_data *log_data, const char *argstring)
+{
+ char *orig_copy, *copy;
+
+ /* Copy the argument into memory so that we can change it. We
+ don't want to change the argument because, at least as of this
+ writing, we will use it if we send the arguments to the server. */
+ orig_copy = copy = xstrdup (argstring);
+ while (copy != NULL)
+ {
+ struct datelist *nd, **pd;
+ char *cpend, *cp, *ds, *de;
+
+ nd = xmalloc (sizeof *nd);
+
+ cpend = strchr (copy, ';');
+ if (cpend != NULL)
+ *cpend++ = '\0';
+
+ pd = &log_data->datelist;
+ nd->inclusive = 0;
+
+ if ((cp = strchr (copy, '>')) != NULL)
+ {
+ *cp++ = '\0';
+ if (*cp == '=')
+ {
+ ++cp;
+ nd->inclusive = 1;
+ }
+ ds = cp;
+ de = copy;
+ }
+ else if ((cp = strchr (copy, '<')) != NULL)
+ {
+ *cp++ = '\0';
+ if (*cp == '=')
+ {
+ ++cp;
+ nd->inclusive = 1;
+ }
+ ds = copy;
+ de = cp;
+ }
+ else
+ {
+ ds = NULL;
+ de = copy;
+ pd = &log_data->singledatelist;
+ }
+
+ if (ds == NULL)
+ nd->start = NULL;
+ else if (*ds != '\0')
+ nd->start = Make_Date (ds);
+ else
+ {
+ /* 1970 was the beginning of time, as far as get_date and
+ Make_Date are concerned. FIXME: That is true only if time_t
+ is a POSIX-style time and there is nothing in ANSI that
+ mandates that. It would be cleaner to set a flag saying
+ whether or not there is a start date. */
+ nd->start = Make_Date ("1/1/1970 UTC");
+ }
+
+ if (*de != '\0')
+ nd->end = Make_Date (de);
+ else
+ {
+ /* We want to set the end date to some time sufficiently far
+ in the future to pick up all revisions that have been
+ created since the specified date and the time `cvs log'
+ completes. FIXME: The date in question only makes sense
+ if time_t is a POSIX-style time and it is 32 bits
+ and signed. We should instead be setting a flag saying
+ whether or not there is an end date. Note that using
+ something like "next week" would break the testsuite (and,
+ perhaps less importantly, loses if the clock is set grossly
+ wrong). */
+ nd->end = Make_Date ("2038-01-01");
+ }
+
+ nd->next = *pd;
+ *pd = nd;
+
+ copy = cpend;
+ }
+
+ free (orig_copy);
+}
+
+
+
+/*
+ * Parse a comma separated list of items, and add each one to *PLIST.
+ */
+static void
+log_parse_list (List **plist, const char *argstring)
+{
+ while (1)
+ {
+ Node *p;
+ char *cp;
+
+ p = getnode ();
+
+ cp = strchr (argstring, ',');
+ if (cp == NULL)
+ p->key = xstrdup (argstring);
+ else
+ {
+ size_t len;
+
+ len = cp - argstring;
+ p->key = xmalloc (len + 1);
+ strncpy (p->key, argstring, len);
+ p->key[len] = '\0';
+ }
+
+ if (*plist == NULL)
+ *plist = getlist ();
+ if (addnode (*plist, p) != 0)
+ freenode (p);
+
+ if (cp == NULL)
+ break;
+
+ argstring = cp + 1;
+ }
+}
+
+
+
+static int
+printlock_proc (Node *lock, void *foo)
+{
+ cvs_output ("\n\t", 2);
+ cvs_output (lock->data, 0);
+ cvs_output (": ", 2);
+ cvs_output (lock->key, 0);
+ return 0;
+}
+
+
+
+/*
+ * Do an rlog on a file
+ */
+static int
+log_fileproc (void *callerdat, struct file_info *finfo)
+{
+ struct log_data *log_data = callerdat;
+ Node *p;
+ char *baserev;
+ int selrev = -1;
+ RCSNode *rcsfile;
+ char buf[50];
+ struct revlist *revlist = NULL;
+ struct log_data_and_rcs log_data_and_rcs;
+
+ rcsfile = finfo->rcs;
+ p = findnode (finfo->entries, finfo->file);
+ if (p != NULL)
+ {
+ Entnode *e = p->data;
+ baserev = e->version;
+ if (baserev[0] == '-') ++baserev;
+ }
+ else
+ baserev = NULL;
+
+ if (rcsfile == NULL)
+ {
+ /* no rcs file. What *do* we know about this file? */
+ if (baserev != NULL)
+ {
+ if (baserev[0] == '0' && baserev[1] == '\0')
+ {
+ if (!really_quiet)
+ error (0, 0, "%s has been added, but not committed",
+ finfo->file);
+ return 0;
+ }
+ }
+
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", finfo->file);
+
+ return 1;
+ }
+
+ if (log_data->sup_header || !log_data->nameonly)
+ {
+
+ /* We will need all the information in the RCS file. */
+ RCS_fully_parse (rcsfile);
+
+ /* Turn any symbolic revisions in the revision list into numeric
+ revisions. */
+ revlist = log_expand_revlist (finfo, baserev, log_data->revlist,
+ log_data->default_branch);
+ if (log_data->sup_header
+ || (!log_data->header && !log_data->long_header))
+ {
+ log_data_and_rcs.log_data = log_data;
+ log_data_and_rcs.revlist = revlist;
+ log_data_and_rcs.rcs = rcsfile;
+
+ /* If any single dates were specified, we need to identify the
+ revisions they select. Each one selects the single
+ revision, which is otherwise selected, of that date or
+ earlier. The log_fix_singledate routine will fill in the
+ start date for each specific revision. */
+ if (log_data->singledatelist != NULL)
+ walklist (rcsfile->versions, log_fix_singledate,
+ &log_data_and_rcs);
+
+ selrev = walklist (rcsfile->versions, log_count_print,
+ &log_data_and_rcs);
+ if (log_data->sup_header && selrev == 0)
+ {
+ log_free_revlist (revlist);
+ return 0;
+ }
+ }
+
+ }
+
+ if (log_data->nameonly)
+ {
+ cvs_output (rcsfile->print_path, 0);
+ cvs_output ("\n", 1);
+ log_free_revlist (revlist);
+ return 0;
+ }
+
+ /* The output here is intended to be exactly compatible with the
+ output of rlog. I'm not sure whether this code should be here
+ or in rcs.c; I put it here because it is specific to the log
+ function, even though it uses information gathered by the
+ functions in rcs.c. */
+
+ cvs_output ("\n", 1);
+
+ cvs_output ("RCS file: ", 0);
+ cvs_output (rcsfile->print_path, 0);
+
+ if (!is_rlog)
+ {
+ cvs_output ("\nWorking file: ", 0);
+ if (finfo->update_dir[0] != '\0')
+ {
+ cvs_output (finfo->update_dir, 0);
+ cvs_output ("/", 0);
+ }
+ cvs_output (finfo->file, 0);
+ }
+
+ cvs_output ("\nhead:", 0);
+ if (rcsfile->head != NULL)
+ {
+ cvs_output (" ", 1);
+ cvs_output (rcsfile->head, 0);
+ }
+
+ cvs_output ("\nbranch:", 0);
+ if (rcsfile->branch != NULL)
+ {
+ cvs_output (" ", 1);
+ cvs_output (rcsfile->branch, 0);
+ }
+
+ cvs_output ("\nlocks:", 0);
+ if (rcsfile->strict_locks)
+ cvs_output (" strict", 0);
+ walklist (RCS_getlocks (rcsfile), printlock_proc, NULL);
+
+ cvs_output ("\naccess list:", 0);
+ if (rcsfile->access != NULL)
+ {
+ const char *cp;
+
+ cp = rcsfile->access;
+ while (*cp != '\0')
+ {
+ const char *cp2;
+
+ cvs_output ("\n\t", 2);
+ cp2 = cp;
+ while (!isspace ((unsigned char)*cp2) && *cp2 != '\0')
+ ++cp2;
+ cvs_output (cp, cp2 - cp);
+ cp = cp2;
+ while (isspace ((unsigned char)*cp) && *cp != '\0')
+ ++cp;
+ }
+ }
+
+ if (!log_data->notags)
+ {
+ List *syms;
+
+ cvs_output ("\nsymbolic names:", 0);
+ syms = RCS_symbols (rcsfile);
+ walklist (syms, log_symbol, NULL);
+ }
+
+ cvs_output ("\nkeyword substitution: ", 0);
+ if (rcsfile->expand == NULL)
+ cvs_output ("kv", 2);
+ else
+ cvs_output (rcsfile->expand, 0);
+
+ cvs_output ("\ntotal revisions: ", 0);
+ sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL));
+ cvs_output (buf, 0);
+
+ if (selrev >= 0)
+ {
+ cvs_output (";\tselected revisions: ", 0);
+ sprintf (buf, "%d", selrev);
+ cvs_output (buf, 0);
+ }
+
+ cvs_output ("\n", 1);
+
+ if (!log_data->header || log_data->long_header)
+ {
+ cvs_output ("description:\n", 0);
+ if (rcsfile->desc != NULL)
+ cvs_output (rcsfile->desc, 0);
+ }
+
+ if (!log_data->header && ! log_data->long_header && rcsfile->head != NULL)
+ {
+ p = findnode (rcsfile->versions, rcsfile->head);
+ if (p == NULL)
+ error (1, 0, "can not find head revision in `%s'",
+ finfo->fullname);
+ while (p != NULL)
+ {
+ RCSVers *vers = p->data;
+
+ log_version (log_data, revlist, rcsfile, vers, 1);
+ if (vers->next == NULL)
+ p = NULL;
+ else
+ {
+ p = findnode (rcsfile->versions, vers->next);
+ if (p == NULL)
+ error (1, 0, "can not find next revision `%s' in `%s'",
+ vers->next, finfo->fullname);
+ }
+ }
+
+ log_tree (log_data, revlist, rcsfile, rcsfile->head);
+ }
+
+ cvs_output("\
+=============================================================================\n",
+ 0);
+
+ /* Free up the new revlist and restore the old one. */
+ log_free_revlist (revlist);
+
+ /* If singledatelist is not NULL, free up the start dates we added
+ to it. */
+ if (log_data->singledatelist != NULL)
+ {
+ struct datelist *d;
+
+ for (d = log_data->singledatelist; d != NULL; d = d->next)
+ {
+ if (d->start != NULL)
+ free (d->start);
+ d->start = NULL;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Fix up a revision list in order to compare it against versions.
+ * Expand any symbolic revisions.
+ */
+static struct revlist *
+log_expand_revlist (struct file_info *finfo, char *baserev,
+ struct option_revlist *revlist, int default_branch)
+{
+ struct option_revlist *r;
+ struct revlist *ret, **pr;
+ char *first = NULL;
+ char *last = NULL;
+ RCSNode *rcs = finfo->rcs;
+ ret = NULL;
+ pr = &ret;
+ for (r = revlist; r != NULL; r = r->next)
+ {
+ struct revlist *nr;
+
+ nr = xmalloc (sizeof *nr);
+ nr->inclusive = r->inclusive;
+
+ if (RCS_is_relative (r->first)) {
+ first = Version_resolve_relTag (finfo, r->first, !is_rlog);
+ if (!first)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.",
r->first);
+ }
+ } else {
+ first = xstrdup (r->first);
+ }
+ if (RCS_is_relative (r->last)) {
+ last = Version_resolve_relTag (finfo, r->last, !is_rlog);
+ if (!last && first)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.", r->last);
+ }
+ } else {
+ last = xstrdup (r->last);
+ }
+
+ if (first == NULL && last == NULL)
+ {
+ /* If both first and last are NULL, it means that we want
+ just the head of the default branch, which is RCS_head. */
+ nr->first = RCS_head (rcs);
+ if (!nr->first)
+ {
+ if (!really_quiet)
+ error (0, 0, "No head revision in archive `%s'.",
+ rcs->path);
+ nr->last = NULL;
+ nr->fields = 0;
+ }
+ else
+ {
+ nr->last = xstrdup (nr->first);
+ nr->fields = numdots (nr->first) + 1;
+ }
+ }
+ else if (r->branchhead)
+ {
+ char *branch;
+
+ /* Print just the head of the branch. */
+ if (!RCS_is_symbolic (first))
+ nr->first = RCS_getbranch (rcs, first, 1);
+ else
+ {
+ branch = RCS_whatbranch (rcs, first);
+ if (branch == NULL)
+ nr->first = NULL;
+ else
+ {
+ nr->first = RCS_getbranch (rcs, branch, 1);
+ free (branch);
+ }
+ }
+
+ if (!nr->first)
+ {
+ if (!really_quiet)
+ error (0, 0, "warning: no branch `%s' in `%s'",
+ r->first, rcs->print_path);
+ nr->last = NULL;
+ nr->fields = 0;
+ }
+ else
+ {
+ nr->last = xstrdup (nr->first);
+ nr->fields = numdots (nr->first) + 1;
+ }
+ }
+ else
+ {
+ if (first == NULL || !RCS_is_symbolic (first))
+ nr->first = xstrdup (first);
+ else
+ {
+ if (baserev && strcmp (first, TAG_BASE) == 0)
+ nr->first = xstrdup (baserev);
+ else if (RCS_nodeisbranch (rcs, first))
+ nr->first = RCS_whatbranch (rcs, first);
+ else
+ nr->first = RCS_gettag (rcs, first, 1, NULL);
+ if (nr->first == NULL && !really_quiet)
+ {
+ error (0, 0, "warning: no revision `%s' in `%s'",
+ r->first, rcs->print_path);
+ }
+ }
+
+ if (r->last == r->first || (r->last != NULL && r->first != NULL &&
+ strcmp (r->last, r->first) == 0))
+ nr->last = xstrdup (nr->first);
+ else if (last == NULL || !RCS_is_symbolic (last))
+ nr->last = xstrdup (last);
+ else
+ {
+ if (baserev && strcmp (last, TAG_BASE) == 0)
+ nr->last = xstrdup (baserev);
+ else if (RCS_nodeisbranch (rcs, last))
+ nr->last = RCS_whatbranch (rcs, last);
+ else
+ nr->last = RCS_gettag (rcs, last, 1, NULL);
+ if (nr->last == NULL && !really_quiet)
+ {
+ error (0, 0, "warning: no revision `%s' in `%s'",
+ r->last, rcs->print_path);
+ }
+ }
+
+ /* Process the revision numbers the same way that rlog
+ does. This code is a bit cryptic for my tastes, but
+ keeping the same implementation as rlog ensures a
+ certain degree of compatibility. */
+ if (first == NULL && nr->last != NULL)
+ {
+ nr->fields = numdots (nr->last) + 1;
+ if (nr->fields < 2)
+ nr->first = xstrdup (".0");
+ else
+ {
+ char *cp;
+
+ nr->first = xstrdup (nr->last);
+ cp = strrchr (nr->first, '.');
+ assert (cp);
+ strcpy (cp + 1, "0");
+ }
+ }
+ else if (last == NULL && nr->first != NULL)
+ {
+ nr->fields = numdots (nr->first) + 1;
+ nr->last = xstrdup (nr->first);
+ if (nr->fields < 2)
+ nr->last[0] = '\0';
+ else
+ {
+ char *cp;
+
+ cp = strrchr (nr->last, '.');
+ assert (cp);
+ *cp = '\0';
+ }
+ }
+ else if (nr->first == NULL || nr->last == NULL)
+ nr->fields = 0;
+ else if (strcmp (nr->first, nr->last) == 0)
+ nr->fields = numdots (nr->last) + 1;
+ else
+ {
+ int ord;
+ int dots1 = numdots (nr->first);
+ int dots2 = numdots (nr->last);
+ if (dots1 > dots2 || (dots1 == dots2 &&
+ version_compare (nr->first, nr->last, dots1 + 1) > 0))
+ {
+ char *tmp = nr->first;
+ nr->first = nr->last;
+ nr->last = tmp;
+ nr->fields = dots2 + 1;
+ dots2 = dots1;
+ dots1 = nr->fields - 1;
+ }
+ else
+ nr->fields = dots1 + 1;
+ dots1 += (nr->fields & 1);
+ ord = version_compare (nr->first, nr->last, dots1);
+ if (ord > 0 || (nr->fields > 2 && ord < 0))
+ {
+ error (0, 0,
+ "invalid branch or revision pair %s:%s in `%s'",
+ r->first, r->last, rcs->print_path);
+ free (nr->first);
+ nr->first = NULL;
+ free (nr->last);
+ nr->last = NULL;
+ nr->fields = 0;
+ }
+ else
+ {
+ if (nr->fields <= dots2 && (nr->fields & 1))
+ {
+ char *p = Xasprintf ("%s.0", nr->first);
+ free (nr->first);
+ nr->first = p;
+ ++nr->fields;
+ }
+ while (nr->fields <= dots2)
+ {
+ char *p;
+ int i;
+
+ nr->next = NULL;
+ *pr = nr;
+ nr = xmalloc (sizeof *nr);
+ nr->inclusive = 1;
+ nr->first = xstrdup ((*pr)->last);
+ nr->last = xstrdup ((*pr)->last);
+ nr->fields = (*pr)->fields;
+ p = (*pr)->last;
+ for (i = 0; i < nr->fields; i++)
+ p = strchr (p, '.') + 1;
+ p[-1] = '\0';
+ p = strchr (nr->first + (p - (*pr)->last), '.');
+ if (p != NULL)
+ {
+ *++p = '0';
+ *++p = '\0';
+ nr->fields += 2;
+ }
+ else
+ ++nr->fields;
+ pr = &(*pr)->next;
+ }
+ }
+ }
+ }
+
+ nr->next = NULL;
+ *pr = nr;
+ pr = &nr->next;
+ }
+
+ /* If the default branch was requested, add a revlist entry for
+ it. This is how rlog handles this option. */
+ if (default_branch
+ && (rcs->head != NULL || rcs->branch != NULL))
+ {
+ struct revlist *nr;
+
+ nr = xmalloc (sizeof *nr);
+ if (rcs->branch != NULL)
+ nr->first = xstrdup (rcs->branch);
+ else
+ {
+ char *cp;
+
+ nr->first = xstrdup (rcs->head);
+ assert (nr->first);
+ cp = strrchr (nr->first, '.');
+ assert (cp);
+ *cp = '\0';
+ }
+ nr->last = xstrdup (nr->first);
+ nr->fields = numdots (nr->first) + 1;
+ nr->inclusive = 1;
+
+ nr->next = NULL;
+ *pr = nr;
+ }
+
+ free (first);
+ free (last);
+ return ret;
+}
+
+
+
+/*
+ * Free a revlist created by log_expand_revlist.
+ */
+static void
+log_free_revlist (struct revlist *revlist)
+{
+ struct revlist *r;
+
+ r = revlist;
+ while (r != NULL)
+ {
+ struct revlist *next;
+
+ if (r->first != NULL)
+ free (r->first);
+ if (r->last != NULL)
+ free (r->last);
+ next = r->next;
+ free (r);
+ r = next;
+ }
+}
+
+
+
+/*
+ * Return nonzero if a revision should be printed, based on the
+ * options provided.
+ */
+static int
+log_version_requested (struct log_data *log_data, struct revlist *revlist,
+ RCSNode *rcs, RCSVers *vnode)
+{
+ /* Handle the list of states from the -s option. */
+ if (log_data->statelist != NULL
+ && findnode (log_data->statelist, vnode->state) == NULL)
+ {
+ return 0;
+ }
+
+ /* Handle the list of authors from the -w option. */
+ if (log_data->authorlist != NULL)
+ {
+ if (vnode->author != NULL
+ && findnode (log_data->authorlist, vnode->author) == NULL)
+ {
+ return 0;
+ }
+ }
+
+ /* rlog considers all the -d options together when it decides
+ whether to print a revision, so we must be compatible. */
+ if (log_data->datelist != NULL || log_data->singledatelist != NULL)
+ {
+ struct datelist *d;
+
+ for (d = log_data->datelist; d != NULL; d = d->next)
+ {
+ int cmp;
+
+ cmp = RCS_datecmp (vnode->date, d->start);
+ if (cmp > 0 || (cmp == 0 && d->inclusive))
+ {
+ cmp = RCS_datecmp (vnode->date, d->end);
+ if (cmp < 0 || (cmp == 0 && d->inclusive))
+ break;
+ }
+ }
+
+ if (d == NULL)
+ {
+ /* Look through the list of specific dates. We want to
+ select the revision with the exact date found in the
+ start field. The commit code ensures that it is
+ impossible to check in multiple revisions of a single
+ file in a single second, so checking the date this way
+ should never select more than one revision. */
+ for (d = log_data->singledatelist; d != NULL; d = d->next)
+ {
+ if (d->start != NULL
+ && RCS_datecmp (vnode->date, d->start) == 0)
+ {
+ break;
+ }
+ }
+
+ if (d == NULL)
+ return 0;
+ }
+ }
+
+ /* If the -r or -b options were used, REVLIST will be non NULL,
+ and we print the union of the specified revisions. */
+ if (revlist != NULL)
+ {
+ char *v;
+ int vfields;
+ struct revlist *r;
+
+ /* This code is taken from rlog. */
+ v = vnode->version;
+ vfields = numdots (v) + 1;
+ for (r = revlist; r != NULL; r = r->next)
+ {
+ if (vfields == r->fields + (r->fields & 1) &&
+ (r->inclusive ? version_compare (v, r->first, r->fields) >= 0 :
+ version_compare (v, r->first, r->fields) > 0)
+ && version_compare (v, r->last, r->fields) <= 0)
+ {
+ return 1;
+ }
+ }
+
+ /* If we get here, then the -b and/or the -r option was used,
+ but did not match this revision, so we reject it. */
+
+ return 0;
+ }
+
+ /* By default, we print all revisions. */
+ return 1;
+}
+
+
+
+/*
+ * Output a single symbol. This is called via walklist.
+ */
+/*ARGSUSED*/
+static int
+log_symbol (Node *p, void *closure)
+{
+ cvs_output ("\n\t", 2);
+ cvs_output (p->key, 0);
+ cvs_output (": ", 2);
+ cvs_output (p->data, 0);
+ return 0;
+}
+
+
+
+/*
+ * Count the number of entries on a list. This is called via walklist.
+ */
+/*ARGSUSED*/
+static int
+log_count (Node *p, void *closure)
+{
+ return 1;
+}
+
+
+
+/*
+ * Sort out a single date specification by narrowing down the date
+ * until we find the specific selected revision.
+ */
+static int
+log_fix_singledate (Node *p, void *closure)
+{
+ struct log_data_and_rcs *data = closure;
+ Node *pv;
+ RCSVers *vnode;
+ struct datelist *holdsingle, *holddate;
+ int requested;
+
+ pv = findnode (data->rcs->versions, p->key);
+ if (pv == NULL)
+ error (1, 0, "missing version `%s' in RCS file `%s'",
+ p->key, data->rcs->print_path);
+ vnode = pv->data;
+
+ /* We are only interested if this revision passes any other tests.
+ Temporarily clear log_data->singledatelist to avoid confusing
+ log_version_requested. We also clear log_data->datelist,
+ because rlog considers all the -d options together. We don't
+ want to reject a revision because it does not match a date pair
+ if we are going to select it on the basis of the singledate. */
+ holdsingle = data->log_data->singledatelist;
+ data->log_data->singledatelist = NULL;
+ holddate = data->log_data->datelist;
+ data->log_data->datelist = NULL;
+ requested = log_version_requested (data->log_data, data->revlist,
+ data->rcs, vnode);
+ data->log_data->singledatelist = holdsingle;
+ data->log_data->datelist = holddate;
+
+ if (requested)
+ {
+ struct datelist *d;
+
+ /* For each single date, if this revision is before the
+ specified date, but is closer than the previously selected
+ revision, select it instead. */
+ for (d = data->log_data->singledatelist; d != NULL; d = d->next)
+ {
+ if (RCS_datecmp (vnode->date, d->end) <= 0
+ && (d->start == NULL
+ || RCS_datecmp (vnode->date, d->start) > 0))
+ {
+ if (d->start != NULL)
+ free (d->start);
+ d->start = xstrdup (vnode->date);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Count the number of revisions we are going to print.
+ */
+static int
+log_count_print (Node *p, void *closure)
+{
+ struct log_data_and_rcs *data = closure;
+ Node *pv;
+
+ pv = findnode (data->rcs->versions, p->key);
+ if (pv == NULL)
+ error (1, 0, "missing version `%s' in RCS file `%s'",
+ p->key, data->rcs->print_path);
+ if (log_version_requested (data->log_data, data->revlist, data->rcs,
+ pv->data))
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ * Print the list of changes, not including the trunk, in reverse
+ * order for each branch.
+ */
+static void
+log_tree (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
+ const char *ver)
+{
+ Node *p;
+ RCSVers *vnode;
+
+ p = findnode (rcs->versions, ver);
+ if (p == NULL)
+ error (1, 0, "missing version `%s' in RCS file `%s'",
+ ver, rcs->print_path);
+ vnode = p->data;
+ if (vnode->next != NULL)
+ log_tree (log_data, revlist, rcs, vnode->next);
+ if (vnode->branches != NULL)
+ {
+ Node *head, *branch;
+
+ /* We need to do the branches in reverse order. This breaks
+ the List abstraction, but so does most of the branch
+ manipulation in rcs.c. */
+ head = vnode->branches->list;
+ for (branch = head->prev; branch != head; branch = branch->prev)
+ {
+ log_abranch (log_data, revlist, rcs, branch->key);
+ log_tree (log_data, revlist, rcs, branch->key);
+ }
+ }
+}
+
+
+
+/*
+ * Log the changes for a branch, in reverse order.
+ */
+static void
+log_abranch (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
+ const char *ver)
+{
+ Node *p;
+ RCSVers *vnode;
+
+ p = findnode (rcs->versions, ver);
+ if (p == NULL)
+ error (1, 0, "missing version `%s' in RCS file `%s'",
+ ver, rcs->print_path);
+ vnode = p->data;
+ if (vnode->next != NULL)
+ log_abranch (log_data, revlist, rcs, vnode->next);
+ log_version (log_data, revlist, rcs, vnode, 0);
+}
+
+
+
+/*
+ * Print the log output for a single version.
+ */
+static void
+log_version (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
+ RCSVers *ver, int trunk)
+{
+ Node *p;
+ int year, mon, mday, hour, min, sec;
+ char buf[100];
+ Node *padd, *pdel;
+
+ if (! log_version_requested (log_data, revlist, rcs, ver))
+ return;
+
+ cvs_output ("----------------------------\nrevision ", 0);
+ cvs_output (ver->version, 0);
+
+ p = findnode (RCS_getlocks (rcs), ver->version);
+ if (p != NULL)
+ {
+ cvs_output ("\tlocked by: ", 0);
+ cvs_output (p->data, 0);
+ cvs_output (";", 1);
+ }
+ cvs_output ("\n", 1);
+
+ cvs_output_tagged ("text", "date: ");
+ (void)sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min,
+ &sec);
+ if (year < 1900)
+ year += 1900;
+ sprintf (buf, "%04d-%02d-%02d %02d:%02d:%02d +0000", year, mon, mday,
+ hour, min, sec);
+ cvs_output_tagged ("date", buf);
+
+ cvs_output_tagged ("text", "; author: ");
+ cvs_output_tagged ("text", ver->author);
+
+ cvs_output_tagged ("text", "; state: ");
+ cvs_output_tagged ("text", ver->state);
+ cvs_output_tagged ("text", ";");
+
+ if (! trunk)
+ {
+ padd = findnode (ver->other, ";add");
+ pdel = findnode (ver->other, ";delete");
+ }
+ else if (ver->next == NULL)
+ {
+ padd = NULL;
+ pdel = NULL;
+ }
+ else
+ {
+ Node *nextp;
+ RCSVers *nextver;
+
+ nextp = findnode (rcs->versions, ver->next);
+ if (nextp == NULL)
+ error (1, 0, "missing version `%s' in `%s'", ver->next,
+ rcs->print_path);
+ nextver = nextp->data;
+ pdel = findnode (nextver->other, ";add");
+ padd = findnode (nextver->other, ";delete");
+ }
+
+ if (padd != NULL)
+ {
+ assert (pdel);
+ cvs_output_tagged ("text", " lines: +");
+ cvs_output_tagged ("text", padd->data);
+ cvs_output_tagged ("text", " -");
+ cvs_output_tagged ("text", pdel->data);
+ cvs_output_tagged ("text", ";");
+ }
+
+ p = findnode(ver->other_delta,"commitid");
+ if(p && p->data)
+ {
+ cvs_output_tagged ("text", " commitid: ");
+ cvs_output_tagged ("text", p->data);
+ cvs_output_tagged ("text", ";");
+ }
+
+ cvs_output_tagged ("newline", NULL);
+
+ if (ver->branches != NULL)
+ {
+ cvs_output ("branches:", 0);
+ walklist (ver->branches, log_branch, NULL);
+ cvs_output ("\n", 1);
+ }
+
+ p = findnode (ver->other, "log");
+ /* The p->date == NULL case is the normal one for an empty log
+ message (rcs-14 in sanity.sh). I don't think the case where
+ p->data is "" can happen (getrcskey in rcs.c checks for an
+ empty string and set the value to NULL in that case). My guess
+ would be the p == NULL case would mean an RCS file which was
+ missing the "log" keyword (which is invalid according to
+ rcsfile.5). */
+ if (p == NULL || p->data == NULL || *(char *)p->data == '\0')
+ cvs_output ("*** empty log message ***\n", 0);
+ else
+ {
+ /* FIXME: Technically, the log message could contain a null
+ byte. */
+ cvs_output (p->data, 0);
+ if (((char *)p->data)[strlen (p->data) - 1] != '\n')
+ cvs_output ("\n", 1);
+ }
+}
+
+
+
+/*
+ * Output a branch version. This is called via walklist.
+ */
+/*ARGSUSED*/
+static int
+log_branch (Node *p, void *closure)
+{
+ cvs_output (" ", 2);
+ if ((numdots (p->key) & 1) == 0)
+ cvs_output (p->key, 0);
+ else
+ {
+ char *f, *cp;
+
+ f = xstrdup (p->key);
+ cp = strrchr (f, '.');
+ *cp = '\0';
+ cvs_output (f, 0);
+ free (f);
+ }
+ cvs_output (";", 1);
+ return 0;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+log_dirproc (void *callerdat, const char *dir, const char *repository,
+ const char *update_dir, List *entries)
+{
+ if (!isdir (dir))
+ return R_SKIP_ALL;
+
+ if (!quiet)
+ error (0, 0, "Logging %s", update_dir);
+ return R_PROCESS;
+}
+
+
+
+/*
+ * Compare versions. This is taken from RCS compartial.
+ */
+static int
+version_compare (const char *v1, const char *v2, int len)
+{
+ while (1)
+ {
+ int d1, d2, r;
+
+ if (*v1 == '\0')
+ return 1;
+ if (*v2 == '\0')
+ return -1;
+
+ while (*v1 == '0')
+ ++v1;
+ for (d1 = 0; isdigit ((unsigned char) v1[d1]); ++d1)
+ ;
+
+ while (*v2 == '0')
+ ++v2;
+ for (d2 = 0; isdigit ((unsigned char) v2[d2]); ++d2)
+ ;
+
+ if (d1 != d2)
+ return d1 < d2 ? -1 : 1;
+
+ r = memcmp (v1, v2, d1);
+ if (r != 0)
+ return r;
+
+ --len;
+ if (len == 0)
+ return 0;
+
+ v1 += d1;
+ v2 += d1;
+
+ if (*v1 == '.')
+ ++v1;
+ if (*v2 == '.')
+ ++v2;
+ }
+}
Index: ccvs/src/patch.c
diff -u /dev/null ccvs/src/patch.c:1.106.8.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/patch.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Patch
+ *
+ * Create a Larry Wall format "patch" file between a previous release and the
+ * current head of a module, or between two releases. Can specify the
+ * release as either a date or a revision number.
+ */
+
+#include "cvs.h"
+#include "getline.h"
+
+static RETSIGTYPE patch_cleanup (int);
+static Dtype patch_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int patch_fileproc (void *callerdat, struct file_info *finfo);
+static int patch_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+
+static int force_tag_match = 1;
+static int patch_short = 0;
+static int toptwo_diffs = 0;
+static char *options = NULL;
+static char *rev1 = NULL;
+static int rev1_validated = 0;
+static char *rev2 = NULL;
+static int rev2_validated = 0;
+static char *date1 = NULL;
+static char *date2 = NULL;
+static char *tmpfile1 = NULL;
+static char *tmpfile2 = NULL;
+static char *tmpfile3 = NULL;
+static int unidiff = 0;
+
+static const char *const patch_usage[] =
+{
+ "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
+ " -r rev|-D date [-r rev2 | -D date2] modules...\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-c\tContext diffs (default)\n",
+ "\t-u\tUnidiff format.\n",
+ "\t-s\tShort patch - one liner per file.\n",
+ "\t-t\tTop two diffs - last change made to the file.\n",
+ "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
+ "\t-k kopt\tSpecify keyword expansion mode.\n",
+ "\t-D date\tDate.\n",
+ "\t-r rev\tRevision - symbolic or numeric.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+
+
+int
+patch (int argc, char **argv)
+{
+ register int i;
+ int local = 0;
+ int c;
+ int err = 0;
+ DBM *db;
+
+ if (argc == -1)
+ usage (patch_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ case 'q':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_name);
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 't':
+ toptwo_diffs = 1;
+ break;
+ case 's':
+ patch_short = 1;
+ break;
+ case 'D':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ date2 = Make_Date (optarg);
+ else
+ date1 = Make_Date (optarg);
+ break;
+ case 'r':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ rev2 = optarg;
+ else
+ rev1 = optarg;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'V':
+ /* This option is pretty seriously broken:
+ 1. It is not clear what it does (does it change keyword
+ expansion behavior? If so, how? Or does it have
+ something to do with what version of RCS we are using?
+ Or the format we write RCS files in?).
+ 2. Because both it and -k use the options variable,
+ specifying both -V and -k doesn't work.
+ 3. At least as of CVS 1.9, it doesn't work (failed
+ assertion in RCS_checkout where it asserts that options
+ starts with -k). Few people seem to be complaining.
+ In the future (perhaps the near future), I have in mind
+ removing it entirely, and updating NEWS and cvs.texinfo,
+ but in case it is a good idea to give people more time
+ to complain if they would miss it, I'll just add this
+ quick and dirty error message for now. */
+ error (1, 0,
+ "the -V option is obsolete and should not be used");
+ break;
+ case 'u':
+ unidiff = 1; /* Unidiff */
+ break;
+ case 'c': /* Context diff */
+ unidiff = 0;
+ break;
+ case '?':
+ default:
+ usage (patch_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Sanity checks */
+ if (argc < 1)
+ usage (patch_usage);
+
+ if (toptwo_diffs && patch_short)
+ error (1, 0, "-t and -s options are mutually exclusive");
+ if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
+ rev1 != NULL || rev2 != NULL))
+ error (1, 0, "must not specify revisions/dates with -t option!");
+
+ if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
+ rev1 == NULL && rev2 == NULL))
+ error (1, 0, "must specify at least one revision/date!");
+ if (date1 != NULL && date2 != NULL)
+ if (RCS_datecmp (date1, date2) >= 0)
+ error (1, 0, "second date must come after first date!");
+
+ /* if options is NULL, make it a NULL string */
+ if (options == NULL)
+ options = xstrdup ("");
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ /* We're the client side. Fire up the remote server. */
+ start_server ();
+
+ ign_setup ();
+
+ if (local)
+ send_arg("-l");
+ if (!force_tag_match)
+ send_arg("-f");
+ if (toptwo_diffs)
+ send_arg("-t");
+ if (patch_short)
+ send_arg("-s");
+ if (unidiff)
+ send_arg("-u");
+
+ if (rev1)
+ option_with_arg ("-r", rev1);
+ if (date1)
+ client_senddate (date1);
+ if (rev2)
+ option_with_arg ("-r", rev2);
+ if (date2)
+ client_senddate (date2);
+ if (options[0] != '\0')
+ send_arg (options);
+
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ }
+
+ send_to_server ("rdiff\012", 0);
+ return get_responses_and_close ();
+ }
+#endif
+
+ /* clean up if we get a signal */
+#ifdef SIGABRT
+ (void)SIG_register (SIGABRT, patch_cleanup);
+#endif
+#ifdef SIGHUP
+ (void)SIG_register (SIGHUP, patch_cleanup);
+#endif
+#ifdef SIGINT
+ (void)SIG_register (SIGINT, patch_cleanup);
+#endif
+#ifdef SIGQUIT
+ (void)SIG_register (SIGQUIT, patch_cleanup);
+#endif
+#ifdef SIGPIPE
+ (void)SIG_register (SIGPIPE, patch_cleanup);
+#endif
+#ifdef SIGTERM
+ (void)SIG_register (SIGTERM, patch_cleanup);
+#endif
+
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
+ NULL, 0, local, 0, 0, NULL);
+ close_module (db);
+ free (options);
+ patch_cleanup (0);
+ return err;
+}
+
+
+
+/*
+ * callback proc for doing the real work of patching
+ */
+/* ARGSUSED */
+static int
+patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
+ int shorten, int local_specified, char *mname, char *msg)
+{
+ char *myargv[2];
+ int err = 0;
+ int which;
+ char *repository;
+ char *where;
+
+ TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )",
+ xwhere ? xwhere : "(null)",
+ mwhere ? mwhere : "(null)",
+ mfile ? mfile : "(null)",
+ shorten, local_specified,
+ mname ? mname : "(null)",
+ msg ? msg : "(null)" );
+
+ repository = xmalloc (strlen (current_parsed_root->directory)
+ + strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
+ (void)sprintf (repository, "%s/%s",
+ current_parsed_root->directory, argv[0]);
+ where = xmalloc (strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1)
+ + 1);
+ (void)strcpy (where, argv[0]);
+
+ /* if mfile isn't null, we need to set up to do only part of the module */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char *path;
+
+ /* if the portion of the module is a path, put the dir part on repos */
+ if ((cp = strrchr (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void)strcat (repository, "/");
+ (void)strcat (repository, mfile);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ path = xmalloc (strlen (repository) + strlen (mfile) + 2);
+ (void)sprintf (path, "%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void)strcpy (repository, path);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ }
+ else
+ {
+ myargv[0] = argv[0];
+ myargv[1] = mfile;
+ argc = 2;
+ argv = myargv;
+ }
+ free (path);
+ }
+
+ /* cd to the starting repository */
+ if (CVS_CHDIR (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (repository);
+ free (where);
+ return 1;
+ }
+
+ if (force_tag_match)
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+
+ if (rev1 != NULL && !rev1_validated)
+ {
+ tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
+ repository, false);
+ rev1_validated = 1;
+ }
+ if (rev2 != NULL && !rev2_validated)
+ {
+ tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
+ repository, false);
+ rev2_validated = 1;
+ }
+
+ /* start the recursion processor */
+ err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL,
+ argc - 1, argv + 1, local_specified,
+ which, 0, CVS_LOCK_READ, where, 1, repository );
+ free (repository);
+ free (where);
+
+ return err;
+}
+
+
+
+/*
+ * Called to examine a particular RCS file, as appropriate with the options
+ * that were set above.
+ */
+/* ARGSUSED */
+static int
+patch_fileproc (void *callerdat, struct file_info *finfo)
+{
+ struct utimbuf t;
+ char *vers_tag, *vers_head;
+ char *rcs = NULL;
+ char *rcs_orig = NULL;
+ RCSNode *rcsfile;
+ FILE *fp1, *fp2, *fp3;
+ int ret = 0;
+ int isattic = 0;
+ int retcode = 0;
+ char *file1;
+ char *file2;
+ char *strippath;
+ char *line1, *line2;
+ size_t line1_chars_allocated;
+ size_t line2_chars_allocated;
+ char *cp1, *cp2;
+ FILE *fp;
+ int line_length;
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+ Vers_TS *vers;
+
+ line1 = NULL;
+ line1_chars_allocated = 0;
+ line2 = NULL;
+ line2_chars_allocated = 0;
+ vers_tag = vers_head = NULL;
+
+ /* find the parsed rcs file */
+ if ((rcsfile = finfo->rcs) == NULL)
+ {
+ ret = 1;
+ goto out2;
+ }
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ isattic = 1;
+
+ rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT);
+
+ /* if vers_head is NULL, may have been removed from the release */
+ if (isattic && rev2 == NULL && date2 == NULL)
+ vers_head = NULL;
+ else
+ {
+ vers = Version_TS (finfo, NULL, rev2, date2, force_tag_match, 0);
+ if (vers->vn_rcs != NULL)
+ vers_head = xstrdup (vers->vn_rcs);
+ freevers_ts (&vers);
+
+ if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
+ {
+ free (vers_head);
+ vers_head = NULL;
+ }
+ }
+
+ if (toptwo_diffs)
+ {
+ if (vers_head == NULL)
+ {
+ ret = 1;
+ goto out2;
+ }
+
+ if (!date1)
+ date1 = xmalloc (MAXDATELEN);
+ *date1 = '\0';
+ if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
+ {
+ if (!really_quiet)
+ error (0, 0, "cannot find date in rcs file %s revision %s",
+ rcs, vers_head);
+ ret = 1;
+ goto out2;
+ }
+ }
+ vers = Version_TS (finfo, NULL, rev1, date1, force_tag_match, 0);
+ if (vers->vn_rcs != NULL)
+ vers_tag = xstrdup (vers->vn_rcs);
+ freevers_ts (&vers);
+ if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
+ {
+ free (vers_tag);
+ vers_tag = NULL;
+ }
+
+ if ((vers_tag == NULL && vers_head == NULL) ||
+ (vers_tag != NULL && vers_head != NULL &&
+ strcmp (vers_head, vers_tag) == 0))
+ {
+ /* Nothing known about specified revs or
+ * not changed between releases.
+ */
+ ret = 0;
+ goto out2;
+ }
+
+ if (patch_short && (vers_tag == NULL || vers_head == NULL))
+ {
+ /* For adds & removes with a short patch requested, we can print our
+ * error message now and get out.
+ */
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ if (vers_tag == NULL)
+ {
+ cvs_output (" is new; ", 0);
+ cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0);
+ cvs_output (" revision ", 0);
+ cvs_output (vers_head, 0);
+ cvs_output ("\n", 1);
+ }
+ else
+ {
+ cvs_output (" is removed; ", 0);
+ cvs_output (rev1 ? rev1 : date1, 0);
+ cvs_output (" revision ", 0);
+ cvs_output (vers_tag, 0);
+ cvs_output ("\n", 1);
+ }
+ ret = 0;
+ goto out2;
+ }
+
+ /* Create 3 empty files. I'm not really sure there is any advantage
+ * to doing so now rather than just waiting until later.
+ *
+ * There is - cvs_temp_file opens the file so that it can guarantee that
+ * we have exclusive write access to the file. Unfortunately we spoil that
+ * by closing it and reopening it again. Of course any better solution
+ * requires that the RCS functions accept open file pointers rather than
+ * simple file names.
+ */
+ if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile1);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp1) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile1);
+ if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile2);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp2) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile2);
+ if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile3);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp3) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile3);
+
+ if (vers_tag != NULL)
+ {
+ retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options,
+ tmpfile1, NULL, NULL);
+ if (retcode != 0)
+ {
+ error (0, 0,
+ "cannot check out revision %s of %s", vers_tag, rcs);
+ ret = 1;
+ goto out;
+ }
+ memset ((char *) &t, 0, sizeof (t));
+ if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
+ NULL, 0)) != -1)
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void)utime (tmpfile1, &t);
+ }
+ else if (toptwo_diffs)
+ {
+ ret = 1;
+ goto out;
+ }
+ if (vers_head != NULL)
+ {
+ retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options,
+ tmpfile2, NULL, NULL);
+ if (retcode != 0)
+ {
+ error (0, 0,
+ "cannot check out revision %s of %s", vers_head, rcs);
+ ret = 1;
+ goto out;
+ }
+ if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
+ NULL, 0)) != -1)
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void)utime (tmpfile2, &t);
+ }
+
+ if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
+ else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
+ switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
+ tmpfile3))
+ {
+ case -1: /* fork/wait failure */
+ error (1, errno, "fork for diff failed on %s", rcs);
+ break;
+ case 0: /* nothing to do */
+ break;
+ case 1:
+ /*
+ * The two revisions are really different, so read the first two
+ * lines of the diff output file, and munge them to include more
+ * reasonable file names that "patch" will understand, unless the
+ * user wanted a short patch. In that case, just output the short
+ * message.
+ */
+ if (patch_short)
+ {
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" changed from revision ", 0);
+ cvs_output (vers_tag, 0);
+ cvs_output (" to ", 0);
+ cvs_output (vers_head, 0);
+ cvs_output ("\n", 1);
+ ret = 0;
+ goto out;
+ }
+
+ /* Output an "Index:" line for patch to use */
+ cvs_output ("Index: ", 0);
+ cvs_output (finfo->fullname, 0);
+ cvs_output ("\n", 1);
+
+ /* Now the munging. */
+ fp = xfopen (tmpfile3, "r");
+ if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
+ getline (&line2, &line2_chars_allocated, fp) < 0)
+ {
+ if (feof (fp))
+ error (0, 0, "\
+failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
+ else
+ error (0, errno,
+ "failed to read diff file header %s for %s",
+ tmpfile3, rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ if (!unidiff)
+ {
+ if (strncmp (line1, "*** ", 4) != 0 ||
+ strncmp (line2, "--- ", 4) != 0 ||
+ (cp1 = strchr (line1, '\t')) == NULL ||
+ (cp2 = strchr (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid diff header for %s", rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ }
+ else
+ {
+ if (strncmp (line1, "--- ", 4) != 0 ||
+ strncmp (line2, "+++ ", 4) != 0 ||
+ (cp1 = strchr (line1, '\t')) == NULL ||
+ (cp2 = strchr (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid unidiff header for %s", rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ }
+ assert (current_parsed_root != NULL);
+ assert (current_parsed_root->directory != NULL);
+
+ strippath = Xasprintf ("%s/", current_parsed_root->directory);
+
+ if (strncmp (rcs, strippath, strlen (strippath)) == 0)
+ rcs += strlen (strippath);
+ free (strippath);
+ if (vers_tag != NULL)
+ file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
+ else
+ file1 = xstrdup (DEVNULL);
+
+ file2 = Xasprintf ("%s:%s", finfo->fullname,
+ vers_head ? vers_head : "removed");
+
+ /* Note that the string "diff" is specified by POSIX (for -c)
+ and is part of the diff output format, not the name of a
+ program. */
+ if (unidiff)
+ {
+ cvs_output ("diff -u ", 0);
+ cvs_output (file1, 0);
+ cvs_output (" ", 1);
+ cvs_output (file2, 0);
+ cvs_output ("\n", 1);
+
+ cvs_output ("--- ", 0);
+ cvs_output (file1, 0);
+ cvs_output (cp1, 0);
+ cvs_output ("+++ ", 0);
+ }
+ else
+ {
+ cvs_output ("diff -c ", 0);
+ cvs_output (file1, 0);
+ cvs_output (" ", 1);
+ cvs_output (file2, 0);
+ cvs_output ("\n", 1);
+
+ cvs_output ("*** ", 0);
+ cvs_output (file1, 0);
+ cvs_output (cp1, 0);
+ cvs_output ("--- ", 0);
+ }
+
+ cvs_output (finfo->fullname, 0);
+ cvs_output (cp2, 0);
+
+ /* spew the rest of the diff out */
+ while ((line_length
+ = getline (&line1, &line1_chars_allocated, fp))
+ >= 0)
+ cvs_output (line1, 0);
+ if (line_length < 0 && !feof (fp))
+ error (0, errno, "cannot read %s", tmpfile3);
+
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", tmpfile3);
+ free (file1);
+ free (file2);
+ break;
+ default:
+ error (0, 0, "diff failed for %s", finfo->fullname);
+ }
+ out:
+ if (line1)
+ free (line1);
+ if (line2)
+ free (line2);
+ if (CVS_UNLINK (tmpfile1) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile1);
+ if (CVS_UNLINK (tmpfile2) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile2);
+ if (CVS_UNLINK (tmpfile3) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile3);
+ free (tmpfile1);
+ free (tmpfile2);
+ free (tmpfile3);
+ tmpfile1 = tmpfile2 = tmpfile3 = NULL;
+ if (darg_allocated)
+ {
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+ }
+
+ out2:
+ if (vers_tag != NULL)
+ free (vers_tag);
+ if (vers_head != NULL)
+ free (vers_head);
+ if (rcs_orig)
+ free (rcs_orig);
+ return ret;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+patch_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return R_PROCESS;
+}
+
+
+
+/*
+ * Clean up temporary files
+ */
+static RETSIGTYPE
+patch_cleanup (int sig)
+{
+ /* Note that the checks for existence_error are because we are
+ called from a signal handler, without SIG_begincrsect, so
+ we don't know whether the files got created. */
+
+ if (tmpfile1 != NULL)
+ {
+ if (unlink_file (tmpfile1) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile1);
+ free (tmpfile1);
+ }
+ if (tmpfile2 != NULL)
+ {
+ if (unlink_file (tmpfile2) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile2);
+ free (tmpfile2);
+ }
+ if (tmpfile3 != NULL)
+ {
+ if (unlink_file (tmpfile3) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile3);
+ free (tmpfile3);
+ }
+ tmpfile1 = tmpfile2 = tmpfile3 = NULL;
+
+ if (sig != 0)
+ {
+ const char *name;
+ char temp[10];
+
+ switch (sig)
+ {
+#ifdef SIGABRT
+ case SIGABRT:
+ name = "abort";
+ break;
+#endif
+#ifdef SIGHUP
+ case SIGHUP:
+ name = "hangup";
+ break;
+#endif
+#ifdef SIGINT
+ case SIGINT:
+ name = "interrupt";
+ break;
+#endif
+#ifdef SIGQUIT
+ case SIGQUIT:
+ name = "quit";
+ break;
+#endif
+#ifdef SIGPIPE
+ case SIGPIPE:
+ name = "broken pipe";
+ break;
+#endif
+#ifdef SIGTERM
+ case SIGTERM:
+ name = "termination";
+ break;
+#endif
+ default:
+ /* This case should never be reached, because we list
+ above all the signals for which we actually establish a
+ signal handler. */
+ sprintf (temp, "%d", sig);
+ name = temp;
+ break;
+ }
+ error (0, 0, "received %s signal", name);
+ }
+}
Index: ccvs/src/rcs.c
diff -u /dev/null ccvs/src/rcs.c:1.357.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/rcs.c Tue Jan 17 15:41:23 2006
@@ -0,0 +1,9873 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * The routines contained in this file do all the rcs file parsing and
+ * manipulation
+ */
+
+#include "cvs.h"
+#include "edit.h"
+#include "hardlink.h"
+
+/* These need to be source after cvs.h or HAVE_MMAP won't be set... */
+#ifdef HAVE_MMAP
+# include "getpagesize.h"
+# include <sys/mman.h>
+
+/* Define MAP_FILE when it isn't otherwise. */
+# ifndef MAP_FILE
+# define MAP_FILE 0
+# endif
+/* Define MAP_FAILED for old systems which neglect to. */
+# ifndef MAP_FAILED
+# define MAP_FAILED ((void *)-1)
+# endif
+#endif
+
+/* The RCS -k options, and a set of enums that must match the array.
+ These come first so that we can use enum kflag in function
+ prototypes. */
+static const char *const kflags[] =
+ {"kv", "kvl", "k", "v", "o", "b", NULL};
+enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B };
+
+/* A structure we use to buffer the contents of an RCS file. The
+ various fields are only referenced directly by the rcsbuf_*
+ functions. We declare the struct here so that we can allocate it
+ on the stack, rather than in memory. */
+
+struct rcsbuffer
+{
+ /* Points to the current position in the buffer. */
+ char *ptr;
+ /* Points just after the last valid character in the buffer. */
+ char *ptrend;
+ /* The file. */
+ FILE *fp;
+ /* The name of the file, used for error messages. */
+ const char *filename;
+ /* The starting file position of the data in the buffer. */
+ unsigned long pos;
+ /* The length of the value. */
+ size_t vlen;
+ /* Whether the value contains an '@' string. If so, we can not
+ compress whitespace characters. */
+ int at_string;
+ /* The number of embedded '@' characters in an '@' string. If
+ this is non-zero, we must search the string for pairs of '@'
+ and convert them to a single '@'. */
+ int embedded_at;
+};
+
+static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile);
+static char *RCS_getdatetrunk (RCSNode * rcs, const char *date,
+ int force_tag_match);
+static char *RCS_getdatebranch (RCSNode * rcs, const char *date,
+ const char *branch);
+static void rcsbuf_open (struct rcsbuffer *, FILE *fp,
+ const char *filename, unsigned long pos);
+static void rcsbuf_close (struct rcsbuffer *);
+static int rcsbuf_getkey (struct rcsbuffer *, char **keyp, char **valp);
+static int rcsbuf_getrevnum (struct rcsbuffer *, char **revp);
+static char *rcsbuf_fill (struct rcsbuffer *, char *ptr, char **keyp,
+ char **valp);
+static int rcsbuf_valcmp (struct rcsbuffer *);
+static char *rcsbuf_valcopy (struct rcsbuffer *, char *val, int polish,
+ size_t *lenp);
+static void rcsbuf_valpolish (struct rcsbuffer *, char *val, int polish,
+ size_t *lenp);
+static void rcsbuf_valpolish_internal (struct rcsbuffer *, char *to,
+ const char *from, size_t *lenp);
+static off_t rcsbuf_ftello (struct rcsbuffer *);
+static void rcsbuf_get_buffered (struct rcsbuffer *, char **datap,
+ size_t *lenp);
+static void rcsbuf_cache (RCSNode *, struct rcsbuffer *);
+static void rcsbuf_cache_close (void);
+static void rcsbuf_cache_open (RCSNode *, off_t, FILE **, struct rcsbuffer *);
+static int checkmagic_proc (Node *p, void *closure);
+static void do_branches (List * list, char *val);
+static void do_symbols (List * list, char *val);
+static void do_locks (List * list, char *val);
+static void free_rcsnode_contents (RCSNode *);
+static void free_rcsvers_contents (RCSVers *);
+static void rcsvers_delproc (Node * p);
+static char *RCS_getroot (RCSNode *, const char *);
+static char *RCS_getprevious (RCSNode *, const char *);
+static char *RCS_getnext (RCSNode *, const char *);
+static char *RCS_getorigin (RCSNode *, const char *);
+static char *RCS_getcommitid (RCSNode *, const char *, bool);
+static char *translate_tag (RCSNode *rcs, const char *, bool);
+static char *translate_symtag (RCSNode *, const char *);
+static char *RCS_addbranch (RCSNode *, const char *);
+static char *truncate_revnum (const char *);
+static char *printable_date (const char *);
+static char *escape_keyword_value (const char *, int *);
+static void expand_keywords (RCSNode *, RCSVers *, const char *,
+ const char *, size_t, enum kflag, char *,
+ size_t, char **, size_t *);
+static void cmp_file_buffer (void *, const char *, size_t);
+
+/* Routines for reading, parsing and writing RCS files. */
+static RCSVers *getdelta (struct rcsbuffer *, char *, char **, char **);
+static Deltatext *RCS_getdeltatext (RCSNode *, FILE *, struct rcsbuffer *);
+static void freedeltatext (Deltatext *);
+
+static void RCS_putadmin (RCSNode *, FILE *);
+static void RCS_putdtree (RCSNode *, char *, FILE *);
+static void RCS_putdesc (RCSNode *, FILE *);
+static void putdelta (RCSVers *, FILE *);
+static int putrcsfield_proc (Node *, void *);
+static int putsymbol_proc (Node *, void *);
+static void RCS_copydeltas (RCSNode *, FILE *, struct rcsbuffer *, FILE *,
+ Deltatext *, char *);
+static int count_delta_actions (Node *, void *);
+static void putdeltatext (FILE *, Deltatext *);
+
+static FILE *rcs_internal_lockfile (char *);
+static void rcs_internal_unlockfile (FILE *, char *);
+static char *rcs_lockfilename (const char *);
+
+/* The RCS file reading functions are called a lot, and they do some
+ string comparisons. This macro speeds things up a bit by skipping
+ the function call when the first characters are different. It
+ evaluates its arguments multiple times. */
+#define STREQ(a, b) (*(char *)(a) == *(char *)(b) && strcmp ((a), (b)) == 0)
+
+static char * getfullCVSname (char *, char **);
+
+/*
+ * We don't want to use isspace() from the C library because:
+ *
+ * 1. The definition of "whitespace" in RCS files includes ASCII
+ * backspace, but the C locale doesn't.
+ * 2. isspace is an very expensive function call in some implementations
+ * due to the addition of wide character support.
+ */
+static const char spacetab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, /* 0x00 - 0x0f
*/
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */
+};
+
+#define whitespace(c) (spacetab[(unsigned char)c] != 0)
+
+static char *rcs_lockfile = NULL;
+static int rcs_lockfd = -1;
+
+
+
+/*
+ * char *
+ * locate_rcs ( const char* file, const char *repository , int *inattic )
+ *
+ * Find an RCS file in the repository, case insensitively when the cased name
+ * doesn't exist, we are running as the server, and a client has asked us to
+ * ignore case.
+ *
+ * Most parts of CVS will want to rely instead on RCS_parse which calls this
+ * function and is called by recurse.c which then puts the result in useful
+ * places like the rcs field of struct file_info.
+ *
+ * INPUTS
+ *
+ * repository the repository (including the directory)
+ * file the filename within that directory (without RCSEXT).
+ * inattic NULL or a pointer to the output boolean
+ *
+ * OUTPUTS
+ *
+ * inattic If this input was non-null, the destination will be
+ * set to true if the file was found in the attic or
+ * false if not. If no RCS file is found, this value
+ * is undefined.
+ *
+ * RETURNS
+ *
+ * a newly-malloc'd array containing the absolute pathname of the RCS
+ * file that was found or NULL when none was found.
+ *
+ * ERRORS
+ *
+ * errno can be set by the return value of the final call to
+ * locate_file_in_dir(). This should resolve to the system's existence error
+ * value (sometime ENOENT) if the Attic directory did not exist and ENOENT if
+ * the Attic was found but no matching files were found in the Attic or its
+ * parent.
+ */
+static char *
+locate_rcs (const char *repository, const char *file, int *inattic)
+{
+ char *retval;
+
+ /* First, try to find the file as cased. */
+ retval = xmalloc (strlen (repository)
+ + sizeof (CVSATTIC)
+ + strlen (file)
+ + sizeof (RCSEXT)
+ + 3);
+ sprintf (retval, "%s/%s%s", repository, file, RCSEXT);
+ if (isreadable (retval))
+ {
+ if (inattic)
+ *inattic = 0;
+ return retval;
+ }
+ sprintf (retval, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+ if (isreadable (retval))
+ {
+ if (inattic)
+ *inattic = 1;
+ return retval;
+ }
+ free (retval);
+
+ return NULL;
+}
+
+
+
+/* A few generic thoughts on error handling, in particular the
+ printing of unexpected characters that we find in the RCS file
+ (that is, why we use '\x%x' rather than %c or some such).
+
+ * Avoiding %c means we don't have to worry about what is printable
+ and other such stuff. In error handling, often better to keep it
+ simple.
+
+ * Hex rather than decimal or octal because character set standards
+ tend to use hex.
+
+ * Saying "character 0x%x" might make it sound like we are printing
+ a file offset. So we use '\x%x'.
+
+ * Would be nice to print the offset within the file, but I can
+ imagine various portability hassles (in particular, whether
+ unsigned long is always big enough to hold file offsets). */
+
+/* Parse an rcsfile given a user file name and a repository. If there is
+ an error, we print an error message and return NULL. If the file
+ does not exist, we return NULL without printing anything (I'm not
+ sure this allows the caller to do anything reasonable, but it is
+ the current behavior). */
+RCSNode *
+RCS_parse (const char *file, const char *repos)
+{
+ RCSNode *rcs;
+ FILE *fp;
+ RCSNode *retval = NULL;
+ char *rcsfile;
+ int inattic;
+
+ /* We're creating a new RCSNode, so there is no hope of finding it
+ in the cache. */
+ rcsbuf_cache_close ();
+
+ if (!(rcsfile = locate_rcs (repos, file, &inattic)))
+ {
+ /* Handle the error cases */
+ }
+ else if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)))
+ {
+ rcs = RCS_parsercsfile_i (fp, rcsfile);
+ if (rcs)
+ {
+ rcs->flags |= VALID;
+ if (inattic)
+ rcs->flags |= INATTIC;
+ }
+
+ free (rcsfile);
+ retval = rcs;
+ }
+ else if (!existence_error (errno))
+ {
+ error (0, errno, "cannot open `%s'", rcsfile);
+ free (rcsfile);
+ }
+
+ return retval;
+}
+
+
+
+/*
+ * Parse a specific rcsfile.
+ */
+RCSNode *
+RCS_parsercsfile (const char *rcsfile)
+{
+ FILE *fp;
+ RCSNode *rcs;
+
+ /* We're creating a new RCSNode, so there is no hope of finding it
+ in the cache. */
+ rcsbuf_cache_close ();
+
+ /* open the rcsfile */
+ if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL)
+ {
+ error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
+ return NULL;
+ }
+
+ rcs = RCS_parsercsfile_i (fp, rcsfile);
+
+ return rcs;
+}
+
+
+
+/*
+ */
+static RCSNode *
+RCS_parsercsfile_i (FILE *fp, const char *rcsfile)
+{
+ RCSNode *rdata;
+ struct rcsbuffer rcsbuf;
+ char *key, *value;
+
+ /* make a node */
+ rdata = xmalloc (sizeof (RCSNode));
+ memset (rdata, 0, sizeof (RCSNode));
+ rdata->refcount = 1;
+ rdata->path = xstrdup (rcsfile);
+ rdata->print_path = xstrdup (primary_root_inverse_translate (rcsfile));
+
+ /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header.
+
+ Most cvs operations on the main branch don't need any more
+ information. Those that do call RCS_reparsercsfile to parse
+ the rest of the header and the deltas. */
+
+ rcsbuf_open (&rcsbuf, fp, rcsfile, 0);
+
+ if (! rcsbuf_getkey (&rcsbuf, &key, &value))
+ goto l_error;
+ if (STREQ (key, RCSDESC))
+ goto l_error;
+
+ if (STREQ (RCSHEAD, key) && value != NULL)
+ rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+
+ if (! rcsbuf_getkey (&rcsbuf, &key, &value))
+ goto l_error;
+ if (STREQ (key, RCSDESC))
+ goto l_error;
+
+ if (STREQ (RCSBRANCH, key) && value != NULL)
+ {
+ char *cp;
+
+ rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+ if ((numdots (rdata->branch) & 1) != 0)
+ {
+ /* turn it into a branch if it's a revision */
+ cp = strrchr (rdata->branch, '.');
+ *cp = '\0';
+ }
+ }
+
+ /* Look ahead for expand, stopping when we see desc or a revision
+ number. */
+ while (1)
+ {
+ char *cp;
+
+ if (STREQ (RCSEXPAND, key))
+ {
+ rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+ break;
+ }
+
+ for (cp = key;
+ (isdigit ((unsigned char)*cp) || *cp == '.') && *cp != '\0';
+ cp++)
+ /* do nothing */ ;
+ if (*cp == '\0')
+ break;
+
+ if (STREQ (RCSDESC, key))
+ break;
+
+ if (! rcsbuf_getkey (&rcsbuf, &key, &value))
+ break;
+ }
+
+ rdata->flags |= PARTIAL;
+
+ rcsbuf_cache (rdata, &rcsbuf);
+
+ return rdata;
+
+l_error:
+ error (0, 0, "`%s' does not appear to be a valid rcs file",
+ rcsfile);
+ rcsbuf_close (&rcsbuf);
+ freercsnode (&rdata);
+ fclose (fp);
+ return NULL;
+}
+
+
+
+/* Do the real work of parsing an RCS file.
+
+ On error, die with a fatal error; if it returns at all it was successful.
+
+ If PFP is NULL, close the file when done. Otherwise, leave it open
+ and store the FILE * in *PFP. */
+void
+RCS_reparsercsfile (RCSNode *rdata, FILE **pfp, struct rcsbuffer *rcsbufp)
+{
+ FILE *fp;
+ char *rcsfile;
+ struct rcsbuffer rcsbuf;
+ Node *q, *kv;
+ RCSVers *vnode;
+ int gotkey;
+ char *cp;
+ char *key, *value;
+
+ assert (rdata != NULL);
+ rcsfile = rdata->path;
+
+ rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf);
+
+ /* make a node */
+ /* This probably shouldn't be done until later: if a file has an
+ empty revision tree (which is permissible), rdata->versions
+ should be NULL. -twp */
+ rdata->versions = getlist ();
+
+ /*
+ * process all the special header information, break out when we get to
+ * the first revision delta
+ */
+ gotkey = 0;
+ for (;;)
+ {
+ /* get the next key/value pair */
+ if (!gotkey)
+ {
+ if (! rcsbuf_getkey (&rcsbuf, &key, &value))
+ {
+ error (1, 0, "`%s' does not appear to be a valid rcs file",
+ rcsfile);
+ }
+ }
+
+ gotkey = 0;
+
+ /* Skip head, branch and expand tags; we already have them. */
+ if (STREQ (key, RCSHEAD)
+ || STREQ (key, RCSBRANCH)
+ || STREQ (key, RCSEXPAND))
+ {
+ continue;
+ }
+
+ if (STREQ (key, "access"))
+ {
+ if (value != NULL)
+ {
+ /* We pass the POLISH parameter as 1 because
+ RCS_addaccess expects nothing but spaces. FIXME:
+ It would be easy and more efficient to change
+ RCS_addaccess. */
+ if (rdata->access)
+ {
+ error (0, 0,
+ "Duplicate `access' keyword found in RCS file.");
+ free (rdata->access);
+ }
+ rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
+ }
+ continue;
+ }
+
+ /* We always save lock information, so that we can handle
+ -kkvl correctly when checking out a file. */
+ if (STREQ (key, "locks"))
+ {
+ if (value != NULL)
+ {
+ if (rdata->locks_data)
+ {
+ error (0, 0,
+ "Duplicate `locks' keyword found in RCS file.");
+ free (rdata->locks_data);
+ }
+ rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+ }
+ if (! rcsbuf_getkey (&rcsbuf, &key, &value))
+ {
+ error (1, 0, "premature end of file reading %s", rcsfile);
+ }
+ if (STREQ (key, "strict") && value == NULL)
+ {
+ rdata->strict_locks = 1;
+ }
+ else
+ gotkey = 1;
+ continue;
+ }
+
+ if (STREQ (RCSSYMBOLS, key))
+ {
+ if (value != NULL)
+ {
+ if (rdata->symbols_data)
+ {
+ error (0, 0,
+ "Duplicate `%s' keyword found in RCS file.",
+ RCSSYMBOLS);
+ free (rdata->symbols_data);
+ }
+ rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+ }
+ continue;
+ }
+
+ /*
+ * check key for '.''s and digits (probably a rev) if it is a
+ * revision or `desc', we are done with the headers and are down to the
+ * revision deltas, so we break out of the loop
+ */
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
+ /* do nothing */ ;
+ /* Note that when comparing with RCSDATE, we are not massaging
+ VALUE from the string found in the RCS file. This is OK
+ since we know exactly what to expect. */
+ if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0)
+ break;
+
+ if (STREQ (key, RCSDESC))
+ break;
+
+ if (STREQ (key, "comment"))
+ {
+ if (rdata->comment)
+ {
+ error (0, 0,
+ "warning: duplicate key `%s' in RCS file `%s'",
+ key, rcsfile);
+ free (rdata->comment);
+ }
+ rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
+ continue;
+ }
+ if (rdata->other == NULL)
+ rdata->other = getlist ();
+ kv = getnode ();
+ kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
+ kv->key = xstrdup (key);
+ kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD, NULL);
+ if (addnode (rdata->other, kv) != 0)
+ {
+ error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
+ key, rcsfile);
+ freenode (kv);
+ }
+
+ /* if we haven't grabbed it yet, we didn't want it */
+ }
+
+ /* We got out of the loop, so we have the first part of the first
+ revision delta in KEY (the revision) and VALUE (the date key
+ and its value). This is what getdelta expects to receive. */
+
+ while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL)
+ {
+ /* get the node */
+ q = getnode ();
+ q->type = RCSVERS;
+ q->delproc = rcsvers_delproc;
+ q->data = vnode;
+ q->key = vnode->version;
+
+ /* add the nodes to the list */
+ if (addnode (rdata->versions, q) != 0)
+ {
+#if 0
+ purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
+ q->key, rcsfile);
+ freenode (q);
+#endif
+ }
+ }
+
+ /* Here KEY and VALUE are whatever caused getdelta to return NULL. */
+
+ if (STREQ (key, RCSDESC))
+ {
+ if (rdata->desc != NULL)
+ {
+ error (0, 0,
+ "warning: duplicate key `%s' in RCS file `%s'",
+ key, rcsfile);
+ free (rdata->desc);
+ }
+ rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
+ }
+
+ rdata->delta_pos = rcsbuf_ftello (&rcsbuf);
+
+ if (pfp == NULL)
+ rcsbuf_cache (rdata, &rcsbuf);
+ else
+ {
+ *pfp = fp;
+ *rcsbufp = rcsbuf;
+ }
+ rdata->flags &= ~PARTIAL;
+}
+
+
+
+/* Move RCS into or out of the Attic, depending on TOATTIC. If the
+ file is already in the desired place, return without doing
+ anything. At some point may want to think about how this relates
+ to RCS_rewrite but that is a bit hairy (if one wants renames to be
+ atomic, or that kind of thing). If there is an error, print a message
+ and return 1. On success, return 0. */
+int
+RCS_setattic (RCSNode *rcs, int toattic)
+{
+ char *newpath;
+ const char *p;
+ char *q;
+
+ /* Some systems aren't going to let us rename an open file. */
+ rcsbuf_cache_close ();
+
+ /* Could make the pathname computations in this file, and probably
+ in other parts of rcs.c too, easier if the REPOS and FILE
+ arguments to RCS_parse got stashed in the RCSNode. */
+
+ if (toattic)
+ {
+ mode_t omask;
+
+ if (rcs->flags & INATTIC)
+ return 0;
+
+ /* Example: rcs->path is "/foo/bar/baz,v". */
+ newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path);
+ strcpy (newpath + (p - rcs->path), CVSATTIC);
+
+ /* Create the Attic directory if it doesn't exist. */
+ omask = umask (cvsumask);
+ if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
+ error (0, errno, "cannot make directory %s", newpath);
+ (void) umask (omask);
+
+ strcat (newpath, "/");
+ strcat (newpath, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ int save_errno = errno;
+
+ /* The checks for isreadable look awfully fishy, but
+ I'm going to leave them here for now until I
+ can think harder about whether they take care of
+ some cases which should be handled somehow. */
+
+ if (isreadable (rcs->path) || !isreadable (newpath))
+ {
+ error (0, save_errno, "cannot rename %s to %s",
+ rcs->path, newpath);
+ free (newpath);
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ if (!(rcs->flags & INATTIC))
+ return 0;
+
+ newpath = xmalloc (strlen (rcs->path));
+
+ /* Example: rcs->path is "/foo/bar/Attic/baz,v". */
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path - 1);
+ newpath[p - rcs->path - 1] = '\0';
+ q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
+ assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
+ strcpy (q, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ error (0, errno, "failed to move `%s' out of the attic",
+ rcs->path);
+ free (newpath);
+ return 1;
+ }
+ }
+
+ free (rcs->path);
+ rcs->path = newpath;
+
+ return 0;
+}
+
+
+
+/*
+ * Fully parse the RCS file. Store all keyword/value pairs, fetch the
+ * log messages for each revision, and fetch add and delete counts for
+ * each revision (we could fetch the entire text for each revision,
+ * but the only caller, log_fileproc, doesn't need that information,
+ * so we don't waste the memory required to store it). The add and
+ * delete counts are stored on the OTHER field of the RCSVERSNODE
+ * structure, under the names ";add" and ";delete", so that we don't
+ * waste the memory space of extra fields in RCSVERSNODE for code
+ * which doesn't need this information.
+ */
+void
+RCS_fully_parse (RCSNode *rcs)
+{
+ FILE *fp;
+ struct rcsbuffer rcsbuf;
+
+ RCS_reparsercsfile (rcs, &fp, &rcsbuf);
+
+ while (1)
+ {
+ char *key, *value;
+ Node *vers;
+ RCSVers *vnode;
+
+ /* Rather than try to keep track of how much information we
+ have read, just read to the end of the file. */
+ if (!rcsbuf_getrevnum (&rcsbuf, &key))
+ break;
+
+ vers = findnode (rcs->versions, key);
+ if (vers == NULL)
+ error (1, 0,
+ "mismatch in rcs file %s between deltas and deltatexts (%s)",
+ rcs->print_path, key);
+
+ vnode = vers->data;
+
+ while (rcsbuf_getkey (&rcsbuf, &key, &value))
+ {
+ if (!STREQ (key, "text"))
+ {
+ Node *kv;
+
+ if (vnode->other == NULL)
+ vnode->other = getlist ();
+ kv = getnode ();
+ kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
+ kv->key = xstrdup (key);
+ kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
+ NULL);
+ if (addnode (vnode->other, kv) != 0)
+ {
+ error (0, 0,
+ "\
+warning: duplicate key `%s' in version `%s' of RCS file `%s'",
+ key, vnode->version, rcs->print_path);
+ freenode (kv);
+ }
+
+ continue;
+ }
+
+ if (!STREQ (vnode->version, rcs->head))
+ {
+ unsigned long add, del;
+ char buf[50];
+ Node *kv;
+
+ /* This is a change text. Store the add and delete
+ counts. */
+ add = 0;
+ del = 0;
+ if (value != NULL)
+ {
+ size_t vallen;
+ const char *cp;
+
+ rcsbuf_valpolish (&rcsbuf, value, 0, &vallen);
+ cp = value;
+ while (cp < value + vallen)
+ {
+ char op;
+ unsigned long count;
+
+ op = *cp++;
+ if (op != 'a' && op != 'd')
+ error (1, 0, "\
+unrecognized operation '\\x%x' in %s",
+ op, rcs->print_path);
+ (void) strtoul (cp, (char **) &cp, 10);
+ if (*cp++ != ' ')
+ error (1, 0, "space expected in %s revision %s",
+ rcs->print_path, vnode->version);
+ count = strtoul (cp, (char **) &cp, 10);
+ if (*cp++ != '\012')
+ error (1, 0, "linefeed expected in %s revision %s",
+ rcs->print_path, vnode->version);
+
+ if (op == 'd')
+ del += count;
+ else
+ {
+ add += count;
+ while (count != 0)
+ {
+ if (*cp == '\012')
+ --count;
+ else if (cp == value + vallen)
+ {
+ if (count != 1)
+ error (1, 0, "\
+premature end of value in %s revision %s",
+ rcs->print_path, vnode->version);
+ else
+ break;
+ }
+ ++cp;
+ }
+ }
+ }
+ }
+
+ sprintf (buf, "%lu", add);
+ kv = getnode ();
+ kv->type = RCSFIELD;
+ kv->key = xstrdup (";add");
+ kv->data = xstrdup (buf);
+ if (addnode (vnode->other, kv) != 0)
+ {
+ error (0, 0,
+ "\
+warning: duplicate key `%s' in version `%s' of RCS file `%s'",
+ key, vnode->version, rcs->print_path);
+ freenode (kv);
+ }
+
+ sprintf (buf, "%lu", del);
+ kv = getnode ();
+ kv->type = RCSFIELD;
+ kv->key = xstrdup (";delete");
+ kv->data = xstrdup (buf);
+ if (addnode (vnode->other, kv) != 0)
+ {
+ error (0, 0,
+ "\
+warning: duplicate key `%s' in version `%s' of RCS file `%s'",
+ key, vnode->version, rcs->print_path);
+ freenode (kv);
+ }
+ }
+
+ /* We have found the "text" key which ends the data for
+ this revision. Break out of the loop and go on to the
+ next revision. */
+ break;
+ }
+ }
+
+ rcsbuf_cache (rcs, &rcsbuf);
+}
+
+
+
+/*
+ * freercsnode - free up the info for an RCSNode
+ */
+void
+freercsnode (RCSNode **rnodep)
+{
+ if (rnodep == NULL || *rnodep == NULL)
+ return;
+
+ ((*rnodep)->refcount)--;
+ if ((*rnodep)->refcount != 0)
+ {
+ *rnodep = NULL;
+ return;
+ }
+ free ((*rnodep)->path);
+ free ((*rnodep)->print_path);
+ if ((*rnodep)->head != NULL)
+ free ((*rnodep)->head);
+ if ((*rnodep)->branch != NULL)
+ free ((*rnodep)->branch);
+ free_rcsnode_contents (*rnodep);
+ free (*rnodep);
+ *rnodep = NULL;
+}
+
+
+
+/*
+ * free_rcsnode_contents - free up the contents of an RCSNode without
+ * freeing the node itself, or the file name, or the head, or the
+ * path. This returns the RCSNode to the state it is in immediately
+ * after a call to RCS_parse.
+ */
+static void
+free_rcsnode_contents (RCSNode *rnode)
+{
+ dellist (&rnode->versions);
+ if (rnode->symbols != NULL)
+ dellist (&rnode->symbols);
+ if (rnode->symbols_data != NULL)
+ free (rnode->symbols_data);
+ if (rnode->expand != NULL)
+ free (rnode->expand);
+ if (rnode->other != NULL)
+ dellist (&rnode->other);
+ if (rnode->access != NULL)
+ free (rnode->access);
+ if (rnode->locks_data != NULL)
+ free (rnode->locks_data);
+ if (rnode->locks != NULL)
+ dellist (&rnode->locks);
+ if (rnode->comment != NULL)
+ free (rnode->comment);
+ if (rnode->desc != NULL)
+ free (rnode->desc);
+}
+
+
+
+/* free_rcsvers_contents -- free up the contents of an RCSVers node,
+ but also free the pointer to the node itself. */
+/* Note: The `hardlinks' list is *not* freed, since it is merely a
+ pointer into the `hardlist' structure (defined in hardlink.c), and
+ that structure is freed elsewhere in the program. */
+static void
+free_rcsvers_contents (RCSVers *rnode)
+{
+ if (rnode->branches != NULL)
+ dellist (&rnode->branches);
+ if (rnode->date != NULL)
+ free (rnode->date);
+ if (rnode->next != NULL)
+ free (rnode->next);
+ if (rnode->author != NULL)
+ free (rnode->author);
+ if (rnode->state != NULL)
+ free (rnode->state);
+ if (rnode->other != NULL)
+ dellist (&rnode->other);
+ if (rnode->other_delta != NULL)
+ dellist (&rnode->other_delta);
+ if (rnode->text != NULL)
+ freedeltatext (rnode->text);
+ free (rnode);
+}
+
+
+
+/*
+ * rcsvers_delproc - free up an RCSVers type node
+ */
+static void
+rcsvers_delproc (Node *p)
+{
+ free_rcsvers_contents (p->data);
+}
+
+
+
+/* These functions retrieve keys and values from an RCS file using a
+ buffer. We use this somewhat complex approach because it turns out
+ that for many common operations, CVS spends most of its time
+ reading keys, so it's worth doing some fairly hairy optimization. */
+
+/* The number of bytes we try to read each time we need more data. */
+
+#define RCSBUF_BUFSIZE (8192)
+
+/* The buffer we use to store data. This grows as needed. */
+
+static char *rcsbuf_buffer = NULL;
+static size_t rcsbuf_buffer_size = 0;
+
+/* Whether rcsbuf_buffer is in use. This is used as a sanity check. */
+
+static int rcsbuf_inuse;
+
+/* Set up to start gathering keys and values from an RCS file. This
+ initializes RCSBUF. */
+
+static void
+rcsbuf_open (struct rcsbuffer *rcsbuf, FILE *fp, const char *filename,
+ long unsigned int pos)
+{
+ if (rcsbuf_inuse)
+ error (1, 0, "rcsbuf_open: internal error");
+ rcsbuf_inuse = 1;
+
+#ifdef HAVE_MMAP
+ {
+ /* When we have mmap, it is much more efficient to let the system do the
+ * buffering and caching for us
+ */
+ struct stat fs;
+ size_t mmap_off = 0;
+
+ if ( fstat (fileno(fp), &fs) < 0 )
+ error ( 1, errno, "Could not stat RCS archive %s for mapping",
filename );
+
+ if (pos)
+ {
+ size_t ps = getpagesize ();
+ mmap_off = ( pos / ps ) * ps;
+ }
+
+ /* Map private here since this particular buffer is read only */
+ rcsbuf_buffer = mmap ( NULL, fs.st_size - mmap_off,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fileno(fp), mmap_off );
+ if ( rcsbuf_buffer == NULL || rcsbuf_buffer == MAP_FAILED )
+ error ( 1, errno, "Could not map memory to RCS archive %s",
filename );
+
+ rcsbuf_buffer_size = fs.st_size - mmap_off;
+ rcsbuf->ptr = rcsbuf_buffer + pos - mmap_off;
+ rcsbuf->ptrend = rcsbuf_buffer + fs.st_size - mmap_off;
+ rcsbuf->pos = mmap_off;
+ }
+#else /* !HAVE_MMAP */
+ if (rcsbuf_buffer_size < RCSBUF_BUFSIZE)
+ expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE);
+
+ rcsbuf->ptr = rcsbuf_buffer;
+ rcsbuf->ptrend = rcsbuf_buffer;
+ rcsbuf->pos = pos;
+#endif /* HAVE_MMAP */
+ rcsbuf->fp = fp;
+ rcsbuf->filename = filename;
+ rcsbuf->vlen = 0;
+ rcsbuf->at_string = 0;
+ rcsbuf->embedded_at = 0;
+}
+
+
+
+/* Stop gathering keys from an RCS file. */
+static void
+rcsbuf_close (struct rcsbuffer *rcsbuf)
+{
+ if (! rcsbuf_inuse)
+ error (1, 0, "rcsbuf_close: internal error");
+#ifdef HAVE_MMAP
+ munmap ( rcsbuf_buffer, rcsbuf_buffer_size );
+#endif
+ rcsbuf_inuse = 0;
+}
+
+
+
+/* Read a key/value pair from an RCS file. This sets *KEYP to point
+ to the key, and *VALUEP to point to the value. A missing or empty
+ value is indicated by setting *VALUEP to NULL.
+
+ This function returns 1 on success, or 0 on EOF. If there is an
+ error reading the file, or an EOF in an unexpected location, it
+ gives a fatal error.
+
+ This sets *KEYP and *VALUEP to point to storage managed by
+ rcsbuf_getkey. Moreover, *VALUEP has not been massaged from the
+ RCS format: it may contain embedded whitespace and embedded '@'
+ characters. Call rcsbuf_valcopy or rcsbuf_valpolish to do
+ appropriate massaging. */
+
+/* Note that the extreme hair in rcsbuf_getkey is because profiling
+ statistics show that it was worth it. */
+static int
+rcsbuf_getkey (struct rcsbuffer *rcsbuf, char **keyp, char **valp)
+{
+ register const char * const my_spacetab = spacetab;
+ register char *ptr, *ptrend;
+ char c;
+
+#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0)
+
+ rcsbuf->vlen = 0;
+ rcsbuf->at_string = 0;
+ rcsbuf->embedded_at = 0;
+
+ ptr = rcsbuf->ptr;
+ ptrend = rcsbuf->ptrend;
+
+ /* Sanity check. */
+ assert (ptr >= rcsbuf_buffer && ptr <= rcsbuf_buffer + rcsbuf_buffer_size);
+ assert (ptrend >= rcsbuf_buffer && ptrend <= rcsbuf_buffer +
rcsbuf_buffer_size);
+
+#ifndef HAVE_MMAP
+ /* If the pointer is more than RCSBUF_BUFSIZE bytes into the
+ buffer, move back to the start of the buffer. This keeps the
+ buffer from growing indefinitely. */
+ if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE)
+ {
+ int len;
+
+ len = ptrend - ptr;
+
+ /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes
+ at a time, so we can't have more bytes than that past PTR. */
+ assert (len <= RCSBUF_BUFSIZE);
+
+ /* Update the POS field, which holds the file offset of the
+ first byte in the RCSBUF_BUFFER buffer. */
+ rcsbuf->pos += ptr - rcsbuf_buffer;
+
+ memcpy (rcsbuf_buffer, ptr, len);
+ ptr = rcsbuf_buffer;
+ ptrend = ptr + len;
+ rcsbuf->ptrend = ptrend;
+ }
+#endif /* HAVE_MMAP */
+
+ /* Skip leading whitespace. */
+
+ while (1)
+ {
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
+ if (ptr == NULL)
+ return 0;
+ ptrend = rcsbuf->ptrend;
+ }
+
+ c = *ptr;
+ if (! my_whitespace (c))
+ break;
+
+ ++ptr;
+ }
+
+ /* We've found the start of the key. */
+
+ *keyp = ptr;
+
+ if (c != ';')
+ {
+ while (1)
+ {
+ ++ptr;
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
+ if (ptr == NULL)
+ error (1, 0, "EOF in key in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+ c = *ptr;
+ if (c == ';' || my_whitespace (c))
+ break;
+ }
+ }
+
+ /* Here *KEYP points to the key in the buffer, C is the character
+ we found at the of the key, and PTR points to the location in
+ the buffer where we found C. We must set *PTR to \0 in order
+ to terminate the key. If the key ended with ';', then there is
+ no value. */
+
+ *ptr = '\0';
+ ++ptr;
+
+ if (c == ';')
+ {
+ *valp = NULL;
+ rcsbuf->ptr = ptr;
+ return 1;
+ }
+
+ /* C must be whitespace. Skip whitespace between the key and the
+ value. If we find ';' now, there is no value. */
+
+ while (1)
+ {
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
+ if (ptr == NULL)
+ error (1, 0, "EOF while looking for value in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+ c = *ptr;
+ if (c == ';')
+ {
+ *valp = NULL;
+ rcsbuf->ptr = ptr + 1;
+ return 1;
+ }
+ if (! my_whitespace (c))
+ break;
+ ++ptr;
+ }
+
+ /* Now PTR points to the start of the value, and C is the first
+ character of the value. */
+
+ if (c != '@')
+ *valp = ptr;
+ else
+ {
+ char *pat;
+ size_t vlen;
+
+ /* Optimize the common case of a value composed of a single
+ '@' string. */
+
+ rcsbuf->at_string = 1;
+
+ ++ptr;
+
+ *valp = ptr;
+
+ while (1)
+ {
+ while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
+ {
+ /* Note that we pass PTREND as the PTR value to
+ rcsbuf_fill, so that we will wind up setting PTR to
+ the location corresponding to the old PTREND, so
+ that we don't search the same bytes again. */
+ ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
+ if (ptr == NULL)
+ error (1, 0,
+ "EOF while looking for end of string in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+
+ /* Handle the special case of an '@' right at the end of
+ the known bytes. */
+ if (pat + 1 >= ptrend)
+ {
+ /* Note that we pass PAT, not PTR, here. */
+ pat = rcsbuf_fill (rcsbuf, pat, keyp, valp);
+ if (pat == NULL)
+ {
+ /* EOF here is OK; it just means that the last
+ character of the file was an '@' terminating a
+ value for a key type which does not require a
+ trailing ';'. */
+ pat = rcsbuf->ptrend - 1;
+
+ }
+ ptrend = rcsbuf->ptrend;
+
+ /* Note that the value of PTR is bogus here. This is
+ OK, because we don't use it. */
+ }
+
+ if (pat + 1 >= ptrend || pat[1] != '@')
+ break;
+
+ /* We found an '@' pair in the string. Keep looking. */
+ ++rcsbuf->embedded_at;
+ ptr = pat + 2;
+ }
+
+ /* Here PAT points to the final '@' in the string. */
+
+ *pat = '\0';
+
+ vlen = pat - *valp;
+ if (vlen == 0)
+ *valp = NULL;
+ rcsbuf->vlen = vlen;
+
+ ptr = pat + 1;
+ }
+
+ /* Certain keywords only have a '@' string. If there is no '@'
+ string, then the old getrcskey function assumed that they had
+ no value, and we do the same. */
+
+ {
+ char *k;
+
+ k = *keyp;
+ if (STREQ (k, RCSDESC)
+ || STREQ (k, "text")
+ || STREQ (k, "log"))
+ {
+ if (c != '@')
+ *valp = NULL;
+ rcsbuf->ptr = ptr;
+ return 1;
+ }
+ }
+
+ /* If we've already gathered a '@' string, try to skip whitespace
+ and find a ';'. */
+ if (c == '@')
+ {
+ while (1)
+ {
+ char n;
+
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
+ if (ptr == NULL)
+ error (1, 0, "EOF in value in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+ n = *ptr;
+ if (n == ';')
+ {
+ /* We're done. We already set everything up for this
+ case above. */
+ rcsbuf->ptr = ptr + 1;
+ return 1;
+ }
+ if (! my_whitespace (n))
+ break;
+ ++ptr;
+ }
+
+ /* The value extends past the '@' string. We need to undo the
+ '@' stripping done in the default case above. This
+ case never happens in a plain RCS file, but it can happen
+ if user defined phrases are used. */
+ ((*valp)--)[rcsbuf->vlen++] = '@';
+ }
+
+ /* Here we have a value which is not a simple '@' string. We need
+ to gather up everything until the next ';', including any '@'
+ strings. *VALP points to the start of the value. If
+ RCSBUF->VLEN is not zero, then we have already read an '@'
+ string, and PTR points to the data following the '@' string.
+ Otherwise, PTR points to the start of the value. */
+
+ while (1)
+ {
+ char *start, *psemi, *pat;
+
+ /* Find the ';' which must end the value. */
+ start = ptr;
+ while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL)
+ {
+ int slen;
+
+ /* Note that we pass PTREND as the PTR value to
+ rcsbuf_fill, so that we will wind up setting PTR to the
+ location corresponding to the old PTREND, so that we
+ don't search the same bytes again. */
+ slen = start - *valp;
+ ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
+ if (ptr == NULL)
+ error (1, 0, "EOF in value in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ start = *valp + slen;
+ ptrend = rcsbuf->ptrend;
+ }
+
+ /* See if there are any '@' strings in the value. */
+ pat = memchr (start, '@', psemi - start);
+
+ if (pat == NULL)
+ {
+ size_t vlen;
+
+ /* We're done with the value. Trim any trailing
+ whitespace. */
+
+ rcsbuf->ptr = psemi + 1;
+
+ start = *valp;
+ while (psemi > start && my_whitespace (psemi[-1]))
+ --psemi;
+ *psemi = '\0';
+
+ vlen = psemi - start;
+ if (vlen == 0)
+ *valp = NULL;
+ rcsbuf->vlen = vlen;
+
+ return 1;
+ }
+
+ /* We found an '@' string in the value. We set RCSBUF->AT_STRING
+ and RCSBUF->EMBEDDED_AT to indicate that we won't be able to
+ compress whitespace correctly for this type of value.
+ Since this type of value never arises in a normal RCS file,
+ this should not be a big deal. It means that if anybody
+ adds a phrase which can have both an '@' string and regular
+ text, they will have to handle whitespace compression
+ themselves. */
+
+ rcsbuf->at_string = 1;
+ rcsbuf->embedded_at = -1;
+
+ ptr = pat + 1;
+
+ while (1)
+ {
+ while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
+ {
+ /* Note that we pass PTREND as the PTR value to
+ rcsbuff_fill, so that we will wind up setting PTR
+ to the location corresponding to the old PTREND, so
+ that we don't search the same bytes again. */
+ ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
+ if (ptr == NULL)
+ error (1, 0,
+ "EOF while looking for end of string in RCS file %s",
+ primary_root_inverse_translate (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",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+
+ if (pat[1] != '@')
+ break;
+
+ /* We found an '@' pair in the string. Keep looking. */
+ ptr = pat + 2;
+ }
+
+ /* Here PAT points to the final '@' in the string. */
+ ptr = pat + 1;
+ }
+
+#undef my_whitespace
+}
+
+
+
+/* Read an RCS revision number from an RCS file. This sets *REVP to
+ point to the revision number; it will point to space that is
+ managed by the rcsbuf functions, and is only good until the next
+ call to rcsbuf_getkey or rcsbuf_getrevnum.
+
+ This function returns 1 on success, or 0 on EOF. If there is an
+ error reading the file, or an EOF in an unexpected location, it
+ gives a fatal error. */
+static int
+rcsbuf_getrevnum (struct rcsbuffer *rcsbuf, char **revp)
+{
+ char *ptr, *ptrend;
+ char c;
+
+ ptr = rcsbuf->ptr;
+ ptrend = rcsbuf->ptrend;
+
+ *revp = NULL;
+
+ /* Skip leading whitespace. */
+
+ while (1)
+ {
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
+ if (ptr == NULL)
+ return 0;
+ ptrend = rcsbuf->ptrend;
+ }
+
+ c = *ptr;
+ if (! whitespace (c))
+ break;
+
+ ++ptr;
+ }
+
+ if (! isdigit ((unsigned char) c) && c != '.')
+ error (1, 0,
+ "\
+unexpected '\\x%x' reading revision number in RCS file %s",
+ c, primary_root_inverse_translate (rcsbuf->filename));
+
+ *revp = ptr;
+
+ do
+ {
+ ++ptr;
+ if (ptr >= ptrend)
+ {
+ ptr = rcsbuf_fill (rcsbuf, ptr, revp, NULL);
+ if (ptr == NULL)
+ error (1, 0,
+ "unexpected EOF reading revision number in RCS file %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+ ptrend = rcsbuf->ptrend;
+ }
+
+ c = *ptr;
+ }
+ while (isdigit ((unsigned char) c) || c == '.');
+
+ if (! whitespace (c))
+ error (1, 0, "\
+unexpected '\\x%x' reading revision number in RCS file %s",
+ c, primary_root_inverse_translate (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 (struct rcsbuffer *rcsbuf, char *ptr, char **keyp, char **valp)
+{
+#ifdef HAVE_MMAP
+ return NULL;
+#else /* HAVE_MMAP */
+ int got;
+
+ if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size)
+ {
+ int poff, peoff, koff, voff;
+
+ poff = ptr - rcsbuf_buffer;
+ peoff = rcsbuf->ptrend - rcsbuf_buffer;
+ koff = keyp == NULL ? 0 : *keyp - rcsbuf_buffer;
+ voff = valp == NULL ? 0 : *valp - rcsbuf_buffer;
+
+ expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size,
+ rcsbuf_buffer_size + RCSBUF_BUFSIZE);
+
+ ptr = rcsbuf_buffer + poff;
+ rcsbuf->ptrend = rcsbuf_buffer + peoff;
+ if (keyp != NULL)
+ *keyp = rcsbuf_buffer + koff;
+ if (valp != NULL)
+ *valp = rcsbuf_buffer + voff;
+ }
+
+ got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp);
+ if (got == 0)
+ {
+ if (ferror (rcsbuf->fp))
+ error (1, errno, "cannot read %s", rcsbuf->filename);
+ return NULL;
+ }
+
+ rcsbuf->ptrend += got;
+
+ return ptr;
+#endif /* HAVE_MMAP */
+}
+
+
+
+/* Test whether the last value returned by rcsbuf_getkey is a composite
+ value or not. */
+static int
+rcsbuf_valcmp (struct rcsbuffer *rcsbuf)
+{
+ return rcsbuf->at_string && rcsbuf->embedded_at < 0;
+}
+
+
+
+/* Copy the value VAL returned by rcsbuf_getkey into a memory buffer,
+ returning the memory buffer. Polish the value like
+ rcsbuf_valpolish, q.v. */
+static char *
+rcsbuf_valcopy (struct rcsbuffer *rcsbuf, char *val, int polish, size_t *lenp)
+{
+ size_t vlen;
+ int embedded_at;
+ char *ret;
+
+ if (val == NULL)
+ {
+ if (lenp != NULL)
+ *lenp = 0;
+ return NULL;
+ }
+
+ vlen = rcsbuf->vlen;
+ embedded_at = rcsbuf->embedded_at < 0 ? 0 : rcsbuf->embedded_at;
+
+ ret = xmalloc (vlen - embedded_at + 1);
+
+ if (rcsbuf->at_string ? embedded_at == 0 : ! polish)
+ {
+ /* No special action to take. */
+ memcpy (ret, val, vlen + 1);
+ if (lenp != NULL)
+ *lenp = vlen;
+ return ret;
+ }
+
+ rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp);
+ return ret;
+}
+
+
+
+/* Polish the value VAL returned by rcsbuf_getkey. The POLISH
+ parameter is non-zero if multiple embedded whitespace characters
+ should be compressed into a single whitespace character. Note that
+ leading and trailing whitespace was already removed by
+ rcsbuf_getkey. Within an '@' string, pairs of '@' characters are
+ compressed into a single '@' character regardless of the value of
+ POLISH. If LENP is not NULL, set *LENP to the length of the value. */
+static void
+rcsbuf_valpolish (struct rcsbuffer *rcsbuf, char *val, int polish,
+ size_t *lenp)
+{
+ if (val == NULL)
+ {
+ if (lenp != NULL)
+ *lenp= 0;
+ return;
+ }
+
+ if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish)
+ {
+ /* No special action to take. */
+ if (lenp != NULL)
+ *lenp = rcsbuf->vlen;
+ return;
+ }
+
+ rcsbuf_valpolish_internal (rcsbuf, val, val, lenp);
+}
+
+
+
+/* Internal polishing routine, called from rcsbuf_valcopy and
+ rcsbuf_valpolish. */
+static void
+rcsbuf_valpolish_internal (struct rcsbuffer *rcsbuf, char *to,
+ const char *from, size_t *lenp)
+{
+ size_t len;
+
+ len = rcsbuf->vlen;
+
+ if (! rcsbuf->at_string)
+ {
+ char *orig_to;
+ size_t clen;
+
+ orig_to = to;
+
+ for (clen = len; clen > 0; ++from, --clen)
+ {
+ char c;
+
+ c = *from;
+ if (whitespace (c))
+ {
+ /* Note that we know that clen can not drop to zero
+ while we have whitespace, because we know there is
+ no trailing whitespace. */
+ while (whitespace (from[1]))
+ {
+ ++from;
+ --clen;
+ }
+ c = ' ';
+ }
+ *to++ = c;
+ }
+
+ *to = '\0';
+
+ if (lenp != NULL)
+ *lenp = to - orig_to;
+ }
+ else
+ {
+ const char *orig_from;
+ char *orig_to;
+ int embedded_at;
+ size_t clen;
+
+ orig_from = from;
+ orig_to = to;
+
+ embedded_at = rcsbuf->embedded_at;
+ assert (embedded_at > 0);
+
+ if (lenp != NULL)
+ *lenp = len - embedded_at;
+
+ for (clen = len; clen > 0; ++from, --clen)
+ {
+ char c;
+
+ c = *from;
+ *to++ = c;
+ if (c == '@')
+ {
+ ++from;
+
+ /* Sanity check.
+ *
+ * FIXME: I restored this to an abort from an assert based on
+ * advice from Larry Jones that asserts should not be used to
+ * confirm the validity of an RCS file... This leaves two
+ * issues here: 1) I am uncertain that the fact that we will
+ * only find double '@'s hasn't already been confirmed; and:
+ * 2) If this is the proper place to spot the error in the RCS
+ * file, then we should print a much clearer error here for the
+ * user!!!!!!!
+ *
+ * - DRP
+ */
+ if (*from != '@' || clen == 0)
+ abort ();
+
+ --clen;
+
+ --embedded_at;
+ if (embedded_at == 0)
+ {
+ /* We've found all the embedded '@' characters.
+ We can just memcpy the rest of the buffer after
+ this '@' character. */
+ if (orig_to != orig_from)
+ memcpy (to, from + 1, clen - 1);
+ else
+ memmove (to, from + 1, clen - 1);
+ from += clen;
+ to += clen - 1;
+ break;
+ }
+ }
+ }
+
+ /* Sanity check. */
+ assert (from == orig_from + len
+ && to == orig_to + (len - rcsbuf->embedded_at));
+
+ *to = '\0';
+ }
+}
+
+
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+
+/* Copy the next word from the value VALP returned by rcsbuf_getkey into a
+ memory buffer, updating VALP and returning the memory buffer. Return
+ NULL when there are no more words. */
+
+static char *
+rcsbuf_valword (struct rcsbuffer *rcsbuf, char **valp)
+{
+ register const char * const my_spacetab = spacetab;
+ register char *ptr, *pat;
+ char c;
+
+# define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0)
+
+ if (*valp == NULL)
+ return NULL;
+
+ for (ptr = *valp; my_whitespace (*ptr); ++ptr) ;
+ if (*ptr == '\0')
+ {
+ assert (ptr - *valp == rcsbuf->vlen);
+ *valp = NULL;
+ rcsbuf->vlen = 0;
+ return NULL;
+ }
+
+ /* PTR now points to the start of a value. Find out whether it is
+ a num, an id, a string or a colon. */
+ c = *ptr;
+ if (c == ':')
+ {
+ rcsbuf->vlen -= ++ptr - *valp;
+ *valp = ptr;
+ return xstrdup (":");
+ }
+
+ if (c == '@')
+ {
+ int embedded_at = 0;
+ size_t vlen;
+
+ pat = ++ptr;
+ while ((pat = strchr (pat, '@')) != NULL)
+ {
+ if (pat[1] != '@')
+ break;
+ ++embedded_at;
+ pat += 2;
+ }
+
+ /* Here PAT points to the final '@' in the string. */
+ *pat++ = '\0';
+ assert (rcsbuf->at_string);
+ vlen = rcsbuf->vlen - (pat - *valp);
+ rcsbuf->vlen = pat - ptr - 1;
+ rcsbuf->embedded_at = embedded_at;
+ ptr = rcsbuf_valcopy (rcsbuf, ptr, 0, NULL);
+ *valp = pat;
+ rcsbuf->vlen = vlen;
+ if (strchr (pat, '@') == NULL)
+ rcsbuf->at_string = 0;
+ else
+ rcsbuf->embedded_at = -1;
+ return ptr;
+ }
+
+ /* *PTR is neither `:', `;' nor `@', so it should be the start of a num
+ or an id. Make sure it is not another special character. */
+ if (c == '$' || c == '.' || c == ',')
+ error (1, 0, "invalid special character in RCS field in %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+
+ pat = ptr;
+ while (1)
+ {
+ /* Legitimate ID characters are digits, dots and any `graphic
+ printing character that is not a special.' This test ought
+ to do the trick. */
+ c = *++pat;
+ if (!isprint ((unsigned char) c) ||
+ c == ';' || c == '$' || c == ',' || c == '@' || c == ':')
+ break;
+ }
+
+ /* PAT points to the last non-id character in this word, and C is
+ the character in its memory cell. Check to make sure that it
+ is a legitimate word delimiter -- whitespace or end. */
+ if (c != '\0' && !my_whitespace (c))
+ error (1, 0, "invalid special character in RCS field in %s",
+ primary_root_inverse_translate (rcsbuf->filename));
+
+ *pat = '\0';
+ rcsbuf->vlen -= pat - *valp;
+ *valp = pat;
+ return xstrdup (ptr);
+
+# undef my_whitespace
+}
+
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+
+
+
+/* Return the current position of an rcsbuf. */
+static off_t
+rcsbuf_ftello (struct rcsbuffer *rcsbuf)
+{
+ return rcsbuf->pos + rcsbuf->ptr - rcsbuf_buffer;
+}
+
+
+
+/* Return a pointer to any data buffered for RCSBUF, along with the
+ length. */
+static void
+rcsbuf_get_buffered (struct rcsbuffer *rcsbuf, char **datap, size_t *lenp)
+{
+ *datap = rcsbuf->ptr;
+ *lenp = rcsbuf->ptrend - rcsbuf->ptr;
+}
+
+
+
+/* CVS optimizes by quickly reading some header information from a
+ file. If it decides it needs to do more with the file, it reopens
+ it. We speed that up here by maintaining a cache of a single open
+ file, to save the time it takes to reopen the file in the common
+ case. */
+static RCSNode *cached_rcs;
+static struct rcsbuffer cached_rcsbuf;
+
+/* Cache RCS and RCSBUF. This takes responsibility for closing
+ RCSBUF->FP. */
+static void
+rcsbuf_cache (RCSNode *rcs, struct rcsbuffer *rcsbuf)
+{
+ if (cached_rcs != NULL)
+ rcsbuf_cache_close ();
+ cached_rcs = rcs;
+ ++rcs->refcount;
+ cached_rcsbuf = *rcsbuf;
+}
+
+
+
+/* If there is anything in the cache, close it. */
+static void
+rcsbuf_cache_close (void)
+{
+ if (cached_rcs != NULL)
+ {
+ rcsbuf_close (&cached_rcsbuf);
+ if (fclose (cached_rcsbuf.fp) != 0)
+ error (0, errno, "cannot close %s", cached_rcsbuf.filename);
+ freercsnode (&cached_rcs);
+ cached_rcs = NULL;
+ }
+}
+
+
+
+/* Open an rcsbuffer for RCS, getting it from the cache if possible.
+ Set *FPP to the file, and *RCSBUFP to the rcsbuf. The file should
+ be put at position POS. */
+static void
+rcsbuf_cache_open (RCSNode *rcs, off_t pos, FILE **pfp,
+ struct rcsbuffer *prcsbuf)
+{
+#ifndef HAVE_MMAP
+ if (cached_rcs == rcs)
+ {
+ if (rcsbuf_ftello (&cached_rcsbuf) != pos)
+ {
+ if (fseeko (cached_rcsbuf.fp, pos, SEEK_SET) != 0)
+ error (1, 0, "cannot fseeko RCS file %s",
+ cached_rcsbuf.filename);
+ cached_rcsbuf.ptr = rcsbuf_buffer;
+ cached_rcsbuf.ptrend = rcsbuf_buffer;
+ cached_rcsbuf.pos = pos;
+ }
+ *pfp = cached_rcsbuf.fp;
+
+ /* When RCS_parse opens a file using fopen_case, it frees the
+ filename which we cached in CACHED_RCSBUF and stores a new
+ file name in RCS->PATH. We avoid problems here by always
+ copying the filename over. FIXME: This is hackish. */
+ cached_rcsbuf.filename = rcs->path;
+
+ *prcsbuf = cached_rcsbuf;
+
+ cached_rcs = NULL;
+
+ /* Removing RCS from the cache removes a reference to it. */
+ --rcs->refcount;
+ if (rcs->refcount <= 0)
+ error (1, 0, "rcsbuf_cache_open: internal error");
+ }
+ else
+ {
+#endif /* ifndef HAVE_MMAP */
+ /* FIXME: If these routines can be rewritten to not write to the
+ * rcs file buffer, there would be a considerably larger memory savings
+ * from using mmap since the shared file would never need be copied to
+ * process memory.
+ *
+ * If this happens, cached mmapped buffers would be usable, but don't
+ * forget to make sure rcs->pos < pos here...
+ */
+ if (cached_rcs != NULL)
+ rcsbuf_cache_close ();
+
+ *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ);
+ if (*pfp == NULL)
+ error (1, 0, "unable to reopen `%s'", rcs->path);
+#ifndef HAVE_MMAP
+ if (pos != 0)
+ {
+ if (fseeko (*pfp, pos, SEEK_SET) != 0)
+ error (1, 0, "cannot fseeko RCS file %s", rcs->path);
+ }
+#endif /* ifndef HAVE_MMAP */
+ rcsbuf_open (prcsbuf, *pfp, rcs->path, pos);
+#ifndef HAVE_MMAP
+ }
+#endif /* ifndef HAVE_MMAP */
+}
+
+
+
+/*
+ * process the symbols list of the rcs file
+ */
+static void
+do_symbols (List *list, char *val)
+{
+ Node *p;
+ char *cp = val;
+ char *tag, *rev;
+
+ assert (cp);
+
+ for (;;)
+ {
+ /* skip leading whitespace */
+ while (whitespace (*cp))
+ cp++;
+
+ /* if we got to the end, we are done */
+ if (*cp == '\0')
+ break;
+
+ /* split it up into tag and rev */
+ tag = cp;
+ cp = strchr (cp, ':');
+ *cp++ = '\0';
+ rev = cp;
+ while (!whitespace (*cp) && *cp != '\0')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* make a new node and add it to the list */
+ p = getnode ();
+ p->key = xstrdup (tag);
+ p->data = xstrdup (rev);
+ (void) addnode (list, p);
+ }
+}
+
+
+
+/*
+ * process the locks list of the rcs file
+ * Like do_symbols, but hash entries are keyed backwards: i.e.
+ * an entry like `user:rev' is keyed on REV rather than on USER.
+ */
+static void
+do_locks (List *list, char *val)
+{
+ Node *p;
+ char *cp = val;
+ char *user, *rev;
+
+ assert (cp);
+
+ for (;;)
+ {
+ /* skip leading whitespace */
+ while (whitespace (*cp))
+ cp++;
+
+ /* if we got to the end, we are done */
+ if (*cp == '\0')
+ break;
+
+ /* split it up into user and rev */
+ user = cp;
+ cp = strchr (cp, ':');
+ *cp++ = '\0';
+ rev = cp;
+ while (!whitespace (*cp) && *cp != '\0')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* make a new node and add it to the list */
+ p = getnode ();
+ p->key = xstrdup (rev);
+ p->data = xstrdup (user);
+ (void) addnode (list, p);
+ }
+}
+
+
+
+/*
+ * process the branches list of a revision delta
+ */
+static void
+do_branches (List *list, char *val)
+{
+ Node *p;
+ char *cp = val;
+ char *branch;
+
+ for (;;)
+ {
+ /* skip leading whitespace */
+ while (whitespace (*cp))
+ cp++;
+
+ /* if we got to the end, we are done */
+ if (*cp == '\0')
+ break;
+
+ /* find the end of this branch */
+ branch = cp;
+ while (!whitespace (*cp) && *cp != '\0')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* make a new node and add it to the list */
+ p = getnode ();
+ p->key = xstrdup (branch);
+ (void) addnode (list, p);
+ }
+}
+
+
+
+/* Return true if TAG does not start with a number or
+ * does not entirely consist of numbers and dots.
+ *
+ * (N.N.N.N.prev is now a valid tag).
+ */
+static inline bool
+is_symbolic (const char *tag)
+{
+ assert (tag && *tag);
+ if (!isdigit (*tag)) return true;
+ for (tag++; *tag; tag++)
+ if (!(isdigit (*tag) || *tag == '.')) return true;
+ if (*--tag == '.') return true;
+ return false;
+}
+
+bool
+RCS_is_symbolic (const char *tag)
+{
+ return is_symbolic (tag);
+}
+
+bool
+RCS_is_relative (const char *tag)
+{
+ if (tag && *tag == '.')
+ {
+ int len;
+ char *next;
+ if (++tag)
+ {
+ next = strchr (tag, '.');
+ if (next)
+ len = next - tag;
+ else
+ len = strlen (tag);
+ if (!strncmp (tag, TAG_DOTHEAD, len)
+ || !strncmp (tag, TAG_DOTBASE, len)
+ || !strncmp (tag, TAG_PREVIOUS, len)
+ || !strncmp (tag, TAG_NEXT, len)
+ || !strncmp (tag, TAG_ROOT, len)
+ || !strncmp (tag, TAG_ORIGIN, len))
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Version Number
+ *
+ * Returns the requested version number of the RCS file, satisfying tags and/or
+ * dates, and walking branches, if necessary.
+ *
+ * The result is returned; null-string if error.
+ */
+char *
+RCS_getversion (RCSNode *rcs, const char *tag, const char *date,
+ int force_tag_match, int *simple_tag)
+{
+ if (simple_tag != NULL)
+ *simple_tag = 0;
+
+ /* make sure we have something to look at... */
+ assert (rcs != NULL);
+
+ if (tag && date)
+ {
+ char *branch, *rev;
+
+ if (! RCS_nodeisbranch (rcs, tag))
+ {
+ /* We can't get a particular date if the tag is not a
+ branch. */
+ return NULL;
+ }
+
+ /* Work out the branch. */
+ if (is_symbolic (tag))
+ branch = RCS_whatbranch (rcs, tag);
+ else
+ branch = xstrdup (tag);
+
+ int dots = numdots (branch);
+
+ /* Fetch the revision of branch as of date. */
+ if (!dots)
+ rev = RCS_getdatetrunk (rcs, date, force_tag_match);
+ else
+ rev = RCS_getdatebranch (rcs, date, branch);
+
+ free (branch);
+ return rev;
+ }
+ else if (tag)
+ return RCS_gettag (rcs, tag, force_tag_match, simple_tag);
+ else if (date)
+ return RCS_getdate (rcs, date, force_tag_match);
+ else
+ return RCS_head (rcs);
+
+}
+
+
+
+/*
+ * Get existing revision number corresponding to tag or revision.
+ * Similar to RCS_gettag but less interpretation imposed.
+ * For example:
+ * -- If tag designates a magic branch, RCS_tag2rev
+ * returns the magic branch number.
+ * -- If tag is a branch tag, returns the branch number, not
+ * the revision of the head of the branch.
+ * -- An exception is made for '.trunk' as it returns the
+ * head revision of the trunk
+ * If tag or revision is not valid or does not exist in file,
+ * return NULL.
+ */
+char *
+RCS_tag2rev (RCSNode *rcs, char *tag)
+{
+ char *rev, *pa, *pb;
+ int i;
+
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* If a valid revision, try to look it up */
+ if ( RCS_valid_rev (tag) )
+ {
+ /* Make a copy so we can scribble on it */
+ rev = xstrdup (tag);
+
+ /* If revision exists, return the copy */
+ if (RCS_exist_rev (rcs, tag))
+ return rev;
+
+ /* Nope, none such. If tag is not a branch we're done. */
+ i = numdots (rev);
+ if ((i & 1) == 1 )
+ {
+ pa = strrchr (rev, '.');
+ if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.')
+ {
+ free (rev);
+ error (1, 0, "revision `%s' does not exist", tag);
+ }
+ }
+
+ /* Try for a real (that is, exists in the RCS deltas) branch
+ (RCS_exist_rev just checks for real revisions and revisions
+ which have tags pointing to them). */
+ pa = RCS_getbranch (rcs, rev, 1);
+ if (pa != NULL)
+ {
+ free (pa);
+ return rev;
+ }
+
+ /* Tag is branch, but does not exist, try corresponding
+ * magic branch tag.
+ *
+ * FIXME: assumes all magic branches are of
+ * form "n.n.n ... .0.n". I'll fix if somebody can
+ * send me a method to get a magic branch tag with
+ * the 0 in some other position -- <address@hidden>
+ */
+ pa = strrchr (rev, '.');
+ if (!pa)
+ /* This might happen, for instance, if an RCS file only contained
+ * revisions 2.x and higher, and REV == "1".
+ */
+ error (1, 0, "revision `%s' does not exist", tag);
+
+ *pa++ = 0;
+ pb = Xasprintf ("%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa);
+ free (rev);
+ rev = pb;
+ if (RCS_exist_rev (rcs, rev))
+ return rev;
+ error (1, 0, "revision `%s' does not exist", tag);
+ }
+
+ /* If tag is "HEAD", special case to get head RCS revision */
+ if (tag && STREQ (tag, TAG_HEAD))
+ return RCS_head (rcs);
+
+ /* If valid tag let RCS_extract_tag say yea or nay. */
+ char *tmp = RCS_extract_tag (tag, true);
+ if (tmp) free (tmp);
+
+ /* We need to preserve magic branch numbers here */
+ rev = translate_tag (rcs, tag, true);
+
+ /* Trust the caller to print warnings. */
+ return rev;
+}
+
+
+
+/*
+ * Find the revision for a specific tag.
+ * If force_tag_match is set, return NULL if an exact match is not
+ * possible otherwise return RCS_head (). We are careful to look for
+ * and handle "magic" revisions specially.
+ *
+ * If the matched tag is a branch tag, find the head of the branch.
+ *
+ * Returns pointer to newly malloc'd string, or NULL.
+ */
+char *
+RCS_gettag (RCSNode *rcs, const char *symtag, int force_tag_match,
+ int *simple_tag)
+{
+ char *tag;
+
+ if (simple_tag != NULL)
+ *simple_tag = 0;
+
+ /* make sure we have something to look at... */
+ assert (rcs != NULL);
+
+ /* XXX this is probably not necessary, --jtc */
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* If symtag is "HEAD", special case to get head RCS revision */
+ if (symtag && STREQ (symtag, TAG_HEAD))
+#if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */
+ if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
+ return NULL; /* head request for removed file */
+ else
+#endif
+ return RCS_head (rcs);
+
+ if (is_symbolic (symtag))
+ {
+ char *version;
+
+ /* Resolve to a numeric revision number */
+ version = translate_tag (rcs, symtag, false);
+ if (version)
+ {
+ int dots;
+ tag = version;
+
+ /*
+ * If this is a branch tag, we turn it into either its
+ * physical branch equivalent (if one exists) or into
+ * its base revision, which we assume exists.
+ */
+ dots = numdots (tag);
+ if (dots && !(dots & 1))
+ {
+ version = RCS_getbranch (rcs, tag, 1);
+ if (version != NULL)
+ {
+ free (tag);
+ return version;
+ }
+ *strrchr (tag, '.') = '\0';
+ return tag;
+ }
+ }
+ else
+ {
+ /* The tag wasn't there, so return the head or NULL */
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+ }
+ else
+ tag = xstrdup (symtag);
+
+ /* tag is always allocated and numeric now. */
+
+ /*
+ * numeric tag processing:
+ * 1) revision number - just return it
+ * 2) branch number - find head of branch
+ */
+
+ /* strip trailing dots */
+ while (tag[strlen (tag) - 1] == '.')
+ tag[strlen (tag) - 1] = '\0';
+
+ if ((numdots (tag) & 1) == 0)
+ {
+ char *branch;
+
+ /* we have a branch tag, so we need to walk the branch */
+ branch = RCS_getbranch (rcs, tag, force_tag_match);
+ free (tag);
+ return branch;
+ }
+ else
+ {
+ Node *p;
+
+ /* we have a revision tag, so make sure it exists */
+ p = findnode (rcs->versions, tag);
+ if (p != NULL)
+ {
+ /* We have found a numeric revision for the revision tag.
+ To support expanding the RCS keyword Name, if
+ SIMPLE_TAG is not NULL, tell the the caller that this
+ is a simple tag which co will recognize. FIXME: Are
+ there other cases in which we should set this? In
+ particular, what if we expand RCS keywords internally
+ without calling co? */
+ if (simple_tag != NULL)
+ *simple_tag = 1;
+ return tag;
+ }
+ else
+ {
+ /* The revision wasn't there, so return the head or NULL */
+ free (tag);
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+ }
+}
+
+
+
+/*
+ * Return a "magic" revision as a virtual branch off of REV for the RCS file.
+ * A "magic" revision is one which is unique in the RCS file. By unique, I
+ * mean we return a revision which:
+ * - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
+ * - has a revision component which is not an existing branch off REV
+ * - has a revision component which is not an existing magic revision
+ * - is an even-numbered revision, to avoid conflicts with vendor branches
+ * The first point is what makes it "magic".
+ *
+ * As an example, if we pass in 1.37 as REV, we will look for an existing
+ * branch called 1.37.2. If it did not exist, we would look for an
+ * existing symbolic tag with a numeric part equal to 1.37.0.2. If that
+ * didn't exist, then we know that the 1.37.2 branch can be reserved by
+ * creating a symbolic tag with 1.37.0.2 as the numeric part.
+ *
+ * This allows us to fork development with very little overhead -- just a
+ * symbolic tag is used in the RCS file. When a commit is done, a physical
+ * branch is dynamically created to hold the new revision.
+ *
+ * Note: We assume that REV is an RCS revision and not a branch number.
+ */
+static char *check_rev;
+char *
+RCS_magicrev (RCSNode *rcs, char *rev)
+{
+ int rev_num;
+ char *xrev, *test_branch, *local_branch_num;
+
+ xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
+ check_rev = xrev;
+
+ local_branch_num = getenv("CVS_LOCAL_BRANCH_NUM");
+ if (local_branch_num)
+ {
+ rev_num = atoi(local_branch_num);
+ if (rev_num < 2)
+ rev_num = 2;
+ else
+ rev_num &= ~1;
+ }
+ else
+ rev_num = 2;
+
+ /* only look at even numbered branches */
+ for ( ; ; rev_num += 2)
+ {
+ /* see if the physical branch exists */
+ (void) sprintf (xrev, "%s.%d", rev, rev_num);
+ test_branch = RCS_getbranch (rcs, xrev, 1);
+ if (test_branch != NULL) /* it did, so keep looking */
+ {
+ free (test_branch);
+ continue;
+ }
+
+ /* now, create a "magic" revision */
+ (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
+
+ /* walk the symbols list to see if a magic one already exists */
+ if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0)
+ continue;
+
+ /* we found a free magic branch. Claim it as ours */
+ return xrev;
+ }
+}
+
+
+
+/*
+ * walklist proc to look for a match in the symbols list.
+ * Returns 0 if the symbol does not match, 1 if it does.
+ */
+static int
+checkmagic_proc (Node *p, void *closure)
+{
+ if (STREQ (check_rev, p->data))
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ * Given an RCSNode, returns non-zero if the specified revision number
+ * or symbolic tag resolves to a "branch" within the rcs file.
+ *
+ * FIXME: this is the same as RCS_nodeisbranch except for the special
+ * case for handling a null rcsnode and numeric magic branches.
+ */
+int
+RCS_isbranch (RCSNode *rcs, const char *rev)
+{
+ /* numeric revisions are easy -- even number of dots is a branch
+ * Note that magic branch numbers are not checked
+ */
+ if (isdigit ((unsigned char) *rev))
+ return (numdots (rev) & 1) == 0;
+
+ /* assume a revision if you can't find the RCS info */
+ if (rcs == NULL)
+ return 0;
+
+ /* now, look for a match in the symbols list */
+ return RCS_nodeisbranch (rcs, rev);
+}
+
+
+
+/*
+ * Given an RCSNode, returns non-zero if the specified revision number
+ * or symbolic TAG resolves to a "branch" within the rcs file. We do
+ * take into account any magic branches as well.
+ *
+ * NOTES
+ * Resolves TAG when it is symbolic but does not verify existance of
+ * revisions.
+ */
+bool
+RCS_nodeisbranch (RCSNode *rcs, const char *tag)
+{
+ int dots;
+ char *version;
+
+ assert (rcs);
+
+ /* numeric revisions are easy -- even number of dots is a branch
+ * Note that we need to care about magic branch numbers
+ */
+ if (!is_symbolic (tag))
+ {
+ dots = numdots (tag);
+ if (dots > 1)
+ {
+ char *magic;
+ char *p = strrchr (tag, '.') - 1;
+ while (*p != '.') --p;
+ magic = Xasprintf (".%d.", RCS_MAGIC_BRANCH);
+ if (!strncmp (p, magic, strlen (magic)))
+ {
+ free (magic);
+ return (dots & 1);
+ }
+ free (magic);
+ }
+ return !(dots & 1);
+ }
+
+ /* resolve to its numeric equivalent and remove magic */
+ version = translate_tag (rcs, tag, false);
+
+ if (!version) return false;
+
+ dots = numdots (version);
+ free (version);
+
+ if (!(dots & 1))
+ return true;
+
+ return false;
+}
+
+
+
+/* Returns a pointer to malloc'ed memory which contains the branch
+ * for the specified, and possibly symbolic, TAG. Magic branches are handled
+ * correctly.
+ */
+char *
+RCS_whatbranch (RCSNode *rcs, const char *tag)
+{
+ char *version;
+ int dots;
+
+ assert (tag);
+
+ /* assume no branch if you can't find the RCS info */
+ if (!rcs) return NULL;
+
+ if (is_symbolic (tag))
+ {
+ /* now, look for a match in the symbols list */
+ version = translate_tag (rcs, tag, false);
+ if (!version) return NULL;
+ dots = numdots (version);
+ }
+ else
+ {
+ version = xstrdup (tag);
+ dots = numdots (version);
+
+ if (dots > 2 && (dots & 1))
+ {
+ /* See if it's magic; Convert into physical equivalent if so */
+ char *magic;
+ char *branch = strrchr (version, '.');
+ char *p = branch++ - 1;
+ while (*p != '.') --p;
+
+ /* see if we have .magic-branch. (".0.") */
+ magic = Xasprintf (".%d.", RCS_MAGIC_BRANCH);
+ if (!strncmp (p, magic, strlen (magic)))
+ {
+ /* It's magic! Construct the real branch */
+ free (magic);
+ *p = '\0'; /* turn it into a revision */
+ magic = Xasprintf ("%s.%s", version, branch);
+ free (version);
+ return magic;
+ }
+ free (magic);
+ }
+ }
+
+ if (!(dots & 1))
+ return version;
+
+ free (version);
+ return NULL;
+}
+
+
+
+/*
+ * Get the head of the specified branch. If the branch does not exist,
+ * return NULL or RCS_head depending on force_tag_match.
+ * Returns NULL or a newly malloc'd string.
+ */
+char *
+RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match)
+{
+ Node *p, *head;
+ RCSVers *vn;
+ char *xtag;
+ char *nextvers;
+ char *cp;
+
+ /* make sure we have something to look at... */
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* find out if the tag contains a dot, or is on the trunk */
+ cp = strrchr (tag, '.');
+
+ /* trunk processing is the special case */
+ if (cp == NULL)
+ {
+ xtag = Xasprintf ("%s.", tag);
+ for (cp = rcs->head; cp != NULL;)
+ {
+ if (strncmp (xtag, cp, strlen (xtag)) == 0)
+ break;
+ p = findnode (rcs->versions, cp);
+ if (p == NULL)
+ {
+ free (xtag);
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+ vn = p->data;
+ cp = vn->next;
+ }
+ free (xtag);
+ if (cp == NULL)
+ {
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+ return xstrdup (cp);
+ }
+
+ /* if it had a `.', terminate the string so we have the base revision */
+ *cp = '\0';
+
+ /* look up the revision this branch is based on */
+ p = findnode (rcs->versions, tag);
+
+ /* put the . back so we have the branch again */
+ *cp = '.';
+
+ if (p == NULL)
+ {
+ /* if the base revision didn't exist, return head or NULL */
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+
+ /* find the first element of the branch we are looking for */
+ vn = p->data;
+ if (vn->branches == NULL)
+ return NULL;
+ xtag = Xasprintf ("%s.", tag);
+ head = vn->branches->list;
+ for (p = head->next; p != head; p = p->next)
+ if (strncmp (p->key, xtag, strlen (xtag)) == 0)
+ break;
+ free (xtag);
+
+ if (p == head)
+ {
+ /* we didn't find a match so return head or NULL */
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+
+ /* now walk the next pointers of the branch */
+ nextvers = p->key;
+ do
+ {
+ p = findnode (rcs->versions, nextvers);
+ if (p == NULL)
+ {
+ /* a link in the chain is missing - return head or NULL */
+ if (force_tag_match)
+ return NULL;
+ else
+ return RCS_head (rcs);
+ }
+ vn = p->data;
+ nextvers = vn->next;
+ } while (nextvers != NULL);
+
+ /* we have the version in our hand, so go for it */
+ return xstrdup (vn->version);
+}
+
+
+
+/* Revision number string, R, must contain a `.'.
+ * R must be writable. Replace the rightmost `.' in R with
+ * the NUL byte and return a pointer to that NUL byte.
+ */
+static inline char *
+truncate_revnum_in_place (char *r)
+{
+ char *dot = strrchr (r, '.');
+ assert (dot);
+ *dot = '\0';
+ return dot;
+}
+
+
+
+/* Returns the head of the branch which the possibly symbolic TAG is on.
+ * TAG can be a branch tag or non-branch tag; symbolic or numeric.
+ *
+ * Returns a newly malloc'd string. Returns NULL if a symbolic name
+ * isn't found.
+ */
+char *
+RCS_branch_head (RCSNode *rcs, const char *tag)
+{
+ char *num;
+ char *retval;
+
+ assert (rcs && tag);
+
+ if (is_symbolic (tag))
+ {
+ num = translate_tag (rcs, tag, false);
+ if (!num) return NULL;
+ }
+ else
+ /* FIXME: Validate revnum? */
+ num = xstrdup (tag);
+
+ if (!RCS_nodeisbranch (rcs, num))
+ /* Make NUM = NUM's branch. */
+ truncate_revnum_in_place (num);
+
+ retval = RCS_getbranch (rcs, num, 1);
+ free (num);
+ return retval;
+}
+
+
+
+/* Get the branch point for a particular branch, that is the first
+ revision on that branch. For example, RCS_getbranchpoint (rcs,
+ "1.3.2") will normally return "1.3.2.1". TARGET may be either a
+ branch number or a revision number; if a revnum, find the
+ branchpoint of the branch to which TARGET belongs.
+
+ Return RCS_head if TARGET is on the trunk or if the root node could
+ not be found (this is sort of backwards from our behavior on a branch;
+ the rationale is that the return value is a revision from which you
+ can start walking the next fields and end up at TARGET).
+ Return NULL on error. */
+static char *
+RCS_getbranchpoint (RCSNode *rcs, char *target)
+{
+ char *branch, *bp;
+ Node *vp;
+ RCSVers *rev;
+ int dots, isrevnum, brlen;
+
+ dots = numdots (target);
+ isrevnum = dots & 1;
+
+ if (dots == 1)
+ /* TARGET is a trunk revision; return rcs->head. */
+ return RCS_head (rcs);
+
+ /* Get the revision number of the node at which TARGET's branch is
+ rooted. If TARGET is a branch number, lop off the last field;
+ if it's a revision number, lop off the last *two* fields. */
+ branch = xstrdup (target);
+ bp = strrchr (branch, '.');
+ if (bp == NULL)
+ error (1, 0, "%s: confused revision number %s",
+ rcs->print_path, target);
+ if (isrevnum)
+ while (*--bp != '.')
+ ;
+ *bp = '\0';
+
+ vp = findnode (rcs->versions, branch);
+ if (vp == NULL)
+ {
+ error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
+ free (branch);
+ return NULL;
+ }
+ rev = vp->data;
+
+ *bp++ = '.';
+ while (*bp && *bp != '.')
+ ++bp;
+ brlen = bp - branch;
+
+ vp = rev->branches->list->next;
+ while (vp != rev->branches->list)
+ {
+ /* BRANCH may be a genuine branch number, e.g. `1.1.3', or
+ maybe a full revision number, e.g. `1.1.3.6'. We have
+ found our branch point if the first BRANCHLEN characters
+ of the revision number match, *and* if the following
+ character is a dot. */
+ if (strncmp (vp->key, branch, brlen) == 0 && vp->key[brlen] == '.')
+ break;
+ vp = vp->next;
+ }
+
+ free (branch);
+ if (vp == rev->branches->list)
+ {
+ error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
+ return NULL;
+ }
+ else
+ return xstrdup (vp->key);
+}
+
+
+
+/*
+ * Get the head of the RCS file. If branch is set, this is the head of the
+ * branch, otherwise the real head.
+ *
+ * INPUTS
+ * rcs The parsed rcs node information.
+ *
+ * RETURNS
+ * NULL when rcs->branch exists and cannot be found or when there are no
+ * revisions in the RCS file.
+ *
+ * A newly malloc'd string, otherwise.
+ */
+char *
+RCS_head (RCSNode *rcs)
+{
+ char *retval;
+
+ /* make sure we have something to look at... */
+ assert (rcs);
+
+ /*
+ * NOTE: we call getbranch with force_tag_match set to avoid any
+ * possibility of recursion
+ */
+ if (rcs->branch)
+ return RCS_getbranch (rcs, rcs->branch, 1);
+
+ retval = xstrdup (rcs->head);
+ if (!retval) return NULL;
+
+ if (!(numdots (retval) & 1))
+ {
+ error (0, 0, "Head revision is a branch in `%s'.", rcs->path);
+ free (retval);
+ return NULL;
+ }
+
+ return retval;
+}
+
+
+
+/*
+ * Get the most recent revision, based on the supplied date, but use some
+ * funky stuff and follow the vendor branch maybe
+ */
+char *
+RCS_getdate (RCSNode *rcs, const char *date, int force_tag_match)
+{
+ char *cur_rev = NULL;
+ char *retval = NULL;
+ Node *p;
+ RCSVers *vers = NULL;
+
+ /* make sure we have something to look at... */
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* if the head is on a branch, try the branch first */
+ if (rcs->branch != NULL)
+ {
+ retval = RCS_getdatebranch (rcs, date, rcs->branch);
+ if (retval != NULL)
+ return retval;
+ }
+
+ /* otherwise if we have a trunk, use it */
+ return RCS_getdatetrunk (rcs, date, force_tag_match);
+}
+
+
+
+/*
+ * Look up the last element on the trunk that was put in before or on
+ * the specified date and time (return the rev or NULL)
+ * Follow the vendor branch if not found on the trunk
+ */
+static char *
+RCS_getdatetrunk (RCSNode *rcs, const char *date, int force_tag_match)
+{
+ Node *p = NULL;
+ RCSVers *vers = NULL;
+ char *retval = NULL;
+
+ assert(rcs);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rcs->head)
+ {
+ p = findnode (rcs->versions, rcs->head);
+ if (p == NULL)
+ {
+ error (0, 0, "%s: head revision %s doesn't exist", rcs->print_path,
+ rcs->head);
+ }
+ while (p != NULL)
+ {
+ /* if the date of this one is before date, take it */
+ vers = p->data;
+ if (RCS_datecmp (vers->date, date) <= 0)
+ {
+ retval = vers->version;
+ break;
+ }
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ p = findnode (rcs->versions, vers->next);
+ else
+ p = NULL;
+ }
+ }
+ else
+ error (0, 0, "%s: no head revision", rcs->print_path);
+
+ /*
+ * at this point, either we have the revision we want, or we have the
+ * first revision on the trunk (1.1?) in our hands, or we've come up
+ * completely empty
+ */
+
+ /* if we found what we're looking for, and it's not 1.1 return it */
+ if (retval != NULL)
+ {
+ if (! STREQ (retval, "1.1"))
+ return xstrdup (retval);
+
+ /* This is 1.1; if the date of 1.1 is not the same as that for the
+ 1.1.1.1 version, then return 1.1. This happens when the first
+ version of a file is created by a regular cvs add and commit,
+ and there is a subsequent cvs import of the same file. */
+ p = findnode (rcs->versions, "1.1.1.1");
+ if (p)
+ {
+ char *date_1_1 = vers->date;
+
+ vers = p->data;
+ if (RCS_datecmp (vers->date, date_1_1) != 0)
+ return xstrdup ("1.1");
+ }
+ }
+
+ retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
+
+ /*
+ * if we found a match, return it; otherwise, we return the first
+ * revision on the trunk or NULL depending on force_tag_match and the
+ * date of the first rev
+ */
+ if (retval != NULL)
+ return retval;
+
+ if (vers && (!force_tag_match || RCS_datecmp (vers->date, date) <= 0))
+ return xstrdup (vers->version);
+ else
+ return NULL;
+}
+
+
+
+/*
+ * Look up the last element on a branch that was put in before or on
+ * the specified date and time (return the rev or NULL)
+ */
+static char *
+RCS_getdatebranch (RCSNode *rcs, const char *date, const char *branch)
+{
+ char *cur_rev = NULL;
+ char *cp;
+ char *xbranch, *xrev;
+ Node *p;
+ RCSVers *vers;
+
+ /* look up the first revision on the branch */
+ xrev = xstrdup (branch);
+ cp = strrchr (xrev, '.');
+ if (cp == NULL)
+ {
+ free (xrev);
+ return NULL;
+ }
+ *cp = '\0'; /* turn it into a revision */
+
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ p = findnode (rcs->versions, xrev);
+ free (xrev);
+ if (p == NULL)
+ return NULL;
+ vers = p->data;
+
+ /* Tentatively use this revision, if it is early enough. */
+ if (RCS_datecmp (vers->date, date) <= 0)
+ cur_rev = vers->version;
+
+ /* If no branches list, return now. This is what happens if the branch
+ is a (magic) branch with no revisions yet. */
+ if (vers->branches == NULL)
+ return xstrdup (cur_rev);
+
+ /* walk the branches list looking for the branch number */
+ xbranch = Xasprintf ("%s.", branch);
+ for (p = vers->branches->list->next; p != vers->branches->list; p =
p->next)
+ if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
+ break;
+ free (xbranch);
+ if (p == vers->branches->list)
+ {
+ /* This is what happens if the branch is a (magic) branch with
+ no revisions yet. Similar to the case where vers->branches ==
+ NULL, except here there was a another branch off the same
+ branchpoint. */
+ return xstrdup (cur_rev);
+ }
+
+ p = findnode (rcs->versions, p->key);
+
+ /* walk the next pointers until you find the end, or the date is too late
*/
+ while (p != NULL)
+ {
+ vers = p->data;
+ if (RCS_datecmp (vers->date, date) <= 0)
+ cur_rev = vers->version;
+ else
+ break;
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ p = findnode (rcs->versions, vers->next);
+ else
+ p = NULL;
+ }
+
+ /* Return whatever we found, which may be NULL. */
+ return xstrdup (cur_rev);
+}
+
+
+
+/*
+ * Compare two dates in RCS format. Beware the change in format on January 1,
+ * 2000, when years go from 2-digit to full format.
+ */
+int
+RCS_datecmp (const char *date1, const char *date2)
+{
+ int length_diff = strlen (date1) - strlen (date2);
+
+ return length_diff ? length_diff : strcmp (date1, date2);
+}
+
+
+
+/* Look up revision REV in RCS and return the date specified for the
+ revision minus FUDGE seconds (FUDGE will generally be one, so that the
+ logically previous revision will be found later, or zero, if we want
+ the exact date).
+
+ The return value is the date being returned as a time_t, or (time_t)-1
+ on error (previously was documented as zero on error; I haven't checked
+ the callers to make sure that they really check for (time_t)-1, but
+ the latter is what this function really returns). If DATE is non-NULL,
+ then it must point to MAXDATELEN characters, and we store the same
+ return value there in DATEFORM format. */
+time_t
+RCS_getrevtime (RCSNode *rcs, const char *rev, char *date, int fudge)
+{
+ char *tdate;
+ struct tm xtm, *ftm;
+ struct timespec revdate;
+ Node *p;
+ RCSVers *vers;
+
+ /* make sure we have something to look at... */
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* look up the revision */
+ p = findnode (rcs->versions, rev);
+ if (p == NULL)
+ return -1;
+ vers = p->data;
+
+ /* split up the date */
+ if (sscanf (vers->date, SDATEFORM, &xtm.tm_year, &xtm.tm_mon,
+ &xtm.tm_mday, &xtm.tm_hour, &xtm.tm_min, &xtm.tm_sec) != 6)
+ error (1, 0, "%s: invalid date for revision %s (%s)", rcs->print_path,
+ rev, vers->date);
+
+ /* If the year is from 1900 to 1999, RCS files contain only two
+ digits, and sscanf gives us a year from 0-99. If the year is
+ 2000+, RCS files contain all four digits and we subtract 1900,
+ because the tm_year field should contain years since 1900. */
+
+ if (xtm.tm_year >= 100 && xtm.tm_year < 2000)
+ error (0, 0, "%s: non-standard date format for revision %s (%s)",
+ rcs->print_path, rev, vers->date);
+ if (xtm.tm_year >= 1900)
+ xtm.tm_year -= 1900;
+
+ /* put the date in a form getdate can grok */
+ tdate = Xasprintf ("%d-%d-%d %d:%d:%d -0000",
+ xtm.tm_year + 1900, xtm.tm_mon, xtm.tm_mday,
+ xtm.tm_hour, xtm.tm_min, xtm.tm_sec);
+
+ /* Turn it into seconds since the epoch.
+ *
+ * We use a struct timespec since that is what getdate requires, then
+ * truncate the nanoseconds.
+ */
+ if (!get_date (&revdate, tdate, NULL))
+ {
+ free (tdate);
+ return (time_t)-1;
+ }
+ free (tdate);
+
+ revdate.tv_sec -= fudge; /* remove "fudge" seconds */
+ if (date)
+ {
+ /* Put an appropriate string into `date', if we were given one. */
+ ftm = gmtime (&revdate.tv_sec);
+ (void) sprintf (date, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ }
+
+ return revdate.tv_sec;
+}
+
+
+
+List *
+RCS_getlocks (RCSNode *rcs)
+{
+ assert(rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rcs->locks_data) {
+ rcs->locks = getlist ();
+ do_locks (rcs->locks, rcs->locks_data);
+ free(rcs->locks_data);
+ rcs->locks_data = NULL;
+ }
+
+ return rcs->locks;
+}
+
+
+
+List *
+RCS_symbols(RCSNode *rcs)
+{
+ assert(rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rcs->symbols_data) {
+ rcs->symbols = getlist ();
+ do_symbols (rcs->symbols, rcs->symbols_data);
+ free(rcs->symbols_data);
+ rcs->symbols_data = NULL;
+ }
+
+ return rcs->symbols;
+}
+
+
+
+/*
+ * Find the previous revision
+ *
+ * RETURN
+ * Branch: return the HEAD-1 revision or NULL
+ * Revision: return the previous revision or NULL
+ *
+ * ASSUMPTIONS
+ * The tag is valid, it has been validated by RCS_check_tag
+ * The Tag does not contain RCS_MAGIC_BRANCH
+ */
+static char *
+RCS_getprevious (RCSNode *rcs, const char *rev)
+{
+ char *retval = NULL;
+ int dots = numdots (rev);
+ char *trev = NULL;
+
+ if (!(dots & 1)) /* branch handling => turn into a head revision */
+ {
+ trev = RCS_branch_head (rcs, rev);
+ if (!trev)
+ {
+ /* branch not found,
+ * so only the tag exists ... take its revision
+ */
+ trev = xstrdup (rev);
+ truncate_revnum_in_place (trev);
+ }
+ dots = numdots (trev);
+ }
+ else
+ trev = xstrdup (rev);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (dots > 1) /* revision on a branch */
+ {
+ /* find the root revision */
+ char *p1 = strrchr (trev, '.');
+ *p1 = '\0';
+ int len = strlen (trev);
+ char *p2 = strrchr (trev, '.');
+ *p2 = '\0';
+
+ RCSVers *vers = NULL;
+ Node *node = findnode (rcs->versions, trev);
+ if (node && (vers = node->data) && vers->branches)
+ {
+ *p1 = '.';
+ *p2 = '.';
+ char *rootdate = vers->date;
+ Node *head = vers->branches->list;
+ Node *br;
+ for (br = head->next; br != head; br = br->next)
+ {
+ if (node = findnode (rcs->versions, br->key))
+ {
+ if (strncmp (((RCSVers *)node->data)->version, trev, len))
+ continue;
+
+ ++len;
+ char *p = NULL;
+ while (node != NULL)
+ {
+ vers = node->data;
+ if (STREQ (vers->version + len, trev + len))
+ {
+ if (p)
+ retval = xstrdup (p);
+ else if (RCS_datecmp (vers->date, rootdate))
+ {
+ *p2 = '\0';
+ retval = xstrdup (trev);
+ }
+ break;
+ }
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ {
+ p = vers->version;
+ node = findnode (rcs->versions, vers->next);
+ }
+ else
+ node = NULL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else /* revision on trunk */
+ {
+ char *prev = NULL;
+ char *curdate = NULL;
+
+ assert (dots == 1);
+
+ if (!STREQ (trev, "1.1"))
+ {
+ Node *node = findnode (rcs->versions, trev);
+ if (node)
+ {
+ RCSVers *v = node->data;
+ curdate = v->date;
+ if (v->next && (node = findnode (rcs->versions, v->next)))
+ {
+ free (trev);
+ prev = ((RCSVers *)node->data)->version;
+ trev = xstrdup (prev);
+ }
+ }
+ }
+
+ if (prev && STREQ (trev, "1.1"))
+ {
+ /* This is 1.1; if the date of 1.1 is the same as that for
+ * the VENDOR.1 version, then return the latest VENDOR version with
+ * a timestamp before the 1.2 timestamp (point of merge). The
+ * date of 1.1. and VENDOR.1 differs if the first version of
+ * a file is created by a regular cvs add and commit, and there
+ * is a subsequent cvs import of the same file ==> return 1.1.
+ * If 1.1 is dead, the file was initially added on a branch
+ * ==> return NULL.
+ */
+ Node *rootnode = findnode (rcs->versions, trev);
+ if (rootnode)
+ {
+ RCSVers *vers = rootnode->data;
+ if (!vers->dead && vers->branches)
+ {
+ char *date_1_1 = vers->date;//vers 1.1
+
+ Node *head = vers->branches->list;
+ Node *br;
+ for (br = head->next; br != head; br = br->next)
+ {
+ Node *node = NULL;
+ if (node = findnode (rcs->versions, br->key))
+ {
+ vers = node->data;
+ if (!vers->dead
+ && !RCS_datecmp (vers->date, date_1_1))
+ {
+ /* get head of branch */
+ retval = vers->version;
+ while (vers->next)
+ {
+ node = findnode (rcs->versions,
+ vers->next);
+ if (node)
+ {
+ vers = node->data;
+ if (RCS_datecmp (curdate,
+ vers->date) > 0)
+ {
+ vers = node->data;
+ retval = vers->version;
+ continue;
+ }
+ }
+ break;
+ }
+ retval = xstrdup (retval);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!retval && prev)
+ retval = xstrdup (prev);
+ }
+ free (trev);
+
+ return retval;
+}
+
+
+
+/*
+ * Find the next revision
+ *
+ * RETURN
+ * Branch: NULL (There cannot be a revision)
+ * Revision: returns the next revision or NULL
+ *
+ * ASSUMPTIONS
+ * The tag is valid, it has been validated by RCS_check_tag
+ * The Tag does not contain RCS_MAGIC_BRANCH
+ */
+static char *
+RCS_getnext (RCSNode *rcs, const char *rev)
+{
+ Node *node = NULL;
+ RCSVers *vers = NULL;
+ char *retval = NULL;
+ const char *cmp = rev;
+ int dots = numdots (rev);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (dots > 1)
+ {
+ node = findnode (rcs->versions, rev);
+ if (!node)
+ return NULL;
+
+ vers = node->data;
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ {
+ node = findnode (rcs->versions, vers->next);
+ if (node)
+ return xstrdup (((RCSVers *)node->data)->version);
+ }
+ }
+ else if (rcs->head)
+ {
+ node = findnode (rcs->versions, rcs->head);
+ if (node)
+ {
+ RCSVers *next = NULL;
+ vers = NULL;
+ while (node != NULL)
+ {
+ vers = next;
+ next = node->data;
+ if (next && STREQ (next->version, cmp))
+ {
+ if (vers)
+ retval = xstrdup (vers->version);
+ break;
+ }
+
+ /* if there is a next version, find the node */
+ if (next->next != NULL)
+ node = findnode (rcs->versions, next->next);
+ else
+ node = NULL;
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+
+/*
+ * Find the origin revision, which is the first revision
+ * on either the trunk or a branch if added there.
+ *
+ * ASSUMPTIONS
+ * The tag is valid, it has been validated by RCS_check_tag
+ * The Tag does not contain RCS_MAGIC_BRANCH
+ */
+static char *
+RCS_getorigin (RCSNode *rcs, const char *rev)
+{
+ char *trev;
+ int dots = numdots (rev);
+
+ if (!(dots & 1)) /* branch handling => turn into a head revision */
+ {
+ trev = RCS_branch_head (rcs, rev);
+ if (trev)
+ {
+ if (dots > (dots = numdots (trev)))
+ {
+ /* Got the root revision. The first commit to the branch is
+ * defined as the origin, so return NULL.
+ */
+ free (trev);
+ return NULL;
+ }
+ /* Else, we have a head revision on the branch. */
+ }
+ else
+ /* Error - branch not found... */
+ return NULL;
+ }
+ else
+ trev = xstrdup (rev);
+
+ assert (dots == numdots (trev));
+
+ if (RCS_exist_rev (rcs, trev))
+ {
+ Node *rootnode = NULL;
+
+ while (dots > 1)
+ {
+ int len;
+ char *tmp = trev;
+ trev = xstrdup (tmp);
+
+ truncate_revnum_in_place (trev);
+ len = strlen (trev);
+ truncate_revnum_in_place (trev);
+
+ /* If a file was added on the trunk, and it is added on
+ * a branch in a second step, the '1.1.2.1' revision is
+ * dead, and timestamp of 1.1 and 1.1.2.1 are equal.
+ * Prevent returning this as root!
+ */
+ bool found = false;
+ rootnode = findnode (rcs->versions, trev);
+ if (rootnode)
+ {
+ RCSVers *vers = rootnode->data;
+ if (vers->branches)
+ {
+ Node *head = vers->branches->list;
+ Node *br;
+ for (br = head->next; br != head; br = br->next)
+ {
+ /* check if br->key is on branch rev */
+ if (!strncmp (tmp, br->key, len))
+ {
+ Node *bn = NULL;
+ if (bn = findnode (rcs->versions, br->key))
+ {
+ RCSVers *cv = bn->data;
+ if (cv->dead)
+ {
+ if (cv->next)
+ {
+ free (trev);
+ if (STREQ (cv->version, rev))
+ trev = xstrdup (rev);
+ else
+ {
+ bn = findnode (rcs->versions,
+ cv->next);
+ cv = bn->data;
+ trev = xstrdup (cv->version);
+ }
+ found = true;
+ }
+ else
+ {
+ free (trev);
+ trev = NULL;
+ }
+ }
+ else if (vers->dead)
+ {
+ /* root dead => stay on branch */
+ free (trev);
+ trev = xstrdup (cv->version);
+ found = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (found || !trev)
+ return trev;
+ else
+ dots = numdots (trev);
+ free (tmp);
+ }
+
+ RCSVers *vers;
+ char *prev = NULL;
+
+ if (!rootnode)
+ rootnode = findnode (rcs->versions, trev);
+ free (trev);
+
+ while (rootnode)
+ {
+ vers = rootnode->data;
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ {
+ prev = vers->version;
+ rootnode = findnode (rcs->versions, vers->next);
+ }
+ else if (vers->dead)
+ return xstrdup (prev);
+ else
+ return xstrdup (vers->version);
+ }
+ }
+ free (trev);
+ return NULL;
+}
+
+
+
+/*
+ * Find the branchpoint, no matter if rev points
+ * to a branch or a revision
+ *
+ * ASSUMPTIONS
+ * The tag is valid, it has been validated by RCS_check_tag
+ * The Tag does not contain RCS_MAGIC_BRANCH
+ */
+static char *
+RCS_getroot (RCSNode *rcs, const char *rev)
+{
+ char *retval = NULL;
+ RCSVers *vers = NULL;
+ int dots = numdots (rev);
+
+ if (dots > 1)
+ {
+ int len;
+ retval = xstrdup (rev);
+
+ if (dots & 1)
+ {
+ *strrchr (retval, '.') = '\0';
+ len = strlen (retval);
+ *strrchr (retval, '.') = '\0';
+ }
+ else
+ {
+ len = strlen (retval);
+ *strrchr (retval, '.') = '\0';
+ }
+
+ /* If a file was added on the trunk, and it is added on
+ * a branch in a second step, the '1.1.2.1' revision is
+ * dead, and timestamp of 1.1 and 1.1.2.1 are equal.
+ * Return 1.1.2.1 in this case!
+ */
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ Node *node = findnode(rcs->versions, retval);
+ if (node)
+ {
+ vers = node->data;
+ if (vers->branches)
+ {
+ char *rootdate = vers->date;
+ Node *head = vers->branches->list;
+ Node *br;
+ for (br = head->next; br != head; br = br->next)
+ {
+ //check if br->key is on branch rev
+ if (!strncmp (rev, br->key, len))
+ {
+ if (node = findnode (rcs->versions, br->key))
+ {
+ vers = node->data;
+ if (vers->dead && !RCS_datecmp (rootdate,
vers->date))
+ {
+ free (retval);
+ if (STREQ (vers->version, rev))
+ return NULL;
+ else
+ return xstrdup (vers->version);
+ }
+ }
+ return retval;
+ }
+ }
+ }
+ else
+ {
+ /* Branch tag exists, but no revisions are on this branch
+ * Return the revision, it already is the root
+ */
+ return retval;
+ }
+ }
+ free (retval);
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Find the commitid, if prev is true return the
+ * previous revision
+ * If a revisions was initially added on a branch, there will
+ * be two revision with identical commitid. In this case,
+ * take the one that's not dead.
+ */
+static char *
+RCS_getcommitid (RCSNode *rcs, const char *commitid, bool prev)
+{
+ Node *p;
+ RCSVers *oldvers = NULL;
+ char *result = NULL;
+ char *cmp = xstrdup (commitid);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ Node *head = rcs->versions->list;
+ for (p = head->next; p != head; p = p->next)
+ {
+ RCSVers *vers = (RCSVers *)p->data;
+
+ Node *info = findnode (vers->other_delta, "commitid");
+ if (info != NULL)
+ if (!strcmp (info->data, cmp))
+ {
+ RCSVers *next_vers = NULL;
+ bool maybe_duplicate = false;
+ if (!prev)
+ {
+ if (result)
+ free (result);
+ result = xstrdup (vers->version);
+ maybe_duplicate = vers->dead;
+ }
+ else
+ {
+ if (p->next)
+ {
+ if (numdots (vers->version) == 1)
+ next_vers = (RCSVers *)p->next->data;
+ }
+ else
+ next_vers = oldvers;
+ if (next_vers &&
+ (RCS_datecmp (next_vers->date, vers->date) < 0))
+ {
+ result = xstrdup (next_vers->version);
+ if (next_vers->dead)
+ {
+ info = findnode (next_vers->other_delta,
"commitid");
+ if (info)
+ {
+ free (cmp);
+ cmp = xstrdup (info->data);
+ maybe_duplicate = true;
+ prev = false;
+ }
+ }
+ }
+ else
+ result = RCS_getprevious (rcs, vers->version);
+ }
+ if (!maybe_duplicate)
+ break;
+ }
+ oldvers = vers;
+ }
+ free (cmp);
+ return result;
+}
+
+
+
+/* Translate special/symbolic tags into their revision thus:
+ *
+ * - If TAG starts numeric, rely on its formating up to the first
+ * non-numberic-tag character ([^0-9.]) to resolve a base revision.
+ * - Else, resolve everything before the first `.' as a symbolic tag to
+ * determine the base tag. Convert magic branch numbers into their
+ * physical equivalent.
+ * - Resolve any remaining tag extensions (.trunk, .head, ...).
+ *
+ * RETURNS
+ * NULL on error.
+ * A newly malloc'd string containing the resolved revision, otherwise.
+ *
+ * ASSUMPTIONS
+ * The Tag is valid, it has been validated by RCS_check_tag
+ */
+static char *
+translate_tag (RCSNode *rcs, const char *tag, bool keepmagic)
+{
+/* printf("translate_tag: %s\n",tag); */
+ char c;
+ char *retval = NULL;
+ char *tmp, *tmpval;
+ bool dotstart = false;
+
+ assert (rcs && tag);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (tag[0] == '@')
+ {
+ /* handle cvsnt compatibility stuff */
+ if (tag[1] == '<')
+ return RCS_getcommitid (rcs, tag + 2, true);
+ else
+ return RCS_getcommitid (rcs, tag + 1, false);
+ }
+
+ tmpval = xstrdup (tag);
+ if (isdigit ((unsigned char) *tmpval))
+ {
+ /* extract initial revision num */
+ char *p = tmpval;
+ do
+ ++p;
+ while (isdigit ((unsigned char) *p) || *p == '.');
+ tmp = p;
+ if (*p--) *p = '\0';
+ retval = xstrdup (tmpval);
+ }
+ else
+ tmp = tmpval;
+
+
+ char *token = strtok (tmp,".");
+ if (token)
+ {
+ bool force;
+ if (!keepmagic && retval && RCS_nodeisbranch (rcs, retval))
+ {
+ assert (*token); /* see => assumptions */
+ /* We have an initial numeric revision, check
+ * for magic rev numbers and convert to their
+ * physical equivalent if so.
+ */
+ char *p = RCS_whatbranch (rcs, retval);
+ free (retval);
+ retval = p;
+ }
+ do
+ {
+ assert (*token); /* see => assumptions */
+ force = false;
+ if (!retval)
+ {
+ /* there is no base revision and no initial numeric revision */
+ if (token == tmp)
+ {
+ /* tag does not start with a dot */
+ retval = translate_symtag (rcs, token);
+ if (retval)
+ {
+ if (keepmagic && (token = strtok (NULL,".")))
+ {
+ /* The next token needs to be evaluated here because
+ * we need to know whether magic revision numbers
+ * need to be converted to their physical
equivalent.
+ * Set force flag to not lose this token
+ */
+ force = true;
+ keepmagic = false;
+ }
+
+ if (!keepmagic && retval && RCS_nodeisbranch (rcs,
retval))
+ {
+ /* convert magic rev numbers into their
+ * physical equivalent
+ */
+ char *p = RCS_whatbranch (rcs, retval);
+ free (retval);
+ retval = p;
+ if (!retval) force = false;
+ }
+ }
+ }
+ else if (STREQ (token, TAG_TRUNK))
+ {
+ /* .trunk is a branch tag, but rcs->head is a revision. */
+ char *p;
+ retval = RCS_head (rcs);
+
+ if (retval)
+ {
+ /* A non-null return from RCS_head is guaranteed to not
+ * specify a branch and to have at least one dot.
+ */
+ p = strrchr (retval, '.');
+ *p = '\0';
+ }
+ }
+ else if (STREQ (token, TAG_COMMITID))
+ {
+ char *commitid = strtok (NULL,".");
+ if (token = strtok (NULL,"."))
+ /* The next token needs to be evaluated here because
+ * RCS_getcommitid needs to know whether a previous
+ * revision is requested (performance enhancement).
+ * Set force flag to ensure the next token gets resolved
+ * if it is not a '.prev' token.
+ */
+ force = true;
+ if (commitid)
+ {
+ bool previous = (token && STREQ (token, TAG_PREVIOUS));
+ retval = RCS_getcommitid (rcs, commitid, previous);
+ if (previous || !retval)
+ force = false;
+ }
+ }
+ else if (!STREQ (token, TAG_DOTBASE))
+ /* Does it make sense to output an error msg here?
+ * Actualy this is more or less a help for debugging
+ * since this only happens if the assumptions fail
+ */
+ error (1, 0, "Tag `%s': invalid head: `%s'", token, tag);
+
+ /* If no retval and no force flag => return NULL.
+ * Callers will generate the "TAG not found" message.
+ */
+ }
+ else
+ {
+ char *p = retval;
+ if (STREQ (token, TAG_DOTHEAD))
+ retval = RCS_branch_head (rcs, p);
+ else if (STREQ (token, TAG_PREVIOUS))
+ retval = RCS_getprevious (rcs, p);
+ else if (STREQ (token, TAG_ORIGIN))
+ retval = RCS_getorigin (rcs, p);
+ else if (STREQ (token, TAG_ROOT))
+ retval = RCS_getroot (rcs, p);
+ else if (STREQ (token, TAG_NEXT))
+ retval = RCS_getnext (rcs, p);
+ else
+ /* Does it make sense to output an error msg here?
+ * Actualy this is more or less a help for debugging
+ * since this only happens if the assumptions fail
+ */
+ error (1, 0,
+ "Tag `%s': invalid extension: `%s'", token, tag);
+ free (p);
+ }
+ }
+ while (force || (retval && (token = strtok (NULL, "."))) );
+ }
+ free (tmpval);
+/* printf("translate_tag: '%s' => '%s'\n",tag,retval); */
+ return retval;
+}
+
+
+
+/*
+ * Return the version associated with a particular symbolic tag.
+ * Returns NULL or a newly malloc'd string.
+ */
+static char *
+translate_symtag (RCSNode *rcs, const char *tag)
+{
+ if (rcs->symbols != NULL)
+ {
+ Node *p;
+
+ /* The symbols have already been converted into a list. */
+ p = findnode (rcs->symbols, tag);
+ if (p == NULL)
+ return NULL;
+
+ return xstrdup (p->data);
+ }
+
+ if (rcs->symbols_data != NULL)
+ {
+ size_t len;
+ char *cp, *last;
+
+ /* Look through the RCS symbols information. This is like
+ do_symbols, but we don't add the information to a list. In
+ most cases, we will only be called once for this file, so
+ generating the list is unnecessary overhead. */
+
+ len = strlen (tag);
+ cp = rcs->symbols_data;
+ /* Keeping track of LAST below isn't strictly necessary, now that tags
+ * should be parsed for validity before they are accepted, but tags
+ * with spaces used to cause the code below to loop indefintely, so
+ * I have corrected for that. Now, in the event that I missed
+ * something, the server cannot be hung. -DRP
+ */
+ last = NULL;
+ while ((cp = strchr (cp, tag[0])) != NULL)
+ {
+ if (cp == last) break;
+ if ((cp == rcs->symbols_data || whitespace (cp[-1]))
+ && strncmp (cp, tag, len) == 0
+ && cp[len] == ':')
+ {
+ char *v, *r;
+
+ /* We found the tag. Return the version number. */
+
+ cp += len + 1;
+ v = cp;
+ while (! whitespace (*cp) && *cp != '\0')
+ ++cp;
+ r = xmalloc (cp - v + 1);
+ strncpy (r, v, cp - v);
+ r[cp - v] = '\0';
+ return r;
+ }
+
+ while (! whitespace (*cp) && *cp != '\0')
+ ++cp;
+ if (*cp == '\0')
+ break;
+ last = cp;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ * The argument ARG is the getopt remainder of the -k option specified on the
+ * command line. This function returns malloc'ed space that can be used
+ * directly in calls to RCS V5, with the -k flag munged correctly.
+ */
+char *
+RCS_check_kflag (const char *arg)
+{
+ static const char *const keyword_usage[] =
+ {
+ "%s %s: invalid RCS keyword expansion mode\n",
+ "Valid expansion modes include:\n",
+ " -kkv\tGenerate keywords using the default form.\n",
+ " -kkvl\tLike -kkv, except locker's name inserted.\n",
+ " -kk\tGenerate only keyword names in keyword strings.\n",
+ " -kv\tGenerate only keyword values in keyword strings.\n",
+ " -ko\tGenerate the old keyword string (no changes from checked in
file).\n",
+ " -kb\tGenerate binary file unmodified (merges not allowed) (RCS
5.7).\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL,
+ };
+ char const *const *cpp = NULL;
+
+ if (arg)
+ {
+ for (cpp = kflags; *cpp != NULL; cpp++)
+ {
+ if (STREQ (arg, *cpp))
+ break;
+ }
+ }
+
+ if (arg == NULL || *cpp == NULL)
+ {
+ usage (keyword_usage);
+ }
+
+ return Xasprintf ("-k%s", *cpp);
+}
+
+
+
+/*
+ * Do some consistency checks on the symbolic tag... These should equate
+ * pretty close to what RCS checks, though I don't know for certain.
+ */
+void
+RCS_check_tag (const char *tag)
+{
+ char *invalid = "$,.:;@"; /* invalid RCS tag characters */
+ const char *cp;
+
+ /*
+ * The first character must be an alphabetic letter. The remaining
+ * characters cannot be non-visible graphic characters, and must not be
+ * in the set of "invalid" RCS identifier characters.
+ */
+ if (isalpha ((unsigned char) *tag))
+ {
+ for (cp = tag; *cp; cp++)
+ {
+ if (!isgraph ((unsigned char) *cp))
+ error (1, 0, "tag `%s' has non-visible graphic characters",
+ tag);
+ if (strchr (invalid, *cp))
+ error (1, 0, "tag `%s' must not contain the characters `%s'",
+ tag, invalid);
+ }
+ }
+ else
+ error (1, 0, "tag `%s' must start with a letter", tag);
+}
+
+
+
+/*
+ * Do some consistency checks ...
+ * If a symbolic tag is found, return a newly allocated string
+ * Allow only:
+ * SYMTAG | SYMTAG[.prev]* | .trunk[.prev]* | HEAD | BASE | x.x.x[.prev]*
+ *
+ * ERRORS
+ * fatal errors are generated for illegal syntax
+ */
+char *
+RCS_extract_tag (const char *tag, bool files)
+{
+ char *retval = NULL;
+ const char *p = tag;
+ bool first = true;
+
+ assert(p);
+
+ if (*p == '@')
+ {
+ if (*(p+1) == '<')
+ {
+ retval = xstrdup (p+1);
+ *retval = '@';
+ }
+ else
+ retval = xstrdup (p);
+ return retval;
+ }
+
+ if (strstr (p,".."))
+ error (1, 0, "\
+Numeric tag `%s' invalid. Numeric tags should be of the form X[.X]...", tag);
+
+ bool dot = false;
+ while (p)
+ {
+ if (*p == '.')
+ dot = true;
+ else if (!isdigit ((unsigned char) *p))
+ break;
+ else if (dot && first)
+ error (1, 0, "\
+Numeric tag `%s' invalid. Numeric tags should be of the form X[.X]...", tag);
+ else
+ {
+ dot = false;
+ first = false;
+ }
+ ++p;
+ }
+
+ if ( (*p && !first && !dot) || tag[strlen (tag)-1] == '.')
+ error (1, 0, "\
+Tag `%s' invalid. Combined tags should be of the form X[.X]...", tag);
+
+ bool deptag = false;
+ bool multi = true;
+ char *prev = NULL;
+ char *tmp = xstrdup (p);
+ char *token = strtok(tmp,".");
+ if (token)
+ {
+ do
+ {
+ if (first)
+ {
+ if (!dot)
+ {
+ if (STREQ (token, TAG_HEAD)
+ || STREQ (token, TAG_BASE))
+ {
+ deptag = true;
+ first = false;
+ }
+ else if (!STREQ (token, TAG_TRUNK)
+ && !STREQ (token, TAG_COMMITID)
+ && !STREQ (token, TAG_PREVIOUS)
+ && !STREQ (token, TAG_NEXT)
+ && !STREQ (token, TAG_DOTHEAD)
+ && !STREQ (token, TAG_DOTBASE)
+ && !STREQ (token, TAG_ORIGIN)
+ && !STREQ (token, TAG_ROOT))
+ {
+ RCS_check_tag (token);
+ retval = xstrdup (token);
+ first = false;
+ }
+ else
+ error (1, 0,"\
+Tag `%s' invalid. Reserved expression without leading dot: `%s'", tag, token);
+ }
+ else if (STREQ (token, TAG_TRUNK))
+ {
+ first = false;
+ }
+ else if (STREQ (token, TAG_DOTBASE))
+ {
+ first = false;
+ multi = false;
+ }
+ else if (STREQ (token, TAG_COMMITID))
+ {
+ token = strtok (NULL,".");
+ if (token)
+ {
+ retval = Xasprintf ("@%s",token);
+ first = false;
+ }
+ }
+ else if (!files)
+ error (1, 0,"\
+Tag `%s' invalid. Tag must not be relative: `.%s'", tag, token);
+ else if (STREQ (token, TAG_ORIGIN)
+ || STREQ (token, TAG_DOTHEAD))
+ {
+ first = false;
+ prev = token;
+ }
+ else if (STREQ (token, TAG_PREVIOUS)
+ || STREQ (token, TAG_NEXT)
+ || STREQ (token, TAG_ROOT))
+ {
+ first = false;
+ }
+ else
+ error (1, 0,"\
+Tag `%s' invalid. Cannot resolve head: `.%s'", tag, token);
+ }
+ else if (deptag)
+ error (1, 0,"\
+Tag `%s' invalid. Deprecated prefix before extension: `%s'", tag, token);
+ else if (!multi)
+ error (1, 0,"\
+Tag `%s' invalid. Extension not allowed: `%s'", tag, token);
+ else if (STREQ (token, TAG_ORIGIN)
+ || STREQ (token, TAG_DOTHEAD))
+ {
+ if (!prev || !STREQ (prev, token))
+ prev = token;
+ else
+ error (1, 0,"\
+Tag `%s' invalid. Duplicate extension: `%s'", tag, token);
+
+ }
+ else if ((!STREQ (token, TAG_PREVIOUS))
+ && (!STREQ (token, TAG_NEXT))
+ && (!STREQ (token, TAG_ROOT)))
+ error (1, 0,"\
+Tag `%s' invalid. Cannot resolve extension: `%s'", tag, token);
+ else
+ prev = NULL;
+ }
+ while (!first && (token = strtok (NULL, ".")) );
+ }
+ free (tmp);
+
+ return retval;
+}
+
+
+
+/*
+ * TRUE if argument has valid syntax for an RCS revision or
+ * branch number. All characters must be digits or dots, first
+ * and last characters must be digits, and no two consecutive
+ * characters may be dots.
+ *
+ * Intended for classifying things, so this function doesn't
+ * call error.
+ */
+int
+RCS_valid_rev (const char *rev)
+{
+ char last, c;
+ last = *rev++;
+ if (!isdigit ((unsigned char) last))
+ return 0;
+ while ((c = *rev++)) /* Extra parens placate -Wall gcc option */
+ {
+ if (c == '.')
+ {
+ if (last == '.')
+ return 0;
+ continue;
+ }
+ last = c;
+ if (!isdigit ((unsigned char) c))
+ return 0;
+ }
+ if (!isdigit ((unsigned char) last))
+ return 0;
+ return 1;
+}
+
+
+
+/*
+ * Return true if RCS revision with TAG is a dead revision.
+ */
+int
+RCS_isdead (RCSNode *rcs, const char *tag)
+{
+ Node *p;
+ RCSVers *version;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ p = findnode (rcs->versions, tag);
+ if (p == NULL)
+ return 0;
+
+ version = p->data;
+ return version->dead;
+}
+
+
+
+/* Return the RCS keyword expansion mode. For example "b" for binary.
+ Returns a pointer into storage which is allocated and freed along with
+ the rest of the RCS information; the caller should not modify this
+ storage. Returns NULL if the RCS file does not specify a keyword
+ expansion mode; for all other errors, die with a fatal error. */
+char *
+RCS_getexpand (RCSNode *rcs)
+{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
+ assert (rcs != NULL);
+ return rcs->expand;
+}
+
+
+
+/* Set keyword expansion mode to EXPAND. For example "b" for binary. */
+void
+RCS_setexpand (RCSNode *rcs, const char *expand)
+{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
+ assert (rcs != NULL);
+ if (rcs->expand != NULL)
+ free (rcs->expand);
+ rcs->expand = xstrdup (expand);
+}
+
+
+
+/* RCS keywords, and a matching enum. */
+enum keyword
+{
+ KEYWORD_AUTHOR = 0,
+ KEYWORD_DATE,
+ KEYWORD_CVSHEADER,
+ KEYWORD_HEADER,
+ KEYWORD_ID,
+ KEYWORD_LOCKER,
+ KEYWORD_LOG,
+ KEYWORD_NAME,
+ KEYWORD_RCSFILE,
+ KEYWORD_REVISION,
+ KEYWORD_SOURCE,
+ KEYWORD_STATE,
+ KEYWORD_LOCALID
+};
+struct rcs_keyword
+{
+ const char *string;
+ size_t len;
+ enum keyword expandto;
+ bool expandit;
+};
+
+
+
+static inline struct rcs_keyword *
+new_keywords (void)
+{
+ struct rcs_keyword *new;
+ new = xcalloc (KEYWORD_LOCALID + 2, sizeof (struct rcs_keyword));
+
+#define KEYWORD_INIT(k, i, s) \
+ k[i].string = s; \
+ k[i].len = sizeof s - 1; \
+ k[i].expandto = i; \
+ k[i].expandit = true
+
+ KEYWORD_INIT (new, KEYWORD_AUTHOR, "Author");
+ KEYWORD_INIT (new, KEYWORD_DATE, "Date");
+ KEYWORD_INIT (new, KEYWORD_CVSHEADER, "CVSHeader");
+ KEYWORD_INIT (new, KEYWORD_HEADER, "Header");
+ KEYWORD_INIT (new, KEYWORD_ID, "Id");
+ KEYWORD_INIT (new, KEYWORD_LOCKER, "Locker");
+ KEYWORD_INIT (new, KEYWORD_LOG, "Log");
+ KEYWORD_INIT (new, KEYWORD_NAME, "Name");
+ KEYWORD_INIT (new, KEYWORD_RCSFILE, "RCSfile");
+ KEYWORD_INIT (new, KEYWORD_REVISION, "Revision");
+ KEYWORD_INIT (new, KEYWORD_SOURCE, "Source");
+ KEYWORD_INIT (new, KEYWORD_STATE, "State");
+
+ return new;
+}
+
+
+
+void
+free_keywords (void *keywords)
+{
+ free (keywords);
+}
+
+
+
+/* Convert an RCS date string into a readable string. This is like
+ the RCS date2str function. */
+static char *
+printable_date (const char *rcs_date)
+{
+ int year, mon, mday, hour, min, sec;
+ char buf[100];
+
+ (void) sscanf (rcs_date, SDATEFORM, &year, &mon, &mday, &hour, &min,
+ &sec);
+ if (year < 1900)
+ year += 1900;
+ sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
+ hour, min, sec);
+ return xstrdup (buf);
+}
+
+
+
+/* Escape the characters in a string so that it can be included in an
+ RCS value. */
+static char *
+escape_keyword_value (const char *value, int *free_value)
+{
+ char *ret, *t;
+ const char *s;
+
+ for (s = value; *s != '\0'; s++)
+ {
+ char c;
+
+ c = *s;
+ if (c == '\t'
+ || c == '\n'
+ || c == '\\'
+ || c == ' '
+ || c == '$')
+ {
+ break;
+ }
+ }
+
+ if (*s == '\0')
+ {
+ *free_value = 0;
+ return (char *) value;
+ }
+
+ ret = xmalloc (strlen (value) * 4 + 1);
+ *free_value = 1;
+
+ for (s = value, t = ret; *s != '\0'; s++, t++)
+ {
+ switch (*s)
+ {
+ default:
+ *t = *s;
+ break;
+ case '\t':
+ *t++ = '\\';
+ *t = 't';
+ break;
+ case '\n':
+ *t++ = '\\';
+ *t = 'n';
+ break;
+ case '\\':
+ *t++ = '\\';
+ *t = '\\';
+ break;
+ case ' ':
+ *t++ = '\\';
+ *t++ = '0';
+ *t++ = '4';
+ *t = '0';
+ break;
+ case '$':
+ *t++ = '\\';
+ *t++ = '0';
+ *t++ = '4';
+ *t = '4';
+ break;
+ }
+ }
+
+ *t = '\0';
+
+ return ret;
+}
+
+
+
+/* Expand RCS keywords in the memory buffer BUF of length LEN. This
+ applies to file RCS and version VERS. If NAME is not NULL, and is
+ not a numeric revision, then it is the symbolic tag used for the
+ checkout. EXPAND indicates how to expand the keywords. This
+ function sets *RETBUF and *RETLEN to the new buffer and length.
+ This function may modify the buffer BUF. If BUF != *RETBUF, then
+ RETBUF is a newly allocated buffer. */
+static void
+expand_keywords (RCSNode *rcs, RCSVers *ver, const char *name, const char *log,
+ size_t loglen, enum kflag expand, char *buf, size_t len,
+ char **retbuf, size_t *retlen)
+{
+ struct expand_buffer
+ {
+ struct expand_buffer *next;
+ char *data;
+ size_t len;
+ int free_data;
+ } *ebufs = NULL;
+ struct expand_buffer *ebuf_last = NULL;
+ size_t ebuf_len = 0;
+ char *locker;
+ char *srch, *srch_next;
+ size_t srch_len;
+ const struct rcs_keyword *keywords;
+
+ if (!config /* For `cvs init', config may not be set. */
+ ||expand == KFLAG_O || expand == KFLAG_B)
+ {
+ *retbuf = buf;
+ *retlen = len;
+ return;
+ }
+
+ if (!config->keywords) config->keywords = new_keywords ();
+ keywords = config->keywords;
+
+ /* If we are using -kkvl, dig out the locker information if any. */
+ locker = NULL;
+ if (expand == KFLAG_KVL)
+ {
+ Node *lock;
+ lock = findnode (RCS_getlocks(rcs), ver->version);
+ if (lock != NULL)
+ locker = xstrdup (lock->data);
+ }
+
+ /* RCS keywords look like $STRING$ or $STRING: VALUE$. */
+ srch = buf;
+ srch_len = len;
+ while ((srch_next = memchr (srch, '$', srch_len)) != NULL)
+ {
+ char *s, *send;
+ size_t slen;
+ const struct rcs_keyword *keyword;
+ char *value;
+ int free_value;
+ char *sub;
+ size_t sublen;
+
+ srch_len -= (srch_next + 1) - srch;
+ srch = srch_next + 1;
+
+ /* Look for the first non alphabetic character after the '$'. */
+ send = srch + srch_len;
+ for (s = srch; s < send; s++)
+ if (! isalpha ((unsigned char) *s))
+ break;
+
+ /* If the first non alphabetic character is not '$' or ':',
+ then this is not an RCS keyword. */
+ if (s == send || (*s != '$' && *s != ':'))
+ continue;
+
+ /* See if this is one of the keywords. */
+ slen = s - srch;
+ for (keyword = keywords; keyword->string != NULL; keyword++)
+ {
+ if (keyword->expandit
+ && keyword->len == slen
+ && strncmp (keyword->string, srch, slen) == 0)
+ {
+ break;
+ }
+ }
+ if (keyword->string == NULL)
+ continue;
+
+ /* If the keyword ends with a ':', then the old value consists
+ of the characters up to the next '$'. If there is no '$'
+ before the end of the line, though, then this wasn't an RCS
+ keyword after all. */
+ if (*s == ':')
+ {
+ for (; s < send; s++)
+ if (*s == '$' || *s == '\n')
+ break;
+ if (s == send || *s != '$')
+ continue;
+ }
+
+ /* At this point we must replace the string from SRCH to S
+ with the expansion of the keyword KW. */
+
+ /* Get the value to use. */
+ free_value = 0;
+ if (expand == KFLAG_K)
+ value = NULL;
+ else
+ {
+ switch (keyword->expandto)
+ {
+ default:
+ assert (!"unreached");
+
+ case KEYWORD_AUTHOR:
+ value = ver->author;
+ break;
+
+ case KEYWORD_DATE:
+ value = printable_date (ver->date);
+ free_value = 1;
+ break;
+
+ case KEYWORD_CVSHEADER:
+ case KEYWORD_HEADER:
+ case KEYWORD_ID:
+ case KEYWORD_LOCALID:
+ {
+ const char *path;
+ int free_path;
+ char *date;
+ char *old_path;
+
+ old_path = NULL;
+ if (keyword->expandto == KEYWORD_HEADER)
+ path = rcs->print_path;
+ else if (keyword->expandto == KEYWORD_CVSHEADER)
+ path = getfullCVSname (rcs->print_path, &old_path);
+ else
+ path = last_component (rcs->print_path);
+ path = escape_keyword_value (path, &free_path);
+ date = printable_date (ver->date);
+ value = Xasprintf ("%s %s %s %s %s%s%s",
+ path, ver->version, date, ver->author,
+ ver->state,
+ locker != NULL ? " " : "",
+ locker != NULL ? locker : "");
+ if (free_path)
+ /* If free_path is set then we know we allocated path
+ * and we can discard the const.
+ */
+ free ((char *)path);
+ if (old_path)
+ free (old_path);
+ free (date);
+ free_value = 1;
+ }
+ break;
+
+ case KEYWORD_LOCKER:
+ value = locker;
+ break;
+
+ case KEYWORD_LOG:
+ case KEYWORD_RCSFILE:
+ value = escape_keyword_value (last_component (rcs->print_path),
+ &free_value);
+ break;
+
+ case KEYWORD_NAME:
+ if (name != NULL && ! isdigit ((unsigned char) *name))
+ value = (char *) name;
+ else
+ value = NULL;
+ break;
+
+ case KEYWORD_REVISION:
+ value = ver->version;
+ break;
+
+ case KEYWORD_SOURCE:
+ value = escape_keyword_value (rcs->print_path, &free_value);
+ break;
+
+ case KEYWORD_STATE:
+ value = ver->state;
+ break;
+ }
+ }
+
+ sub = xmalloc (keyword->len
+ + (value == NULL ? 0 : strlen (value))
+ + 10);
+ if (expand == KFLAG_V)
+ {
+ /* Decrement SRCH and increment S to remove the $
+ characters. */
+ --srch;
+ ++srch_len;
+ ++s;
+ sublen = 0;
+ }
+ else
+ {
+ strcpy (sub, keyword->string);
+ sublen = strlen (keyword->string);
+ if (expand != KFLAG_K)
+ {
+ sub[sublen] = ':';
+ sub[sublen + 1] = ' ';
+ sublen += 2;
+ }
+ }
+ if (value != NULL)
+ {
+ strcpy (sub + sublen, value);
+ sublen += strlen (value);
+ }
+ if (expand != KFLAG_V && expand != KFLAG_K)
+ {
+ sub[sublen] = ' ';
+ ++sublen;
+ sub[sublen] = '\0';
+ }
+
+ if (free_value)
+ free (value);
+
+ /* The Log keyword requires special handling. This behaviour
+ is taken from RCS 5.7. The special log message is what RCS
+ uses for ci -k. */
+ if (keyword->expandto == KEYWORD_LOG
+ && (sizeof "checked in with -k by " <= loglen
+ || log == NULL
+ || strncmp (log, "checked in with -k by ",
+ sizeof "checked in with -k by " - 1) != 0))
+ {
+ char *start;
+ char *leader;
+ size_t leader_len, leader_sp_len;
+ const char *logend;
+ const char *snl;
+ int cnl;
+ char *date;
+ const char *sl;
+
+ /* We are going to insert the trailing $ ourselves, before
+ the log message, so we must remove it from S, if we
+ haven't done so already. */
+ if (expand != KFLAG_V)
+ ++s;
+
+ /* CVS never has empty log messages, but old RCS files might. */
+ if (log == NULL)
+ log = "";
+
+ /* Find the start of the line. */
+ start = srch;
+ leader_len = 0;
+ while (start > buf && start[-1] != '\n'
+ && leader_len <= xsum (config->MaxCommentLeaderLength,
+ expand != KFLAG_V ? 1 : 0))
+ {
+ --start;
+ ++leader_len;
+ }
+
+ if (expand != KFLAG_V)
+ /* When automagically determined and !KFLAG_V, we wish to avoid
+ * including the leading `$' of the Log keyword in our leader.
+ */
+ --leader_len;
+
+ /* If the automagically determined leader exceeds the limit set in
+ * CVSROOT/config, try to use a fallback.
+ */
+ if (leader_len > config->MaxCommentLeaderLength)
+ {
+ if (config->UseArchiveCommentLeader && rcs->comment)
+ {
+ leader = xstrdup (rcs->comment);
+ leader_len = strlen (rcs->comment);
+ }
+ else
+ {
+ error (0, 0,
+"Skipping `$" "Log$' keyword due to excessive comment leader.");
+ continue;
+ }
+ }
+ else /* leader_len <= config->MaxCommentLeaderLength */
+ {
+ /* Copy the start of the line to use as a comment leader. */
+ leader = xmalloc (leader_len);
+ memcpy (leader, start, leader_len);
+ }
+
+ leader_sp_len = leader_len;
+ while (leader_sp_len > 0 && isspace (leader[leader_sp_len - 1]))
+ --leader_sp_len;
+
+ /* RCS does some checking for an old style of Log here,
+ but we don't bother. RCS issues a warning if it
+ changes anything. */
+
+ /* Count the number of newlines in the log message so that
+ we know how many copies of the leader we will need. */
+ cnl = 0;
+ logend = log + loglen;
+ for (snl = log; snl < logend; snl++)
+ if (*snl == '\n')
+ ++cnl;
+
+ /* If the log message did not end in a newline, increment
+ * the newline count so we have space for the extra leader.
+ * Failure to do so results in a buffer overrun.
+ */
+ if (loglen && snl[-1] != '\n')
+ ++cnl;
+
+ date = printable_date (ver->date);
+ sub = xrealloc (sub,
+ (sublen
+ + sizeof "Revision"
+ + strlen (ver->version)
+ + strlen (date)
+ + strlen (ver->author)
+ + loglen
+ /* Use CNL + 2 below: One leader for each log
+ * line, plus the Revision/Author/Date line,
+ * plus a trailing blank line.
+ */
+ + (cnl + 2) * leader_len
+ + 20));
+ if (expand != KFLAG_V)
+ {
+ sub[sublen] = '$';
+ ++sublen;
+ }
+ sub[sublen] = '\n';
+ ++sublen;
+ memcpy (sub + sublen, leader, leader_len);
+ sublen += leader_len;
+ sprintf (sub + sublen, "Revision %s %s %s\n",
+ ver->version, date, ver->author);
+ sublen += strlen (sub + sublen);
+ free (date);
+
+ sl = log;
+ while (sl < logend)
+ {
+ if (*sl == '\n')
+ {
+ memcpy (sub + sublen, leader, leader_sp_len);
+ sublen += leader_sp_len;
+ sub[sublen] = '\n';
+ ++sublen;
+ ++sl;
+ }
+ else
+ {
+ const char *slnl;
+
+ memcpy (sub + sublen, leader, leader_len);
+ sublen += leader_len;
+ for (slnl = sl; slnl < logend && *slnl != '\n'; ++slnl)
+ ;
+ if (slnl < logend)
+ ++slnl;
+ memcpy (sub + sublen, sl, slnl - sl);
+ sublen += slnl - sl;
+ if (slnl == logend && slnl[-1] != '\n')
+ {
+ /* There was no EOL at the end of the log message. Add
+ * one.
+ */
+ sub[sublen] = '\n';
+ ++sublen;
+ }
+ sl = slnl;
+ }
+ }
+
+ memcpy (sub + sublen, leader, leader_sp_len);
+ sublen += leader_sp_len;
+
+ free (leader);
+ }
+
+ /* Now SUB contains a string which is to replace the string
+ from SRCH to S. SUBLEN is the length of SUB. */
+
+ if (srch + sublen == s)
+ {
+ memcpy (srch, sub, sublen);
+ free (sub);
+ }
+ else
+ {
+ struct expand_buffer *ebuf;
+
+ /* We need to change the size of the buffer. We build a
+ list of expand_buffer structures. Each expand_buffer
+ structure represents a portion of the final output. We
+ concatenate them back into a single buffer when we are
+ done. This minimizes the number of potentially large
+ buffer copies we must do. */
+
+ if (ebufs == NULL)
+ {
+ ebufs = xmalloc (sizeof *ebuf);
+ ebufs->next = NULL;
+ ebufs->data = buf;
+ ebufs->free_data = 0;
+ ebuf_len = srch - buf;
+ ebufs->len = ebuf_len;
+ ebuf_last = ebufs;
+ }
+ else
+ {
+ assert (srch >= ebuf_last->data);
+ assert (srch <= ebuf_last->data + ebuf_last->len);
+ ebuf_len -= ebuf_last->len - (srch - ebuf_last->data);
+ ebuf_last->len = srch - ebuf_last->data;
+ }
+
+ ebuf = xmalloc (sizeof *ebuf);
+ ebuf->data = sub;
+ ebuf->len = sublen;
+ ebuf->free_data = 1;
+ ebuf->next = NULL;
+ ebuf_last->next = ebuf;
+ ebuf_last = ebuf;
+ ebuf_len += sublen;
+
+ ebuf = xmalloc (sizeof *ebuf);
+ ebuf->data = s;
+ ebuf->len = srch_len - (s - srch);
+ ebuf->free_data = 0;
+ ebuf->next = NULL;
+ ebuf_last->next = ebuf;
+ ebuf_last = ebuf;
+ ebuf_len += srch_len - (s - srch);
+ }
+
+ srch_len -= (s - srch);
+ srch = s;
+ }
+
+ if (locker != NULL)
+ free (locker);
+
+ if (ebufs == NULL)
+ {
+ *retbuf = buf;
+ *retlen = len;
+ }
+ else
+ {
+ char *ret;
+
+ ret = xmalloc (ebuf_len);
+ *retbuf = ret;
+ *retlen = ebuf_len;
+ while (ebufs != NULL)
+ {
+ struct expand_buffer *next;
+
+ memcpy (ret, ebufs->data, ebufs->len);
+ ret += ebufs->len;
+ if (ebufs->free_data)
+ free (ebufs->data);
+ next = ebufs->next;
+ free (ebufs);
+ ebufs = next;
+ }
+ }
+}
+
+
+
+/* Check out a revision from an RCS file.
+
+ If PFN is not NULL, then ignore WORKFILE and SOUT. Call PFN zero
+ or more times with the contents of the file. CALLERDAT is passed,
+ uninterpreted, to PFN. (The current code will always call PFN
+ exactly once for a non empty file; however, the current code
+ assumes that it can hold the entire file contents in memory, which
+ is not a good assumption, and might change in the future).
+
+ Otherwise, if WORKFILE is not NULL, check out the revision to
+ WORKFILE. However, if WORKFILE is not NULL, and noexec is set,
+ then don't do anything.
+
+ Otherwise, if WORKFILE is NULL, check out the revision to SOUT. If
+ SOUT is RUN_TTY, then write the contents of the revision to
+ standard output. When using SOUT, the output is generally a
+ temporary file; don't bother to get the file modes correct. When
+ NOEXEC is set, WORKFILEs are not written but SOUTs are.
+
+ REV is the numeric revision to check out. It may be NULL, which
+ means to check out the head of the default branch.
+
+ If NAMETAG is not NULL, and is not a numeric revision, then it is
+ the tag that should be used when expanding the RCS Name keyword.
+
+ OPTIONS is a string such as "-kb" or "-kv" for keyword expansion
+ options. It may be NULL to use the default expansion mode of the
+ file, typically "-kkv".
+
+ On an error which prevented checking out the file, either print a
+ nonfatal error and return 1, or give a fatal error. On success,
+ return 0. */
+
+/* This function mimics the behavior of `rcs co' almost exactly. The
+ chief difference is in its support for preserving file ownership,
+ permissions, and special files across checkin and checkout -- see
+ comments in RCS_checkin for some issues about this. -twp */
+int
+RCS_checkout (RCSNode *rcs, const char *workfile, const char *rev,
+ const char *nametag, const char *options, const char *sout,
+ RCSCHECKOUTPROC pfn, void *callerdat)
+{
+ int free_rev = 0;
+ enum kflag expand;
+ FILE *fp,
+ *ofp = NULL; /* Initialize since -Wall doesn't understand that
+ * error (1, ...) does not return.
+ */
+ struct stat sb;
+ struct rcsbuffer rcsbuf;
+ char *key;
+ char *value;
+ size_t len;
+ int free_value = 0;
+ char *log = NULL;
+ size_t loglen = 0;
+ Node *vp = NULL;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ uid_t rcs_owner = (uid_t) -1;
+ gid_t rcs_group = (gid_t) -1;
+ mode_t rcs_mode;
+ int change_rcs_owner_or_group = 0;
+ int change_rcs_mode = 0;
+ int special_file = 0;
+ unsigned long devnum_long;
+ dev_t devnum = 0;
+#endif
+
+ TRACE (TRACE_FUNCTION, "RCS_checkout (%s, %s, %s, %s, %s)",
+ rcs->path,
+ rev != NULL ? rev : "",
+ nametag != NULL ? nametag : "",
+ options != NULL ? options : "",
+ (pfn != NULL ? "(function)"
+ : (workfile != NULL ? workfile
+ : (sout != RUN_TTY ? sout
+ : "(stdout)"))));
+
+ assert (rev == NULL || isdigit ((unsigned char) *rev));
+
+ if (noexec && !server_active && workfile != NULL)
+ return 0;
+
+ assert (sout == RUN_TTY || workfile == NULL);
+ assert (pfn == NULL || (sout == RUN_TTY && workfile == NULL));
+
+ /* Some callers, such as Checkin or remove_file, will pass us a
+ branch. */
+ if (rev != NULL && (numdots (rev) & 1) == 0)
+ {
+ rev = RCS_getbranch (rcs, rev, 1);
+ if (rev == NULL)
+ error (1, 0, "internal error: bad branch tag in checkout");
+ free_rev = 1;
+ }
+
+ if (rev == NULL || STREQ (rev, rcs->head))
+ {
+ int gothead;
+
+ /* We want the head revision. Try to read it directly. */
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, &fp, &rcsbuf);
+ else
+ rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf);
+
+ gothead = 0;
+ if (! rcsbuf_getrevnum (&rcsbuf, &key))
+ error (1, 0, "unexpected EOF reading %s", rcs->print_path);
+ while (rcsbuf_getkey (&rcsbuf, &key, &value))
+ {
+ if (STREQ (key, "log"))
+ {
+ if (log)
+ {
+ error (0, 0,
+"Duplicate log keyword found for head revision in RCS file.");
+ free (log);
+ }
+ log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen);
+ }
+ else if (STREQ (key, "text"))
+ {
+ gothead = 1;
+ break;
+ }
+ }
+
+ if (! gothead)
+ {
+ error (0, 0, "internal error: cannot find head text");
+ if (free_rev)
+ /* It's okay to discard the const when free_rev is set, because
+ * we know we allocated it in this function.
+ */
+ free ((char *)rev);
+ return 1;
+ }
+
+ rcsbuf_valpolish (&rcsbuf, value, 0, &len);
+
+ if (fstat (fileno (fp), &sb) < 0)
+ error (1, errno, "cannot fstat %s", rcs->path);
+
+ rcsbuf_cache (rcs, &rcsbuf);
+ }
+ else
+ {
+ struct rcsbuffer *rcsbufp;
+
+ /* It isn't the head revision of the trunk. We'll need to
+ walk through the deltas. */
+
+ fp = NULL;
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, &fp, &rcsbuf);
+
+ if (fp == NULL)
+ {
+ /* If RCS_deltas didn't close the file, we could use fstat
+ here too. Probably should change it thusly.... */
+ if (stat (rcs->path, &sb) < 0)
+ error (1, errno, "cannot stat %s", rcs->path);
+ rcsbufp = NULL;
+ }
+ else
+ {
+ if (fstat (fileno (fp), &sb) < 0)
+ error (1, errno, "cannot fstat %s", rcs->path);
+ rcsbufp = &rcsbuf;
+ }
+
+ RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len,
+ &log, &loglen);
+ free_value = 1;
+ }
+
+ /* If OPTIONS is NULL or the empty string, then the old code would
+ invoke the RCS co program with no -k option, which means that
+ co would use the string we have stored in rcs->expand. */
+ if ((options == NULL || options[0] == '\0') && rcs->expand == NULL)
+ expand = KFLAG_KV;
+ else
+ {
+ const char *ouroptions;
+ const char * const *cpp;
+
+ if (options != NULL && options[0] != '\0')
+ {
+ assert (options[0] == '-' && options[1] == 'k');
+ ouroptions = options + 2;
+ }
+ else
+ ouroptions = rcs->expand;
+
+ for (cpp = kflags; *cpp != NULL; cpp++)
+ if (STREQ (*cpp, ouroptions))
+ break;
+
+ if (*cpp != NULL)
+ expand = (enum kflag) (cpp - kflags);
+ else
+ {
+ error (0, 0,
+ "internal error: unsupported substitution string -k%s",
+ ouroptions);
+ expand = KFLAG_KV;
+ }
+ }
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* Handle special files and permissions, if that is desired. */
+ if (preserve_perms)
+ {
+ RCSVers *vers;
+ Node *info;
+
+ vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
+ if (vp == NULL)
+ error (1, 0, "internal error: no revision information for %s",
+ rev == NULL ? rcs->head : rev);
+ vers = vp->data;
+
+ /* First we look for symlinks, which are simplest to handle. */
+ info = findnode (vers->other_delta, "symlink");
+ if (info != NULL)
+ {
+ char *dest;
+
+ if (pfn != NULL || (workfile == NULL && sout == RUN_TTY))
+ error (1, 0, "symbolic link %s:%s cannot be piped",
+ rcs->path, vers->version);
+ if (workfile == NULL)
+ dest = sout;
+ else
+ dest = workfile;
+
+ /* Remove `dest', just in case. It's okay to get ENOENT here,
+ since we just want the file not to be there. (TODO: decide
+ whether it should be considered an error for `dest' to exist
+ at this point. If so, the unlink call should be removed and
+ `symlink' should signal the error. -twp) */
+ if (CVS_UNLINK (dest) < 0 && !existence_error (errno))
+ error (1, errno, "cannot remove %s", dest);
+ if (symlink (info->data, dest) < 0)
+ error (1, errno, "cannot create symbolic link from %s to %s",
+ dest, (char *)info->data);
+ if (free_value)
+ free (value);
+ if (free_rev)
+ /* It's okay to discard the const when free_rev is set, because
+ * we know we allocated it in this function.
+ */
+ free ((char *)rev);
+ return 0;
+ }
+
+ /* Next, we look at this file's hardlinks field, and see whether
+ it is linked to any other file that has been checked out.
+ If so, we don't do anything else -- just link it to that file.
+
+ If we are checking out a file to a pipe or temporary storage,
+ none of this should matter. Hence the `workfile != NULL'
+ wrapper around the whole thing. -twp */
+
+ if (workfile != NULL)
+ {
+ List *links = vers->hardlinks;
+ if (links != NULL)
+ {
+ Node *uptodate_link;
+
+ /* For each file in the hardlinks field, check to see
+ if it exists, and if so, if it has been checked out
+ this iteration. When walklist returns, uptodate_link
+ should point to a hardlist node representing a file
+ in `links' which has recently been checked out, or
+ NULL if no file in `links' has yet been checked out. */
+
+ uptodate_link = NULL;
+ (void) walklist (links, find_checkedout_proc, &uptodate_link);
+ dellist (&links);
+
+ /* If we've found a file that `workfile' is supposed to be
+ linked to, and it has been checked out since CVS was
+ invoked, then simply link workfile to that file and return.
+
+ If one of these conditions is not met, then
+ workfile is the first one in its hardlink group to
+ be checked out, and we must continue with a full
+ checkout. */
+
+ if (uptodate_link != NULL)
+ {
+ struct hardlink_info *hlinfo = uptodate_link->data;
+
+ if (link (uptodate_link->key, workfile) < 0)
+ error (1, errno, "cannot link %s to %s",
+ workfile, uptodate_link->key);
+ hlinfo->checked_out = 1; /* probably unnecessary */
+ if (free_value)
+ free (value);
+ if (free_rev)
+ /* It's okay to discard the const when free_rev is set,
+ * because we know we allocated it in this function.
+ */
+ free ((char *)rev);
+ return 0;
+ }
+ }
+ }
+
+ info = findnode (vers->other_delta, "owner");
+ if (info != NULL)
+ {
+ change_rcs_owner_or_group = 1;
+ rcs_owner = (uid_t) strtoul (info->data, NULL, 10);
+ }
+ info = findnode (vers->other_delta, "group");
+ if (info != NULL)
+ {
+ change_rcs_owner_or_group = 1;
+ rcs_group = (gid_t) strtoul (info->data, NULL, 10);
+ }
+ info = findnode (vers->other_delta, "permissions");
+ if (info != NULL)
+ {
+ change_rcs_mode = 1;
+ rcs_mode = (mode_t) strtoul (info->data, NULL, 8);
+ }
+ info = findnode (vers->other_delta, "special");
+ if (info != NULL)
+ {
+ /* If the size of `devtype' changes, fix the sscanf call also */
+ char devtype[16];
+
+ if (sscanf (info->data, "%15s %lu",
+ devtype, &devnum_long) < 2)
+ error (1, 0, "%s:%s has bad `special' newphrase %s",
+ workfile, vers->version, (char *)info->data);
+ devnum = devnum_long;
+ if (STREQ (devtype, "character"))
+ special_file = S_IFCHR;
+ else if (STREQ (devtype, "block"))
+ special_file = S_IFBLK;
+ else
+ error (0, 0, "%s is a special file of unsupported type `%s'",
+ workfile, (char *)info->data);
+ }
+ }
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+
+ if (expand != KFLAG_O && expand != KFLAG_B)
+ {
+ char *newvalue;
+
+ /* Don't fetch the delta node again if we already have it. */
+ if (vp == NULL)
+ {
+ vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
+ if (vp == NULL)
+ error (1, 0, "internal error: no revision information for %s",
+ rev == NULL ? rcs->head : rev);
+ }
+
+ expand_keywords (rcs, vp->data, nametag, log, loglen,
+ expand, value, len, &newvalue, &len);
+
+ if (newvalue != value)
+ {
+ if (free_value)
+ free (value);
+ value = newvalue;
+ free_value = 1;
+ }
+ }
+
+ if (free_rev)
+ /* It's okay to discard the const when free_rev is set, because
+ * we know we allocated it in this function.
+ */
+ free ((char *)rev);
+
+ if (log != NULL)
+ {
+ free (log);
+ log = NULL;
+ }
+
+ if (pfn != NULL)
+ {
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (special_file)
+ error (1, 0, "special file %s cannot be piped to anything",
+ rcs->path);
+#endif
+ /* The PFN interface is very simple to implement right now, as
+ we always have the entire file in memory. */
+ if (len != 0)
+ pfn (callerdat, value, len);
+ }
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ else if (special_file)
+ {
+# ifdef HAVE_MKNOD
+ char *dest;
+
+ /* Can send either to WORKFILE or to SOUT, as long as SOUT is
+ not RUN_TTY. */
+ dest = workfile;
+ if (dest == NULL)
+ {
+ if (sout == RUN_TTY)
+ error (1, 0, "special file %s cannot be written to stdout",
+ rcs->path);
+ dest = sout;
+ }
+
+ /* Unlink `dest', just in case. It's okay if this provokes a
+ ENOENT error. */
+ if (CVS_UNLINK (dest) < 0 && existence_error (errno))
+ error (1, errno, "cannot remove %s", dest);
+ if (mknod (dest, special_file, devnum) < 0)
+ error (1, errno, "could not create special file %s",
+ dest);
+# else
+ error (1, 0,
+"cannot create %s: unable to create special files on this system",
+workfile);
+# endif
+ }
+#endif
+ else
+ {
+ /* Not a special file: write to WORKFILE or SOUT. */
+ if (workfile == NULL)
+ {
+ if (sout == RUN_TTY)
+ ofp = stdout;
+ else
+ {
+ /* Symbolic links should be removed before replacement, so that
+ `fopen' doesn't follow the link and open the wrong file. */
+ if (islink (sout))
+ if (unlink_file (sout) < 0)
+ error (1, errno, "cannot remove %s", sout);
+ ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w");
+ if (ofp == NULL)
+ error (1, errno, "cannot open %s", sout);
+ }
+ }
+ else
+ {
+ /* Output is supposed to go to WORKFILE, so we should open that
+ file. Symbolic links should be removed first (see above). */
+ if (islink (workfile))
+ if (unlink_file (workfile) < 0)
+ error (1, errno, "cannot remove %s", workfile);
+
+ ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
+
+ /* If the open failed because the existing workfile was not
+ writable, try to chmod the file and retry the open. */
+ if (ofp == NULL && errno == EACCES
+ && isfile (workfile) && !iswritable (workfile))
+ {
+ xchmod (workfile, 1);
+ ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
+ }
+
+ if (ofp == NULL)
+ {
+ error (0, errno, "cannot open %s", workfile);
+ if (free_value)
+ free (value);
+ return 1;
+ }
+ }
+
+ if (workfile == NULL && sout == RUN_TTY)
+ {
+ if (expand == KFLAG_B)
+ cvs_output_binary (value, len);
+ else
+ {
+ /* cvs_output requires the caller to check for zero
+ length. */
+ if (len > 0)
+ cvs_output (value, len);
+ }
+ }
+ else
+ {
+ /* NT 4.0 is said to have trouble writing 2099999 bytes
+ (for example) in a single fwrite. So break it down
+ (there is no need to be writing that much at once
+ anyway; it is possible that LARGEST_FWRITE should be
+ somewhat larger for good performance, but for testing I
+ want to start with a small value until/unless a bigger
+ one proves useful). */
+#define LARGEST_FWRITE 8192
+ size_t nleft = len;
+ size_t nstep = (len < LARGEST_FWRITE ? len : LARGEST_FWRITE);
+ char *p = value;
+
+ while (nleft > 0)
+ {
+ if (fwrite (p, 1, nstep, ofp) != nstep)
+ {
+ error (0, errno, "cannot write %s",
+ (workfile != NULL
+ ? workfile
+ : (sout != RUN_TTY ? sout : "stdout")));
+ if (free_value)
+ free (value);
+ return 1;
+ }
+ p += nstep;
+ nleft -= nstep;
+ if (nleft < nstep)
+ nstep = nleft;
+ }
+ }
+ }
+
+ if (free_value)
+ free (value);
+
+ if (workfile != NULL)
+ {
+ int ret;
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (!special_file && fclose (ofp) < 0)
+ {
+ error (0, errno, "cannot close %s", workfile);
+ return 1;
+ }
+
+ if (change_rcs_owner_or_group)
+ {
+ if (chown (workfile, rcs_owner, rcs_group) < 0)
+ error (0, errno, "could not change owner or group of %s",
+ workfile);
+ }
+
+ ret = chmod (workfile,
+ change_rcs_mode
+ ? rcs_mode
+ : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
+#else
+ if (fclose (ofp) < 0)
+ {
+ error (0, errno, "cannot close %s", workfile);
+ return 1;
+ }
+
+ ret = chmod (workfile,
+ sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
+#endif
+ if (ret < 0)
+ {
+ error (0, errno, "cannot change mode of file %s",
+ workfile);
+ }
+ }
+ else if (sout != RUN_TTY)
+ {
+ if (
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ !special_file &&
+#endif
+ fclose (ofp) < 0)
+ {
+ error (0, errno, "cannot close %s", sout);
+ return 1;
+ }
+ }
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* If we are in the business of preserving hardlinks, then
+ mark this file as having been checked out. */
+ if (preserve_perms && workfile != NULL)
+ update_hardlink_info (workfile);
+#endif
+
+ return 0;
+}
+
+
+
+/* Find the delta currently locked by the user. From the `ci' man page:
+
+ "If rev is omitted, ci tries to derive the new revision
+ number from the caller's last lock. If the caller has
+ locked the tip revision of a branch, the new revision is
+ appended to that branch. The new revision number is
+ obtained by incrementing the tip revision number. If the
+ caller locked a non-tip revision, a new branch is started
+ at that revision by incrementing the highest branch number
+ at that revision. The default initial branch and level
+ numbers are 1.
+
+ If rev is omitted and the caller has no lock, but owns the
+ file and locking is not set to strict, then the revision
+ is appended to the default branch (normally the trunk; see
+ the -b option of rcs(1))."
+
+ RCS_findlock_or_tip finds the unique revision locked by the caller
+ and returns its delta node. If the caller has not locked any
+ revisions (and is permitted to commit to an unlocked delta, as
+ described above), return the tip of the default branch. */
+static RCSVers *
+RCS_findlock_or_tip (RCSNode *rcs)
+{
+ char *user = getcaller();
+ Node *lock, *p;
+ List *locklist;
+
+ /* Find unique delta locked by caller. This code is very similar
+ to the code in RCS_unlock -- perhaps it could be abstracted
+ into a RCS_findlock function. */
+ locklist = RCS_getlocks (rcs);
+ lock = NULL;
+ for (p = locklist->list->next; p != locklist->list; p = p->next)
+ {
+ if (STREQ (p->data, user))
+ {
+ if (lock != NULL)
+ {
+ error (0, 0, "\
+%s: multiple revisions locked by %s; please specify one", rcs->print_path,
user);
+ return NULL;
+ }
+ lock = p;
+ }
+ }
+
+ if (lock != NULL)
+ {
+ /* Found an old lock, but check that the revision still exists. */
+ p = findnode (rcs->versions, lock->key);
+ if (p == NULL)
+ {
+ error (0, 0, "%s: can't unlock nonexistent revision %s",
+ rcs->print_path,
+ lock->key);
+ return NULL;
+ }
+ return p->data;
+ }
+
+ /* No existing lock. The RCS rule is that this is an error unless
+ locking is nonstrict AND the file is owned by the current
+ user. Trying to determine the latter is a portability nightmare
+ in the face of NT, VMS, AFS, and other systems with non-unix-like
+ ideas of users and owners. In the case of CVS, we should never get
+ here (as long as the traditional behavior of making sure to call
+ RCS_lock persists). Anyway, we skip the RCS error checks
+ and just return the default branch or head. The reasoning is that
+ those error checks are to make users lock before a checkin, and we do
+ that in other ways if at all anyway (e.g. rcslock.pl). */
+
+ p = findnode (rcs->versions, RCS_getbranch (rcs, rcs->branch, 0));
+ if (!p)
+ {
+ error (0, 0, "RCS file `%s' does not contain its default revision.",
+ rcs->path);
+ return NULL;
+ }
+
+ return p->data;
+}
+
+
+
+/* Revision number string, R, must contain a `.'.
+ Return a newly-malloc'd copy of the prefix of R up
+ to but not including the final `.'. */
+static char *
+truncate_revnum (const char *r)
+{
+ size_t len;
+ char *new_r;
+ char *dot = strrchr (r, '.');
+
+ assert (dot);
+ len = dot - r;
+ new_r = xmalloc (len + 1);
+ memcpy (new_r, r, len);
+ new_r[len] = '\0';
+ return new_r;
+}
+
+
+
+/* Revision number strings, R and S, must each contain a `.'.
+ R and S must be writable and must have the same number of dots.
+ Truncate R and S for the comparison, then restored them to their
+ original state.
+ Return the result (see compare_revnums) of comparing R and S
+ ignoring differences in any component after the rightmost `.'. */
+static int
+compare_truncated_revnums (char *r, char *s)
+{
+ char *r_dot = truncate_revnum_in_place (r);
+ char *s_dot = truncate_revnum_in_place (s);
+ int cmp;
+
+ assert (numdots (r) == numdots (s));
+
+ cmp = compare_revnums (r, s);
+
+ *r_dot = '.';
+ *s_dot = '.';
+
+ return cmp;
+}
+
+
+
+/* Return a malloc'd copy of the string representing the highest branch
+ number on BRANCHNODE. If there are no branches on BRANCHNODE, return NULL.
+ FIXME: isn't the max rev always the last one?
+ If so, we don't even need a loop. */
+static char *
+max_rev (const RCSVers *branchnode)
+{
+ Node *head;
+ Node *bp;
+ char *max;
+
+ if (branchnode->branches == NULL)
+ {
+ return NULL;
+ }
+
+ max = NULL;
+ head = branchnode->branches->list;
+ for (bp = head->next; bp != head; bp = bp->next)
+ {
+ if (max == NULL || compare_truncated_revnums (max, bp->key) < 0)
+ {
+ max = bp->key;
+ }
+ }
+ assert (max);
+
+ return truncate_revnum (max);
+}
+
+
+
+/* Create BRANCH in RCS's delta tree. BRANCH may be either a branch
+ number or a revision number. In the former case, create the branch
+ with the specified number; in the latter case, create a new branch
+ rooted at node BRANCH with a higher branch number than any others.
+ Return the number of the tip node on the new branch. */
+static char *
+RCS_addbranch (RCSNode *rcs, const char *branch)
+{
+ char *branchpoint, *newrevnum;
+ Node *nodep, *bp;
+ Node *marker;
+ RCSVers *branchnode;
+
+ assert (branch);
+
+ /* Append to end by default. */
+ marker = NULL;
+
+ branchpoint = xstrdup (branch);
+ if ((numdots (branchpoint) & 1) == 0)
+ {
+ truncate_revnum_in_place (branchpoint);
+ }
+
+ /* Find the branch rooted at BRANCHPOINT. */
+ nodep = findnode (rcs->versions, branchpoint);
+ if (nodep == NULL)
+ {
+ error (0, 0, "%s: can't find branch point %s", rcs->print_path,
branchpoint);
+ free (branchpoint);
+ return NULL;
+ }
+ free (branchpoint);
+ branchnode = nodep->data;
+
+ /* If BRANCH was a full branch number, make sure it is higher than MAX. */
+ if ((numdots (branch) & 1) == 1)
+ {
+ if (branchnode->branches == NULL)
+ {
+ /* We have to create the first branch on this node, which means
+ appending ".2" to the revision number. */
+ newrevnum = Xasprintf ("%s.2", branch);
+ }
+ else
+ {
+ char *max = max_rev (branchnode);
+ assert (max);
+ newrevnum = increment_revnum (max);
+ free (max);
+ }
+ }
+ else
+ {
+ newrevnum = xstrdup (branch);
+
+ if (branchnode->branches != NULL)
+ {
+ Node *head;
+ Node *bp;
+
+ /* Find the position of this new branch in the sorted list
+ of branches. */
+ head = branchnode->branches->list;
+ for (bp = head->next; bp != head; bp = bp->next)
+ {
+ char *dot;
+ int found_pos;
+
+ /* The existing list must be sorted on increasing revnum. */
+ assert (bp->next == head
+ || compare_truncated_revnums (bp->key,
+ bp->next->key) < 0);
+ dot = truncate_revnum_in_place (bp->key);
+ found_pos = (compare_revnums (branch, bp->key) < 0);
+ *dot = '.';
+
+ if (found_pos)
+ {
+ break;
+ }
+ }
+ marker = bp;
+ }
+ }
+
+ newrevnum = xrealloc (newrevnum, strlen (newrevnum) + 3);
+ strcat (newrevnum, ".1");
+
+ /* Add this new revision number to BRANCHPOINT's branches list. */
+ if (branchnode->branches == NULL)
+ branchnode->branches = getlist();
+ bp = getnode();
+ bp->key = xstrdup (newrevnum);
+
+ /* Append to the end of the list by default, that is, just before
+ the header node, `list'. */
+ if (marker == NULL)
+ marker = branchnode->branches->list;
+
+ {
+ int fail;
+ fail = insert_before (branchnode->branches, marker, bp);
+ assert (!fail);
+ }
+
+ return newrevnum;
+}
+
+
+
+/* Check in to RCSFILE with revision REV (which must be greater than
+ the largest revision) and message MESSAGE (which is checked for
+ validity). If FLAGS & RCS_FLAGS_DEAD, check in a dead revision.
+ If FLAGS & RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS &
+ RCS_FLAGS_MODTIME, use the working file's modification time for the
+ checkin time. WORKFILE is the working file to check in from, or
+ NULL to use the usual RCS rules for deriving it from the RCSFILE.
+ If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file;
+ unlinking the working file is standard RCS behavior, but is rarely
+ appropriate for CVS.
+
+ UPDATE_DIR is used to print the path for the file. This argument is
+ unnecessary when FLAGS & RCS_FLAGS_QUIET since the path won't be printed
+ anyhow.
+
+ This function should almost exactly mimic the behavior of `rcs ci'. The
+ principal point of difference is the support here for preserving file
+ ownership and permissions in the delta nodes. This is not a clean
+ solution -- precisely because it diverges from RCS's behavior -- but
+ it doesn't seem feasible to do this anywhere else in the code. [-twp]
+
+ Return value is -1 for error (and errno is set to indicate the
+ error), positive for error (and an error message has been printed),
+ or zero for success. */
+int
+RCS_checkin (RCSNode *rcs, const char *update_dir, const char *workfile_in,
+ const char *message, const char *rev, time_t citime, int flags)
+{
+ RCSVers *delta, *commitpt;
+ Deltatext *dtext;
+ Node *nodep;
+ char *tmpfile, *changefile;
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+ size_t bufsize;
+ int status, checkin_quiet;
+ struct tm *ftm;
+ time_t modtime;
+ int adding_branch = 0;
+ char *workfile = xstrdup (workfile_in);
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ struct stat sb;
+#endif
+ Node *np;
+
+ commitpt = NULL;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* Get basename of working file. Is there a library function to
+ do this? I couldn't find one. -twp */
+ if (workfile == NULL)
+ {
+ char *p;
+ int extlen = strlen (RCSEXT);
+ assert (rcs->path);
+ workfile = xstrdup (last_component (rcs->path));
+ p = workfile + (strlen (workfile) - extlen);
+ assert (strncmp (p, RCSEXT, extlen) == 0);
+ *p = '\0';
+ }
+
+ /* If the filename is a symbolic link, follow it and replace it
+ with the destination of the link. We need to do this before
+ calling rcs_internal_lockfile, or else we won't put the lock in
+ the right place. */
+ resolve_symlink (&(rcs->path));
+
+ checkin_quiet = flags & RCS_FLAGS_QUIET;
+ if (!(checkin_quiet || really_quiet))
+ {
+ cvs_output (rcs->path, 0);
+ cvs_output (" <-- ", 7);
+ if (update_dir && strlen (update_dir))
+ {
+ cvs_output (update_dir, 0);
+ cvs_output ("/", 1);
+ }
+ cvs_output (workfile, 0);
+ cvs_output ("\n", 1);
+ }
+
+ /* Create new delta node. */
+ delta = xmalloc (sizeof (RCSVers));
+ memset (delta, 0, sizeof (RCSVers));
+ delta->author = xstrdup (getcaller ());
+ if (flags & RCS_FLAGS_MODTIME)
+ {
+ struct stat ws;
+ if (stat (workfile, &ws) < 0)
+ {
+ error (1, errno, "cannot stat %s", workfile);
+ }
+ modtime = ws.st_mtime;
+ }
+ else if (flags & RCS_FLAGS_USETIME)
+ modtime = citime;
+ else
+ (void) time (&modtime);
+ ftm = gmtime (&modtime);
+ delta->date = Xasprintf (DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ if (flags & RCS_FLAGS_DEAD)
+ {
+ delta->state = xstrdup (RCSDEAD);
+ delta->dead = 1;
+ }
+ else
+ delta->state = xstrdup ("Exp");
+
+ delta->other_delta = getlist();
+
+ /* save the commit ID */
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("commitid");
+ np->data = xstrdup(global_session_id);
+ addnode (delta->other_delta, np);
+
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* If permissions should be preserved on this project, then
+ save the permission info. */
+ if (preserve_perms)
+ {
+ Node *np;
+ char buf[64]; /* static buffer should be safe: see usage. -twp */
+
+ delta->other_delta = getlist();
+
+ if (lstat (workfile, &sb) < 0)
+ error (1, errno, "cannot lstat %s", workfile);
+
+ if (S_ISLNK (sb.st_mode))
+ {
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("symlink");
+ np->data = Xreadlink (workfile, sb.st_size);
+ addnode (delta->other_delta, np);
+ }
+ else
+ {
+ (void) sprintf (buf, "%u", sb.st_uid);
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("owner");
+ np->data = xstrdup (buf);
+ addnode (delta->other_delta, np);
+
+ (void) sprintf (buf, "%u", sb.st_gid);
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("group");
+ np->data = xstrdup (buf);
+ addnode (delta->other_delta, np);
+
+ (void) sprintf (buf, "%o", sb.st_mode & 07777);
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("permissions");
+ np->data = xstrdup (buf);
+ addnode (delta->other_delta, np);
+
+ /* Save device number. */
+ switch (sb.st_mode & S_IFMT)
+ {
+ case S_IFREG: break;
+ case S_IFCHR:
+ case S_IFBLK:
+# ifdef HAVE_STRUCT_STAT_ST_RDEV
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("special");
+ sprintf (buf, "%s %lu",
+ ((sb.st_mode & S_IFMT) == S_IFCHR
+ ? "character" : "block"),
+ (unsigned long) sb.st_rdev);
+ np->data = xstrdup (buf);
+ addnode (delta->other_delta, np);
+# else
+ error (0, 0,
+"can't preserve %s: unable to save device files on this system",
+workfile);
+# endif
+ break;
+
+ default:
+ error (0, 0, "special file %s has unknown type", workfile);
+ }
+
+ /* Save hardlinks. */
+ delta->hardlinks = list_linked_files_on_disk (workfile);
+ }
+ }
+#endif
+
+ /* Create a new deltatext node. */
+ dtext = xmalloc (sizeof (Deltatext));
+ memset (dtext, 0, sizeof (Deltatext));
+
+ dtext->log = make_message_rcsvalid (message);
+
+ /* If the delta tree is empty, then there's nothing to link the
+ new delta into. So make a new delta tree, snarf the working
+ file contents, and just write the new RCS file. */
+ if (rcs->head == NULL)
+ {
+ char *newrev;
+ FILE *fout;
+
+ /* Figure out what the first revision number should be. */
+ if (rev == NULL || *rev == '\0')
+ newrev = xstrdup ("1.1");
+ else if (numdots (rev) == 0)
+ {
+ newrev = Xasprintf ("%s.1", rev);
+ }
+ else
+ newrev = xstrdup (rev);
+
+ /* Don't need to xstrdup NEWREV because it's already dynamic, and
+ not used for anything else. (Don't need to free it, either.) */
+ rcs->head = newrev;
+ delta->version = xstrdup (newrev);
+ nodep = getnode();
+ nodep->type = RCSVERS;
+ nodep->delproc = rcsvers_delproc;
+ nodep->data = delta;
+ nodep->key = delta->version;
+ (void) addnode (rcs->versions, nodep);
+
+ dtext->version = xstrdup (newrev);
+ bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ bufsize = 0;
+ else
+#endif
+ get_file (workfile, workfile,
+ rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
+ &dtext->text, &bufsize, &dtext->len);
+
+ if (!(checkin_quiet || really_quiet))
+ {
+ cvs_output ("initial revision: ", 0);
+ cvs_output (rcs->head, 0);
+ cvs_output ("\n", 1);
+ }
+
+ /* We are probably about to invalidate any cached file. */
+ rcsbuf_cache_close ();
+
+ fout = rcs_internal_lockfile (rcs->path);
+ RCS_putadmin (rcs, fout);
+ RCS_putdtree (rcs, rcs->head, fout);
+ RCS_putdesc (rcs, fout);
+ rcs->delta_pos = ftello (fout);
+ if (rcs->delta_pos == -1)
+ error (1, errno, "cannot ftello for %s", rcs->path);
+ putdeltatext (fout, dtext);
+ rcs_internal_unlockfile (fout, rcs->path);
+
+ if ((flags & RCS_FLAGS_KEEPFILE) == 0)
+ {
+ if (unlink_file (workfile) < 0)
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (0, errno, "cannot remove %s", workfile);
+ }
+
+ status = 0;
+ goto checkin_done;
+ }
+
+ /* Derive a new revision number. From the `ci' man page:
+
+ "If rev is a revision number, it must be higher than the
+ latest one on the branch to which rev belongs, or must
+ start a new branch.
+
+ If rev is a branch rather than a revision number, the new
+ revision is appended to that branch. The level number is
+ obtained by incrementing the tip revision number of that
+ branch. If rev indicates a non-existing branch, that
+ branch is created with the initial revision numbered
+ rev.1."
+
+ RCS_findlock_or_tip handles the case where REV is omitted.
+ RCS 5.7 also permits REV to be "$" or to begin with a dot, but
+ we do not address those cases -- every routine that calls
+ RCS_checkin passes it a numeric revision. */
+
+ if (rev == NULL || *rev == '\0')
+ {
+ /* Figure out where the commit point is by looking for locks.
+ If the commit point is at the tip of a branch (or is the
+ head of the delta tree), then increment its revision number
+ to obtain the new revnum. Otherwise, start a new
+ branch. */
+ commitpt = RCS_findlock_or_tip (rcs);
+ if (commitpt == NULL)
+ {
+ status = 1;
+ goto checkin_done;
+ }
+ else if (commitpt->next == NULL
+ || STREQ (commitpt->version, rcs->head))
+ delta->version = increment_revnum (commitpt->version);
+ else
+ delta->version = RCS_addbranch (rcs, commitpt->version);
+ }
+ else
+ {
+ /* REV is either a revision number or a branch number. Find the
+ tip of the target branch. */
+ char *branch, *tip, *newrev, *p;
+ int dots, isrevnum;
+
+ assert (isdigit ((unsigned char) *rev));
+
+ newrev = xstrdup (rev);
+ dots = numdots (newrev);
+ isrevnum = dots & 1;
+
+ branch = xstrdup (rev);
+ if (isrevnum)
+ {
+ p = strrchr (branch, '.');
+ *p = '\0';
+ }
+
+ /* Find the tip of the target branch. If we got a one- or two-digit
+ revision number, this will be the head of the tree. Exception:
+ if rev is a single-field revision equal to the branch number of
+ the trunk (usually "1") then we want to treat it like an ordinary
+ branch revision. */
+ if (dots == 0)
+ {
+ tip = xstrdup (rcs->head);
+ if (atoi (tip) != atoi (branch))
+ {
+ newrev = xrealloc (newrev, strlen (newrev) + 3);
+ strcat (newrev, ".1");
+ dots = isrevnum = 1;
+ }
+ }
+ else if (dots == 1)
+ tip = xstrdup (rcs->head);
+ else
+ tip = RCS_getbranch (rcs, branch, 1);
+
+ /* If the branch does not exist, and we were supplied an exact
+ revision number, signal an error. Otherwise, if we were
+ given only a branch number, create it and set COMMITPT to
+ the branch point. */
+ if (tip == NULL)
+ {
+ if (isrevnum)
+ {
+ error (0, 0, "%s: can't find branch point %s",
+ rcs->print_path, branch);
+ free (branch);
+ free (newrev);
+ status = 1;
+ goto checkin_done;
+ }
+ delta->version = RCS_addbranch (rcs, branch);
+ if (!delta->version)
+ {
+ free (branch);
+ free (newrev);
+ status = 1;
+ goto checkin_done;
+ }
+ adding_branch = 1;
+ p = strrchr (branch, '.');
+ *p = '\0';
+ tip = xstrdup (branch);
+ }
+ else
+ {
+ if (isrevnum)
+ {
+ /* NEWREV must be higher than TIP. */
+ if (compare_revnums (tip, newrev) >= 0)
+ {
+ error (0, 0,
+ "%s: revision %s too low; must be higher than %s",
+ rcs->print_path,
+ newrev, tip);
+ free (branch);
+ free (newrev);
+ free (tip);
+ status = 1;
+ goto checkin_done;
+ }
+ delta->version = xstrdup (newrev);
+ }
+ else
+ /* Just increment the tip number to get the new revision. */
+ delta->version = increment_revnum (tip);
+ }
+
+ nodep = findnode (rcs->versions, tip);
+ commitpt = nodep->data;
+
+ free (branch);
+ free (newrev);
+ free (tip);
+ }
+
+ assert (delta->version != NULL);
+
+ /* If COMMITPT is locked by us, break the lock. If it's locked
+ by someone else, signal an error. */
+ nodep = findnode (RCS_getlocks (rcs), commitpt->version);
+ if (nodep != NULL)
+ {
+ if (! STREQ (nodep->data, delta->author))
+ {
+ /* If we are adding a branch, then leave the old lock around.
+ That is sensible in the sense that when adding a branch,
+ we don't need to use the lock to tell us where to check
+ in. It is fishy in the sense that if it is our own lock,
+ we break it. However, this is the RCS 5.7 behavior (at
+ the end of addbranch in ci.c in RCS 5.7, it calls
+ removelock only if it is our own lock, not someone
+ else's). */
+
+ if (!adding_branch)
+ {
+ error (0, 0, "%s: revision %s locked by %s",
+ rcs->print_path,
+ nodep->key, (char *)nodep->data);
+ status = 1;
+ goto checkin_done;
+ }
+ }
+ else
+ delnode (nodep);
+ }
+
+ dtext->version = xstrdup (delta->version);
+
+ /* Obtain the change text for the new delta. If DELTA is to be the
+ new head of the tree, then its change text should be the contents
+ of the working file, and LEAFNODE's change text should be a diff.
+ Else, DELTA's change text should be a diff between LEAFNODE and
+ the working file. */
+
+ tmpfile = cvs_temp_name();
+ status = RCS_checkout (rcs, NULL, commitpt->version, NULL,
+ ((rcs->expand != NULL
+ && STREQ (rcs->expand, "b"))
+ ? "-kb"
+ : "-ko"),
+ tmpfile,
+ NULL, NULL);
+ if (status != 0)
+ error (1, 0,
+ "could not check out revision %s of `%s'",
+ commitpt->version, rcs->print_path);
+
+ bufsize = 0;
+ changefile = cvs_temp_name();
+
+ /* Diff options should include --binary if the RCS file has -kb set
+ in its `expand' field. */
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-a");
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
+ if (rcs->expand != NULL && STREQ (rcs->expand, "b"))
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "--binary");
+
+ if (STREQ (commitpt->version, rcs->head) &&
+ numdots (delta->version) == 1)
+ {
+ /* If this revision is being inserted on the trunk, the change text
+ for the new delta should be the contents of the working file ... */
+ bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ ;
+ else
+#endif
+ get_file (workfile, workfile,
+ rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
+ &dtext->text, &bufsize, &dtext->len);
+
+ /* ... and the change text for the old delta should be a diff. */
+ commitpt->text = xmalloc (sizeof (Deltatext));
+ memset (commitpt->text, 0, sizeof (Deltatext));
+
+ bufsize = 0;
+ switch (diff_exec (workfile, tmpfile, NULL, NULL,
+ dargc, dargv, changefile))
+ {
+ case 0:
+ case 1:
+ break;
+ case -1:
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (1, errno, "error diffing %s", workfile);
+ break;
+ default:
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (1, 0, "error diffing %s", workfile);
+ break;
+ }
+
+ /* OK, the text file case here is really dumb. Logically
+ speaking we want diff to read the files in text mode,
+ convert them to the canonical form found in RCS files
+ (which, we hope at least, is independent of OS--always
+ bare linefeeds), and then work with change texts in that
+ format. However, diff_exec both generates change
+ texts and produces output for user purposes (e.g. patch.c),
+ and there is no way to distinguish between the two cases.
+ So we actually implement the text file case by writing the
+ change text as a text file, then reading it as a text file.
+ This should cause no harm, but doesn't strike me as
+ immensely clean. */
+ get_file (changefile, changefile,
+ rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
+ &commitpt->text->text, &bufsize, &commitpt->text->len);
+
+ /* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE
+ was empty and that there are no differences between revisions.
+ In that event, we want to force RCS_rewrite to write an empty
+ string for COMMITPT's change text. Leaving the change text
+ field set NULL won't work, since that means "preserve the original
+ change text for this delta." */
+ if (commitpt->text->text == NULL)
+ {
+ commitpt->text->text = xstrdup ("");
+ commitpt->text->len = 0;
+ }
+ }
+ else
+ {
+ /* This file is not being inserted at the head, but on a side
+ branch somewhere. Make a diff from the previous revision
+ to the working file. */
+ switch (diff_exec (tmpfile, workfile, NULL, NULL,
+ dargc, dargv, changefile))
+ {
+ case 0:
+ case 1:
+ break;
+ case -1:
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (1, errno, "error diffing %s", workfile);
+ break;
+ default:
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (1, 0, "error diffing %s", workfile);
+ break;
+ }
+ /* See the comment above, at the other get_file invocation,
+ regarding binary vs. text. */
+ get_file (changefile, changefile,
+ rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
+ &dtext->text, &bufsize,
+ &dtext->len);
+ if (dtext->text == NULL)
+ {
+ dtext->text = xstrdup ("");
+ dtext->len = 0;
+ }
+ }
+
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+
+ /* Update DELTA linkage. It is important not to do this before
+ the very end of RCS_checkin; if an error arises that forces
+ us to abort checking in, we must not have malformed deltas
+ partially linked into the tree.
+
+ If DELTA and COMMITPT are on different branches, do nothing --
+ DELTA is linked to the tree through COMMITPT->BRANCHES, and we
+ don't want to change `next' pointers.
+
+ Otherwise, if the nodes are both on the trunk, link DELTA to
+ COMMITPT; otherwise, link COMMITPT to DELTA. */
+
+ if (numdots (commitpt->version) == numdots (delta->version))
+ {
+ if (STREQ (commitpt->version, rcs->head))
+ {
+ delta->next = rcs->head;
+ rcs->head = xstrdup (delta->version);
+ }
+ else
+ commitpt->next = xstrdup (delta->version);
+ }
+
+ /* Add DELTA to RCS->VERSIONS. */
+ if (rcs->versions == NULL)
+ rcs->versions = getlist();
+ nodep = getnode();
+ nodep->type = RCSVERS;
+ nodep->delproc = rcsvers_delproc;
+ nodep->data = delta;
+ nodep->key = delta->version;
+ (void) addnode (rcs->versions, nodep);
+
+ /* Write the new RCS file, inserting the new delta at COMMITPT. */
+ if (!(checkin_quiet || really_quiet))
+ {
+ cvs_output ("new revision: ", 14);
+ cvs_output (delta->version, 0);
+ cvs_output ("; previous revision: ", 21);
+ cvs_output (commitpt->version, 0);
+ cvs_output ("\n", 1);
+ }
+
+ RCS_rewrite (rcs, dtext, commitpt->version);
+
+ if ((flags & RCS_FLAGS_KEEPFILE) == 0)
+ {
+ if (unlink_file (workfile) < 0)
+ /* FIXME-update-dir: message does not include update_dir. */
+ error (1, errno, "cannot remove %s", workfile);
+ }
+ if (unlink_file (tmpfile) < 0)
+ error (0, errno, "cannot remove %s", tmpfile);
+ free (tmpfile);
+ if (unlink_file (changefile) < 0)
+ error (0, errno, "cannot remove %s", changefile);
+ free (changefile);
+
+ checkin_done:
+ free (workfile);
+
+ if (commitpt != NULL && commitpt->text != NULL)
+ {
+ freedeltatext (commitpt->text);
+ commitpt->text = NULL;
+ }
+
+ freedeltatext (dtext);
+ if (status != 0)
+ {
+ /* If delta has not been added to a List, then freeing the Node key
+ * won't free delta->version.
+ */
+ if (delta->version) free (delta->version);
+ free_rcsvers_contents (delta);
+ }
+
+ return status;
+}
+
+
+
+/* This structure is passed between RCS_cmp_file and cmp_file_buffer. */
+struct cmp_file_data
+{
+ const char *filename;
+ FILE *fp;
+ int different;
+};
+
+/* Compare the contents of revision REV1 of RCS file RCS with the
+ contents of REV2 if given, otherwise, compare with the contents of
+ the file FILENAME. OPTIONS is a string for the keyword
+ expansion options. Return 0 if the contents of the revision are
+ the same as the contents of the file, 1 if they are different. */
+int
+RCS_cmp_file (RCSNode *rcs, const char *rev1, char **rev1_cache,
+ const char *rev2, const char *options, const char *filename)
+{
+ int binary;
+
+ TRACE (TRACE_FUNCTION, "RCS_cmp_file( %s, %s, %s, %s, %s )",
+ rcs->path ? rcs->path : "(null)",
+ rev1 ? rev1 : "(null)", rev2 ? rev2 : "(null)",
+ options ? options : "(null)", filename ? filename : "(null)");
+
+ if (options != NULL && options[0] != '\0')
+ binary = STREQ (options, "-kb");
+ else
+ {
+ char *expand;
+
+ expand = RCS_getexpand (rcs);
+ if (expand != NULL && STREQ (expand, "b"))
+ binary = 1;
+ else
+ binary = 0;
+ }
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* If CVS is to deal properly with special files (when
+ PreservePermissions is on), the best way is to check out the
+ revision to a temporary file and call `xcmp' on the two disk
+ files. xcmp needs to handle non-regular files properly anyway,
+ so calling it simplifies RCS_cmp_file. We *could* just yank
+ the delta node out of the version tree and look for device
+ numbers, but writing to disk and calling xcmp is a better
+ abstraction (therefore probably more robust). -twp */
+
+ if (preserve_perms)
+ {
+ char *tmp;
+ int retcode;
+
+ tmp = cvs_temp_name();
+ retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL);
+ if (retcode != 0)
+ return 1;
+
+ retcode = xcmp (tmp, filename);
+ if (CVS_UNLINK (tmp) < 0)
+ error (0, errno, "cannot remove %s", tmp);
+ free (tmp);
+ return retcode;
+ }
+ else
+#endif
+ {
+ FILE *fp;
+ struct cmp_file_data data;
+ const char *use_file1;
+ char *tmpfile = NULL;
+
+ if (rev2 != NULL)
+ {
+ /* Open & cache rev1 */
+ tmpfile = cvs_temp_name();
+ if (RCS_checkout (rcs, NULL, rev1, NULL, options, tmpfile,
+ NULL, NULL))
+ error (1, errno,
+ "cannot check out revision %s of %s",
+ rev1, rcs->print_path);
+ use_file1 = tmpfile;
+ if (rev1_cache != NULL)
+ *rev1_cache = tmpfile;
+ }
+ else
+ use_file1 = filename;
+
+ fp = CVS_FOPEN (use_file1, binary ? FOPEN_BINARY_READ : "r");
+ if (fp == NULL)
+ /* FIXME-update-dir: should include update_dir in message. */
+ error (1, errno, "cannot open file %s for comparing", use_file1);
+
+ data.filename = use_file1;
+ data.fp = fp;
+ data.different = 0;
+
+ if (RCS_checkout (rcs, NULL, rev2 ? rev2 : rev1, NULL, options,
+ RUN_TTY, cmp_file_buffer, &data ))
+ error (1, errno,
+ "cannot check out revision %s of %s",
+ rev2 ? rev2 : rev1, rcs->print_path);
+
+ /* If we have not yet found a difference, make sure that we are at
+ the end of the file. */
+ if (!data.different)
+ {
+ if (getc (fp) != EOF)
+ data.different = 1;
+ }
+
+ fclose (fp);
+ if (rev1_cache == NULL && tmpfile)
+ {
+ if (CVS_UNLINK (tmpfile ) < 0)
+ error (0, errno, "cannot remove %s", tmpfile);
+ free (tmpfile);
+ }
+
+ return data.different;
+ }
+}
+
+
+
+/* This is a subroutine of RCS_cmp_file. It is passed to
+ RCS_checkout. */
+#define CMP_BUF_SIZE (8 * 1024)
+
+static void
+cmp_file_buffer (void *callerdat, const char *buffer, size_t len)
+{
+ struct cmp_file_data *data = callerdat;
+ char *filebuf;
+
+ /* If we've already found a difference, we don't need to check
+ further. */
+ if (data->different)
+ return;
+
+ filebuf = xmalloc (len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len);
+
+ while (len > 0)
+ {
+ size_t checklen;
+
+ checklen = len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len;
+ if (fread (filebuf, 1, checklen, data->fp) != checklen)
+ {
+ if (ferror (data->fp))
+ error (1, errno, "cannot read file %s for comparing",
+ data->filename);
+ data->different = 1;
+ free (filebuf);
+ return;
+ }
+
+ if (memcmp (filebuf, buffer, checklen) != 0)
+ {
+ data->different = 1;
+ free (filebuf);
+ return;
+ }
+
+ buffer += checklen;
+ len -= checklen;
+ }
+
+ free (filebuf);
+}
+
+
+
+/* For RCS file RCS, make symbolic tag TAG point to revision REV.
+ This validates that TAG is OK for a user to use. Return value is
+ -1 for error (and errno is set to indicate the error), positive for
+ error (and an error message has been printed), or zero for success. */
+int
+RCS_settag (RCSNode *rcs, const char *tag, const char *rev)
+{
+ List *symbols;
+ Node *node;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* FIXME: This check should be moved to RCS_check_tag. There is no
+ reason for it to be here. */
+ if (STREQ (tag, TAG_BASE)
+ || STREQ (tag, TAG_HEAD)
+ || *tag == '.')
+ {
+ /* Print the name of the tag might be considered redundant
+ with the caller, which also prints it. Perhaps this helps
+ clarify why the tag name is considered reserved, I don't
+ know. */
+ error (0, 0, "Attempt to add reserved tag name %s", tag);
+ return 1;
+ }
+
+ /* A revision number of NULL means use the head or default branch.
+ If rev is not NULL, it may be a symbolic tag or branch number;
+ expand it to the correct numeric revision or branch head. */
+ if (rev == NULL)
+ rev = rcs->branch ? rcs->branch : rcs->head;
+
+ /* At this point rcs->symbol_data may not have been parsed.
+ Calling RCS_symbols will force it to be parsed into a list
+ which we can easily manipulate. */
+ symbols = RCS_symbols (rcs);
+ if (symbols == NULL)
+ {
+ symbols = getlist ();
+ rcs->symbols = symbols;
+ }
+ node = findnode (symbols, tag);
+ if (node != NULL)
+ {
+ free (node->data);
+ node->data = xstrdup (rev);
+ }
+ else
+ {
+ node = getnode ();
+ node->key = xstrdup (tag);
+ node->data = xstrdup (rev);
+ (void)addnode_at_front (symbols, node);
+ }
+
+ return 0;
+}
+
+
+
+/* Delete the symbolic tag TAG from the RCS file RCS. Return 0 if
+ the tag was found (and removed), or 1 if it was not present. (In
+ either case, the tag will no longer be in RCS->SYMBOLS.) */
+int
+RCS_deltag (RCSNode *rcs, const char *tag)
+{
+ List *symbols;
+ Node *node;
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ symbols = RCS_symbols (rcs);
+ if (symbols == NULL)
+ return 1;
+
+ node = findnode (symbols, tag);
+ if (node == NULL)
+ return 1;
+
+ delnode (node);
+
+ return 0;
+}
+
+
+
+/* Set the default branch of RCS to REV. */
+int
+RCS_setbranch (RCSNode *rcs, const char *rev)
+{
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rev && ! *rev)
+ rev = NULL;
+
+ if (rev == NULL && rcs->branch == NULL)
+ return 0;
+ if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch))
+ return 0;
+
+ if (rcs->branch != NULL)
+ free (rcs->branch);
+ rcs->branch = xstrdup (rev);
+
+ return 0;
+}
+
+
+
+/* Lock revision REV. LOCK_QUIET is 1 to suppress output. FIXME:
+ Most of the callers only call us because RCS_checkin still tends to
+ like a lock (a relic of old behavior inherited from the RCS ci
+ program). If we clean this up, only "cvs admin -l" will still need
+ to call RCS_lock. */
+
+/* FIXME-twp: if a lock owned by someone else is broken, should this
+ send mail to the lock owner? Prompt user? It seems like such an
+ obscure situation for CVS as almost not worth worrying much
+ about. */
+int
+RCS_lock (RCSNode *rcs, const char *rev, int lock_quiet)
+{
+ List *locks;
+ Node *p;
+ char *user;
+ char *xrev = NULL;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ locks = RCS_getlocks (rcs);
+ if (locks == NULL)
+ locks = rcs->locks = getlist();
+ user = getcaller();
+
+ /* A revision number of NULL means lock the head or default branch. */
+ if (rev == NULL)
+ xrev = RCS_head (rcs);
+ else
+ xrev = RCS_gettag (rcs, rev, 1, NULL);
+
+ /* Make sure that the desired revision exists. Technically,
+ we can update the locks list without even checking this,
+ but RCS 5.7 did this. And it can't hurt. */
+ if (xrev == NULL || findnode (rcs->versions, xrev) == NULL)
+ {
+ if (!lock_quiet)
+ error (0, 0, "%s: revision %s absent", rcs->print_path, rev);
+ free (xrev);
+ return 1;
+ }
+
+ /* Is this rev already locked? */
+ p = findnode (locks, xrev);
+ if (p != NULL)
+ {
+ if (STREQ (p->data, user))
+ {
+ /* We already own the lock on this revision, so do nothing. */
+ free (xrev);
+ return 0;
+ }
+
+#if 0
+ /* Well, first of all, "rev" below should be "xrev" to avoid
+ core dumps. But more importantly, should we really be
+ breaking the lock unconditionally? What CVS 1.9 does (via
+ RCS) is to prompt "Revision 1.1 is already locked by fred.
+ Do you want to break the lock? [ny](n): ". Well, we don't
+ want to interact with the user (certainly not at the
+ server/protocol level, and probably not in the command-line
+ client), but isn't it more sensible to give an error and
+ let the user run "cvs admin -u" if they want to break the
+ lock? */
+
+ /* Break the lock. */
+ if (!lock_quiet)
+ {
+ cvs_output (rev, 0);
+ cvs_output (" unlocked\n", 0);
+ }
+ delnode (p);
+#else
+ error (1, 0, "Revision %s is already locked by %s",
+ xrev, (char *)p->data);
+#endif
+ }
+
+ /* Create a new lock. */
+ p = getnode();
+ p->key = xrev; /* already xstrdupped */
+ p->data = xstrdup (getcaller());
+ (void)addnode_at_front (locks, p);
+
+ if (!lock_quiet)
+ {
+ cvs_output (xrev, 0);
+ cvs_output (" locked\n", 0);
+ }
+
+ return 0;
+}
+
+
+
+/* Unlock revision REV. UNLOCK_QUIET is 1 to suppress output. FIXME:
+ Like RCS_lock, this can become a no-op if we do the checkin
+ ourselves.
+
+ If REV is not null and is locked by someone else, break their
+ lock and notify them. It is an open issue whether RCS_unlock
+ queries the user about whether or not to break the lock. */
+int
+RCS_unlock (RCSNode *rcs, char *rev, int unlock_quiet)
+{
+ Node *lock;
+ List *locks;
+ char *user;
+ char *xrev = NULL;
+
+ user = getcaller();
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ /* If rev is NULL, unlock the revision held by the caller; if more
+ than one, make the user specify the revision explicitly. This
+ differs from RCS which unlocks the latest revision (first in
+ rcs->locks) held by the caller. */
+ if (rev == NULL)
+ {
+ Node *p;
+
+ /* No-ops: attempts to unlock an empty tree or an unlocked file. */
+ if (rcs->head == NULL)
+ {
+ if (!unlock_quiet)
+ cvs_outerr ("can't unlock an empty tree\n", 0);
+ return 0;
+ }
+
+ locks = RCS_getlocks (rcs);
+ if (locks == NULL)
+ {
+ if (!unlock_quiet)
+ cvs_outerr ("No locks are set.\n", 0);
+ return 0;
+ }
+
+ lock = NULL;
+ for (p = locks->list->next; p != locks->list; p = p->next)
+ {
+ if (STREQ (p->data, user))
+ {
+ if (lock != NULL)
+ {
+ if (!unlock_quiet)
+ error (0, 0, "\
+%s: multiple revisions locked by %s; please specify one", rcs->print_path,
user);
+ return 1;
+ }
+ lock = p;
+ }
+ }
+ if (lock == NULL)
+ {
+ if (!unlock_quiet)
+ error (0, 0, "No locks are set for %s.\n", user);
+ return 0; /* no lock found, ergo nothing to do */
+ }
+ xrev = xstrdup (lock->key);
+ }
+ else
+ {
+ xrev = RCS_gettag (rcs, rev, 1, NULL);
+ if (xrev == NULL)
+ {
+ error (0, 0, "%s: revision %s absent", rcs->print_path, rev);
+ return 1;
+ }
+ }
+
+ lock = findnode (RCS_getlocks (rcs), xrev);
+ if (lock == NULL)
+ {
+ /* This revision isn't locked. */
+ free (xrev);
+ return 0;
+ }
+
+ if (! STREQ (lock->data, user))
+ {
+ /* If the revision is locked by someone else, notify
+ them. Note that this shouldn't ever happen if RCS_unlock
+ is called with a NULL revision, since that means "whatever
+ revision is currently locked by the caller." */
+ char *repos, *workfile;
+ if (!unlock_quiet)
+ error (0, 0, "\
+%s: revision %s locked by %s; breaking lock", rcs->print_path, xrev,
+ (char *)lock->data);
+ repos = xstrdup (rcs->path);
+ workfile = strrchr (repos, '/');
+ *workfile++ = '\0';
+ notify_do ('C', workfile, NULL, user, NULL, NULL, repos);
+ free (repos);
+ }
+
+ delnode (lock);
+ if (!unlock_quiet)
+ {
+ cvs_output (xrev, 0);
+ cvs_output (" unlocked\n", 0);
+ }
+
+ free (xrev);
+ return 0;
+}
+
+
+
+/* Add USER to the access list of RCS. Do nothing if already present.
+ FIXME-twp: check syntax of USER to make sure it's a valid id. */
+
+void
+RCS_addaccess (RCSNode *rcs, char *user)
+{
+ char *access, *a;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rcs->access == NULL)
+ rcs->access = xstrdup (user);
+ else
+ {
+ access = xstrdup (rcs->access);
+ for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " "))
+ {
+ if (STREQ (a, user))
+ {
+ free (access);
+ return;
+ }
+ }
+ free (access);
+ rcs->access = xrealloc (rcs->access,
+ strlen (rcs->access) + strlen (user) + 2);
+ strcat (rcs->access, " ");
+ strcat (rcs->access, user);
+ }
+}
+
+
+
+/* Remove USER from the access list of RCS. */
+void
+RCS_delaccess (RCSNode *rcs, char *user)
+{
+ char *p, *s;
+ int ulen;
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (rcs->access == NULL)
+ return;
+
+ if (user == NULL)
+ {
+ free (rcs->access);
+ rcs->access = NULL;
+ return;
+ }
+
+ p = rcs->access;
+ ulen = strlen (user);
+ while (p != NULL)
+ {
+ if (strncmp (p, user, ulen) == 0 && (p[ulen] == '\0' || p[ulen] == ' '))
+ break;
+ p = strchr (p, ' ');
+ if (p != NULL)
+ ++p;
+ }
+
+ if (p == NULL)
+ return;
+
+ s = p + ulen;
+ while (*s != '\0')
+ *p++ = *s++;
+ *p = '\0';
+}
+
+
+
+char *
+RCS_getaccess (RCSNode *rcs)
+{
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ return rcs->access;
+}
+
+
+
+/* Return a nonzero value if the revision specified by ARG is found. */
+static int
+findtag (Node *node, void *arg)
+{
+ char *rev = arg;
+
+ if (STREQ (node->data, rev))
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/* Delete revisions between REV1 and REV2. The changes between the two
+ revisions must be collapsed, and the result stored in the revision
+ immediately preceding the lower one. Return 0 for successful completion,
+ 1 otherwise.
+
+ Solution: check out the revision preceding REV1 and the revision
+ following REV2. Use call_diff to find aggregate diffs between
+ these two revisions, and replace the delta text for the latter one
+ with the new aggregate diff. Alternatively, we could write a
+ function that takes two change texts and combines them to produce a
+ new change text, without checking out any revs or calling diff. It
+ would be hairy, but so, so cool.
+
+ If INCLUSIVE is set, then TAG1 and TAG2, if non-NULL, tell us to
+ delete that revision as well (cvs admin -o tag1:tag2). If clear,
+ delete up to but not including that revision (cvs admin -o tag1::tag2).
+ This does not affect TAG1 or TAG2 being NULL; the meaning of the start
+ point in ::tag2 and :tag2 is the same and likewise for end points. */
+int
+RCS_delete_revs (RCSNode *rcs, char *tag1, char *tag2, int inclusive)
+{
+ char *next;
+ Node *nodep;
+ RCSVers *revp = NULL;
+ RCSVers *beforep;
+ int status, found;
+ int save_noexec;
+
+ char *branchpoint = NULL;
+ char *rev1 = NULL;
+ char *rev2 = NULL;
+ int rev1_inclusive = inclusive;
+ int rev2_inclusive = inclusive;
+ char *before = NULL;
+ char *after = NULL;
+ char *beforefile = NULL;
+ char *afterfile = NULL;
+ char *outfile = NULL;
+
+ if (tag1 == NULL && tag2 == NULL)
+ return 0;
+
+ /* Assume error status until everything is finished. */
+ status = 1;
+
+ /* Make sure both revisions exist. */
+ if (tag1 != NULL)
+ {
+ rev1 = RCS_gettag (rcs, tag1, 1, NULL);
+ if (rev1 == NULL || (nodep = findnode (rcs->versions, rev1)) == NULL)
+ {
+ error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path,
tag1);
+ goto delrev_done;
+ }
+ }
+ if (tag2 != NULL)
+ {
+ rev2 = RCS_gettag (rcs, tag2, 1, NULL);
+ if (rev2 == NULL || (nodep = findnode (rcs->versions, rev2)) == NULL)
+ {
+ error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path,
tag2);
+ goto delrev_done;
+ }
+ }
+
+ /* If rev1 is on the trunk and rev2 is NULL, rev2 should be
+ RCS->HEAD. (*Not* RCS_head(rcs), which may return rcs->branch
+ instead.) We need to check this special case early, in order
+ to make sure that rev1 and rev2 get ordered correctly. */
+ if (rev2 == NULL && numdots (rev1) == 1)
+ {
+ rev2 = xstrdup (rcs->head);
+ rev2_inclusive = 1;
+ }
+
+ if (rev2 == NULL)
+ rev2_inclusive = 1;
+
+ if (rev1 != NULL && rev2 != NULL)
+ {
+ /* A range consisting of a branch number means the latest revision
+ on that branch. */
+ if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2))
+ {
+ char *tmp = RCS_getbranch (rcs, rev1, 0);
+ free (rev1);
+ free (rev2);
+ rev1 = rev2 = tmp;
+ }
+ else
+ {
+ /* Make sure REV1 and REV2 are ordered correctly (in the
+ same order as the next field). For revisions on the
+ trunk, REV1 should be higher than REV2; for branches,
+ REV1 should be lower. */
+ /* Shouldn't we just be giving an error in the case where
+ the user specifies the revisions in the wrong order
+ (that is, always swap on the trunk, never swap on a
+ branch, in the non-error cases)? It is not at all
+ clear to me that users who specify -o 1.4:1.2 really
+ meant to type -o 1.2:1.4, and the out of order usage
+ has never been documented, either by cvs.texinfo or
+ rcs(1). */
+ char *temp;
+ int temp_inclusive;
+ if (numdots (rev1) == 1)
+ {
+ if (compare_revnums (rev1, rev2) <= 0)
+ {
+ temp = rev2;
+ rev2 = rev1;
+ rev1 = temp;
+
+ temp_inclusive = rev2_inclusive;
+ rev2_inclusive = rev1_inclusive;
+ rev1_inclusive = temp_inclusive;
+ }
+ }
+ else if (compare_revnums (rev1, rev2) > 0)
+ {
+ temp = rev2;
+ rev2 = rev1;
+ rev1 = temp;
+
+ temp_inclusive = rev2_inclusive;
+ rev2_inclusive = rev1_inclusive;
+ rev1_inclusive = temp_inclusive;
+ }
+ }
+ }
+
+ /* Basically the same thing; make sure that the ordering is what we
+ need. */
+ if (rev1 == NULL)
+ {
+ assert (rev2 != NULL);
+ if (numdots (rev2) == 1)
+ {
+ /* Swap rev1 and rev2. */
+ int temp_inclusive;
+
+ rev1 = rev2;
+ rev2 = NULL;
+
+ temp_inclusive = rev2_inclusive;
+ rev2_inclusive = rev1_inclusive;
+ rev1_inclusive = temp_inclusive;
+ }
+ }
+
+ /* Put the revision number preceding the first one to delete into
+ BEFORE (where "preceding" means according to the next field).
+ If the first revision to delete is the first revision on its
+ branch (e.g. 1.3.2.1), BEFORE should be the node on the trunk
+ at which the branch is rooted. If the first revision to delete
+ is the head revision of the trunk, set BEFORE to NULL.
+
+ Note that because BEFORE may not be on the same branch as REV1,
+ it is not very handy for navigating the revision tree. It's
+ most useful just for checking out the revision preceding REV1. */
+ before = NULL;
+ branchpoint = RCS_getbranchpoint (rcs, rev1 != NULL ? rev1 : rev2);
+ if (rev1 == NULL)
+ {
+ rev1 = xstrdup (branchpoint);
+ if (numdots (branchpoint) > 1)
+ {
+ char *bp;
+ bp = strrchr (branchpoint, '.');
+ while (*--bp != '.')
+ ;
+ *bp = '\0';
+ /* Note that this is exclusive, always, because the inclusive
+ flag doesn't affect the meaning when rev1 == NULL. */
+ before = xstrdup (branchpoint);
+ *bp = '.';
+ }
+ }
+ else if (! STREQ (rev1, branchpoint))
+ {
+ /* Walk deltas from BRANCHPOINT on, looking for REV1. */
+ nodep = findnode (rcs->versions, branchpoint);
+ revp = nodep->data;
+ while (revp->next != NULL && ! STREQ (revp->next, rev1))
+ {
+ revp = nodep->data;
+ nodep = findnode (rcs->versions, revp->next);
+ }
+ if (revp->next == NULL)
+ {
+ error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path,
rev1);
+ goto delrev_done;
+ }
+ if (rev1_inclusive)
+ before = xstrdup (revp->version);
+ else
+ {
+ before = rev1;
+ nodep = findnode (rcs->versions, before);
+ rev1 = xstrdup (((RCSVers *)nodep->data)->next);
+ }
+ }
+ else if (!rev1_inclusive)
+ {
+ before = rev1;
+ nodep = findnode (rcs->versions, before);
+ rev1 = xstrdup (((RCSVers *)nodep->data)->next);
+ }
+ else if (numdots (branchpoint) > 1)
+ {
+ /* Example: rev1 is "1.3.2.1", branchpoint is "1.3.2.1".
+ Set before to "1.3". */
+ char *bp;
+ bp = strrchr (branchpoint, '.');
+ while (*--bp != '.')
+ ;
+ *bp = '\0';
+ before = xstrdup (branchpoint);
+ *bp = '.';
+ }
+
+ /* If any revision between REV1 and REV2 is locked or is a branch point,
+ we can't delete that revision and must abort. */
+ after = NULL;
+ next = rev1;
+ found = 0;
+ while (!found && next != NULL)
+ {
+ nodep = findnode (rcs->versions, next);
+ revp = nodep->data;
+
+ if (rev2 != NULL)
+ found = STREQ (revp->version, rev2);
+ next = revp->next;
+
+ if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL)
+ {
+ if (findnode (RCS_getlocks (rcs), revp->version))
+ {
+ error (0, 0, "%s: can't remove locked revision %s",
+ rcs->print_path,
+ revp->version);
+ goto delrev_done;
+ }
+ if (revp->branches != NULL)
+ {
+ error (0, 0, "%s: can't remove branch point %s",
+ rcs->print_path,
+ revp->version);
+ goto delrev_done;
+ }
+
+ /* Doing this only for the :: syntax is for compatibility.
+ See cvs.texinfo for somewhat more discussion. */
+ if (!inclusive
+ && walklist (RCS_symbols (rcs), findtag, revp->version))
+ {
+ /* We don't print which file this happens to on the theory
+ that the caller will print the name of the file in a
+ more useful fashion (fullname not rcs->path). */
+ error (0, 0, "cannot remove revision %s because it has tags",
+ revp->version);
+ goto delrev_done;
+ }
+
+ /* It's misleading to print the `deleting revision' output
+ here, since we may not actually delete these revisions.
+ But that's how RCS does it. Bleah. Someday this should be
+ moved to the point where the revs are actually marked for
+ deletion. -twp */
+ cvs_output ("deleting revision ", 0);
+ cvs_output (revp->version, 0);
+ cvs_output ("\n", 1);
+ }
+ }
+
+ if (rev2 == NULL)
+ ;
+ else if (found)
+ {
+ if (rev2_inclusive)
+ after = xstrdup (next);
+ else
+ after = xstrdup (revp->version);
+ }
+ else if (!inclusive)
+ {
+ /* In the case of an empty range, for example 1.2::1.2 or
+ 1.2::1.3, we want to just do nothing. */
+ status = 0;
+ goto delrev_done;
+ }
+ else
+ {
+ /* This looks fishy in the cases where tag1 == NULL or tag2 == NULL.
+ Are those cases really impossible? */
+ assert (tag1 != NULL);
+ assert (tag2 != NULL);
+
+ error (0, 0, "%s: invalid revision range %s:%s", rcs->print_path,
+ tag1, tag2);
+ goto delrev_done;
+ }
+
+ if (after == NULL && before == NULL)
+ {
+ /* The user is trying to delete all revisions. While an
+ RCS file without revisions makes sense to RCS (e.g. the
+ state after "rcs -i"), CVS has never been able to cope with
+ it. So at least for now we just make this an error.
+
+ We don't include rcs->path in the message since "cvs admin"
+ already printed "RCS file:" and the name. */
+ error (1, 0, "attempt to delete all revisions");
+ }
+
+ /* The conditionals at this point get really hairy. Here is the
+ general idea:
+
+ IF before != NULL and after == NULL
+ THEN don't check out any revisions, just delete them
+ IF before == NULL and after != NULL
+ THEN only check out after's revision, and use it for the new deltatext
+ ELSE
+ check out both revisions and diff -n them. This could use
+ RCS_exec_rcsdiff with some changes, like being able
+ to suppress diagnostic messages and to direct output. */
+
+ if (after != NULL)
+ {
+ char *diffbuf;
+ size_t bufsize, len;
+
+#if defined (WOE32) && !defined (__CYGWIN32__)
+ /* FIXME: This is an awful kludge, but at least until I have
+ time to work on it a little more and test it, I'd rather
+ give a fatal error than corrupt the file. I think that we
+ need to use "-kb" and "--binary" and "rb" to get_file
+ (probably can do it always, not just for binary files, if
+ we are consistent between the RCS_checkout and the diff). */
+ {
+ char *expand = RCS_getexpand (rcs);
+ if (expand != NULL && STREQ (expand, "b"))
+ error (1, 0,
+ "admin -o not implemented yet for binary on this system");
+ }
+#endif /* WOE32 */
+
+ afterfile = cvs_temp_name();
+ status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
+ NULL, NULL);
+ if (status > 0)
+ goto delrev_done;
+
+ if (before == NULL)
+ {
+ /* We are deleting revisions from the head of the tree,
+ so must create a new head. */
+ diffbuf = NULL;
+ bufsize = 0;
+ get_file (afterfile, afterfile, "r", &diffbuf, &bufsize, &len);
+
+ save_noexec = noexec;
+ noexec = 0;
+ if (unlink_file (afterfile) < 0)
+ error (0, errno, "cannot remove %s", afterfile);
+ noexec = save_noexec;
+
+ free (afterfile);
+ afterfile = NULL;
+
+ free (rcs->head);
+ rcs->head = xstrdup (after);
+ }
+ else
+ {
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+
+ beforefile = cvs_temp_name();
+ status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile,
+ NULL, NULL);
+ if (status > 0)
+ goto delrev_done;
+
+ outfile = cvs_temp_name();
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-a");
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
+ status = diff_exec (beforefile, afterfile, NULL, NULL,
+ dargc, dargv, outfile);
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+
+ if (status == 2)
+ {
+ /* Not sure we need this message; will diff_exec already
+ have printed an error? */
+ error (0, 0, "%s: could not diff", rcs->print_path);
+ status = 1;
+ goto delrev_done;
+ }
+
+ diffbuf = NULL;
+ bufsize = 0;
+ get_file (outfile, outfile, "r", &diffbuf, &bufsize, &len);
+ }
+
+ /* Save the new change text in after's delta node. */
+ nodep = findnode (rcs->versions, after);
+ revp = nodep->data;
+
+ assert (revp->text == NULL);
+
+ revp->text = xmalloc (sizeof (Deltatext));
+ memset (revp->text, 0, sizeof (Deltatext));
+ revp->text->version = xstrdup (revp->version);
+ revp->text->text = diffbuf;
+ revp->text->len = len;
+
+ /* If DIFFBUF is NULL, it means that OUTFILE is empty and that
+ there are no differences between the two revisions. In that
+ case, we want to force RCS_copydeltas to write an empty string
+ for the new change text (leaving the text field set NULL
+ means "preserve the original change text for this delta," so
+ we don't want that). */
+ if (revp->text->text == NULL)
+ revp->text->text = xstrdup ("");
+ }
+
+ /* Walk through the revisions (again) to mark each one as
+ outdated. (FIXME: would it be safe to use the `dead' field for
+ this? Doubtful.) */
+ for (next = rev1;
+ next != NULL && (after == NULL || ! STREQ (next, after));
+ next = revp->next)
+ {
+ nodep = findnode (rcs->versions, next);
+ revp = nodep->data;
+ revp->outdated = 1;
+ }
+
+ /* Update delta links. If BEFORE == NULL, we're changing the
+ head of the tree and don't need to update any `next' links. */
+ if (before != NULL)
+ {
+ /* If REV1 is the first node on its branch, then BEFORE is its
+ root node (on the trunk) and we have to update its branches
+ list. Otherwise, BEFORE is on the same branch as AFTER, and
+ we can just change BEFORE's `next' field to point to AFTER.
+ (This should be safe: since findnode manages its lists via
+ the `hashnext' and `hashprev' fields, rather than `next' and
+ `prev', mucking with `next' and `prev' should not corrupt the
+ delta tree's internal structure. Much. -twp) */
+
+ if (rev1 == NULL)
+ /* beforep's ->next field already should be equal to after,
+ which I think is always NULL in this case. */
+ ;
+ else if (STREQ (rev1, branchpoint))
+ {
+ nodep = findnode (rcs->versions, before);
+ revp = nodep->data;
+ nodep = revp->branches->list->next;
+ while (nodep != revp->branches->list &&
+ ! STREQ (nodep->key, rev1))
+ nodep = nodep->next;
+ assert (nodep != revp->branches->list);
+ if (after == NULL)
+ delnode (nodep);
+ else
+ {
+ free (nodep->key);
+ nodep->key = xstrdup (after);
+ }
+ }
+ else
+ {
+ nodep = findnode (rcs->versions, before);
+ beforep = nodep->data;
+ free (beforep->next);
+ beforep->next = xstrdup (after);
+ }
+ }
+
+ status = 0;
+
+ delrev_done:
+ if (rev1 != NULL)
+ free (rev1);
+ if (rev2 && rev2 != rev1)
+ free (rev2);
+ if (branchpoint != NULL)
+ free (branchpoint);
+ if (before != NULL)
+ free (before);
+ if (after != NULL)
+ free (after);
+
+ save_noexec = noexec;
+ noexec = 0;
+ if (beforefile != NULL)
+ {
+ if (unlink_file (beforefile) < 0)
+ error (0, errno, "cannot remove %s", beforefile);
+ free (beforefile);
+ }
+ if (afterfile != NULL)
+ {
+ if (unlink_file (afterfile) < 0)
+ error (0, errno, "cannot remove %s", afterfile);
+ free (afterfile);
+ }
+ if (outfile != NULL)
+ {
+ if (unlink_file (outfile) < 0)
+ error (0, errno, "cannot remove %s", outfile);
+ free (outfile);
+ }
+ noexec = save_noexec;
+
+ return status;
+}
+
+
+
+/*
+ * TRUE if there exists a symbolic tag "tag" in file.
+ */
+int
+RCS_exist_tag (RCSNode *rcs, char *tag)
+{
+
+ assert (rcs != NULL);
+
+ if (findnode (RCS_symbols (rcs), tag))
+ return 1;
+ return 0;
+
+}
+
+
+
+/*
+ * TRUE if RCS revision number "rev" exists.
+ * This includes magic branch revisions, not found in rcs->versions,
+ * but only in rcs->symbols, requiring a list walk to find them.
+ * Take advantage of list walk callback function already used by
+ * RCS_delete_revs, above.
+ */
+int
+RCS_exist_rev (RCSNode *rcs, char *rev)
+{
+
+ assert (rcs != NULL);
+
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL, NULL);
+
+ if (findnode(rcs->versions, rev) != 0)
+ return 1;
+
+ if (walklist (RCS_symbols(rcs), findtag, rev) != 0)
+ return 1;
+
+ return 0;
+
+}
+
+
+
+
+/* RCS_deltas and friends. Processing of the deltas in RCS files. */
+struct line
+{
+ /* Text of this line. Part of the same malloc'd block as the struct
+ line itself (we probably should use the "struct hack" (char text[1])
+ and save ourselves sizeof (char *) bytes). Does not include \n;
+ instead has_newline indicates the presence or absence of \n. */
+ char *text;
+ /* Length of this line, not counting \n if has_newline is true. */
+ size_t len;
+ /* Version in which it was introduced. */
+ RCSVers *vers;
+ /* Nonzero if this line ends with \n. This will always be true
+ except possibly for the last line. */
+ int has_newline;
+ /* Number of pointers to this struct line. */
+ int refcount;
+};
+
+struct linevector
+{
+ /* How many lines in use for this linevector? */
+ unsigned int nlines;
+ /* How many lines allocated for this linevector? */
+ unsigned int lines_alloced;
+ /* Pointer to array containing a pointer to each line. */
+ struct line **vector;
+};
+
+
+
+/* Initialize *VEC to be a linevector with no lines. */
+static void
+linevector_init (struct linevector *vec)
+{
+ vec->lines_alloced = 0;
+ vec->nlines = 0;
+ vec->vector = NULL;
+}
+
+
+
+/* Given some text TEXT, add each of its lines to VEC before line POS
+ (where line 0 is the first line). The last line in TEXT may or may
+ not be \n terminated.
+ Set the version for each of the new lines to VERS. This
+ function returns non-zero for success. It returns zero if the line
+ number is out of range.
+
+ Each of the lines in TEXT are copied to space which is managed with
+ the linevector (and freed by linevector_free). So the caller doesn't
+ need to keep TEXT around after the call to this function. */
+static int
+linevector_add (struct linevector *vec, const char *text, size_t len,
+ RCSVers *vers, unsigned int pos)
+{
+ const char *textend;
+ unsigned int i;
+ unsigned int nnew;
+ const char *p;
+ const char *nextline_text;
+ size_t nextline_len;
+ int nextline_newline;
+ struct line *q;
+
+ if (len == 0)
+ return 1;
+
+ textend = text + len;
+
+ /* Count the number of lines we will need to add. */
+ nnew = 1;
+ for (p = text; p < textend; ++p)
+ if (*p == '\n' && p + 1 < textend)
+ ++nnew;
+
+ /* Expand VEC->VECTOR if needed. */
+ if (vec->nlines + nnew >= vec->lines_alloced)
+ {
+ if (vec->lines_alloced == 0)
+ vec->lines_alloced = 10;
+ while (vec->nlines + nnew >= vec->lines_alloced)
+ vec->lines_alloced *= 2;
+ vec->vector = xnrealloc (vec->vector,
+ vec->lines_alloced, sizeof (*vec->vector));
+ }
+
+ /* Make room for the new lines in VEC->VECTOR. */
+ for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i)
+ vec->vector[i] = vec->vector[i - nnew];
+
+ if (pos > vec->nlines)
+ return 0;
+
+ /* Actually add the lines, to VEC->VECTOR. */
+ i = pos;
+ nextline_text = text;
+ nextline_newline = 0;
+ for (p = text; p < textend; ++p)
+ if (*p == '\n')
+ {
+ nextline_newline = 1;
+ if (p + 1 == textend)
+ /* If there are no characters beyond the last newline, we
+ don't consider it another line. */
+ break;
+ nextline_len = p - nextline_text;
+ q = xmalloc (sizeof (struct line) + nextline_len);
+ q->vers = vers;
+ q->text = (char *)q + sizeof (struct line);
+ q->len = nextline_len;
+ q->has_newline = nextline_newline;
+ q->refcount = 1;
+ memcpy (q->text, nextline_text, nextline_len);
+ vec->vector[i++] = q;
+
+ nextline_text = (char *)p + 1;
+ nextline_newline = 0;
+ }
+ nextline_len = p - nextline_text;
+ q = xmalloc (sizeof (struct line) + nextline_len);
+ q->vers = vers;
+ q->text = (char *)q + sizeof (struct line);
+ q->len = nextline_len;
+ q->has_newline = nextline_newline;
+ q->refcount = 1;
+ memcpy (q->text, nextline_text, nextline_len);
+ vec->vector[i] = q;
+
+ vec->nlines += nnew;
+
+ return 1;
+}
+
+
+
+/* Remove NLINES lines from VEC at position POS (where line 0 is the
+ first line). */
+static void
+linevector_delete (struct linevector *vec, unsigned int pos,
+ unsigned int nlines)
+{
+ unsigned int i;
+ unsigned int last;
+
+ last = vec->nlines - nlines;
+ for (i = pos; i < pos + nlines; ++i)
+ {
+ if (--vec->vector[i]->refcount == 0)
+ free (vec->vector[i]);
+ }
+ for (i = pos; i < last; ++i)
+ vec->vector[i] = vec->vector[i + nlines];
+ vec->nlines -= nlines;
+}
+
+
+
+/* Copy FROM to TO, copying the vectors but not the lines pointed to. */
+static void
+linevector_copy (struct linevector *to, struct linevector *from)
+{
+ unsigned int ln;
+
+ for (ln = 0; ln < to->nlines; ++ln)
+ {
+ if (--to->vector[ln]->refcount == 0)
+ free (to->vector[ln]);
+ }
+ if (from->nlines > to->lines_alloced)
+ {
+ if (to->lines_alloced == 0)
+ to->lines_alloced = 10;
+ while (from->nlines > to->lines_alloced)
+ to->lines_alloced *= 2;
+ to->vector = xnrealloc (to->vector,
+ to->lines_alloced,
+ sizeof (*to->vector));
+ }
+ memcpy (to->vector, from->vector,
+ xtimes (from->nlines, sizeof (*to->vector)));
+ to->nlines = from->nlines;
+ for (ln = 0; ln < to->nlines; ++ln)
+ ++to->vector[ln]->refcount;
+}
+
+
+
+/* Free storage associated with linevector. */
+static void
+linevector_free (struct linevector *vec)
+{
+ unsigned int ln;
+
+ if (vec->vector != NULL)
+ {
+ for (ln = 0; ln < vec->nlines; ++ln)
+ if (--vec->vector[ln]->refcount == 0)
+ free (vec->vector[ln]);
+
+ free (vec->vector);
+ }
+}
+
+
+
+/* Given a textual string giving the month (1-12), terminated with any
+ character not recognized by atoi, return the 3 character name to
+ print it with. I do not think it is a good idea to change these
+ strings based on the locale; they are standard abbreviations (for
+ example in rfc822 mail messages) which should be widely understood.
+ Returns a pointer into static readonly storage. */
+static const char *
+month_printname (const char *month)
+{
+ static const char *const months[] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ int mnum;
+
+ mnum = atoi (month);
+ if (mnum < 1 || mnum > 12)
+ return "???";
+ return months[mnum - 1];
+}
+
+
+
+/* Apply changes to the line vector LINES. DIFFBUF is a buffer of
+ length DIFFLEN holding the change text from an RCS file (the output
+ of diff -n). NAME is used in error messages. The VERS field of
+ any line added is set to ADDVERS. The VERS field of any line
+ deleted is set to DELVERS, unless DELVERS is NULL, in which case
+ the VERS field of deleted lines is unchanged. The function returns
+ non-zero if the change text is applied successfully. It returns
+ zero if the change text does not appear to apply to LINES (e.g., a
+ line number is invalid). If the change text is improperly
+ formatted (e.g., it is not the output of diff -n), the function
+ calls error with a status of 1, causing the program to exit. */
+static int
+apply_rcs_changes (struct linevector *lines, const char *diffbuf,
+ size_t difflen, const char *name, RCSVers *addvers,
+ RCSVers *delvers)
+{
+ const char *p;
+ const char *q;
+ int op;
+ /* The RCS format throws us for a loop in that the deltafrags (if
+ we define a deltafrag as an add or a delete) need to be applied
+ in reverse order. So we stick them into a linked list. */
+ struct deltafrag {
+ enum {FRAG_ADD, FRAG_DELETE} type;
+ unsigned long pos;
+ unsigned long nlines;
+ const char *new_lines;
+ size_t len;
+ struct deltafrag *next;
+ };
+ struct deltafrag *dfhead;
+ struct deltafrag *df;
+ int err;
+
+ dfhead = NULL;
+ for (p = diffbuf; p != NULL && p < diffbuf + difflen; )
+ {
+ op = *p++;
+ if (op != 'a' && op != 'd')
+ /* Can't just skip over the deltafrag, because the value
+ of op determines the syntax. */
+ error (1, 0, "unrecognized operation '\\x%x' in %s",
+ op, name);
+ df = xmalloc (sizeof (struct deltafrag));
+ df->next = dfhead;
+ dfhead = df;
+ df->pos = strtoul (p, (char **) &q, 10);
+
+ if (p == q)
+ error (1, 0, "number expected in %s", name);
+ p = q;
+ if (*p++ != ' ')
+ error (1, 0, "space expected in %s", name);
+ df->nlines = strtoul (p, (char **) &q, 10);
+ if (p == q)
+ error (1, 0, "number expected in %s", name);
+ p = q;
+ if (*p++ != '\012')
+ error (1, 0, "linefeed expected in %s", name);
+
+ if (op == 'a')
+ {
+ unsigned int i;
+
+ df->type = FRAG_ADD;
+ i = df->nlines;
+ /* The text we want is the number of lines specified, or
+ until the end of the value, whichever comes first (it
+ will be the former except in the case where we are
+ adding a line which does not end in newline). */
+ for (q = p; i != 0; ++q)
+ if (*q == '\n')
+ --i;
+ else if (q == diffbuf + difflen)
+ {
+ if (i != 1)
+ error (1, 0, "premature end of change in %s", name);
+ else
+ break;
+ }
+
+ /* Stash away a pointer to the text we are adding. */
+ df->new_lines = p;
+ df->len = q - p;
+
+ p = q;
+ }
+ else
+ {
+ /* Correct for the fact that line numbers in RCS files
+ start with 1. */
+ --df->pos;
+
+ assert (op == 'd');
+ df->type = FRAG_DELETE;
+ }
+ }
+
+ err = 0;
+ for (df = dfhead; df != NULL;)
+ {
+ unsigned int ln;
+
+ /* Once an error is encountered, just free the rest of the list and
+ * return.
+ */
+ if (!err)
+ switch (df->type)
+ {
+ case FRAG_ADD:
+ if (! linevector_add (lines, df->new_lines, df->len, addvers,
+ df->pos))
+ err = 1;
+ break;
+ case FRAG_DELETE:
+ if (df->pos > lines->nlines
+ || df->pos + df->nlines > lines->nlines)
+ return 0;
+ if (delvers != NULL)
+ for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
+ lines->vector[ln]->vers = delvers;
+ linevector_delete (lines, df->pos, df->nlines);
+ break;
+ }
+
+ df = df->next;
+ free (dfhead);
+ dfhead = df;
+ }
+
+ return !err;
+}
+
+
+
+/* Apply an RCS change text to a buffer. The function name starts
+ with rcs rather than RCS because this does not take an RCSNode
+ argument. NAME is used in error messages. TEXTBUF is the text
+ buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are
+ the change buffer and size. The new buffer is returned in *RETBUF
+ and *RETLEN. The new buffer is allocated by xmalloc.
+
+ Return 1 for success. On failure, call error and return 0. */
+int
+rcs_change_text (const char *name, char *textbuf, size_t textlen,
+ const char *diffbuf, size_t difflen, char **retbuf,
+ size_t *retlen)
+{
+ struct linevector lines;
+ int ret;
+
+ *retbuf = NULL;
+ *retlen = 0;
+
+ linevector_init (&lines);
+
+ if (! linevector_add (&lines, textbuf, textlen, NULL, 0))
+ error (1, 0, "cannot initialize line vector");
+
+ if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL))
+ {
+ error (0, 0, "invalid change text in %s", name);
+ ret = 0;
+ }
+ else
+ {
+ char *p;
+ size_t n;
+ unsigned int ln;
+
+ n = 0;
+ for (ln = 0; ln < lines.nlines; ++ln)
+ /* 1 for \n */
+ n += lines.vector[ln]->len + 1;
+
+ p = xmalloc (n);
+ *retbuf = p;
+
+ for (ln = 0; ln < lines.nlines; ++ln)
+ {
+ memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len);
+ p += lines.vector[ln]->len;
+ if (lines.vector[ln]->has_newline)
+ *p++ = '\n';
+ }
+
+ *retlen = p - *retbuf;
+ assert (*retlen <= n);
+
+ ret = 1;
+ }
+
+ linevector_free (&lines);
+
+ return ret;
+}
+
+
+
+/* Walk the deltas in RCS to get to revision VERSION.
+
+ If OP is RCS_ANNOTATE, then write annotations using cvs_output.
+
+ If OP is RCS_FETCH, then put the contents of VERSION into a
+ newly-malloc'd array and put a pointer to it in *TEXT. Each line
+ is \n terminated; the caller is responsible for converting text
+ files if desired. The total length is put in *LEN.
+
+ If FP is non-NULL, it should be a file descriptor open to the file
+ RCS with file position pointing to the deltas. We close the file
+ when we are done.
+
+ If LOG is non-NULL, then *LOG is set to the log message of VERSION,
+ and *LOGLEN is set to the length of the log message.
+
+ On error, give a fatal error. */
+void
+RCS_deltas (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf,
+ const char *version, enum rcs_delta_op op, char **text,
+ size_t *len, char **log, size_t *loglen)
+{
+ struct rcsbuffer rcsbuf_local;
+ char *branchversion;
+ char *cpversion;
+ char *key;
+ char *value;
+ size_t vallen;
+ RCSVers *vers;
+ RCSVers *prev_vers;
+ RCSVers *trunk_vers;
+ char *next;
+ int ishead, isnext, isversion, onbranch;
+ Node *node;
+ struct linevector headlines;
+ struct linevector curlines;
+ struct linevector trunklines;
+ int foundhead;
+
+ assert (version);
+
+ if (fp == NULL)
+ {
+ rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local);
+ rcsbuf = &rcsbuf_local;
+ }
+
+ if (log) *log = NULL;
+
+ ishead = 1;
+ vers = NULL;
+ prev_vers = NULL;
+ trunk_vers = NULL;
+ next = NULL;
+ onbranch = 0;
+ foundhead = 0;
+
+ linevector_init (&curlines);
+ linevector_init (&headlines);
+ linevector_init (&trunklines);
+
+ /* We set BRANCHVERSION to the version we are currently looking
+ for. Initially, this is the version on the trunk from which
+ VERSION branches off. If VERSION is not a branch, then
+ BRANCHVERSION is just VERSION. */
+ branchversion = xstrdup (version);
+ cpversion = strchr (branchversion, '.');
+ if (cpversion != NULL)
+ cpversion = strchr (cpversion + 1, '.');
+ if (cpversion != NULL)
+ *cpversion = '\0';
+
+ do {
+ if (! rcsbuf_getrevnum (rcsbuf, &key))
+ error (1, 0, "unexpected EOF reading RCS file %s", rcs->print_path);
+
+ if (next != NULL && ! STREQ (next, key))
+ {
+ /* This is not the next version we need. It is a branch
+ version which we want to ignore. */
+ isnext = 0;
+ isversion = 0;
+ }
+ else
+ {
+ isnext = 1;
+
+ /* look up the revision */
+ node = findnode (rcs->versions, key);
+ if (node == NULL)
+ error (1, 0,
+ "mismatch in rcs file %s between deltas and deltatexts
(%s)",
+ rcs->print_path, key);
+
+ /* Stash the previous version. */
+ prev_vers = vers;
+
+ vers = node->data;
+ next = vers->next;
+
+ /* Compare key and trunkversion now, because key points to
+ storage controlled by rcsbuf_getkey. */
+ if (STREQ (branchversion, key))
+ isversion = 1;
+ else
+ isversion = 0;
+ }
+
+ while (1)
+ {
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ error (1, 0, "%s does not appear to be a valid rcs file",
+ rcs->print_path);
+
+ if (log != NULL
+ && isversion
+ && STREQ (key, "log")
+ && STREQ (branchversion, version))
+ {
+ if (*log != NULL)
+ {
+ error (0, 0, "Duplicate `log' keyword in RCS file (`%s').",
+ rcs->print_path);
+ free (*log);
+ }
+ *log = rcsbuf_valcopy (rcsbuf, value, 0, loglen);
+ }
+
+ if (STREQ (key, "text"))
+ {
+ rcsbuf_valpolish (rcsbuf, value, 0, &vallen);
+ if (ishead)
+ {
+ if (! linevector_add (&curlines, value, vallen, NULL, 0))
+ error (1, 0, "invalid rcs file %s", rcs->print_path);
+
+ ishead = 0;
+ }
+ else if (isnext)
+ {
+ if (! apply_rcs_changes (&curlines, value, vallen,
+ rcs->path,
+ onbranch ? vers : NULL,
+ onbranch ? NULL : prev_vers))
+ error (1, 0, "invalid change text in %s",
rcs->print_path);
+ }
+ break;
+ }
+ }
+
+ if (isversion)
+ {
+ /* This is either the version we want, or it is the
+ branchpoint to the version we want. */
+ if (STREQ (branchversion, version))
+ {
+ /* This is the version we want. */
+ linevector_copy (&headlines, &curlines);
+ foundhead = 1;
+ if (onbranch)
+ {
+ /* We have found this version by tracking up a
+ branch. Restore back to the lines we saved
+ when we left the trunk, and continue tracking
+ down the trunk. */
+ onbranch = 0;
+ vers = trunk_vers;
+ next = vers->next;
+ linevector_copy (&curlines, &trunklines);
+ }
+ }
+ else
+ {
+ Node *p;
+
+ /* We need to look up the branch. */
+ onbranch = 1;
+
+ if (numdots (branchversion) < 2)
+ {
+ unsigned int ln;
+
+ /* We are leaving the trunk; save the current
+ lines so that we can restore them when we
+ continue tracking down the trunk. */
+ trunk_vers = vers;
+ linevector_copy (&trunklines, &curlines);
+
+ /* Reset the version information we have
+ accumulated so far. It only applies to the
+ changes from the head to this version. */
+ for (ln = 0; ln < curlines.nlines; ++ln)
+ curlines.vector[ln]->vers = NULL;
+ }
+
+ /* The next version we want is the entry on
+ VERS->branches which matches this branch. For
+ example, suppose VERSION is 1.21.4.3 and
+ BRANCHVERSION was 1.21. Then we look for an entry
+ starting with "1.21.4" and we'll put it (probably
+ 1.21.4.1) in NEXT. We'll advance BRANCHVERSION by
+ two dots (in this example, to 1.21.4.3). */
+
+ if (vers->branches == NULL)
+ error (1, 0, "missing expected branches in %s",
+ rcs->print_path);
+ if (!cpversion)
+ error (1, 0, "Invalid revision number in `%s'.",
+ rcs->print_path);
+ *cpversion = '.';
+ ++cpversion;
+ cpversion = strchr (cpversion, '.');
+ if (cpversion == NULL)
+ error (1, 0, "version number confusion in %s",
+ rcs->print_path);
+ for (p = vers->branches->list->next;
+ p != vers->branches->list;
+ p = p->next)
+ if (strncmp (p->key, branchversion,
+ cpversion - branchversion) == 0)
+ break;
+ if (p == vers->branches->list)
+ error (1, 0, "missing expected branch in %s",
+ rcs->print_path);
+
+ next = p->key;
+
+ cpversion = strchr (cpversion + 1, '.');
+ if (cpversion != NULL)
+ *cpversion = '\0';
+ }
+ }
+ if (op == RCS_FETCH && foundhead)
+ break;
+ } while (next != NULL);
+
+ free (branchversion);
+
+ rcsbuf_cache (rcs, rcsbuf);
+
+ if (! foundhead)
+ error (1, 0, "could not find desired version %s in %s",
+ version, rcs->print_path);
+
+ /* Now print out or return the data we have just computed. */
+ switch (op)
+ {
+ case RCS_ANNOTATE:
+ {
+ unsigned int ln;
+
+ for (ln = 0; ln < headlines.nlines; ++ln)
+ {
+ char *buf;
+ /* Period which separates year from month in date. */
+ char *ym;
+ /* Period which separates month from day in date. */
+ char *md;
+ RCSVers *prvers;
+
+ prvers = headlines.vector[ln]->vers;
+ if (prvers == NULL)
+ prvers = vers;
+
+ buf = Xasprintf ("%-12s (%-*.*s ",
+ prvers->version,
+ annotate_width, annotate_width,
+ prvers->author);
+ cvs_output (buf, 0);
+ free (buf);
+
+ /* Now output the date. */
+ ym = strchr (prvers->date, '.');
+ if (ym == NULL)
+ {
+ cvs_output ("??", 0);
+ cvs_output ("-???", 0);
+ cvs_output ("-??", 0);
+ }
+ else
+ {
+ md = strchr (ym + 1, '.');
+ if (md == NULL)
+ cvs_output ("??", 0);
+ else
+ cvs_output (md + 1, 2);
+
+ cvs_output ("-", 1);
+ cvs_output (month_printname (ym + 1), 0);
+ cvs_output ("-", 1);
+ /* Only output the last two digits of the year. Our
output
+ lines are long enough as it is without printing the
+ century. */
+ cvs_output (ym - 2, 2);
+ }
+ cvs_output ("): ", 0);
+ if (headlines.vector[ln]->len != 0)
+ cvs_output (headlines.vector[ln]->text,
+ headlines.vector[ln]->len);
+ cvs_output ("\n", 1);
+ }
+ }
+ break;
+ case RCS_FETCH:
+ {
+ char *p;
+ size_t n;
+ unsigned int ln;
+
+ assert (text != NULL);
+ assert (len != NULL);
+
+ n = 0;
+ for (ln = 0; ln < headlines.nlines; ++ln)
+ /* 1 for \n */
+ n += headlines.vector[ln]->len + 1;
+ p = xmalloc (n);
+ *text = p;
+ for (ln = 0; ln < headlines.nlines; ++ln)
+ {
+ memcpy (p, headlines.vector[ln]->text,
+ headlines.vector[ln]->len);
+ p += headlines.vector[ln]->len;
+ if (headlines.vector[ln]->has_newline)
+ *p++ = '\n';
+ }
+ *len = p - *text;
+ assert (*len <= n);
+ }
+ break;
+ }
+
+ linevector_free (&curlines);
+ linevector_free (&headlines);
+ linevector_free (&trunklines);
+
+ return;
+}
+
+
+
+/* Read the information for a single delta from the RCS buffer RCSBUF,
+ whose name is RCSFILE. *KEYP and *VALP are either NULL, or the
+ first key/value pair to read, as set by rcsbuf_getkey. Return NULL
+ if there are no more deltas. Store the key/value pair which
+ terminated the read in *KEYP and *VALP. */
+static RCSVers *
+getdelta (struct rcsbuffer *rcsbuf, char *rcsfile, char **keyp, char **valp)
+{
+ RCSVers *vnode;
+ char *key, *value, *cp;
+ Node *kv;
+
+ /* Get revision number if it wasn't passed in. This uses
+ rcsbuf_getkey because it doesn't croak when encountering
+ unexpected input. As a result, we have to play unholy games
+ with `key' and `value'. */
+ if (*keyp != NULL)
+ {
+ key = *keyp;
+ value = *valp;
+ }
+ else
+ {
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ error (1, 0, "%s: unexpected EOF", rcsfile);
+ }
+
+ /* Make sure that it is a revision number and not a cabbage
+ or something. */
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
+ /* do nothing */ ;
+ /* Note that when comparing with RCSDATE, we are not massaging
+ VALUE from the string found in the RCS file. This is OK since
+ we know exactly what to expect. */
+ if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0)
+ {
+ *keyp = key;
+ *valp = value;
+ return NULL;
+ }
+
+ vnode = xmalloc (sizeof (RCSVers));
+ memset (vnode, 0, sizeof (RCSVers));
+
+ vnode->version = xstrdup (key);
+
+ /* Grab the value of the date from value. Note that we are not
+ massaging VALUE from the string found in the RCS file. */
+ cp = value + (sizeof RCSDATE) - 1; /* skip the "date" keyword */
+ while (whitespace (*cp)) /* take space off front of value */
+ cp++;
+
+ vnode->date = xstrdup (cp);
+
+ /* Get author field. */
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ {
+ error (1, 0, "unexpected end of file reading %s", rcsfile);
+ }
+ if (! STREQ (key, "author"))
+ error (1, 0, "\
+unable to parse %s; `author' not in the expected place", rcsfile);
+ vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
+
+ /* Get state field. */
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ {
+ error (1, 0, "unexpected end of file reading %s", rcsfile);
+ }
+ if (! STREQ (key, "state"))
+ error (1, 0, "\
+unable to parse %s; `state' not in the expected place", rcsfile);
+ vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
+ /* The value is optional, according to rcsfile(5). */
+ if (value != NULL && STREQ (value, RCSDEAD))
+ {
+ vnode->dead = 1;
+ }
+
+ /* Note that "branches" and "next" are in fact mandatory, according
+ to doc/RCSFILES. */
+
+ /* fill in the branch list (if any branches exist) */
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ {
+ error (1, 0, "unexpected end of file reading %s", rcsfile);
+ }
+ if (STREQ (key, RCSDESC))
+ {
+ *keyp = key;
+ *valp = value;
+ /* Probably could/should be a fatal error. */
+ error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile);
+ return vnode;
+ }
+ if (value != NULL)
+ {
+ vnode->branches = getlist ();
+ /* Note that we are not massaging VALUE from the string found
+ in the RCS file. */
+ do_branches (vnode->branches, value);
+ }
+
+ /* fill in the next field if there is a next revision */
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ {
+ error (1, 0, "unexpected end of file reading %s", rcsfile);
+ }
+ if (STREQ (key, RCSDESC))
+ {
+ *keyp = key;
+ *valp = value;
+ /* Probably could/should be a fatal error. */
+ error (0, 0, "warning: 'next' keyword missing from %s", rcsfile);
+ return vnode;
+ }
+ if (value != NULL)
+ vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
+
+ /*
+ * XXX - this is where we put the symbolic link stuff???
+ * (into newphrases in the deltas).
+ */
+ while (1)
+ {
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ error (1, 0, "unexpected end of file reading %s", rcsfile);
+
+ /* The `desc' keyword is the end of the deltas. */
+ if (strcmp (key, RCSDESC) == 0)
+ break;
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+
+ /* The `hardlinks' value is a group of words, which must
+ be parsed separately and added as a list to vnode->hardlinks. */
+ if (strcmp (key, "hardlinks") == 0)
+ {
+ char *word;
+
+ vnode->hardlinks = getlist();
+ while ((word = rcsbuf_valword (rcsbuf, &value)) != NULL)
+ {
+ Node *n = getnode();
+ n->key = word;
+ addnode (vnode->hardlinks, n);
+ }
+ continue;
+ }
+#endif
+
+ /* Enable use of repositories created by certain obsolete
+ versions of CVS. This code should remain indefinately;
+ there is no procedure for converting old repositories, and
+ checking for it is harmless. */
+ if (STREQ (key, RCSDEAD))
+ {
+ vnode->dead = 1;
+ if (vnode->state != NULL)
+ free (vnode->state);
+ vnode->state = xstrdup (RCSDEAD);
+ continue;
+ }
+ /* if we have a new revision number, we're done with this delta */
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
+ /* do nothing */ ;
+ /* Note that when comparing with RCSDATE, we are not massaging
+ VALUE from the string found in the RCS file. This is OK
+ since we know exactly what to expect. */
+ if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
+ break;
+
+ /* At this point, key and value represent a user-defined field
+ in the delta node. */
+ if (vnode->other_delta == NULL)
+ vnode->other_delta = getlist ();
+ kv = getnode ();
+ kv->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
+ kv->key = xstrdup (key);
+ kv->data = rcsbuf_valcopy (rcsbuf, value, kv->type == RCSFIELD, NULL);
+ if (addnode (vnode->other_delta, kv) != 0)
+ {
+ /* Complaining about duplicate keys in newphrases seems
+ questionable, in that we don't know what they mean and
+ doc/RCSFILES has no prohibition on several newphrases
+ with the same key. But we can't store more than one as
+ long as we store them in a List *. */
+ error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
+ key, rcsfile);
+ freenode (kv);
+ }
+ }
+
+ /* Return the key which caused us to fail back to the caller. */
+ *keyp = key;
+ *valp = value;
+
+ return vnode;
+}
+
+
+
+static void
+freedeltatext (Deltatext *d)
+{
+ if (d->version != NULL)
+ free (d->version);
+ if (d->log != NULL)
+ free (d->log);
+ if (d->text != NULL)
+ free (d->text);
+ if (d->other != NULL)
+ dellist (&d->other);
+ free (d);
+}
+
+static Deltatext *
+RCS_getdeltatext (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf)
+{
+ char *num;
+ char *key, *value;
+ Node *p;
+ Deltatext *d;
+
+ /* Get the revision number. */
+ if (! rcsbuf_getrevnum (rcsbuf, &num))
+ {
+ /* If num == NULL, it means we reached EOF naturally. That's
+ fine. */
+ if (num == NULL)
+ return NULL;
+ else
+ error (1, 0, "%s: unexpected EOF", rcs->print_path);
+ }
+
+ p = findnode (rcs->versions, num);
+ if (p == NULL)
+ error (1, 0, "mismatch in rcs file %s between deltas and deltatexts
(%s)",
+ rcs->print_path, num);
+
+ d = xmalloc (sizeof (Deltatext));
+ d->version = xstrdup (num);
+
+ /* Get the log message. */
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ error (1, 0, "%s, delta %s: unexpected EOF", rcs->print_path, num);
+ if (! STREQ (key, "log"))
+ error (1, 0, "%s, delta %s: expected `log', got `%s'",
+ rcs->print_path, num, key);
+ d->log = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
+
+ /* Get random newphrases. */
+ d->other = getlist();
+ while (1)
+ {
+ if (! rcsbuf_getkey (rcsbuf, &key, &value))
+ error (1, 0, "%s, delta %s: unexpected EOF", rcs->print_path, num);
+
+ if (STREQ (key, "text"))
+ break;
+
+ p = getnode();
+ p->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
+ p->key = xstrdup (key);
+ p->data = rcsbuf_valcopy (rcsbuf, value, p->type == RCSFIELD, NULL);
+ if (addnode (d->other, p) < 0)
+ {
+ error (0, 0, "warning: %s, delta %s: duplicate field `%s'",
+ rcs->print_path, num, key);
+ }
+ }
+
+ /* Get the change text. We already know that this key is `text'. */
+ d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len);
+
+ return d;
+}
+
+
+
+/* RCS output functions, for writing RCS format files from RCSNode
+ structures.
+
+ For most of this work, RCS 5.7 uses an `aprintf' function which aborts
+ program upon error. Instead, these functions check the output status
+ of the stream right before closing it, and aborts if an error condition
+ is found. The RCS solution is probably the better one: it produces
+ more overhead, but will produce a clearer diagnostic in the case of
+ catastrophic error. In either case, however, the repository will probably
+ not get corrupted. */
+static int
+putsymbol_proc (Node *symnode, void *fparg)
+{
+ FILE *fp = 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;
+}
+
+
+
+/* putlock_proc is like putsymbol_proc, but key and data are reversed. */
+static int
+putlock_proc (Node *symnode, void *fp)
+{
+ return fprintf (fp, "\n\t%s:%s", (char *)symnode->data, symnode->key);
+}
+
+
+
+static int
+putrcsfield_proc (Node *node, void *vfp)
+{
+ FILE *fp = vfp;
+
+ /* Some magic keys used internally by CVS start with `;'. Skip them. */
+ if (node->key[0] == ';')
+ return 0;
+
+ fprintf (fp, "\n%s\t", node->key);
+ if (node->data != NULL)
+ {
+ /* If the field's value contains evil characters,
+ it must be stringified. */
+ /* FIXME: This does not quite get it right. "7jk8f" is not a valid
+ value for a value in a newpharse, according to doc/RCSFILES,
+ because digits are not valid in an "id". We might do OK by
+ always writing strings (enclosed in @@). Would be nice to
+ explicitly mention this one way or another in doc/RCSFILES.
+ A case where we are wrong in a much more clear-cut way is that
+ we let through non-graphic characters such as whitespace and
+ control characters. */
+
+ if (node->type == RCSCMPFLD || strpbrk (node->data, "$,.:;@") == NULL)
+ fputs (node->data, fp);
+ else
+ {
+ putc ('@', fp);
+ expand_at_signs (node->data, (off_t) strlen (node->data), fp);
+ putc ('@', fp);
+ }
+ }
+
+ /* desc, log and text fields should not be terminated with semicolon;
+ all other fields should be. */
+ if (! STREQ (node->key, "desc") &&
+ ! STREQ (node->key, "log") &&
+ ! STREQ (node->key, "text"))
+ {
+ putc (';', fp);
+ }
+ return 0;
+}
+
+
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+
+/* Save a filename in a `hardlinks' RCS field. NODE->KEY will contain
+ a full pathname, but currently only basenames are stored in the RCS
+ node. Assume that the filename includes nasty characters and
+ @-escape it. */
+
+static int
+puthardlink_proc (node, vfp)
+ Node *node;
+ void *vfp;
+{
+ FILE *fp = vfp;
+ char *basename = strrchr (node->key, '/');
+
+ if (basename == NULL)
+ basename = node->key;
+ else
+ ++basename;
+
+ putc ('\t', fp);
+ putc ('@', fp);
+ (void) expand_at_signs (basename, strlen (basename), fp);
+ putc ('@', fp);
+
+ return 0;
+}
+
+#endif /* PRESERVE_PERMISSIONS_SUPPORT */
+
+
+
+/* Output the admin node for RCS into stream FP. */
+static void
+RCS_putadmin (RCSNode *rcs, FILE *fp)
+{
+ fprintf (fp, "%s\t%s;\n", RCSHEAD, rcs->head ? rcs->head : "");
+ if (rcs->branch)
+ fprintf (fp, "%s\t%s;\n", RCSBRANCH, rcs->branch);
+
+ fputs ("access", fp);
+ if (rcs->access)
+ {
+ char *p, *s;
+ s = xstrdup (rcs->access);
+ for (p = strtok (s, " \n\t"); p != NULL; p = strtok (NULL, " \n\t"))
+ fprintf (fp, "\n\t%s", p);
+ free (s);
+ }
+ fputs (";\n", fp);
+
+ fputs (RCSSYMBOLS, fp);
+ /* If we haven't had to convert the symbols to a list yet, don't
+ force a conversion now; just write out the string. */
+ if (rcs->symbols == NULL && rcs->symbols_data != NULL)
+ {
+ fputs ("\n\t", fp);
+ fputs (rcs->symbols_data, fp);
+ }
+ else
+ walklist (RCS_symbols (rcs), putsymbol_proc, fp);
+ fputs (";\n", fp);
+
+ fputs ("locks", fp);
+ if (rcs->locks_data)
+ fprintf (fp, "\t%s", rcs->locks_data);
+ else if (rcs->locks)
+ walklist (rcs->locks, putlock_proc, fp);
+ if (rcs->strict_locks)
+ fprintf (fp, "; strict");
+ fputs (";\n", fp);
+
+ if (rcs->comment)
+ {
+ fprintf (fp, "comment\t@");
+ expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp);
+ fputs ("@;\n", fp);
+ }
+ if (rcs->expand && ! STREQ (rcs->expand, "kv"))
+ fprintf (fp, "address@hidden@;\n", RCSEXPAND, rcs->expand);
+
+ walklist (rcs->other, putrcsfield_proc, fp);
+
+ putc ('\n', fp);
+}
+
+
+
+static void
+putdelta (RCSVers *vers, FILE *fp)
+{
+ Node *bp, *start;
+
+ /* Skip if no revision was supplied, or if it is outdated (cvs admin -o) */
+ if (vers == NULL || vers->outdated)
+ return;
+
+ fprintf (fp, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
+ vers->version,
+ RCSDATE, vers->date,
+ "author", vers->author,
+ "state", vers->state ? vers->state : "");
+
+ if (vers->branches != NULL)
+ {
+ start = vers->branches->list;
+ for (bp = start->next; bp != start; bp = bp->next)
+ fprintf (fp, "\n\t%s", bp->key);
+ }
+
+ fprintf (fp, ";\nnext\t%s;", vers->next ? vers->next : "");
+
+ walklist (vers->other_delta, putrcsfield_proc, fp);
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (vers->hardlinks)
+ {
+ fprintf (fp, "\nhardlinks");
+ walklist (vers->hardlinks, puthardlink_proc, fp);
+ putc (';', fp);
+ }
+#endif
+ putc ('\n', fp);
+}
+
+
+
+static void
+RCS_putdtree (RCSNode *rcs, char *rev, FILE *fp)
+{
+ RCSVers *versp;
+ Node *p, *branch;
+
+ /* Previously, this function used a recursive implementation, but
+ if the trunk has a huge number of revisions and the program
+ stack is not big, a stack overflow could occur, so this
+ nonrecursive version was developed to be more safe. */
+ Node *branchlist, *onebranch;
+ List *branches;
+ List *onebranchlist;
+
+ if (rev == NULL)
+ return;
+
+ branches = getlist();
+
+ for (; rev != NULL;)
+ {
+ /* Find the delta node for this revision. */
+ p = findnode (rcs->versions, rev);
+ if (p == NULL)
+ {
+ error (1, 0,
+ "error parsing repository file %s, file may be corrupt.",
+ rcs->path);
+ }
+
+ versp = p->data;
+
+ /* Print the delta node and go for its `next' node. This
+ prints the trunk. If there are any branches printed on this
+ revision, mark we have some. */
+ putdelta (versp, fp);
+ /* Store branch information into branch list so to write its
+ trunk afterwards */
+ if (versp->branches != NULL)
+ {
+ branch = getnode();
+ branch->data = versp->branches;
+
+ addnode(branches, branch);
+ }
+
+ rev = versp->next;
+ }
+
+ /* If there are any branches printed on this revision,
+ print those trunks as well. */
+ branchlist = branches->list;
+ for (branch = branchlist->next;
+ branch != branchlist;
+ branch = branch->next)
+ {
+ onebranchlist = (List *)(branch->data);
+ onebranch = onebranchlist->list;
+ for (p = onebranch->next; p != onebranch; p = p->next)
+ RCS_putdtree (rcs, p->key, fp);
+
+ branch->data = NULL; /* so to prevent its freeing on dellist */
+ }
+
+ dellist(&branches);
+}
+
+
+
+static void
+RCS_putdesc (RCSNode *rcs, FILE *fp)
+{
+ fprintf (fp, "\n\n%s\n@", RCSDESC);
+ if (rcs->desc != NULL)
+ {
+ off_t len = (off_t) strlen (rcs->desc);
+ if (len > 0)
+ {
+ expand_at_signs (rcs->desc, len, fp);
+ if (rcs->desc[len-1] != '\n')
+ putc ('\n', fp);
+ }
+ }
+ fputs ("@\n", fp);
+}
+
+
+
+static void
+putdeltatext (FILE *fp, Deltatext *d)
+{
+ fprintf (fp, "\n\n%s\nlog\n@", d->version);
+ if (d->log != NULL)
+ {
+ int loglen = strlen (d->log);
+ expand_at_signs (d->log, (off_t) loglen, fp);
+ if (d->log[loglen-1] != '\n')
+ putc ('\n', fp);
+ }
+ putc ('@', fp);
+
+ walklist (d->other, putrcsfield_proc, fp);
+
+ fputs ("\ntext\n@", fp);
+ if (d->text != NULL)
+ expand_at_signs (d->text, (off_t) d->len, fp);
+ fputs ("@\n", fp);
+}
+
+
+
+/* TODO: the whole mechanism for updating deltas is kludgey... more
+ sensible would be to supply all the necessary info in a `newdeltatext'
+ field for RCSVers nodes. -twp */
+
+/* Copy delta text nodes from FIN to FOUT. If NEWDTEXT is non-NULL, it
+ is a new delta text node, and should be added to the tree at the
+ node whose revision number is INSERTPT. (Note that trunk nodes are
+ written in decreasing order, and branch nodes are written in
+ increasing order.) */
+static void
+RCS_copydeltas (RCSNode *rcs, FILE *fin, struct rcsbuffer *rcsbufin,
+ FILE *fout, Deltatext *newdtext, char *insertpt)
+{
+ int actions;
+ RCSVers *dadmin;
+ Node *np;
+ int insertbefore, found;
+ char *bufrest;
+ int nls;
+ size_t buflen;
+#ifndef HAVE_MMAP
+ char buf[8192];
+ int got;
+#endif
+
+ /* Count the number of versions for which we have to do some
+ special operation. */
+ actions = walklist (rcs->versions, count_delta_actions, NULL);
+
+ /* Make a note of whether NEWDTEXT should be inserted
+ before or after its INSERTPT. */
+ insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1);
+
+ while (actions != 0 || newdtext != NULL)
+ {
+ Deltatext *dtext;
+
+ dtext = RCS_getdeltatext (rcs, fin, rcsbufin);
+
+ /* We shouldn't hit EOF here, because that would imply that
+ some action was not taken, or that we could not insert
+ NEWDTEXT. */
+ if (dtext == NULL)
+ error (1, 0, "internal error: EOF too early in RCS_copydeltas");
+
+ found = (insertpt != NULL && STREQ (dtext->version, insertpt));
+ if (found && insertbefore)
+ {
+ putdeltatext (fout, newdtext);
+ newdtext = NULL;
+ insertpt = NULL;
+ }
+
+ np = findnode (rcs->versions, dtext->version);
+ dadmin = np->data;
+
+ /* If this revision has been outdated, just skip it. */
+ if (dadmin->outdated)
+ {
+ freedeltatext (dtext);
+ --actions;
+ continue;
+ }
+
+ /* Update the change text for this delta. New change text
+ data may come from cvs admin -m, cvs admin -o, or cvs ci. */
+ if (dadmin->text != NULL)
+ {
+ if (dadmin->text->log != NULL || dadmin->text->text != NULL)
+ --actions;
+ if (dadmin->text->log != NULL)
+ {
+ free (dtext->log);
+ dtext->log = dadmin->text->log;
+ dadmin->text->log = NULL;
+ }
+ if (dadmin->text->text != NULL)
+ {
+ free (dtext->text);
+ dtext->text = dadmin->text->text;
+ dtext->len = dadmin->text->len;
+ dadmin->text->text = NULL;
+ }
+ }
+ putdeltatext (fout, dtext);
+ freedeltatext (dtext);
+
+ if (found && !insertbefore)
+ {
+ putdeltatext (fout, newdtext);
+ newdtext = NULL;
+ insertpt = NULL;
+ }
+ }
+
+ /* Copy the rest of the file directly, without bothering to
+ interpret it. The caller will handle error checking by calling
+ ferror.
+
+ We just wrote a newline to the file, either in putdeltatext or
+ in the caller. However, we may not have read the corresponding
+ newline from the file, because rcsbuf_getkey returns as soon as
+ it finds the end of the '@' string for the desc or text key.
+ Therefore, we may read three newlines when we should really
+ only write two, and we check for that case here. This is not
+ an semantically important issue; we only do it to make our RCS
+ files look traditional. */
+
+ nls = 3;
+
+ rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen);
+ if (buflen > 0)
+ {
+ if (bufrest[0] != '\n'
+ || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0)
+ {
+ nls = 0;
+ }
+ else
+ {
+ if (buflen < 3)
+ nls -= buflen;
+ else
+ {
+ ++bufrest;
+ --buflen;
+ nls = 0;
+ }
+ }
+
+ fwrite (bufrest, 1, buflen, fout);
+ }
+#ifndef HAVE_MMAP
+ /* This bit isn't necessary when using mmap since the entire file
+ * will already be available via the RCS buffer. Besides, the
+ * mmap code doesn't always keep the file pointer up to date, so
+ * this adds some data twice.
+ */
+ while ((got = fread (buf, 1, sizeof buf, fin)) != 0)
+ {
+ if (nls > 0
+ && got >= nls
+ && buf[0] == '\n'
+ && strncmp (buf, "\n\n\n", nls) == 0)
+ {
+ fwrite (buf + 1, 1, got - 1, fout);
+ }
+ else
+ {
+ fwrite (buf, 1, got, fout);
+ }
+
+ nls = 0;
+ }
+#endif /* HAVE_MMAP */
+}
+
+
+
+/* A helper procedure for RCS_copydeltas. This is called via walklist
+ to count the number of RCS revisions for which some special action
+ is required. */
+static int
+count_delta_actions (Node *np, void *ignore)
+{
+ RCSVers *dadmin = np->data;
+
+ if (dadmin->outdated)
+ return 1;
+
+ if (dadmin->text != NULL
+ && (dadmin->text->log != NULL || dadmin->text->text != NULL))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Clean up temporary files.
+ *
+ * NOTES
+ * This function needs to be reentrant since a call to exit() can cause a
+ * call to this function, which can then be interrupted by a signal, which
+ * can cause a second call to this function.
+ *
+ * RETURNS
+ * Nothing.
+ */
+static void
+rcs_cleanup (void)
+{
+ TRACE (TRACE_FUNCTION, "rcs_cleanup()");
+
+ /* FIXME: Do not perform buffered I/O from an interrupt handler like
+ * this (via error). However, I'm leaving the error-calling code there
+ * in the hope that on the rare occasion the error call is actually made
+ * (e.g., a fluky I/O error or permissions problem prevents the deletion
+ * of a just-created file) reentrancy won't be an issue.
+ */
+
+ /* We don't want to be interrupted during calls which set globals to NULL,
+ * but we know that by the time we reach this function, interrupts have
+ * already been blocked.
+ */
+ if (rcs_lockfile != NULL)
+ {
+ /* Use a tmp var since any of these functions could call exit, causing
+ * us to be called a second time.
+ */
+ char *tmp = rcs_lockfile;
+ rcs_lockfile = NULL;
+ if (rcs_lockfd >= 0)
+ {
+ if (close (rcs_lockfd) != 0)
+ error (0, errno, "error closing lock file %s", tmp);
+ rcs_lockfd = -1;
+ }
+
+ /* Note that the checks for existence_error are because we can be
+ * called from a signal handler, so we don't know whether the
+ * files got created.
+ */
+ if (unlink_file (tmp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmp);
+ }
+}
+
+
+
+/* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style
+ locking on the specified RCSFILE: for a file called `foo,v', open
+ for writing a file called `,foo,'.
+
+ Note that we what do here is quite different from what RCS does.
+ RCS creates the ,foo, file before it reads the RCS file (if it
+ knows that it will be writing later), so that it actually serves as
+ a lock. We don't; instead we rely on CVS writelocks. This means
+ that if someone is running RCS on the file at the same time they
+ are running CVS on it, they might lose (we read the file,
+ then RCS writes it, then we write it, clobbering the
+ changes made by RCS). I believe the current sentiment about this
+ is "well, don't do that".
+
+ A concern has been expressed about whether adopting the RCS
+ strategy would slow us down. I don't think so, since we need to
+ write the ,foo, file anyway (unless perhaps if O_EXCL is slower or
+ something).
+
+ These do not perform quite the same function as the RCS -l option
+ for locking files: they are intended to prevent competing RCS
+ processes from stomping all over each other's laundry. Hence,
+ they are `internal' locking functions.
+
+ If there is an error, give a fatal error; if we return we always
+ return a non-NULL value. */
+static FILE *
+rcs_internal_lockfile (char *rcsfile)
+{
+ struct stat rstat;
+ FILE *fp;
+ static int first_call = 1;
+
+ if (first_call)
+ {
+ first_call = 0;
+ /* Clean up if we get a signal or exit. */
+ cleanup_register (rcs_cleanup);
+ }
+
+ /* Get the lock file name: `,file,' for RCS file `file,v'. */
+ assert (rcs_lockfile == NULL);
+ assert (rcs_lockfd < 0);
+ rcs_lockfile = rcs_lockfilename (rcsfile);
+
+ /* Use the existing RCS file mode, or read-only if this is a new
+ file. (Really, this is a lie -- if this is a new file,
+ RCS_checkin uses the permissions from the working copy. For
+ actually creating the file, we use 0444 as a safe default mode.) */
+ if (stat (rcsfile, &rstat) < 0)
+ {
+ if (existence_error (errno))
+ rstat.st_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ else
+ error (1, errno, "cannot stat %s", rcsfile);
+ }
+
+ /* Try to open exclusively. POSIX.1 guarantees that O_EXCL|O_CREAT
+ guarantees an exclusive open. According to the RCS source, with
+ NFS v2 we must also throw in O_TRUNC and use an open mask that makes
+ the file unwriteable. For extensive justification, see the comments for
+ rcswriteopen() in rcsedit.c, in RCS 5.7. This is kind of pointless
+ in the CVS case; see comment at the start of this file concerning
+ general ,foo, file strategy.
+
+ There is some sentiment that with NFSv3 and such, that one can
+ rely on O_EXCL these days. This might be true for unix (I
+ don't really know), but I am still pretty skeptical in the case
+ of the non-unix systems. */
+ rcs_lockfd = open (rcs_lockfile,
+ OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+
+ if (rcs_lockfd < 0)
+ {
+ error (1, errno, "could not open lock file `%s'", rcs_lockfile);
+ }
+
+ /* Force the file permissions, and return a stream object. */
+ /* Because we change the modes later, we don't worry about
+ this in the non-HAVE_FCHMOD case. */
+#ifdef HAVE_FCHMOD
+ if (fchmod (rcs_lockfd, rstat.st_mode) < 0)
+ error (1, errno, "cannot change mode for %s", rcs_lockfile);
+#endif
+ fp = fdopen (rcs_lockfd, FOPEN_BINARY_WRITE);
+ if (fp == NULL)
+ error (1, errno, "cannot fdopen %s", rcs_lockfile);
+
+ return fp;
+}
+
+
+
+static void
+rcs_internal_unlockfile (FILE *fp, char *rcsfile)
+{
+ assert (rcs_lockfile != NULL);
+ assert (rcs_lockfd >= 0);
+
+ /* Abort if we could not write everything successfully to LOCKFILE.
+ This is not a great error-handling mechanism, but should prevent
+ corrupting the repository. */
+
+ if (ferror (fp))
+ /* Using errno here may well be misleanding since the most recent
+ call that set errno may not have anything whatsoever to do with
+ the error that set the flag, but it's better than nothing. The
+ real solution is to check each call to fprintf rather than waiting
+ until the end like this. */
+ error (1, errno, "error writing to lock file %s", rcs_lockfile);
+
+ /* Flush and sync the file, or the user may be told the commit completed,
+ * while a server crash/power failure could still cause the data to be
+ * lost.
+ *
+ * Invoking rename(",<file>," , "<file>,v") on Linux and almost all UNIXs
+ * only flushes the inode for the target file to disk, it does not
+ * guarantee flush of the kernel buffers allocated for the ,<file>,.
+ * Depending upon the load on the machine, the Linux kernel's flush daemon
+ * process may not flush for a while. In the meantime the CVS transaction
+ * could have been declared committed to the end CVS user (CVS process has
+ * returned the final "OK"). If the machine crashes prior to syncing the
+ * changes to disk, the committed transaction can be lost.
+ */
+ if (fflush (fp) != 0)
+ error (1, errno, "error flushing file `%s' to kernel buffers",
+ rcs_lockfile);
+#ifdef HAVE_FSYNC
+ if (fsync (rcs_lockfd) < 0)
+ error (1, errno, "error fsyncing file `%s'", rcs_lockfile);
+#endif
+
+ if (fclose (fp) == EOF)
+ error (1, errno, "error closing lock file %s", rcs_lockfile);
+ rcs_lockfd = -1;
+
+ rename_file (rcs_lockfile, rcsfile);
+
+ {
+ /* Use a temporary to make sure there's no interval
+ (after rcs_lockfile has been freed but before it's set to NULL)
+ during which the signal handler's use of rcs_lockfile would
+ reference freed memory. */
+ char *tmp = rcs_lockfile;
+ rcs_lockfile = NULL;
+ free (tmp);
+ }
+}
+
+
+
+static char *
+rcs_lockfilename (const char *rcsfile)
+{
+ char *lockfile, *lockp;
+ const char *rcsbase, *rcsp, *rcsend;
+ int rcslen;
+
+ /* Create the lockfile name. */
+ rcslen = strlen (rcsfile);
+ lockfile = xmalloc (rcslen + 10);
+ rcsbase = last_component (rcsfile);
+ rcsend = rcsfile + rcslen - sizeof(RCSEXT);
+ for (lockp = lockfile, rcsp = rcsfile; rcsp < rcsbase; ++rcsp)
+ *lockp++ = *rcsp;
+ *lockp++ = ',';
+ while (rcsp <= rcsend)
+ *lockp++ = *rcsp++;
+ *lockp++ = ',';
+ *lockp = '\0';
+
+ return lockfile;
+}
+
+
+
+/* Rewrite an RCS file. The basic idea here is that the caller should
+ first call RCS_reparsercsfile, then munge the data structures as
+ desired (via RCS_delete_revs, RCS_settag, &c), then call RCS_rewrite. */
+void
+RCS_rewrite (RCSNode *rcs, Deltatext *newdtext, char *insertpt)
+{
+ FILE *fin, *fout;
+ struct rcsbuffer rcsbufin;
+
+ if (noexec)
+ return;
+
+ /* Make sure we're operating on an actual file and not a symlink. */
+ resolve_symlink (&(rcs->path));
+
+ fout = rcs_internal_lockfile (rcs->path);
+
+ RCS_putadmin (rcs, fout);
+ RCS_putdtree (rcs, rcs->head, fout);
+ RCS_putdesc (rcs, fout);
+
+ /* Open the original RCS file and seek to the first delta text. */
+ rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin);
+
+ /* Update delta_pos to the current position in the output file.
+ Do NOT move these statements: they must be done after fin has
+ been positioned at the old delta_pos, but before any delta
+ texts have been written to fout.
+ */
+ rcs->delta_pos = ftello (fout);
+ if (rcs->delta_pos == -1)
+ error (1, errno, "cannot ftello in RCS file %s", rcs->path);
+
+ RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt);
+
+ /* We don't want to call rcsbuf_cache here, since we're about to
+ delete the file. */
+ rcsbuf_close (&rcsbufin);
+ if (ferror (fin))
+ /* The only case in which using errno here would be meaningful
+ is if we happen to have left errno unmolested since the call
+ which produced the error (e.g. fread). That is pretty
+ fragile even if it happens to sometimes be true. The real
+ solution is to make sure that all the code which reads
+ from fin checks for errors itself (some does, some doesn't). */
+ error (0, 0, "warning: ferror set while rewriting RCS file `%s'",
rcs->path);
+ if (fclose (fin) < 0)
+ error (0, errno, "warning: closing RCS file `%s'", rcs->path);
+
+ rcs_internal_unlockfile (fout, rcs->path);
+}
+
+
+
+/* Abandon changes to an RCS file. */
+void
+RCS_abandon (RCSNode *rcs)
+{
+ free_rcsnode_contents (rcs);
+ rcs->symbols_data = NULL;
+ rcs->expand = NULL;
+ rcs->access = NULL;
+ rcs->locks_data = NULL;
+ rcs->comment = NULL;
+ rcs->desc = NULL;
+ rcs->flags |= PARTIAL;
+}
+
+
+
+/*
+ * For a given file with full pathname PATH and revision number REV,
+ * produce a file label suitable for passing to diff. The default
+ * file label as used by RCS 5.7 looks like this:
+ *
+ * FILENAME <tab> YYYY/MM/DD <sp> HH:MM:SS <tab> REVNUM
+ *
+ * The date and time used are the revision's last checkin date and time.
+ * If REV is NULL, use the working copy's mtime instead.
+ *
+ * /dev/null is not statted but assumed to have been created on the Epoch.
+ * At least using the POSIX.2 definition of patch, this should cause creation
+ * of files on platforms such as Windoze where the null IO device isn't named
+ * /dev/null to be parsed by patch properly.
+ */
+char *
+make_file_label (const char *path, const char *rev, RCSNode *rcs)
+{
+ char datebuf[MAXDATELEN + 1];
+ char *label;
+
+ if (rev)
+ {
+ char date[MAXDATELEN + 1];
+ /* revs cannot be attached to /dev/null ... duh. */
+ assert (strcmp(DEVNULL, path));
+ RCS_getrevtime (rcs, rev, datebuf, 0);
+ (void) date_to_internet (date, datebuf);
+ label = Xasprintf ("-L%s\t%s\t%s", path, date, rev);
+ }
+ else
+ {
+ struct stat sb;
+ struct tm *wm;
+
+ if (strcmp(DEVNULL, path))
+ {
+ const char *file = last_component (path);
+ if (stat (file, &sb) < 0)
+ /* Assume that if the stat fails,then the later read for the
+ * diff will too.
+ */
+ error (1, errno, "could not get info for `%s'", path);
+ wm = gmtime (&sb.st_mtime);
+ }
+ else
+ {
+ time_t t = 0;
+ wm = gmtime(&t);
+ }
+
+ (void) tm_to_internet (datebuf, wm);
+ label = Xasprintf ("-L%s\t%s", path, datebuf);
+ }
+ return label;
+}
+
+
+
+/*
+ * Set up a local/custom RCS keyword for expansion.
+ *
+ * INPUTS
+ * infopath Path to file being parsed, for error messages.
+ * ln Line number of INFOPATH being processed, for
error
+ * messages.
+ * keywords_in
+ * arg
+ *
+ * OUTPUTS
+ * keywords_in
+ */
+void
+RCS_setlocalid (const char *infopath, unsigned int ln,
+ void **keywords_in, const char *arg)
+{
+ char *copy, *next, *key, *s;
+ struct rcs_keyword *keywords;
+ enum keyword save_expandto;
+
+ if (!*keywords_in)
+ *keywords_in = new_keywords ();
+ keywords = *keywords_in;
+
+ copy = xstrdup (arg);
+ next = copy;
+ key = strtok (next, "=");
+
+ /*
+ * Validate key
+ */
+ for (s = key; *s != '\0'; s++)
+ {
+ if (! isalpha ((unsigned char) *s))
+ {
+ if (!parse_error (infopath, ln))
+ error (0, 0,
+"%s [%u]: LocalKeyword ignored: Bad character `%c' in key `%s'",
+ primary_root_inverse_translate (infopath),
+ ln, *s, key);
+ free (copy);
+ return;
+ }
+ }
+
+ save_expandto = keywords[KEYWORD_LOCALID].expandto;
+
+ /* options? */
+ while ((key = strtok (NULL, ",")) != NULL) {
+ if (!strcmp(key, keywords[KEYWORD_ID].string))
+ keywords[KEYWORD_LOCALID].expandto = KEYWORD_ID;
+ else if (!strcmp(key, keywords[KEYWORD_HEADER].string))
+ keywords[KEYWORD_LOCALID].expandto = KEYWORD_HEADER;
+ else if (!strcmp(key, keywords[KEYWORD_CVSHEADER].string))
+ keywords[KEYWORD_LOCALID].expandto = KEYWORD_CVSHEADER;
+ else
+ {
+ keywords[KEYWORD_LOCALID].expandto = save_expandto;
+ if (!parse_error (infopath, ln))
+ error (0, 0,
+"%s [%u]: LocalKeyword ignored: Unknown LocalId mode: `%s'",
+ primary_root_inverse_translate (infopath),
+ ln, key);
+ free (copy);
+ return;
+ }
+ }
+
+ keywords[KEYWORD_LOCALID].string = xstrdup (next);
+ keywords[KEYWORD_LOCALID].len = strlen (next);
+ keywords[KEYWORD_LOCALID].expandit = 1;
+
+ free (copy);
+}
+
+
+
+void
+RCS_setincexc (void **keywords_in, const char *arg)
+{
+ char *key;
+ char *copy, *next;
+ bool include = false;
+ struct rcs_keyword *keyword;
+ struct rcs_keyword *keywords;
+
+ if (!*keywords_in)
+ *keywords_in = new_keywords ();
+ keywords = *keywords_in;
+
+ copy = xstrdup(arg);
+ next = copy;
+ switch (*next++) {
+ case 'e':
+ include = false;
+ break;
+ case 'i':
+ include = true;
+ break;
+ default:
+ free(copy);
+ return;
+ }
+
+ if (include)
+ for (keyword = keywords; keyword->string != NULL; keyword++)
+ {
+ keyword->expandit = false;
+ }
+
+ key = strtok(next, ",");
+ while (key) {
+ for (keyword = keywords; keyword->string != NULL; keyword++) {
+ if (strcmp (keyword->string, key) == 0)
+ keyword->expandit = include;
+ }
+ key = strtok(NULL, ",");
+ }
+ free(copy);
+ return;
+}
+
+
+
+#define ATTIC "/" CVSATTIC
+static char *
+getfullCVSname(char *CVSname, char **pathstore)
+{
+ if (current_parsed_root->directory) {
+ int rootlen;
+ char *c = NULL;
+ int alen = sizeof(ATTIC) - 1;
+
+ *pathstore = xstrdup(CVSname);
+ if ((c = strrchr(*pathstore, '/')) != NULL) {
+ if (c - *pathstore >= alen) {
+ if (!strncmp(c - alen, ATTIC, alen)) {
+ while (*c != '\0') {
+ *(c - alen) = *c;
+ c++;
+ }
+ *(c - alen) = '\0';
+ }
+ }
+ }
+
+ rootlen = strlen(current_parsed_root->directory);
+ if (!strncmp(*pathstore, current_parsed_root->directory, rootlen) &&
+ (*pathstore)[rootlen] == '/')
+ CVSname = (*pathstore + rootlen + 1);
+ else
+ CVSname = (*pathstore);
+ }
+ return CVSname;
+}
Index: ccvs/src/rcs.h
diff -u /dev/null ccvs/src/rcs.h:1.83.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/rcs.h Tue Jan 17 15:41:23 2006
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * RCS source control definitions needed by rcs.c and friends
+ */
+
+/* Strings which indicate a conflict if they occur at the start of a line. */
+#define RCS_MERGE_PAT_1 "<<<<<<< "
+#define RCS_MERGE_PAT_2 "=======\n"
+#define RCS_MERGE_PAT_3 ">>>>>>> "
+
+#define RCSEXT ",v"
+#define RCSPAT "*,v"
+#define RCSHEAD "head"
+#define RCSBRANCH "branch"
+#define RCSSYMBOLS "symbols"
+#define RCSDATE "date"
+#define RCSDESC "desc"
+#define RCSEXPAND "expand"
+
+/* Used by the version of death support which resulted from old
+ versions of CVS (e.g. 1.5 if you define DEATH_SUPPORT and not
+ DEATH_STATE). Only a hacked up RCS (used by those old versions of
+ CVS) will put this into RCS files. Considered obsolete. */
+#define RCSDEAD "dead"
+
+#define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d"
+#define SDATEFORM "%d.%d.%d.%d.%d.%d"
+
+/*
+ * Opaque structure definitions used by RCS specific lookup routines
+ */
+#define VALID 0x1 /* flags field contains valid data */
+#define INATTIC 0x2 /* RCS file is located in the
Attic */
+#define PARTIAL 0x4 /* RCS file not completly parsed */
+
+/* All the "char *" fields in RCSNode, Deltatext, and RCSVers are
+ '\0'-terminated (except "text" in Deltatext). This means that we
+ can't deal with fields containing '\0', which is a limitation that
+ RCS does not have. Would be nice to fix this some day. */
+
+struct rcsnode
+{
+ /* Reference count for this structure. Used to deal with the
+ fact that there might be a pointer from the Vers_TS or might
+ not. Callers who increment this field are responsible for
+ calling freercsnode when they are done with their reference. */
+ int refcount;
+
+ /* Flags (INATTIC, PARTIAL, &c), see above. */
+ int flags;
+
+ /* File name of the RCS file. This is not necessarily the name
+ as specified by the user, but it is a name which can be passed to
+ system calls and a name which is OK to print in error messages
+ (the various names might differ in case). */
+ char *path;
+
+ /* Use when printing paths. */
+ char *print_path;
+
+ /* Value for head keyword from RCS header, or NULL if empty. HEAD may only
+ * be empty in a valid RCS file when the file has no revisions, a state
+ * that should not be able to occur with CVS.
+ */
+ char *head;
+
+ /* Value for branch keyword from RCS header, or NULL if omitted. */
+ char *branch;
+
+ /* Raw data on symbolic revisions. The first time that RCS_symbols is
+ called, we parse these into ->symbols, and free ->symbols_data. */
+ char *symbols_data;
+
+ /* Value for expand keyword from RCS header, or NULL if omitted. */
+ char *expand;
+
+ /* List of nodes, the key of which is the symbolic name and the data
+ of which is the numeric revision that it corresponds to (malloc'd). */
+ List *symbols;
+
+ /* List of nodes (type RCSVERS), the key of which the numeric revision
+ number, and the data of which is an RCSVers * for the revision. */
+ List *versions;
+
+ /* Value for access keyword from RCS header, or NULL if empty.
+ FIXME: RCS_delaccess would also seem to use "" for empty. We
+ should pick one or the other. */
+ char *access;
+
+ /* Raw data on locked revisions. The first time that RCS_getlocks is
+ called, we parse these into ->locks, and free ->locks_data. */
+ char *locks_data;
+
+ /* List of nodes, the key of which is the numeric revision and the
+ data of which is the user that it corresponds to (malloc'd). */
+ List *locks;
+
+ /* Set for the strict keyword from the RCS header. */
+ int strict_locks;
+
+ /* Value for the comment keyword from RCS header (comment leader), or
+ NULL if omitted. */
+ char *comment;
+
+ /* Value for the desc field in the RCS file, or NULL if empty. */
+ char *desc;
+
+ /* File offset of the first deltatext node, so we can seek there. */
+ off_t delta_pos;
+
+ /* Newphrases from the RCS header. List of nodes, the key of which
+ is the "id" which introduces the newphrase, and the value of which
+ is the value from the newphrase. */
+ List *other;
+};
+
+typedef struct rcsnode RCSNode;
+
+struct deltatext {
+ char *version;
+
+ /* Log message, or NULL if we do not intend to change the log message
+ (that is, RCS_copydeltas should just use the log message from the
+ file). */
+ char *log;
+
+ /* Change text, or NULL if we do not intend to change the change text
+ (that is, RCS_copydeltas should just use the change text from the
+ file). Note that it is perfectly valid to have log be NULL and
+ text non-NULL, or vice-versa. */
+ char *text;
+ size_t len;
+
+ /* Newphrase fields from deltatext nodes. FIXME: duplicates the
+ other field in the rcsversnode, I think. */
+ List *other;
+};
+typedef struct deltatext Deltatext;
+
+struct rcsversnode
+{
+ /* Duplicate of the key by which this structure is indexed. */
+ char *version;
+
+ char *date;
+ char *author;
+ char *state;
+ char *next;
+ int dead;
+ int outdated;
+ Deltatext *text;
+ List *branches;
+ /* Newphrase fields from deltatext nodes. Also contains ";add" and
+ ";delete" magic fields (see rcs.c, log.c). I think this is
+ only used by log.c (where it looks up "log"). Duplicates the
+ other field in struct deltatext, I think. */
+ List *other;
+ /* Newphrase fields from delta nodes. */
+ List *other_delta;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ /* Hard link information for each revision. */
+ List *hardlinks;
+#endif
+};
+typedef struct rcsversnode RCSVers;
+
+/*
+ * CVS reserves all even-numbered branches for its own use. "magic" branches
+ * (see rcs.c) are contained as virtual revision numbers (within symbolic
+ * tags only) off the RCS_MAGIC_BRANCH, which is 0. CVS also reserves the
+ * ".1" branch for vendor revisions. So, if you do your own branching, you
+ * should limit your use to odd branch numbers starting at 3.
+ */
+#define RCS_MAGIC_BRANCH 0
+
+/* The type of a function passed to RCS_checkout. */
+typedef void (*RCSCHECKOUTPROC) (void *, const char *, size_t);
+
+struct rcsbuffer;
+
+/* What RCS_deltas is supposed to do. */
+enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH};
+
+/*
+ * exported interfaces
+ */
+RCSNode *RCS_parse (const char *file, const char *repos);
+RCSNode *RCS_parsercsfile (const char *rcsfile);
+void RCS_fully_parse (RCSNode *);
+void RCS_reparsercsfile (RCSNode *, FILE **, struct rcsbuffer *);
+extern int RCS_setattic (RCSNode *, int);
+
+char *RCS_check_kflag (const char *arg);
+char *RCS_getdate (RCSNode * rcs, const char *date, int force_tag_match);
+char *RCS_gettag (RCSNode * rcs, const char *symtag, int force_tag_match,
+ int *simple_tag);
+int RCS_exist_rev (RCSNode *rcs, char *rev);
+int RCS_exist_tag (RCSNode *rcs, char *tag);
+char *RCS_tag2rev (RCSNode *rcs, char *tag);
+char *RCS_getversion (RCSNode *rcs, const char *tag, const char *date,
+ int force_tag_match, int *simple_tag);
+char *RCS_magicrev (RCSNode *rcs, char *rev);
+int RCS_isbranch (RCSNode *rcs, const char *rev);
+bool RCS_nodeisbranch (RCSNode *rcs, const char *tag);
+char *RCS_whatbranch (RCSNode *rcs, const char *tag);
+char *RCS_head (RCSNode * rcs);
+int RCS_datecmp (const char *date1, const char *date2);
+time_t RCS_getrevtime (RCSNode * rcs, const char *rev, char *date, int fudge);
+List *RCS_symbols (RCSNode *rcs);
+void RCS_check_tag (const char *tag);
+char *RCS_extract_tag (const char *tag, bool files);
+int RCS_valid_rev (const char *rev);
+List *RCS_getlocks (RCSNode *rcs);
+void freercsnode (RCSNode ** rnodep);
+char *RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match);
+char *RCS_branch_head (RCSNode *rcs, const char *tag);
+
+int RCS_isdead (RCSNode *, const char *);
+char *RCS_getexpand (RCSNode *);
+void RCS_setexpand (RCSNode *, const char *);
+int RCS_checkout (RCSNode *, const char *, const char *, const char *,
+ const char *, const char *, RCSCHECKOUTPROC, void *);
+int RCS_checkin (RCSNode *rcs, const char *update_dir, const char *workfile,
+ const char *message, const char *rev, time_t citime,
+ int flags);
+int RCS_cmp_file (RCSNode *, const char *, char **, const char *, const char *,
+ const char * );
+int RCS_settag (RCSNode *, const char *, const char *);
+int RCS_deltag (RCSNode *, const char *);
+int RCS_setbranch (RCSNode *, const char *);
+int RCS_lock (RCSNode *, const char *, int);
+int RCS_unlock (RCSNode *, char *, int);
+int RCS_delete_revs (RCSNode *, char *, char *, int);
+void RCS_addaccess (RCSNode *, char *);
+void RCS_delaccess (RCSNode *, char *);
+char *RCS_getaccess (RCSNode *);
+void RCS_rewrite (RCSNode *, Deltatext *, char *);
+void RCS_abandon (RCSNode *);
+int rcs_change_text (const char *, char *, size_t, const char *,
+ size_t, char **, size_t *);
+void RCS_deltas (RCSNode *, FILE *, struct rcsbuffer *, const char *,
+ enum rcs_delta_op, char **, size_t *,
+ char **, size_t *);
+void RCS_setincexc (void **, const char *arg);
+void RCS_setlocalid (const char *, unsigned int, void **, const char *arg);
+char *make_file_label (const char *, const char *, RCSNode *);
+bool RCS_is_symbolic (const char *);
+bool RCS_is_relative (const char *);
+
+extern bool preserve_perms;
+extern int annotate_width;
+
+/* From import.c. */
+extern int add_rcs_file (const char *, const char *, const char *,
+ const char *, const char *, const char *,
+ const char *, int, char **, const char *, size_t,
+ FILE *, bool);
+void free_keywords (void *keywords);
Index: ccvs/src/sanity.sh
diff -u /dev/null ccvs/src/sanity.sh:1.1108.2.1
--- /dev/null Tue Jan 17 15:41:24 2006
+++ ccvs/src/sanity.sh Tue Jan 17 15:41:23 2006
@@ -0,0 +1,36907 @@
+#! /bin/sh
+:
+# sanity.sh -- a growing testsuite for cvs.
+#
+# The copyright notice said: "Copyright (C) 1992, 1993 Cygnus Support"
+# I'm not adding new copyright notices for new years as our recent
+# practice has been to include copying terms without copyright notices.
+#
+# 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.
+#
+# Original Author: K. Richard Pixley
+
+# usage:
+usage ()
+{
+ echo "Usage: `basename $0` --help"
+ echo "Usage: `basename $0` [--eklr] [-c CONFIG-FILE] [-f FROM-TEST] \\"
+ echo " [-h HOSTNAME] [-s CVS-FOR-CVS-SERVER] CVS-TO-TEST
\\"
+ echo " [TESTS-TO-RUN...]"
+}
+
+exit_usage ()
+{
+ usage 1>&2
+ exit 2
+}
+
+exit_help ()
+{
+ usage
+ echo
+ echo "-H|--help display this text"
+ echo "-c CONFIG-FILE"
+ echo "--config=CONFIG_FILE"
+ echo " use an alternate test suite config file (defaults to"
+ echo " \`sanity.config.sh' in the same directory as"
+ echo " CVS-TO-TEST is found in)"
+ echo "-e|--skipfail Treat tests that would otherwise be nonfatally skipped"
+ echo " for reasons like missing tools as failures, exiting"
+ echo " with an error message. Also treat warnings as"
+ echo " failures."
+ echo "-f FROM-TEST"
+ echo "--from-test=FROM-TEST"
+ echo " run TESTS-TO-RUN, skipping all tests in the list before"
+ echo " FROM-TEST"
+ echo "-h HOSTNAME"
+ echo "--hostname HOSTNAME"
+ echo " Use :ext:HOSTNAME to run remote tests rather than"
+ echo " :fork:. Implies --remote and assumes that \$TESTDIR"
+ echo " resolves to the same directory on both the client and"
+ echo " the server."
+ echo "-k|--keep try to keep directories created by individual tests"
+ echo " around, exiting after the first test which supports"
+ echo " --keep"
+ echo "-l|--link-root"
+ echo " test CVS using a symlink to a real CVSROOT"
+ echo "-n|--noredirect"
+ echo " test a secondary/primary CVS server (writeproxy)"
+ echo " configuration with the Redirect response disabled"
+ echo " (implies --proxy)."
+ echo "-p|--proxy test a secondary/primary CVS server (writeproxy)"
+ echo " configuration (implies --remote)."
+ echo "-r|--remote test client/server, as opposed to local, CVS"
+ echo "-s CVS-FOR-CVS-SERVER"
+ echo "--server=CVS-FOR-CVS-SERVER"
+ echo " use CVS-FOR-CVS-SERVER as the path to the CVS SERVER"
+ echo " executable to be tested (defaults to CVS-TO-TEST and"
+ echo " implies --remote)"
+ echo
+ echo "CVS-TO-TEST the path to the CVS executable to be tested; used as"
+ echo " the path to the CVS client when CVS-FOR-CVS-SERVER is"
+ echo " specified"
+ echo "TESTS-TO-RUN the names of the tests to run (defaults to all tests)"
+ exit 2
+}
+
+checklongoptarg()
+{
+ if test "x$1" != xoptional && test -z "$OPTARG"; then
+ echo "option \`--$LONGOPT' requires an argument" >&2
+ exit_usage
+ fi
+}
+
+# See TODO list at end of file.
+
+# required to make this script work properly.
+unset CVSREAD
+
+# We want to invoke a predictable set of i18n behaviors, not whatever
+# the user running this script might have set.
+# In particular:
+# 'sort' and tabs and spaces (LC_COLLATE).
+# Messages from getopt (LC_MESSAGES) (in the future, CVS itself might
+# also alter its messages based on LC_MESSAGES).
+LANG=C
+export LANG
+LC_ALL=C
+export LC_ALL
+
+# And a few tests want a predictable umask.
+umask 0002
+
+#
+# Initialize the test counts.
+#
+passed=0
+skipped=0
+warnings=0
+
+
+
+#
+# read our options
+#
+unset configfile
+unset fromtest
+unset remotehost
+unset rootoptions
+keep=false
+linkroot=false
+noredirect=false
+proxy=false
+remote=false
+servercvs=false
+skipfail=false
+while getopts Hc:ef:h:klnprs:-: option ; do
+ # convert the long opts to short opts
+ if test x$option = x-; then
+ # remove any argument
+ if echo "$OPTARG" |grep = >/dev/null; then
+ LONGOPT=`echo "$OPTARG" |sed 's/=.*$//'`
+ OPTARG=`echo "$OPTARG" |sed -e 's/^.*=//'`
+ else
+ LONGOPT=$OPTARG
+ OPTARG=
+ fi
+ # Convert LONGOPT to lower case
+ LONGOPT=`echo "$LONGOPT" |sed
'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ case "$LONGOPT" in
+ c|co|con|conf|confi|config)
+ option=c
+ checklongoptarg
+ ;;
+ f|fr|fro|from|from-|from-t|from-te|from-tes|from-test)
+ option=f
+ checklongoptarg
+ ;;
+ h)
+ echo "\`--h' is ambiguous. Could mean \`--help' or
\`--hostname'" >&2
+ exit_usage
+ ;;
+ he|hel|help)
+ option=H
+ OPTARG=
+ ;;
+ ho|hos|host|hostn|hostna|hostnam|hostname)
+ option=h
+ checklongoptarg
+ ;;
+ k|ke|kee|keep)
+ option=k
+ OPTARG=
+ ;;
+ l|li|lin|link|link-|link-r]|link-ro|link-roo|link-root)
+ option=l
+ OPTARG=
+ ;;
+ n|no|nor|nore|nored|noredi|noredir|noredire|noredirec|noredirect)
+ option=n
+ OPTARG=
+ ;;
+ p|pr|pro|prox|proxy)
+ option=p
+ OPTARG=
+ ;;
+ r|re|rem|remo|remot|remote)
+ option=r
+ OPTARG=
+ ;;
+ s)
+ echo "\`--s' is ambiguous. Could mean \`--server' or
\`--skipfail'" >&2
+ exit_usage
+ ;;
+ se|ser|serv|serve|server)
+ option=s
+ checklongoptarg
+ ;;
+ sk|ski|skip|skipf|skipfa|skipfai|skipfail)
+ option=e
+ OPTARG=
+ ;;
+ *)
+ option=\?
+ OPTARG=
+ esac
+ fi
+ case "$option" in
+ c)
+ configfile="$OPTARG"
+ ;;
+ e)
+ skipfail=:
+ ;;
+ f)
+ fromtest="$OPTARG"
+ ;;
+ h)
+ # Set a remotehost to run the remote tests on via :ext:
+ # Implies `-r' and assumes that $TESTDIR resolves to the same
+ # directory on the client and the server.
+ remotehost="$OPTARG"
+ remote=:
+ ;;
+ H)
+ exit_help
+ ;;
+ k)
+ # The -k (keep) option will eventually cause all the tests to
+ # leave around the contents of the /tmp directory; right now only
+ # some implement it. Not originally intended to be useful with
+ # more than one test, but this should work if each test uses a
+ # uniquely named dir (use the name of the test).
+ keep=:
+ ;;
+ l)
+ linkroot=:
+ ;;
+ n)
+ proxy=:
+ noredirect=:
+ remote=:
+ ;;
+ p)
+ proxy=:
+ remote=:
+ ;;
+ r)
+ remote=:
+ ;;
+ s)
+ servercvs="$OPTARG"
+ remote=:
+ ;;
+ \?)
+ exit_usage
+ ;;
+ esac
+done
+
+# boot the arguments we used above
+while test $OPTIND -gt 1 ; do
+ shift
+ OPTIND=`expr $OPTIND - 1`
+done
+
+# Use full path for CVS executable, so that CVS_SERVER gets set properly
+# for remote.
+case $1 in
+"")
+ exit_usage
+ ;;
+/*)
+ testcvs=$1
+ ;;
+*)
+ testcvs=`pwd`/$1
+ ;;
+esac
+shift
+
+# Verify that $testcvs looks like CVS.
+# we can't use test -x since BSD 4.3 doesn't support it.
+if test ! -f $testcvs || test ! -r $testcvs; then
+ echo "No such file or file not readable: $testcvs" >&2
+ exit 1
+fi
+if $testcvs --version </dev/null 2>/dev/null |
+ grep '^Concurrent Versions System' >/dev/null 2>&1; then :; else
+ echo "Not a CVS executable: $testcvs" >&2
+ exit 1
+fi
+
+# If $remotehost is set, warn if $TESTDIR isn't since we are pretty sure
+# that its default value of `/tmp/cvs-sanity' will not resolve to the same
+# directory on two different machines.
+if test -n "$remotehost" && test -z "$TESTDIR"; then
+ echo "WARNING: CVS server hostname is set and \$TESTDIR is not. If" >&2
+ echo "$remotehost is not the local machine, then it is unlikely that" >&2
+ echo "the default value assigned to \$TESTDIR will resolve to the same" >&2
+ echo "directory on both this client and the CVS server." >&2
+fi
+
+# Read our config file if we can find it.
+#
+# The config file should always be located in the same directory as the CVS
+# executable, unless we are testing an executable outside of the build
+# directory. In this case, we echo a warning and attempt to assume the most
+# portable configuration.
+if test -z "$configfile"; then
+ configfile=`dirname $testcvs`/sanity.config.sh
+fi
+if test -r "$configfile"; then
+ . "$configfile"
+else
+ echo "WARNING: Failed to locate test suite config file" >&2
+ echo " \`$configfile'." >&2
+fi
+
+
+
+# Set a default value for $CVS_RSH. The sanity.config.sh file will
+# have the configured value in the RSH_DFLT variable.
+#
+: ${CVS_RSH=${RSH_DFLT:-ssh}}; export CVS_RSH
+
+if test -n "$remotehost"; then
+ # Verify that $CVS_RSH $remotehost works.
+ result=`$CVS_RSH $remotehost 'echo test'`
+ if test $? != 0 || test "x$result" != "xtest"; then
+ echo "\`$CVS_RSH $remotehost' failed." >&2
+ exit 1
+ fi
+fi
+
+case "$servercvs" in
+"")
+ exit_usage
+ ;;
+false)
+ ;;
+/*)
+ ;;
+*)
+ servercvs=`pwd`/$servercvs
+ ;;
+esac
+
+if test false != $servercvs; then
+ # Allow command line to override $CVS_SERVER
+ CVS_SERVER=$servercvs
+else
+ # default $CVS_SERVER to ${testcvs}
+ : ${CVS_SERVER=$testcvs}
+ # With the previous command, effectively defaults $servercvs to $CVS_SERVER,
+ # then $testcvs
+ servercvs=$CVS_SERVER
+fi
+export CVS_SERVER
+servercvs_orig=$servercvs
+
+# Fail in client/server mode if our ${servercvs} does not contain server
+# support.
+if $remote; then
+ if test -n "$remotehost"; then
+ if $CVS_RSH $remotehost "test ! -f ${servercvs} || test ! -r ${servercvs}"
+ then
+ echo "No such file or file not readable: $remotehost:${testcvs}" >&2
+ exit 1
+ fi
+ if $CVS_RSH $remotehost "${servercvs} --version </dev/null 2>/dev/null |
+ grep '^Concurrent Versions System' >/dev/null 2>&1"; then :; else
+ echo "Not a CVS executable: $remotehost:${servercvs}" >&2
+ exit 1
+ fi
+ if $CVS_RSH $remotehost "${servercvs} --version </dev/null |
+ grep '^Concurrent.*(.*server)$' >/dev/null 2>&1"; then :; else
+ echo "CVS executable \`$remotehost:${servercvs}' does not contain server
support." >&2
+ exit 1
+ fi
+ else
+ if test ! -f ${servercvs} || test ! -r ${servercvs}; then
+ echo "No such file or file not readable: ${testcvs}" >&2
+ exit 1
+ fi
+ if ${servercvs} --version </dev/null 2>/dev/null |
+ grep '^Concurrent Versions System' >/dev/null 2>&1; then :; else
+ echo "Not a CVS executable: ${servercvs}" >&2
+ exit 1
+ fi
+ if ${servercvs} --version </dev/null |
+ grep '^Concurrent.*(.*server)$' >/dev/null 2>&1; then :; else
+ echo "CVS executable \`${servercvs}' does not contain server support."
>&2
+ exit 1
+ fi
+ fi
+fi
+
+# Fail in client/server mode if our ${testcvs} does not contain client
+# support.
+if $remote; then
+ if ${testcvs} --version </dev/null |
+ grep '^Concurrent.*(client.*)$' >/dev/null 2>&1; then :; else
+ echo "CVS executable \`${testcvs}' does not contain client support." >&2
+ exit 1
+ fi
+fi
+
+# For the "fork" tests.
+if ${testcvs} --version </dev/null |
+ grep '^Concurrent.*(.*server)$' >/dev/null 2>&1
+then
+ testcvs_server_support=:
+else
+ testcvs_server_support=false
+fi
+
+
+
+dokeep()
+{
+ if ${keep}; then
+ echo "Keeping ${TESTDIR} for test case \`${what}' and exiting due to
--keep"
+ exit 0
+ fi
+}
+
+
+
+###
+### GUTS
+###
+
+# "debugger"
+#set -x
+
+echo 'This test should produce no other output than this message, and a final
"OK".'
+echo '(Note that the test can take an hour or more to run and periodically
stops'
+echo 'for as long as one minute. Do not assume there is a problem just
because'
+echo 'nothing seems to happen for a long time. If you cannot live without'
+echo "running status, try the command: \`tail -f check.log' from another
window.)"
+
+# Regexp to match what the CVS client will call itself in output that it
prints.
+# FIXME: we don't properly quote this--if the name contains . we'll
+# just spuriously match a few things; if the name contains other regexp
+# special characters we are probably in big trouble.
+CPROG=`basename ${testcvs} |sed 's/\.exe$//'`
+# And the regexp for the CVS server when we have one. In local mode, this
+# defaults to $CPROG since $servercvs already did.
+# FIXCVS: There are a few places in error messages where CVS suggests a command
+# and outputs $SPROG as the suggested executable. This could hopefully use
+# MT (tagged text - see doc/cvs-client.texi) to request that the client print
+# its own name.
+SPROG=`basename ${servercvs} |sed 's/\.exe$//'`
+
+
+# Match the hostname
+hostname="[-_.a-zA-Z0-9]*"
+
+# Regexp to match a commitid
+commitid="[a-zA-Z0-9]*"
+
+# Regexp to match the name of a temporary file (from cvs_temp_name).
+# This appears in certain diff output.
+tempfile="cvs[-a-zA-Z0-9.%_]*"
+# $tempname set after $TMPDIR, below.
+
+# Regexp to match a date in RFC822 format (as amended by RFC1123).
+RFCDATE="[a-zA-Z0-9 ][a-zA-Z0-9 ]* [0-9:][0-9:]* -0000"
+RFCDATE_EPOCH="1 Jan 1970 00:00:00 -0000"
+
+# Special times used in touch -t commands and the regular expresions
+# to match them. Now that the tests set TZ=UTC0, it
+# should be easier to be more exact in their regexp.
+TOUCH1971="197107040343"
+# This date regexp was 1971/07/0[3-5] [0-9][0-9]:43:[0-9][0-9]
+ISO8601DATE1971="1971-07-04 03:43:[0-9][0-9] [+-]0000"
+
+TOUCH2034="203412251801"
+# This date regexp was 2034/12/2[4-6] [0-9][0-9]:01:[0-9][0-9]
+ISO8601DATE2034="2034-12-25 18:01:[0-9][0-9] [+-]0000"
+
+# Used in admin tests for exporting RCS files.
+# The RAWRCSDATE..... format is for internal ,v files and
+# the ISO8601DATE..... format is to allow for a regular expression in
+# 'cvs log' output patterns. The tests that use this set of specific
+# ${ISO8601DATE.....} variables also force TZ=UTC0 for the test.
+RAWRCSDATE2000A="2000.11.24.15.58.37"
+RAWRCSDATE1996A="96.11.24.15.57.41"
+RAWRCSDATE1996B="96.11.24.15.56.05"
+ISO8601DATE2000A="2000-11-24 15:58:37 [+-]0000"
+ISO8601DATE1996A="1996-11-24 15:57:41 [+-]0000"
+ISO8601DATE1996B="1996-11-24 15:56:05 [+-]0000"
+
+# Regexp to match the date in cvs log command output
+# This format has been enhanced in the future to accept either
+# old-style cvs log output dates or new-style ISO8601 timezone
+# information similar to the ISODATE format. The RCSKEYDATE is
+# similar, but uses '/' instead of '-' to sepearate year/month/day
+# and does not include the optional timezone offset.
+ISO8601DATE="[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]
[0-2][0-9]:[0-6][0-9]:[0-6][0-9] [-+][0-1][0-9][0-6][0-9]"
+
+# Regexp to match the dates found in rcs keyword strings
+RCSKEYDATE="[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]"
+
+# Regexp to match the date in the delta section of rcs format files.
+# Dates in very old RCS files may not have included the century.
+RCSDELTADATE="[0-9][0-9]*\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]"
+
+# Regexp to match a date in standard Unix format as used by rdiff
+# FIXCVS: There's no reason for rdiff to use a different date format
+# than diff does
+DATE="[a-zA-Z]* [a-zA-Z]* [ 1-3][0-9] [0-9:]* [0-9]*"
+# ISO 8601 format "yyyy-mm-dd hh:mm -0000"
+ISODATE="[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][0-9][0-9]"
+# %p format is not well defined (nil) and hex digits are common. Using
+# ..* is a bad idea as the tests take a very long time to run due to
+# the complexity of the expressions. If you run into any other characters
+# that are used in a %p format, add them here.
+PFMT="[0-9a-zA-Z()][0-9a-zA-Z()]*"
+
+# Which directories should Which and find_tool search for executables?
+SEARCHPATH=$PATH:/usr/local/bin:/usr/contrib/bin:/usr/contrib:/usr/gnu/bin:/local/bin:/local/gnu/bin:/gnu/bin:/sw/bin:/usr/pkg/bin
+
+# Do not assume that `type -p cmd` is portable
+# Usage: Which [-a] [-x|-f|-r] prog [$SEARCHPATH:/with/directories:/to/search]
+Which() {
+ # Optional first argument for file type, defaults to -x.
+ # Second argument is the file or directory to be found.
+ # Third argument is the PATH to search.
+ # By default, print only the first file that matches,
+ # -a will cause all matches to be printed.
+ notevery=:
+ if [ "x$1" = "x-a" ]; then notevery=false; shift; fi
+ case "$1" in
+ -*) t=$1; shift ;;
+ *) t=-x ;;
+ esac
+ case "$1" in
+ # FIXME: Someday this may need to be fixed
+ # to deal better with C:\some\path\to\ssh values...
+ /*) test $t $1 && echo $1 ;;
+ *) for d in `IFS=:; echo ${2-$SEARCHPATH}`
+ do
+ test $t $d/$1 && { echo $d/$1; if $notevery; then break; fi; }
+ done
+ ;;
+ esac
+}
+
+
+# On cygwin32, we may not have /bin/sh.
+if test -r /bin/sh; then
+ TESTSHELL="/bin/sh"
+else
+ TESTSHELL=`Which -f sh`
+ if test ! -r "$TESTSHELL"; then
+ TESTSHELL="/bin/sh"
+ fi
+fi
+
+# FIXME: try things (what things? checkins?) without -m.
+#
+# Some of these tests are written to expect -Q. But testing with
+# -Q is kind of bogus, it is not the way users actually use CVS (usually).
+# So new tests probably should invoke ${testcvs} directly, rather than ${CVS}.
+# and then they've obviously got to do something with the output....
+#
+CVS="${testcvs} -Q"
+
+LOGFILE=`pwd`/check.log
+
+# Save the previous log in case the person running the tests decides
+# they want to look at it. The extension ".plog" is chosen for consistency
+# with dejagnu.
+test -f check.plog && mv check.plog check.plog~
+test -f check.log && mv check.log check.plog
+
+# Create the log file so check.log can be tailed almost immediately after
+# this script is started. Otherwise it can take up to a minute or two before
+# the log file gets created when $remotehost is specified on some systems,
+# which makes for a lot of failed `tail -f' attempts.
+touch check.log
+
+# Workaround any X11Forwarding by ssh. Otherwise this text:
+# Warning: No xauth data; using fake authentication data for X11 forwarding.
+# has been known to end up in the test results below
+# causing the test to fail.
+[ -n "$DISPLAY" ] && unset DISPLAY
+
+# The default value of /tmp/cvs-sanity for TESTDIR is dubious,
+# because it loses if two people/scripts try to run the tests
+# at the same time. Some possible solutions:
+# 1. Use /tmp/cvs-test$$. One disadvantage is that the old
+# cvs-test* directories would pile up, because they wouldn't
+# necessarily get removed.
+# 2. Have everyone/everything running the testsuite set
+# TESTDIR to some appropriate directory.
+# 3. Have the default value of TESTDIR be some variation of
+# `pwd`/cvs-sanity. The biggest problem here is that we have
+# been fairly careful to test that CVS prints in messages the
+# actual pathnames that we pass to it, rather than a different
+# pathname for the same directory, as may come out of `pwd`.
+# So this would be lost if everything was `pwd`-based. I suppose
+# if we wanted to get baroque we could start making symlinks
+# to ensure the two are different.
+if test -n "$remotehost"; then
+ # We need to set $tmp on the server since $TMPDIR is compared against
+ # messages generated by the server.
+ tmp=`$CVS_RSH $remotehost 'cd /tmp; /bin/pwd || pwd' 2>/dev/null`
+ if test $? != 0; then
+ echo "$CVS_RSH $remotehost failed." >&2
+ exit 1
+ fi
+else
+ tmp=`(cd /tmp; /bin/pwd || pwd) 2>/dev/null`
+fi
+
+# Now:
+# 1) Set TESTDIR if it's not set already
+# 2) Remove any old test remnants
+# 3) Create $TESTDIR
+# 4) Normalize TESTDIR with `cd && (/bin/pwd || pwd)`
+# (This will match CVS output later)
+: ${TESTDIR=$tmp/cvs-sanity}
+# clean any old remnants (we need the chmod because some tests make
+# directories read-only)
+if test -d $TESTDIR; then
+ chmod -R a+wx $TESTDIR
+ rm -rf $TESTDIR
+fi
+# These exits are important. The first time I tried this, if the `mkdir && cd`
+# failed then the build directory would get blown away. Some people probably
+# wouldn't appreciate that.
+mkdir $TESTDIR || exit 1
+cd $TESTDIR || exit 1
+# Ensure $TESTDIR is absolute
+if echo "$TESTDIR" |grep '^[^/]'; then
+ # Don't resolve this unless we have to. This keeps symlinks intact. This
+ # is important at least when testing using -h $remotehost, because the same
+ # value for $TESTDIR must resolve to the same directory on the client and
+ # the server and we likely used Samba, and possibly symlinks, to do this.
+ TESTDIR=`(/bin/pwd || pwd) 2>/dev/null`
+fi
+
+if test -z "$TESTDIR" || echo "$TESTDIR" |grep '^[^/]'; then
+ echo "Unable to resolve TESTDIR to an absolute directory." >&2
+ exit 1
+fi
+cd $TESTDIR
+
+
+
+: ${TIMING=false}
+if $remote; then
+ # Now override our CVS_RSH in order to forward variables which affect the
+ # test suite through. This always needs to be done when $remotehost is
+ # set, needs to be done in $proxy mode for the crerepos tests, and needs to
+ # be done in $remote mode for the writeproxy-ssh tests.
+ if $TIMING; then
+ time="/usr/bin/time -ao'$TESTDIR/time.out'"
+ else
+ time=
+ fi
+ cat >$TESTDIR/ssh-wrapper-env <<EOF
+#! $TESTSHELL
+while [ \$# -gt 0 ]
+do
+ case "\$1" in
+ *=*)
+ eval "\$1"
+ var=\`echo "\$1" | sed 's/^\\(.*\\)=.*\$/\\1/'\`
+ export \$var
+ ;;
+ *) break;;
+ esac
+ shift
+done
+exec \${1+"\$@"}
+EOF
+ chmod a+x $TESTDIR/ssh-wrapper-env
+ cat >$TESTDIR/ssh-wrapper <<EOF
+#! $TESTSHELL
+hostname=\$1
+shift
+exec \
+$CVS_RSH \
+ \$hostname \
+ $TESTDIR/ssh-wrapper-env \
+ "CVS_SERVER='\$CVS_SERVER'" \
+ "CVS_SERVER_SLEEP='\$CVS_SERVER_SLEEP'" \
+ "CVS_PARENT_SERVER_SLEEP='\$CVS_PARENT_SERVER_SLEEP'" \
+ "CVS_SERVER_LOG='\$CVS_SERVER_LOG'" \
+ "CVS_SECONDARY_LOG='\$CVS_SECONDARY_LOG'" \
+ "TMPDIR='\$TMPDIR'" \
+ "CVS_RSH='$TESTDIR/ssh-wrapper'" \
+ "CVSUMASK='\$CVSUMASK'" \
+ "CVS_PID='\$CVS_PID'" \
+ $time \
+ \${1+"\$@"}
+EOF
+ chmod a+x $TESTDIR/ssh-wrapper
+ CVS_RSH=$TESTDIR/ssh-wrapper
+fi # $remotehost
+
+
+
+# Now set $TMPDIR if the user hasn't overridden it.
+#
+# We use a $TMPDIR under $TESTDIR by default so that two tests may be run at
+# the same time without bumping heads without requiring the user to specify
+# more than $TESTDIR. See the test for leftover cvs-serv* directories near the
+# end of this script at the end of "The big loop".
+: ${TMPDIR=$TESTDIR/tmp}
+export TMPDIR
+if test -d $TMPDIR; then :; else
+ mkdir $TMPDIR
+fi
+
+
+# Regexp to match the the full path to a temporary file (from cvs_temp_name).
+# This appears in certain diff output.
+tempname=$TMPDIR/$tempfile
+
+# Make sure various tools work the way we expect, or try to find
+# versions that do.
+: ${AWK=awk}
+: ${DIFF=diff}
+: ${EXPR=expr}
+: ${ID=id}
+: ${TR=tr}
+
+# Keep track of tools that are found, but do NOT work as we hope
+# in order to avoid them in future
+badtools=
+set_bad_tool ()
+{
+ badtools=$badtools:$1
+}
+is_bad_tool ()
+{
+ case ":$badtools:" in *:$1:*) return 0 ;; *) return 1 ; esac
+}
+
+version_test ()
+{
+ vercmd=$1
+ verbad=:
+ if RES=`$vercmd --version </dev/null 2>&1`; then
+ if test "X$RES" != "X--version" && test "X$RES" != "X" ; then
+ echo "$RES"
+ verbad=false
+ fi
+ fi
+ if $verbad; then
+ echo "The command \`$vercmd' does not support the --version option."
+ fi
+ # It does not really matter that --version is not supported
+ return 0
+}
+
+# Try to find a tool that satisfies all of the tests.
+# Usage: list:of:colon:separated:alternatives test1 test2 test3 test4...
+# Example: find_tool awk:gawk:nawk awk_tooltest1 awk_tooltest2
+find_tool ()
+{
+ dTn=$1
+ default_TOOL=$2
+ echo find_tool: ${1+"$@"} >>$LOGFILE
+ cmds="`IFS=:; echo $2`"; shift; shift; tooltests="address@hidden"
+ if test -z "$tooltests"; then tooltests=version_test; fi
+ clist=; for cmd in $cmds; do clist="$clist `Which -a $cmd`"; done
+ # Make sure the default tool is just the first real command name
+ for default_TOOL in $clist `IFS=:; echo $default_TOOL`; do break; done
+ TOOL=""
+ TEST_MARGINALS=0
+ for trytool in $clist ; do
+ pass=:
+ MARGINALS=0
+ for tooltest in $tooltests; do
+ result=`eval $tooltest $trytool 2>&1`
+ rc=$?
+ echo "Running $tooltest $trytool" >>$LOGFILE
+ if test -n "$result"; then
+ echo "$result" >>$LOGFILE
+ fi
+ if test "$rc" = "0"; then
+ echo "PASS: $tooltest $trytool" >>$LOGFILE
+ elif test "$rc" = "77"; then
+ echo "MARGINAL: $tooltest $trytool; rc=$rc" >>$LOGFILE
+ MARGINALS=`expr $MARGINALS + 1`
+ pass=false
+ else
+ set_bad_tool $trytool
+ echo "FAIL: $tooltest $trytool; rc=$rc" >>$LOGFILE
+ pass=false
+ fi
+ done
+ if $pass; then
+ echo $trytool
+ return 0
+ fi
+ if test $MARGINALS -gt 0 \
+ && (test -z "$TOOL" || test $MARGINALS -lt $TEST_MARGINALS); then
+ if is_bad_tool $trytool; then
+ # Ignore tools with some MARGINAL results and some FAIL
+ :
+ else
+ TOOL=$trytool
+ TEST_MARGINALS=$MARGINALS
+ fi
+ fi
+ done
+ if test -n "$TOOL"; then
+ echo "Notice: The default version of $dTn (\`$default_TOOL')" >>$LOGFILE
+ echo "is defective. Using \`$TOOL' and hoping for the best." >>$LOGFILE
+ echo "Notice: The default version of $dTn (\`$default_TOOL')" >&2
+ echo "is defective. Using \`$TOOL' and hoping for the best." >&2
+ echo $TOOL
+ else
+ echo $default_TOOL
+ fi
+}
+
+id_tool_test ()
+{
+ id=$1
+ if $id -u >/dev/null 2>&1 && $id -un >/dev/null 2>&1; then
+ return 0
+ else
+ echo "Running these tests requires an \`id' program that understands the"
+ echo "-u and -n flags. Make sure that such an id (GNU, or many but not"
+ echo "all vendor-supplied versions) is in your path."
+ return 1
+ fi
+}
+
+ID=`find_tool id id version_test id_tool_test`
+echo "Using ID=$ID" >>$LOGFILE
+
+# You can't run CVS as root; print a nice error message here instead
+# of somewhere later, after making a mess.
+for pass in false :; do
+ case "`$ID -u 2>/dev/null`" in
+ "0")
+ echo "Test suite does not work correctly when run as root" >&2
+ exit 1
+ ;;
+
+ *)
+ break
+ ;;
+ esac
+done
+
+
+
+# Test if diff supports the -u option, falling back on -c, then no arguments.
+#
+# Set $diff_u to `$1 -u' if $1 -u works, `$1 -c' if not and $1 -c
+# works, and `$1' otherwise.
+#
+# $diff_u is intended to be used for tests expecting no differences, since the
+# non-matching output is going to vary depending on what version of diff is
+# found. Used in tests which expect no differences, output will always mean
+# errors and will make the error log more verbose, and correspondingly more
+# readable by a human, regardless of the source.
+diff_u_test()
+{
+ diff=$1
+ touch sanity.1 sanity.2
+ if output=`$diff -u sanity.1 sanity.2 2>&1` && test -z "$output"; then
+ diff_u="$diff -u"
+ retval=0
+ elif output=`$diff -c sanity.1 sanity.2 2>&1` && test -z "$output"; then
+ diff_u="$diff -c"
+ retval=77
+ elif output=`$diff sanity.1 sanity.2 2>&1` && test -z "$output"; then
+ echo "A diff that supports the -u or -c options and which does not output"
+ echo "text when files are identical can make the output of some of this"
+ echo "script's tests more readable on failure."
+ diff_u=$diff
+ retval=77
+ else
+ echo "This test suite requires either \`diff' or \`cmp' to run."
+ retval=1
+ fi
+ rm sanity.1 sanity.2
+ return $retval
+}
+
+
+
+# Test if diff supports the -u, --recursive, && --exclude options.
+diff_recursive_test()
+{
+ diff=$1
+ mkdir sanitydir.1
+ mkdir sanitydir.2
+ mkdir sanitydir.1/CVS
+ mkdir sanitydir.2/CVS
+ touch sanitydir.1/sanity.1 sanitydir.1/sanity.2 sanitydir.1/CVS/fileX \
+ sanitydir.2/sanity.1 sanitydir.2/sanity.2 sanitydir.2/CVS/fileY
+
+ if $diff -u --recursive --exclude=CVS sanitydir.1 sanitydir.2 \
+ >/dev/null 2>&1; then
+ retval=0
+ else
+ echo "GNU diff can make the output of some tests more readable."
+ retval=77
+ fi
+ rm -r sanitydir.1 sanitydir.2
+ return $retval
+}
+
+DIFF=`find_tool diff $DIFF:gdiff:cmp \
+ version_test diff_u_test diff_recursive_test`
+# Make sure $diff_u is set based on the tool find_tool returned.
+diff_u_test $DIFF
+
+
+
+# Cause NextStep 3.3 users to lose in a more graceful fashion.
+expr_tooltest1 ()
+{
+expr=$1
+if $expr 'abc
+def' : 'abc
+def' >/dev/null; then
+ # good, it works
+ return 0
+else
+ echo 'Running these tests requires an "expr" program that can handle'
+ echo 'multi-line patterns. Make sure that such an expr (GNU, or many but'
+ echo 'not all vendor-supplied versions) is in your path.'
+ return 1
+fi
+}
+
+# Warn SunOS, SysVr3.2, etc., users that they may be partially losing
+# if we can't find a GNU expr to ease their troubles...
+expr_tooltest2 ()
+{
+expr=$1
+if $expr 'a
+b' : 'a
+c' >/dev/null; then
+ echo 'Warning: you are using a version of expr that does not correctly'
+ echo 'match multi-line patterns. Some tests may spuriously pass or fail.'
+ echo 'You may wish to make sure GNU expr is in your path.'
+ return 1
+else
+ return 0
+fi
+}
+
+expr_create_bar ()
+{
+echo 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' >${TESTDIR}/foo
+cat ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo >${TESTDIR}/bar
+cat ${TESTDIR}/bar ${TESTDIR}/bar ${TESTDIR}/bar ${TESTDIR}/bar >${TESTDIR}/foo
+cat ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo ${TESTDIR}/foo >${TESTDIR}/bar
+rm -f ${TESTDIR}/foo
+}
+
+expr_tooltest3 ()
+{
+expr=$1
+# More SunOS lossage...
+test ! -f ${TESTDIR}/bar && expr_create_bar
+if $expr "`cat ${TESTDIR}/bar`" : "`cat ${TESTDIR}/bar`" >/dev/null; then
+ : good, it works
+else
+ echo 'Warning: you are using a version of expr that does not correctly'
+ echo 'match large patterns. Some tests may spuriously pass or fail.'
+ echo 'You may wish to make sure GNU expr is in your path.'
+ return 1
+fi
+if $expr "`cat ${TESTDIR}/bar`x" : "`cat ${TESTDIR}/bar`y" >/dev/null; then
+ echo 'Warning: you are using a version of expr that does not correctly'
+ echo 'match large patterns. Some tests may spuriously pass or fail.'
+ echo 'You may wish to make sure GNU expr is in your path.'
+ return 1
+fi
+# good, it works
+return 0
+}
+
+# That we should have to do this is total bogosity, but GNU expr
+# version 1.9.4-1.12 uses the emacs definition of "$" instead of the unix
+# (e.g. SunOS 4.1.3 expr) one. Rumor has it this will be fixed in the
+# next release of GNU expr after 1.12 (but we still have to cater to the old
+# ones for some time because they are in many linux distributions).
+ENDANCHOR="$"
+expr_set_ENDANCHOR ()
+{
+expr=$1
+ENDANCHOR="$"
+if $expr 'abc
+def' : 'abc$' >/dev/null; then
+ ENDANCHOR='\'\'
+ echo "Notice: An ENDANCHOR of dollar does not work."
+ echo "Using a workaround for GNU expr versions 1.9.4 thru 1.12"
+fi
+return 0
+}
+
+# Work around another GNU expr (version 1.10-1.12) bug/incompatibility.
+# "." doesn't appear to match a newline (it does with SunOS 4.1.3 expr).
+# Note that the workaround is not a complete equivalent of .* because
+# the first parenthesized expression in the regexp must match something
+# in order for expr to return a successful exit status.
+# Rumor has it this will be fixed in the
+# next release of GNU expr after 1.12 (but we still have to cater to the old
+# ones for some time because they are in many linux distributions).
+DOTSTAR='.*'
+expr_set_DOTSTAR ()
+{
+expr=$1
+DOTSTAR='.*'
+if $expr 'abc
+def' : "a${DOTSTAR}f" >/dev/null; then
+ : good, it works
+else
+ DOTSTAR='\(.\|
+\)*'
+ echo "Notice: DOTSTAR changed from sane \`.*' value to \`$DOTSTAR\`"
+ echo "to workaround GNU expr version 1.10 thru 1.12 bug where \`.'"
+ echo "does not match a newline."
+fi
+return 0
+}
+
+# Now that we have DOTSTAR, make sure it works with big matches
+expr_tooltest_DOTSTAR ()
+{
+expr=$1
+test ! -f ${TESTDIR}/bar && expr_create_bar
+if $expr "`cat ${TESTDIR}/bar`" : "${DOTSTAR}xyzABC${DOTSTAR}$" >/dev/null;
then
+ # good, it works
+ return 0
+else
+ echo 'Warning: you are using a version of expr that does not correctly'
+ echo 'match large patterns. Some tests may spuriously pass or fail.'
+ echo 'You may wish to make sure GNU expr is in your path.'
+ return 77
+fi
+}
+
+EXPR=`find_tool expr ${EXPR}:gexpr \
+ version_test expr_tooltest1 expr_tooltest2 expr_tooltest3 \
+expr_set_ENDANCHOR expr_set_DOTSTAR expr_tooltest_DOTSTAR`
+
+# Set the ENDANCHOR and DOTSTAR for the chosen expr version.
+expr_set_ENDANCHOR ${EXPR} >/dev/null
+expr_tooltest_DOTSTAR ${EXPR} >/dev/null
+
+echo "Using EXPR=$EXPR" >>$LOGFILE
+echo "Using ENDANCHOR=$ENDANCHOR" >>$LOGFILE
+echo "Using DOTSTAR=$DOTSTAR" >>$LOGFILE
+
+# Cleanup
+rm -f ${TESTDIR}/bar
+
+# Work around yet another GNU expr (version 1.10) bug/incompatibility.
+# "+" is a special character, yet for unix expr (e.g. SunOS 4.1.3)
+# it is not. I doubt that POSIX allows us to use \+ and assume it means
+# (non-special) +, so here is another workaround
+# Rumor has it this will be fixed in the
+# next release of GNU expr after 1.12 (but we still have to cater to the old
+# ones for some time because they are in many linux distributions).
+PLUS='+'
+if $EXPR 'a +b' : "a ${PLUS}b" >/dev/null; then
+ : good, it works
+else
+ PLUS='\+'
+fi
+
+# Likewise, for ?
+QUESTION='?'
+if $EXPR 'a?b' : "a${QUESTION}b" >/dev/null; then
+ : good, it works
+else
+ QUESTION='\?'
+fi
+
+# Now test the username to make sure it contains only valid characters
+username=`$ID -un`
+if $EXPR "${username}" : "${username}" >/dev/null; then
+ : good, it works
+else
+ echo "Test suite does not work correctly when run by a username" >&2
+ echo "containing regular expression meta-characters." >&2
+ exit 1
+fi
+
+# Also check for exactly the length of the username
+userlen=`echo $username | wc -c`
+userlen=`expr $userlen - 1`
+
+# Only 8 characters of $username appear in some output.
+if test `echo $username |wc -c` -gt 8; then
+ username8=`echo $username |sed 's/^\(........\).*/\1/'`
+else
+ username8=$username
+fi
+
+# Only 1 character of $username appear in some output.
+if test `echo $username |wc -c` -gt 1; then
+ username1=`echo $username |sed 's/^\(.\).*/\1/'`
+else
+ username1=$username
+fi
+
+# Rarely, we need to match any username, not just the name of the user
+# running this test. This variable usually shouldn't be used. $username
+# contains the name of the user actually running this test.
+#
+# I believe this only ever actually gets compared to usernames created by this
+# test. It used to be compared to the username of the user running this test,
+# but this hasn't been true for a long time. Regardless, I tried to get the
+# allowed character set right, based on a list in a private email from Mark
+# Baushke, basically the allowed names from Linux systems (plus `.', which is
+# only allowed on Gentoo Linux as of 2005-09-13).
+anyusername="[_a-zA-Z0-9][-_.$a-zA-Z0-9]*"
+
+# now make sure that tr works on NULs
+tr_tooltest1 ()
+{
+tr=$1
+if $EXPR `echo "123" | $tr '2' '\0'` : "123" >/dev/null 2>&1; then
+ echo 'Warning: you are using a version of tr which does not correctly'
+ echo 'handle NUL bytes. Some tests may spuriously pass or fail.'
+ echo 'You may wish to make sure GNU tr is in your path.'
+ return 77
+fi
+# good, it works
+return 0
+}
+
+TR=`find_tool tr ${TR}:gtr version_test tr_tooltest1`
+echo "Using TR=$TR" >>$LOGFILE
+
+# MacOS X (10.2.8) has a /bin/ls that does not work correctly in that
+# it will return true even if the wildcard argument does not match any
+# files.
+ls_tooltest ()
+{
+ls=$1
+# Force cleanup
+if test -d $TESTDIR/ls-test; then
+ chmod -R a+wx $TESTDIR/ls-test
+ rm -rf $TESTDIR/ls-test
+fi
+if $ls $TESTDIR/ls-test >/dev/null 2>&1; then
+ echo "Notice: \`$ls' is defective."
+ echo 'This is a version of ls which does not correctly'
+ echo 'return false for files that do not exist. Some tests may'
+ echo 'spuriously pass or fail.'
+ echo 'You may wish to put a an ls from GNU coreutils into your path.'
+ return 77
+else
+ return 0
+fi
+}
+LS=`find_tool ls ls:gls version_test ls_tooltest`
+echo "Using LS=$LS" >>$LOGFILE
+
+# Awk testing
+
+awk_tooltest1 ()
+{
+awk=$1
+$awk 'BEGIN {printf("one\ntwo\nthree\nfour\nfive\nsix")}' </dev/null >abc
+if $EXPR "`cat abc`" : \
+'one
+two
+three
+four
+five
+six'; then
+ rm abc
+ return 0
+else
+ rm abc
+ echo "Notice: awk BEGIN clause or printf is not be working properly."
+ return 1
+fi
+}
+
+# Format item %c check
+awk_tooltest2 ()
+{
+awk=$1
+$awk 'BEGIN { printf "%c%c%c", 2, 3, 4 }' </dev/null \
+ | ${TR} '\002\003\004' '123' >abc
+if $EXPR "`cat abc`" : "123" ; then
+ : good, found it
+else
+ echo "Notice: awk format %c string may not be working properly."
+ rm abc
+ return 77
+fi
+rm abc
+return 0
+}
+
+AWK=`find_tool awk gawk:nawk:awk version_test awk_tooltest1 awk_tooltest2`
+echo "Using AWK=$AWK" >>$LOGFILE
+
+
+###
+### Functions used by tests.
+###
+
+# Execute a command on the repository, syncing when done if necessary.
+#
+# Syntax is as `eval'.
+modify_repo ()
+{
+ eval "$*"
+ if $proxy; then
+ # And now resync the secondary.
+ $TESTDIR/sync-secondary "repo modification" modify_repo ALL "$@"
+ fi
+}
+
+# Restore changes to CVSROOT admin files.
+restore_adm ()
+{
+ modify_repo rm -rf $CVSROOT_DIRNAME/CVSROOT
+ modify_repo cp -Rp $TESTDIR/CVSROOT.save $CVSROOT_DIRNAME/CVSROOT
+}
+
+# Test that $RSYNC supports the options we need or try to find a
+# replacement. If $RSYNC works or we replace it, and return 0.
+# Otherwise, set $skipreason and return 77.
+require_rsync ()
+{
+ rsyncworks=false
+ # rsync is NOT a GNU tool, so do NOT use find_tool for name munging.
+ for rsync in ${RSYNC} `Which -a rsync`;
+ do
+
+ if is_bad_tool `Which $rsync` ; then continue ; fi
+ # Make some data to test rsync on.
+ mkdir $TESTDIR/rsync-test
+ mkdir $TESTDIR/rsync-test/Attic && touch $TESTDIR/rsync-test/Attic/6
+ mkdir $TESTDIR/rsync-test/otherdir && touch $TESTDIR/rsync-test/otherdir/7
+ for file in 1 2 3 4 5; do
+ touch $TESTDIR/rsync-test/$file
+ done
+
+ if test -f "$rsync" && test -r "$rsync" \
+ && $rsync -rglop --delete $TESTDIR/rsync-test/ $TESTDIR/rsync-test-copy \
+ >/dev/null 2>&1 \
+ && $rsync -rglop --delete --include Attic --exclude '*/' \
+ $TESTDIR/rsync-test/ $TESTDIR/rsync-test-copy2 \
+ >/dev/null 2>&1 \
+ && test -f $TESTDIR/rsync-test/5 \
+ && mv $TESTDIR/rsync-test/5 $TESTDIR/rsync-test/Attic/5 \
+ && test -f $TESTDIR/rsync-test-copy/Attic/6 \
+ && $rsync -rglop --delete $TESTDIR/rsync-test/ $TESTDIR/rsync-test-copy \
+ >/dev/null 2>&1 \
+ && $rsync -rglop --delete --include Attic --exclude '*/' \
+ $TESTDIR/rsync-test/ $TESTDIR/rsync-test-copy2 \
+ >/dev/null 2>&1 \
+ && test ! -f $TESTDIR/rsync-test-copy/5 \
+ && test ! -f $TESTDIR/rsync-test-copy2/5 \
+ && test -f $TESTDIR/rsync-test-copy2/Attic/5 \
+ && test ! -f $TESTDIR/rsync-test-copy2/otherdir/7
+ then
+ # good, it works
+ rsyncworks=:
+ RSYNC=$rsync
+ else
+ # Only use Which because of ${RSYNC} in the for loop.
+ set_bad_tool `Which $rsync`
+ fi
+
+ rm -rf $TESTDIR/rsync-test $TESTDIR/rsync-test-copy \
+ $TESTDIR/rsync-test-copy2
+
+ if $rsyncworks; then
+ return 0
+ else
+ (echo $rsync failed to work properly;\
+ echo "$rsync --version"; $rsync --version) >>$LOGFILE 2>&1
+ fi
+ done
+
+ unset RSYNC
+ skipreason="unusable or no rsync found"
+ return 77
+}
+
+# Test that $1 works as a remote shell. If so, set $host, $CVS_RSH, &
+# $save_CVS_RSH to match and return 0. Otherwise, set $skipreason and return
+# 77.
+require_rsh ()
+{
+ host=${remotehost-"`hostname`"}
+ result=`$1 $host 'echo test'`
+ rc=$?
+ if test $? != 0 || test "x$result" != "xtest"; then
+ skipreason="\`$1 $host' failed rc=$rc result=$result"
+ return 77
+ fi
+
+ save_CVS_RSH=$CVS_RSH
+ CVS_RSH=$1; export CVS_RSH
+ return 0
+}
+
+# Find a usable SSH. When a usable ssh is found, set $host, $CVS_RSH, and
+# $save_CVS_RSH and return 0. Otherwise, set $skipreason and return 77.
+require_ssh ()
+{
+ case "$CVS_RSH" in
+ *ssh*|*putty*)
+ tryssh=`Which $CVS_RSH`
+ if [ ! -n "$tryssh" ]; then
+ skipreason="Unable to find CVS_RSH=$CVS_RSH executable"
+ return 77
+ elif [ ! -x "$tryssh" ]; then
+ skipreason="Unable to execute $tryssh program"
+ return 77
+ fi
+ ;;
+ *)
+ # Look in the user's PATH for "ssh"
+ tryssh=`Which ssh`
+ if test ! -r "$tryssh"; then
+ skipreason="Unable to find ssh program"
+ return 77
+ fi
+ ;;
+ esac
+
+ require_rsh "$tryssh"
+ return $?
+}
+
+pass ()
+{
+ echo "PASS: $1" >>${LOGFILE}
+ passed=`expr $passed + 1`
+}
+
+# Like skip(), but don't fail when $skipfail is set.
+skip_always ()
+{
+ echo "SKIP: $1${2+ ($2)}" >>$LOGFILE
+ skipped=`expr $skipped + 1`
+}
+
+skip ()
+{
+ if $skipfail; then
+ # exits
+ fail "$1${2+ ($2)}"
+ fi
+
+ skip_always ${1+"$@"}
+}
+
+# Convenience function for skipping tests run only in remote mode.
+remoteonly ()
+{
+ skip_always $1 "only tested in remote mode"
+}
+
+# Convenience function for skipping tests not run in proxy mode.
+notproxy ()
+{
+ skip_always $1 "not tested in proxy mode"
+}
+
+# Convenience function for skipping tests not run in proxy mode.
+notnoredirect ()
+{
+ skip_always $1 "not tested in proxy-noredirect mode"
+}
+
+warn ()
+{
+ if $skipfail; then
+ fail "$1${2+ ($2)}"
+ else
+ echo "WARNING: $1${2+ ($2)}" >>$LOGFILE
+ fi
+ warnings=`expr $warnings + 1`
+}
+
+fail ()
+{
+ echo "FAIL: $1" | tee -a ${LOGFILE}
+ echo "*** Please see the \`TESTS' and \`check.log' files for more
information." >&2
+ # This way the tester can go and see what remnants were left
+ exit 1
+}
+
+verify_tmp_empty ()
+{
+ # Test our temp directory for cvs-serv* directories and cvsXXXXXX temp
+ # files. We would like to not leave any behind.
+ if $remote && $LS $TMPDIR/cvs-serv* >/dev/null 2>&1; then
+ # A true value means ls found files/directories with these names.
+ # Give the server some time to finish, then retry.
+ sleep 1
+ if $LS $TMPDIR/cvs-serv* >/dev/null 2>&1; then
+ warn "$1" "Found cvs-serv* directories in $TMPDIR."
+ # The above will exit if $skipfail
+ rm -rf $TMPDIR/cvs-serv*
+ fi
+ fi
+ if $LS $TMPDIR/cvs?????? >/dev/null 2>&1; then
+ # A true value means ls found files/directories with these names.
+ warn "$1" "Found cvsXXXXXX temp files in $TMPDIR."
+ # The above will exit if $skipfail
+ rm -f ls $TMPDIR/cvs??????
+ fi
+}
+
+# See dotest and dotest_fail for explanation (this is the parts
+# of the implementation common to the two).
+dotest_internal ()
+{
+ if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : "$3${ENDANCHOR}" >/dev/null; then
+ # Why, I hear you ask, do we write this to the logfile
+ # even when the test passes? The reason is that the test
+ # may give us the regexp which we were supposed to match,
+ # but sometimes it may be useful to look at the exact
+ # text which was output. For example, suppose one wants
+ # to grep for a particular warning, and make _sure_ that
+ # CVS never hits it (even in cases where the tests might
+ # match it with .*). Or suppose one wants to see the exact
+ # date format output in a certain case (where the test will
+ # surely use a somewhat non-specific pattern).
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ verify_tmp_empty "$1"
+ # expr can't distinguish between "zero characters matched" and "no match",
+ # so special-case it.
+ elif test -z "$3" && test ! -s ${TESTDIR}/dotest.tmp; then
+ pass "$1"
+ verify_tmp_empty "$1"
+ elif test x"$4" != x; then
+ if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : "$4${ENDANCHOR}" >/dev/null; then
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ verify_tmp_empty "$1"
+ else
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "$3" > ${TESTDIR}/dotest.ex1
+ echo "** or: " >>${LOGFILE}
+ echo "$4" >>${LOGFILE}
+ echo "$4" > ${TESTDIR}/dotest.ex2
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ fi
+ else
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "$3" > ${TESTDIR}/dotest.exp
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ fi
+}
+
+dotest_all_in_one ()
+{
+ if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \
+ "`cat ${TESTDIR}/dotest.exp`" >/dev/null; then
+ return 0
+ fi
+ return 1
+}
+
+# WARNING: this won't work with REs that match newlines....
+#
+dotest_line_by_line ()
+{
+ line=1
+ while [ $line -le `wc -l <${TESTDIR}/dotest.tmp` ] ; do
+ if $EXPR "`sed -n ${line}p ${TESTDIR}/dotest.tmp`" : \
+ "`sed -n ${line}p ${TESTDIR}/dotest.exp`" >/dev/null; then
+ :
+ elif test -z "`sed -n ${line}p ${TESTDIR}/dotest.tmp`" &&
+ test -z "`sed -n ${line}p ${TESTDIR}/dotest.exp`"; then
+ :
+ else
+ echo "Line $line:" >> ${LOGFILE}
+ echo "**** expected: " >>${LOGFILE}
+ sed -n ${line}p ${TESTDIR}/dotest.exp >>${LOGFILE}
+ echo "**** got: " >>${LOGFILE}
+ sed -n ${line}p ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ unset line
+ return 1
+ fi
+ line=`expr $line + 1`
+ done
+ unset line
+ return 0
+}
+
+# If you are having trouble telling which line of a multi-line
+# expression is not being matched, replace calls to dotest_internal()
+# with calls to this function:
+#
+dotest_internal_debug ()
+{
+ if test -z "$3"; then
+ if test -s ${TESTDIR}/dotest.tmp; then
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "$3" > ${TESTDIR}/dotest.exp
+ rm -f ${TESTDIR}/dotest.ex2
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ else
+ pass "$1"
+ verify_tmp_empty "$1"
+ fi
+ else
+ echo "$3" > ${TESTDIR}/dotest.exp
+ if dotest_line_by_line "$1" "$2"; then
+ pass "$1"
+ verify_tmp_empty "$1"
+ else
+ if test x"$4" != x; then
+ mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex1
+ echo "$4" > ${TESTDIR}/dotest.exp
+ if dotest_line_by_line "$1" "$2"; then
+ pass "$1"
+ verify_tmp_empty "$1"
+ else
+ mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex2
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "** or: " >>${LOGFILE}
+ echo "$4" >>${LOGFILE}
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ fi
+ else
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ fi
+ fi
+ fi
+}
+
+# This function allows the test output to be filtered before being verified.
+# The dotest_* functions all call this function, which runs the command
+# in the env var $TEST_FILTER on its argument if $TEST_FILTER is set. If
+# $TEST_FILTER is not set, this function does nothing.
+#
+# I found this primarily useful when running the test suite on a CVS
+# executable linked with memory and function profilers which can generate
+# spurious output.
+run_filter ()
+{
+ if test -n "$TEST_FILTER"; then
+ # Make sure there is an EOL
+ echo >>$1
+ sed '${/^$/d}' <$1 >$1.filter1
+ # Run the filter
+ eval "$TEST_FILTER" <$1.filter1 >$1.filter2
+ $diff_u $1 $1.filter2 \
+ >$1.diff
+ mv $1.filter2 $1
+ rm $1.filter1
+ fi
+}
+
+# Usage:
+# dotest TESTNAME COMMAND OUTPUT [OUTPUT2]
+# TESTNAME is the name used in the log to identify the test.
+# COMMAND is the command to run; for the test to pass, it exits with
+# exitstatus zero.
+# OUTPUT is a regexp which is compared against the output (stdout and
+# stderr combined) from the test. It is anchored to the start and end
+# of the output, so should start or end with ".*" if that is what is desired.
+# Trailing newlines are stripped from the command's actual output before
+# matching against OUTPUT.
+# If OUTPUT2 is specified and the output matches it, then it is also
+# a pass (partial workaround for the fact that some versions of expr
+# lack \|).
+dotest ()
+{
+ rm -f $TESTDIR/dotest.ex? 2>&1
+ eval "$2" >$TESTDIR/dotest.tmp 2>&1
+ status=$?
+ run_filter $TESTDIR/dotest.tmp
+ if test "$status" != 0; then
+ cat $TESTDIR/dotest.tmp >>$LOGFILE
+ echo "exit status was $status" >>${LOGFILE}
+ fail "$1"
+ fi
+ dotest_internal "$@"
+}
+
+# Like dotest except only 2 args and result must exactly match stdin
+dotest_lit ()
+{
+ rm -f $TESTDIR/dotest.ex? 2>&1
+ eval "$2" >$TESTDIR/dotest.tmp 2>&1
+ status=$?
+ run_filter $TESTDIR/dotest.tmp
+ if test "$status" != 0; then
+ cat $TESTDIR/dotest.tmp >>$LOGFILE
+ echo "exit status was $status" >>$LOGFILE
+ fail "$1"
+ fi
+ cat >$TESTDIR/dotest.exp
+ if cmp $TESTDIR/dotest.exp $TESTDIR/dotest.tmp >/dev/null 2>&1; then
+ pass "$1"
+ verify_tmp_empty "$1"
+ else
+ echo "** expected: " >>$LOGFILE
+ cat $TESTDIR/dotest.exp >>$LOGFILE
+ echo "** got: " >>$LOGFILE
+ cat $TESTDIR/dotest.tmp >>$LOGFILE
+ fail "$1"
+ fi
+}
+
+# Like dotest except exitstatus should be nonzero.
+dotest_fail ()
+{
+ rm -f $TESTDIR/dotest.ex? 2>&1
+ eval "$2" >$TESTDIR/dotest.tmp 2>&1
+ status=$?
+ run_filter $TESTDIR/dotest.tmp
+ if test "$status" = 0; then
+ cat $TESTDIR/dotest.tmp >>$LOGFILE
+ echo "exit status was $status" >>$LOGFILE
+ fail "$1"
+ fi
+ dotest_internal "$@"
+}
+
+# Like dotest except output is sorted.
+dotest_sort ()
+{
+ rm -f $TESTDIR/dotest.ex? 2>&1
+ eval "$2" >$TESTDIR/dotest.tmp1 2>&1
+ status=$?
+ run_filter $TESTDIR/dotest.tmp1
+ if test "$status" != 0; then
+ cat $TESTDIR/dotest.tmp1 >>$LOGFILE
+ echo "exit status was $status" >>$LOGFILE
+ fail "$1"
+ fi
+ $TR ' ' ' ' < $TESTDIR/dotest.tmp1 | sort > $TESTDIR/dotest.tmp
+ dotest_internal "$@"
+}
+
+# Like dotest_fail except output is sorted.
+dotest_fail_sort ()
+{
+ rm -f $TESTDIR/dotest.ex? 2>&1
+ eval "$2" >$TESTDIR/dotest.tmp1 2>&1
+ status=$?
+ run_filter $TESTDIR/dotest.tmp1
+ if test "$status" = 0; then
+ cat $TESTDIR/dotest.tmp1 >>$LOGFILE
+ echo "exit status was $status" >>$LOGFILE
+ fail "$1"
+ fi
+ $TR ' ' ' ' < $TESTDIR/dotest.tmp1 | sort > $TESTDIR/dotest.tmp
+ dotest_internal "$@"
+}
+
+# A function for fetching the timestamp of a revison of a file
+getrlogdate () {
+ ${testcvs} -n rlog -N ${1+"$@"} |
+ while read token value; do
+ case "$token" in
+ date:)
+ echo $value | sed "s,;.*,,"
+ break;
+ ;;
+ esac
+ done
+}
+
+# Avoid picking up any stray .cvsrc, etc., from the user running the tests
+mkdir home
+HOME=$TESTDIR/home; export HOME
+
+# Make sure this variable is not defined to anything that would
+# change the format of rcs dates. Otherwise people using e.g.,
+# RCSINIT=-zLT get lots of spurious failures.
+RCSINIT=; export RCSINIT
+
+# Remaining arguments are the names of tests to run.
+#
+# The testsuite is broken up into (hopefully manageably-sized)
+# independently runnable tests, so that one can quickly get a result
+# from a cvs or testsuite change, and to facilitate understanding the
+# tests.
+
+if test x"$*" = x; then
+ # Basic/miscellaneous functionality
+ tests="version basica basicb basicc basic1 deep basic2 ls"
+ tests="$tests parseroot parseroot2 parseroot3 files spacefiles"
+ tests="${tests} commit-readonly commit-add-missing"
+ tests="${tests} status"
+ # Branching, tagging, removing, adding, multiple directories
+ tests="${tests} rdiff rdiff-short"
+ tests="${tests} rdiff2 diff diffnl death death2"
+ tests="${tests} rm-update-message rmadd rmadd2 rmadd3 resurrection"
+ tests="${tests} dirs dirs2 branches branches2 branches3"
+ tests="${tests} branches4 tagc tagf tag-space"
+ tests="${tests} rcslib multibranch import importb importc importX"
+ tests="$tests importX2 import-CVS import-quirks"
+ tests="${tests} update-p import-after-initial branch-after-import"
+ tests="${tests} join join2 join3 join4 join5 join6 join7"
+ tests="${tests} join-readonly-conflict join-admin join-admin-2"
+ tests="${tests} join-rm"
+ tests="${tests} new newb conflicts conflicts2 conflicts3"
+ tests="${tests} clean"
+ tests="${tests} keywordexpand"
+ tests="${tests} tag-ext"
+ # Checking out various places (modules, checkout -d, &c)
+ tests="${tests} modules modules2 modules3 modules4 modules5 modules6"
+ tests="${tests} modules7 mkmodules co-d"
+ tests="${tests} cvsadm emptydir abspath abspath2 toplevel toplevel2"
+ tests="${tests} rstar-toplevel trailingslashes checkout_repository"
+ # Log messages, error messages.
+ tests="${tests} mflag editor env errmsg1 errmsg2 adderrmsg opterrmsg"
+ tests="${tests} errmsg3"
+ tests="${tests} close-stdout"
+ tests="$tests debug-log-nonfatal"
+ # Watches, binary files, history browsing, &c.
+ tests="${tests} devcom devcom2 devcom3 watch4 watch5 watch6-0 watch6"
+ tests="${tests} edit-check"
+ tests="${tests} unedit-without-baserev"
+ tests="${tests} ignore ignore-on-branch binfiles binfiles2 binfiles3"
+ tests="${tests} mcopy binwrap binwrap2"
+ tests="${tests} binwrap3 mwrap info taginfo posttag"
+ tests="$tests config config2 config3 config4 compression"
+ tests="${tests} serverpatch log log2 logopt ann ann-id"
+ # Repository Storage (RCS file format, CVS lock files, creating
+ # a repository without "cvs init", &c).
+ tests="${tests} crerepos rcs rcs2 rcs3 rcs4 rcs5"
+ tests="$tests lockfiles backuprecover"
+ tests="${tests} sshstdio"
+ # More history browsing, &c.
+ tests="${tests} history"
+ tests="${tests} big modes modes2 modes3 stamps"
+ # PreservePermissions stuff: permissions, symlinks et al.
+ # tests="${tests} perms symlinks symlinks2 hardlinks"
+ # More tag and branch tests, keywords.
+ tests="${tests} sticky keyword keywordlog keywordname keyword2"
+ tests="${tests} head tagdate multibranch2 tag8k"
+ # "cvs admin", reserved checkouts.
+ tests="${tests} admin reserved"
+ # Nuts and bolts of diffing/merging (diff library, &c)
+ tests="${tests} diffmerge1 diffmerge2"
+ # Release of multiple directories
+ tests="${tests} release"
+ tests="${tests} recase"
+ # Multiple root directories and low-level protocol tests.
+ tests="${tests} multiroot multiroot2 multiroot3 multiroot4"
+ tests="${tests} rmroot reposmv pserver server server2 client"
+ tests="${tests} dottedroot fork commit-d template"
+ tests="${tests} writeproxy writeproxy-noredirect writeproxy-ssh"
+ tests="${tests} writeproxy-ssh-noredirect"
+else
+ tests="$*"
+fi
+
+# Now check the -f argument for validity.
+if test -n "$fromtest"; then
+ # Don't allow spaces - they are our delimiters in tests
+ count=0
+ for sub in $fromtest; do
+ count=`expr $count + 1`
+ done
+ if test $count != 1; then
+ echo "No such test \`$fromtest'." >&2
+ exit 2
+ fi
+ # make sure it is in $tests
+ case " $tests " in
+ *" $fromtest "*)
+ ;;
+ *)
+ echo "No such test \`$fromtest'." >&2
+ exit 2
+ ;;
+ esac
+fi
+
+
+
+if diff_recursive_test $DIFF >/dev/null 2>&1; then
+ directory_cmp ()
+ {
+ $DIFF -u --recursive --exclude=CVS $1 $2
+ }
+else
+ # a simple function to compare directory contents
+ #
+ # Returns: 0 for same, 1 for different
+ #
+ directory_cmp ()
+ {
+ OLDPWD=`pwd`
+ DIR_1=$1
+ DIR_2=$2
+
+ cd $DIR_1
+ find . -print | fgrep -v /CVS | sort > $TESTDIR/dc$$d1
+
+ # go back where we were to avoid symlink hell...
+ cd $OLDPWD
+ cd $DIR_2
+ find . -print | fgrep -v /CVS | sort > $TESTDIR/dc$$d2
+
+ if $diff_u $TESTDIR/dc$$d1 $TESTDIR/dc$$d2
+ then
+ :
+ else
+ echo "directory_cmp: file lists of \`$DIR_1' & \`$DIR_2' differ" >&2
+ return 1
+ fi
+ cd $OLDPWD
+ while read a
+ do
+ if test -f $DIR_1/"$a" ; then
+ cmp $DIR_1/"$a" $DIR_2/"$a"
+ if test $? -ne 0 ; then
+ return 1
+ fi
+ fi
+ done < $TESTDIR/dc$$d1
+ rm -f $TESTDIR/dc$$*
+ return 0
+ }
+fi
+
+
+
+#
+# The following 4 functions are used by the diffmerge1 test case. They set up,
+# respectively, the four versions of the files necessary:
+#
+# 1. Ancestor revisions.
+# 2. "Your" changes.
+# 3. "My" changes.
+# 4. Expected merge result.
+#
+
+# Create ancestor revisions for diffmerge1
+diffmerge_create_older_files() {
+ # This test case was supplied by Noah Friedman:
+ cat >testcase01 <<EOF
+// Button.java
+
+package random.application;
+
+import random.util.*;
+
+public class Button
+{
+ /* Instantiates a Button with origin (0, 0) and zero width and height.
+ * You must call an initializer method to properly initialize the Button.
+ */
+ public Button ()
+ {
+ super ();
+
+ _titleColor = Color.black;
+ _disabledTitleColor = Color.gray;
+ _titleFont = Font.defaultFont ();
+ }
+
+ /* Convenience constructor for instantiating a Button with
+ * bounds x, y, width, and height. Equivalent to
+ * foo = new Button ();
+ * foo.init (x, y, width, height);
+ */
+ public Button (int x, int y, int width, int height)
+ {
+ this ();
+ init (x, y, width, height);
+ }
+}
+EOF
+
+ # This test case was supplied by Jacob Burckhardt:
+ cat >testcase02 <<EOF
+a
+a
+a
+a
+a
+EOF
+
+ # This test case was supplied by Karl Tomlinson who also wrote the
+ # patch which lets CVS correctly handle this and several other cases:
+ cat >testcase03 <<EOF
+x
+s
+a
+b
+s
+y
+EOF
+
+ # This test case was supplied by Karl Tomlinson:
+ cat >testcase04 <<EOF
+s
+x
+m
+m
+x
+s
+v
+s
+x
+m
+m
+x
+s
+EOF
+
+ # This test case was supplied by Karl Tomlinson:
+ cat >testcase05 <<EOF
+s
+x
+m
+m
+x
+x
+x
+x
+x
+x
+x
+x
+x
+x
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+v
+EOF
+
+ # This test case was supplied by Jacob Burckhardt:
+ cat >testcase06 <<EOF
+g
+
+
+
+
+
+
+
+
+
+
+
+i
+EOF
+
+ # This test is supposed to verify that the horizon lines are the same
+ # for both 2-way diffs, but unfortunately, it does not fail with the
+ # old version of cvs. However, Karl Tomlinson still thought it would
+ # be good to test it anyway:
+ cat >testcase07 <<EOF
+h
+f
+
+
+
+
+
+
+
+
+
+g
+r
+
+
+
+i
+
+
+
+
+
+
+
+
+
+
+i
+EOF
+
+ # This test case was supplied by Jacob Burckhardt:
+ cat >testcase08 <<EOF
+Both changes move this line to the end of the file.
+
+no
+changes
+here
+
+First change will delete this line.
+
+First change will also delete this line.
+
+ no
+ changes
+ here
+
+Second change will change it here.
+
+ no
+ changes
+ here
+EOF
+
+ # This test case was supplied by Jacob Burckhardt. Note that I do not
+ # think cvs has ever failed with this case, but I include it anyway,
+ # since I think it is a hard case. It is hard because Peter Miller's
+ # fmerge utility fails on it:
+ cat >testcase09 <<EOF
+m
+a
+{
+}
+b
+{
+}
+EOF
+
+ # This test case was supplied by Martin Dorey and simplified by Jacob
+ # Burckhardt:
+ cat >testcase10 <<EOF
+
+ petRpY ( MtatRk );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ MtatRk = MQfr_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_KRLIep *
jfle_Uecopd_MfJe_fY_nEtek );
+ OjZy MtatRk = Uead_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_MfJe_fY_nEtek,
nRVVep );
+
+ Bloke_GttpfIRte_MtpeaL ( &acI );
+MTGTXM Uead_Ktz_qjT_jfle_Uecopd ( fYt Y, uofd *nRVVep )
+{
+ fV ( Y < 16 )
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ Y *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+ elke
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtqfppHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ ( Y - 16 ) *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+
+}
+
+
+/****************************************************************************
+* *
+* Uead Mectopk ( Uelatfue to tze cRppeYt raptftfoY ) *
+* *
+****************************************************************************/
+
+MTGTXM Uead_Mectopk ( RfYt64 Mtapt_Mectop, RfYt64 KRL_Mectopk, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ MtatRk = Uead_HfkQ ( FaptftfoY_TaIle.Uelatfue_Mectop + Mtapt_Mectop,
KRL_Mectopk, nRVVep );
+
+ petRpY ( MtatRk );
+
+}
+ HfkQipfte ( waYdle, /* waYdle */
+ waYdleFok, /* ZVVket VpoL ktapt oV dfkQ */
+ (coYkt RfYt8*) nRVVep, /* nRVVep */
+ 0, /* MRrepVlRoRk KfxoYfkL */
+ beYgtz /* nEtek to Apfte */
+ );
+
+ petRpY ( Zy );
+}
+EOF
+}
+
+# Create "your" revisions for diffmerge1
+diffmerge_create_your_files() {
+ # remove the Button() method
+ cat >testcase01 <<\EOF
+// Button.java
+
+package random.application;
+
+import random.util.*;
+
+public class Button
+{
+ /* Instantiates a Button with origin (0, 0) and zero width and height.
+ * You must call an initializer method to properly initialize the Button.
+ */
+ public Button ()
+ {
+ super ();
+
+ _titleColor = Color.black;
+ _disabledTitleColor = Color.gray;
+ _titleFont = Font.defaultFont ();
+ }
+}
+EOF
+
+ cat >testcase02 <<\EOF
+y
+a
+a
+a
+a
+EOF
+
+ cat >testcase03 <<\EOF
+x
+s
+a
+b
+s
+b
+s
+y
+EOF
+
+ cat >testcase04 <<\EOF
+s
+m
+s
+v
+s
+m
+s
+EOF
+
+ cat >testcase05 <<\EOF
+v
+s
+m
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+v
+EOF
+
+ # Test case 6 and test case 7 both use the same input files, but they
+ # order the input files differently. In one case, a certain file is
+ # used as the older file, but in the other test case, that same file
+ # is used as the file which has changes. I could have put echo
+ # commands here, but since the echo lines would be the same as those
+ # in the previous function, I decided to save space and avoid
repeating
+ # several lines of code. Instead, I merely swap the files:
+ mv testcase07 tmp
+ mv testcase06 testcase07
+ mv tmp testcase06
+
+ # Make the date newer so that cvs thinks that the files are changed:
+ touch testcase06 testcase07
+
+ cat >testcase08 <<\EOF
+no
+changes
+here
+
+First change has now added this in.
+
+ no
+ changes
+ here
+
+Second change will change it here.
+
+ no
+ changes
+ here
+
+Both changes move this line to the end of the file.
+EOF
+
+ cat >testcase09 <<\EOF
+
+m
+a
+{
+}
+b
+{
+}
+c
+{
+}
+EOF
+
+ cat >testcase10 <<\EOF
+
+ fV ( BzQkV_URYYfYg ) (*jfle_Uecopdk)[0].jfle_Uecopd_KRLIep = ZpfgfYal_jUK;
+
+ petRpY ( MtatRk );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ fV ( jfle_Uecopd_KRLIep < 16 )
+ {
+ MtatRk = Uead_Ktz_qjT_jfle_Uecopd ( jfle_Uecopd_KRLIep, (uofd*)nRVVep
);
+ }
+ elke
+ {
+ MtatRk = ZreY_GttpfIRte_MtpeaL ( qjT_jfle_Uecopdk,
qjT_jfle_Uecopd_BoRYt, HGTG_TvFD, KXbb, KXbb, &acI );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ MtatRk = MQfr_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_KRLIep *
jfle_Uecopd_MfJe_fY_nEtek );
+ OjZy MtatRk = Uead_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_MfJe_fY_nEtek,
nRVVep );
+
+ Bloke_GttpfIRte_MtpeaL ( &acI );
+MTGTXM Uead_Ktz_qjT_jfle_Uecopd ( fYt Y, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ fV ( Y < 16 )
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ Y *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+ elke
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtqfppHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ ( Y - 16 ) *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+
+ petRpY ( MtatRk );
+
+}
+
+
+/****************************************************************************
+* *
+* Uead Mectopk ( Uelatfue to tze cRppeYt raptftfoY ) *
+* *
+****************************************************************************/
+
+MTGTXM Uead_Mectopk ( RfYt64 Mtapt_Mectop, RfYt64 KRL_Mectopk, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ MtatRk = Uead_HfkQ ( FaptftfoY_TaIle.Uelatfue_Mectop + Mtapt_Mectop,
KRL_Mectopk, nRVVep );
+
+ petRpY ( MtatRk );
+
+}
+ HfkQipfte ( waYdle, /* waYdle */
+ waYdleFok, /* ZVVket VpoL ktapt oV dfkQ */
+ (coYkt RfYt8*) nRVVep, /* nRVVep */
+ 0, /* MRrepVlRoRk KfxoYfkL */
+ beYgtz /* nEtek to Apfte */
+ );
+
+ petRpY ( Zy );
+}
+
+EOF
+}
+
+# Create "my" revisions for diffmerge1
+diffmerge_create_my_files() {
+ # My working copy still has the Button() method, but I
+ # comment out some code at the top of the class.
+ cat >testcase01 <<\EOF
+// Button.java
+
+package random.application;
+
+import random.util.*;
+
+public class Button
+{
+ /* Instantiates a Button with origin (0, 0) and zero width and height.
+ * You must call an initializer method to properly initialize the Button.
+ */
+ public Button ()
+ {
+ super ();
+
+ // _titleColor = Color.black;
+ // _disabledTitleColor = Color.gray;
+ // _titleFont = Font.defaultFont ();
+ }
+
+ /* Convenience constructor for instantiating a Button with
+ * bounds x, y, width, and height. Equivalent to
+ * foo = new Button ();
+ * foo.init (x, y, width, height);
+ */
+ public Button (int x, int y, int width, int height)
+ {
+ this ();
+ init (x, y, width, height);
+ }
+}
+EOF
+
+ cat >testcase02 <<\EOF
+a
+a
+a
+a
+m
+EOF
+
+ cat >testcase03 <<\EOF
+x
+s
+c
+s
+b
+s
+y
+EOF
+
+ cat >testcase04 <<\EOF
+v
+s
+x
+m
+m
+x
+s
+v
+s
+x
+m
+m
+x
+s
+v
+EOF
+
+ # Note that in test case 5, there are no changes in the "mine"
+ # section, which explains why there is no command here which writes to
+ # file testcase05.
+
+ # no changes for testcase06
+
+ # The two branches make the same changes:
+ cp ../yours/testcase07 .
+
+ cat >testcase08 <<\EOF
+no
+changes
+here
+
+First change will delete this line.
+
+First change will also delete this line.
+
+ no
+ changes
+ here
+
+Second change has now changed it here.
+
+ no
+ changes
+ here
+
+Both changes move this line to the end of the file.
+EOF
+
+ cat >testcase09 <<\EOF
+m
+a
+{
+}
+b
+{
+}
+c
+{
+}
+EOF
+
+ cat >testcase10 <<\EOF
+
+ petRpY ( MtatRk );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ MtatRk = MQfr_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_KRLIep *
jfle_Uecopd_MfJe_fY_nEtek );
+ OjZy MtatRk = Uead_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_MfJe_fY_nEtek,
nRVVep );
+
+ Bloke_GttpfIRte_MtpeaL ( &acI );
+MTGTXM Uead_Ktz_qjT_jfle_Uecopd ( fYt Y, uofd *nRVVep )
+{
+ fV ( Y < 16 )
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ Y *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+ elke
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtqfppHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ ( Y - 16 ) *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+
+}
+
+
+/****************************************************************************
+* *
+* Uead Mectopk ( Uelatfue to tze cRppeYt raptftfoY ) *
+* *
+****************************************************************************/
+
+MTGTXM Uead_Mectopk ( RfYt64 Mtapt_Mectop, RfYt64 KRL_Mectopk, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ MtatRk = Uead_HfkQ ( FaptftfoY_TaIle.Uelatfue_Mectop + Mtapt_Mectop,
KRL_Mectopk, nRVVep );
+
+ petRpY ( MtatRk );
+
+}
+ HfkQipfte ( waYdle, /* waYdle */
+ waYdleFok, /* ZVVket VpoL ktapt oV dfkQ */
+ (coYkt RfYt8*) nRVVep, /* nRVVep */
+ beYgtz /* nEtek to Apfte */
+ );
+
+ petRpY ( Zy );
+}
+
+EOF
+}
+
+# Create expected results of merge for diffmerge1
+diffmerge_create_expected_files() {
+ cat >testcase01 <<\EOF
+// Button.java
+
+package random.application;
+
+import random.util.*;
+
+public class Button
+{
+ /* Instantiates a Button with origin (0, 0) and zero width and height.
+ * You must call an initializer method to properly initialize the Button.
+ */
+ public Button ()
+ {
+ super ();
+
+ // _titleColor = Color.black;
+ // _disabledTitleColor = Color.gray;
+ // _titleFont = Font.defaultFont ();
+ }
+}
+EOF
+
+ cat >testcase02 <<\EOF
+y
+a
+a
+a
+m
+EOF
+
+ cat >testcase03 <<\EOF
+x
+s
+c
+s
+b
+s
+b
+s
+y
+EOF
+
+ cat >testcase04 <<\EOF
+v
+s
+m
+s
+v
+s
+m
+s
+v
+EOF
+
+ # Since there are no changes in the "mine" section, just take exactly
+ # the version in the "yours" section:
+ cp ../yours/testcase05 .
+
+ cp ../yours/testcase06 .
+
+ # Since the two branches make the same changes, the result should be
+ # the same as both branches. Here, I happen to pick yours to copy
from,
+ # but I could have also picked mine, since the source of the copy is
+ # the same in either case. However, the mine has already been
+ # altered by the update command, so don't use it. Instead, use the
+ # yours section which has not had an update on it and so is unchanged:
+ cp ../yours/testcase07 .
+
+ cat >testcase08 <<\EOF
+no
+changes
+here
+
+First change has now added this in.
+
+ no
+ changes
+ here
+
+Second change has now changed it here.
+
+ no
+ changes
+ here
+
+Both changes move this line to the end of the file.
+EOF
+
+ cat >testcase09 <<\EOF
+
+m
+a
+{
+}
+b
+{
+}
+c
+{
+}
+EOF
+
+ cat >testcase10 <<\EOF
+
+ fV ( BzQkV_URYYfYg ) (*jfle_Uecopdk)[0].jfle_Uecopd_KRLIep = ZpfgfYal_jUK;
+
+ petRpY ( MtatRk );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ fV ( jfle_Uecopd_KRLIep < 16 )
+ {
+ MtatRk = Uead_Ktz_qjT_jfle_Uecopd ( jfle_Uecopd_KRLIep, (uofd*)nRVVep
);
+ }
+ elke
+ {
+ MtatRk = ZreY_GttpfIRte_MtpeaL ( qjT_jfle_Uecopdk,
qjT_jfle_Uecopd_BoRYt, HGTG_TvFD, KXbb, KXbb, &acI );
+ fV ( MtatRk != Zy ) UDTXUK_DUUZU ( BGKT_ZFDK_qjT_HGTG );
+
+ MtatRk = MQfr_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_KRLIep *
jfle_Uecopd_MfJe_fY_nEtek );
+ OjZy MtatRk = Uead_GttpfIRte_MtpeaL ( &acI, jfle_Uecopd_MfJe_fY_nEtek,
nRVVep );
+
+ Bloke_GttpfIRte_MtpeaL ( &acI );
+MTGTXM Uead_Ktz_qjT_jfle_Uecopd ( fYt Y, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ fV ( Y < 16 )
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ Y *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+ elke
+ {
+ petRpY ( Uead_Mectopk ( noot_Uecopd.qVtqfppHatabcY0 *
noot_Uecopd.MectopkFepBlRktep +
+ ( Y - 16 ) *
jfle_Uecopd_MfJe_fY_Mectopk,
+ jfle_Uecopd_MfJe_fY_Mectopk,
+ nRVVep ) );
+ }
+
+ petRpY ( MtatRk );
+
+}
+
+
+/****************************************************************************
+* *
+* Uead Mectopk ( Uelatfue to tze cRppeYt raptftfoY ) *
+* *
+****************************************************************************/
+
+MTGTXM Uead_Mectopk ( RfYt64 Mtapt_Mectop, RfYt64 KRL_Mectopk, uofd *nRVVep )
+{
+MTGTXM MtatRk = Zy;
+
+ MtatRk = Uead_HfkQ ( FaptftfoY_TaIle.Uelatfue_Mectop + Mtapt_Mectop,
KRL_Mectopk, nRVVep );
+
+ petRpY ( MtatRk );
+
+}
+ HfkQipfte ( waYdle, /* waYdle */
+ waYdleFok, /* ZVVket VpoL ktapt oV dfkQ */
+ (coYkt RfYt8*) nRVVep, /* nRVVep */
+ beYgtz /* nEtek to Apfte */
+ );
+
+ petRpY ( Zy );
+}
+
+EOF
+}
+
+
+
+# Echo a new CVSROOT based on $1, $remote, and $remotehost
+newroot() {
+ if $remote; then
+ if test -n "$remotehost"; then
+ echo :ext$rootoptions:$remotehost$1
+ else
+ echo :fork$rootoptions:$1
+ fi
+ else
+ echo $1
+ fi
+}
+
+
+
+# Set up CVSROOT (the crerepos tests will test operating without CVSROOT set).
+#
+# Currently we test :fork: and :ext: (see crerepos test). There is a
+# known difference between the two in modes-15 (see comments there).
+#
+# :ext: can be tested against a remote machine if:
+#
+# 1. $remotehost is set using the `-h' option to this script.
+# 2. ${CVS_RSH=rsh} $remotehost works.
+# 3. The path to $TESTDIR is the same on both machines (symlinks are okay)
+# 4. The path to $testcvs is the same on both machines (symlinks are okay)
+# or $CVS_SERVER is overridden in this script's environment to point to
+# a working CVS exectuable on the remote machine.
+#
+# Testing :pserver: would be hard (inetd issues). (How about using tcpserver
+# and some high port number? DRP)
+
+if $linkroot; then
+ mkdir ${TESTDIR}/realcvsroot
+ ln -s realcvsroot ${TESTDIR}/cvsroot
+fi
+CVSROOT_DIRNAME=${TESTDIR}/cvsroot
+CVSROOT=`newroot $CVSROOT_DIRNAME`; export CVSROOT
+
+
+
+###
+### Initialize the repository
+###
+dotest init-1 "$testcvs init"
+
+# Now hide the primary root behind a secondary if requested.
+if $proxy; then
+ # Save the primary root.
+ PRIMARY_CVSROOT=$CVSROOT
+ PRIMARY_CVSROOT_DIRNAME=$CVSROOT_DIRNAME
+ # Where the secondary root will be
+ SECONDARY_CVSROOT_DIRNAME=$TESTDIR/secondary_cvsroot
+ if $noredirect; then
+ rootoptions=";Redirect=no"
+ SECONDARY_CVSROOT=`newroot $PRIMARY_CVSROOT_DIRNAME`
+ else
+ SECONDARY_CVSROOT=`newroot $SECONDARY_CVSROOT_DIRNAME`
+ fi
+ # Now set the global CVSROOT to use the secondary.
+ CVSROOT=$SECONDARY_CVSROOT; export CVSROOT
+
+ require_rsync
+ if test $? -eq 77; then
+ echo "Unable to test in proxy mode: $skipreason" >&2
+ skip all "missing or broken rsync command."
+ exit 0
+ fi
+
+ if $noredirect; then
+ # Wrap the CVS server to allow --primary-root to be set by the
+ # secondary.
+ cat <<EOF >$TESTDIR/secondary-wrapper
+#! $TESTSHELL
+CVS_SERVER=$TESTDIR/primary-wrapper
+export CVS_SERVER
+
+# No need to check the PID of the last client since we are testing with
+# Redirect disabled.
+proot_arg="--allow-root=$SECONDARY_CVSROOT_DIRNAME
--allow-root=$PRIMARY_CVSROOT_DIRNAME"
+exec $CVS_SERVER \$proot_arg "\$@"
+EOF
+ cat <<EOF >$TESTDIR/primary-wrapper
+#! $TESTSHELL
+if test -n "$CVS_SERVER_LOG"; then
+ CVS_SERVER_LOG=`dirname "$CVS_SERVER_LOG"`/cvsprimarylog
+ export CVS_SERVER_LOG
+fi
+exec $CVS_SERVER "\$@"
+EOF
+
+ CVS_SERVER_secondary=$TESTDIR/secondary-wrapper
+ CVS_SERVER=$CVS_SERVER_secondary
+
+ chmod a+x $TESTDIR/secondary-wrapper \
+ $TESTDIR/primary-wrapper
+ fi
+
+ # Script to sync the secondary root.
+ cat >$TESTDIR/sync-secondary <<EOF
+#! $TESTSHELL
+# date >>$TESTDIR/update-log
+
+ps=\$1
+cmd=\$2
+dir=\$3
+shift
+shift
+shift
+
+# echo "updating from \$ps for command \\\`\$cmd' in dir \\\`\$dir'"
\${1+"\$@"} \\
+# >>$TESTDIR/update-log
+
+# If multiple CVS executables could attempt to access the repository, we would
+# Need to lock for this sync and sleep
+case "\$dir" in
+ ALL)
+ # This is a hack to allow a few of the tests to play with the
+ # UseNewInfoFmtStrings key in CVSROOT/config. It's inefficient, but there
+ # aren't many tests than need it and the alternative is an awful lot of
+ # special casing.
+ $RSYNC -rglop --delete --exclude '#cvs.*' \\
+ $PRIMARY_CVSROOT_DIRNAME/ \\
+ $SECONDARY_CVSROOT_DIRNAME
+ ;;
+
+ *)
+ # For the majority of the tests we will only sync the directories that
+ # were written to.
+ case "\$cmd" in
+ add|import)
+ # For \`add', we need a recursive update due to quirks in rsync syntax,
+ # but it shouldn't affect efficiency since any new dir should be empty.
+ #
+ # For \`import', a recursive update is necessary since subdirs may have
+ # been added underneath the root dir we were passed.
+ $RSYNC -rglop \\
+ $PRIMARY_CVSROOT_DIRNAME/"\$dir" \\
+ $SECONDARY_CVSROOT_DIRNAME/\`dirname -- "\$dir"\`
+ ;;
+
+ tag)
+ # \`tag' may have changed CVSROOT/val-tags too.
+ $RSYNC -glop \\
+ $PRIMARY_CVSROOT_DIRNAME/CVSROOT/val-tags \\
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT
+ # Otherwise it is identical to other write commands.
+ $RSYNC -rglop --delete \\
+ --include Attic --include CVS \
+ --exclude '#cvs.*' --exclude '*/' \\
+ $PRIMARY_CVSROOT_DIRNAME/"\$dir"/ \\
+ $SECONDARY_CVSROOT_DIRNAME/"\$dir"
+ ;;
+
+ *)
+ # By default, sync just what changed.
+ $RSYNC -rglop --delete \\
+ --include Attic --include CVS \
+ --exclude '#cvs.*' --exclude '*/' \\
+ $PRIMARY_CVSROOT_DIRNAME/"\$dir"/ \\
+ $SECONDARY_CVSROOT_DIRNAME/"\$dir"
+ ;;
+ esac # \$cmd
+
+ # And keep the history file up to date for all commands.
+ $RSYNC -glop \\
+ $PRIMARY_CVSROOT_DIRNAME/CVSROOT/history \\
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT
+ ;; # \$dir = *
+esac # \$dir
+
+# Avoid timestamp comparison issues with rsync.
+sleep 1
+EOF
+ chmod a+x $TESTDIR/sync-secondary
+
+ # And now init the secondary.
+ $TESTDIR/sync-secondary "- no, before - create secondary root" \
+ sanity-setup ALL
+
+ # Initialize the primary repository
+ mkdir proxy-init; cd proxy-init
+ dotest proxy-init-1 "$testcvs -Qd$PRIMARY_CVSROOT co CVSROOT"
+ cd CVSROOT
+ cat >>config <<EOF
+PrimaryServer=$PRIMARY_CVSROOT
+EOF
+ cat >>loginfo <<EOF
+ALL $TESTDIR/sync-secondary loginfo %c %p %{sVv}
+EOF
+ cat >>postadmin <<EOF
+ALL $TESTDIR/sync-secondary postadmin %c %p
+EOF
+ cat >>posttag <<EOF
+ALL $TESTDIR/sync-secondary posttag %c %p %o %b %t %{sVv}
+EOF
+ cat >>postwatch <<EOF
+ALL $TESTDIR/sync-secondary postwatch %c %p
+EOF
+ dotest proxy-init-2 \
+"$testcvs -Q ci -mconfigure-writeproxy"
+
+ # Save these files for later reference
+ cp config $TESTDIR/config-clean
+ cp loginfo $TESTDIR/loginfo-clean
+ cp postadmin $TESTDIR/postadmin-clean
+ cp posttag $TESTDIR/posttag-clean
+ cp postwatch $TESTDIR/postwatch-clean
+
+ # done in here
+ cd ../..
+ rm -rf proxy-init
+else # !$proxy
+ # Set this even when not testing $proxy to match messages, like $SPROG.
+ SECONDARY_CVSROOT_DIRNAME=$CVSROOT_DIRNAME
+fi # $proxy
+
+# Save a copy of the initial repository so that it may be restored after the
+# tests that alter it.
+cp -Rp $CVSROOT_DIRNAME/CVSROOT $TESTDIR/CVSROOT.save
+
+
+###
+### The tests
+###
+dotest init-2 "$testcvs init"
+
+
+
+###
+### The big loop
+###
+for what in $tests; do
+ if test -n "$fromtest" ; then
+ if test $fromtest = $what ; then
+ unset fromtest
+ else
+ continue
+ fi
+ fi
+ case $what in
+
+ version)
+ # We've had cases where the version command started dumping core,
+ # so we might as well test it
+ dotest version-1 "${testcvs} --version" \
+'
+Concurrent Versions System (CVS) [0-9.]*.*
+
+Copyright (C) [0-9]* Free Software Foundation, Inc.
+
+Senior active maintainers include Larry Jones, Derek R. Price,
+and Mark D. Baushke. Please see the AUTHORS and README files from the CVS
+distribution kit for a complete list of contributors and copyrights.
+
+CVS may be copied only under the terms of the GNU General Public License,
+a copy of which can be found with the CVS distribution kit.
+
+Specify the --help option for further information about CVS'
+
+# Maybe someday...
+# if $proxy; then
+# dotest version-2r "${testcvs} version" \
+#'Client: Concurrent Versions System (CVS) [0-9p.]* (client.*)
+#Server: Concurrent Versions System (CVS) [0-9p.]* (.*server)
+#Secondary Server: Concurrent Versions System (CVS) [0-9p.]* (.*server)'
+ if $remote; then
+ dotest version-2r "${testcvs} version" \
+'Client: Concurrent Versions System (CVS) [0-9p.]* (client.*)
+Server: Concurrent Versions System (CVS) [0-9p.]* (.*server)'
+ else
+ dotest version-2 "${testcvs} version" \
+'Concurrent Versions System (CVS) [0-9.]*.*'
+ fi
+ ;;
+
+
+
+ basica)
+ # Similar in spirit to some of the basic1, and basic2
+ # tests, but hopefully a lot faster. Also tests operating on
+ # files two directories down *without* operating on the parent dirs.
+
+ # Tests basica-0a and basica-0b provide the equivalent of the:
+ # mkdir ${CVSROOT_DIRNAME}/first-dir
+ # used by many of the tests. It is "more official" in the sense
+ # that is does everything through CVS; the reason most of the
+ # tests don't use it is mostly historical.
+ mkdir 1; cd 1
+ dotest basica-0a "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest basica-0b "$testcvs add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd ..
+ rm -r 1
+
+ dotest basica-1 "$testcvs -q co first-dir" ''
+ cd first-dir
+
+ # Test a few operations, to ensure they gracefully do
+ # nothing in an empty directory.
+ dotest basica-1a0 "$testcvs -q update"
+ dotest basica-1a1 "$testcvs -q diff -c"
+ dotest basica-1a2 "$testcvs -q status"
+ dotest basica-1a3 "$testcvs -q update ."
+ dotest basica-1a4 "$testcvs -q update ./"
+
+ mkdir sdir
+ # Remote CVS gives the "cannot open CVS/Entries" error, which is
+ # clearly a bug, but not a simple one to fix.
+ dotest basica-1a10 "$testcvs -n add sdir" \
+"Directory $CVSROOT_DIRNAME/first-dir/sdir added to the repository" \
+"$SPROG add: cannot open CVS/Entries for reading: No such file or directory
+Directory $CVSROOT_DIRNAME/first-dir/sdir added to the repository"
+ dotest_fail basica-1a11 \
+ "test -d $CVSROOT_DIRNAME/first-dir/sdir"
+ dotest basica-2 "$testcvs add sdir" \
+"Directory $CVSROOT_DIRNAME/first-dir/sdir added to the repository"
+ cd sdir
+ mkdir ssdir
+ dotest basica-3 "$testcvs add ssdir" \
+"Directory $CVSROOT_DIRNAME/first-dir/sdir/ssdir added to the repository"
+ cd ssdir
+ echo ssfile >ssfile
+
+ # Trying to commit it without a "cvs add" should be an error.
+ # The "use `cvs add' to create an entry" message is the one
+ # that I consider to be more correct, but local cvs prints the
+ # "nothing known" message and noone has gotten around to fixing it.
+ dotest_fail basica-notadded "${testcvs} -q ci ssfile" \
+"${CPROG} commit: use .${CPROG} add. to create an entry for \`ssfile'
+${CPROG}"' \[commit aborted\]: correct above errors first!' \
+"${CPROG}"' commit: nothing known about `ssfile'\''
+'"${CPROG}"' \[commit aborted\]: correct above errors first!'
+
+ dotest basica-4 "${testcvs} add ssfile" \
+"${SPROG}"' add: scheduling file `ssfile'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest_fail basica-4a "${testcvs} tag tag0 ssfile" \
+"${SPROG} tag: nothing known about ssfile
+${SPROG} "'\[tag aborted\]: correct the above errors first!'
+ cd ../..
+ dotest basica-5 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- sdir/ssdir/ssfile
+initial revision: 1\.1"
+ dotest_fail basica-5a \
+ "${testcvs} -q tag BASE sdir/ssdir/ssfile" \
+"${SPROG} tag: Attempt to add reserved tag name BASE
+${SPROG} \[tag aborted\]: failed to set tag BASE to revision 1\.1 in
${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v"
+ dotest basica-5b "${testcvs} -q tag NOT_RESERVED" \
+'T sdir/ssdir/ssfile'
+
+ dotest basica-6 "${testcvs} -q update" ''
+ echo "ssfile line 2" >>sdir/ssdir/ssfile
+ dotest_fail basica-6.2 "${testcvs} -q diff -c" \
+"Index: sdir/ssdir/ssfile
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+retrieving revision 1\.1
+diff -c -r1\.1 ssfile
+\*\*\* sdir/ssdir/ssfile ${RFCDATE} 1\.1
+--- sdir/ssdir/ssfile ${RFCDATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ ssfile
+${PLUS} ssfile line 2"
+ dotest_fail basica-6.3 "${testcvs} -q diff -c -rBASE" \
+"Index: sdir/ssdir/ssfile
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+retrieving revision 1\.1
+diff -c -r1\.1 ssfile
+\*\*\* sdir/ssdir/ssfile ${RFCDATE} 1\.1
+--- sdir/ssdir/ssfile ${RFCDATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ ssfile
+${PLUS} ssfile line 2"
+ dotest_fail basica-6.4 "${testcvs} -q diff -c -rBASE -C3isacrowd" \
+"Index: sdir/ssdir/ssfile
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+retrieving revision 1\.1
+diff -c -C 3isacrowd -r1\.1 ssfile
+${SPROG} diff: invalid context length argument"
+ dotest basica-7 "${testcvs} -q ci -m modify-it" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- sdir/ssdir/ssfile
+new revision: 1\.2; previous revision: 1\.1"
+ dotest_fail basica-nonexist "${testcvs} -q ci nonexist" \
+"${CPROG}"' commit: nothing known about `nonexist'\''
+'"${CPROG}"' \[commit aborted\]: correct above errors first!'
+ dotest basica-8 "${testcvs} -q update ." ''
+
+ # Test the -f option to ci
+ cd sdir/ssdir
+ dotest basica-8a0 "${testcvs} -q ci -m not-modified ssfile" ''
+ dotest basica-8a "${testcvs} -q ci -f -m force-it" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 1\.3; previous revision: 1\.2"
+ dotest basica-8a1 "${testcvs} -q ci -m bump-it -r 2.0" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.0; previous revision: 1\.3"
+ dotest basica-8a1a "${testcvs} -q ci -m bump-it -r 2.9" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.9; previous revision: 2\.0"
+ # Test string-based revion number increment rollover
+ dotest basica-8a1b "${testcvs} -q ci -m bump-it -f -r 2" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.10; previous revision: 2\.9"
+ dotest basica-8a1c "${testcvs} -q ci -m bump-it -r 2.99" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.99; previous revision: 2\.10"
+ # Test string-based revion number increment rollover
+ dotest basica-8a1d "${testcvs} -q ci -m bump-it -f -r 2" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.100; previous revision: 2\.99"
+ dotest basica-8a1e "${testcvs} -q ci -m bump-it -r 2.1099" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.1099; previous revision: 2\.100"
+ # Test string-based revion number increment rollover
+ dotest basica-8a1f "${testcvs} -q ci -m bump-it -f -r 2" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 2\.1100; previous revision: 2\.1099"
+ # -f should not be necessary, but it should be harmless.
+ # Also test the "-r 3" (rather than "-r 3.0") usage.
+ dotest basica-8a2 "${testcvs} -q ci -m bump-it -f -r 3" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 3\.1; previous revision: 2\.1100"
+
+ # Test using -r to create a branch
+ dotest_fail basica-8a3 "${testcvs} -q ci -m bogus -r 3.0.0" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+$SPROG commit: $CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v: can't find
branch point 3\.0
+$SPROG commit: could not check in ssfile"
+ dotest basica-8a4 "${testcvs} -q ci -m valid -r 3.1.2" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 3\.1\.2\.1; previous revision: 3\.1"
+ # now get rid of the sticky tag and go back to the trunk
+ dotest basica-8a5 "${testcvs} -q up -A ./" "U ssfile"
+
+ cd ../..
+ dotest basica-8b "${testcvs} -q diff -r1.2 -r1.3"
+
+ dotest basica-8b1 "${testcvs} -q diff -r1.2 -r1.3 -C 3isacrowd"
+
+ # The .* here will normally be "No such file or directory",
+ # but if memory serves some systems (AIX?) have a different message.
+: dotest_fail basica-9 \
+ "${testcvs} -q -d ${TESTDIR}/nonexist update" \
+"${SPROG}: cannot access cvs root ${TESTDIR}/nonexist: .*"
+ dotest_fail basica-9a \
+ "${testcvs} -q -d ${TESTDIR}/nonexist update" \
+"${CPROG} \[update aborted\]: ${TESTDIR}/nonexist/CVSROOT: .*"
+
+ dotest basica-10 "${testcvs} annotate" \
+'
+Annotations for sdir/ssdir/ssfile
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 .'"$username8"' *[0-9a-zA-Z-]*.: ssfile
+1\.2 .'"$username8"' *[0-9a-zA-Z-]*.: ssfile line 2'
+
+ dotest basica-10w1 "${testcvs} annotate -w 1" \
+'
+Annotations for sdir/ssdir/ssfile
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 .'"$username1"' *[0-9a-zA-Z-]*.: ssfile
+1\.2 .'"$username1"' *[0-9a-zA-Z-]*.: ssfile line 2'
+ if test $userlen -lt 80; then
+ dotest basica-10wmax "${testcvs} annotate -w $userlen" \
+'
+Annotations for sdir/ssdir/ssfile
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 .'"$username"' *[0-9a-zA-Z-]*.: ssfile
+1\.2 .'"$username"' *[0-9a-zA-Z-]*.: ssfile line 2'
+ fi
+
+ # Test resurrecting with strange revision numbers
+ cd sdir/ssdir
+ dotest basica-r1 "${testcvs} rm -f ssfile" \
+"${SPROG} remove: scheduling .ssfile. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest basica-r2 "${testcvs} -q ci -m remove" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: delete; previous revision: 3\.1"
+ dotest basica-r3 "${testcvs} -q up -p -r 3.1 ./ssfile >ssfile" ""
+ dotest basica-r4 "${testcvs} add ssfile" \
+"${SPROG} add: Re-adding file .ssfile. after dead revision 3\.2\.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basica-r5 "${testcvs} -q ci -m resurrect" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 3\.3; previous revision: 3\.2"
+ cd ../..
+
+ # As long as we have a file with a few revisions, test
+ # a few "cvs admin -o" invocations.
+ cd sdir/ssdir
+ dotest_fail basica-o1 "${testcvs} admin -o 1.2::1.2" \
+"${CPROG} admin: while processing more than one file:
+${CPROG} \[admin aborted\]: attempt to specify a numeric revision"
+ dotest basica-o2 "${testcvs} admin -o 1.2::1.2 ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+done"
+ dotest basica-o2a "${testcvs} admin -o 1.1::NOT_RESERVED ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+done"
+ dotest_fail basica-o2b "${testcvs} admin -o 1.1::NOT_EXIST ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v: Revision
NOT_EXIST doesn't exist.
+${SPROG} admin: RCS file for .ssfile. not modified\."
+ dotest basica-o3 "${testcvs} admin -o 1.2::1.3 ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+done"
+ dotest basica-o4 "${testcvs} admin -o 3.1:: ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+deleting revision 3\.3
+deleting revision 3\.2
+done"
+ dotest basica-o5 "${testcvs} admin -o ::1.1 ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+done"
+ dotest basica-o5a "${testcvs} -n admin -o 1.2::3.1 ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+deleting revision 2\.1100
+deleting revision 2\.1099
+deleting revision 2\.100
+deleting revision 2\.99
+deleting revision 2\.10
+deleting revision 2\.9
+deleting revision 2\.0
+deleting revision 1\.3
+done"
+ dotest basica-o6 "${testcvs} admin -o 1.2::3.1 ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+deleting revision 2\.1100
+deleting revision 2\.1099
+deleting revision 2\.100
+deleting revision 2\.99
+deleting revision 2\.10
+deleting revision 2\.9
+deleting revision 2\.0
+deleting revision 1\.3
+done"
+ dotest basica-o6a "${testcvs} admin -o 3.1.2: ssfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+deleting revision 3\.1\.2\.1
+done"
+ dotest basica-o7 "${testcvs} log -N ssfile" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir/ssdir/ssfile,v
+Working file: ssfile
+head: 3\.1
+branch:
+locks: strict
+access list:
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 3\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+bump-it
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-it
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-it
+============================================================================="
+ dotest basica-o8 "${testcvs} -q update -p -r 1.1 ./ssfile" "ssfile"
+ cd ../..
+
+ cd ..
+
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r first-dir
+ ;;
+
+
+
+ basicb)
+ # More basic tests, including non-branch tags and co -d.
+ mkdir 1; cd 1
+ dotest basicb-0a "${testcvs} -q co -l ." ''
+ touch topfile
+ dotest basicb-0b "${testcvs} add topfile" \
+"${SPROG} add: scheduling file .topfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basicb-0c "${testcvs} -q ci -m add-it topfile" \
+"$CVSROOT_DIRNAME/topfile,v <-- topfile
+initial revision: 1\.1"
+ cd ..
+ rm -r 1
+ mkdir 2; cd 2
+ dotest basicb-0d "${testcvs} -q co -l ." "U topfile"
+ # Now test the ability to run checkout on an existing working
+ # directory without having it lose its mind. I don't know
+ # whether this is tested elsewhere in sanity.sh. A more elaborate
+ # test might also have modified files, make sure it works if
+ # the modules file was modified to add new directories to the
+ # module, and such.
+ dotest basicb-0d0 "${testcvs} -q co -l ." ""
+ mkdir first-dir
+ dotest basicb-0e "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd ..
+ rm -r 2
+
+ dotest basicb-1 "${testcvs} -q co first-dir" ''
+
+ # The top-level CVS directory is not created by default.
+ # I'm leaving basicb-1a and basicb-1b untouched, mostly, in
+ # case we decide that the default should be reversed...
+
+ dotest_fail basicb-1a "test -d CVS" ''
+
+ dotest basicb-1c "cat first-dir/CVS/Repository" "first-dir"
+
+ cd first-dir
+ # Note that the name Emptydir is chosen to test that CVS just
+ # treats it like any other directory name. It should be
+ # special only when it is directly in $CVSROOT/CVSROOT.
+ mkdir Emptydir sdir2
+ dotest basicb-2 "${testcvs} add Emptydir sdir2" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/Emptydir added to the repository
+Directory ${CVSROOT_DIRNAME}/first-dir/sdir2 added to the repository"
+ cd Emptydir
+ echo sfile1 starts >sfile1
+ dotest basicb-2a10 "${testcvs} -n add sfile1" \
+"${SPROG} add: scheduling file .sfile1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basicb-2a11 "${testcvs} status sfile1" \
+"${SPROG} status: use \`${SPROG} add' to create an entry for \`sfile1'
+===================================================================
+File: sfile1 Status: Unknown
+
+ Working revision: No entry for sfile1
+ Repository revision: No revision control file"
+ dotest basicb-3 "${testcvs} add sfile1" \
+"${SPROG} add: scheduling file .sfile1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basicb-3a1 "${testcvs} status sfile1" \
+"===================================================================
+File: sfile1 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ cd ../sdir2
+ echo sfile2 starts >sfile2
+ dotest basicb-4 "${testcvs} add sfile2" \
+"${SPROG} add: scheduling file .sfile2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basicb-4a "${testcvs} -q ci CVS" \
+"${CPROG} commit: warning: directory CVS specified in argument
+${CPROG} commit: but CVS uses CVS for its own purposes; skipping CVS directory"
+ cd ..
+ dotest basicb-5 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/Emptydir/sfile1,v <-- Emptydir/sfile1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/sdir2/sfile2,v <-- sdir2/sfile2
+initial revision: 1\.1"
+ echo sfile1 develops >Emptydir/sfile1
+ dotest basicb-6 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/Emptydir/sfile1,v <-- Emptydir/sfile1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest basicb-7 "${testcvs} -q tag release-1" 'T Emptydir/sfile1
+T sdir2/sfile2'
+ echo not in time for release-1 >sdir2/sfile2
+ dotest basicb-8 "${testcvs} -q ci -m modify-2" \
+"$CVSROOT_DIRNAME/first-dir/sdir2/sfile2,v <-- sdir2/sfile2
+new revision: 1\.2; previous revision: 1\.1"
+ # See if CVS can correctly notice when an invalid numeric
+ # revision is specified.
+ # Commented out until we get around to fixing CVS
+: dotest basicb-8a0 "${testcvs} diff -r 1.5 -r 1.7 sfile2" 'error msg'
+ cd ..
+
+ # Test that we recurse into the correct directory when checking
+ # for existing files, even if co -d is in use.
+ touch first-dir/extra
+ dotest basicb-cod-1 "${testcvs} -q co -d first-dir1 first-dir" \
+'U first-dir1/Emptydir/sfile1
+U first-dir1/sdir2/sfile2'
+ rm -r first-dir1
+
+ rm -r first-dir
+
+ # FIXME? basicb-9 used to check things out like this:
+ # U newdir/Emptydir/sfile1
+ # U newdir/sdir2/sfile2
+ # but that's difficult to do. The whole "shorten" thing
+ # is pretty bogus, because it will break on things
+ # like "cvs co foo/bar baz/quux". Unless there's some
+ # pretty detailed expansion and analysis of the command-line
+ # arguments, we shouldn't do "shorten" stuff at all.
+
+ dotest basicb-9 \
+"${testcvs} -q co -d newdir -r release-1 first-dir/Emptydir first-dir/sdir2" \
+'U newdir/first-dir/Emptydir/sfile1
+U newdir/first-dir/sdir2/sfile2'
+
+ # basicb-9a and basicb-9b: see note about basicb-1a
+
+ dotest_fail basicb-9a "test -d CVS" ''
+
+ dotest basicb-9c "cat newdir/CVS/Repository" "\."
+ dotest basicb-9d "cat newdir/first-dir/CVS/Repository" \
+"${CVSROOT_DIRNAME}/first-dir" \
+"first-dir"
+ dotest basicb-9e "cat newdir/first-dir/Emptydir/CVS/Repository" \
+"${CVSROOT_DIRNAME}/first-dir/Emptydir" \
+"first-dir/Emptydir"
+ dotest basicb-9f "cat newdir/first-dir/sdir2/CVS/Repository" \
+"${CVSROOT_DIRNAME}/first-dir/sdir2" \
+"first-dir/sdir2"
+
+ dotest basicb-10 "cat newdir/first-dir/Emptydir/sfile1
newdir/first-dir/sdir2/sfile2" \
+"sfile1 develops
+sfile2 starts"
+
+ rm -r newdir
+
+ # Hmm, this might be a case for CVSNULLREPOS, but CVS doesn't
+ # seem to deal with it...
+ if false; then
+ dotest basicb-11 "${testcvs} -q co -d sub1/sub2 first-dir" \
+"U sub1/sub2/Emptydir/sfile1
+U sub1/sub2/sdir2/sfile2"
+ cd sub1
+ dotest basicb-12 "${testcvs} -q update ./." ''
+ touch xx
+ dotest basicb-13 "${testcvs} add xx" fixme
+ cd ..
+ rm -r sub1
+ # to test: sub1/sub2/sub3
+ fi # end of tests commented out.
+
+ # Create a second directory.
+ mkdir 1
+ cd 1
+ dotest basicb-14 "${testcvs} -q co -l ." 'U topfile'
+ mkdir second-dir
+ dotest basicb-15 "${testcvs} add second-dir" \
+"Directory ${CVSROOT_DIRNAME}/second-dir added to the repository"
+ cd second-dir
+ touch aa
+ dotest basicb-16 "${testcvs} add aa" \
+"${SPROG} add: scheduling file .aa. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest basicb-17 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/second-dir/aa,v <-- aa
+initial revision: 1\.1"
+ cd ..
+
+ # Try to remove all revisions in a file.
+ dotest_fail basicb-o1 "${testcvs} admin -o1.1 topfile" \
+"RCS file: ${CVSROOT_DIRNAME}/topfile,v
+deleting revision 1\.1
+${SPROG} \[admin aborted\]: attempt to delete all revisions"
+ dotest basicb-o2 "${testcvs} -q update -d first-dir" \
+"U first-dir/Emptydir/sfile1
+U first-dir/sdir2/sfile2"
+ dotest_fail basicb-o3 \
+"${testcvs} admin -o1.1:1.2 first-dir/sdir2/sfile2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/sdir2/sfile2,v
+deleting revision 1\.2
+deleting revision 1\.1
+${SPROG} \[admin aborted\]: attempt to delete all revisions"
+ cd ..
+ rm -r 1
+
+ mkdir 1; cd 1
+ # Note that -H is an invalid option.
+ # I suspect that the choice between "illegal" and "invalid"
+ # depends on the user's environment variables, the phase
+ # of the moon (weirdness with optind), and who knows what else.
+ # I've been seeing "illegal"...
+ # And I switched it to "invalid". -DRP
+ # POSIX 1003.2 specifies the format should be 'illegal option'
+ # many other folks are still using the older 'invalid option'
+ # lib/getopt.c will use POSIX when __posixly_correct
+ # otherwise the other, so accept both of them. -- mdb
+ dotest_fail basicb-21 "${testcvs} -q admin -H" \
+"admin: invalid option -- H
+${CPROG} \[admin aborted\]: specify ${CPROG} -H admin for usage information" \
+"admin: illegal option -- H
+${CPROG} \[admin aborted\]: specify ${CPROG} -H admin for usage information"
+ cd ..
+ rmdir 1
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ modify_repo rm -f $CVSROOT_DIRNAME/topfile,v
+ ;;
+
+
+
+ basicc)
+ # More tests of basic/miscellaneous functionality.
+ mkdir 1; cd 1
+ dotest_fail basicc-1 "$testcvs diff" \
+"$CPROG diff: in directory \.:
+$CPROG \[diff aborted\]: there is no version here; run .$CPROG checkout. first"
+ dotest basicc-2 "$testcvs -q co -l ."
+ mkdir first-dir second-dir
+ dotest basicc-3 "${testcvs} add first-dir second-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/second-dir added to the repository"
+ # Old versions of CVS often didn't create this top-level CVS
+ # directory in the first place. I think that maybe the only
+ # way to get it to work currently is to let CVS create it,
+ # and then blow it away (don't complain if it does not
+ # exist). But that is perfectly valid; people who are used
+ # to the old behavior especially may be interested.
+ # FIXME: this test is intended for the TopLevelAdmin=yes case;
+ # should adjust/move it accordingly.
+ rm -rf CVS
+ dotest basicc-4 "echo *" "first-dir second-dir"
+ dotest basicc-5 "${testcvs} update" \
+"${SPROG} update: Updating first-dir
+${SPROG} update: Updating second-dir" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating first-dir
+${SPROG} update: Updating second-dir"
+
+ cd first-dir
+ dotest basicc-6 "${testcvs} release -d" ""
+ dotest basicc-7 "test -d ../first-dir" ""
+ # The Linux 2.2 kernel lets you delete ".". That's OK either way,
+ # the point is that CVS must not mess with anything *outside* "."
+ # the way that CVS 1.10 and older tried to.
+ dotest basicc-8 "${testcvs} -Q release -d ." \
+"" "${CPROG} release: deletion of directory \. failed: .*"
+ dotest basicc-9 "test -d ../second-dir" ""
+ # For CVS to make a syntactic check for "." wouldn't suffice.
+ # On Linux 2.2 systems, the cwd may be gone, so we recreate it
+ # to allow basicc-11 to actually happen
+ if test ! -d ../first-dir; then
+ # Apparently `cd ..' doesn't work with Linux 2.2 & Bash 2.05b.
+ cd $TESTDIR/1
+ mkdir ./first-dir
+ cd ./first-dir
+ fi
+ dotest basicc-11 "${testcvs} -Q release -d ./." \
+"" "${CPROG} release: deletion of directory \./\. failed: .*"
+ dotest basicc-11a "test -d ../second-dir" ""
+
+ cd ../..
+
+ mkdir 2; cd 2
+ dotest basicc-12 "${testcvs} -Q co ." ""
+ # actual entries can be in either Entries or Entries.log, do
+ # an update to get them consolidated into Entries
+ dotest basicc-12a "${testcvs} -Q up" ""
+ dotest basicc-12b "cat CVS/Entries" \
+"D/CVSROOT////
+D/first-dir////
+D/second-dir////"
+ dotest basicc-13 "echo *" "CVS CVSROOT first-dir second-dir"
+ dotest basicc-14 "${testcvs} -Q release first-dir second-dir" ""
+ # a normal release shouldn't affect the Entries file
+ dotest basicc-14b "cat CVS/Entries" \
+"D/CVSROOT////
+D/first-dir////
+D/second-dir////"
+ # FIXCVS: but release -d probably should
+ dotest basicc-15 "${testcvs} -Q release -d first-dir second-dir" ""
+ dotest basicc-16 "echo *" "CVS CVSROOT"
+ dotest basicc-17 "cat CVS/Entries" \
+"D/CVSROOT////
+D/first-dir////
+D/second-dir////"
+ # FIXCVS: if not, update should notice the missing directories
+ # and update Entries accordingly
+ dotest basicc-18 "${testcvs} -Q up" ""
+ dotest basicc-19 "cat CVS/Entries" \
+"D/CVSROOT////
+D/first-dir////
+D/second-dir////"
+
+ cd ..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ basic1)
+ # first dive - add a files, first singly, then in a group.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir basic1; cd basic1
+ # check out an empty directory
+ dotest basic1-1 "${testcvs} -q co first-dir" ''
+
+ cd first-dir
+ echo file2 >file2
+ echo file3 >file3
+ echo file4 >file4
+ echo file5 >file5
+
+ dotest basic1-14-add-add "${testcvs} add file2 file3 file4 file5" \
+"${SPROG} add: scheduling file \`file2' for addition
+${SPROG} add: scheduling file \`file3' for addition
+${SPROG} add: scheduling file \`file4' for addition
+${SPROG} add: scheduling file \`file5' for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest basic1-15-add-add \
+"${testcvs} -q update file2 file3 file4 file5" \
+"A file2
+A file3
+A file4
+A file5"
+ dotest basic1-16-add-add "${testcvs} -q update" \
+"A file2
+A file3
+A file4
+A file5"
+ dotest basic1-17-add-add "${testcvs} -q status" \
+"===================================================================
+File: file2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file3 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file4 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file5 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest basic1-18-add-add "${testcvs} -q log" \
+"${SPROG} log: file2 has been added, but not committed
+${SPROG} log: file3 has been added, but not committed
+${SPROG} log: file4 has been added, but not committed
+${SPROG} log: file5 has been added, but not committed"
+ cd ..
+ dotest basic1-21-add-add "${testcvs} -q update" \
+"A first-dir/file2
+A first-dir/file3
+A first-dir/file4
+A first-dir/file5"
+ # FIXCVS? Shouldn't this read first-dir/file2 instead of file2?
+ dotest basic1-22-add-add "${testcvs} log first-dir" \
+"${SPROG} log: Logging first-dir
+${SPROG} log: file2 has been added, but not committed
+${SPROG} log: file3 has been added, but not committed
+${SPROG} log: file4 has been added, but not committed
+${SPROG} log: file5 has been added, but not committed"
+ dotest basic1-23-add-add "${testcvs} status first-dir" \
+"${SPROG} status: Examining first-dir
+===================================================================
+File: file2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file3 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file4 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file5 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest basic1-24-add-add "${testcvs} update first-dir" \
+"${SPROG} update: Updating first-dir
+A first-dir/file2
+A first-dir/file3
+A first-dir/file4
+A first-dir/file5"
+ dotest basic1-27-add-add "${testcvs} co first-dir" \
+"${SPROG} checkout: Updating first-dir
+A first-dir/file2
+A first-dir/file3
+A first-dir/file4
+A first-dir/file5"
+ cd first-dir
+ dotest basic1-14-add-ci \
+"$testcvs commit -m test file2 file3 file4 file5" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+initial revision: 1\.1"
+ dotest basic1-15-add-ci \
+"${testcvs} -q update file2 file3 file4 file5" ''
+ dotest basic1-16-add-ci "${testcvs} -q update" ''
+ dotest basic1-17-add-ci "${testcvs} -q status" \
+"===================================================================
+File: file2 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file3 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file4 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file4,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file5 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file5,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ # The "log" tests and friends probably already test the output
+ # from log quite adequately.
+ # Note: using dotest fails here. It seems to be related
+ # to the output being sufficiently large (Red Hat 4.1).
+ # dotest basic1-18-add-ci "${testcvs} log" "${DOTSTAR}"
+ if ${testcvs} -q log >>${LOGFILE}; then
+ pass basic1-18-add-ci
+ else
+ pass basic1-18-add-ci
+ fi
+ cd ..
+ dotest basic1-21-add-ci "${testcvs} -q update" ''
+ # See test basic1-18-add-ci for explanation of non-use of dotest.
+ if ${testcvs} -q log first-dir >>${LOGFILE}; then
+ pass basic1-22-add-ci
+ else
+ pass basic1-22-add-ci
+ fi
+ # At least for the moment I am going to consider 17-add-ci
+ # an adequate test of the output here.
+ # See test basic1-18-add-ci for explanation of non-use of dotest.
+ if ${testcvs} -q status first-dir >>${LOGFILE}; then
+ pass basic1-23-add-ci
+ else
+ pass basic1-23-add-ci
+ fi
+ dotest basic1-24-add-ci "${testcvs} -q update first-dir" ''
+ dotest basic1-27-add-ci "${testcvs} -q co first-dir" ''
+
+ cd first-dir
+ rm file2 file3 file4 file5
+ dotest basic1-14-rm-rm "${testcvs} rm file2 file3 file4 file5" \
+"${SPROG} remove: scheduling .file2. for removal
+${SPROG} remove: scheduling .file3. for removal
+${SPROG} remove: scheduling .file4. for removal
+${SPROG} remove: scheduling .file5. for removal
+${SPROG} remove: use .${SPROG} commit. to remove these files permanently"
+ # 15-rm-rm was commented out. Why?
+ dotest basic1-15-rm-rm \
+"${testcvs} -q update file2 file3 file4 file5" \
+"R file2
+R file3
+R file4
+R file5"
+ dotest basic1-16-rm-rm "${testcvs} -q update" \
+"R file2
+R file3
+R file4
+R file5"
+ dotest basic1-17-rm-rm "${testcvs} -q status" \
+"===================================================================
+File: no file file2 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file3 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file4 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file4,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file5 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file5,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ # Would be nice to test that real logs appear (with dead state
+ # and all), either here or someplace like log2 tests.
+ if ${testcvs} -q log >>${LOGFILE}; then
+ pass basic1-18-rm-rm
+ else
+ fail basic1-18-rm-rm
+ fi
+ cd ..
+ dotest basic1-21-rm-rm "${testcvs} -q update" \
+"R first-dir/file2
+R first-dir/file3
+R first-dir/file4
+R first-dir/file5"
+ if ${testcvs} -q log first-dir >>${LOGFILE}; then
+ pass basic1-22-rm-rm
+ else
+ fail basic1-22-rm-rm
+ fi
+ if ${testcvs} -q status first-dir >>${LOGFILE}; then
+ pass basic1-23-rm-rm
+ else
+ fail basic1-23-rm-rm
+ fi
+ dotest basic1-24-rm-rm "${testcvs} -q update first-dir" \
+"R first-dir/file2
+R first-dir/file3
+R first-dir/file4
+R first-dir/file5"
+ dotest basic1-27-rm-rm "${testcvs} -q co first-dir" \
+"R first-dir/file2
+R first-dir/file3
+R first-dir/file4
+R first-dir/file5"
+ cd first-dir
+ dotest basic1-14-rm-ci "${testcvs} -q commit -m test" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+new revision: delete; previous revision: 1\.1"
+ dotest basic1-15-rm-ci \
+"${testcvs} -q update file2 file3 file4 file5" ''
+ dotest basic1-16-rm-ci "${testcvs} -q update" ''
+ dotest basic1-17-rm-ci "${testcvs} -q status" ''
+ # Would be nice to test that real logs appear (with dead state
+ # and all), either here or someplace like log2 tests.
+ if ${testcvs} -q log >>${LOGFILE}; then
+ pass basic1-18-rm-ci
+ else
+ fail basic1-18-rm-ci
+ fi
+ cd ..
+ dotest basic1-21-rm-ci "${testcvs} -q update" ''
+ if ${testcvs} -q log first-dir >>${LOGFILE}; then
+ pass basic1-22-rm-ci
+ else
+ fail basic1-22-rm-ci
+ fi
+ if ${testcvs} -q status first-dir >>${LOGFILE}; then
+ pass basic1-23-rm-ci
+ else
+ fail basic1-23-rm-ci
+ fi
+ dotest basic1-24-rm-ci "${testcvs} -q update first-dir" ''
+ dotest basic1-27-rm-ci "${testcvs} -q co first-dir" ''
+ cd first-dir
+ # All the files are removed, so nothing gets tagged.
+ dotest basic1-28 "${testcvs} -q tag first-dive" ''
+ cd ..
+ cd ..
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ rm -r basic1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ deep)
+ # Test the ability to operate on directories nested rather deeply.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest deep-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+ for i in dir1 dir2 dir3 dir4 dir5 dir6 dir7 dir8; do
+ mkdir $i
+ dotest deep-2-$i "${testcvs} add $i" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir1[/dir0-9]* added to the repository"
+ cd $i
+ echo file1 >file1
+ dotest deep-3-$i "${testcvs} add file1" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ done
+ cd ../../../../../../../../..
+ dotest_lit deep-4 "$testcvs -q ci -m add-them first-dir" <<HERE
+$CVSROOT_DIRNAME/first-dir/dir1/file1,v <-- first-dir/dir1/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/file1,v <-- first-dir/dir1/dir2/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/file1,v <--
first-dir/dir1/dir2/dir3/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/file1,v <--
first-dir/dir1/dir2/dir3/dir4/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v <--
first-dir/dir1/dir2/dir3/dir4/dir5/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v <--
first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v <--
first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v
<-- first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1
+initial revision: 1.1
+HERE
+
+ cd first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
+ rm file1
+ dotest deep-4a0 "$testcvs rm file1" \
+"$SPROG remove: scheduling .file1. for removal
+$SPROG remove: use .$SPROG commit. to remove this file permanently"
+ dotest deep-4a1 "$testcvs -q ci -m rm-it" \
+"$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v
<-- file1
+new revision: delete; previous revision: 1\.1"
+ cd ../../..
+ dotest deep-4a2 "${testcvs} -q update -P dir6/dir7" ''
+ # Should be using "test -e", but it's not portable enough -
+ # Solaris 2.5 does not have it.
+ dotest_fail deep-4a3 "test -d dir6/dir7/dir8" ''
+
+ # Test that if we remove the working directory, CVS does not
+ # recreate it. (I realize that this behavior is what the
+ # users expect, but in the longer run we might want to
+ # re-think it. The corresponding behavior for a file is that
+ # CVS *will* recreate it, and we might want to make it so
+ # that "cvs release -d" is the way to delete the directory
+ # and have it stay gone -kingdon, Oct1996).
+ rm -r dir6
+ dotest deep-4b0a "${testcvs} -q diff"
+ dotest deep-4b0b "${testcvs} -q ci"
+ dotest deep-4b1 "${testcvs} -q update"
+ dotest deep-4b2 "${testcvs} -q update -d -P" \
+'U dir6/file1
+U dir6/dir7/file1'
+
+ # Test what happens if one uses -P when there are files removed
+ # but not committed.
+ cd dir6/dir7
+ dotest deep-rm1 "${testcvs} rm -f file1" \
+"${SPROG} remove: scheduling .file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ cd ..
+ dotest deep-rm2 "${testcvs} -q update -d -P" 'R dir7/file1'
+ dotest deep-rm3 "test -d dir7" ''
+ dotest deep-rm4 "$testcvs -q ci -m rm-it" \
+"$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file1,v <--
dir7/file1
+new revision: delete; previous revision: 1\.1"
+ dotest deep-rm5 "${testcvs} -q update -d -P" ''
+ dotest_fail deep-rm6 "test -d dir7" ''
+
+ # Test rm -f -R.
+ cd ../..
+ dotest deep-rm7 "${testcvs} rm -f -R dir5" \
+"${SPROG} remove: Removing dir5
+${SPROG} remove: scheduling .dir5/file1. for removal
+${SPROG} remove: Removing dir5/dir6
+${SPROG} remove: scheduling .dir5/dir6/file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove these files permanently"
+ dotest deep-rm8 "${testcvs} -q ci -m rm-it" \
+"$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/file1,v <-- dir5/file1
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/file1,v <--
dir5/dir6/file1
+new revision: delete; previous revision: 1\.1"
+ dotest deep-rm9 "${testcvs} -q update -d -P" ''
+ dotest_fail deep-rm10 "test -d dir5"
+
+ cd ../../../../..
+
+ if echo "yes" | $testcvs release -d first-dir >>$LOGFILE 2>&1; then
+ pass deep-5
+ else
+ fail deep-5
+ fi
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ basic2)
+ # Test rtag, import, history, various miscellaneous operations
+
+ # NOTE: this section has reached the size and
+ # complexity where it is getting to be a good idea to
+ # add new tests to a new section rather than
+ # continuing to piggyback them onto the tests here.
+
+ # First empty the history file
+ modify_repo rm -rf $CVSROOT_DIRNAME/CVSROOT/history
+ modify_repo touch $CVSROOT_DIRNAME/CVSROOT/history
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest basic2-1 "$testcvs -q co first-dir"
+ for i in first-dir dir1 dir2 ; do
+ if test ! -d $i ; then
+ mkdir $i
+ dotest basic2-2-$i "${testcvs} add $i" \
+"Directory ${CVSROOT_DIRNAME}/.*/$i added to the repository"
+ fi
+
+ cd $i
+
+ for j in file6 file7; do
+ echo $j > $j
+ done
+
+ dotest basic2-3-$i "${testcvs} add file6 file7" \
+"${SPROG} add: scheduling file .file6. for addition
+${SPROG} add: scheduling file .file7. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+
+ done
+ cd ../../..
+ dotest basic2-4 "${testcvs} update first-dir" \
+"${SPROG} update: Updating first-dir
+A first-dir/file6
+A first-dir/file7
+${SPROG} update: Updating first-dir/dir1
+A first-dir/dir1/file6
+A first-dir/dir1/file7
+${SPROG} update: Updating first-dir/dir1/dir2
+A first-dir/dir1/dir2/file6
+A first-dir/dir1/dir2/file7"
+
+ # fixme: doesn't work right for added files.
+ dotest basic2-5 "${testcvs} log first-dir" \
+"${SPROG} log: Logging first-dir
+${SPROG} log: file6 has been added, but not committed
+${SPROG} log: file7 has been added, but not committed
+${SPROG} log: Logging first-dir/dir1
+${SPROG} log: file6 has been added, but not committed
+${SPROG} log: file7 has been added, but not committed
+${SPROG} log: Logging first-dir/dir1/dir2
+${SPROG} log: file6 has been added, but not committed
+${SPROG} log: file7 has been added, but not committed"
+
+ dotest basic2-6 "${testcvs} status first-dir" \
+"${SPROG} status: Examining first-dir
+===================================================================
+File: file6 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file7 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+${SPROG} status: Examining first-dir/dir1
+===================================================================
+File: file6 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file7 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+${SPROG} status: Examining first-dir/dir1/dir2
+===================================================================
+File: file6 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file7 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+# XXX why is this commented out???
+# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ;
then
+# pass 34
+# else
+# fail 34
+# fi
+
+ dotest basic2-8 "${testcvs} -q ci -m 'second dive' first-dir" \
+"$CVSROOT_DIRNAME/first-dir/file6,v <-- first-dir/file6
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file7,v <-- first-dir/file7
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/file6,v <-- first-dir/dir1/file6
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/file7,v <-- first-dir/dir1/file7
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/file6,v <-- first-dir/dir1/dir2/file6
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/dir2/file7,v <-- first-dir/dir1/dir2/file7
+initial revision: 1\.1"
+
+ dotest basic2-9 "${testcvs} tag second-dive first-dir" \
+"${SPROG} tag: Tagging first-dir
+T first-dir/file6
+T first-dir/file7
+${SPROG} tag: Tagging first-dir/dir1
+T first-dir/dir1/file6
+T first-dir/dir1/file7
+${SPROG} tag: Tagging first-dir/dir1/dir2
+T first-dir/dir1/dir2/file6
+T first-dir/dir1/dir2/file7"
+
+ # third dive - in bunch o' directories, add bunch o' files,
+ # delete some, change some.
+
+ for i in first-dir dir1 dir2 ; do
+ cd $i
+
+ # modify a file
+ echo file6 >>file6
+
+ # delete a file
+ rm file7
+
+ dotest basic2-10-$i "${testcvs} rm file7" \
+"${SPROG} remove: scheduling .file7. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+
+ # and add a new file
+ echo file14 >file14
+
+ dotest basic2-11-$i "${testcvs} add file14" \
+"${SPROG} add: scheduling file .file14. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ done
+
+ cd ../../..
+ dotest basic2-12 "${testcvs} update first-dir" \
+"${SPROG} update: Updating first-dir
+A first-dir/file14
+M first-dir/file6
+R first-dir/file7
+${SPROG} update: Updating first-dir/dir1
+A first-dir/dir1/file14
+M first-dir/dir1/file6
+R first-dir/dir1/file7
+${SPROG} update: Updating first-dir/dir1/dir2
+A first-dir/dir1/dir2/file14
+M first-dir/dir1/dir2/file6
+R first-dir/dir1/dir2/file7"
+
+ # FIXME: doesn't work right for added files
+ dotest basic2-13 "${testcvs} log first-dir" \
+"${SPROG} log: Logging first-dir
+${SPROG} log: file14 has been added, but not committed
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file6,v
+Working file: first-dir/file6
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file7,v
+Working file: first-dir/file7
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+=============================================================================
+${SPROG} log: Logging first-dir/dir1
+${SPROG} log: file14 has been added, but not committed
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir1/file6,v
+Working file: first-dir/dir1/file6
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir1/file7,v
+Working file: first-dir/dir1/file7
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+=============================================================================
+${SPROG} log: Logging first-dir/dir1/dir2
+${SPROG} log: file14 has been added, but not committed
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file6,v
+Working file: first-dir/dir1/dir2/file6
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file7,v
+Working file: first-dir/dir1/dir2/file7
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ second-dive: 1\.1
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+second dive
+============================================================================="
+
+ dotest basic2-14 "${testcvs} status first-dir" \
+"${SPROG} status: Examining first-dir
+===================================================================
+File: file14 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file6 Status: Locally Modified
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file6,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file7 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file7,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+${SPROG} status: Examining first-dir/dir1
+===================================================================
+File: file14 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file6 Status: Locally Modified
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/dir1/file6,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file7 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/dir1/file7,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+${SPROG} status: Examining first-dir/dir1/dir2
+===================================================================
+File: file14 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file6 Status: Locally Modified
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file6,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: no file file7 Status: Locally Removed
+
+ Working revision: -1\.1.*
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file7,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)${DOTSTAR}"
+
+# XXX why is this commented out?
+# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ; then
+# pass 42
+# else
+# fail 42
+# fi
+
+ dotest basic2-16 "${testcvs} ci -m 'third dive' first-dir" \
+"${CPROG} commit: Examining first-dir
+${CPROG} commit: Examining first-dir/dir1
+${CPROG} commit: Examining first-dir/dir1/dir2
+${CVSROOT_DIRNAME}/first-dir/file14,v <-- first-dir/file14
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/file6,v <-- first-dir/file6
+new revision: 1\.2; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/file7,v <-- first-dir/file7
+new revision: delete; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/file14,v <-- first-dir/dir1/file14
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/file6,v <-- first-dir/dir1/file6
+new revision: 1\.2; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/file7,v <-- first-dir/dir1/file7
+new revision: delete; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file14,v <--
first-dir/dir1/dir2/file14
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file6,v <-- first-dir/dir1/dir2/file6
+new revision: 1\.2; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/dir1/dir2/file7,v <-- first-dir/dir1/dir2/file7
+new revision: delete; previous revision: 1\.1"
+ dotest basic2-17 "${testcvs} -q update first-dir" ''
+
+ dotest basic2-18 "${testcvs} tag third-dive first-dir" \
+"${SPROG} tag: Tagging first-dir
+T first-dir/file14
+T first-dir/file6
+${SPROG} tag: Tagging first-dir/dir1
+T first-dir/dir1/file14
+T first-dir/dir1/file6
+${SPROG} tag: Tagging first-dir/dir1/dir2
+T first-dir/dir1/dir2/file14
+T first-dir/dir1/dir2/file6"
+
+ dotest basic2-19 "echo yes | ${testcvs} release -d first-dir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .first-dir.: "
+
+ # end of third dive
+ dotest_fail basic2-20 "test -d first-dir" ""
+
+ # now try some rtags
+
+ # rtag HEADS
+ dotest basic2-21 "${testcvs} rtag rtagged-by-head first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: Tagging first-dir/dir1
+${SPROG} rtag: Tagging first-dir/dir1/dir2"
+
+ dotest basic2-21b "${testcvs} co -p -r rtagged-by-head
first-dir/file6" \
+"===================================================================
+Checking out first-dir/file6
+RCS: $CVSROOT_DIRNAME/first-dir/file6,v
+VERS: 1\.2
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+file6
+file6"
+ # see what happens when val-tags is removed
+ modify_repo mv $CVSROOT_DIRNAME/CVSROOT/val-tags \
+ $CVSROOT_DIRNAME/CVSROOT/val-tags.save
+ # The output for this used to be something like:
+ # "${SPROG} checkout: cannot open CVS/Entries for reading: No
such file or directory
+ # ${SPROG} \[checkout aborted\]: no such tag \`rtagged-by-head'"
+
+ dotest basic2-21c \
+"${testcvs} co -p -r rtagged-by-head first-dir/file6" \
+"===================================================================
+Checking out first-dir/file6
+RCS: $CVSROOT_DIRNAME/first-dir/file6,v
+VERS: 1\.2
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+file6
+file6"
+ modify_repo mv $CVSROOT_DIRNAME/CVSROOT/val-tags.save \
+ $CVSROOT_DIRNAME/CVSROOT/val-tags
+
+ # tag by tag
+ dotest basic2-22 "${testcvs} rtag -r rtagged-by-head
rtagged-by-tag first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: Tagging first-dir/dir1
+${SPROG} rtag: Tagging first-dir/dir1/dir2"
+
+ # tag by revision
+ dotest basic2-23 "${testcvs} rtag -r1.1 rtagged-by-revision
first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: Tagging first-dir/dir1
+${SPROG} rtag: Tagging first-dir/dir1/dir2"
+
+ # rdiff by revision
+ dotest basic2-24 "${testcvs} rdiff -r1.1 -rrtagged-by-head
first-dir" \
+"${SPROG} rdiff: Diffing first-dir
+Index: first-dir/file6
+diff -c first-dir/file6:1\.1 first-dir/file6:1\.2
+\*\*\* first-dir/file6:1\.1 ${DATE}
+--- first-dir/file6 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ file6
+${PLUS} file6
+Index: first-dir/file7
+diff -c first-dir/file7:1\.1 first-dir/file7:removed
+\*\*\* first-dir/file7:1.1 ${DATE}
+--- first-dir/file7 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- file7
+--- 0 ----
+${SPROG} rdiff: Diffing first-dir/dir1
+Index: first-dir/dir1/file6
+diff -c first-dir/dir1/file6:1\.1 first-dir/dir1/file6:1\.2
+\*\*\* first-dir/dir1/file6:1\.1 ${DATE}
+--- first-dir/dir1/file6 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ file6
+${PLUS} file6
+Index: first-dir/dir1/file7
+diff -c first-dir/dir1/file7:1\.1 first-dir/dir1/file7:removed
+\*\*\* first-dir/dir1/file7:1\.1 ${DATE}
+--- first-dir/dir1/file7 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- file7
+--- 0 ----
+${SPROG} rdiff: Diffing first-dir/dir1/dir2
+Index: first-dir/dir1/dir2/file6
+diff -c first-dir/dir1/dir2/file6:1\.1 first-dir/dir1/dir2/file6:1\.2
+\*\*\* first-dir/dir1/dir2/file6:1\.1 ${DATE}
+--- first-dir/dir1/dir2/file6 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ file6
+${PLUS} file6
+Index: first-dir/dir1/dir2/file7
+diff -c first-dir/dir1/dir2/file7:1\.1 first-dir/dir1/dir2/file7:removed
+\*\*\* first-dir/dir1/dir2/file7:1\.1 ${DATE}
+--- first-dir/dir1/dir2/file7 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- file7
+--- 0 ----"
+ dotest basic2-24a "${testcvs} rdiff -l -r1.1 -rrtagged-by-head
first-dir" \
+"${SPROG} rdiff: Diffing first-dir
+Index: first-dir/file6
+diff -c first-dir/file6:1\.1 first-dir/file6:1\.2
+\*\*\* first-dir/file6:1\.1 ${DATE}
+--- first-dir/file6 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+--- 1,2 ----
+ file6
+${PLUS} file6
+Index: first-dir/file7
+diff -c first-dir/file7:1\.1 first-dir/file7:removed
+\*\*\* first-dir/file7:1.1 ${DATE}
+--- first-dir/file7 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- file7
+--- 0 ----"
+ # now export by rtagged-by-head and rtagged-by-tag and compare.
+ dotest basic2-25 "${testcvs} export -r rtagged-by-head -d 1dir
first-dir" \
+"${SPROG} export: Updating 1dir
+U 1dir/file14
+U 1dir/file6
+${SPROG} export: Updating 1dir/dir1
+U 1dir/dir1/file14
+U 1dir/dir1/file6
+${SPROG} export: Updating 1dir/dir1/dir2
+U 1dir/dir1/dir2/file14
+U 1dir/dir1/dir2/file6"
+ dotest_fail basic2-25a "test -d 1dir/CVS"
+ dotest_fail basic2-25b "test -d 1dir/dir1/CVS"
+ dotest_fail basic2-25c "test -d 1dir/dir1/dir2/CVS"
+
+ dotest basic2-26 "${testcvs} export -r rtagged-by-tag
first-dir" \
+"${SPROG} export: Updating first-dir
+U first-dir/file14
+U first-dir/file6
+${SPROG} export: Updating first-dir/dir1
+U first-dir/dir1/file14
+U first-dir/dir1/file6
+${SPROG} export: Updating first-dir/dir1/dir2
+U first-dir/dir1/dir2/file14
+U first-dir/dir1/dir2/file6"
+ dotest_fail basic2-26a "test -d first-dir/CVS"
+ dotest_fail basic2-26b "test -d first-dir/dir1/CVS"
+ dotest_fail basic2-26c "test -d first-dir/dir1/dir2/CVS"
+
+ dotest basic2-27 "directory_cmp 1dir first-dir"
+ rm -r 1dir first-dir
+
+ # checkout by revision vs export by rtagged-by-revision and
compare.
+ mkdir export-dir
+ dotest basic2-28 "${testcvs} export -rrtagged-by-revision -d
export-dir first-dir" \
+"${SPROG} export: Updating export-dir
+U export-dir/file14
+U export-dir/file6
+U export-dir/file7
+${SPROG} export: Updating export-dir/dir1
+U export-dir/dir1/file14
+U export-dir/dir1/file6
+U export-dir/dir1/file7
+${SPROG} export: Updating export-dir/dir1/dir2
+U export-dir/dir1/dir2/file14
+U export-dir/dir1/dir2/file6
+U export-dir/dir1/dir2/file7"
+ dotest_fail basic2-28a "test -d export-dir/CVS"
+ dotest_fail basic2-28b "test -d export-dir/dir1/CVS"
+ dotest_fail basic2-28c "test -d export-dir/dir1/dir2/CVS"
+
+ dotest basic2-29 "${testcvs} co -r1.1 first-dir" \
+"${SPROG} checkout: Updating first-dir
+U first-dir/file14
+U first-dir/file6
+U first-dir/file7
+${SPROG} checkout: Updating first-dir/dir1
+U first-dir/dir1/file14
+U first-dir/dir1/file6
+U first-dir/dir1/file7
+${SPROG} checkout: Updating first-dir/dir1/dir2
+U first-dir/dir1/dir2/file14
+U first-dir/dir1/dir2/file6
+U first-dir/dir1/dir2/file7"
+
+ # directory copies are done in an oblique way in order to avoid
a bug in sun's tmp filesystem.
+ mkdir first-dir.cpy ; (cd first-dir ; tar cf - . | (cd
../first-dir.cpy ; tar xf -))
+
+ dotest basic2-30 "directory_cmp first-dir export-dir"
+
+ # interrupt, while we've got a clean 1.1 here, let's import it
+ # into a couple of other modules.
+ cd export-dir
+ dotest_sort basic2-31 \
+"$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
+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
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/second-dir/dir1
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/second-dir/dir1/dir2"
+ cd ..
+
+ dotest basic2-32 "${testcvs} export -r HEAD second-dir" \
+"${SPROG} export: Updating second-dir
+U second-dir/file14
+U second-dir/file6
+U second-dir/file7
+${SPROG} export: Updating second-dir/dir1
+U second-dir/dir1/file14
+U second-dir/dir1/file6
+U second-dir/dir1/file7
+${SPROG} export: Updating second-dir/dir1/dir2
+U second-dir/dir1/dir2/file14
+U second-dir/dir1/dir2/file6
+U second-dir/dir1/dir2/file7"
+
+ dotest basic2-33 "directory_cmp first-dir second-dir"
+
+ rm -r second-dir
+
+ rm -r export-dir first-dir
+ mkdir first-dir
+ (cd first-dir.cpy ; tar cf - . | (cd ../first-dir ; tar xf -))
+
+ # update the top, cancelling sticky tags, retag, update other
copy, compare.
+ cd first-dir
+ dotest basic2-34 "${testcvs} update -A -l *file*" \
+"U file6
+${SPROG} update: \`file7' is no longer in the repository"
+
+ # If we don't delete the tag first, cvs won't retag it.
+ # This would appear to be a feature.
+ dotest basic2-35 "${testcvs} tag -l -d rtagged-by-revision" \
+"${SPROG} tag: Untagging \.
+D file14
+D file6"
+ dotest basic2-36 "${testcvs} tag -l rtagged-by-revision" \
+"${SPROG} tag: Tagging \.
+T file14
+T file6"
+
+ cd ..
+ mv first-dir 1dir
+ mv first-dir.cpy first-dir
+ cd first-dir
+
+ dotest basic2-37 "${testcvs} -q diff -u" ''
+
+ dotest basic2-38 "${testcvs} update" \
+"${SPROG} update: Updating .
+${SPROG} update: Updating dir1
+${SPROG} update: Updating dir1/dir2"
+
+ cd ..
+
+ #### FIXME: is this expected to work??? Need to investigate
+ #### and fix or remove the test.
+# dotest basic2-39 "directory_cmp 1dir first-dir"
+
+ rm -r 1dir first-dir
+
+ # Test the cvs history command.
+ #
+ # Just skip these in write proxy mode for now. We should only
+ # see write commands and maybe the last few reads in the
+ # secondary history file the way we currently sync, but I'm not
+ # going to try and test this yet.
+ if $proxy; then :; else
+
+ # The reason that there are two patterns rather than using
+ # \(${TESTDIR}\|<remote>\) is that we are trying to
+ # make this portable. Perhaps at some point we should
+ # ditch that notion and require GNU expr (or dejagnu or....)
+ # since it seems to be so painful.
+
+ dotest basic2-64 "${testcvs} his -x TOFWUPCGMAR -a" \
+"O [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir=
${TESTDIR}/\*
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir
== ${TESTDIR}
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir
== ${TESTDIR}
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1
== ${TESTDIR}
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1
== ${TESTDIR}
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1
== ${TESTDIR}
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2
== ${TESTDIR}
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2
== ${TESTDIR}
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1/dir2
== ${TESTDIR}
+F [0-9-]* [0-9:]* ${PLUS}0000 ${username} =first-dir=
${TESTDIR}/\*
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\]
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir
\[rtagged-by-tag:rtagged-by-head\]
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir
\[rtagged-by-revision:1\.1\]
+O [0-9-]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir
=first-dir= ${TESTDIR}/\*
+U [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir
== ${TESTDIR}/first-dir
+W [0-9-]* [0-9:]* ${PLUS}0000 ${username} file7 first-dir ==
${TESTDIR}/first-dir" \
+"O [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir =first-dir=
<remote>/\*
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6 first-dir/dir1/dir2
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file7 first-dir/dir1/dir2
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir
== <remote>
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir
== <remote>
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1
== <remote>
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1
== <remote>
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1
== <remote>
+A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file14 first-dir/dir1/dir2
== <remote>
+M [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir/dir1/dir2
== <remote>
+R [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file7 first-dir/dir1/dir2
== <remote>
+F [0-9-]* [0-9:]* ${PLUS}0000 ${username} =first-dir=
<remote>/\*
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir \[rtagged-by-head:A\]
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir
\[rtagged-by-tag:rtagged-by-head\]
+T [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir
\[rtagged-by-revision:1\.1\]
+O [0-9-]* [0-9:]* ${PLUS}0000 ${username} \[1\.1\] first-dir
=first-dir= <remote>/\*
+P [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.2 file6 first-dir
== <remote>
+W [0-9-]* [0-9:]* ${PLUS}0000 ${username} file7 first-dir ==
<remote>"
+ fi
+
+ dokeep
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ ls)
+ # Test the ls & rls commands. There are some tests of
+ # Interaction of ls, rls, and branches in branches2.
+ mkdir ls; cd ls
+ dotest ls-init-1 "$testcvs -Q co -dtop ."
+ cd top
+ dotest ls-1 "$testcvs ls CVSROOT" \
+"checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg"
+ dotest ls-2 "$testcvs ls -R" \
+"\.:
+CVSROOT
+
+CVSROOT:
+checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg"
+ # This used to cause a fatal error.
+ modify_repo mkdir $CVSROOT_DIRNAME/notcheckedout
+ dotest ls-3 "$testcvs ls -RP" \
+"\.:
+CVSROOT
+notcheckedout
+
+CVSROOT:
+checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg"
+
+ # Make sure the previous command did not create the notcheckedout
+ # directory.
+ dotest_fail ls-4 "test -d notcheckedout"
+
+ dotest ls-5 "$testcvs ls -R" \
+"\.:
+CVSROOT
+notcheckedout
+
+CVSROOT:
+checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg
+
+notcheckedout:"
+ dotest_fail ls-6 "test -d notcheckedout"
+
+ # Several test for ls -d, which shows dead revisions
+
+ # Set up the dead files
+ mkdir cemetery
+ dotest ls-d-init-1 "$testcvs -Q add cemetery"
+ cd cemetery
+ touch dead living
+ dotest ls-d-init-2 "$testcvs -Q add dead living"
+ dotest ls-d-init-3 "$testcvs -Q ci -mm dead living"
+ dotest ls-d-init-4 "$testcvs -Q tag -b branch"
+ dotest ls-d-init-5 "$testcvs -Q up -A"
+ rm dead
+ dotest ls-d-init-6 "$testcvs -Q rm dead"
+ dotest ls-d-init-7 "$testcvs -Q ci -mm dead"
+ dotest ls-d-init-8 "$testcvs -Q up -r branch"
+ rm dead
+ dotest ls-d-init-9 "$testcvs -Q rm dead"
+ dotest ls-d-init-10 "$testcvs -Q ci -mm dead"
+
+ # Possible output
+ output_living="living"
+ output_dead="dead
+living"
+
+ # The basic test is to make sure that dead revisions are shown if and
+ # only if -d is speficified (and that live revisions are always
+ # shown). The following test cases cover all combinations of these
+ # factors:
+ #
+ # + Working directory is on branch or trunk
+ # + ls or rls
+ # + implicit branch, explicit trunk, or explicit branch
+ # + -d present or absent
+
+ # Working directory on trunk
+ $testcvs -Q up -A
+
+ ## ls
+ dotest ls-d-1 "$testcvs ls" "$output_living"
+ dotest ls-d-2 "$testcvs ls -d" "$output_dead"
+
+ dotest ls-d-3 "$testcvs ls -rHEAD" "$output_living"
+ dotest ls-d-4 "$testcvs ls -drHEAD" "$output_dead"
+
+ dotest ls-d-5 "$testcvs ls -rbranch" "$output_living"
+ dotest ls-d-6 "$testcvs ls -drbranch" "$output_dead"
+
+ ## rls
+ dotest ls-d-7 "$testcvs rls cemetery" \
+"$SPROG rls: Listing module: \`cemetery'
+$output_living"
+ dotest ls-d-8 "$testcvs rls -d cemetery" \
+"$SPROG rls: Listing module: \`cemetery'
+$output_dead"
+
+ dotest ls-d-9 "$testcvs -q rls -rHEAD cemetery" "$output_living"
+ dotest ls-d-10 "$testcvs -q rls -drHEAD cemetery" "$output_dead"
+
+ dotest ls-d-11 "$testcvs -q rls -rbranch cemetery" "$output_living"
+ dotest ls-d-12 "$testcvs -q rls -drbranch cemetery" "$output_dead"
+
+ # Working directory on branch
+ $testcvs -Q up -r branch
+
+ ## ls
+ dotest ls-d-13 "$testcvs ls" "$output_living"
+ dotest ls-d-14 "$testcvs ls -d" "$output_dead"
+
+ dotest ls-d-15 "$testcvs ls -r HEAD" "$output_living"
+ dotest ls-d-16 "$testcvs ls -d -r HEAD" "$output_dead"
+
+ dotest ls-d-17 "$testcvs ls -r branch" "$output_living"
+ dotest ls-d-18 "$testcvs ls -d -r branch" "$output_dead"
+
+ ## rls
+ dotest ls-d-19 "$testcvs -q rls cemetery" "$output_living"
+ dotest ls-d-20 "$testcvs -q rls -d cemetery" "$output_dead"
+
+ dotest ls-d-21 "$testcvs -q rls -rHEAD cemetery" "$output_living"
+ dotest ls-d-22 "$testcvs -q rls -drHEAD cemetery" "$output_dead"
+
+ dotest ls-d-23 "$testcvs -q rls -rbranch cemetery" "$output_living"
+ dotest ls-d-24 "$testcvs -q rls -drbranch cemetery" "$output_dead"
+
+ # Some tests to cover specifying a file name as an option
+ # Combinations of factors:
+ #
+ # + file in CVS/Entries or not
+ # + current directory or subdirectory
+ # + file dead or not
+
+ # Switch back to the trunk
+ $testcvs -Q up -A
+
+ ## file in CVS/Entries
+ dotest ls-filename-1 "$testcvs ls dead"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-filename-2 "$testcvs ls living" "living"
+
+ cd ..
+ dotest ls-filename-3 "$testcvs ls cemetery/dead"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-filename-4 "$testcvs ls cemetery/living" "cemetery/living"
+ cd cemetery
+
+ ## file not in CVS/Entries
+ echo D > CVS/Entries
+
+ dotest ls-filename-5 "$testcvs ls dead"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-filename-6 "$testcvs ls living" "living"
+
+ cd ..
+ dotest ls-filename-7 "$testcvs ls cemetery/dead"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-filename-8 "$testcvs ls cemetery/living" "cemetery/living"
+
+ cd cemetery
+
+ # Test the -D date option to cvs ls
+
+ # try and list a file before it's created, during an old revision, in
+ # a period when it was dead and in the future
+ time_prebirth=`date '+%Y-%m-%d %H:%M:%S'` ; sleep 1
+ touch dated
+ dotest ls-D-init-1 "$testcvs -Q add dated"
+ dotest ls-D-init-2 "$testcvs -Q ci -mm dated"
+ time_newborn=`date '+%Y-%m-%d %H:%M:%S'` ; sleep 1
+ echo mm >> dated
+ dotest ls-D-init-2 "$testcvs -Q ci -mm dated"
+ time_predeath=`date '+%Y-%m-%d %H:%M:%S'` ; sleep 1
+ rm dated
+ dotest ls-D-init-3 "$testcvs -Q rm dated"
+ dotest ls-D-init-4 "$testcvs -Q ci -mm dated"
+ time_postdeath=`date '+%Y-%m-%d %H:%M:%S'`
+
+ dotest ls-D-1 "$testcvs ls -D '$time_prebirth' -e dated"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-D-2 "$testcvs ls -D '$time_newborn' -e dated" \
+"/dated/1\.1/.*"
+
+ # ls'ing a file that already exists once caused an assertion failure.
+ dotest ls-D-3 "$testcvs ls -D '$time_predeath' -e dated" \
+"/dated/1.2/.*"
+
+ dotest ls-D-4 "$testcvs ls -D '$time_postdeath' -e dated"
+
+ dokeep
+ cd ../../..
+ rm -r ls
+ modify_repo rm -rf $CVSROOT_DIRNAME/notcheckedout \
+ $CVSROOT_DIRNAME/cemetery
+ unset output_living output_dead
+ ;;
+
+
+
+ parseroot)
+ mkdir 1; cd 1
+ # Test odd cases involving CVSROOT. At the moment, that means we
+ # are testing roots with '/'s on the end, which CVS should parse off.
+ CVSROOT_save=${CVSROOT}
+ CVSROOT="${CVSROOT}/////"
+ dotest parseroot-1 "${testcvs} -q co CVSROOT/modules" \
+"U CVSROOT/modules"
+ dotest parseroot-2 "${testcvs} -q ci -fmnull-change CVSROOT/modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.2; previous revision: 1\.1
+$SPROG commit: Rebuilding administrative file database"
+
+ if $remote; then
+ # I only test these when testing remote in case CVS was compiled
+ # without client support.
+
+ # logout does not try to contact the server.
+ CVSROOT=":pserver;proxy=localhost;proxyport=8080:localhost/dev/null"
+ dotest parseroot-3r "$testcvs -d'$CVSROOT' logout" \
+"Logging out of :pserver:address@hidden:2401/dev/null
+$CPROG logout: warning: failed to open $HOME/\.cvspass for reading: No such
file or directory
+$CPROG logout: Entry not found."
+ CVSROOT=":pserver;proxyport=8080:localhost/dev/null"
+ dotest_fail parseroot-4r "$testcvs -d'$CVSROOT' logout" \
+"$CPROG logout: Proxy port specified in CVSROOT without proxy host\.
+$CPROG \[logout aborted\]: Bad CVSROOT:
\`:pserver;proxyport=8080:localhost/dev/null'\."
+ CVSROOT=":pserver;optionnoarg:localhost/dev/null"
+ dotest_fail parseroot-5r "$testcvs -d'$CVSROOT' logout" \
+"$CPROG logout: Option (\`optionnoarg') has no argument in CVSROOT\.
+$CPROG \[logout aborted\]: Bad CVSROOT:
\`:pserver;optionnoarg:localhost/dev/null'\."
+ CVSROOT=":pserver;notanoption=anything:localhost/dev/null"
+ dotest_fail parseroot-6r "$testcvs -d'$CVSROOT' logout" \
+"$CPROG logout: Unknown option (\`notanoption') in CVSROOT\.
+$CPROG \[logout aborted\]: Bad CVSROOT:
\`:pserver;notanoption=anything:localhost/dev/null'\."
+ CVSROOT=":local;proxy=localhost:/dev/null"
+ dotest_fail parseroot-7r "$testcvs -d'$CVSROOT' logout" \
+"$CPROG logout: CVSROOT proxy specification is only valid for gserver and
+$CPROG logout: pserver connection methods\.
+$CPROG \[logout aborted\]: Bad CVSROOT: \`:local;proxy=localhost:/dev/null'\."
+ CVSROOT="::address@hidden@test.org:/cvs"
+ dotest_fail parseroot-8r "$testcvs -d'$CVSROOT' co test" \
+"$CPROG checkout: Unknown method (\`') in CVSROOT\.
+$CPROG \[checkout aborted\]: Bad CVSROOT: \`$CVSROOT'\."
+ fi
+
+ dokeep
+
+ # Clean up
+ CVSROOT=$CVSROOT_save
+ cd ..
+ rm -r 1
+ ;;
+
+
+
+ files)
+ # Test of how we specify files on the command line
+ # (recurse.c and that sort of thing). Vaguely similar to
+ # tests like basic* and deep. See modules and such tests
+ # for what happens when we throw in modules and co -d, &c.
+
+ # This particular test is fairly carefully crafted, to spot
+ # one particular issue with remote.
+ mkdir 1; cd 1
+ dotest files-1 "${testcvs} -q co -l ." ""
+ mkdir first-dir
+ dotest files-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch tfile
+ dotest files-3 "${testcvs} add tfile" \
+"${SPROG} add: scheduling file .tfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest files-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/tfile,v <-- tfile
+initial revision: 1\.1"
+ dotest files-5 "${testcvs} -q tag -b C" "T tfile"
+ dotest files-6 "${testcvs} -q update -r C" ""
+ mkdir dir
+ dotest files-7 "${testcvs} add dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir added to the repository
+--> Using per-directory sticky tag .C'"
+ cd dir
+ touch .file
+ dotest files-7b "${testcvs} add .file" \
+"${SPROG} add: scheduling file .\.file' for addition on branch .C.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ mkdir sdir
+ dotest files-7c "${testcvs} add sdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir/sdir added to the repository
+--> Using per-directory sticky tag .C'"
+ cd sdir
+ mkdir ssdir
+ dotest files-8 "${testcvs} add ssdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir/sdir/ssdir added to the repository
+--> Using per-directory sticky tag .C'"
+ cd ssdir
+ touch .file
+ dotest files-9 "${testcvs} add .file" \
+"${SPROG} add: scheduling file .\.file' for addition on branch .C.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ../..
+ dotest files-10 "${testcvs} -q ci -m test" \
+"$CVSROOT_DIRNAME/first-dir/dir/Attic/\.file,v <-- \.file
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir/sdir/ssdir/Attic/\.file,v <--
sdir/ssdir/\.file
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest files-11 \
+"${testcvs} commit -m test -f ./.file ./sdir/ssdir/.file" \
+"${CVSROOT_DIRNAME}/first-dir/dir/Attic/\.file,v <-- \.file
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1
+${CVSROOT_DIRNAME}/first-dir/dir/sdir/ssdir/Attic/\.file,v <--
\./sdir/ssdir/\.file
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+ if $remote; then
+ # FIXCVS:
+ # This is a bug, looks like that toplevel_repos cruft in
+ # client.c is coming back to haunt us.
+ # May want to think about the whole issue, toplevel_repos
+ # has always been crufty and trying to patch it up again
+ # might be a mistake.
+ dotest files-12r \
+"$testcvs commit -f -m test ./sdir/ssdir/.file ./.file" \
+"$CVSROOT_DIRNAME/first-dir/dir/sdir/ssdir/Attic/\.file,v <--
\./sdir/ssdir/\.file
+new revision: 1\.1\.2\.3; previous revision: 1\.1\.2\.2"
+
+ # Sync up the version numbers so that the rest of the
+ # tests don't need to expect different numbers based
+ # local or remote.
+ dotest files-12rworkaround \
+"$testcvs commit -f -m test .file" \
+"$CVSROOT_DIRNAME/first-dir/dir/Attic/\.file,v <-- \.file
+new revision: 1\.1\.2\.3; previous revision: 1\.1\.2\.2"
+ else
+ dotest files-12 \
+"${testcvs} commit -f -m test ./sdir/ssdir/.file ./.file" \
+"${CVSROOT_DIRNAME}/first-dir/dir/sdir/ssdir/Attic/\.file,v <--
\./sdir/ssdir/\.file
+new revision: 1\.1\.2\.3; previous revision: 1\.1\.2\.2
+${CVSROOT_DIRNAME}/first-dir/dir/Attic/\.file,v <-- \.file
+new revision: 1\.1\.2\.3; previous revision: 1\.1\.2\.2"
+ fi
+ dotest files-13 \
+"${testcvs} commit -fmtest ./sdir/../sdir/ssdir/..///ssdir/.file" \
+"${CVSROOT_DIRNAME}/first-dir/dir/sdir/ssdir/Attic/\.file,v <--
\./sdir/\.\./sdir/ssdir/\.\.///ssdir/\.file
+new revision: 1\.1\.2\.4; previous revision: 1\.1\.2\.3"
+ dotest files-14 \
+"${testcvs} commit -fmtest ../../first-dir/dir/.file" \
+"${CVSROOT_DIRNAME}/first-dir/dir/Attic/\.file,v <--
\.\./\.\./first-dir/dir/\.file
+new revision: 1\.1\.2\.4; previous revision: 1\.1\.2\.3"
+
+ dokeep
+ cd ../../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ spacefiles)
+ # More filename tests, in particular spaces in file names.
+ # (it might be better to just change a few of the names in
+ # basica or some other test instead, always good to keep the
+ # testsuite concise).
+
+ mkdir 1; cd 1
+ dotest spacefiles-1 "${testcvs} -q co -l ." ""
+ touch ./-c
+ dotest spacefiles-2 "${testcvs} add -- -c" \
+"${SPROG} add: scheduling file .-c. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest spacefiles-3 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/-c,v <-- -c
+initial revision: 1\.1"
+ mkdir 'first dir'
+ dotest spacefiles-4 "${testcvs} add 'first dir'" \
+"Directory ${CVSROOT_DIRNAME}/first dir added to the repository"
+ mkdir ./-b
+ dotest spacefiles-5 "${testcvs} add -- -b" \
+"Directory ${CVSROOT_DIRNAME}/-b added to the repository"
+ cd 'first dir'
+ touch 'a file'
+ dotest spacefiles-6 "${testcvs} add 'a file'" \
+"${SPROG} add: scheduling file .a file. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest spacefiles-7 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first dir/a file,v <-- a file
+initial revision: 1\.1"
+ dotest spacefiles-8 "${testcvs} -q tag new-tag" "T a file"
+ cd ../..
+
+ mkdir 2; cd 2
+ dotest spacefiles-10 "${testcvs} co -- -b" \
+"${SPROG} checkout: Updating -b"
+ dotest spacefiles-11 "${testcvs} -q co -- -c" "U \./-c"
+ rm ./-c
+ dotest spacefiles-13 "${testcvs} -q co 'first dir'" \
+"U first dir/a file"
+ cd ..
+
+ mkdir 3; cd 3
+ dotest spacefiles-14 "${testcvs} -q co 'first dir/a file'" \
+"U first dir/a file"
+ cd ..
+
+ rm -r 1 2 3
+ modify_repo rm -rf "'$CVSROOT_DIRNAME/first dir'" \
+ $CVSROOT_DIRNAME/-b $CVSROOT_DIRNAME/-c,v
+ ;;
+
+
+
+ commit-readonly)
+ mkdir 1; cd 1
+ module=x
+
+ : > junk
+ dotest commit-readonly-1 "$testcvs -Q import -m . $module X Y" ''
+ dotest commit-readonly-2 "$testcvs -Q co $module" ''
+ cd $module
+
+ file=m
+
+ # Include an rcs keyword to be expanded.
+ echo '$Id''$' > $file
+
+ dotest commit-readonly-3 "$testcvs add $file" \
+"$SPROG add: scheduling file .$file. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest commit-readonly-4 "$testcvs -Q ci -m . $file"
+
+ echo line2 >> $file
+ # Make the file read-only.
+ chmod a-w $file
+
+ dotest commit-readonly-5 "$testcvs -Q ci -m . $file"
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/"$module"
+ ;;
+
+
+
+ status)
+ # This tests for a bug in the status command which failed to
+ # notice resolved conflicts.
+ mkdir status; cd status
+ dotest status-init-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest status-init-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo a line >tfile
+ dotest status-init-3 "${testcvs} add tfile" \
+"${SPROG} add: scheduling file .tfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest status-init-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/tfile,v <-- tfile
+initial revision: 1\.1"
+ cd ..
+ dotest status-init-5 "${testcvs} -q co -dsecond-dir first-dir" \
+"U second-dir/tfile"
+ cd second-dir
+ echo some junk >>tfile
+ dotest status-init-6 "${testcvs} -q ci -maline" \
+"$CVSROOT_DIRNAME/first-dir/tfile,v <-- tfile
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../first-dir
+ echo force a conflict >>tfile
+ dotest status-init-7 "${testcvs} -q up" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/tfile,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into tfile
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in tfile
+C tfile"
+
+ # Now note our status
+ dotest status-1 "${testcvs} status tfile" \
+"===================================================================
+File: tfile Status: Unresolved Conflict
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/tfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # touch the file, leaving conflict markers in place
+ # and note our status
+ touch tfile
+ dotest status-2 "${testcvs} status tfile" \
+"===================================================================
+File: tfile Status: File had conflicts on merge
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/tfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # resolve the conflict
+ echo resolution >tfile
+ dotest status-3 "${testcvs} status tfile" \
+"===================================================================
+File: tfile Status: Locally Modified
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/tfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # Check that there are no problems just using CVS/Root too.
+ save_CVSROOT=$CVSROOT
+ unset CVSROOT
+ dotest status-3a "${testcvs} status tfile" \
+"===================================================================
+File: tfile Status: Locally Modified
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/tfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ CVSROOT=$save_CVSROOT
+ export CVSROOT
+
+ # FIXCVS:
+ # Update is supposed to re-Register() the file when it
+ # finds resolved conflicts:
+ dotest status-4 "grep 'Result of merge' CVS/Entries" \
+"/tfile/1\.2/Result of merge${PLUS}[a-zA-Z0-9 :]*//"
+
+ cd ..
+ mkdir fourth-dir
+ dotest status-init-8 "$testcvs add fourth-dir" \
+"Directory $CVSROOT_DIRNAME/fourth-dir added to the repository"
+ cd fourth-dir
+ echo yet another line >t3file
+ dotest status-init-9 "$testcvs add t3file" \
+"$SPROG add: scheduling file .t3file. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest status-init-10 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/fourth-dir/t3file,v <-- t3file
+initial revision: 1\.1"
+ cd ../first-dir
+ mkdir third-dir
+ dotest status-init-11 "$testcvs add third-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir/third-dir added to the repository"
+ cd third-dir
+ echo another line >t2file
+ dotest status-init-12 "$testcvs add t2file" \
+"$SPROG add: scheduling file .t2file. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest status-init-13 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/third-dir/t2file,v <-- t2file
+initial revision: 1\.1"
+ dotest status-5 "$testcvs status ../tfile" \
+"===================================================================
+File: tfile Status: Locally Modified
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 $CVSROOT_DIRNAME/first-dir/tfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest status-6 "$testcvs status ../../fourth-dir/t3file" \
+"===================================================================
+File: t3file Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/fourth-dir/t3file,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ dokeep
+ cd ../../..
+ rm -rf status
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/fourth-dir
+ ;;
+
+
+
+ commit-readonlyfs)
+ mkdir 1; cd 1
+ module=x
+ : > junk
+ dotest commit-readonlyfs-1 "${testcvs} -Q import -m . $module X Y" ''
+ if $remote; then
+ dotest_fail commit-readonlyfs-2r1 "${testcvs} -Q -R co $module" \
+"${CPROG} \[checkout aborted\]: Read-only repository feature unavailable with
remote roots (cvsroot = ${CVSROOT_DIRNAME})"
+ dotest commit-readonlyfs-2r2 "${testcvs} -Q co $module" ''
+ else
+ dotest commit-readonlyfs-2 "${testcvs} -Q -R co $module" ''
+ rm -rf $module
+ dotest commit-readonlyfs-2r3 "${testcvs} -q -R co $module" \
+"U $module/junk"
+ rm -rf $module
+ dotest commit-readonlyfs-2r4 "${testcvs} -R co $module" \
+"${SPROG}: WARNING: Read-only repository access mode selected via \`cvs -R'\.
+Using this option to access a repository which some users write to may
+cause intermittent sandbox corruption\.
+${SPROG} checkout: Updating $module
+U $module/junk"
+ fi
+ cd $module
+ echo test > junk
+ if $remote; then
+ dotest_fail commit-readonlyfs-3r "${testcvs} -Q -R ci -m. junk" \
+"${SPROG} \[commit aborted\]: Read-only repository feature unavailable with
remote roots (cvsroot = ${CVSROOT_DIRNAME})"
+ else
+ dotest_fail commit-readonlyfs-3 "${testcvs} -Q -R ci -m. junk" \
+"${SPROG} commit: write lock failed\.
+WARNING: Read-only repository access mode selected via \`cvs -R'\.
+Attempting to write to a read-only filesystem is not allowed\.
+${SPROG} \[commit aborted\]: lock failed - giving up"
+ fi
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/"$module"
+ ;;
+
+
+
+ rdiff)
+ # Test rdiff
+ # XXX for now this is just the most essential test...
+ cd ${TESTDIR}
+
+ mkdir testimport
+ cd testimport
+ echo '$''Id$' > foo
+ echo '$''Name$' >> foo
+ echo '$''Id$' > bar
+ echo '$''Name$' >> bar
+ dotest_sort rdiff-1 \
+ "${testcvs} import -I ! -m test-import-with-keyword trdiff
TRDIFF T1" \
+'
+
+N trdiff/bar
+N trdiff/foo
+No conflicts created by this import'
+ dotest rdiff-2 \
+ "${testcvs} co -ko trdiff" \
+"${SPROG} checkout: Updating trdiff
+U trdiff/bar
+U trdiff/foo"
+ cd trdiff
+ echo something >> foo
+ dotest rdiff-3 \
+ "${testcvs} ci -m added-something foo" \
+"${CVSROOT_DIRNAME}/trdiff/foo,v <-- foo
+new revision: 1\.2; previous revision: 1\.1"
+ echo '#ident "@(#)trdiff:$''Name$:$''Id$"' > new
+ echo "new file" >> new
+ dotest rdiff-4 \
+ "${testcvs} add -m new-file-description new" \
+"${SPROG} add: scheduling file \`new' for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rdiff-5 \
+ "${testcvs} commit -m added-new-file new" \
+"${CVSROOT_DIRNAME}/trdiff/new,v <-- new
+initial revision: 1\.1"
+ dotest rdiff-6 \
+ "${testcvs} tag local-v0" \
+"${SPROG} tag: Tagging .
+T bar
+T foo
+T new"
+ dotest rdiff-7 \
+ "${testcvs} status -v foo" \
+"===================================================================
+File: foo Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/trdiff/foo,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -ko
+
+ Existing Tags:
+ local-v0 (revision: 1\.2)
+ T1 (revision: 1\.1\.1\.1)
+ TRDIFF (branch: 1\.1\.1)"
+
+ cd ..
+ rm -r trdiff
+
+ dotest rdiff-8 \
+ "${testcvs} rdiff -r T1 -r local-v0 trdiff" \
+"${SPROG}"' rdiff: Diffing trdiff
+Index: trdiff/foo
+diff -c trdiff/foo:1\.1\.1\.1 trdiff/foo:1\.2
+\*\*\* trdiff/foo:1\.1\.1\.1 '"${DATE}"'
+--- trdiff/foo '"${DATE}"'
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1,2 \*\*\*\*
+! \$''Id: foo,v 1\.1\.1\.1 [0-9/]* [0-9:]* '"${username}"' Exp \$
+! \$''Name: T1 \$
+--- 1,3 ----
+! \$''Id: foo,v 1\.2 [0-9/]* [0-9:]* '"${username}"' Exp \$
+! \$''Name: local-v0 \$
+! something
+Index: trdiff/new
+diff -c /dev/null trdiff/new:1\.1
+\*\*\* /dev/null '"${DATE}"'
+--- trdiff/new '"${DATE}"'
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1,2 ----
+'"${PLUS}"' #ident "@(#)trdiff:\$''Name: local-v0 \$:\$''Id: new,v 1\.1
[0-9/]* [0-9:]* '"${username}"' Exp \$"
+'"${PLUS}"' new file'
+
+ dokeep
+ cd ..
+ rm -r testimport
+ modify_repo rm -rf $CVSROOT_DIRNAME/trdiff
+ ;;
+
+
+
+ rdiff-short)
+ # Test that the short patch behaves as expected
+ # 1) Added file.
+ # 2) Removed file.
+ # 3) Different revision number with no difference.
+ # 4) Different revision number with changes.
+ # 5) Against trunk.
+ # 6) Same revision number (no difference).
+ mkdir rdiff-short; cd rdiff-short
+ mkdir abc
+ dotest rdiff-short-init-1 \
+"${testcvs} -q import -I ! -m initial-import abc vendor initial" \
+'
+No conflicts created by this import'
+
+ dotest rdiff-short-init-2 "${testcvs} -q get abc" ''
+ cd abc
+ echo "abc" >file1.txt
+ dotest rdiff-short-init-3 "${testcvs} add file1.txt" \
+"${SPROG} add: scheduling file .file1\.txt' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest rdiff-short-init-4 \
+"${testcvs} commit -madd-file1 file1.txt" \
+"${CVSROOT_DIRNAME}/abc/file1\.txt,v <-- file1\.txt
+initial revision: 1\.1"
+ echo def >>file1.txt
+ dotest rdiff-short-init-5 \
+"${testcvs} commit -mchange-file1 file1.txt" \
+"${CVSROOT_DIRNAME}/abc/file1\.txt,v <-- file1\.txt
+new revision: 1\.2; previous revision: 1\.1"
+ echo "abc" >file1.txt
+ dotest rdiff-short-init-6 \
+"${testcvs} commit -mrestore-file1-rev1 file1.txt" \
+"${CVSROOT_DIRNAME}/abc/file1\.txt,v <-- file1\.txt
+new revision: 1\.3; previous revision: 1\.2"
+ dotest rdiff-short-init-7 \
+"${testcvs} tag -r 1.1 tag1 file1.txt" \
+"T file1\.txt"
+ dotest rdiff-short-init-8 \
+"${testcvs} tag -r 1.2 tag2 file1.txt" \
+"T file1\.txt"
+ dotest rdiff-short-init-9 \
+"${testcvs} tag -r 1.3 tag3 file1.txt" \
+"T file1\.txt"
+ echo "abc" >file2.txt
+ dotest rdiff-short-init-10 \
+"${testcvs} add file2.txt" \
+"${SPROG} add: scheduling file .file2\.txt' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest rdiff-add-remove-nodiff-init-11 \
+"${testcvs} commit -madd-file2 file2.txt" \
+"${CVSROOT_DIRNAME}/abc/file2\.txt,v <-- file2\.txt
+initial revision: 1\.1"
+ dotest rdiff-short-init-12 \
+"${testcvs} tag -r 1.1 tag4 file2.txt" \
+"T file2\.txt"
+ dotest rdiff-short-init-13 \
+"${testcvs} tag -r 1.1 tag5 file2.txt" \
+"T file2\.txt"
+ cd ../..
+ rm -fr rdiff-short
+
+ # 3) Different revision number with no difference.
+ dotest rdiff-short-no-real-change \
+"${testcvs} -q rdiff -s -r tag1 -r tag3 abc"
+
+ # 4) Different revision number with changes.
+ dotest rdiff-short-real-change \
+"${testcvs} -q rdiff -s -r tag1 -r tag2 abc" \
+'File abc/file1.txt changed from revision 1\.1 to 1\.2'
+
+ # 1) Added file.
+ # 2) Removed file.
+ dotest_sort rdiff-short-remove-add \
+"${testcvs} -q rdiff -s -r tag2 -r tag4 abc" \
+'File abc/file1\.txt is removed; tag2 revision 1\.2
+File abc/file2\.txt is new; tag4 revision 1\.1'
+
+ # 6) Same revision number (no difference).
+ dotest rdiff-short-no-change \
+"${testcvs} -q rdiff -s -r tag4 -r tag5 abc"
+
+ # 5) Against trunk.
+ # Check that the messages change when we diff against the trunk
+ # rather than a tag or date.
+ dotest rdiff-short-against-trunk-1 \
+"${testcvs} -q rdiff -s -rtag4 abc" \
+"File abc/file1\.txt is new; current revision 1\.3"
+
+ dotest rdiff-short-against-trunk-2 \
+"${testcvs} -q rdiff -s -rtag2 abc" \
+"File abc/file1\.txt changed from revision 1\.2 to 1\.3
+File abc/file2\.txt is new; current revision 1\.1"
+
+ modify_repo rm -rf $CVSROOT_DIRNAME/abc
+ ;;
+
+
+
+ rdiff2)
+ # Test for the segv problem reported by James Cribb
+ # Somewhere to work
+ mkdir rdiff2; cd rdiff2
+ # Create a module "m" with files "foo" and "d/bar"
+ mkdir m; cd m
+ echo foo >foo
+ mkdir d
+ echo bar >d/bar
+ dotest_sort rdiff2-1 \
+"${testcvs} -q import -I ! -m initial-import m vendor initial" \
+'
+
+N m/d/bar
+N m/foo
+No conflicts created by this import'
+
+ cd ..
+ rm -r m
+
+ # Remove "foo"
+ dotest rdiff2-2 "${testcvs} get m" \
+"${SPROG} checkout: Updating m
+U m/foo
+${SPROG} checkout: Updating m/d
+U m/d/bar"
+ cd m
+ dotest rdiff2-3 "${testcvs} rm -f foo" \
+"${SPROG} remove: scheduling .foo. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+
+ dotest rdiff2-4 "${testcvs} commit -m Removed foo" \
+"${CVSROOT_DIRNAME}/m/foo,v <-- foo
+new revision: delete; previous revision: 1\.1\.1\.1"
+
+ # Modify "d/bar"
+ echo foo >d/bar
+ dotest rdiff2-5 "${testcvs} commit -m Changed d/bar" \
+"${CVSROOT_DIRNAME}/m/d/bar,v <-- d/bar
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Crash before showing d/bar diffs
+ dotest_fail rdiff2-6 "${testcvs} rdiff -t m" \
+"${SPROG} rdiff: Diffing m
+${SPROG} rdiff: Diffing m/d
+Index: m/d/bar
+diff -c m/d/bar:1\.1\.1\.1 m/d/bar:1\.2
+\*\*\* m/d/bar:1\.1\.1\.1 ${DATE}
+--- m/d/bar ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! bar
+--- 1 ----
+! foo"
+
+ dokeep
+ cd ../..
+ rm -rf rdiff2
+ modify_repo rm -rf $CVSROOT_DIRNAME/m
+ ;;
+
+
+
+ diff)
+ # Various tests specific to the "cvs diff" command.
+ # Related tests:
+ # death2: -N
+ # rcslib: cvs diff and $Name.
+ # rdiff: cvs rdiff.
+ # diffmerge*: nuts and bolts (stuff within diff library)
+ mkdir 1; cd 1
+ dotest diff-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest diff-2 "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+
+ # diff is anomalous. Most CVS commands print the "nothing
+ # known" message (or worse yet, no message in some cases) but
+ # diff says "I know nothing". Shrug.
+ dotest_fail diff-3 "${testcvs} diff xyzpdq" \
+"${SPROG} diff: I know nothing about xyzpdq"
+ touch abc
+ dotest diff-4 "${testcvs} add abc" \
+"${SPROG} add: scheduling file .abc. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest diff-5 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+initial revision: 1\.1"
+ echo "extern int gethostname ();" >abc
+ dotest diff-6 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+new revision: 1\.2; previous revision: 1\.1"
+ echo "#include <winsock.h>" >abc
+ # check the behavior of the --ifdef=MACRO option
+ dotest_fail diff-7 "${testcvs} -q diff --ifdef=HAVE_WINSOCK_H" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.2
+diff --ifdef HAVE_WINSOCK_H -r1\.2 abc
+#ifndef HAVE_WINSOCK_H
+extern int gethostname ();
+#else /\* HAVE_WINSOCK_H \*/
+#include <winsock\.h>
+#endif /\* HAVE_WINSOCK_H \*/"
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r 1
+ ;;
+
+
+
+ diffnl)
+ # Test handling of 'cvs diff' of files without newlines
+ mkdir 1; cd 1
+ dotest diffnl-000 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest diffnl-001 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ ${AWK} 'BEGIN {printf("one\ntwo\nthree\nfour\nfive\nsix")}'
</dev/null >abc
+ dotest diffnl-002 "${testcvs} add abc" \
+"${SPROG} add: scheduling file .abc. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest diffnl-003 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+initial revision: 1\.1"
+
+ # change to line near EOF
+ ${AWK} 'BEGIN {printf("one\ntwo\nthree\nfour\nsix")}' </dev/null >abc
+ dotest_fail diffnl-100 "${testcvs} diff abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.1
+diff -r1\.1 abc
+5d4
+< five"
+ dotest_fail diffnl-101 "${testcvs} diff -u abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.1
+diff -u -r1\.1 abc
+--- abc ${RFCDATE} 1\.1
++++ abc ${RFCDATE}
+@@ -2,5 +2,4 @@
+ two
+ three
+ four
+-five
+ six
+\\\\ No newline at end of file"
+ dotest diffnl-102 "${testcvs} -q ci -mtest abc" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Change to last line
+ ${AWK} 'BEGIN {printf("one\ntwo\nthree\nfour\nseven")}' </dev/null
>abc
+ dotest_fail diffnl-200 "${testcvs} diff abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.2
+diff -r1\.2 abc
+5c5
+< six
+\\\\ No newline at end of file
+---
+> seven
+\\\\ No newline at end of file"
+ dotest_fail diffnl-201 "${testcvs} diff -u abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.2
+diff -u -r1\.2 abc
+--- abc ${RFCDATE} 1\.2
++++ abc ${RFCDATE}
+@@ -2,4 +2,4 @@
+ two
+ three
+ four
+-six
+\\\\ No newline at end of file
++seven
+\\\\ No newline at end of file"
+ dotest diffnl-202 "${testcvs} ci -mtest abc" \
+"${CVSROOT_DIRNAME}/first-dir/abc,v <-- abc
+new revision: 1\.3; previous revision: 1\.2"
+
+ # Addition of newline
+ echo "one
+two
+three
+four
+seven" > abc
+ dotest_fail diffnl-300 "${testcvs} diff abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.3
+diff -r1\.3 abc
+5c5
+< seven
+\\\\ No newline at end of file
+---
+> seven"
+ dotest_fail diffnl-301 "${testcvs} diff -u abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.3
+diff -u -r1\.3 abc
+--- abc ${RFCDATE} 1\.3
++++ abc ${RFCDATE}
+@@ -2,4 +2,4 @@
+ two
+ three
+ four
+-seven
+\\\\ No newline at end of file
++seven"
+ dotest diffnl-302 "${testcvs} ci -mtest abc" \
+"${CVSROOT_DIRNAME}/first-dir/abc,v <-- abc
+new revision: 1\.4; previous revision: 1\.3"
+
+ # Removal of newline
+ ${AWK} 'BEGIN {printf("one\ntwo\nthree\nfour\nseven")}' </dev/null
>abc
+ dotest_fail diffnl-400 "${testcvs} diff abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.4
+diff -r1\.4 abc
+5c5
+< seven
+---
+> seven
+\\\\ No newline at end of file"
+ dotest_fail diffnl-401 "${testcvs} diff -u abc" \
+"Index: abc
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/abc,v
+retrieving revision 1\.4
+diff -u -r1\.4 abc
+--- abc ${RFCDATE} 1\.4
++++ abc ${RFCDATE}
+@@ -2,4 +2,4 @@
+ two
+ three
+ four
+-seven
++seven
+\\\\ No newline at end of file"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ death)
+ # next dive. test death support.
+
+ # NOTE: this section has reached the size and
+ # complexity where it is getting to be a good idea to
+ # add new death support tests to a new section rather
+ # than continuing to piggyback them onto the tests here.
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest death-init-1 "$testcvs -Q co first-dir"
+
+ cd first-dir
+
+ # Create a directory with only dead files, to make sure CVS
+ # doesn't get confused by it.
+ mkdir subdir
+ dotest 65a0 "${testcvs} add subdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir added to the repository"
+ cd subdir
+ echo file in subdir >sfile
+ dotest 65a1 "${testcvs} add sfile" \
+"${SPROG}"' add: scheduling file `sfile'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest 65a2 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/subdir/sfile,v <-- sfile
+initial revision: 1\.1"
+ rm sfile
+ dotest 65a3 "${testcvs} rm sfile" \
+"${SPROG}"' remove: scheduling `sfile'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove this file permanently'
+ dotest 65a4 "${testcvs} -q ci -m remove-it" \
+"$CVSROOT_DIRNAME/first-dir/subdir/sfile,v <-- sfile
+new revision: delete; previous revision: 1\.1"
+ cd ..
+ dotest 65a5 "${testcvs} -q update -P" ''
+ dotest_fail 65a6 "test -d subdir" ''
+
+ # add a file.
+ touch file1
+ if ${CVS} add file1 2>> ${LOGFILE}; then
+ pass 66
+ else
+ fail 66
+ fi
+
+ # commit
+ if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
+ pass 67
+ else
+ fail 67
+ fi
+
+ # remove
+ rm file1
+ if ${CVS} rm file1 2>> ${LOGFILE}; then
+ pass 68
+ else
+ fail 68
+ fi
+
+ # commit
+ if ${CVS} ci -m test >>${LOGFILE} ; then
+ pass 69
+ else
+ fail 69
+ fi
+
+ dotest_fail 69a0 "test -f file1" ''
+ # get the old contents of file1 back
+ if ${testcvs} update -p -r 1.1 file1 >file1 2>>${LOGFILE}; then
+ pass 69a1
+ else
+ fail 69a1
+ fi
+ dotest 69a2 "cat file1" ''
+
+ # create second file
+ touch file2
+ if ${CVS} add file1 file2 2>> ${LOGFILE}; then
+ pass 70
+ else
+ fail 70
+ fi
+
+ # commit
+ if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
+ pass 71
+ else
+ fail 71
+ fi
+
+ # log
+ if ${CVS} log file1 >> ${LOGFILE}; then
+ pass 72
+ else
+ fail 72
+ fi
+
+ # file4 will be dead at the time of branching and stay dead.
+ echo file4 > file4
+ dotest death-file4-add "${testcvs} add file4" \
+"${SPROG}"' add: scheduling file `file4'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest death-file4-ciadd "${testcvs} -q ci -m add file4" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+initial revision: 1\.1"
+ rm file4
+ dotest death-file4-rm "${testcvs} remove file4" \
+"${SPROG}"' remove: scheduling `file4'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove this file permanently'
+ dotest death-file4-cirm "${testcvs} -q ci -m remove file4" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.1"
+
+ # Tag the branchpoint.
+ dotest death-72a "${testcvs} -q tag bp_branch1" 'T file1
+T file2'
+
+ # branch1
+ if ${CVS} tag -b branch1 ; then
+ pass 73
+ else
+ fail 73
+ fi
+
+ # and move to the branch.
+ if ${CVS} update -r branch1 ; then
+ pass 74
+ else
+ fail 74
+ fi
+
+ dotest_fail death-file4-3 "test -f file4" ''
+
+ # add a file in the branch
+ echo line1 from branch1 >> file3
+ if ${CVS} add file3 2>> ${LOGFILE}; then
+ pass 75
+ else
+ fail 75
+ fi
+
+ # commit
+ if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
+ pass 76
+ else
+ fail 76
+ fi
+
+ dotest death-76a0 \
+"${testcvs} -q rdiff -r bp_branch1 -r branch1 first-dir" \
+"Index: first-dir/file3
+diff -c /dev/null first-dir/file3:1\.1\.2\.1
+\*\*\* /dev/null ${DATE}
+--- first-dir/file3 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} line1 from branch1"
+ dotest death-76a1 \
+"${testcvs} -q rdiff -r branch1 -r bp_branch1 first-dir" \
+"Index: first-dir/file3
+diff -c first-dir/file3:1\.1\.2\.1 first-dir/file3:removed
+\*\*\* first-dir/file3:1\.1\.2\.1 ${DATE}
+--- first-dir/file3 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- line1 from branch1
+--- 0 ----"
+
+ # remove
+ rm file3
+ if ${CVS} rm file3 2>> ${LOGFILE}; then
+ pass 77
+ else
+ fail 77
+ fi
+
+ # commit
+ if ${CVS} ci -m test >>${LOGFILE} ; then
+ pass 78
+ else
+ fail 78
+ fi
+
+ # add again
+ echo line1 from branch1 >> file3
+ if ${CVS} add file3 2>> ${LOGFILE}; then
+ pass 79
+ else
+ fail 79
+ fi
+
+ # commit
+ if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
+ pass 80
+ else
+ fail 80
+ fi
+
+ # change the first file
+ echo line2 from branch1 >> file1
+
+ # commit
+ if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
+ pass 81
+ else
+ fail 81
+ fi
+
+ # remove the second
+ rm file2
+ if ${CVS} rm file2 2>> ${LOGFILE}; then
+ pass 82
+ else
+ fail 82
+ fi
+
+ # commit
+ if ${CVS} ci -m test >>${LOGFILE}; then
+ pass 83
+ else
+ fail 83
+ fi
+
+ # back to the trunk.
+ if ${CVS} update -A 2>> ${LOGFILE}; then
+ pass 84
+ else
+ fail 84
+ fi
+
+ dotest_fail death-file4-4 "test -f file4" ''
+
+ if test -f file3 ; then
+ fail 85
+ else
+ pass 85
+ fi
+
+ # join
+ dotest death-86 "$testcvs -q update -j branch1" \
+"RCS file: $CVSROOT_DIRNAME/first-dir/file1,v
+retrieving revision 1\.3
+retrieving revision 1\.3\.2\.1
+Merging differences between 1\.3 and 1\.3\.2\.1 into file1
+${SPROG} update: scheduling \`file2' for removal
+U file3"
+
+ dotest_fail death-file4-5 "test -f file4" ''
+
+ if test -f file3 ; then
+ pass 87
+ else
+ fail 87
+ fi
+
+ # Make sure that we joined the correct change to file1
+ dotest death-87a "echo line2 from branch1 |$diff_u - file1"
+
+ # update
+ if ${CVS} update ; then
+ pass 88
+ else
+ fail 88
+ fi
+
+ # commit
+ dotest 89 "${testcvs} -q ci -m test" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: 1\.2; previous revision: 1\.1"
+ cd ..
+ mkdir 2
+ cd 2
+ dotest 89a "${testcvs} -q co first-dir" 'U first-dir/file1
+U first-dir/file3'
+ cd ..
+ rm -r 2
+ cd first-dir
+
+ # remove first file.
+ rm file1
+ if ${CVS} rm file1 2>> ${LOGFILE}; then
+ pass 90
+ else
+ fail 90
+ fi
+
+ # commit
+ if ${CVS} ci -m test >>${LOGFILE}; then
+ pass 91
+ else
+ fail 91
+ fi
+
+ if test -f file1 ; then
+ fail 92
+ else
+ pass 92
+ fi
+
+ # typo; try to get to the branch and fail
+ dotest_fail 92.1a "$testcvs update -r brnach1" \
+ "$SPROG \[update aborted\]: no such tag \`brnach1'"
+ # Make sure we are still on the trunk
+ if test -f file1 ; then
+ fail 92.1b
+ else
+ pass 92.1b
+ fi
+ if test -f file3 ; then
+ pass 92.1c
+ else
+ fail 92.1c
+ fi
+
+ # back to branch1
+ if ${CVS} update -r branch1 2>> ${LOGFILE}; then
+ pass 93
+ else
+ fail 93
+ fi
+
+ dotest_fail death-file4-6 "test -f file4" ''
+
+ if test -f file1 ; then
+ pass 94
+ else
+ fail 94
+ fi
+
+ # and join
+ dotest 95 "${testcvs} -q update -j HEAD" \
+"${SPROG}"' update: file file1 has been modified, but has been removed in
revision HEAD
+'"${SPROG}"' update: file file3 exists, but has been added in revision HEAD'
+
+ dotest_fail death-file4-7 "test -f file4" ''
+
+ # file2 should not have been recreated. It was
+ # deleted on the branch, and has not been modified on
+ # the trunk. That means that there have been no
+ # changes between the greatest common ancestor (the
+ # trunk version) and HEAD.
+ dotest_fail death-file2-1 "test -f file2" ''
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ death2)
+ # More tests of death support.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest death2-1 "$testcvs -q co first-dir"
+
+ cd first-dir
+
+ # Add two files on the trunk.
+ echo "first revision" > file1
+ echo "file4 first revision" > file4
+ dotest death2-2 "${testcvs} add file1 file4" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: scheduling file `file4'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+
+ dotest death2-3 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+initial revision: 1\.1"
+
+ # Make a branch and a non-branch tag.
+ 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" ''
+
+ # Delete the file on the branch.
+ rm file1
+ dotest death2-7 "${testcvs} rm file1" \
+"${SPROG} remove: scheduling .file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+
+ # Test diff of the removed file before it is committed.
+ dotest_fail death2-diff-1 "${testcvs} -q diff file1" \
+"${SPROG} diff: file1 was removed, no comparison available"
+
+ dotest_fail death2-diff-2 "${testcvs} -q diff -N -c file1" \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* file1 ${RFCDATE} [0-9.]*
+--- /dev/null ${RFCDATE_EPOCH}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- first revision
+--- 0 ----"
+
+ dotest death2-8 "${testcvs} -q ci -m removed" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.1"
+
+ # Test diff of a dead file.
+ dotest_fail death2-diff-3 \
+"${testcvs} -q diff -r1.1 -rbranch -c file1" \
+"${SPROG} diff: Tag branch refers to a dead (removed) revision in file
.file1.\.
+${SPROG} diff: No comparison available\. Pass .-N. to .${SPROG}
diff.${QUESTION}"
+ # and in reverse
+ dotest_fail death2-diff-3a \
+"${testcvs} -q diff -rbranch -r1.1 -c file1" \
+"${SPROG} diff: Tag branch refers to a dead (removed) revision in file
.file1.\.
+${SPROG} diff: No comparison available\. Pass .-N. to .${SPROG}
diff.${QUESTION}"
+
+ dotest_fail death2-diff-4 \
+"${testcvs} -q diff -r1.1 -rbranch -N -c file1" \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* file1 ${RFCDATE} [0-9.]*
+--- /dev/null ${RFCDATE_EPOCH}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- first revision
+--- 0 ----"
+ # and in reverse
+ dotest_fail death2-diff-4a \
+"${testcvs} -q diff -rbranch -r1.1 -N -c file1" \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file1 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
++ first revision"
+
+
+ dotest_fail death2-diff-5 "${testcvs} -q diff -rtag -c ." \
+"${SPROG} diff: file1 no longer exists, no comparison available"
+
+ dotest_fail death2-diff-6 "${testcvs} -q diff -rtag -N -c ." \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* file1 [-a-zA-Z0-9: ]* [0-9.]*
+--- /dev/null ${RFCDATE_EPOCH}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- first revision
+--- 0 ----"
+
+ # Test rdiff of a dead file.
+ dotest death2-rdiff-1 \
+"${testcvs} -q rtag -rbranch rdiff-tag first-dir" ''
+
+ dotest death2-rdiff-2 "${testcvs} -q rdiff -rtag -rbranch first-dir" \
+"Index: first-dir/file1
+diff -c first-dir/file1:1\.1 first-dir/file1:removed
+\*\*\* first-dir/file1:1\.1 [a-zA-Z0-9: ]*
+--- first-dir/file1 [a-zA-Z0-9: ]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- first revision
+--- 0 ----"
+
+ # Readd the file to the branch.
+ echo "second revision" > file1
+ dotest death2-9 "${testcvs} add file1" \
+"${SPROG} add: Re-adding file \`file1' on branch \`branch' after dead revision
1\.1\.2\.1\.
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+
+ # Test diff of the added file before it is committed.
+ dotest_fail death2-diff-7 "${testcvs} -q diff file1" \
+"${SPROG} diff: file1 is a new entry, no comparison available"
+
+ dotest_fail death2-diff-8 "${testcvs} -q diff -N -c file1" \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file1 ${RFCDATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} second revision"
+
+ dotest death2-10 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # Delete file4 from the branch
+ dotest death2-10a "${testcvs} rm -f file4" \
+"${SPROG} remove: scheduling .file4. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest death2-10b "${testcvs} -q ci -m removed" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.1"
+
+ # Back to the trunk.
+ dotest death2-11 "${testcvs} -q update -A" \
+"U file1
+U file4"
+
+ # Add another file on the trunk.
+ echo "first revision" > file2
+ dotest death2-12 "${testcvs} add file2" \
+"${SPROG}"' add: scheduling file `file2'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest death2-13 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ # Modify file4 on the trunk.
+ echo "new file4 revision" > file4
+ dotest death2-13a "${testcvs} -q commit -m mod" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2; previous revision: 1\.1"
+
+ # 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
+${SPROG} update: \`file2' is no longer in the repository
+${SPROG} update: \`file4' is no longer in the repository"
+
+ # Add a file on the branch with the same name.
+ echo "branch revision" > file2
+ dotest death2-15 "${testcvs} add file2" \
+"${SPROG}"' add: scheduling file `file2'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest death2-16 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # Add a new file on the branch.
+ echo "first revision" > file3
+ dotest death2-17 "${testcvs} add file3" \
+"${SPROG}"' add: scheduling file `file3'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest death2-18 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/Attic/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Test diff of a nonexistent tag
+ dotest_fail death2-diff-9 "$testcvs -q diff -rtag -c file3" \
+"$SPROG diff: tag tag is not in file file3"
+
+ dotest_fail death2-diff-10 "${testcvs} -q diff -rtag -N -c file3" \
+"Index: file3
+===================================================================
+RCS file: file3
+diff -N file3
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file3 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} first revision"
+
+ dotest_fail death2-diff-11 "${testcvs} -q diff -rtag -c ." \
+"Index: file1
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.2
+diff -c -r1\.1 -r1\.1\.2\.2
+\*\*\* file1 ${RFCDATE} [0-9.]*
+--- file1 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! first revision
+--- 1 ----
+! second revision
+${SPROG} diff: tag tag is not in file file2
+${SPROG} diff: tag tag is not in file file3
+${SPROG} diff: file4 no longer exists, no comparison available"
+
+ dotest_fail death2-diff-12 "${testcvs} -q diff -rtag -c -N ." \
+"Index: file1
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.2
+diff -c -r1\.1 -r1\.1\.2\.2
+\*\*\* file1 ${RFCDATE} [0-9.]*
+--- file1 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! first revision
+--- 1 ----
+! second revision
+Index: file2
+===================================================================
+RCS file: file2
+diff -N file2
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file2 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} branch revision
+Index: file3
+===================================================================
+RCS file: file3
+diff -N file3
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file3 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} first revision
+Index: file4
+===================================================================
+RCS file: file4
+diff -N file4
+\*\*\* file4 ${RFCDATE} [0-9.]*
+--- /dev/null ${RFCDATE_EPOCH}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+- file4 first revision
+--- 0 ----"
+
+ # Switch to the nonbranch tag.
+ dotest death2-19 "${testcvs} -q update -r tag" \
+"U file1
+${SPROG} update: \`file2' is no longer in the repository
+${SPROG} update: \`file3' is no longer in the repository
+U file4"
+
+ dotest_fail death2-20 "test -f file2"
+
+ # Make sure diff only reports appropriate files.
+ dotest_fail death2-diff-13 "${testcvs} -q diff -r rdiff-tag" \
+"${SPROG} diff: Tag rdiff-tag refers to a dead (removed) revision in file
.file1.\.
+${SPROG} diff: No comparison available\. Pass .-N. to .${SPROG}
diff.${QUESTION}"
+
+ dotest_fail death2-diff-14 "${testcvs} -q diff -r rdiff-tag -c -N" \
+"Index: file1
+===================================================================
+RCS file: file1
+diff -N file1
+\*\*\* /dev/null ${RFCDATE_EPOCH}
+--- file1 ${RFCDATE} [0-9.]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 0 \*\*\*\*
+--- 1 ----
+${PLUS} first revision"
+
+ # now back to the trunk
+ dotest death2-21 "${testcvs} -q update -A" \
+"U file2
+U file4"
+
+ # test merging with a dead file
+ dotest death2-22 "${testcvs} -q co first-dir" \
+"U first-dir/file1
+U first-dir/file2
+U first-dir/file4"
+
+ cd first-dir
+ dotest death2-23 "${testcvs} rm -f file4" \
+"${SPROG} remove: scheduling .file4. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest death2-24 "${testcvs} -q ci -m removed file4" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.2"
+ cd ..
+ echo "new stuff" >file4
+ dotest_fail death2-25 "${testcvs} up file4" \
+"${SPROG} update: conflict: \`file4' is modified but no longer in the
repository
+C file4"
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rm-update-message)
+ # FIXME
+ # local CVS prints a warning message when update notices a missing
+ # file and client/server CVS doesn't. These should be identical.
+ mkdir rm-update-message; cd rm-update-message
+ modify_repo mkdir $CVSROOT_DIRNAME/rm-update-message
+ dotest rm-update-message-setup-1 "$testcvs -q co rm-update-message" ''
+ cd rm-update-message
+ file=x
+ echo >$file
+ dotest rm-update-message-setup-2 "$testcvs -q add $file" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rm-update-message-setup-3 "$testcvs -q ci -mcreate $file" \
+"$CVSROOT_DIRNAME/rm-update-message/$file,v <-- $file
+initial revision: 1\.1"
+
+ rm $file
+ dotest rm-update-message-1 "$testcvs up $file" \
+"${SPROG} update: warning: \`$file' was lost
+U $file"
+
+ dokeep
+ cd ../..
+ rm -r rm-update-message
+ modify_repo rm -rf $CVSROOT_DIRNAME/rm-update-message
+ ;;
+
+
+
+ rmadd)
+ # More tests of adding and removing files.
+ # In particular ci -r.
+ # Other ci -r tests:
+ # * editor-9: checking in a modified file,
+ # where "ci -r" means a branch.
+ # * basica-8a1: checking in a modified file with numeric revision.
+ # * basica-8a2: likewise.
+ # * keywordlog-4: adding a new file with numeric revision.
+ mkdir 1; cd 1
+ dotest rmadd-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest rmadd-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo first file1 >file1
+ dotest rmadd-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ dotest_fail rmadd-4 "${testcvs} -q ci -r 1.2.2.4 -m add" \
+"${SPROG} commit: cannot add file .file1' with revision .1\.2\.2\.4'; must be
on trunk
+${SPROG} \[commit aborted\]: correct above errors first!"
+ dotest_fail rmadd-5 "${testcvs} -q ci -r 1.2.2 -m add" \
+"${SPROG} commit: cannot add file .file1' with revision .1\.2\.2'; must be on
trunk
+${SPROG} \[commit aborted\]: correct above errors first!"
+ dotest_fail rmadd-6 "$testcvs -q ci -r mybranch -m add" \
+"$SPROG \[commit aborted\]: no such tag \`mybranch'"
+
+ # The thing with the trailing periods strikes me as a very
+ # bizarre behavior, but it would seem to be intentional
+ # (see commit.c). It probably could go away....
+ dotest rmadd-7 "${testcvs} -q ci -r 7.... -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 7\.1"
+ if $remote; then
+ # I guess remote doesn't set a sticky tag in this case.
+ # Kind of odd, in the sense that rmadd-24a does set one
+ # both local and remote.
+ dotest_fail rmadd-7a "test -f CVS/Tag"
+ echo T7 >CVS/Tag
+ else
+ dotest rmadd-7a "cat CVS/Tag" "T7"
+ fi
+
+ dotest rmadd-8 "${testcvs} -q tag -b mybranch" "T file1"
+ dotest rmadd-9 "${testcvs} -q tag mynonbranch" "T file1"
+
+ touch file2
+ # The previous "cvs ci -r" set a sticky tag of '7'. Seems a
+ # bit odd, and I guess commit.c (findmaxrev) makes '7' sticky
+ # tags unnecessary (?). I kind of suspect that it should be
+ # saying "sticky tag is not a branch" like keywordlog-4b.
+ # Or something.
+ dotest rmadd-10 "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition on branch .7'
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ # As in the previous example, CVS is confused....
+ dotest rmadd-11 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 7\.1"
+
+ dotest rmadd-12 "${testcvs} -q update -A" ""
+ touch file3
+ dotest rmadd-13 "${testcvs} add file3" \
+"${SPROG} add: scheduling file .file3. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ # Huh? file2 is not up to date? Seems buggy to me....
+ dotest_fail rmadd-14 "${testcvs} -q ci -r mybranch -m add" \
+"${SPROG} commit: Up-to-date check failed for .file2'
+${SPROG} \[commit aborted\]: correct above errors first!"
+ # Whatever, let's not let file2 distract us....
+ dotest rmadd-15 "${testcvs} -q ci -r mybranch -m add file3" \
+"$CVSROOT_DIRNAME/first-dir/Attic/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ touch file4
+ dotest rmadd-16 "${testcvs} add file4" \
+"${SPROG} add: scheduling file .file4. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ # Prior to CVS 1.12.10, this would fail with a, "no such tag" message
+ # since val-tags used to be updated the first time the tag was used
+ # rather than when it was created.
+
+ # Try to make CVS write val-tags.
+ if $proxy; then :; else
+ # First remove the tag.
+ grep -v mynonbranch $CVSROOT_DIRNAME/CVSROOT/val-tags \
+ >$CVSROOT_DIRNAME/CVSROOT/val-tags-tmp
+ mv $CVSROOT_DIRNAME/CVSROOT/val-tags-tmp \
+ $CVSROOT_DIRNAME/CVSROOT/val-tags
+
+ dotest rmadd-18 "$testcvs -q update -p -r mynonbranch file1" \
+"first file1"
+ # Oops, -p suppresses writing val-tags (probably a questionable
+ # behavior).
+ dotest_fail rmadd-19 \
+"$testcvs -q ci -r mynonbranch -m add file4" \
+"$SPROG \[commit aborted\]: no such tag \`mynonbranch'"
+ # Now make CVS write val-tags for real.
+ dotest rmadd-20 "$testcvs -q update -r mynonbranch file1"
+ fi # !$proxy
+
+ # Oops - CVS isn't distinguishing between a branch tag and
+ # a non-branch tag.
+ dotest rmadd-21 \
+"${testcvs} -q ci -r mynonbranch -m add file4" \
+"$CVSROOT_DIRNAME/first-dir/Attic/file4,v <-- file4
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # OK, we add this one in a vanilla way, but then check in
+ # a modification with ci -r and sniff around for sticky tags.
+ echo file5 >file5
+ dotest rmadd-22 "${testcvs} add file5" \
+"${SPROG} add: scheduling file .file5. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ if $remote; then
+ # Interesting bug (or missing feature) here. findmaxrev
+ # gets the major revision from the Entries. Well, remote
+ # doesn't send the entries for files which are not involved.
+ dotest rmadd-23r "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+initial revision: 1\.1"
+ dotest rmadd-23-workaroundr \
+"${testcvs} -q ci -r 7 -m bump-it file5" \
+"$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+new revision: 7\.1; previous revision: 1\.1"
+ else
+ dotest rmadd-23 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+initial revision: 7\.1"
+ fi
+ echo change it >file5
+ dotest_fail rmadd-24 "$testcvs -q ci -r 4.8 -m change file5" \
+"$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+$SPROG commit: $CVSROOT_DIRNAME/first-dir/file5,v: revision 4\.8 too low; must
be higher than 7\.1
+$SPROG commit: could not check in file5"
+ dotest rmadd-24a "${testcvs} -q ci -r 8.4 -m change file5" \
+"$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+new revision: 8\.4; previous revision: 7\.1"
+ # I'm not really sure that a sticky tag make sense here.
+ # It seems to be longstanding behavior for what that is worth.
+ dotest rmadd-25 "${testcvs} status file5" \
+"===================================================================
+File: file5 Status: Up-to-date
+
+ Working revision: 8\.4.*
+ Repository revision: 8\.4 ${CVSROOT_DIRNAME}/first-dir/file5,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: 8\.4
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # now try forced revision with recursion
+ mkdir sub
+ dotest rmadd-26 "${testcvs} -q add sub" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/sub added to the repository"
+ echo hello >sub/subfile
+ dotest rmadd-27 "${testcvs} -q add sub/subfile" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ dotest rmadd-28 "${testcvs} -q ci -m. sub" \
+"$CVSROOT_DIRNAME/first-dir/sub/subfile,v <-- sub/subfile
+initial revision: 1\.1"
+
+ # lose the branch
+ dotest rmadd-29 "${testcvs} -q up -A" \
+"${SPROG} update: \`file3' is no longer in the repository
+${SPROG} update: \`file4' is no longer in the repository"
+
+ # -f disables recursion
+ dotest rmadd-30 "${testcvs} -q ci -f -r9 -m." \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 9\.1; previous revision: 7\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 9\.1; previous revision: 7\.1
+$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+new revision: 9\.1; previous revision: 8\.4"
+
+ # add -R to force recursion
+ dotest rmadd-31 "${testcvs} -q ci -f -r9 -R -m." \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 9\.2; previous revision: 9\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 9\.2; previous revision: 9\.1
+$CVSROOT_DIRNAME/first-dir/file5,v <-- file5
+new revision: 9\.2; previous revision: 9\.1
+$CVSROOT_DIRNAME/first-dir/sub/subfile,v <-- sub/subfile
+new revision: 9\.1; previous revision: 1\.1"
+
+ if $remote; then
+ # as noted above, remote doesn't set a sticky tag
+ :
+ else
+ dotest rmadd-32 "cat CVS/Tag" "T9"
+ dotest rmadd-33 "cat sub/CVS/Tag" "T9"
+ fi
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rmadd2)
+ # Tests of undoing commits, including in the presence of
+ # adding and removing files. See join for a list of -j tests.
+ mkdir 1; cd 1
+ dotest rmadd2-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest rmadd2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo 'initial contents' >file1
+ dotest rmadd2-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rmadd2-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest rmadd2-4a "${testcvs} -Q tag tagone" ""
+ dotest rmadd2-5 "${testcvs} rm -f file1" \
+"${SPROG} remove: scheduling .file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest rmadd2-6 "${testcvs} -q ci -m remove" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.1"
+ dotest rmadd2-7 "$testcvs -q update -j 1.2 -j 1.1 file1" "U file1"
+ dotest rmadd2-8 "${testcvs} -q ci -m readd" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ echo 'new contents' >file1
+ dotest rmadd2-9 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3"
+ dotest rmadd2-10 "${testcvs} -q update -j 1.4 -j 1.3 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.4
+retrieving revision 1\.3
+Merging differences between 1\.4 and 1\.3 into file1"
+ dotest rmadd2-11 "${testcvs} -q ci -m undo" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4"
+ dotest rmadd2-12 "cat file1" "initial contents"
+ dotest rmadd2-13 "${testcvs} -q update -p -r 1.3" "initial contents"
+
+ # Hmm, might be a bit odd that this works even if 1.3 is not
+ # the head.
+ dotest rmadd2-14 "${testcvs} -q update -j 1.3 -j 1.2 file1" \
+"${SPROG} update: scheduling \`file1' for removal"
+
+ # Check that -p can get arbitrary revisions of a removed file
+ dotest rmadd2-14a "${testcvs} -q update -p" "initial contents"
+ dotest rmadd2-14b "${testcvs} -q update -p -r 1.5" "initial contents"
+ dotest rmadd2-14c "${testcvs} -q update -p -r 1.3" "initial contents"
+
+ dotest rmadd2-15 "${testcvs} -q ci -m re-remove" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.5"
+ dotest rmadd2-16 "${testcvs} log -h file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+Working file: file1
+head: 1\.6
+branch:
+locks: strict
+access list:
+symbolic names:
+ tagone: 1\.1
+keyword substitution: kv
+total revisions: 6
+============================================================================="
+ dotest rmadd2-17 "${testcvs} status -v file1" \
+"===================================================================
+File: no file file1 Status: Up-to-date
+
+ Working revision: No entry for file1
+ Repository revision: 1\.6
${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+ Commit Identifier: ${commitid}
+
+ Existing Tags:
+ tagone (revision: 1.1)"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rmadd3)
+ # This test demonstrates that CVS notices that file1 exists rather
+ # that deleting or writing over it after:
+ #
+ # cvs remove -f file1; touch file1; cvs add file1.
+ #
+ # According to the manual, this should work for:
+ #
+ # rm file1; cvs remove file1; cvs add file1
+ #
+ # but in past version of CVS, new content in file1 would be
+ # erroneously deleted when file1 reappeared between the remove and
+ # the add.
+ #
+ # Later versions of CVS would refuse to perform the add, but still
+ # allow a subsequent local commit to erase the file from the
+ # workspace, possibly losing data.
+ mkdir 1; cd 1
+ dotest rmadd3-init1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest rmadd3-init2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo initial content for file1 >file1
+ dotest rmadd3-init3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file \`file1' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest rmadd3-init4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ # Here begins the guts of this test, as detailed above.
+ dotest rmadd3-1 "${testcvs} rm -f file1" \
+"${SPROG} remove: scheduling \`file1' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+
+ # Now recreate the file:
+ echo desired future contents for file1 >file1
+
+ # And attempt to resurrect it at the same time:
+ dotest_fail rmadd3-2 "${testcvs} add file1" \
+"${SPROG} add: \`file1' should be removed and is still there (or is back
again)"
+
+ # Now prove that commit knows that it shouldn't erase files.
+ dotest_fail rmadd3-3 "${testcvs} -q ci -m." \
+"$CPROG commit: \`file1' should be removed and is still there (or is back
again)
+$CPROG \[commit aborted\]: correct above errors first!"
+
+ # Then these should pass too:
+ dotest rmadd3-4 "test -f file1"
+ dotest rmadd3-5 "cat file1" "desired future contents for file1"
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ resurrection)
+ # This test tests a few file resurrection scenarios.
+ mkdir 1; cd 1
+ dotest resurrection-init1 "$testcvs -q co -l ." ''
+ mkdir first-dir
+ dotest resurrection-init2 "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+
+ echo initial content for file1 >file1
+ dotest resurrection-init3 "$testcvs add file1" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ dotest resurrection-init4 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ dotest resurrection-init5 "$testcvs -Q rm -f file1"
+
+ # The first test is that `cvs add' will resurrect a file before its
+ # removal has been committed.
+ dotest_sort resurrection-1 "$testcvs add file1" \
+"U file1
+$SPROG add: \`file1', version 1\.1, resurrected"
+ dotest resurrection-2 "$testcvs -Q diff file1" ""
+
+ dotest resurrection-init6 "$testcvs -Q tag -b resurrection"
+ dotest resurrection-init7 "$testcvs -Q rm -f file1"
+ dotest resurrection-init8 "$testcvs -Q ci -mrm"
+
+ # The next test is that CVS will resurrect a committed removal.
+ dotest_sort resurrection-3 "$testcvs add file1" \
+"U file1
+$SPROG add: Re-adding file \`file1' after dead revision 1\.2\.
+$SPROG add: Resurrecting file \`file1' from revision 1\.1\.
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ dotest resurrection-4 "$testcvs -q diff -r1.1 file1" ""
+ dotest resurrection-5 "$testcvs -q ci -mreadd" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+
+ dotest resurrection-init9 "$testcvs -Q up -rresurrection"
+ dotest resurrection-init10 "$testcvs -Q rm -f file1"
+ dotest resurrection-init11 "$testcvs -Q ci -mrm-on-resurrection"
+
+ # The next test is that CVS will resurrect a committed removal to a
+ # branch.
+ dotest_sort resurrection-6 "$testcvs -r add file1" \
+"U file1
+$SPROG add: Re-adding file \`file1' on branch \`resurrection' after dead
revision 1\.1\.2\.1\.
+$SPROG add: Resurrecting file \`file1' from revision 1\.1\.
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ # If the file is modified, it had better be read-write
+ # regardless of what the user has requested with the CVSREAD
+ # environment variable or the global -r switch
+ dotest resurrection-6b 'test -w file1' ''
+ dotest resurrection-7 "$testcvs -Q diff -r1.1 file1" ""
+ dotest resurrection-8 "$testcvs -q ci -mreadd" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # The next few tests verify that an attempted resurrection of a file
+ # with no previous revision on the trunk fails.
+ touch file2
+ dotest resurrection-9 "$testcvs -Q add file2"
+ dotest resurrection-10 "$testcvs -Q ci -mnew-file2"
+ dotest resurrection-11 "$testcvs -Q up -A"
+
+ # This command once caused an assertion failure.
+ dotest resurrection-12 "$testcvs add file2" \
+"$SPROG add: File \`file2' has no previous revision to resurrect\."
+
+ # Check what 'cvs -r add' does with resurrected files.
+ dotest resurrection-13 "$testcvs -Q rm -f file1"
+ dotest_sort resurrection-14 "$testcvs -r add file1" \
+"U file1
+$SPROG add: \`file1', version 1\.3, resurrected"
+ dotest_fail resurrection-15 'test -w file1' ''
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ dirs)
+ # Tests related to removing and adding directories.
+ # See also:
+ # conflicts (especially dir1 in conflicts-130): What happens if
+ # directory exists in repository and a non-CVS-controlled
+ # directory in the working directory?
+ # conflicts3-15. More cases, especially where CVS directory
+ # exists but without CVS/Repository and friends.
+ # conflicts3-22. Similar to conflicts-130 but there is a file
+ # in the directory.
+ # dirs2. Sort of similar to conflicts3-22 but somewhat different.
+ mkdir imp-dir; cd imp-dir
+ echo file1 >file1
+ mkdir sdir
+ echo sfile >sdir/sfile
+ dotest_sort dirs-1 \
+"${testcvs} import -m import-it dir1 vend rel" "
+
+N dir1/file1
+N dir1/sdir/sfile
+No conflicts created by this import
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/dir1/sdir"
+ cd ..
+
+ mkdir 1; cd 1
+ dotest dirs-2 "$testcvs -Q co dir1" ""
+
+ # Various CVS administrators are in the habit of removing
+ # the repository directory for things they don't want any
+ # more. I've even been known to do it myself (on rare
+ # occasions). Not the usual recommended practice, but we want
+ # to try to come up with some kind of reasonable/documented/sensible
+ # behavior.
+ modify_repo rm -rf $CVSROOT_DIRNAME/dir1/sdir
+
+ dotest dirs-3 "${testcvs} update" \
+"${SPROG} update: Updating dir1
+${SPROG} update: Updating dir1/sdir
+${SPROG} update: cannot open directory ${CVSROOT_DIRNAME}/dir1/sdir: No such
file or directory
+${SPROG} update: skipping directory dir1/sdir"
+ dotest dirs-3a "${testcvs} update -d" \
+"${SPROG} update: Updating dir1
+${SPROG} update: Updating dir1/sdir
+${SPROG} update: cannot open directory ${CVSROOT_DIRNAME}/dir1/sdir: No such
file or directory
+${SPROG} update: skipping directory dir1/sdir"
+
+ # If we say "yes", then CVS gives errors about not being able to
+ # create lock files.
+ # The fact that it says "skipping directory " rather than
+ # "skipping directory dir1/sdir" is some kind of bug.
+ dotest dirs-4 "echo no | ${testcvs} release -d dir1/sdir" \
+"${SPROG} update: cannot open directory ${CVSROOT_DIRNAME}/dir1/sdir: No such
file or directory
+${SPROG} update: skipping directory
+You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .dir1/sdir': ..
.release' aborted by user choice."
+
+ # OK, if "cvs release" won't help, we'll try it the other way...
+ rm -r dir1/sdir
+
+ dotest dirs-5 "cat dir1/CVS/Entries" \
+"/file1/1.1.1.1/[a-zA-Z0-9 :]*//
+D/sdir////"
+ dotest dirs-6 "${testcvs} update" "${SPROG} update: Updating dir1"
+ dotest dirs-7 "cat dir1/CVS/Entries" \
+"/file1/1.1.1.1/[a-zA-Z0-9 :]*//
+D/sdir////"
+ dotest dirs-8 "${testcvs} update -d dir1" \
+"${SPROG} update: Updating dir1"
+
+ dokeep
+ cd ..
+ rm -r imp-dir 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/dir1
+ ;;
+
+
+
+ dirs2)
+ # See "dirs" for a list of tests involving adding and
+ # removing directories.
+ mkdir 1; cd 1
+ dotest dirs2-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest dirs2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ mkdir sdir
+ dotest dirs2-3 "${testcvs} add sdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/sdir added to the repository"
+ touch sdir/file1
+ dotest dirs2-4 "${testcvs} add sdir/file1" \
+"${SPROG} add: scheduling file .sdir/file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest dirs2-5 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/sdir/file1,v <-- sdir/file1
+initial revision: 1\.1"
+ rm -r sdir/CVS
+ if $remote; then
+ # This is just like conflicts3-23
+ dotest_fail dirs2-6r "${testcvs} update -d" \
+"${QUESTION} sdir
+${SPROG} update: Updating \.
+${SPROG} update: Updating sdir
+${CPROG} update: move away \`sdir/file1'; it is in the way
+C sdir/file1"
+ rm sdir/file1
+ rm -r sdir/CVS
+
+ # This is where things are not just like conflicts3-23
+ dotest dirs2-7r "${testcvs} update -d" \
+"${QUESTION} sdir
+${SPROG} update: Updating \.
+${SPROG} update: Updating sdir
+U sdir/file1"
+ else
+ dotest dirs2-6 "${testcvs} update -d" \
+"${CPROG} update: Updating \.
+${QUESTION} sdir"
+ rm sdir/file1
+ dotest dirs2-7 "${testcvs} update -d" \
+"${CPROG} update: Updating \.
+${QUESTION} sdir"
+ fi
+ cd ../..
+
+ # Now, the same thing (more or less) on a branch.
+ mkdir 2; cd 2
+ dotest dirs2-8 "${testcvs} -q co first-dir" 'U first-dir/sdir/file1'
+ cd first-dir
+ dotest dirs2-9 "${testcvs} -q tag -b br" "T sdir/file1"
+ rm -r sdir/CVS
+
+ if $remote; then
+ # val-tags used to have a cute little quirk; if an update didn't
+ # recurse into the directories where the tag is defined, val-tags
+ # wouldn't get updated. This is no longer a problem as of 1.12.10.
+ dotest_fail dirs2-10-againr "$testcvs update -d -r br" \
+"$QUESTION sdir
+$SPROG update: Updating \.
+$SPROG update: Updating sdir
+$CPROG update: move away \`sdir/file1'; it is in the way
+C sdir/file1"
+ else
+ dotest dirs2-10 "${testcvs} update -d -r br" \
+"$SPROG update: Updating \.
+$QUESTION sdir"
+# This is what used to happen. I'm not sure why it changed with 1.12.10, but
+# as near as I can tell from the comments in update_direntproc, the new
+# behavior was the intended behavior.
+#"$CPROG update: in directory \`sdir':
+#$CPROG \[update aborted\]: there is no version here; do \`$CPROG checkout'
first"
+ fi
+ cd ../..
+
+ # OK, the above tests make the situation somewhat harder
+ # than it might be, in the sense that they actually have a
+ # file which is alive on the branch we are updating. Let's
+ # try it where it is just a directory where all the files
+ # have been removed.
+ mkdir 3; cd 3
+ dotest dirs2-11 "${testcvs} -q co -r br first-dir" \
+"U first-dir/sdir/file1"
+ cd first-dir
+ # Hmm, this doesn't mention the branch like add does. That's
+ # an odd non-orthogonality.
+ dotest dirs2-12 "${testcvs} rm -f sdir/file1" \
+"${SPROG} remove: scheduling .sdir/file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest dirs2-13 "${testcvs} -q ci -m remove" \
+"$CVSROOT_DIRNAME/first-dir/sdir/file1,v <-- sdir/file1
+new revision: delete; previous revision: 1\.1"
+ cd ../../2/first-dir
+ if $remote; then
+ dotest dirs2-14 "${testcvs} update -d -r br" \
+"${QUESTION} sdir/file1
+${SPROG} update: Updating \.
+${SPROG} update: Updating sdir"
+ else
+ dotest dirs2-14 "${testcvs} update -d -r br" \
+"${CPROG} update: Updating \.
+${QUESTION} sdir"
+ fi
+
+ dokeep
+ cd ../..
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ branches)
+ # More branch tests, including branches off of branches
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest branches-1 "$testcvs -q co first-dir"
+ cd first-dir
+ echo 1:ancest >file1
+ echo 2:ancest >file2
+ echo 3:ancest >file3
+ echo 4:trunk-1 >file4
+ dotest branches-2 "${testcvs} add file1 file2 file3 file4" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: scheduling file \`file2' for addition
+$SPROG add: scheduling file \`file3' for addition
+$SPROG add: scheduling file \`file4' for addition
+$SPROG add: use .$SPROG commit. to add these files permanently"
+ dotest branches-2a "$testcvs -n -q ci -m dont-commit"
+ dotest_lit branches-3 "$testcvs -q ci -m add-it" <<HERE
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1.1
+${CVSROOT_DIRNAME}/first-dir/file3,v <-- file3
+initial revision: 1.1
+${CVSROOT_DIRNAME}/first-dir/file4,v <-- file4
+initial revision: 1.1
+HERE
+ echo 4:trunk-2 >file4
+ dotest branches-3.2 "${testcvs} -q ci -m trunk-before-branch" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2; previous revision: 1\.1"
+ # The "cvs log file4" in test branches-14.3 will test that we
+ # didn't really add the tag.
+ dotest branches-3.3 "${testcvs} -qn tag dont-tag" \
+"T file1
+T file2
+T file3
+T file4"
+ # Modify this file before branching, to deal with the case where
+ # someone is hacking along, says "oops, I should be doing this on
+ # a branch", and only then creates the branch.
+ echo 1:br1 >file1
+ dotest branches-4 "${testcvs} tag -b br1" "${SPROG}"' tag: Tagging \.
+T file1
+T file2
+T file3
+T file4'
+ dotest branches-5 "${testcvs} update -r br1" \
+"${SPROG} update: Updating \.
+M file1"
+ echo 2:br1 >file2
+ echo 4:br1 >file4
+ dotest branches-6 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2\.2\.1; previous revision: 1\.2"
+ dotest branches-7 "${testcvs} -q tag -b brbr" 'T file1
+T file2
+T file3
+T file4'
+ dotest branches-8 "${testcvs} -q update -r brbr" ''
+ echo 1:brbr >file1
+ echo 4:brbr >file4
+ dotest branches-9 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1\.2\.1; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2\.2\.1\.2\.1; previous revision: 1\.2\.2\.1"
+ dotest branches-10 "cat file1 file2 file3 file4" '1:brbr
+2:br1
+3:ancest
+4:brbr'
+ dotest branches-11 "${testcvs} -q update -r br1" \
+'U file1
+U file4'
+ dotest branches-12 "cat file1 file2 file3 file4" '1:br1
+2:br1
+3:ancest
+4:br1'
+ echo 4:br1-2 >file4
+ dotest branches-12.2 "${testcvs} -q ci -m change-on-br1" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1"
+ dotest branches-13 "${testcvs} -q update -A" 'U file1
+U file2
+U file4'
+ dotest branches-14 "cat file1 file2 file3 file4" '1:ancest
+2:ancest
+3:ancest
+4:trunk-2'
+ echo 4:trunk-3 >file4
+ dotest branches-14.2 \
+ "${testcvs} -q ci -m trunk-change-after-branch" \
+"$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.3; previous revision: 1\.2"
+ dotest branches-14.3 "${testcvs} log file4" \
+"
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file4,v
+Working file: file4
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ brbr: 1\.2\.2\.1\.0\.2
+ br1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 6; selected revisions: 6
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+trunk-change-after-branch
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+branches: 1\.2\.2;
+trunk-before-branch
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-it
+----------------------------
+revision 1\.2\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+change-on-br1
+----------------------------
+revision 1\.2\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+branches: 1\.2\.2\.1\.2;
+modify
+----------------------------
+revision 1\.2\.2\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+modify
+============================================================================="
+ dotest_fail branches-14.4 \
+ "${testcvs} diff -c -r 1.1 -r 1.3 file4" \
+"Index: file4
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file4,v
+retrieving revision 1\.1
+retrieving revision 1\.3
+diff -c -r1\.1 -r1\.3
+\*\*\* file4 ${RFCDATE} 1\.1
+--- file4 ${RFCDATE} 1\.3
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! 4:trunk-1
+--- 1 ----
+! 4:trunk-3"
+ dotest_fail branches-14.5 \
+ "${testcvs} diff -c -r 1.1 -r 1.2.2.1 file4" \
+"Index: file4
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file4,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1
+diff -c -r1\.1 -r1\.2\.2\.1
+\*\*\* file4 ${RFCDATE} 1\.1
+--- file4 ${RFCDATE} 1\.2\.2\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+\*\*\* 1 \*\*\*\*
+! 4:trunk-1
+--- 1 ----
+! 4:br1"
+ dotest branches-15 \
+ "${testcvs} update -j 1.1.2.1 -j 1.1.2.1.2.1 file1" \
+ "RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.1
+Merging differences between 1\.1\.2\.1 and 1\.1\.2\.1\.2\.1 into file1
+rcsmerge: warning: conflicts during merge"
+ dotest branches-16 "cat file1" '<<<<<<< file1
+1:ancest
+[=]======
+1:brbr
+[>]>>>>>> 1\.1\.2\.1\.2\.1'
+
+ dotest branches-o1 "${testcvs} -q admin -o ::brbr" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file3,v
+done
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file4,v
+done"
+
+ dokeep
+ cd ..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ 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.
+
+ modify_repo 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" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest branches2-3 "${testcvs} commit -m add file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ # 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" \
+"${SPROG}"' add: scheduling file `file2'\'' for addition on branch `b1'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ mkdir dir1
+ dotest branches2-9 "${testcvs} add dir1" \
+"Directory ${CVSROOT_DIRNAME}/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" \
+"${SPROG}"' add: scheduling file `dir1/file3'\'' for addition on branch `b1'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest branches2-11 "${testcvs} -q ci -madd ." \
+"$CVSROOT_DIRNAME/first-dir/Attic/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir1/Attic/file3,v <-- dir1/file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # 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" \
+"${SPROG} update: 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 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ 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
${CVSROOT_DIRNAME}/first-dir/dir1/Attic/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: b1 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # Test some calls to rls here because we can. These should probably
+ # be somewhere else, but we already have some directories set up.
+ dotest branches2-14-rls-1 "$testcvs rls" \
+"$SPROG rls: Listing module: \`.'
+CVSROOT
+first-dir"
+ dotest branches2-14-rls-2 "$testcvs rls -R" \
+"$SPROG rls: Listing module: \`.'
+\.:
+CVSROOT
+first-dir
+
+CVSROOT:
+checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg
+Emptydir
+
+CVSROOT/Emptydir:
+
+first-dir:
+file1
+dir1
+
+first-dir/dir1:"
+ dotest branches2-14-rls-3 "$testcvs rls -l -R" \
+"$SPROG rls: Listing module: \`.'
+\.:
+d--- $ISO8601DATE CVSROOT
+d--- $ISO8601DATE first-dir
+
+CVSROOT:
+---- $ISO8601DATE 1\.[0-9][0-9]* checkoutlist
+---- $ISO8601DATE 1\.[0-9][0-9]* commitinfo
+---- $ISO8601DATE 1\.[0-9][0-9]* config
+---- $ISO8601DATE 1\.[0-9][0-9]* cvswrappers
+---- $ISO8601DATE 1\.[0-9][0-9]* loginfo
+---- $ISO8601DATE 1\.[0-9][0-9]* modules
+---- $ISO8601DATE 1\.[0-9][0-9]* notify
+---- $ISO8601DATE 1\.[0-9][0-9]* postadmin
+---- $ISO8601DATE 1\.[0-9][0-9]* postproxy
+---- $ISO8601DATE 1\.[0-9][0-9]* posttag
+---- $ISO8601DATE 1\.[0-9][0-9]* postwatch
+---- $ISO8601DATE 1\.[0-9][0-9]* preproxy
+---- $ISO8601DATE 1\.[0-9][0-9]* rcsinfo
+---- $ISO8601DATE 1\.[0-9][0-9]* taginfo
+---- $ISO8601DATE 1\.[0-9][0-9]* verifymsg
+d--- $ISO8601DATE Emptydir
+
+CVSROOT/Emptydir:
+
+first-dir:
+---- $ISO8601DATE 1\.1 file1
+d--- $ISO8601DATE dir1
+
+first-dir/dir1:"
+ dotest branches2-14-rls-4 "$testcvs rls -eR" \
+"$SPROG rls: Listing module: \`.'
+\.:
+D/CVSROOT////
+D/first-dir////
+
+CVSROOT:
+/checkoutlist/1\.[0-9][0-9]*/$DATE//
+/commitinfo/1\.[0-9][0-9]*/$DATE//
+/config/1\.[0-9][0-9]*/$DATE//
+/cvswrappers/1\.[0-9][0-9]*/$DATE//
+/loginfo/1\.[0-9][0-9]*/$DATE//
+/modules/1\.[0-9][0-9]*/$DATE//
+/notify/1\.[0-9][0-9]*/$DATE//
+/postadmin/1\.[0-9][0-9]*/$DATE//
+/postproxy/1\.[0-9][0-9]*/$DATE//
+/posttag/1\.[0-9][0-9]*/$DATE//
+/postwatch/1\.[0-9][0-9]*/$DATE//
+/preproxy/1\.[0-9][0-9]*/$DATE//
+/rcsinfo/1\.[0-9][0-9]*/$DATE//
+/taginfo/1\.[0-9][0-9]*/$DATE//
+/verifymsg/1\.[0-9][0-9]*/$DATE//
+D/Emptydir////
+
+CVSROOT/Emptydir:
+
+first-dir:
+/file1/1\.1/$DATE//
+D/dir1////
+
+first-dir/dir1:"
+ dotest branches2-14-rls-5 "$testcvs -q rls -R" \
+"\.:
+CVSROOT
+first-dir
+
+CVSROOT:
+checkoutlist
+commitinfo
+config
+cvswrappers
+loginfo
+modules
+notify
+postadmin
+postproxy
+posttag
+postwatch
+preproxy
+rcsinfo
+taginfo
+verifymsg
+Emptydir
+
+CVSROOT/Emptydir:
+
+first-dir:
+file1
+dir1
+
+first-dir/dir1:"
+ dotest branches2-14-rls-6 "$testcvs -q rls -lRrb1" \
+"\.:
+d--- $ISO8601DATE CVSROOT
+d--- $ISO8601DATE first-dir
+
+CVSROOT:
+d--- $ISO8601DATE Emptydir
+
+CVSROOT/Emptydir:
+
+first-dir:
+---- $ISO8601DATE 1\.1 file1
+---- $ISO8601DATE 1\.1\.2\.1 file2
+d--- $ISO8601DATE dir1
+
+first-dir/dir1:
+---- $ISO8601DATE 1\.1\.2\.1 file3"
+ dotest branches2-14-rls-7 "$testcvs -q rls -lRrb2" \
+"\.:
+d--- $ISO8601DATE CVSROOT
+d--- $ISO8601DATE first-dir
+
+CVSROOT:
+d--- $ISO8601DATE Emptydir
+
+CVSROOT/Emptydir:
+
+first-dir:
+---- $ISO8601DATE 1\.1 file1
+d--- $ISO8601DATE dir1
+
+first-dir/dir1:"
+
+ # Now some calls to ls. These are more appropriate here.
+ dotest branches2-14-ls-1 "$testcvs ls" \
+"file1
+dir1"
+ dotest branches2-14-ls-2 "$testcvs ls -e" \
+"/file1/1\.1/$DATE//
+D/dir1////"
+ dotest branches2-14-ls-3 "$testcvs ls -R" \
+"\.:
+file1
+dir1
+
+dir1:
+file3"
+ dotest branches2-14-ls-4 "$testcvs ls -eRrHEAD" \
+"\.:
+/file1/1\.1/$DATE//THEAD
+D/dir1////
+
+dir1:"
+ dotest branches2-14-ls-5 "$testcvs ls -eRrb1" \
+"\.:
+/file1/1\.1/$DATE//Tb1
+/file2/1\.1\.2\.1/$DATE//Tb1
+D/dir1////
+
+dir1:
+/file3/1\.1\.2\.1/$DATE//Tb1"
+ dotest branches2-14-ls-6 "$testcvs ls -eRrb2" \
+"\.:
+/file1/1.1/$DATE//Tb2
+D/dir1////
+
+dir1:"
+ # Nonexistant tags used to cause assertion failures.
+ dotest_fail branches2-14-ls-7 "$testcvs ls -eRrnosuchtag" \
+"$SPROG \[ls aborted\]: no such tag \`nosuchtag'"
+
+ # 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" \
+"${SPROG} update: 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 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ 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" \
+"${SPROG} update: Updating dir1"
+ dotest_fail branches2-18 "test -d dir1"
+ dotest branches2-19 "${testcvs} update -d -P -r b1 dir1" \
+"${SPROG} update: 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 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ 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
${CVSROOT_DIRNAME}/first-dir/dir1/Attic/file3,v
+ Commit Identifier: ${commitid}
+ 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" \
+"${SPROG} update: 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 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file3 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/dir1/Attic/file3,v
+ Commit Identifier: ${commitid}
+ 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 ${CVSROOT_DIRNAME}/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" \
+"${SPROG}"' add: scheduling file `dir2/file4'\'' for addition on branch `b1'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest branches2-27 "${testcvs} -q commit -madd" \
+"$CVSROOT_DIRNAME/first-dir/dir2/Attic/file4,v <-- dir2/file4
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ cd ../../b1b/first-dir
+ dotest branches2-28 "${testcvs} update -d dir2" \
+"${SPROG} update: 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
${CVSROOT_DIRNAME}/first-dir/dir2/Attic/file4,v
+ Commit Identifier: ${commitid}
+ 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" \
+"${SPROG} update: 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" \
+"${SPROG}"' add: scheduling file `file5'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest branches2-35 "${testcvs} -q commit -madd" \
+"$CVSROOT_DIRNAME/first-dir/dir2/file5,v <-- file5
+initial revision: 1\.1"
+
+ 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
${CVSROOT_DIRNAME}/first-dir/dir2/file5,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest_fail branches2-38 "test -f CVS/status"
+
+ dotest branches2-39 "$testcvs rls -rb1 -l -R first-dir" \
+"$SPROG rls: Listing module: \`first-dir'
+first-dir:
+---- $ISO8601DATE 1\.1 file1
+---- $ISO8601DATE 1\.1\.2\.1 file2
+d--- $ISO8601DATE dir1
+d--- $ISO8601DATE dir2
+
+first-dir/dir1:
+---- $ISO8601DATE 1\.1\.2\.1 file3
+
+first-dir/dir2:
+---- $ISO8601DATE 1\.1\.2\.1 file4"
+
+ dokeep
+ cd ../../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r trunk b1a b1b
+ ;;
+
+
+
+ branches3)
+ # test local branch number support
+
+ # This test is skipped in $remotehost mode since the
+ # CVS_LOCAL_BRANCH_NUM is not inherited by the server process as it
+ # is with :fork:, for hopefully obvious reasons.
+ #
+ # FIXCVS? Is this correct? Should CVS_LOCAL_BRANCH_NUM be sent as
+ # a protocol extension or is it reasonable to only want this set on
+ # the server?
+
+ if test -n "$remotehost"; then :;else
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir branches3; cd branches3
+
+ dotest branches3-1 "$testcvs -q co first-dir"
+ cd first-dir
+ echo "file1 first revision" > file1
+ dotest branches3-2 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest branches3-3 "${testcvs} commit -m add file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ # Tag the file using a CVS_LOCAL_BRANCH_NUM of 1000
+ CVS_LOCAL_BRANCH_NUM=1000; export CVS_LOCAL_BRANCH_NUM
+ dotest branches3-4 "${testcvs} -q tag -b tag1" 'T file1'
+ unset CVS_LOCAL_BRANCH_NUM
+ dotest branches3-5 "${testcvs} -q log file1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ tag1: 1\.1\.0\.1000
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add
+============================================================================="
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r branches3
+ fi # !$remotehost
+ ;;
+
+
+
+ branches4)
+ # test where a tag is a branch tag in some files and a revision
+ # tag in others
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir branches4; cd branches4
+
+ dotest branches4-1 "$testcvs -q co first-dir"
+ cd first-dir
+ mkdir branches mixed mixed2 versions
+ dotest branches4-2 "${testcvs} -q add branches mixed mixed2 versions"
\
+"Directory ${CVSROOT_DIRNAME}/first-dir/branches added to the repository
+Directory ${CVSROOT_DIRNAME}/first-dir/mixed added to the repository
+Directory ${CVSROOT_DIRNAME}/first-dir/mixed2 added to the repository
+Directory ${CVSROOT_DIRNAME}/first-dir/versions added to the repository"
+
+ echo file1 >branches/file1
+ echo file2 >branches/file2
+ echo file3 >branches/file3
+ echo file4 >branches/file4
+ cp branches/file* mixed
+ cp branches/file* mixed2
+ cp branches/file* versions
+
+ dotest branches4-3 "${testcvs} -q add */file*" \
+"${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest branches4-3a "${testcvs} -Q ci -m."
+
+ dotest branches4-4 "${testcvs} -q tag xxx versions/file* mixed*/file1
mixed*/file3" \
+"T versions/file1
+T versions/file2
+T versions/file3
+T versions/file4
+T mixed/file1
+T mixed/file3
+T mixed2/file1
+T mixed2/file3"
+
+ dotest branches4-5 "${testcvs} -q tag -b xxx branches/file*
mixed*/file2 mixed*/file4" \
+"T branches/file1
+T branches/file2
+T branches/file3
+T branches/file4
+T mixed/file2
+T mixed/file4
+T mixed2/file2
+T mixed2/file4"
+
+ # make sure we get the appropriate warnings when updating
+ dotest branches4-6 "${testcvs} update -r xxx" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating branches
+${SPROG} update: Updating mixed
+${SPROG} update: warning: xxx is a branch tag in some files and a revision tag
in others\.
+${SPROG} update: Updating mixed2
+${SPROG} update: warning: xxx is a branch tag in some files and a revision tag
in others\.
+${SPROG} update: Updating versions"
+
+ # make sure we don't get warned in quiet modes
+ dotest branches4-7 "${testcvs} -q update -A"
+ dotest branches4-8 "${testcvs} -q update -r xxx"
+ dotest branches4-9 "${testcvs} -q update -A"
+ dotest branches4-10 "${testcvs} -Q update -r xxx"
+
+ # make sure the Tag files are correct
+ dotest branches4-11 "cat branches/CVS/Tag" "Txxx"
+ dotest branches4-12 "cat mixed/CVS/Tag" "Nxxx"
+ dotest branches4-13 "cat mixed2/CVS/Tag" "Nxxx"
+ dotest branches4-14 "cat versions/CVS/Tag" "Nxxx"
+
+ # We only warn if there's mixed usage in a single directory.
+ # We may want to consider changing that in the future.
+ dotest branches4-15 "${testcvs} update -r xxx branches versions" \
+"${SPROG} update: Updating branches
+${SPROG} update: Updating versions"
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r branches4
+ ;;
+
+
+
+ tagc)
+ # Test the tag -c option.
+ mkdir 1; cd 1
+ dotest tagc-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest tagc-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1 file2
+ dotest tagc-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest tagc-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ dotest tagc-5 "${testcvs} -q tag -c tag1" \
+"T file1
+T file2"
+ touch file1 file2
+ dotest tagc-6 "${testcvs} -q tag -c tag2" \
+"T file1
+T file2"
+ # Avoid timestamp granularity bugs (FIXME: CVS should be
+ # doing the sleep, right?).
+ sleep 1
+ echo myedit >>file1
+ dotest tagc-6a "${testcvs} rm -f file2" \
+"${SPROG} remove: scheduling .file2. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ touch file3
+ dotest tagc-6b "${testcvs} add file3" \
+"${SPROG} add: scheduling file .file3. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest_fail tagc-7 "${testcvs} -q tag -c tag3" \
+"${SPROG} tag: file1 is locally modified
+${SPROG} tag: file2 is locally modified
+${SPROG} tag: file3 is locally modified
+${SPROG} \[tag aborted\]: correct the above errors first!"
+ cd ../..
+ mkdir 2
+ cd 2
+ dotest tagc-8 "${testcvs} -q co first-dir" \
+"U first-dir/file1
+U first-dir/file2"
+ cd ../1/first-dir
+ dotest tagc-9 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+initial revision: 1\.1"
+ cd ../../2/first-dir
+ dotest tagc-10 "${testcvs} -q tag -c tag4" \
+"${SPROG} tag: \`file2' is no longer in the repository
+T file1
+T file2"
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ update-p)
+ # Make sure `cvs update -p -rT FILE' works from a branch when
+ # FILE is already on the trunk and is being added to that branch.
+
+ mkdir 1; cd 1
+ module=x
+
+ echo > unused-file
+
+ # Create the module.
+ dotest update-p-1 \
+ "$testcvs -Q import -m. $module X Y" ''
+
+ file=F
+ # Check it out and tag it.
+ dotest update-p-2 "$testcvs -Q co $module" ''
+ cd $module
+ dotest update-p-3 "$testcvs -Q tag -b B" ''
+ echo v1 > $file
+ dotest update-p-4 "$testcvs -Q add $file" ''
+ dotest update-p-5 "$testcvs -Q ci -m. $file"
+ dotest update-p-6 "$testcvs -Q tag T $file" ''
+ dotest update-p-7 "$testcvs -Q update -rB" ''
+
+ # This merge effectively adds file F on branch B.
+ dotest update-p-8 "$testcvs -Q update -jT" ''
+
+ # Before the fix that prompted the addition of this test,
+ # the following command would fail with this diagnostic:
+ # cvs update: conflict: F created independently by second party
+ dotest update-p-9 "$testcvs update -p -rT $file" \
+"===================================================================
+Checking out $file
+RCS: ${CVSROOT_DIRNAME}/$module/$file,v
+VERS: 1\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+v1"
+
+ # Repeat the above, but with $file removed.
+ # This exercises a slightly different code path.
+ rm $file
+ # Before the fix that prompted the addition of this test,
+ # the following command would fail with this diagnostic:
+ # cvs update: warning: new-born \`F' has disappeared
+ dotest update-p-10 "$testcvs update -p -rT $file" \
+"===================================================================
+Checking out $file
+RCS: ${CVSROOT_DIRNAME}/$module/$file,v
+VERS: 1\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+v1"
+
+ # Exercise yet another code path:
+ # the one that involves reviving a `dead' file.
+ # And a little more, for good measure...
+ touch new
+ dotest update-p-a1 "$testcvs -Q add new" ''
+ dotest update-p-a2 "$testcvs -Q update -p new" ''
+ dotest update-p-a3 "$testcvs -Q rm -f new" ''
+
+ # Both an update -A, *and* the following update are required
+ # to return to the state of being on the trunk with a $file
+ # that we can then remove.
+ dotest update-p-undead-0 "$testcvs update -A" \
+"${SPROG} update: Updating \.
+${SPROG} update: warning: new-born \`$file' has disappeared"
+ dotest update-p-undead-1 "$testcvs update" \
+"${SPROG} update: Updating \.
+U $file"
+ dotest update-p-undead-2 "$testcvs -Q update -p -rT $file" v1
+ dotest update-p-undead-3 "$testcvs -Q rm -f $file" ''
+ dotest update-p-undead-4 "$testcvs -Q update -p -rT $file" v1
+ dotest update-p-undead-5 "$testcvs -Q ci -m. $file"
+ dotest update-p-undead-6 "$testcvs -Q update -p -rT $file" v1
+ echo v2 > $file
+ dotest update-p-undead-7 "$testcvs -Q update -p -rT $file" v1
+ dotest update-p-undead-8 "$testcvs add $file" \
+"$SPROG add: Re-adding file .$file. after dead revision 1\.2\.
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+
+ dotest update-p-undead-9 "$testcvs -Q update -p -rT $file" v1
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ tagf)
+ # More tagging tests, including using tag -F -B to convert a
+ # branch tag to a regular tag and recovering thereof.
+
+ # Setup; check in first-dir/file1
+ mkdir 1; cd 1
+ dotest tagf-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest tagf-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1 file2
+ dotest tagf-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest tagf-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ # Now create a branch and commit a revision there.
+ dotest tagf-5 "${testcvs} -q tag -b br" "T file1
+T file2"
+ dotest tagf-6 "${testcvs} -q update -r br" ""
+ echo brmod >> file1
+ echo brmod >> file2
+ dotest tagf-7 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ # Here we try to make it a non-branch tag, but will
+ # succeed in getting only warnings, even with -F
+ # because converting a branch tag to non-branch
+ # is potentially catastrophic.
+ dotest tagf-8a "${testcvs} -q tag -F br" \
+"${SPROG} tag: file1: Not moving branch tag .br. from 1\.1\.2\.1 to
1\.1\\.2\.1\.
+${SPROG} tag: file2: Not moving branch tag .br. from 1\.1\.2\.1 to
1\.1\.2\.1\."
+ # however, if we *really* are sure we want to move a branch tag,
+ # "-F -B" will do the trick
+ dotest tagf-8 "${testcvs} -q tag -F -B br" "T file1
+T file2"
+ echo moremod >> file1
+ echo moremod >> file2
+ dotest tagf-9 "${testcvs} -q status -v file1" \
+"===================================================================
+File: file1 Status: Locally Modified
+
+ Working revision: 1\.1\.2\.1.*
+ Repository revision: 1\.1\.2\.1
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br (revision: 1\.1\.2\.1)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ br (revision: 1\.1\.2\.1)"
+
+ # Now, how do we recover?
+ dotest tagf-10 "${testcvs} -q tag -d br" "D file1
+D file2"
+ # This creates a new branch, 1.1.4. See the code in RCS_magicrev
+ # which will notice that there is a (non-magic) 1.1.2 and thus
+ # skip that number.
+ dotest tagf-11 "${testcvs} -q tag -r 1.1 -b br file1" "T file1"
+ # Fix it with admin -n (cf admin-18, admin-26-4).
+ dotest tagf-12 "${testcvs} -q admin -nbr:1.1.2 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+ # Another variation on the file2 test would be to use two working
+ # directories so that the update -r br would need to
+ # a merge to get from 1.1.2.1 to the head of the 1.1.2 branch.
+ dotest tagf-13 "${testcvs} -q update -r br" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1
+Merging differences between 1\.1\.2\.1 and 1\.1 into file1
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in file1
+C file1
+M file2"
+ # CVS is giving a conflict because we are trying to get back to
+ # 1.1.4. I'm not sure why it is a conflict rather than just
+ # "M file1".
+ dotest tagf-14 "cat file1" \
+"<<<<<<< file1
+brmod
+moremod
+[=]======
+[>]>>>>>> 1\.1"
+ echo resolve >file1
+ dotest tagf-15 "${testcvs} -q ci -m recovered" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.4\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+ # try accidentally deleting branch tag, "tag -d"
+ dotest_fail tagf-16 "${testcvs} tag -d br" \
+"${SPROG} tag: Untagging \.
+${SPROG} tag: Not removing branch tag .br. from
.${CVSROOT_DIRNAME}/first-dir/file1,v.\.
+${SPROG} tag: Not removing branch tag .br. from
.${CVSROOT_DIRNAME}/first-dir/file2,v.\."
+ # try accidentally deleting branch tag, "rtag -d"
+ dotest_fail tagf-17 "${testcvs} rtag -d br first-dir" \
+"${SPROG} rtag: Untagging first-dir
+${SPROG} rtag: Not removing branch tag .br. from
.${CVSROOT_DIRNAME}/first-dir/file1,v.\.
+${SPROG} rtag: Not removing branch tag .br. from
.${CVSROOT_DIRNAME}/first-dir/file2,v.\."
+ # try accidentally converting branch tag to non-branch tag "tag -F"
+ dotest tagf-18 "${testcvs} tag -r1.1 -F br file1" \
+"${SPROG} tag: file1: Not moving branch tag .br. from 1\.1\.4\.1 to 1\.1\."
+ # try accidentally converting branch tag to non-branch tag "rtag -F"
+ dotest tagf-19 "${testcvs} rtag -r1.1 -F br first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: first-dir/file1: Not moving branch tag .br. from 1\.1\.4\.1 to
1\.1\.
+${SPROG} rtag: first-dir/file2: Not moving branch tag .br. from 1\.1\.2\.2 to
1\.1\."
+ # create a non-branch tag
+ dotest tagf-20 "${testcvs} rtag regulartag first-dir" \
+"${SPROG} rtag: Tagging first-dir"
+ # try accidentally converting non-branch tag to branch tag (tag -F -B
-b)
+ dotest tagf-21 "${testcvs} tag -F -B -b regulartag file1" \
+"${SPROG} tag: file1: Not moving non-branch tag .regulartag. from 1\.1 to
1\.1\.4\.1\.0\.2 due to .-B. option\."
+ # try accidentally converting non-branch tag to branch rtag (rtag -F
-B -b)
+ dotest tagf-22 "${testcvs} rtag -F -B -b regulartag first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: first-dir/file1: Not moving non-branch tag .regulartag. from
1\.1 to 1\.1\.0\.6 due to .-B. option\.
+${SPROG} rtag: first-dir/file2: Not moving non-branch tag .regulartag. from
1\.1 to 1\.1\.0\.4 due to .-B. option\."
+ # Try accidentally deleting non-branch: (tag -d -B)
+ dotest_fail tagf-23 "${testcvs} tag -d -B regulartag file1" \
+"${SPROG} tag: Not removing non-branch tag .regulartag. from
.${CVSROOT_DIRNAME}/first-dir/file1,v. due to .-B. option\."
+ # Try accidentally deleting non-branch: (rtag -d -B)
+ dotest_fail tagf-24 \
+ "${testcvs} rtag -d -B regulartag first-dir" \
+"${SPROG} rtag: Untagging first-dir
+${SPROG} rtag: Not removing non-branch tag .regulartag. from
.${CVSROOT_DIRNAME}/first-dir/file1,v. due to .-B. option\.
+${SPROG} rtag: Not removing non-branch tag .regulartag. from
.${CVSROOT_DIRNAME}/first-dir/file2,v. due to .-B. option\."
+
+ # the following tests (throught the next commit) keep moving the same
+ # tag back and forth between 1.1.6 & 1.1.8 in file1 and between
+ # 1.1.4 and 1.1.6 in file2 since nothing was checked in on some of
+ # these branches and CVS only tracks branches via tags unless they
contain data.
+
+ # try intentionally converting non-branch tag to branch tag (tag -F
-b)
+ dotest tagf-25a "${testcvs} tag -F -b regulartag file1" "T file1"
+ # try intentionally moving a branch tag to a newly created branch
(tag -F -b -B)
+ dotest tagf-25b "${testcvs} tag -F -B -b -r1.1 regulartag file1" \
+"T file1"
+ # try intentionally converting mixed tags to branch tags (rtag -F -b)
+ dotest tagf-26a "${testcvs} rtag -F -b regulartag first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: first-dir/file1: Not moving branch tag .regulartag. from 1\.1
to 1\.1\.0\.8\."
+ # try intentionally converting a branch to a new branch tag (rtag -F
-b -B)
+ dotest tagf-26b "${testcvs} rtag -F -B -b -r1.1 regulartag first-dir"
\
+"${SPROG} rtag: Tagging first-dir"
+ # update to our new branch
+ dotest tagf-27 "${testcvs} update -r regulartag" \
+"${SPROG} update: Updating \.
+U file1
+U file2"
+ # commit some changes and see that all rev numbers look right
+ echo changes >> file1
+ echo changes >> file2
+ dotest tagf-28 "${testcvs} ci -m changes" \
+"${CPROG} commit: Examining \.
+${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.1\.8\.1; previous revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/file2,v <-- file2
+new revision: 1\.1\.6\.1; previous revision: 1\.1"
+ # try intentional branch to non-branch (tag -F -B)
+ dotest tagf-29 "${testcvs} tag -F -B -r1.1 regulartag file1" \
+"T file1"
+ # try non-branch to non-branch (tag -F -B)
+ dotest tagf-29a "${testcvs} tag -F -B -r br regulartag file1" \
+"${SPROG} tag: file1: Not moving non-branch tag .regulartag. from 1\.1 to
1\.1\.4\.1 due to .-B. option\."
+ # try mixed-branch to non-branch (rtag -F -B )
+ dotest tagf-29b "${testcvs} rtag -F -B -r br regulartag first-dir" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: first-dir/file1: Not moving non-branch tag .regulartag. from
1\.1 to 1\.1\.4\.1 due to .-B. option\."
+ # at this point, regulartag is a regular tag within
+ # file1 and file2
+
+ # try intentional branch to non-branch (rtag -F -B)
+ dotest tagf-30 "${testcvs} rtag -F -B -r1.1 br first-dir" \
+"${SPROG} rtag: Tagging first-dir"
+ # create a branch tag so we can try to delete it.
+ dotest tagf-31 "${testcvs} rtag -b brtag first-dir" \
+"${SPROG} rtag: Tagging first-dir"
+
+ # try intentinal deletion of branch tag (tag -d -B)
+ dotest tagf-32 "${testcvs} tag -d -B brtag file1" "D file1"
+ # try intentinal deletion of branch tag (rtag -d -B)
+ dotest tagf-33 "${testcvs} rtag -d -B brtag first-dir" \
+"${SPROG} rtag: Untagging first-dir"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ tag-space)
+ # Test tags with spaces in the names.
+ #
+ # Prior to releases 1.11.18 & 1.12.10, some commands used with
+ # tags with spaces in the names could hang CVS.
+
+ # Setup; check in first-dir/file1
+ mkdir 1; cd 1
+ dotest tag-space-init-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest tag-space-init-2 "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+ touch file1
+ dotest tag-space-init-3 "$testcvs add file1" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ dotest tag-space-init-4 "$testcvs -Q ci -m add"
+
+ # Reportedly, the following two tags make it past WinCVS.
+ dotest_fail tag-space-1 "$testcvs tag ' spacetag '" \
+"$SPROG \[tag aborted\]: tag \` spacetag ' must start with a letter"
+ dotest_fail tag-space-2 "$testcvs tag 'spacetag '" \
+"$SPROG \[tag aborted\]: tag \`spacetag ' has non-visible graphic characters"
+
+ if $remote; then
+ # Verify that this isn't a client check.
+ dotest tag-space-3 "$testcvs server" \
+"E $SPROG \[tag aborted\]: tag \` spacetag ' must start with a letter
+error " <<EOF
+Root $CVSROOT_DIRNAME
+UseUnchanged
+Argument --
+Argument spacetag
+Directory .
+$CVSROOT_DIRNAME/first-dir
+Entry /file1/1.1///
+Unchanged file1
+tag
+EOF
+
+ dotest tag-space-4 "$testcvs server" \
+"E $SPROG \[tag aborted\]: tag \`spacetag ' has non-visible graphic characters
+error " <<EOF
+Root $CVSROOT_DIRNAME
+UseUnchanged
+Argument --
+Argument spacetag
+Directory .
+$CVSROOT_DIRNAME/first-dir
+Entry /file1/1.1///
+Unchanged file1
+tag
+EOF
+ fi # $remote
+
+ # Any number of normal tags and branches were handled correctly.
+ dotest tag-space-5 "$testcvs -Q tag t1"
+ dotest tag-space-5b "$testcvs -Q tag t2"
+ dotest tag-space-5c "$testcvs -Q tag -b b1"
+
+ cd ../..
+ mkdir 2; cd 2
+
+ # But once a vendor branch exists, it's all over.
+ mkdir project; cd project
+ touch file1
+ dotest tag-space-init-4 \
+"$testcvs -Q import -mimport second-dir VENDOR RELEASE"
+
+ cd ..
+
+ dotest_fail tag-space-6 "$testcvs -Q co -r ' spacetag ' first-dir" \
+"$SPROG \[checkout aborted\]: tag \` spacetag ' must start with a letter"
+
+ # But when any files were imported, this test hung prior to CVS
+ # versions 1.11.18 & 1.12.10.
+ dotest_fail tag-space-7 "$testcvs -Q co -r ' spacetag ' second-dir" \
+"$SPROG \[checkout aborted\]: tag \` spacetag ' must start with a letter"
+
+ if $remote; then
+ # I based the client input in the next two tests on actual input
+ # from WinCVS 1.2.
+ dotest tag-space-8 "$testcvs server" \
+"E $SPROG \[checkout aborted\]: tag \` spacetag ' must start with a letter
+error " <<EOF
+Root $CVSROOT_DIRNAME
+Argument -P
+Argument -r
+Argument spacetag
+Argument first-dir
+Directory .
+$CVSROOT_DIRNAME
+co
+EOF
+
+ # Verify the test is not on the client side.
+ dotest tag-space-9 "$testcvs server" \
+"E $SPROG \[checkout aborted\]: tag \` spacetag ' must start with a letter
+error " <<EOF
+Root $CVSROOT_DIRNAME
+Argument -P
+Argument -r
+Argument spacetag
+Argument second-dir
+Directory .
+$CVSROOT_DIRNAME
+co
+EOF
+ fi # $remote
+
+ dotest tag-space-10 "$testcvs -Q co second-dir"
+ cd second-dir
+
+ # This test would also hang.
+ dotest_fail tag-space-11 "$testcvs -Q up -r ' spacetag '" \
+"$SPROG \[update aborted\]: tag \` spacetag ' must start with a letter"
+
+ if $remote; then
+ dotest tag-space-12 "$testcvs server" \
+"E $SPROG \[update aborted\]: tag \` spacetag ' must start with a letter
+error " <<EOF
+Root $CVSROOT_DIRNAME
+Argument -r
+Argument spacetag
+Argument -u
+Argument --
+Directory .
+$CVSROOT_DIRNAME
+Unchanged file1
+update
+EOF
+ fi # $remote
+
+ # I'm skipping tests for other commands that may have had the same
+ # problem. Hopefully, if a new issue arises, one of the above tests
+ # will catch the problem.
+
+ if $keep; then
+ echo Keeping $TESTDIR and exiting due to --keep
+ exit 0
+ fi
+
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ rcslib)
+ # Test librarification of RCS.
+ # First: test whether `cvs diff' handles $Name expansion
+ # correctly. We diff two revisions with their symbolic tags;
+ # neither tag should be expanded in the output. Also diff
+ # one revision with the working copy.
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest rcslib-diff1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+ echo "I am the first foo, and my name is $""Name$." > foo.c
+ dotest rcslib-diff2 "${testcvs} add -m new-file foo.c" \
+"${SPROG} add: scheduling file .foo\.c. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rcslib-diff3 "${testcvs} commit -m rev1 foo.c" \
+"${CVSROOT_DIRNAME}/first-dir/foo.c,v <-- foo\.c
+initial revision: 1\.1"
+ dotest rcslib-diff4 "${testcvs} tag first foo.c" "T foo\.c"
+ dotest rcslib-diff5 "${testcvs} update -p -r first foo.c" \
+"===================================================================
+Checking out foo\.c
+RCS: ${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+VERS: 1\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+I am the first foo, and my name is \$""Name: first \$\."
+
+ echo "I am the second foo, and my name is $""Name$." > foo.c
+ dotest rcslib-diff6 "${testcvs} commit -m rev2 foo.c" \
+"${CVSROOT_DIRNAME}/first-dir/foo\.c,v <-- foo\.c
+new revision: 1\.2; previous revision: 1\.1"
+ dotest rcslib-diff7 "${testcvs} tag second foo.c" "T foo\.c"
+ dotest rcslib-diff8 "${testcvs} update -p -r second foo.c" \
+"===================================================================
+Checking out foo\.c
+RCS: ${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+VERS: 1\.2
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+I am the second foo, and my name is \$""Name: second \$\."
+
+ dotest_fail rcslib-diff9 "${testcvs} diff -r first -r second" \
+"${SPROG} diff: Diffing \.
+Index: foo\.c
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+diff -r1\.1 -r1\.2
+1c1
+< I am the first foo, and my name is \$""Name: \$\.
+---
+> I am the second foo, and my name is \$""Name: \$\."
+
+ echo "I am the once and future foo, and my name is $""Name$." > foo.c
+ dotest_fail rcslib-diff10 "${testcvs} diff -r first" \
+"${SPROG} diff: Diffing \.
+Index: foo\.c
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+retrieving revision 1\.1
+diff -r1\.1 foo\.c
+1c1
+< I am the first foo, and my name is \$""Name: \$\.
+---
+> I am the once and future foo, and my name is \$""Name\$\."
+
+ # Test handling of libdiff options. diff gets quite enough
+ # of a workout elsewhere in sanity.sh, so we assume that it's
+ # mostly working properly if it passes all the other tests.
+ # The main one we want to try is regex handling, since we are
+ # using CVS's regex matcher and not diff's.
+
+ cat >rgx.c <<EOF
+test_regex (whiz, bang)
+{
+foo;
+bar;
+baz;
+grumble;
+}
+EOF
+
+ dotest rcslib-diffrgx-1 "${testcvs} -q add -m '' rgx.c" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rcslib-diffrgx-2 "${testcvs} -q ci -m '' rgx.c" \
+"$CVSROOT_DIRNAME/first-dir/rgx\.c,v <-- rgx\.c
+initial revision: 1\.1"
+ cat >rgx.c <<EOF
+test_regex (whiz, bang)
+{
+foo;
+bar;
+baz;
+mumble;
+}
+EOF
+ # Use dotest_fail because exit status from `cvs diff' must be 1.
+ #
+ # Incidentally test that CVS no longer splits diff arguments on
+ # spaces.
+ dotest_fail rcslib-diffrgx-3 "$testcvs diff -c -F'.* (' rgx.c" \
+"Index: rgx\.c
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/rgx\.c,v
+retrieving revision 1\.1
+diff -c -F '\.\* (' -r1\.1 rgx\.c
+\*\*\* rgx\.c ${RFCDATE} 1\.1
+--- rgx\.c ${RFCDATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* test_regex (whiz, bang)
+\*\*\* 3,7 \*\*\*\*
+ foo;
+ bar;
+ baz;
+! grumble;
+ }
+--- 3,7 ----
+ foo;
+ bar;
+ baz;
+! mumble;
+ }"
+
+ # Tests of rcsmerge/diff3. Merge operations get a good general
+ # workout elsewhere; we want to make sure that options are still
+ # handled properly. Try merging two branches with -kv, to test
+ # both -j and -k switches.
+
+ cd ..
+
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r first-dir
+
+ mkdir 1; cd 1
+ dotest rcslib-merge-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest rcslib-merge-2 "$testcvs -q add first-dir" \
+"Directory $CVSROOT_DIRNAME.*/first-dir added to the repository"
+ cd ..; rm -r 1
+
+ dotest rcslib-merge-3 "$testcvs -q co first-dir" ""
+ cd first-dir
+
+ echo '$''Revision$' > file1
+ echo '2' >> file1
+ echo '3' >> file1
+ dotest rcslib-merge-4 "${testcvs} -q add file1" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest rcslib-merge-5 "${testcvs} -q commit -m '' file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ sed -e 's/2/two/' file1 > f; mv f file1
+ dotest rcslib-merge-6 "${testcvs} -q commit -m '' file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest rcslib-merge-7 "${testcvs} -q tag -b -r 1.1 patch1" "T file1"
+ dotest rcslib-merge-8 "${testcvs} -q update -r patch1" "U file1"
+ dotest rcslib-merge-9 "${testcvs} -q status" \
+"===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: patch1 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest rcslib-merge-10 "cat file1" \
+'$''Revision: 1\.1 $
+2
+3'
+ sed -e 's/3/three/' file1 > f; mv f file1
+ dotest rcslib-merge-11 "${testcvs} -q commit -m '' file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest rcslib-merge-12 "${testcvs} -q update -kv -j1.2" \
+"U file1
+RCS file: ${CVSROOT_DIRNAME}/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"
+ dotest rcslib-merge-13 "cat file1" \
+"<<<<<<< file1
+1\.1\.2\.1
+2
+three
+[=]======
+1\.2
+two
+3
+[>]>>>>>> 1\.2"
+
+ # Test behavior of symlinks in the repository.
+ if test -n "$remotehost"; then
+ # Create the link on the remote system. This is because Cygwin's
+ # Windows support creates *.lnk files for Windows. When creating
+ # these in an SMB share from UNIX, these links won't work from the
+ # UNIX side.
+ modify_repo $CVS_RSH $remotehost "'ln -s file1,v
$CVSROOT_DIRNAME/first-dir/file2,v'"
+ else
+ modify_repo ln -s file1,v $CVSROOT_DIRNAME/first-dir/file2,v
+ fi
+ dotest rcslib-symlink-2 "$testcvs update file2" "U file2"
+ echo "This is a change" >> file2
+ dotest rcslib-symlink-3 "$testcvs ci -m because file2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file2
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # Switch as for rcslib-symlink-1
+ if test -n "$remotehost"; then
+ dotest rcslib-symlink-4 "$CVS_RSH $remotehost 'ls -l
$CVSROOT_DIRNAME/first-dir/file2,v'" \
+".*$CVSROOT_DIRNAME/first-dir/file2,v -> file1,v"
+ else
+ dotest rcslib-symlink-4 "ls -l $CVSROOT_DIRNAME/first-dir/file2,v" \
+".*$CVSROOT_DIRNAME/first-dir/file2,v -> file1,v"
+ fi
+
+ # CVS was failing to check both the symlink and the file
+ # for timestamp changes for a while. Test that.
+ rm file1
+ dotest rcslib-symlink-3a "${testcvs} -q up file1" \
+"${SPROG} update: warning: \`file1' was lost
+U file1"
+ echo "This is a change" >> file1
+ dotest rcslib-symlink-3b "${testcvs} ci -m because file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.[0-9]*; previous revision: 1\.1\.2\.[0-9]*"
+ dotest rcslib-symlink-3c "${testcvs} update file2" "U file2"
+
+ echo some new text >file3
+ dotest rcslib-symlink-3d "${testcvs} -Q add file3" ''
+ dotest rcslib-symlink-3e "$testcvs -Q ci -mtest file3"
+
+ rm -f ${CVSROOT_DIRNAME}/first-dir/file2,v
+ # As for rcslib-symlink-1
+ if test -n "$remotehost"; then
+ modify_repo "$CVS_RSH $remotehost 'ln -s Attic/file3,v
$CVSROOT_DIRNAME/first-dir/file2,v'"
+ else
+ modify_repo ln -s Attic/file3,v $CVSROOT_DIRNAME/first-dir/file2,v
+ fi
+
+ dotest rcslib-symlink-3g "$testcvs update file2" "U file2"
+
+ # restore the link to file1 for the following tests
+ dotest rcslib-symlink-3i "$testcvs -Q rm -f file3" ''
+ dotest rcslib-symlink-3j "$testcvs -Q ci -mwhatever file3"
+ rm -f $CVSROOT_DIRNAME/first-dir/file2,v
+ rm -f $CVSROOT_DIRNAME/first-dir/Attic/file3,v
+ # As for rcslib-symlink-1
+ if test -n "$remotehost"; then
+ modify_repo "$CVS_RSH $remotehost 'ln -s file1,v
$CVSROOT_DIRNAME/first-dir/file2,v'"
+ else
+ modify_repo ln -s file1,v $CVSROOT_DIRNAME/first-dir/file2,v
+ fi
+
+ # Test 5 reveals a problem with having symlinks in the
+ # repository. CVS will try to tag both of the files
+ # separately. After processing one, it will do the same
+ # operation to the other, which is actually the same file,
+ # so the tag will already be there. FIXME: do we bother
+ # changing operations to notice cases like this? This
+ # strikes me as a difficult problem. -Noel
+ dotest rcslib-symlink-5 "$testcvs tag the_tag" \
+"$SPROG tag: Tagging .
+T file1
+W file2 : the_tag already exists on version 1.1.2.3 : NOT MOVING tag to
version 1.1.2.1"
+ # As for rcslib-symlink-1
+ if test -n "$remotehost"; then
+ dotest rcslib-symlink-6 "$CVS_RSH $remotehost 'ls -l
$CVSROOT_DIRNAME/first-dir/file2,v'" \
+".*$CVSROOT_DIRNAME/first-dir/file2,v -> file1,v"
+ else
+ dotest rcslib-symlink-6 "ls -l $CVSROOT_DIRNAME/first-dir/file2,v" \
+".*$CVSROOT_DIRNAME/first-dir/file2,v -> file1,v"
+ fi
+
+ # Symlinks tend to interact poorly with the Attic.
+ cd ..
+ mkdir 2; cd 2
+ dotest rcslib-symlink-7 "$testcvs -q co first-dir" \
+"U first-dir/file1
+U first-dir/file2"
+ cd first-dir
+ dotest rcslib-symlink-8 "$testcvs rm -f file2" \
+"$SPROG remove: scheduling .file2. for removal
+$SPROG remove: use .$SPROG commit. to remove this file permanently"
+ dotest rcslib-symlink-9 "$testcvs -q ci -m rm-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file2
+new revision: delete; previous revision: 1\.2"
+ # OK, why this message happens twice is relatively clear
+ # (the check_* and rtag_* calls to start_recursion).
+ # Why it happens a third time I didn't try to find out.
+ #
+ # DRP: One of the error messages disappeared while I was making
+ # proxy modifications. Until I detect a deeper issue, I'm not
+ # going to stress over it.
+ #
+ # DRP: Both messages disappear starting with glibc 2.3.3 due to a bug
+ # in the glob function which causes it to fail to return broken
+ # symlinks. I'm submitting a bug fix to glibc which will hopefully
+ # be released with glibc 2.3.6. Once it is released and versions
+ # 2.3.3-2.3.5 of glibc become uncommon, the first, empty case below
+ # should be removed again.
+ dotest rcslib-symlink-10 \
+"$testcvs -q rtag -b -r the_tag brtag first-dir" "" \
+"$SPROG rtag: could not read RCS file for first-dir/file2
+$SPROG rtag: could not read RCS file for first-dir/file2"
+
+ # Restore file1 for the next test.
+ dotest rcslib-long-symlink-init-1 "$testcvs -Q up -A"
+ dotest rcslib-long-symlink-init-2 "$testcvs -Q add file1"
+ dotest rcslib-long-symlink-init-3 "$testcvs -Q ci -mback"
+
+ cd ../.. # $TESTDIR
+
+ # CVS has a hard-coded default link path size of 127 characters.
+ # Make sure it knows how to exceed that.
+ longpath=$CVSROOT_DIRNAME
+ count=0
+ while test $count -lt 10; do
+ # 10 * 30 characters + len $CVSROOT_DIRNAME
+ count=`expr $count + 1`
+ longpath=$longpath/123456789012345678901234567890
+ modify_repo mkdir $longpath
+ done
+ modify_repo cp $CVSROOT_DIRNAME/first-dir/file1,v $longpath
+ modify_repo mkdir $CVSROOT_DIRNAME/second-dir
+
+ # Switch as for rcslib-symlink-1
+ if test -n "$remotehost"; then
+ modify_repo $CVS_RSH $remotehost \
+ 'ln -s $longpath/file1,v $CVSROOT_DIRNAME/second-dir/fileX,v'
+ else
+ modify_repo ln -s $longpath/file1,v \
+ $CVSROOT_DIRNAME/second-dir/fileX,v
+ fi
+
+ dotest rcslib-long-symlink-2 "$testcvs co second-dir" \
+"$SPROG checkout: Updating second-dir
+U second-dir/fileX"
+
+ cd second-dir
+ echo change-it >>fileX
+
+ # Writes actually cause symlinks to be resolved.
+ dotest rcslib-long-symlink-3 "$testcvs -q ci -mwrite-it" \
+"$CVSROOT_DIRNAME/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/123456789012345678901234567890/file1,v
<-- fileX
+new revision: 1\.5; previous revision: 1\.4"
+
+ dokeep
+ cd ..
+
+ # Must remove the symlink first. Samba doesn't appear to show
+ # broken symlink across the SMB share, and rm -rf by itself
+ # will remove file1,v first and leave file2,v a broken link and the
+ # rm -rf will fail since it doesn't find file2,v and it still gets
+ # directory not empty errors removing cvsroot/first-dir.
+ #
+ # I'm not sure why I need to do this on $remotehost. The rm above
+ # rcslib-symlink-3j works fine, but the next one doesn't unless run
+ # remotely under Cygwin and using a TESTDIR on a Samba share.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost \
+"rm -f $CVSROOT_DIRNAME/first-dir/file2,v $CVSROOT_DIRNAME/second-dir/fileX,v"
+ fi
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir \
+ $CVSROOT_DIRNAME/123456789012345678901234567890
+ rm -r first-dir second-dir 2
+ ;;
+
+
+
+ multibranch)
+ # Test the ability to have several branchpoints coming off the
+ # same revision.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest multibranch-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+ echo 1:trunk-1 >file1
+ dotest multibranch-2 "${testcvs} add file1" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest_lit multibranch-3 "${testcvs} -q ci -m add-it" <<HERE
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1.1
+HERE
+ dotest multibranch-4 "${testcvs} tag -b br1" \
+"${SPROG} tag: Tagging \.
+T file1"
+ dotest multibranch-5 "${testcvs} tag -b br2" \
+"${SPROG} tag: Tagging \.
+T file1"
+ dotest multibranch-6 "${testcvs} -q update -r br1" ''
+ echo on-br1 >file1
+ dotest multibranch-7 "${testcvs} -q ci -m modify-on-br1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest multibranch-8 "${testcvs} -q update -r br2" 'U file1'
+ echo br2 adds a line >>file1
+ dotest multibranch-9 "${testcvs} -q ci -m modify-on-br2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.4\.1; previous revision: 1\.1"
+ dotest multibranch-10 "${testcvs} -q update -r br1" 'U file1'
+ dotest multibranch-11 "cat file1" 'on-br1'
+ dotest multibranch-12 "${testcvs} -q update -r br2" 'U file1'
+ dotest multibranch-13 "cat file1" '1:trunk-1
+br2 adds a line'
+
+ dotest multibranch-14 "${testcvs} log file1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ br2: 1\.1\.0\.4
+ br1: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2; 1\.1\.4;
+add-it
+----------------------------
+revision 1\.1\.4\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-br2
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+modify-on-br1
+============================================================================="
+
+ dokeep
+ cd ..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r first-dir
+ ;;
+
+
+
+ import) # test death after import
+ # Tests of "cvs import":
+ # basic2
+ # rdiff -- imports with keywords
+ # import -- more tests of imports with keywords
+ # importb -- -b option.
+ # importc -- bunch o' files in bunch o' directories
+ # importX -- -X option.
+ # importX2 -- CVSROOT/config ImportNewFilesToVendorBranchOnly
+ # flag
+ # modules3
+ # mflag -- various -m messages
+ # ignore -- import and cvsignore
+ # binwrap -- import and -k wrappers
+ # info -- imports which are rejected by verifymsg
+ # head -- intended to test vendor branches and HEAD,
+ # although it doesn't really do it yet.
+ # import-CVS -- refuse to import directories named "CVS".
+ # import-quirks -- short tests of import quirks.
+
+ # import
+ mkdir import-dir ; cd import-dir
+
+ for i in 1 2 3 4 ; do
+ echo imported file"$i" > imported-f"$i"
+ done
+
+ # This directory should be on the default ignore list,
+ # so it shouldn't get imported.
+ mkdir RCS
+ echo ignore.me >RCS/ignore.me
+
+ echo 'import should not expand $''Id$' >>imported-f2
+ cp imported-f2 ../imported-f2-orig.tmp
+
+ dotest_sort import-96 \
+"${testcvs} import -m first-import first-dir vendor-branch junk-1_0" \
+"
+
+I first-dir/RCS
+N first-dir/imported-f1
+N first-dir/imported-f2
+N first-dir/imported-f3
+N first-dir/imported-f4
+No conflicts created by this import"
+
+ dotest import-96.5 "$diff_u ../imported-f2-orig.tmp imported-f2"
+
+ cd ..
+
+ # co
+ dotest import-97 "${testcvs} -q co first-dir" \
+"U first-dir/imported-f1
+U first-dir/imported-f2
+U first-dir/imported-f3
+U first-dir/imported-f4"
+
+ cd first-dir
+
+ for i in 1 2 3 4 ; do
+ dotest import-98-$i "test -f imported-f$i" ''
+ done
+ dotest_fail import-98.5 "test -d RCS" ''
+
+ # remove
+ rm imported-f1
+ dotest import-99 "${testcvs} rm imported-f1" \
+"${SPROG}"' remove: scheduling `imported-f1'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove this file permanently'
+
+ # change
+ echo local-change >> imported-f2
+
+ # commit
+ dotest import-100 "${testcvs} ci -m local-changes" \
+"${CPROG} commit: Examining .
+${CVSROOT_DIRNAME}/first-dir/imported-f1,v <-- imported-f1
+new revision: delete; previous revision: 1\.1\.1\.1
+${CVSROOT_DIRNAME}/first-dir/imported-f2,v <-- imported-f2
+new revision: 1\.2; previous revision: 1\.1"
+
+ # log
+ dotest import-101 "${testcvs} log imported-f1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/imported-f1,v
+Working file: imported-f1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ junk-1_0: 1\.1\.1\.1
+ vendor-branch: 1\.1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+local-changes
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+first-import
+============================================================================="
+
+ # update into the vendor branch.
+ dotest import-102 "${testcvs} update -rvendor-branch" \
+"${SPROG} update: Updating .
+U imported-f1
+U imported-f2"
+
+ # remove file4 on the vendor branch
+ rm imported-f4
+ dotest import-103 "${testcvs} rm imported-f4" \
+"${SPROG}"' remove: scheduling `imported-f4'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove this file permanently'
+
+ # commit
+ dotest import-104 \
+"${testcvs} ci -m vendor-removed imported-f4" \
+"${CVSROOT_DIRNAME}/first-dir/imported-f4,v <-- imported-f4
+new revision: delete; previous revision: 1\.1\.1\.1"
+
+ # update to main line
+ dotest import-105 "${testcvs} -q update -A" \
+"${SPROG} update: \`imported-f1' is no longer in the repository
+U imported-f2"
+
+ # second import - file4 deliberately unchanged
+ cd ../import-dir
+ for i in 1 2 3 ; do
+ echo rev 2 of file $i >> imported-f"$i"
+ done
+ cp imported-f2 ../imported-f2-orig.tmp
+
+ dotest_sort import-106 \
+"${testcvs} import -m second-import first-dir vendor-branch junk-2_0" \
+"
+
+
+ ${CPROG} checkout -j<prev_rel_tag> -jjunk-2_0 first-dir
+2 conflicts created by this import.
+C first-dir/imported-f1
+C first-dir/imported-f2
+I first-dir/RCS
+U first-dir/imported-f3
+U first-dir/imported-f4
+Use the following command to help the merge:"
+
+ dotest import-106.5 "$diff_u ../imported-f2-orig.tmp
imported-f2"
+
+ cd ..
+
+ rm imported-f2-orig.tmp
+
+ # co
+ dotest import-107 "${testcvs} co first-dir" \
+"${SPROG} checkout: Updating first-dir
+U first-dir/imported-f3
+U first-dir/imported-f4"
+
+ cd first-dir
+
+ dotest_fail import-108 "test -f imported-f1" ''
+
+ for i in 2 3 ; do
+ dotest import-109-$i "test -f imported-f$i" ''
+ done
+
+ # check vendor branch for file4
+ dotest import-110 "${testcvs} -q update -rvendor-branch" \
+"U imported-f1
+U imported-f2"
+
+ dotest import-111 "test -f imported-f4" ''
+
+ # update to main line
+ dotest import-112 "${testcvs} -q update -A" \
+"${SPROG} update: \`imported-f1' is no longer in the repository
+U imported-f2"
+
+ cd ..
+
+ dotest import-113 \
+"${testcvs} -q co -jjunk-1_0 -jjunk-2_0 first-dir" \
+"${SPROG} checkout: file first-dir/imported-f1 does not exist, but is present
in revision junk-2_0
+RCS file: ${CVSROOT_DIRNAME}/first-dir/imported-f2,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.2
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into imported-f2
+rcsmerge: warning: conflicts during merge
+first-dir/imported-f3 already contains the differences between 1\.1\.1\.1 and
1\.1\.1\.2
+first-dir/imported-f4 already contains the differences between 1\.1\.1\.1 and
1\.1\.1\.3"
+
+ cd first-dir
+
+ dotest_fail import-114 "test -f imported-f1" ''
+
+ for i in 2 3 ; do
+ dotest import-115-$i "test -f imported-f$i" ''
+ done
+
+ dotest import-116 'cat imported-f2' \
+'imported file2
+[<]<<<<<< imported-f2
+import should not expand \$''Id: imported-f2,v 1\.2 [0-9/]* [0-9:]*
'"${username}"' Exp \$
+local-change
+[=]======
+import should not expand \$''Id: imported-f2,v 1\.1\.1\.2 [0-9/]* [0-9:]*
'"${username}"' Exp \$
+rev 2 of file 2
+[>]>>>>>> 1\.1\.1\.2'
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r import-dir
+ ;;
+
+
+
+ importb)
+ # More cvs import tests, especially -b option.
+
+ # OK, first we get some sources from the NetMunger project, and
+ # import them into the 1.1.1 vendor branch.
+ mkdir imp-dir
+ cd imp-dir
+ echo 'OpenMunger sources' >file1
+ echo 'OpenMunger sources' >file2
+ dotest_sort importb-1 \
+"${testcvs} import -m add first-dir openmunger openmunger-1_0" \
+"
+
+N first-dir/file1
+N first-dir/file2
+No conflicts created by this import"
+ cd ..
+ rm -r imp-dir
+
+ # Now we put the sources we get from FreeMunger into 1.1.3
+ mkdir imp-dir
+ cd imp-dir
+ echo 'FreeMunger sources' >file1
+ echo 'FreeMunger sources' >file2
+ # Not completely sure how the conflict detection is supposed to
+ # be working here (haven't really thought about it).
+ # We use an explicit -d option to test that it is reflected
+ # in the suggested checkout.
+ dotest_sort importb-2 \
+"$testcvs -d '$CVSROOT' import -m add -b 1.1.3 \
+ first-dir freemunger freemunger-1_0" \
+"
+
+
+ ${CPROG} -d ${CVSROOT} checkout -j<prev_rel_tag> -jfreemunger-1_0 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
+
+ # Now a test of main branch import (into second-dir, not first-dir).
+ mkdir imp-dir
+ cd imp-dir
+ echo 'my own stuff' >mine1.c
+ echo 'my own stuff' >mine2.c
+ dotest_fail importb-3 \
+"${testcvs} import -m add -b 1 second-dir dummy really_dumb_y" \
+"$CPROG \[import aborted\]: Only numeric branch specifications with two dots
are
+supported by import, not \`1'\. For example: \`1\.1\.1'\."
+ : when we implement main-branch import, should be \
+"N second-dir/mine1\.c
+N second-dir/mine2\.c
+
+No conflicts created by this import"
+ cd ..
+ rm -r imp-dir
+
+ mkdir 1
+ cd 1
+ # when we implement main branch import, will want to
+ # add "second-dir" here.
+ dotest importb-4 "${testcvs} -q co first-dir" \
+"U first-dir/file1
+U first-dir/file2"
+ cd first-dir
+ dotest importb-5 "${testcvs} -q log file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch: 1\.1\.1
+locks: strict
+access list:
+symbolic names:
+ freemunger-1_0: 1\.1\.3\.1
+ freemunger: 1\.1\.3
+ openmunger-1_0: 1\.1\.1\.1
+ openmunger: 1\.1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1; 1\.1\.3;
+Initial revision
+----------------------------
+revision 1\.1\.3\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+add
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+add
+============================================================================="
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ importc)
+ # Test importing a bunch o' files in a bunch o' directories.
+ # Also the -d option.
+
+ # Set a predictable time zone for these tests.
+ save_TZ=$TZ
+ TZ=UTC0; export TZ
+
+ mkdir 1; cd 1
+ mkdir adir bdir cdir
+ mkdir adir/sub1 adir/sub2
+ mkdir adir/sub1/ssdir
+ mkdir bdir/subdir
+ touch adir/sub1/file1 adir/sub2/file2 adir/sub1/ssdir/ssfile
+ touch -t ${TOUCH1971} bdir/subdir/file1
+ touch -t ${TOUCH2034} cdir/cfile
+ dotest_sort importc-1 \
+"${testcvs} import -d -m import-it first-dir vendor release" \
+"
+
+N first-dir/adir/sub1/file1
+N first-dir/adir/sub1/ssdir/ssfile
+N first-dir/adir/sub2/file2
+N first-dir/bdir/subdir/file1
+N first-dir/cdir/cfile
+No conflicts created by this import
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/adir
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/adir/sub1
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/adir/sub1/ssdir
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/adir/sub2
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/bdir
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/bdir/subdir
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/first-dir/cdir"
+ cd ..
+ mkdir 2; cd 2
+ dotest importc-2 "${testcvs} -q co first-dir" \
+"U first-dir/adir/sub1/file1
+U first-dir/adir/sub1/ssdir/ssfile
+U first-dir/adir/sub2/file2
+U first-dir/bdir/subdir/file1
+U first-dir/cdir/cfile"
+ cd first-dir
+ dotest importc-3 "${testcvs} update adir/sub1" \
+"${SPROG} update: Updating adir/sub1
+${SPROG} update: Updating adir/sub1/ssdir"
+ dotest importc-4 "${testcvs} update adir/sub1 bdir/subdir" \
+"${SPROG} update: Updating adir/sub1
+${SPROG} update: Updating adir/sub1/ssdir
+${SPROG} update: Updating bdir/subdir"
+
+ echo modify >>cdir/cfile
+ dotest importc-5 \
+"${testcvs} -q rtag -b -r release wip_test first-dir" ""
+ dotest importc-6 "${testcvs} -q update -r wip_test" "M cdir/cfile"
+
+ # This used to fail in local mode
+ dotest importc-7 "${testcvs} -q ci -m modify -r wip_test" \
+"$CVSROOT_DIRNAME/first-dir/cdir/cfile,v <-- cdir/cfile
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1"
+
+ # TODO: should also be testing "import -d" when we update
+ # an existing file.
+ dotest importc-8 "${testcvs} -q log cdir/cfile" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/cdir/cfile,v
+Working file: cdir/cfile
+head: 1\.1
+branch: 1\.1\.1
+locks: strict
+access list:
+symbolic names:
+ wip_test: 1\.1\.1\.1\.0\.2
+ release: 1\.1\.1\.1
+ vendor: 1\.1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE2034}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE2034}; author: ${username}; state: Exp; lines: ${PLUS}0
-0; commitid: ${commitid};
+branches: 1\.1\.1\.1\.2;
+import-it
+----------------------------
+revision 1\.1\.1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify
+============================================================================="
+
+ dotest importc-9 "${testcvs} -q log bdir/subdir/file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/bdir/subdir/file1,v
+Working file: bdir/subdir/file1
+head: 1\.1
+branch: 1\.1\.1
+locks: strict
+access list:
+symbolic names:
+ wip_test: 1\.1\.1\.1\.0\.2
+ release: 1\.1\.1\.1
+ vendor: 1\.1\.1
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE1971}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE1971}; author: ${username}; state: Exp; lines: ${PLUS}0
-0; commitid: ${commitid};
+import-it
+============================================================================="
+ cd ..
+
+ # Now tests of absolute pathnames and .. as repository directory.
+ cd ../1
+ dotest_fail importc-10 \
+"${testcvs} import -m imp ../other vendor release2" \
+"${CPROG} \[import aborted\]: directory \.\./other not relative within the
repository"
+ dotest_fail importc-11 \
+"${testcvs} import -m imp ${TESTDIR}/other vendor release3" \
+"${CPROG} \[import aborted\]: directory ${TESTDIR}/other not relative within
the repository"
+ dotest_fail importc-12 "test -d ${TESTDIR}/other" ""
+
+ dokeep
+ TZ=$save_TZ
+ cd ..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ importX)
+ # More cvs import tests, especially -X option.
+
+ # OK, first we get some sources from the Munger version 0.9,
+ # and import them into the 1.1.1 vendor branch (w/o -X). (This
+ # will be used to test subsequent imports of the same file
+ # with -X.)
+ mkdir imp-dir
+ cd imp-dir
+ echo 'Munger sources 0.9' >file0
+ dotest_sort importX-1 \
+"${testcvs} import -m add first-dir munger munger-0_9" \
+"
+
+N first-dir/file0
+No conflicts created by this import"
+ cd ..
+ rm -r imp-dir
+
+ # Now we put the sources we get from Munger version 1.0 on
+ # to the 1.1.1 vendor branch using -X. (This imports a new
+ # version of file0, and imports all-new files file1 and file2.)
+ mkdir imp-dir
+ cd imp-dir
+ echo 'Munger sources' >file0
+ echo 'Munger sources' >file1
+ echo 'Munger sources' >file2
+ dotest_sort importX-2 \
+"${testcvs} import -X -m add first-dir munger munger-1_0" \
+"
+
+
+ ${CPROG} checkout -j<prev_rel_tag> -jmunger-1_0 first-dir
+N first-dir/file1
+N first-dir/file2
+No conflicts created by this import.
+U first-dir/file0
+Use the following command to help the merge:"
+ cd ..
+ rm -r imp-dir
+
+ # Now we put the sources we get from Munger version 1.1 on
+ # to the 1.1.1 vendor branch using -X. (This imports unchanged
+ # versions of file0 and file2, a changed version of file1, and
+ # an all-new file3.)
+ mkdir imp-dir
+ cd imp-dir
+ echo 'Munger sources' >file0
+ echo 'Munger sources 1.1' >file1
+ echo 'Munger sources' >file2
+ echo 'Munger sources 1.1' >file3
+ dotest_sort importX-3 \
+"$testcvs -d '$CVSROOT' import -X -m add first-dir munger munger-1_1" \
+"
+
+
+ ${CPROG} -d ${CVSROOT} checkout -j<prev_rel_tag> -jmunger-1_1 first-dir
+1 conflicts created by this import.
+C first-dir/file1
+N first-dir/file3
+U first-dir/file0
+U first-dir/file2
+Use the following command to help the merge:"
+ cd ..
+ rm -r imp-dir
+
+ mkdir 1
+ cd 1
+ # only file0 should be checked out
+ dotest importX-4 "${testcvs} -q co first-dir" \
+"U first-dir/file0"
+ cd first-dir
+
+ dotest importX-5 "${testcvs} -q log file0" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file0,v
+Working file: file0
+head: 1\.1
+branch: 1\.1\.1
+locks: strict
+access list:
+symbolic names:
+ munger-1_1: 1\.1\.1\.2
+ munger-1_0: 1\.1\.1\.2
+ munger-0_9: 1\.1\.1\.1
+ munger: 1\.1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+add
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+add
+============================================================================="
+
+ dotest importX-6 "${testcvs} -q log file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+Working file: file1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ munger-1_1: 1\.1\.1\.2
+ munger-1_0: 1\.1\.1\.1
+ munger: 1\.1\.1
+keyword substitution: kv
+total revisions: 4; selected revisions: 4
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+Revision 1\.1 was added on the vendor branch\.
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+add
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+add
+============================================================================="
+
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf ${CVSROOT_DIRNAME}/first-dir
+ ;;
+
+
+
+ importX2)
+ # Test ImportNewFilesToVendorBranchOnly config file option.
+
+ # On Windows, we can't check out CVSROOT, because the case
+ # insensitivity means that this conflicts with cvsroot.
+ mkdir wnt
+ cd wnt
+
+ dotest importX2-1 "${testcvs} -q co CVSROOT" "U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ echo "ImportNewFilesToVendorBranchOnly=yes" >> config
+
+ dotest importX2-2 "$testcvs -q ci -m force-cvs-import-X" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ../..
+
+ # Import a sources file, but do NOT specify -X. The new file
+ # should be killed, anyway (because of the config option).
+ mkdir imp-dir
+ cd imp-dir
+ echo 'source' >file1
+ dotest_sort importX2-3 \
+"${testcvs} import -m add first-dir source source-1_0" \
+"
+
+
+ ${CPROG} checkout -j<prev_rel_tag> -jsource-1_0 first-dir
+N first-dir/file1
+No conflicts created by this import.
+Use the following command to help the merge:"
+ cd ..
+ rm -r imp-dir
+
+ mkdir 1
+ cd 1
+ # **nothing** should be checked out**
+ dotest importX2-4 "${testcvs} -q co first-dir" ""
+
+ cd first-dir
+ dotest importX2-5 "${testcvs} -q log file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+Working file: file1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ source-1_0: 1\.1\.1\.1
+ source: 1\.1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+Revision 1\.1 was added on the vendor branch\.
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.1;
+Initial revision
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+add
+============================================================================="
+
+ dokeep
+ cd ../..
+ restore_adm
+ rm -r 1
+ rm -r wnt
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ import-CVS)
+ mkdir import-CVS
+ cd import-CVS
+ touch file1 file2 file3
+ dotest_fail import-CVS-1 "$testcvs import CVS vtag rtag" \
+"$CPROG import: The word \`CVS' is reserved by CVS and may not be used
+$CPROG \[import aborted\]: as a directory in a path or as a file name\."
+ mkdir sdir
+ mkdir sdir/CVS
+ touch sdir/CVS/file4 sdir/CVS/file5 sdir/file6 sdir/file7
+ # Calling the imported directory import-CVS is dual purpose in the
+ # following test. It makes sure the path test which matched above
+ # wasn't too strict.
+ dotest_sort import-CVS-2 \
+"$testcvs import -I! -mimport import-CVS vtag rtag" \
+"
+
+I import-CVS/sdir/CVS
+N import-CVS/file1
+N import-CVS/file2
+N import-CVS/file3
+N import-CVS/sdir/file6
+N import-CVS/sdir/file7
+No conflicts created by this import
+$SPROG import: Importing $CVSROOT_DIRNAME/import-CVS/sdir"
+
+ dokeep
+ cd ..
+ rm -r import-CVS
+ modify_repo rm -rf $CVSROOT_DIRNAME/import-CVS
+ ;;
+
+
+
+ import-quirks)
+ # Short tests of quirky import behavior.
+ #
+ # For a list of other import tests with short descriptions, see the
+ # comment header of the "import" test.
+ mkdir import-quirks
+ cd import-quirks
+ touch file1 file2 file3
+
+ # CVS prior to 1.11.18 and 1.12.10 used to happily import to
+ # "branch 1.1", creating RCS archives with revisions like,
+ # "1.1..1". That double-dot is *not* a typo.
+ dotest_fail import-quirks-1 \
+"$testcvs import -b1.1. -mbad-bad-bad import-quirks VB RT" \
+"$CPROG \[import aborted\]: Only numeric branch specifications with two dots
are
+supported by import, not \`1\.1\.'\. For example: \`1\.1\.1'\."
+
+ dotest_fail import-quirks-2 \
+"$testcvs import -b1.1.1.. -mbad-bad-bad import-quirks VB RT" \
+"$CPROG \[import aborted\]: Only numeric branch specifications with two dots
are
+supported by import, not \`1\.1\.1\.\.'\. For example: \`1\.1\.1'\."
+
+ # Try a few odd numbers. This is hardly comprehensive.
+ dotest_sort import-quirks-2 \
+"$testcvs import -b10.10.101 -mthis-ones-ok import-quirks-2 VB RT" \
+"
+
+N import-quirks-2/file1
+N import-quirks-2/file2
+N import-quirks-2/file3
+No conflicts created by this import"
+
+ dotest_sort import-quirks-3 \
+"$testcvs import -b2345678901.2345678901.2345678901 -mthis-ones-ok
import-quirks-3 VB RT" \
+"
+
+N import-quirks-3/file1
+N import-quirks-3/file2
+N import-quirks-3/file3
+No conflicts created by this import"
+
+ dotest_sort import-quirks-4 \
+"$testcvs import -b1.1.2 -mthis-ones-ok import-quirks-4 VB RT" \
+"
+
+N import-quirks-4/file1
+N import-quirks-4/file2
+N import-quirks-4/file3
+No conflicts created by this import"
+
+ dokeep
+ cd ..
+ rm -r import-quirks
+ rm -rf $CVSROOT_DIRNAME/import-quirks-2 \
+ $CVSROOT_DIRNAME/import-quirks-3 \
+ $CVSROOT_DIRNAME/import-quirks-4
+ ;;
+
+
+
+ import-after-initial)
+ # Properly handle the case in which the first version of a
+ # file is created by a regular cvs add and commit, and there
+ # is a subsequent cvs import of the same file. cvs update with
+ # a date tag must resort to searching the vendor branch only if
+ # the initial version of the file was created at the same time
+ # as the initial version on the vendor branch.
+
+ mkdir 1; cd 1
+ module=x
+
+ echo > unused-file
+
+ # Create the module.
+ dotest import-after-initial-1 \
+ "$testcvs -Q import -m. $module X Y" ''
+
+ file=m
+ # Check it out and add a file.
+ dotest import-after-initial-2 "$testcvs -Q co $module" ''
+ cd $module
+ echo original > $file
+ dotest import-after-initial-3 "${testcvs} -Q add $file" ""
+ dotest import-after-initial-4 "$testcvs -Q ci -m. $file"
+
+ # Delay a little so the following import isn't done in the same
+ # second as the preceding commit.
+ sleep 2
+
+ # Do the first import of $file *after* $file already has an
+ # initial version.
+ mkdir sub
+ cd sub
+ echo newer-via-import > $file
+ dotest import-after-initial-5 \
+ "$testcvs -Q import -m. $module X Y2" ''
+ cd ..
+
+ # Sleep a second so we're sure to be after the second of the import.
+ sleep 1
+
+ dotest import-after-initial-6 \
+ "$testcvs -Q update -p -D now $file" 'original'
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ branch-after-import)
+ # Test branching after an import via both cvs tag -b and
+ # cvs add to verify that the HEAD remains at 1.1.1.1
+ # This was a FreeBSD bug documented at the URL:
+ # http://www.freebsd.org/cgi/query-pr.cgi?pr=4033
+
+ mkdir branch-after-import
+ cd branch-after-import
+
+ # OK, first we get some sources from the NetMunger project,
+ # and import them into the 1.1.1 vendor branch.
+ mkdir imp-dir
+ cd imp-dir
+ echo 'OpenMunger sources' >file1
+ echo 'OpenMunger sources' >file2
+ dotest_sort branch-after-import-1 \
+"${testcvs} import -m add first-dir openmunger openmunger-1_0" \
+'
+
+N first-dir/file1
+N first-dir/file2
+No conflicts created by this import'
+ cd ..
+
+ # Next checkout the new module
+ dotest branch-after-import-2 \
+"${testcvs} -q co first-dir" \
+'U first-dir/file1
+U first-dir/file2'
+ cd first-dir
+ # Branch tag the file1 and cvs add file2,
+ # the branch should remain the same in both cases
+ # such that a new import will not require a conflict
+ # resolution.
+ dotest branch-after-import-3 \
+"${testcvs} tag -b TESTTOTRON file1" \
+'T file1'
+ dotest branch-after-import-4 \
+"${testcvs} -q update -r TESTTOTRON" \
+"${SPROG} update: \`file2' is no longer in the repository"
+
+ cp ../imp-dir/file2 .
+ dotest branch-after-import-5 \
+"${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition on branch .TESTTOTRON.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ dotest branch-after-import-6 \
+"$testcvs commit -m cvs-add file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.1\.1\.2\.2; previous revision: 1\.1\.1\.1\.2\.1"
+
+ dokeep
+ cd ../..
+ rm -r branch-after-import
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ join)
+ # Test doing joins which involve adding and removing files.
+ # Variety of scenarios (see list below), in the context of:
+ # * merge changes from T1 to T2 into the main line
+ # * merge changes from branch 'branch' into the main line
+ # * merge changes from branch 'branch' into branch 'br2'.
+ # See also binfile2, which does similar things with binary files.
+ # See also join2, which tests joining (and update -A) on only
+ # a single file, rather than a directory.
+ # See also rmadd2, which tests -j cases not involving branches
+ # (e.g. undoing a commit)
+ # See also join3, which tests some cases involving the greatest
+ # common ancestor. Here is a list of tests according to branch
+ # topology:
+ #
+ # --->bp---->trunk too many to mention
+ # \----->branch
+ #
+ # /----->branch1
+ # --->bp---->trunk multibranch, multibranch2
+ # \----->branch2
+ #
+ # --->bp1----->bp2---->trunk join3
+ # \->br1 \->br2
+ #
+ # --->bp1----->trunk
+ # \----bp2---->branch branches
+ # \------>branch-of-branch
+
+ # We check merging changes from T1 to T2 into the main line.
+ # Here are the interesting cases I can think of:
+ # 1) File added between T1 and T2, not on main line.
+ # File should be marked for addition.
+ # 2) File added between T1 and T2, also added on main line.
+ # Conflict.
+ # 3) File removed between T1 and T2, unchanged on main line.
+ # File should be marked for removal.
+ # 4) File removed between T1 and T2, modified on main line.
+ # If mod checked in, file should be marked for removal.
+ # If mod still in working directory, conflict.
+ # 5) File removed between T1 and T2, was never on main line.
+ # Nothing should happen.
+ # 6) File removed between T1 and T2, also removed on main line.
+ # Nothing should happen.
+ # 7) File not added between T1 and T2, added on main line.
+ # Nothing should happen.
+ # 8) File not modified between T1 and T2, removed on main line.
+ # Nothing should happen.
+ # 9) File modified between T1 and T2, removed on main line.
+ # Conflict.
+ # 10) File was never on branch, removed on main line.
+ # Nothing should happen.
+
+ # We also check merging changes from a branch into the main
+ # line. Here are the interesting cases:
+ # 1) File added on branch, not on main line.
+ # File should be marked for addition.
+ # 2) File added on branch, also added on main line.
+ # Conflict.
+ # 3) File removed on branch, unchanged on main line.
+ # File should be marked for removal.
+ # 4) File removed on branch, modified on main line.
+ # Conflict.
+ # 5) File removed on branch, was never on main line.
+ # Nothing should happen.
+ # 6) File removed on branch, also removed on main line.
+ # Nothing should happen.
+ # 7) File added on main line, not added on branch.
+ # Nothing should happen.
+ # 8) File removed on main line, not modified on branch.
+ # Nothing should happen.
+ # 9) File modified on branch, removed on main line.
+ # Conflict.
+ # 10) File was never on branch, removed on main line.
+ # Nothing should happen.
+
+ # In the tests below, fileN represents case N in the above
+ # lists.
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+ dotest join-1 "$testcvs -q co first-dir"
+
+ cd first-dir
+
+ # Add two files.
+ echo 'first revision of file3' > file3
+ echo 'first revision of file4' > file4
+ echo 'first revision of file6' > file6
+ echo 'first revision of file8' > file8
+ echo 'first revision of file9' > file9
+ dotest join-2 "${testcvs} add file3 file4 file6 file8 file9" \
+"${SPROG}"' add: scheduling file `file3'\'' for addition
+'"${SPROG}"' add: scheduling file `file4'\'' for addition
+'"${SPROG}"' add: scheduling file `file6'\'' for addition
+'"${SPROG}"' add: scheduling file `file8'\'' for addition
+'"${SPROG}"' add: scheduling file `file9'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+
+ dotest join-3 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file6,v <-- file6
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file8,v <-- file8
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file9,v <-- file9
+initial revision: 1\.1"
+
+ # Make a branch.
+ dotest join-4 "${testcvs} -q tag -b branch ." \
+'T file3
+T file4
+T file6
+T file8
+T file9'
+
+ # Add file2, file7, and file10, modify file4, and remove
+ # file6, file8, and file9.
+ echo 'first revision of file2' > file2
+ echo 'second revision of file4' > file4
+ echo 'first revision of file7' > file7
+ rm file6 file8 file9
+ echo 'first revision of file10' > file10
+ dotest join-5 "${testcvs} add file2 file7 file10" \
+"${SPROG}"' add: scheduling file `file2'\'' for addition
+'"${SPROG}"' add: scheduling file `file7'\'' for addition
+'"${SPROG}"' add: scheduling file `file10'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+ dotest join-6 "${testcvs} rm file6 file8 file9" \
+"${SPROG}"' remove: scheduling `file6'\'' for removal
+'"${SPROG}"' remove: scheduling `file8'\'' for removal
+'"${SPROG}"' remove: scheduling `file9'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove these files
permanently'
+ dotest join-7 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file10,v <-- file10
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file6,v <-- file6
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file7,v <-- file7
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file8,v <-- file8
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file9,v <-- file9
+new revision: delete; previous revision: 1\.1"
+
+ # Remove file10
+ dotest join-7a "${testcvs} rm -f file10" \
+"${SPROG}"' remove: scheduling `file10'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove this file permanently'
+ dotest join-7b "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file10,v <-- file10
+new revision: delete; previous revision: 1\.1"
+
+ # Check out the branch.
+ cd ../..
+ mkdir 2
+ cd 2
+ dotest join-8 "${testcvs} -q co -r branch first-dir" \
+'U first-dir/file3
+U first-dir/file4
+U first-dir/file6
+U first-dir/file8
+U first-dir/file9'
+
+ cd first-dir
+
+ # Modify the files on the branch, so that T1 is not an
+ # ancestor of the main line, and add file5
+ echo 'first branch revision of file3' > file3
+ echo 'first branch revision of file4' > file4
+ echo 'first branch revision of file5' > file5
+ echo 'first branch revision of file6' > file6
+ echo 'first branch revision of file9' > file9
+ dotest join-9 "${testcvs} add file5" \
+"${SPROG}"' add: scheduling file `file5'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest join-10 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file5,v <-- file5
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file6,v <-- file6
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file9,v <-- file9
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Tag the current revisions on the branch.
+ dotest join-11 "${testcvs} -q tag T1 ." \
+'T file3
+T file4
+T file5
+T file6
+T file8
+T file9'
+
+ # Add file1 and file2, modify file9, and remove the other files.
+ echo 'first branch revision of file1' > file1
+ echo 'first branch revision of file2' > file2
+ echo 'second branch revision of file9' > file9
+ rm file3 file4 file5 file6
+ dotest join-12 "${testcvs} add file1 file2" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: scheduling file `file2'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+ dotest join-13 "${testcvs} rm file3 file4 file5 file6" \
+"${SPROG}"' remove: scheduling `file3'\'' for removal
+'"${SPROG}"' remove: scheduling `file4'\'' for removal
+'"${SPROG}"' remove: scheduling `file5'\'' for removal
+'"${SPROG}"' remove: scheduling `file6'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove these files
permanently'
+ dotest join-14 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/Attic/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file5,v <-- file5
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file6,v <-- file6
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file9,v <-- file9
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # Tag the current revisions on the branch.
+ dotest join-15 "${testcvs} -q tag T2 ." \
+'T file1
+T file2
+T file8
+T file9'
+
+ # Do a checkout with a merge.
+ cd ../..
+ mkdir 3
+ cd 3
+ dotest_fail join-16a \
+"$testcvs -q co -jno-such-tag -jT2 first-dir" \
+"$SPROG \[checkout aborted\]: no such tag \`no-such-tag'"
+
+ dotest join-16b "$testcvs -q co -jT1 -jT2 first-dir" \
+"U first-dir/file1
+U first-dir/file2
+${SPROG} checkout: file first-dir/file2 exists, but has been added in revision
T2
+U first-dir/file3
+${SPROG} checkout: scheduling \`first-dir/file3' for removal
+U first-dir/file4
+${SPROG} checkout: scheduling \`first-dir/file4' for removal
+U first-dir/file7
+${SPROG} checkout: file first-dir/file9 does not exist, but is present in
revision T2"
+
+ # Verify that the right changes have been scheduled.
+ cd first-dir
+ dotest join-17 "${testcvs} -q update" \
+'A file1
+R file3
+R file4'
+
+ # Modify file4 locally, and do an update with a merge.
+ cd ../../1/first-dir
+ echo 'third revision of file4' > file4
+ dotest join-18 "${testcvs} -q update -jT1 -jT2 ." \
+"U file1
+$SPROG update: file file2 exists, but has been added in revision T2
+$SPROG update: scheduling \`file3' for removal
+M file4
+$SPROG update: file file4 is locally modified, but has been removed in
revision T2
+$SPROG update: file file9 does not exist, but is present in revision T2"
+
+ # Verify that the right changes have been scheduled.
+ dotest join-19 "${testcvs} -q update" \
+'A file1
+R file3
+M file4'
+
+ # Do a checkout with a merge from a single revision.
+
+ # FIXME: CVS currently gets this wrong. file2 has been
+ # added on both the branch and the main line, and so should
+ # be regarded as a conflict. However, given the way that
+ # CVS sets up the RCS file, there is no way to distinguish
+ # this case from the case of file2 having existed before the
+ # branch was made. This could be fixed by reserving
+ # a revision somewhere, perhaps 1.1, as an always dead
+ # revision which can be used as the source for files added
+ # on branches.
+ cd ../../3
+ rm -r first-dir
+ dotest join-20 "${testcvs} -q co -jbranch first-dir" \
+"U first-dir/file1
+U first-dir/file2
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.2
+Merging differences between 1\.1 and 1\.1\.2\.2 into file2
+U first-dir/file3
+${SPROG} checkout: scheduling \`first-dir/file3' for removal
+U first-dir/file4
+${SPROG} checkout: file first-dir/file4 has been modified, but has been
removed in revision branch
+U first-dir/file7
+${SPROG} checkout: file first-dir/file9 does not exist, but is present in
revision branch"
+
+ # Verify that the right changes have been scheduled.
+ # The M file2 line is a bug; see above join-20.
+ cd first-dir
+ dotest join-21 "${testcvs} -q update" \
+'A file1
+M file2
+R file3'
+
+ # Checkout the main line again.
+ cd ../../1
+ rm -r first-dir
+ dotest join-22 "${testcvs} -q co first-dir" \
+'U first-dir/file2
+U first-dir/file3
+U first-dir/file4
+U first-dir/file7'
+
+ # Modify file4 locally, and do an update with a merge from a
+ # single revision.
+ # The file2 handling is a bug; see above join-20.
+ cd first-dir
+ echo 'third revision of file4' > file4
+ dotest join-23 "${testcvs} -q update -jbranch ." \
+"U file1
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.2
+Merging differences between 1\.1 and 1\.1\.2\.2 into file2
+${SPROG} update: scheduling \`file3' for removal
+M file4
+${SPROG} update: file file4 is locally modified, but has been removed in
revision branch
+${SPROG} update: file file9 does not exist, but is present in revision branch"
+
+ # Verify that the right changes have been scheduled.
+ # The M file2 line is a bug; see above join-20
+ dotest join-24 "${testcvs} -q update" \
+'A file1
+M file2
+R file3
+M file4'
+
+ cd ..
+
+ # Checkout the main line again and make a new branch which we
+ # merge to.
+ rm -r first-dir
+ dotest join-25 "${testcvs} -q co first-dir" \
+'U first-dir/file2
+U first-dir/file3
+U first-dir/file4
+U first-dir/file7'
+ cd first-dir
+ dotest join-26 "${testcvs} -q tag -b br2" \
+"T file2
+T file3
+T file4
+T file7"
+ dotest join-27 "${testcvs} -q update -r br2" ""
+ # The handling of file8 and file9 here look fishy to me. I don't
+ # see why it should be different from the case where we merge to
+ # the trunk (e.g. join-23).
+ dotest join-28 "${testcvs} -q update -j branch" \
+"U file1
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+retrieving revision 1.1
+retrieving revision 1.1.2.2
+Merging differences between 1.1 and 1.1.2.2 into file2
+${SPROG} update: scheduling \`file3' for removal
+${SPROG} update: file file4 has been modified, but has been removed in
revision branch
+U file8
+U file9"
+ # Verify that the right changes have been scheduled.
+ dotest join-29 "${testcvs} -q update" \
+"A file1
+M file2
+R file3
+A file8
+A file9"
+
+ # Checkout the mainline again to try updating and merging between two
+ # branches in the same step
+ # this seems a likely scenario - the user finishes up on branch and
+ # updates to br2 and merges in the same step - and there was a bug
+ # once that if the file was removed in the update then it wouldn't be
+ # readded in the merge
+ cd ..
+ rm -r first-dir
+ dotest join-twobranch-1 "${testcvs} -q co -rbranch first-dir" \
+'U first-dir/file1
+U first-dir/file2
+U first-dir/file8
+U first-dir/file9'
+ cd first-dir
+ dotest join-twobranch-2 "${testcvs} -q update -rbr2 -jbranch" \
+"${SPROG} update: \`file1' is no longer in the repository
+U file1
+U file2
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.2
+Merging differences between 1\.1 and 1\.1\.2\.2 into file2
+U file3
+${SPROG} update: scheduling \`file3' for removal
+U file4
+${SPROG} update: file file4 has been modified, but has been removed in
revision branch
+U file7
+${SPROG} update: \`file8' is no longer in the repository
+U file8
+${SPROG} update: \`file9' is no longer in the repository
+U file9"
+ # Verify that the right changes have been scheduled.
+ dotest join-twobranch-3 "${testcvs} -q update" \
+"A file1
+M file2
+R file3
+A file8
+A file9"
+
+ # Checkout the mainline again to try merging from the trunk
+ # to a branch.
+ cd ..
+ rm -r first-dir
+ dotest join-30 "${testcvs} -q co first-dir" \
+'U first-dir/file2
+U first-dir/file3
+U first-dir/file4
+U first-dir/file7'
+ cd first-dir
+
+ # Tag the current revisions on the trunk.
+ dotest join-31 "${testcvs} -q tag T3 ." \
+'T file2
+T file3
+T file4
+T file7'
+
+ # Modify file7.
+ echo 'second revision of file7' > file7
+ dotest join-32 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file7,v <-- file7
+new revision: 1\.2; previous revision: 1\.1"
+
+ # And Tag again.
+ dotest join-33 "${testcvs} -q tag T4 ." \
+'T file2
+T file3
+T file4
+T file7'
+
+ # Now update branch to T3.
+ cd ../../2/first-dir
+ dotest join-34 "${testcvs} -q up -jT3" \
+"${SPROG} update: file file4 does not exist, but is present in revision T3
+U file7"
+
+ # Verify that the right changes have been scheduled.
+ dotest join-35 "${testcvs} -q update" \
+'A file7'
+
+ # Now update to T4.
+ # This is probably a bug, although in this particular case it just
+ # happens to do the right thing; see above join-20.
+ dotest join-36 "${testcvs} -q up -j T3 -j T4" \
+"A file7
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file7,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into file7"
+
+ # Verify that the right changes have been scheduled.
+ dotest join-37 "${testcvs} -q update" \
+'A file7'
+
+ dokeep
+ cd ../..
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ join2)
+ # More joining tests.
+
+ # First the usual setup; create a directory first-dir, a file
+ # first-dir/file1, and a branch br1.
+ mkdir 1; cd 1
+ dotest join2-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest join2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo 'initial contents of file1' >file1
+ dotest join2-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest join2-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest join2-5 "${testcvs} -q tag -b br1" "T file1"
+ dotest join2-6 "${testcvs} -q update -r br1" ""
+ echo 'modify on branch' >>file1
+ touch bradd
+ dotest join2-6a "${testcvs} add bradd" \
+"${SPROG} add: scheduling file .bradd. for addition on branch .br1.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest join2-7 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/Attic/bradd,v <-- bradd
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Here is the unusual/pathological part. We switch back to
+ # the trunk *for file1 only*, not for the whole directory.
+ dotest join2-8 "${testcvs} -q update -A file1" 'U file1'
+ dotest join2-9 "${testcvs} -q status file1" \
+"===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest join2-10 "cat CVS/Tag" "Tbr1"
+
+ dotest join2-11 "${testcvs} -q update -j br1 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.1
+Merging differences between 1\.1 and 1\.1\.2\.1 into file1"
+ dotest join2-12 "cat file1" "initial contents of file1
+modify on branch"
+ # We should have no sticky tag on file1
+ dotest join2-13 "${testcvs} -q status file1" \
+"===================================================================
+File: file1 Status: Locally Modified
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest join2-14 "cat CVS/Tag" "Tbr1"
+ # And the checkin should go to the trunk
+ dotest join2-15 "${testcvs} -q ci -m modify file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+
+ # OK, the above is all well and good and has worked for some
+ # time. Now try the case where the file had been added on
+ # the branch.
+ dotest join2-16 "${testcvs} -q update -r br1" "U file1"
+ # The workaround is to update the whole directory.
+ # The non-circumvented version won't work. The reason is that
+ # update removes the entry from CVS/Entries, so of course we get
+ # the tag from CVS/Tag and not Entries. I suppose maybe
+ # we could invent some new format in Entries which would handle
+ # this, but doing so, and handling it properly throughout
+ # CVS, would be a lot of work and I'm not sure this case justifies
+ # it.
+ dotest join2-17-circumvent "${testcvs} -q update -A" \
+"${SPROG} update: \`bradd' is no longer in the repository
+U file1"
+: dotest join2-17 "${testcvs} -q update -A bradd" \
+"${SPROG} update: warning: \`bradd' is not (any longer) pertinent"
+ dotest join2-18 "${testcvs} -q update -j br1 bradd" "U bradd"
+ dotest join2-19 "${testcvs} -q status bradd" \
+"===================================================================
+File: bradd Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: 1\.1
${CVSROOT_DIRNAME}/first-dir/Attic/bradd,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest join2-20 "${testcvs} -q ci -m modify bradd" \
+"$CVSROOT_DIRNAME/first-dir/bradd,v <-- bradd
+new revision: 1\.2; previous revision: 1\.1"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ join3)
+ # See "join" for a list of other joining/branching tests.
+ # First the usual setup; create a directory first-dir, a file
+ # first-dir/file1, and a branch br1.
+ mkdir 1; cd 1
+ dotest join3-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest join3-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo 'initial contents of file1' >file1
+ dotest join3-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest join3-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest join3-5 "${testcvs} -q tag -b br1" "T file1"
+ dotest join3-6 "${testcvs} -q update -r br1" ""
+ echo 'br1:line1' >>file1
+ dotest join3-7 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Now back to the trunk for:
+ # another revision and another branch for file1.
+ # add file2, which will exist on trunk and br2 but not br1.
+ dotest join3-8 "${testcvs} -q update -A" "U file1"
+ echo 'trunk:line1' > file2
+ dotest join3-8a "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ echo 'trunk:line1' >>file1
+ dotest join3-9 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ dotest join3-10 "${testcvs} -q tag -b br2" "T file1
+T file2"
+
+ # Before we actually have any revision on br2, let's try a join
+ dotest join3-11 "${testcvs} -q update -r br1" "U file1
+${SPROG} update: \`file2' is no longer in the repository"
+ dotest join3-12 "${testcvs} -q update -j br2" \
+"RCS file: ${CVSROOT_DIRNAME}/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
+U file2"
+ dotest join3-13 "cat file1" \
+"initial contents of file1
+[<]<<<<<< file1
+br1:line1
+[=]======
+trunk:line1
+[>]>>>>>> 1\.2"
+ rm file1
+
+ # OK, we'll try the same thing with a revision on br2.
+ dotest join3-14 "${testcvs} -q update -r br2 file1" \
+"${SPROG} update: warning: \`file1' was lost
+U file1" "U file1"
+ echo 'br2:line1' >>file1
+ dotest join3-15 "${testcvs} -q ci -m modify file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2\.2\.1; previous revision: 1\.2"
+
+ # OK, now we can join br2 to br1
+ dotest join3-16 "${testcvs} -q update -r br1 file1" "U file1"
+ # It may seem odd, to merge a higher branch into a lower
+ # branch, but in fact CVS defines the ancestor as 1.1
+ # and so it merges both the 1.1->1.2 and 1.2->1.2.2.1 changes.
+ # This seems like a reasonably plausible behavior.
+ dotest join3-17 "${testcvs} -q update -j br2 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1
+Merging differences between 1\.1 and 1\.2\.2\.1 into file1
+rcsmerge: warning: conflicts during merge"
+ dotest join3-18 "cat file1" \
+"initial contents of file1
+[<]<<<<<< file1
+br1:line1
+[=]======
+trunk:line1
+br2:line1
+[>]>>>>>> 1\.2\.2\.1"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ join4)
+ # Like join, but with local (uncommitted) modifications.
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+ dotest join4-1 "${testcvs} -q co first-dir" ''
+
+ cd first-dir
+
+ # Add two files.
+ echo 'first revision of file3' > file3
+ echo 'first revision of file4' > file4
+ echo 'first revision of file6' > file6
+ echo 'first revision of file8' > file8
+ echo 'first revision of file9' > file9
+ dotest join4-2 "${testcvs} add file3 file4 file6 file8 file9" \
+"${SPROG}"' add: scheduling file `file3'\'' for addition
+'"${SPROG}"' add: scheduling file `file4'\'' for addition
+'"${SPROG}"' add: scheduling file `file6'\'' for addition
+'"${SPROG}"' add: scheduling file `file8'\'' for addition
+'"${SPROG}"' add: scheduling file `file9'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+
+ dotest join4-3 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file6,v <-- file6
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file8,v <-- file8
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file9,v <-- file9
+initial revision: 1\.1"
+
+ # Make a branch.
+ dotest join4-4 "${testcvs} -q tag -b branch ." \
+'T file3
+T file4
+T file6
+T file8
+T file9'
+
+ # Add file10
+ echo 'first revision of file10' > file10
+ dotest join4-7a "${testcvs} add file10" \
+"${SPROG}"' add: scheduling file `file10'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest join4-7b "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file10,v <-- file10
+initial revision: 1\.1"
+
+ # Add file2 and file7, modify file4, and remove
+ # file6, file8, file9, and file10.
+ echo 'first revision of file2' > file2
+ echo 'second revision of file4' > file4
+ echo 'first revision of file7' > file7
+ rm file6 file8 file9 file10
+ dotest join4-5 "${testcvs} add file2 file7" \
+"${SPROG}"' add: scheduling file `file2'\'' for addition
+'"${SPROG}"' add: scheduling file `file7'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+ dotest join4-6 "${testcvs} rm file6 file8 file9 file10" \
+"${SPROG}"' remove: scheduling `file6'\'' for removal
+'"${SPROG}"' remove: scheduling `file8'\'' for removal
+'"${SPROG}"' remove: scheduling `file9'\'' for removal
+'"${SPROG}"' remove: scheduling `file10'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove these files
permanently'
+
+ # Check out the branch.
+ cd ../..
+ mkdir 2
+ cd 2
+ dotest join4-8 "${testcvs} -q co -r branch first-dir" \
+'U first-dir/file3
+U first-dir/file4
+U first-dir/file6
+U first-dir/file8
+U first-dir/file9'
+
+ cd first-dir
+
+ # Modify the files on the branch, so that T1 is not an
+ # ancestor of the main line, and add file5
+ echo 'first branch revision of file3' > file3
+ echo 'first branch revision of file4' > file4
+ echo 'first branch revision of file5' > file5
+ echo 'first branch revision of file6' > file6
+ echo 'first branch revision of file9' > file9
+ dotest join4-9 "${testcvs} add file5" \
+"${SPROG}"' add: scheduling file `file5'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest join4-10 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file5,v <-- file5
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file6,v <-- file6
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file9,v <-- file9
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Tag the current revisions on the branch.
+ dotest join4-11 "${testcvs} -q tag T1 ." \
+'T file3
+T file4
+T file5
+T file6
+T file8
+T file9'
+
+ # Add file1 and file2, modify file9, and remove the other files.
+ echo 'first branch revision of file1' > file1
+ echo 'first branch revision of file2' > file2
+ echo 'second branch revision of file9' > file9
+ rm file3 file4 file5 file6
+ dotest join4-12 "${testcvs} add file1 file2" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: scheduling file `file2'\'' for addition on branch `branch'\''
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+ dotest join4-13 "${testcvs} rm file3 file4 file5 file6" \
+"${SPROG}"' remove: scheduling `file3'\'' for removal
+'"${SPROG}"' remove: scheduling `file4'\'' for removal
+'"${SPROG}"' remove: scheduling `file5'\'' for removal
+'"${SPROG}"' remove: scheduling `file6'\'' for removal
+'"${SPROG}"' remove: use .'"${SPROG}"' commit. to remove these files
permanently'
+ dotest join4-14 "${testcvs} -q ci -mx ." \
+"$CVSROOT_DIRNAME/first-dir/Attic/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file3,v <-- file3
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file5,v <-- file5
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file6,v <-- file6
+new revision: delete; previous revision: 1\.1\.2\.1
+$CVSROOT_DIRNAME/first-dir/file9,v <-- file9
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+
+ # Tag the current revisions on the branch.
+ dotest join4-15 "${testcvs} -q tag T2 ." \
+'T file1
+T file2
+T file8
+T file9'
+
+ # Modify file4 locally, and do an update with a merge.
+ cd ../../1/first-dir
+ echo 'third revision of file4' > file4
+ dotest join4-18 "${testcvs} -q update -jT1 -jT2 ." \
+"U file1
+R file10
+A file2
+${SPROG} update: file file2 exists, but has been added in revision T2
+${SPROG} update: scheduling \`file3' for removal
+M file4
+${SPROG} update: file file4 is locally modified, but has been removed in
revision T2
+R file6
+A file7
+R file8
+R file9
+${SPROG} update: file file9 does not exist, but is present in revision T2"
+
+ # Verify that the right changes have been scheduled.
+ dotest join4-19 "${testcvs} -q update" \
+'A file1
+R file10
+A file2
+R file3
+M file4
+R file6
+A file7
+R file8
+R file9'
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ join5)
+ # This test verifies that CVS can handle filenames starting with a
+ # dash (`-') properly. What used to happen was that CVS handled it
+ # just fine, until it went to pass them as arguments to the diff
+ # library, at which point it neglected to pass `--' before the file
+ # list, causing the diff library to attempt to interpret the file
+ # name as an argument.
+ mkdir join5; cd join5
+ mkdir 1; cd 1
+ dotest join5-init-1 "${testcvs} -Q co -l ."
+ mkdir join5
+ dotest join5-init-2 "${testcvs} -Q add join5"
+ cd join5
+ echo "there once was a file from harrisburg" >-file
+ echo "who's existance it seems was quiteabsurd" >>-file
+ dotest join5-init-3 "${testcvs} -Q add -- -file"
+ dotest join5-init-4 "${testcvs} -q ci -minitial" \
+"$CVSROOT_DIRNAME/join5/-file,v <-- -file
+initial revision: 1\.1"
+ cd ../..
+
+ mkdir 2; cd 2
+ dotest join5-init-5 "${testcvs} -Q co join5"
+ cd join5
+ echo "it tested for free" >>-file
+ echo "when paid it should be" >>-file
+ dotest join5-init-4 "${testcvs} -q ci -msecond" \
+"$CVSROOT_DIRNAME/join5/-file,v <-- -file
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../..
+
+ cd 1/join5
+ echo "but maybe it could charge bytheword" >>-file
+ # This is the test that used to spew complaints from diff3:
+ dotest join5 "${testcvs} up" \
+"${SPROG} update: Updating \.
+RCS file: ${CVSROOT_DIRNAME}/join5/-file,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into -file
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in -file
+C -file"
+
+ dokeep
+ cd ../../..
+ rm -r join5
+ modify_repo rm -rf $CVSROOT_DIRNAME/join5
+ ;;
+
+
+
+ join6)
+ mkdir join6; cd join6
+ mkdir 1; cd 1
+ dotest join6-init-1 "${testcvs} -Q co -l ."
+ mkdir join6
+ dotest join6-init-2 "${testcvs} -Q add join6"
+ cd join6
+ echo aaa >temp.txt
+ echo bbb >>temp.txt
+ echo ccc >>temp.txt
+ dotest join6-1 "${testcvs} -Q add temp.txt"
+ dotest join6-2 "${testcvs} -q commit -minitial temp.txt" \
+"$CVSROOT_DIRNAME/join6/temp\.txt,v <-- temp\.txt
+initial revision: 1\.1"
+ cp temp.txt temp2.txt
+ echo ddd >>temp.txt
+ dotest join6-3 "${testcvs} -q commit -madd temp.txt" \
+"$CVSROOT_DIRNAME/join6/temp.txt,v <-- temp\.txt
+new revision: 1\.2; previous revision: 1\.1"
+
+ # The case where the merge target is up-to-date and its base revision
+ # matches the second argument to -j: CVS doesn't bother attempting
+ # the merge since it already knows that the target contains the
+ # change.
+ dotest join6-3.3 "${testcvs} update -j1.1 -j1.2 temp.txt" \
+"temp\.txt already contains the differences between 1\.1 and 1\.2"
+ dotest join6-3.4 "${testcvs} diff temp.txt" ""
+
+ # The case where the merge target is modified but already contains
+ # the change.
+ echo bbb >temp.txt
+ echo ccc >>temp.txt
+ echo ddd >>temp.txt
+ dotest join6-3.5 "${testcvs} update -j1.1 -j1.2 temp.txt" \
+"M temp\.txt
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into temp\.txt
+temp\.txt already contains the differences between 1\.1 and 1\.2"
+ dotest_fail join6-3.6 "${testcvs} diff temp.txt" \
+"Index: temp\.txt
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.2
+diff -r1\.2 temp.txt
+1d0
+< aaa"
+
+ cp temp2.txt temp.txt
+ dotest_fail join6-4 "${testcvs} diff temp.txt" \
+"Index: temp.txt
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.2
+diff -r1\.2 temp\.txt
+4d3
+< ddd"
+
+ dotest join6-5 "${testcvs} update -j1.1 -j1.2 temp.txt" \
+"M temp\.txt
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into temp\.txt"
+ dotest join6-6 "${testcvs} diff temp.txt" ""
+ mv temp.txt temp3.txt
+ dotest join6-7 "sed 's/ddd/dddd/' < temp3.txt > temp.txt" ""
+ dotest join6-8 "${testcvs} update -j1.1 -j1.2 temp.txt" \
+"M temp\.txt
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into temp\.txt
+rcsmerge: warning: conflicts during merge"
+ dotest_fail join6-9 "${testcvs} diff temp.txt" \
+"Index: temp\.txt
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/join6/temp.txt,v
+retrieving revision 1\.2
+diff -r1\.2 temp\.txt
+3a4,6
+> <<<<<<< temp\.txt
+> dddd
+> =======
+4a8
+> >>>>>>> 1\.2"
+ cp temp2.txt temp.txt
+ dotest join6-10 "${testcvs} -q ci -m del temp.txt" \
+"$CVSROOT_DIRNAME/join6/temp.txt,v <-- temp\.txt
+new revision: 1\.3; previous revision: 1\.2"
+ cp temp3.txt temp.txt
+ dotest_fail join6-11 "${testcvs} diff temp.txt" \
+"Index: temp\.txt
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/join6/temp.txt,v
+retrieving revision 1\.3
+diff -r1\.3 temp\.txt
+3a4
+> ddd"
+ dotest join6-12 "${testcvs} update -j1.2 -j1.3 temp.txt" \
+"M temp\.txt
+RCS file: ${CVSROOT_DIRNAME}/join6/temp\.txt,v
+retrieving revision 1\.2
+retrieving revision 1\.3
+Merging differences between 1\.2 and 1\.3 into temp\.txt"
+ dotest join6-13 "${testcvs} diff temp.txt" ""
+
+ # The case where the merge target wasn't created until after the
+ # first tag was applied
+ rm temp2.txt temp3.txt
+ dotest join6-20 "${testcvs} -q tag -r1.1 t1" \
+"T temp.txt"
+ echo xxx >temp2.txt
+ dotest join6-21 "${testcvs} -Q add temp2.txt"
+ dotest join6-22 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/join6/temp2\.txt,v <-- temp2\.txt
+initial revision: 1\.1"
+ dotest join6-23 "${testcvs} -q tag t2" \
+"T temp.txt
+T temp2.txt"
+ echo xxx >>temp.txt
+ dotest join6-24 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/join6/temp.txt,v <-- temp\.txt
+new revision: 1\.4; previous revision: 1\.3"
+ dotest join6-25 "${testcvs} -q up -jt1 -jt2" \
+"RCS file: ${CVSROOT_DIRNAME}/join6/temp.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.3
+Merging differences between 1\.1 and 1\.3 into temp.txt
+temp.txt already contains the differences between 1\.1 and 1\.3
+temp2.txt already contains the differences between creation and 1\.1"
+
+ # Now for my next trick: delete the file, recreate it, and
+ # try to merge
+ dotest join6-30 "${testcvs} -q rm -f temp2.txt" \
+"${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest join6-31 "${testcvs} -q ci -m. temp2.txt" \
+"$CVSROOT_DIRNAME/join6/temp2\.txt,v <-- temp2\.txt
+new revision: delete; previous revision: 1\.1"
+ echo new >temp2.txt
+ # FIXCVS: Local and remote really shouldn't be different and there
+ # really shouldn't be two different status lines for temp2.txt
+ if $remote; then
+ dotest_fail join6-32 "${testcvs} -q up -jt1 -jt2" \
+"? temp2\.txt
+RCS file: ${CVSROOT_DIRNAME}/join6/temp.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.3
+Merging differences between 1\.1 and 1\.3 into temp.txt
+temp.txt already contains the differences between 1\.1 and 1\.3
+$CPROG update: move away .\./temp2\.txt.; it is in the way
+C temp2\.txt"
+ else
+ dotest join6-32 "${testcvs} -q up -jt1 -jt2" \
+"RCS file: ${CVSROOT_DIRNAME}/join6/temp.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.3
+Merging differences between 1\.1 and 1\.3 into temp.txt
+temp.txt already contains the differences between 1\.1 and 1\.3
+${SPROG} update: use .${SPROG} add. to create an entry for .temp2\.txt.
+U temp2\.txt
+? temp2\.txt"
+ fi
+
+ dokeep
+ cd ../../..
+ rm -r join6
+ modify_repo rm -rf $CVSROOT_DIRNAME/join6
+ ;;
+
+
+
+ join7)
+ # This test deals with joins that happen with the -n switch
+ mkdir join7; cd join7
+ mkdir impdir; cd impdir
+ echo aaa >temp.txt
+ echo bbb >>temp.txt
+ echo ccc >>temp.txt
+ dotest join7-1 \
+"${testcvs} -Q import -minitial join7 vendor vers-1" \
+""
+ cd ..
+ dotest join7-2 "${testcvs} -Q co join7" ""
+ cd join7
+ echo ddd >> temp.txt
+ dotest join7-3 "${testcvs} -Q ci -madded-line temp.txt" ""
+ cd ../impdir
+ echo aaaa >temp.txt
+ echo bbbb >>temp.txt
+ echo ccc >>temp.txt
+ echo eee >>temp.txt
+ dotest join7-4 \
+"${testcvs} -Q import -minitial join7 vendor vers-2" \
+""
+ cd ../join7
+ dotest join7-5 \
+"${testcvs} -n update -jvers-1 -jvers-2 temp.txt" \
+"RCS file: $CVSROOT_DIRNAME/join7/temp.txt,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.2
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into temp.txt
+rcsmerge: warning: conflicts during merge"
+ touch temp.txt
+ dotest join7-6 "${testcvs} -n update -jvers-1 -jvers-2 temp.txt" \
+"RCS file: $CVSROOT_DIRNAME/join7/temp.txt,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.2
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into temp.txt
+rcsmerge: warning: conflicts during merge" \
+"RCS file: $CVSROOT_DIRNAME/join7/temp.txt,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.2
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into temp.txt
+rcsmerge: warning: conflicts during merge"
+
+ dokeep
+ cd ../..
+ rm -r join7
+ modify_repo rm -rf $CVSROOT_DIRNAME/join7
+ ;;
+
+
+
+ join-readonly-conflict)
+ # Previously, only tests 1 & 11 were being tested. I added the
+ # intermediate dotest's to try and diagnose a different failure
+ #
+ # Demonstrate that cvs-1.9.29 can fail on 2nd and subsequent
+ # conflict-evoking join attempts.
+ # Even with that version of CVS, This test failed only in
+ # client-server mode, and would have been noticed in normal
+ # operation only for files that were read-only (either due to
+ # use of cvs' global -r option, setting the CVSREAD envvar,
+ # or use of watch lists).
+ mkdir join-readonly-conflict; cd join-readonly-conflict
+ dotest join-readonly-conflict-1 "$testcvs -q co -l ." ''
+ module=join-readonly-conflict
+ mkdir $module
+ $testcvs -q add $module >>$LOGFILE 2>&1
+ cd $module
+
+ file=m
+ echo trunk > $file
+ dotest join-readonly-conflict-2 "$testcvs -Q add $file" ''
+
+ dotest join-readonly-conflict-3 "$testcvs -q ci -m . $file" \
+"$CVSROOT_DIRNAME/$module/$file,v <-- $file
+initial revision: 1\.1"
+
+ dotest join-readonly-conflict-4 "$testcvs tag -b B $file" "T $file"
+ dotest join-readonly-conflict-5 "$testcvs -q update -rB $file" ''
+ echo branch B > $file
+ dotest join-readonly-conflict-6 "$testcvs -q ci -m . $file" \
+"$CVSROOT_DIRNAME/$module/$file,v <-- $file
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ rm $file
+ dotest join-readonly-conflict-7 "$testcvs -Q update -A $file" ''
+ # Make sure $file is read-only. This can happen more realistically
+ # via patch -- which could be used to apply a delta, yet would
+ # preserve a file's read-only permissions.
+ echo conflict > $file; chmod u-w $file
+ dotest join-readonly-conflict-8 "$testcvs update -r B $file" \
+"RCS file: $CVSROOT_DIRNAME/$module/$file,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.1
+Merging differences between 1\.1 and 1\.1\.2\.1 into $file
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in $file
+C $file"
+
+ # restore to the trunk
+ rm -f $file
+ dotest join-readonly-conflict-9 "$testcvs -Q update -A $file" ''
+
+ # This one would fail because cvs couldn't open the existing
+ # (and read-only) .# file for writing.
+ echo conflict > $file
+
+ # verify that the backup file is not writable
+ if test -w ".#$file.1.1"; then
+ fail "join-readonly-conflict-10 : .#$file.1.1 is writable"
+ else
+ pass "join-readonly-conflict-10"
+ fi
+ dotest join-readonly-conflict-11 "$testcvs update -r B $file" \
+"RCS file: $CVSROOT_DIRNAME/$module/$file,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.1
+Merging differences between 1\.1 and 1\.1\.2\.1 into $file
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in $file
+C m"
+
+ dokeep
+ cd ../..
+ rm -r join-readonly-conflict
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ join-admin)
+ mkdir 1; cd 1
+ dotest join-admin-0-1 "$testcvs -q co -l ."
+ module=x
+ mkdir $module
+ dotest join-admin-0-2 "$testcvs -q add $module" \
+"Directory $CVSROOT_DIRNAME/$module added to the repository"
+ cd $module
+
+ # Create a file so applying the first tag works.
+ echo foo > a
+ dotest join-admin-0-3 "$testcvs -Q add a" ''
+ dotest join-admin-0-4 "$testcvs -Q ci -m. a" ''
+
+ dotest join-admin-0-5 "$testcvs -Q tag -b B" ''
+ dotest join-admin-0-6 "$testcvs -Q tag -b M1" ''
+ echo '$''Id$' > b
+ dotest join-admin-0-7 "$testcvs -Q add b" ''
+ dotest join-admin-0-8 "$testcvs -Q ci -m. b" ''
+ dotest join-admin-0-9 "$testcvs -Q tag -b M2" ''
+
+ dotest join-admin-0-10 "$testcvs -Q update -r B" ''
+ dotest join-admin-0-11 "$testcvs -Q update -kk -jM1 -jM2" ''
+ dotest join-admin-0-12 "$testcvs -Q ci -m. b" ''
+
+ dotest join-admin-0-13 "$testcvs -Q update -A" ''
+
+ # Verify that the -kk flag from the update did not
+ # propagate to the repository.
+ dotest join-admin-1 "$testcvs status b" \
+"===================================================================
+File: b Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/x/b,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ join-admin-2)
+ # Show that when a merge (via update -kk -jtag1 -jtag2) first
+ # removes a file, then modifies another containing an $Id...$ line,
+ # the resulting file contains the unexpanded `$Id.$' string, as
+ # -kk requires.
+ mkdir 1; cd 1
+ dotest join-admin-2-1 "$testcvs -q co -l ." ''
+ module=x
+ mkdir $module
+ dotest join-admin-2-2 "$testcvs -q add $module" \
+"Directory ${CVSROOT_DIRNAME}/x added to the repository"
+ cd $module
+
+ # Create a file so applying the first tag works.
+ echo '$''Id$' > e0
+ cp e0 e
+ dotest join-admin-2-3 "$testcvs -Q add e"
+ dotest join-admin-2-4 "$testcvs -Q ci -m. e"
+
+ dotest join-admin-2-5 "$testcvs -Q tag -b T" '' "${QUESTION} e0"
+ dotest join-admin-2-6 "$testcvs -Q update -r T" '' "${QUESTION} e0"
+ cp e0 e
+ dotest join-admin-2-7 "$testcvs -Q ci -m. e"
+
+ dotest join-admin-2-8 "$testcvs -Q update -A" '' "${QUESTION} e0"
+ dotest join-admin-2-9 "$testcvs -Q tag -b M1" '' "${QUESTION} e0"
+
+ echo '$''Id$' > b
+ dotest join-admin-2-10 "$testcvs -Q add b" ''
+ cp e0 e
+ dotest join-admin-2-11 "$testcvs -Q ci -m. b e"
+
+ dotest join-admin-2-12 "$testcvs -Q tag -b M2" '' "${QUESTION} e0"
+
+ dotest join-admin-2-13 "$testcvs -Q update -r T" '' "${QUESTION} e0"
+ dotest join-admin-2-14 "$testcvs update -kk -jM1 -jM2" \
+"${SPROG} update: Updating .
+U b
+U e
+RCS file: ${CVSROOT_DIRNAME}/x/e,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into e
+e already contains the differences between 1\.1 and 1\.2
+${QUESTION} e0" \
+"${QUESTION} e0
+${SPROG} update: Updating .
+U b
+U e
+RCS file: ${CVSROOT_DIRNAME}/x/e,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into e
+e already contains the differences between 1\.1 and 1\.2"
+
+ # Verify that the $Id.$ string is not expanded.
+ dotest join-admin-2-15 "cat e" '$''Id$'
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ join-rm)
+ # This first half of this test checks that a single-argument merge
+ # from a branch is capable of removing files.
+ #
+ # The second half verifies that an update to another location with an
+ # uncommitted removal will transfer the destination branch of the
+ # removal.
+
+ module=join-rm
+ mkdir $module; cd $module
+
+ dotest join-rm-init-1 "$testcvs -q co -l ." ''
+ mkdir $module
+ dotest join-rm-init-2 "$testcvs -q add $module" \
+"Directory $CVSROOT_DIRNAME/$module added to the repository"
+ cd $module
+
+ # add some files.
+ touch a b c d e f g
+ dotest join-rm-init-3 "$testcvs -Q add a b c d e f g"
+ dotest join-rm-init-4 "$testcvs -Q ci -m add-em"
+
+ # create the branch and update to it
+ dotest join-rm-init-5 "$testcvs -Q tag -b br"
+ dotest join-rm-init-6 "$testcvs -Q up -rbr"
+
+ # remove a few files from the branch
+ dotest join-rm-init-7 "$testcvs -Q rm -f b d g"
+ dotest join-rm-init-8 "$testcvs -Q ci -mrm"
+
+ # update to the trunk
+ dotest join-rm-init-9 "$testcvs -Q up -A"
+
+ # now for the test - try and merge the removals.
+ dotest join-rm-1 "$testcvs -q up -jbr" \
+"$SPROG update: scheduling \`b' for removal
+$SPROG update: scheduling \`d' for removal
+$SPROG update: scheduling \`g' for removal"
+
+ # And make sure the merge took
+ dotest join-rm-2 "$testcvs -qn up" \
+"R b
+R d
+R g"
+
+ dotest join-rm-3 "$testcvs -q ci -m 'save the merge'" \
+"$CVSROOT_DIRNAME/join-rm/b,v <-- b
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/join-rm/d,v <-- d
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/join-rm/g,v <-- g
+new revision: delete; previous revision: 1\.1"
+
+ # and verify that it was the head revision which was removed.
+ dotest join-rm-4 "$testcvs -q log b" "
+RCS file: $CVSROOT_DIRNAME/join-rm/Attic/b,v
+Working file: b
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ br: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: $username; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+save the merge
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+branches: 1.1.2;
+add-em
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: $username; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+rm
+============================================================================="
+
+ # go back to the branch to set up for the second set of tests
+ dotest join-rm-init-10 "$testcvs -Q up -rbr"
+ dotest join-rm-init-11 "$testcvs -Q rm -f a"
+ dotest join-rm-init-12 "$testcvs -Q ci -m rma"
+
+ # now the test: update to the trunk
+ #
+ # FIXCVS: This update should merge the removal to the trunk. It does
+ # not.
+ dotest join-rm-5 "$testcvs -q up -A" "U a"
+
+ # and verify that there is no sticky tag
+ dotest join-rm-6 "$testcvs status a" \
+"===================================================================
+File: a Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/join-rm/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ rm -r $module
+ ;;
+
+
+
+ new) # look for stray "no longer pertinent" messages.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest new-init-1 "$testcvs -Q co first-dir"
+
+ cd first-dir
+ touch a
+
+ dotest new-1 "$testcvs -Q add a"
+
+ dotest new-2 "$testcvs -Q ci -m added"
+ rm a
+
+ dotest new-3 "$testcvs -Q rm a"
+ dotest new-4 "$testcvs -Q ci -m removed"
+ dotest new-5 "$testcvs -Q update -A"
+ dotest new-6 "$testcvs -Q update -rHEAD"
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ newb)
+ # Test removing a file on a branch and then checking it out.
+
+ # We call this "newb" only because it, like the "new" tests,
+ # has something to do with "no longer pertinent" messages.
+ # Not necessarily the most brilliant nomenclature.
+
+ # Create file 'a'.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest newb-123a "${testcvs} -q co first-dir" ''
+ cd first-dir
+ touch a
+ dotest newb-123b "${testcvs} add a" \
+"${SPROG} add: scheduling file .a. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest newb-123c "${testcvs} -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+initial revision: 1\.1"
+
+ # Make a branch.
+ dotest newb-123d "${testcvs} -q tag -b branch" "T a"
+
+ # Check out the branch.
+ cd ..
+ rm -r first-dir
+ mkdir 1
+ cd 1
+ dotest newb-123e "${testcvs} -q co -r branch first-dir" \
+"U first-dir/a"
+
+ # Remove 'a' on another copy of the branch.
+ cd ..
+ mkdir 2
+ cd 2
+ dotest newb-123f "${testcvs} -q co -r branch first-dir" \
+"U first-dir/a"
+ cd first-dir
+ rm a
+ dotest newb-123g "${testcvs} rm a" \
+"${SPROG} remove: scheduling .a. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest newb-123h "${testcvs} -q ci -m removed" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+new revision: delete; previous revision: 1\.1"
+
+ # Check out the file on the branch. This used to report
+ # that the file is not pertinent, but this only makes sense on
+ # update.
+ cd ..
+ rm -r first-dir
+ dotest newb-123i "$testcvs -q co -r branch first-dir/a"
+
+ # Update the other copy, and make sure that a is removed.
+ cd ../1/first-dir
+ # "Entry Invalid" is a rather strange output here. Something like
+ # "Removed in Repository" would make more sense.
+ dotest newb-123j0 "${testcvs} status a" \
+"${SPROG} status: \`a' is no longer in the repository
+===================================================================
+File: a Status: Entry Invalid
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1\.2\.1 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: branch (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)${DOTSTAR}"
+ dotest newb-123j "${testcvs} -q update" \
+"${SPROG} update: \`a' is no longer in the repository"
+
+ if test -f a; then
+ fail newb-123k
+ else
+ pass newb-123k
+ fi
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ conflicts)
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+
+ mkdir 1
+ cd 1
+
+ dotest conflicts-124 "${testcvs} -q co first-dir" ''
+
+ cd first-dir
+ touch a
+
+ dotest conflicts-125 "${testcvs} add a" \
+"${SPROG} add: scheduling file .a. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest conflicts-126 "${testcvs} -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+initial revision: 1\.1"
+
+ cd ../..
+ mkdir 2
+ cd 2
+
+ dotest conflicts-126.5 "${testcvs} co -p first-dir" \
+"${SPROG} checkout: Updating first-dir
+===================================================================
+Checking out first-dir/a
+RCS: ${CVSROOT_DIRNAME}/first-dir/a,v
+VERS: 1\.1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*"
+ dotest conflicts-127 "${testcvs} -Q co first-dir" ''
+ cd first-dir
+ dotest conflicts-127a "test -f a" ''
+
+ cd ../../1/first-dir
+ echo add a line >>a
+ mkdir dir1
+ dotest conflicts-127b "${testcvs} add dir1" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir1 added to the repository"
+ dotest conflicts-128 "${testcvs} -q ci -m changed" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../..
+
+ # Similar to conflicts-126.5, but now the file has nonempty
+ # contents.
+ mkdir 3
+ cd 3
+ dotest conflicts-128.5 "${testcvs} co -p -l first-dir" \
+"${SPROG} checkout: Updating first-dir
+===================================================================
+Checking out first-dir/a
+RCS: ${CVSROOT_DIRNAME}/first-dir/a,v
+VERS: 1\.2
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+add a line"
+ cd ..
+ rmdir 3
+
+ # Now go over the to the other working directory and
+ # start testing conflicts
+ cd 2/first-dir
+ echo add a conflicting line >>a
+ dotest_fail conflicts-129 "${testcvs} -q ci -m changed" \
+"${SPROG}"' commit: Up-to-date check failed for `a'\''
+'"${SPROG}"' \[commit aborted\]: correct above errors first!'
+ mkdir dir1
+ mkdir sdir
+ dotest conflicts-status-0 "${testcvs} status a" \
+"===================================================================
+File: a Status: Needs Merge
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest conflicts-129a "${testcvs} -nq update a" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/a,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into a
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in a
+C a"
+ dotest conflicts-130 "${testcvs} -q update" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/a,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into a
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in a
+C a
+${QUESTION} dir1
+${QUESTION} sdir" \
+"${QUESTION} dir1
+${QUESTION} sdir
+RCS file: ${CVSROOT_DIRNAME}/first-dir/a,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into a
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in a
+C a"
+ rmdir dir1 sdir
+
+ dotest conflicts-status-1 "${testcvs} status a" \
+"===================================================================
+File: a Status: Unresolved Conflict
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest_fail conflicts-131 "${testcvs} -q ci -m try" \
+"${SPROG} commit: file .a. had a conflict and has not been modified
+${SPROG} \[commit aborted\]: correct above errors first!"
+
+ # Try to check in the file with the conflict markers in it.
+ # Make sure we detect any one of the three conflict markers
+ mv a aa
+ grep '^<<<<<<<' aa >a
+ dotest conflicts-status-2 "${testcvs} -nq ci -m try a" \
+"${SPROG} commit: warning: file .a. seems to still contain conflict indicators"
+
+ grep '^=======' aa >a
+ dotest conflicts-status-3 "${testcvs} -nq ci -m try a" \
+"${SPROG} commit: warning: file .a. seems to still contain conflict indicators"
+
+ grep '^>>>>>>>' aa >a
+ dotest conflicts-status-4 "${testcvs} -qn ci -m try a" \
+"${SPROG} commit: warning: file .a. seems to still contain conflict indicators"
+
+ mv aa a
+ echo lame attempt at resolving it >>a
+ dotest conflicts-status-5 "${testcvs} status a" \
+"===================================================================
+File: a Status: File had conflicts on merge
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest conflicts-132 "$testcvs -q ci -m try" \
+"$SPROG commit: warning: file .a. seems to still contain conflict indicators
+$CVSROOT_DIRNAME/first-dir/a,v <-- a
+new revision: 1\.3; previous revision: 1\.2"
+
+ # OK, the user saw the warning (good user), and now
+ # resolves it for real.
+ echo resolve conflict >a
+ dotest conflicts-status-6 "${testcvs} status a" \
+"===================================================================
+File: a Status: Locally Modified
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest conflicts-133 "${testcvs} -q ci -m resolved" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+new revision: 1\.4; previous revision: 1\.3"
+ dotest conflicts-status-7 "${testcvs} status a" \
+"===================================================================
+File: a Status: Up-to-date
+
+ Working revision: 1\.4.*
+ Repository revision: 1\.4 ${CVSROOT_DIRNAME}/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # Now test that we can add a file in one working directory
+ # and have an update in another get it.
+ cd ../../1/first-dir
+ echo abc >abc
+ if ${testcvs} add abc >>${LOGFILE} 2>&1; then
+ pass 134
+ else
+ fail 134
+ fi
+ if ${testcvs} ci -m 'add abc' abc >>${LOGFILE} 2>&1; then
+ pass 135
+ else
+ fail 135
+ fi
+ cd ../../2
+ mkdir first-dir/dir1 first-dir/sdir
+ dotest conflicts-136 "${testcvs} -q update first-dir" \
+'U first-dir/abc
+'"${QUESTION}"' first-dir/dir1
+'"${QUESTION}"' first-dir/sdir' \
+''"${QUESTION}"' first-dir/dir1
+'"${QUESTION}"' first-dir/sdir
+U first-dir/abc'
+ dotest conflicts-137 'test -f first-dir/abc' ''
+ rmdir first-dir/dir1 first-dir/sdir
+
+ # Now test something similar, but in which the parent directory
+ # (not the directory in question) has the Entries.Static flag
+ # set.
+ cd ../1/first-dir
+ mkdir subdir
+ dotest conflicts-138 "${testcvs} add subdir" "${DOTSTAR}"
+ cd ../..
+ mkdir 3
+ cd 3
+ dotest conflicts-139 \
+"${testcvs} -q co first-dir/abc first-dir/subdir" "${DOTSTAR}"
+ cd ../1/first-dir/subdir
+ echo sss >sss
+ dotest conflicts-140 "${testcvs} add sss" "${DOTSTAR}"
+ dotest conflicts-140a "${testcvs} ci -m adding sss" \
+"${DOTSTAR}"
+ cd ../../../3/first-dir
+ dotest conflicts-141 "${testcvs} -q update" "${DOTSTAR}"
+ dotest conflicts-142 "test -f subdir/sss"
+
+ dokeep
+ cd ../..
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ restore_adm
+ ;;
+
+
+
+ conflicts2)
+ # More conflicts tests; separate from conflicts to keep each
+ # test a manageable size.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+
+ mkdir 1
+ cd 1
+
+ dotest conflicts2-142a1 "${testcvs} -q co first-dir" ''
+
+ cd first-dir
+ touch a abc
+
+ dotest conflicts2-142a2 "${testcvs} add a abc" \
+"${SPROG} add: scheduling file .a. for addition
+${SPROG} add: scheduling file .abc. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest conflicts2-142a3 "${testcvs} -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/abc,v <-- abc
+initial revision: 1\.1"
+
+ cd ../..
+ mkdir 2
+ cd 2
+
+ dotest conflicts2-142a4 "${testcvs} -q co first-dir" 'U first-dir/a
+U first-dir/abc'
+ cd ..
+
+ # BEGIN TESTS USING THE FILE A
+ # FIXME: would be cleaner to separate them out into their own
+ # tests; conflicts2 is getting long.
+ # Now test that if one person modifies and commits a
+ # file and a second person removes it, it is a
+ # conflict
+ cd 1/first-dir
+ echo modify a >>a
+ dotest conflicts2-142b2 "${testcvs} -q ci -m modify-a" \
+"$CVSROOT_DIRNAME/first-dir/a,v <-- a
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../../2/first-dir
+ rm a
+ dotest conflicts2-142b3 "${testcvs} rm a" \
+"${SPROG} remove: scheduling .a. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest_fail conflicts2-142b4 "${testcvs} -q update" \
+"${SPROG} update: conflict: removed \`a' was modified by second party
+C a"
+ # Resolve the conflict by deciding not to remove the file
+ # after all.
+ dotest_sort conflicts2-142b5 "$testcvs add a" "U a
+${SPROG} add: \`a', version 1\.1, resurrected"
+ dotest conflicts2-142b5b1 "$testcvs status a" \
+"===================================================================
+File: a Status: Needs Patch
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.2 $CVSROOT_DIRNAME/first-dir/a,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest conflicts2-142b6 "$testcvs -q update" 'U a'
+
+ # Now one level up.
+ cd ..
+ dotest conflicts2-142b7 "${testcvs} rm -f first-dir/a" \
+"${SPROG} remove: scheduling \`first-dir/a' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+
+ if $remote; then
+ # Haven't investigated this one.
+ dotest_fail conflicts2-142b8r "$testcvs add first-dir/a" \
+"${CPROG} add: in directory \`\.':
+${CPROG} \[add aborted\]: there is no version here; do \`${CPROG} checkout'
first"
+ cd first-dir
+ else
+ dotest conflicts2-142b8 "${testcvs} add first-dir/a" \
+"U first-dir/a
+$SPROG add: \`first-dir/a', version 1\.2, resurrected"
+ cd first-dir
+ # Now recover from the damage that the 142b8 test did.
+ dotest conflicts2-142b9 "${testcvs} rm -f a" \
+"${SPROG} remove: scheduling \`a' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+ fi
+
+ # As before, 1.2 instead of 1.1 is a bug.
+ dotest_sort conflicts2-142b10 "$testcvs add a" "U a
+${SPROG} add: \`a', version 1\.2, resurrected"
+ # As with conflicts2-142b6, check that things are normal again.
+ dotest conflicts2-142b11 "${testcvs} -q update" ''
+ cd ../..
+ # END TESTS USING THE FILE A
+
+ # Now test that if one person removes a file and
+ # commits it, and a second person removes it, is it
+ # not a conflict.
+ cd 1/first-dir
+ rm abc
+ dotest conflicts2-142c0 "${testcvs} rm abc" \
+"${SPROG} remove: scheduling \`abc' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+ dotest conflicts2-142c1 "${testcvs} -q ci -m remove-abc" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+new revision: delete; previous revision: 1\.1"
+ cd ../../2/first-dir
+ rm abc
+ dotest conflicts2-142c2 "${testcvs} rm abc" \
+"${SPROG} remove: scheduling \`abc' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+ dotest conflicts2-142c3 "${testcvs} update" \
+"${SPROG} update: Updating \."
+ cd ../..
+
+ # conflicts2-142d*: test that if one party adds a file, and another
+ # party has a file of the same name, cvs notices
+ cd 1/first-dir
+ touch aa.c
+ echo 'contents unchanged' >same.c
+ dotest conflicts2-142d0 "${testcvs} add aa.c same.c" \
+"${SPROG} add: scheduling file .aa\.c. for addition
+${SPROG} add: scheduling file .same\.c. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest conflicts2-142d1 "${testcvs} -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/aa\.c,v <-- aa\.c
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/same\.c,v <-- same\.c
+initial revision: 1\.1"
+
+ # Test the case where the second user manages the add before the
+ # first commits
+ touch bb.c
+ dotest conflicts2-142d1a "$testcvs add bb.c" \
+"$SPROG add: scheduling file .bb\.c. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ cd ../../2/first-dir
+ echo "don't you dare obliterate this text" >bb.c
+ dotest conflicts2-142d1b "$testcvs add bb.c" \
+"$SPROG add: scheduling file .bb\.c. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ cd ../../1/first-dir
+ dotest conflicts2-142d1c "$testcvs -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/bb\.c,v <-- bb\.c
+initial revision: 1\.1"
+
+ cd ../../2/first-dir
+ echo "don't you dare obliterate this text either" >aa.c
+ echo 'contents unchanged' >same.c
+ # Note the discrepancy between local and remote in the handling
+ # of same.c. I kind
+ # of suspect that the local CVS behavior is the more useful one
+ # although I do sort of wonder whether we should make people run
+ # cvs add just to get them in that habit (also, trying to implement
+ # the local CVS behavior for remote without the cvs add seems
+ # pretty difficult).
+ if $remote; then
+ dotest_fail conflicts2-142d2r "${testcvs} -q update" \
+"${QUESTION} aa\.c
+${QUESTION} same\.c
+${CPROG} update: move away \`\./aa\.c'; it is in the way
+C aa\.c
+${SPROG} update: conflict: \`bb\.c' created independently by second party
+C bb\.c
+${CPROG} update: move away \`\./same\.c'; it is in the way
+C same\.c"
+ else
+ dotest_fail conflicts2-142d2 "${testcvs} -q update" \
+"${CPROG} update: move away \`aa\.c'; it is in the way
+C aa\.c
+${CPROG} update: conflict: \`bb\.c' created independently by second party
+C bb\.c
+U same\.c"
+ fi
+ dotest conflicts2-142d3 "${testcvs} -q status aa.c" \
+"${SPROG} status: move away \`aa\.c'; it is in the way
+===================================================================
+File: aa\.c Status: Unresolved Conflict
+
+ Working revision: No entry for aa\.c
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/aa\.c,v
+ Commit Identifier: ${commitid}"
+ dotest conflicts2-142d3a "${testcvs} -q status bb.c" \
+"${SPROG} status: conflict: \`bb\.c' created independently by second party
+===================================================================
+File: bb\.c Status: Unresolved Conflict
+
+ Working revision: New file!
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/bb\.c,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # FIXCVS
+ # This message seems somewhat bogus. I mean, parallel development
+ # means that we get to work in parallel if we choose, right? And
+ # then at commit time it would be a conflict.
+ #
+ # Well, the status is "Unresolved conflict" before _and_ after
+ # the update/merge (when conflicts happen, not at commit time).
+ # It is possible that this message could be changed to something
+ # more infomrative to novice users, like "File of same name exists
+ # in repository", or "File of same name committed independantly by
+ # second party", but these two messages look too long for the Status
+ # field and the move away & added independantly error messages _are_
+ # displayed. Still, we get a lot of questions about this on the
+ # email lists. Somehow we need to get more information to users
+ # via these messages and the ones generated by update. -DRP
+ dotest_fail conflicts2-142d4 "${testcvs} -q add aa.c" \
+"${SPROG} add: \`aa.c' added independently by second party"
+
+ # The user might want to see just what the conflict is.
+ # Don't bother, diff seems to kind of lose its mind, with or
+ # without -N. This is a CVS bug(s).
+ #dotest conflicts2-142d5 \
+ #"${testcvs} -q diff -r HEAD -N aa.c" FIXCVS THEN FIXME
+
+ # Now: "how can the user resolve this conflict", I hear you cry.
+ # Well, one way is to forget about the file in the working
+ # directory.
+ # Since it didn't let us do the add in conflicts2-142d4, there
+ # is no need to run cvs rm here.
+ #dotest conflicts2-142d6 "${testcvs} -q rm -f aa.c" fixme
+ dotest conflicts2-142d6 "rm aa.c" ''
+ dotest conflicts2-142d7 "${testcvs} -q update aa.c" "U aa\.c"
+ dotest conflicts2-142d8 "cat aa.c" ''
+
+ # The other way is to use the version from the working directory
+ # instead of the version from the repository. Unfortunately,
+ # there doesn't seem to be any particularly clear way to do
+ # this (?).
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ conflicts3)
+ # More tests of conflicts and/or multiple working directories
+ # in general.
+
+ mkdir 1; cd 1
+ dotest conflicts3-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest conflicts3-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd ..
+ mkdir 2; cd 2
+ dotest conflicts3-3 "${testcvs} -q co -l first-dir" ''
+ cd ../1/first-dir
+ touch file1 file2
+ dotest conflicts3-4 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest conflicts3-5 "${testcvs} -q ci -m add-them" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ cd ../../2/first-dir
+ # Check that -n doesn't make CVS lose its mind as it creates
+ # (or rather, doesn't) a new file.
+ dotest conflicts3-6 "${testcvs} -nq update" \
+"U file1
+U file2"
+ dotest_fail conflicts3-7 "test -f file1" ''
+ dotest conflicts3-8 "${testcvs} -q update" \
+"U file1
+U file2"
+ dotest conflicts3-9 "test -f file2" ''
+
+ # OK, now remove two files at once
+ dotest conflicts3-10 "${testcvs} rm -f file1 file2" \
+"${SPROG} remove: scheduling .file1. for removal
+${SPROG} remove: scheduling .file2. for removal
+${SPROG} remove: use .${SPROG} commit. to remove these files permanently"
+ dotest conflicts3-11 "${testcvs} -q ci -m remove-them" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: delete; previous revision: 1\.1"
+ cd ../../1/first-dir
+ dotest conflicts3-12 "${testcvs} -n -q update" \
+"${SPROG} update: \`file1' is no longer in the repository
+${SPROG} update: \`file2' is no longer in the repository"
+ dotest conflicts3-13 "${testcvs} -q update" \
+"${SPROG} update: \`file1' is no longer in the repository
+${SPROG} update: \`file2' is no longer in the repository"
+
+ # OK, now add a directory to both working directories
+ # and see that CVS doesn't lose its mind.
+ mkdir sdir
+ dotest conflicts3-14 "${testcvs} add sdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/sdir added to the repository"
+ touch sdir/sfile
+ dotest conflicts3-14a "${testcvs} add sdir/sfile" \
+"${SPROG} add: scheduling file .sdir/sfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest conflicts3-14b "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/sdir/sfile,v <-- sdir/sfile
+initial revision: 1\.1"
+
+ cd ../../2/first-dir
+
+ # Create a CVS directory without the proper administrative
+ # files in it. This can happen for example if you hit ^C
+ # in the middle of a checkout.
+ mkdir sdir
+ mkdir sdir/CVS
+ # OK, in the local case CVS sees that the directory exists
+ # in the repository and recurses into it. In the remote case
+ # CVS can't see the repository and has no way of knowing
+ # that sdir is even a directory (stat'ing everything would be
+ # too slow). The remote behavior makes more sense to me (but
+ # would this affect other cases?).
+ if $remote; then
+ dotest conflicts3-15 "${testcvs} -q update" \
+"${QUESTION} sdir"
+ else
+ dotest conflicts3-15 "${testcvs} -q update" \
+"${QUESTION} sdir
+${SPROG} update: ignoring sdir (CVS/Repository missing)"
+ touch sdir/CVS/Repository
+ dotest conflicts3-16 "${testcvs} -q update" \
+"${QUESTION} sdir
+${SPROG} update: ignoring sdir (CVS/Entries missing)"
+ cd ..
+ dotest conflicts3-16a "${testcvs} -q update first-dir" \
+"${QUESTION} first-dir/sdir
+${SPROG} update: ignoring first-dir/sdir (CVS/Entries missing)"
+ cd first-dir
+ fi
+ rm -r sdir
+
+ # OK, now the same thing, but the directory doesn't exist
+ # in the repository.
+ mkdir newdir
+ mkdir newdir/CVS
+ dotest conflicts3-17 "${testcvs} -q update" "${QUESTION} newdir"
+ echo "D/newdir////" >> CVS/Entries
+ dotest conflicts3-18 "${testcvs} -q update" \
+"${CPROG} update: ignoring newdir (CVS/Repository missing)"
+ touch newdir/CVS/Repository
+ dotest conflicts3-19 "${testcvs} -q update" \
+"${CPROG} update: ignoring newdir (CVS/Entries missing)"
+ cd ..
+ dotest conflicts3-20 "${testcvs} -q update first-dir" \
+"${CPROG} update: 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" "${QUESTION} sdir"
+ if $remote; then
+ dotest_fail conflicts3-23 "${testcvs} -q update -PdA" \
+"${QUESTION} sdir
+${CPROG} update: move away \`sdir/sfile'; it is in the way
+C sdir/sfile"
+ else
+ dotest conflicts3-23 "${testcvs} -q update -PdA" \
+"${QUESTION} sdir"
+ fi
+
+ # Not that it should really affect much, but let's do the case
+ # where sfile has been removed. For example, suppose that sdir
+ # had been a CVS-controlled directory which was then removed
+ # by removing each file (and using update -P or some such). Then
+ # suppose that the build process creates an sdir directory which
+ # is not supposed to be under CVS.
+ rm -r sdir
+ dotest conflicts3-24 "${testcvs} -q update -d sdir" "U sdir/sfile"
+ rm sdir/sfile
+ dotest conflicts3-25 "${testcvs} rm sdir/sfile" \
+"${SPROG} remove: scheduling .sdir/sfile. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest conflicts3-26 "${testcvs} ci -m remove sdir/sfile" \
+"${CVSROOT_DIRNAME}/first-dir/sdir/sfile,v <-- sdir/sfile
+new revision: delete; previous revision: 1\.1"
+ rm -r sdir/CVS
+ dotest conflicts3-27 "${testcvs} -q update" "${QUESTION} sdir"
+ dotest conflicts3-28 "${testcvs} -q update -PdA" \
+"${QUESTION} sdir"
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ clean)
+ # Test update -C (overwrite local mods w/ repository copies)
+ mkdir 1; cd 1
+ dotest clean-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest clean-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo "The usual boring test text." > cleanme.txt
+ dotest clean-3 "${testcvs} add cleanme.txt" \
+"${SPROG} add: scheduling file .cleanme\.txt. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest clean-4 "${testcvs} -q ci -m clean-3" \
+"$CVSROOT_DIRNAME/first-dir/cleanme\.txt,v <-- cleanme\.txt
+initial revision: 1\.1"
+ # Okay, preparation is done, now test.
+ # Check that updating an unmodified copy works.
+ dotest clean-5 "${testcvs} -q update" ''
+ # Check that updating -C an unmodified copy works.
+ dotest clean-6 "${testcvs} -q update -C" ''
+ # Check that updating a modified copy works.
+ echo "fish" >> cleanme.txt
+ dotest clean-7 "${testcvs} -q update" 'M cleanme\.txt'
+ # Check that updating -C a modified copy works.
+ dotest clean-8 "${testcvs} -q update -C" \
+"(Locally modified cleanme\.txt moved to \.#cleanme\.txt\.1\.1)
+U cleanme\.txt"
+ # And check that the backup copy really was made.
+ dotest clean-9 "cat .#cleanme.txt.1.1" \
+"The usual boring test text\.
+fish"
+
+ # Do it all again, this time naming the file explicitly.
+ rm .#cleanme.txt.1.1
+ dotest clean-10 "${testcvs} -q update cleanme.txt" ''
+ dotest clean-11 "${testcvs} -q update -C cleanme.txt" ''
+ echo "bluegill" >> cleanme.txt
+ dotest clean-12 "${testcvs} -q update cleanme.txt" 'M cleanme\.txt'
+ dotest clean-13 "${testcvs} -q update -C cleanme.txt" \
+"(Locally modified cleanme\.txt moved to \.#cleanme\.txt\.1\.1)
+U cleanme\.txt"
+ # And check that the backup copy really was made.
+ dotest clean-14 "cat .#cleanme.txt.1.1" \
+"The usual boring test text\.
+bluegill"
+
+ # Now try with conflicts
+ cd ..
+ dotest clean-15 "${testcvs} -q co -d second-dir first-dir" \
+'U second-dir/cleanme\.txt'
+ cd second-dir
+ echo "conflict test" >> cleanme.txt
+ dotest clean-16 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/first-dir/cleanme\.txt,v <-- cleanme\.txt
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../first-dir
+ echo "fish" >> cleanme.txt
+ dotest clean-17 "${testcvs} -nq update" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/cleanme\.txt,v
+retrieving revision 1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1 and 1\.2 into cleanme\.txt
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in cleanme\.txt
+C cleanme\.txt"
+ dotest clean-18 "${testcvs} -q update -C" \
+"(Locally modified cleanme\.txt moved to \.#cleanme\.txt\.1\.1)
+U cleanme\.txt"
+ dotest clean-19 "cat .#cleanme.txt.1.1" \
+"The usual boring test text\.
+fish"
+
+ # Done. Clean up.
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ keywordexpand)
+ # Tests of the original *BSD tag= and keywordexpand= features
+ # are done via the LocalKeyword= and KeywordExpand features.
+
+ # Skip this in noredirect mode because it is too easy for the primary
+ # and secondary error messages to get out of sync when the
+ # CVSROOT/config files are broken. This is intentional, since it is
+ # possible and even likely that an administrator might want to set up
+ # different configurations on the two servers and the paths to the
+ # config files on the secondary and primary were intentionally left
+ # intact even though they might be different.
+ if $noredirect; then
+ notnoredirect keywordexpand
+ continue
+ fi
+
+ mkdir keywordexpand; cd keywordexpand
+
+ dotest keywordexpand-1 "${testcvs} -q co CVSROOT" \
+'U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg'
+ cd CVSROOT
+ echo LocalKeyword=MyBSD=CVSHeader >> config
+ # First do not expand any keywords
+ echo KeywordExpand=i >> config
+ dotest keywordexpand-2 "${testcvs} -Q ci -mkeywordexpand config"
+
+ cd ..
+
+ mkdir testimport; cd testimport
+ echo '$''Author$' > file1
+ echo '$''Date$' >> file1
+ echo '$''CVSHeader$' >> file1
+ echo '$''Header$' >> file1
+ echo '$''Id$' >> file1
+ echo '$''Locker$' >> file1
+ echo '$''Log$' >> file1
+ echo '$''Name$' >> file1
+ echo '$''RCSfile$' >> file1
+ echo '$''Revision$' >> file1
+ echo '$''Source$' >> file1
+ echo '$''State$' >> file1
+ echo '$''MyBSD$' >> file1
+ dotest keywordexpand-3 \
+"${testcvs} -Q import -I ! -m test-import-with-bsd-keyword keywordexpand
vendor v1" \
+''
+ cd ..
+
+ dotest keywordexpand-4 "${testcvs} -Q checkout keywordexpand" ''
+ cd keywordexpand
+ dotest keywordexpand-5 "cat file1" \
+"\$""Author\$
+\$""Date\$
+\$""CVSHeader\$
+\$""Header\$
+\$""Id\$
+\$""Locker\$
+\$""Log\$
+\$""Name\$
+\$""RCSfile\$
+\$""Revision\$
+\$""Source\$
+\$""State\$
+\$MyBSD\$"
+ cd ../CVSROOT
+ # Now expand just the MyBSD and Id keywords
+ mv config config.old
+ sed -e 's/KeywordExpand=i/KeywordExpand=iMyBSD,Id/' < config.old >
config
+ rm -f config.old
+ dotest keywordexpand-6 "${testcvs} -Q ci -mkeywordexpand config"
+ cd ../keywordexpand
+ echo 'a change' >> file1
+ dotest keywordexpand-7 "${testcvs} -Q ci -madd"
+ dotest keywordexpand-8 "cat file1" \
+"\$""Author\$
+\$""Date\$
+\$""CVSHeader\$
+\$""Header\$
+\$""Id: file1,v 1\.2 [0-9/]* [0-9:]* ${username} Exp \$
+\$""Locker\$
+\$""Log\$
+\$""Name\$
+\$""RCSfile\$
+\$""Revision\$
+\$""Source\$
+\$""State\$
+\$MyBSD: keywordexpand/file1,v 1\.2 [0-9/]* [0-9:]* ${username} Exp \$
+a change"
+
+ cd ../CVSROOT
+ mv config config.old
+ sed -e 's/LocalKeyword=MyBSD/LocalKeyword=My_BSD/' \
+ <config.old >config
+ dotest keywordexpand-9 "$testcvs -Q ci -minvalidlocalkeyword config"
+ dotest keywordexpand-10 "$testcvs -Q update config" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Bad character \`_' in key \`My_BSD'"
+ cp config.old config
+ dotest keywordexpand-11 "$testcvs -Q ci -mfixit config" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Bad character \`_' in key \`My_BSD'" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Bad character \`_' in key \`My_BSD'
+$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]: LocalKeyword
ignored: Bad character \`_' in key \`My_BSD'"
+ dotest keywordexpand-12 "$testcvs -Q update config"
+ sed -e 's/LocalKeyword=MyBSD=CVSHeader/LocalKeyword=MyBSD=Name/' \
+ <config.old >config
+ dotest keywordexpand-13 \
+"$testcvs -Q ci -minvalidlocalkeyword2 config"
+ dotest keywordexpand-14 "$testcvs -Q update config" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Unknown LocalId mode: \`Name'"
+ cp config.old config
+ dotest keywordexpand-15 "$testcvs -Q ci -mfixit2 config" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Unknown LocalId mode: \`Name'" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
LocalKeyword ignored: Unknown LocalId mode: \`Name'
+$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]: LocalKeyword
ignored: Unknown LocalId mode: \`Name'"
+ dotest keywordexpand-16 "$testcvs -Q update config"
+
+ dokeep
+ # Done. Clean up.
+ cd ../..
+ rm -rf $TESTDIR/keywordexpand
+ modify_repo rm -rf $CVSROOT_DIRNAME/keywordexpand
+ restore_adm
+ ;;
+
+
+
+ tag-ext)
+ # Test the new tag extensions:
+ # .trunk
+ # .base
+ # .next
+ # .prev
+ # .root
+ # .origin
+ save_TZ=$TZ
+ TZ=UTC0; export TZ
+ module=tag-ext
+ mkdir $module; cd $module
+ mkdir top; cd top
+ dotest tag-ext-init-1 "$testcvs -Q co -l ."
+ mkdir tag-ext
+ dotest tag-ext-init-1b "$testcvs -Q add tag-ext"
+ cd ..
+ dotest tag-ext-init-1c "$testcvs -Q co $module"
+ cd $module
+ echo content >file1
+ echo different content >file2
+ dotest tag-ext-init-2 "$testcvs -Q add file1 file2"
+ dotest tag-ext-init-3 "$testcvs -Q ci -madd-em"
+ echo content2 >file1
+ echo different content2 >file2
+ dotest tag-ext-init-4 "$testcvs -Q ci -mvers2"
+ dotest tag-ext-init-5 "$testcvs -Q tag -b BRANCH1"
+ echo content3 >file1
+ echo different content3 >file2
+ dotest tag-ext-init-6 "$testcvs -Q ci -mvers3"
+ dotest tag-ext-init-7 "$testcvs -Q tag -b BRANCH2"
+ dotest tag-ext-init-8 "$testcvs -Q update -r BRANCH1"
+ echo b1content >file3
+ dotest tag-ext-init-9 "$testcvs -Q add file3"
+ dotest tag-ext-init-10 "$testcvs -Q ci -maddbranch1"
+ dotest tag-ext-init-11 "$testcvs -Q update -r BRANCH2"
+ echo b2content >file3
+ dotest tag-ext-init-12 "$testcvs -Q add file3"
+ dotest tag-ext-init-13 "$testcvs -Q ci -maddbranch2"
+ dotest tag-ext-init-14 "$testcvs -Q update -A"
+ echo content >file3
+ dotest tag-ext-init-15 "$testcvs -Q add file3"
+ dotest tag-ext-init-16 "$testcvs -Q ci -maddtrunk"
+ cd ..
+ mkdir import
+ cd import
+ echo content vicontent >file3
+ dotest tag-ext-init-17 \
+"$testcvs -Q import -mimportvendor tag-ext VENDOR RELEASE"
+ cd ..
+ rm -r import
+ cd $module
+
+
+
+ dotest tag-ext-1 "$testcvs -q log" "
+RCS file: $CVSROOT_DIRNAME/$module/file1,v
+Working file: file1
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ BRANCH2: 1\.3\.0\.2
+ BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-em
+=============================================================================
+
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+Working file: file2
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ BRANCH2: 1\.3\.0\.2
+ BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-em
+=============================================================================
+
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+Working file: file3
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ RELEASE: 1\.1\.1\.1
+ VENDOR: 1\.1\.1
+ BRANCH2: 1\.1\.0\.4
+ BRANCH1: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 6; selected revisions: 6
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addtrunk
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; commitid:
${commitid};
+branches: 1\.1\.1; 1\.1\.2; 1\.1\.4;
+file file3 was initially added on branch BRANCH1\.
+----------------------------
+revision 1\.1\.4\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addbranch2
+----------------------------
+revision 1\.1\.4\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: +0 -0;
commitid: ${commitid};
+file file3 was added on branch BRANCH2 on ${ISO8601DATE}
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addbranch1
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+importvendor
+============================================================================="
+
+ dotest tag-ext-2 "$testcvs -q update -r.base"
+ dotest tag-ext-3 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.3/[a-zA-Z0-9 :]*//T\.base
+/file2/1\.3/[a-zA-Z0-9 :]*//T\.base
+/file3/1\.2/[a-zA-Z0-9 :]*//T\.base
+D
+T\.base"
+
+ dotest tag-ext-4 "$testcvs -q update -r.trunk"
+ dotest tag-ext-5 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.3/[a-zA-Z0-9 :]*//T\.trunk
+/file2/1\.3/[a-zA-Z0-9 :]*//T\.trunk
+/file3/1\.2/[a-zA-Z0-9 :]*//T\.trunk
+D
+T\.trunk"
+
+ dotest tag-ext-6 "$testcvs -q update -r.trunk.prev" \
+"U file1
+U file2
+cvs update: \`file3' is no longer in the repository"
+ dotest tag-ext-7 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2/[a-zA-Z0-9 :]*//T1\.2
+/file2/1\.2/[a-zA-Z0-9 :]*//T1\.2
+D
+N\.trunk\.prev"
+
+ dotest tag-ext-8 "$testcvs -q update -rBRANCH1" \
+"U file3"
+ dotest tag-ext-9 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2/[a-zA-Z0-9 :]*//TBRANCH1
+/file2/1\.2/[a-zA-Z0-9 :]*//TBRANCH1
+/file3/1\.1\.2\.1/[a-zA-Z0-9 :]*//TBRANCH1
+D
+TBRANCH1"
+
+ echo b1content >file1
+ echo b1content >file2
+ echo b1content >file3
+ dotest tag-ext-10 "$testcvs -Q ci -maddbranch1"
+ dotest tag-ext-12 "$testcvs -Q tag -b BRANCH1-1"
+ echo b1content2 >file1
+ echo b1content2 >file2
+ echo b1content2 >file3
+ dotest tag-ext-13 "$testcvs -Q ci -maddbranch1"
+ dotest tag-ext-14 "$testcvs -q update -rBRANCH1-1" \
+"U file1
+U file2
+U file3"
+ echo b1-1content >file1
+ echo b1-1content >file2
+ echo b1-1content >file3
+ dotest tag-ext-15 "$testcvs -Q ci -maddbranch1-1"
+ echo b1-1content2 >file1
+ echo b1-1content2 >file2
+ echo b1-1content2 >file3
+ dotest tag-ext-16 "$testcvs -Q ci -maddbranch1-1"
+ dotest tag-ext-17 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+/file2/1\.2\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+/file3/1\.1\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+D
+TBRANCH1-1"
+
+ dotest_fail tag-ext-18 "$testcvs -Q -n diff -r \.prev" \
+"${SPROG} \[diff aborted\]: Tag .\.prev. invalid\. Tag must not be relative:
.\.prev."
+
+ dotest_fail tag-ext-19 "$testcvs diff -r \.prev file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.2\.2\.1\.2\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2\.2\.1\.2\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< b1-1content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1-1content
+---
+> b1-1content2"
+
+ dotest_fail tag-ext-20 "$testcvs diff -r \.root file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.2\.2\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2\.2\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+ dotest_fail tag-ext-21 "$testcvs diff -r \.root.root file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.2
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2 -r1\.2\.2\.1\.2\.2
+1c1
+< different content2
+---
+> b1-1content2
+${SPROG} diff: Tag \.root\.root refers to a dead (removed) revision in file
.file3.\.
+cvs diff: No comparison available\. Pass .-N. to .${SPROG} diff.${QUESTION}"
+
+ dotest_fail tag-ext-22 "$testcvs diff -r \.origin file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< different content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+ dotest_fail tag-ext-23 "$testcvs diff -r \.origin.head file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.3
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.3 -r1\.2\.2\.1\.2\.2
+1c1
+< different content3
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.2
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.2 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content2
+---
+> b1-1content2"
+
+ dotest tag-ext-24 "$testcvs -q update -r\.trunk" \
+"U file1
+U file2
+U file3"
+
+ echo tcontent >file3
+ dotest tag-ext-25 "$testcvs -Q ci -maddtrunk"
+
+ dotest_fail tag-ext-26 "$testcvs -Q -n diff -r \.prev" \
+"${SPROG} \[diff aborted\]: Tag .\.prev. invalid\. Tag must not be relative:
.\.prev."
+
+ echo tcontent >file4
+ dotest tag-ext-27 "$testcvs -Q add file4"
+
+ dotest tag-ext-28 "$testcvs -Q ci -maddtrunk"
+
+ rm file4
+ dotest tag-ext-29 "$testcvs remove file4" \
+"cvs remove: scheduling \`file4' for removal
+cvs remove: use \`cvs commit' to remove this file permanently"
+
+ dotest tag-ext-30 "$testcvs -Q ci -mremtrunk"
+
+ dotest_fail tag-ext-31 "$testcvs diff -r \.prev file2 file3" \
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.2
+retrieving revision 1\.3
+diff -r1\.2 -r1\.3
+1c1
+< different content2
+---
+> different content3
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.2
+retrieving revision 1\.3
+diff -r1\.2 -r1\.3
+1c1
+< content
+---
+> tcontent"
+
+ dotest_fail tag-ext-32 "$testcvs diff -r \.root file2 file3" \
+"cvs diff: tag \.root is not in file file2
+cvs diff: tag \.root is not in file file3"
+
+ dotest tag-ext-33 "$testcvs diff -r \.origin.head file2 file3"
+
+ dotest_fail tag-ext-34 "$testcvs diff -r BRANCH1-1\.root file2 file3"
\
+"Index: file2
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+retrieving revision 1\.2\.2\.1
+retrieving revision 1\.3
+diff -r1\.2\.2\.1 -r1\.3
+1c1
+< b1content
+---
+> different content3
+Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.3
+diff -r1\.1\.2\.1 -r1\.3
+1c1
+< b1content
+---
+> tcontent"
+
+ dotest_fail tag-ext-35 "$testcvs diff -r BRANCH1-1\.root\.prev\.head
file2 file3" \
+"cvs diff: tag BRANCH1-1\.root\.prev\.head is not in file file3"
+
+ dotest tag-ext-36 "$testcvs -q update -r BRANCH1 file3" \
+"U file3"
+
+ dotest tag-ext-37 "$testcvs tag -b BRANCH1-2 file3" \
+"T file3"
+
+ echo b1content3 >file3
+ dotest tag-ext-38 "$testcvs -Q ci -maddbranch1 file3"
+
+ dotest tag-ext-39 "$testcvs -q update -r BRANCH1-2 file3" \
+"U file3"
+
+ echo b1-2content1 >file3
+ dotest tag-ext-40 "$testcvs -Q ci -maddbranch1-2.1 file3"
+
+ echo b1-2content2 >file3
+ dotest tag-ext-41 "$testcvs -Q ci -maddbranch1-2.2 file3"
+
+ dotest tag-ext-42 "$testcvs tag -b BRANCH1-2-1 file3" \
+"T file3"
+
+ echo b1-2content3 >file3
+ dotest tag-ext-43 "$testcvs -Q ci -maddbranch1-2.3 file3"
+
+ dotest tag-ext-44 "$testcvs -q update -r BRANCH1-2-1 file3" \
+"U file3"
+
+ echo b1-2-1content1 >file3
+ dotest tag-ext-45 "$testcvs -Q ci -maddbranch1-2-1.1 file3"
+
+ echo b1-2-1content2 >file3
+ dotest tag-ext-46 "$testcvs -Q ci -maddbranch1-2-1.2 file3"
+
+
+ echo b1-2-1content3 >file3
+ dotest tag-ext-47 "$testcvs -Q ci -maddbranch1-2-1.3 file3"
+
+ echo b1-2-1content4 >file3
+ dotest tag-ext-48 "$testcvs -Q ci -maddbranch1-2-1.4 file3"
+
+ echo b1-2-1content5 >file3
+ dotest tag-ext-49 "$testcvs -Q ci -maddbranch1-2-1.5 file3"
+
+ echo b1-2-1content6 >file3
+ dotest tag-ext-50 "$testcvs -Q ci -maddbranch1-2-1.6 file3"
+
+ echo b1-2-1content7 >file3
+ dotest tag-ext-51 "$testcvs -Q ci -maddbranch1-2-1.7 file3"
+
+ echo b1-2-1content8 >file3
+ dotest tag-ext-52 "$testcvs -Q ci -maddbranch1-2-1.8 file3"
+
+ echo b1-2-1content9 >file3
+ dotest tag-ext-53 "$testcvs -Q ci -maddbranch1-2-1.9 file3"
+
+ dotest tag-ext-54 "$testcvs tag -b BRANCH1-2-1-1 file3" \
+"T file3"
+
+ echo b1-2-1content10 >file3
+ dotest tag-ext-55 "$testcvs -Q ci -maddbranch1-2-1.10 file3"
+
+ rm file3
+ dotest tag-ext-56 "$testcvs remove file3" \
+"cvs remove: scheduling \`file3' for removal
+cvs remove: use \`cvs commit' to remove this file permanently"
+
+ dotest tag-ext-57 "$testcvs -Q ci -mremBRANCH1-2-1.11 file3"
+
+ dotest tag-ext-58 "$testcvs -q update -r BRANCH1-2-1-1 file3" \
+"U file3"
+
+ echo b1-2-1-1content1 >file3
+ dotest tag-ext-59 "$testcvs -Q ci -maddbranch1-2-1-1.1 file3"
+
+ echo b1-2-1-1content2 >file3
+ dotest tag-ext-60 "$testcvs -Q ci -maddbranch1-2-1-1.2 file3"
+
+ dotest tag-ext-61 "$testcvs tag -b BRANCH1-2-1-1-1-1 file3" \
+"T file3"
+
+ echo b1-2-1-1content3 >file3
+ dotest tag-ext-62 "$testcvs -Q ci -maddbranch1-2-1-1.3 file3"
+
+ dotest tag-ext-63 "$testcvs admin -o 1.1.2.2.2.2.2.4::1.1.2.2.2.2.2.8
file3" \
+"RCS file: $CVSROOT_DIRNAME/$module/file3,v
+deleting revision 1\.1\.2\.2\.2\.2\.2\.5
+deleting revision 1\.1\.2\.2\.2\.2\.2\.6
+deleting revision 1\.1\.2\.2\.2\.2\.2\.7
+done"
+
+ dotest tag-ext-64 "$testcvs admin -o 1.1.2.2.2.2.2.2::1.1.2.2.2.2.2.4
file3" \
+"RCS file: $CVSROOT_DIRNAME/$module/file3,v
+deleting revision 1\.1\.2\.2\.2\.2\.2\.3
+done"
+
+ dotest_fail tag-ext-65 "$testcvs diff -r \.root\.root\.root\.head
file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.3
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.3 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1content3
+---
+> b1-2-1-1content3"
+
+ dotest_fail tag-ext-66 "$testcvs diff -r \.origin -r \.origin\.head
file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.3
+diff -r1\.1\.2\.1 -r1\.1\.2\.3
+1c1
+< b1content
+---
+> b1content3"
+
+ dotest tag-ext-67 "$testcvs diff -r \.origin\.prev -r \.root\.head
file3"
+
+ dotest_fail tag-ext-68 "$testcvs diff -r \.origin\.prev -r \.root
file3" \
+"cvs diff: tag \.origin\.prev is not in file file3"
+
+ dotest_fail tag-ext-69 "$testcvs diff -r \.origin -r \.root\.head
file3" \
+"cvs diff: Tag \.root\.head refers to a dead (removed) revision in file
\`file3'\.
+cvs diff: No comparison available\. Pass \`-N' to \`cvs diff'?"
+
+ dotest_fail tag-ext-70 "$testcvs diff -r
\.prev\.prev\.prev\.prev\.prev\.prev\.prev\.prev\.prev\.prev\.prev file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.1 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1content
+---
+> b1-2-1-1content3"
+
+ date_T1=`getrlogdate -r1\.2 tag-ext/file3`
+ date_T2=`getrlogdate -r1\.1\.2\.2\.2\.2\.2\.8 tag-ext/file3`
+
+ dotest_fail tag-ext-71 "$testcvs diff -r \.trunk:'$date_T1' file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.2
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.2 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< content
+---
+> b1-2-1-1content3"
+
+ dotest_fail tag-ext-72 "$testcvs diff -r BRANCH1-2-1:'$date_T2'
file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.8
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.2\.2\.2\.2\.8 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1-2-1content8
+---
+> b1-2-1-1content3"
+
+ dotest_fail tag-ext-73 "$testcvs diff -rBRANCH1.prev file1" \
+"Index: file1
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file1,v
+retrieving revision 1\.2\.2\.1
+retrieving revision 1\.3
+diff -r1\.2\.2\.1 -r1\.3
+1c1
+< b1content
+---
+> content3"
+
+ COMMITID=$(cat $CVSROOT_DIRNAME/tag-ext/file3,v | grep -m 21
commitid | tail -n 1);
+ COMMITID=`expr match ${COMMITID:9} '\([a-zA-Z0-9]*\)'`
+ dotest_fail tag-ext-74 "$testcvs diff -r .commitid.$COMMITID file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.2
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.2\.2\.2\.2\.9\.2\.2 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1-2-1-1content2
+---
+> b1-2-1-1content3"
+
+ dotest_fail tag-ext-75 "$testcvs diff -r @$COMMITID file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.2
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.2\.2\.2\.2\.9\.2\.2 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1-2-1-1content2
+---
+> b1-2-1-1content3"
+
+ dotest_fail tag-ext-76 "$testcvs diff -r '@<$COMMITID' file3" \
+"Index: file3
+===================================================================
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.1
+retrieving revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+diff -r1\.1\.2\.2\.2\.2\.2\.9\.2\.1 -r1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+1c1
+< b1-2-1-1content1
+---
+> b1-2-1-1content3"
+
+ COMMITID=$(cat $CVSROOT_DIRNAME/tag-ext/file1,v | grep -m 3 commitid
| tail -n 1);
+ COMMITID=`expr match ${COMMITID:9} '\([a-zA-Z0-9]*\)'`
+ dotest tag-ext-77 "$testcvs update -r .commitid.$COMMITID" \
+"cvs update: Updating .
+U file1
+U file2
+cvs update: \`file3' is no longer in the repository"
+
+ dotest tag-ext-78 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.1/[a-zA-Z0-9 :]*//T\.commitid\.$COMMITID
+/file2/1\.1/[a-zA-Z0-9 :]*//T\.commitid\.$COMMITID
+D
+N\.commitid\.$COMMITID"
+
+ dotest tag-ext-79 "$testcvs update -r @$COMMITID" \
+"cvs update: Updating ."
+
+ dotest tag-ext-80 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.1/[a-zA-Z0-9 :]*//address@hidden
+/file2/1\.1/[a-zA-Z0-9 :]*//address@hidden
+D
address@hidden"
+
+ dotest tag-ext-81 "$testcvs -Q update -r BRANCH1-1"
+
+ dotest tag-ext-82 "$testcvs -Q update -r BRANCH1 file1"
+
+ COMMITID=$(cat $CVSROOT_DIRNAME/tag-ext/file3,v | grep -m 3 commitid
| tail -n 1);
+ COMMITID=`expr match ${COMMITID:9} '\([a-zA-Z0-9]*\)'`
+ dotest_fail tag-ext-83 "$testcvs diff -r .commitid.$COMMITID" \
+"cvs diff: Diffing .
+cvs diff: tag .commitid.$COMMITID is not in file file1
+cvs diff: tag .commitid.$COMMITID is not in file file2
+Index: file3
+===================================================================
+RCS file: /tmp/cvs-sanity/cvsroot/tag-ext/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+ dotest_fail tag-ext-84 "$testcvs diff -r '@<$COMMITID'" \
+"cvs diff: Diffing .
+cvs diff: tag @<$COMMITID is not in file file1
+cvs diff: tag @<$COMMITID is not in file file2
+cvs diff: tag @<$COMMITID is not in file file3"
+
+ COMMITID=$(cat $CVSROOT_DIRNAME/tag-ext/file3,v | grep -m 2 commitid
| tail -n 1);
+ COMMITID=`expr match ${COMMITID:9} '\([a-zA-Z0-9]*\)'`
+ dotest_fail tag-ext-85 "$testcvs diff -r '@<$COMMITID'" \
+"cvs diff: Diffing .
+cvs diff: tag @<$COMMITID is not in file file1
+cvs diff: tag @<$COMMITID is not in file file2
+Index: file3
+===================================================================
+RCS file: /tmp/cvs-sanity/cvsroot/tag-ext/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+ dotest tag-ext-86 "$testcvs update -j .origin -j .prev file1 file2
file3" \
+"RCS file: /tmp/cvs-sanity/cvsroot/tag-ext/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1
+Merging differences between 1\.1 and 1\.2\.2\.1 into file1
+rcsmerge: warning: conflicts during merge
+RCS file: /tmp/cvs-sanity/cvsroot/tag-ext/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1\.2\.1
+Merging differences between 1\.1 and 1\.2\.2\.1\.2\.1 into file2
+rcsmerge: warning: conflicts during merge
+RCS file: /tmp/cvs-sanity/cvsroot/tag-ext/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.1
+Merging differences between 1\.1\.2\.1 and 1\.1\.2\.1\.2\.1 into file3
+rcsmerge: warning: conflicts during merge"
+
+ dotest tag-ext-87 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2\.2\.2/Result of merge+[a-zA-Z0-9 :]*//TBRANCH1
+/file2/1\.2\.2\.1\.2\.2/Result of merge+[a-zA-Z0-9 :]*//TBRANCH1-1
+/file3/1\.1\.2\.1\.2\.2/Result of merge+[a-zA-Z0-9 :]*//TBRANCH1-1
+D
+TBRANCH1-1"
+
+ dotest tag-ext-88 "$testcvs tag -r .prev atag file1 file2 file3" \
+"T file1
+T file2
+T file3"
+
+ dotest tag-ext-89 "$testcvs rtag -r BRANCH1-1.root.next btag
tag-ext" \
+"cvs rtag: Tagging tag-ext"
+
+ dotest tag-ext-90 "$testcvs rlog tag-ext" \
+"cvs rlog: Logging tag-ext
+
+RCS file: $CVSROOT_DIRNAME/$module/file1,v
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ btag: 1\.2\.2\.2
+ atag: 1\.2\.2\.1
+ BRANCH1-1: 1\.2\.2\.1\.0\.2
+ BRANCH2: 1\.3\.0\.2
+ BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 7; selected revisions: 7
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.2\.2;
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-em
+----------------------------
+revision 1\.2\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1
+----------------------------
+revision 1\.2\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.2\.2\.1\.2;
+addbranch1
+----------------------------
+revision 1\.2\.2\.1\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+----------------------------
+revision 1\.2\.2\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+=============================================================================
+
+RCS file: $CVSROOT_DIRNAME/$module/file2,v
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ btag: 1\.2\.2\.2
+ atag: 1\.2\.2\.1\.2\.1
+ BRANCH1-1: 1\.2\.2\.1\.0\.2
+ BRANCH2: 1\.3\.0\.2
+ BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 7; selected revisions: 7
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.2\.2;
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-em
+----------------------------
+revision 1\.2\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1
+----------------------------
+revision 1\.2\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.2\.2\.1\.2;
+addbranch1
+----------------------------
+revision 1\.2\.2\.1\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+----------------------------
+revision 1\.2\.2\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+=============================================================================
+
+RCS file: $CVSROOT_DIRNAME/$module/file3,v
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ btag: 1\.1\.2\.2
+ atag: 1\.1\.2\.1\.2\.1
+ BRANCH1-2-1-1-1-1: 1\.1\.2\.2\.2\.2\.2\.9\.2\.2\.0\.2
+ BRANCH1-2-1-1: 1\.1\.2\.2\.2\.2\.2\.9\.0\.2
+ BRANCH1-2-1: 1\.1\.2\.2\.2\.2\.0\.2
+ BRANCH1-2: 1\.1\.2\.2\.0\.2
+ BRANCH1-1: 1\.1\.2\.1\.0\.2
+ RELEASE: 1\.1\.1\.1
+ VENDOR: 1\.1\.1
+ BRANCH2: 1\.1\.0\.4
+ BRANCH1: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 24; selected revisions: 24
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addtrunk
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addtrunk
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; commitid:
${commitid};
+branches: 1\.1\.1; 1\.1\.2; 1\.1\.4;
+file file3 was initially added on branch BRANCH1\.
+----------------------------
+revision 1\.1\.4\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addbranch2
+----------------------------
+revision 1\.1\.4\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: +0 -0;
commitid: ${commitid};
+file file3 was added on branch BRANCH2 on ${ISO8601DATE}
+----------------------------
+revision 1\.1\.2\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1
+----------------------------
+revision 1\.1\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.1\.2\.2\.2;
+addbranch1
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+branches: 1\.1\.2\.1\.2;
+addbranch1
+----------------------------
+revision 1\.1\.2\.2\.2\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2\.3
+----------------------------
+revision 1\.1\.2\.2\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.1\.2\.2\.2\.2\.2;
+addbranch1-2\.2
+----------------------------
+revision 1\.1\.2\.2\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2\.1
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.11
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: +0 -0;
commitid: ${commitid};
+remBRANCH1-2-1\.11
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.10
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1\.10
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.9
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+branches: 1\.1\.2\.2\.2\.2\.2\.9\.2;
+addbranch1-2-1\.9
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.8
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1\.8
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.4
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1\.4
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1\.2
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1\.1
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1-1\.3
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1-1\.2
+----------------------------
+revision 1\.1\.2\.2\.2\.2\.2\.9\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-2-1-1\.1
+----------------------------
+revision 1\.1\.2\.1\.2\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+----------------------------
+revision 1\.1\.2\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -1;
commitid: ${commitid};
+addbranch1-1
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+importvendor
+=============================================================================
+
+RCS file: $CVSROOT_DIRNAME/$module/Attic/file4,v
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: +0 -0;
commitid: ${commitid};
+remtrunk
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +1 -0;
commitid: ${commitid};
+addtrunk
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; commitid:
${commitid};
+file file4 was initially added on branch \.trunk\.
+============================================================================="
+
+ # clean up
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r $module
+ modify_repo rm -rf $CVSROOT_DIRNAME/tag-ext
+ TZ=$save_TZ
+ ;;
+
+
+
+ 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*
+ # infinite loops: modules148a1.1 - modules148a1.2
+ # 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
+ # -i, -o, -u, -e, -t: modules5
+ # slashes in module names: modules3
+ # invalid module definitions: modules6
+
+ ############################################################
+ # These tests are to make sure that administrative files get
+ # rebuilt, regardless of how and where files are checked
+ # out.
+ ############################################################
+ # Check out the whole repository
+ mkdir 1; cd 1
+ dotest modules-1 "${testcvs} -q co ." 'U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg'
+ echo "# made a change" >>CVSROOT/modules
+ dotest modules-1d "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+ rm -rf 1
+
+ ############################################################
+ # Check out CVSROOT
+ mkdir 1; cd 1
+ dotest modules-2 "${testcvs} -q co CVSROOT" 'U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg'
+ echo "# made a change" >>CVSROOT/modules
+ dotest modules-2d "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+ rm -rf 1
+
+ ############################################################
+ # Check out CVSROOT in some other directory
+ modify_repo mkdir $CVSROOT_DIRNAME/somedir
+ mkdir 1; cd 1
+ dotest modules-3 "${testcvs} -q co somedir" ''
+ cd somedir
+ dotest modules-3d "${testcvs} -q co CVSROOT" 'U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg'
+ echo "# made a change" >>CVSROOT/modules
+ dotest modules-3g "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/somedir
+ ############################################################
+ # end rebuild tests
+ ############################################################
+
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+
+ mkdir 1
+ cd 1
+
+ dotest modules-143 "${testcvs} -q co first-dir" ""
+
+ cd first-dir
+ mkdir subdir
+ dotest modules-143a "${testcvs} add subdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir added to the repository"
+
+ cd subdir
+ mkdir ssdir
+ dotest modules-143b "${testcvs} add ssdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir/ssdir added to the repository"
+
+ touch a b
+
+ dotest modules-144 "${testcvs} add a b" \
+"${SPROG} add: scheduling file .a. for addition
+${SPROG} add: scheduling file .b. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+
+ dotest modules-145 "$testcvs ci -m added" \
+"$CPROG commit: Examining .
+$CPROG commit: Examining ssdir
+$CVSROOT_DIRNAME/first-dir/subdir/a,v <-- a
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/subdir/b,v <-- b
+initial revision: 1\.1"
+
+ cd ..
+ dotest modules-146 "$testcvs -q co CVSROOT" \
+"U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg"
+
+ # Here we test that CVS can deal with CVSROOT (whose repository
+ # is at top level) in the same directory as subdir (whose repository
+ # is a subdirectory of first-dir). TODO: Might want to check that
+ # files can actually get updated in this state.
+ dotest modules-147 "$testcvs -q update"
+
+ cat >CVSROOT/modules <<EOF
+realmodule first-dir/subdir a
+dirmodule first-dir/subdir
+namedmodule -d nameddir first-dir/subdir
+aliasmodule -a first-dir/subdir/a
+aliasnested -a first-dir/subdir/ssdir
+topfiles -a first-dir/file1 first-dir/file2
+world -a .
+statusmod -s Mungeable
+# Check for ability to block infinite loops.
+infinitealias -a infinitealias
+# Prior to 1.11.12 & 1.12.6, the infinite alias loop check didn't strip
+# slashes or work if a module called a module which then called itself
+# (A -> A was blocked, but not A -> B -> A or deeper).
+infinitealias2 -a infinitealias2/
+infinitealias3 -a infinitealias4/
+infinitealias4 -a aliasmodule infinitealias5
+infinitealias5 -a infinitealias3/
+# Options must come before arguments. It is possible this should
+# be relaxed at some point (though the result would be bizarre for
+# -a); for now test the current behavior.
+bogusalias first-dir/subdir/a -a
+EOF
+ dotest modules-148 "$testcvs ci -m 'add modules' CVSROOT/modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ..
+ # The "statusmod" module contains an error; trying to use it
+ # will produce "modules file missing directory" I think.
+ # However, that shouldn't affect the ability of "cvs co -c" or
+ # "cvs co -s" to do something reasonable with it.
+ dotest modules-148a0 "$testcvs co -c" \
+'aliasmodule -a first-dir/subdir/a
+aliasnested -a first-dir/subdir/ssdir
+bogusalias first-dir/subdir/a -a
+dirmodule first-dir/subdir
+infinitealias -a infinitealias
+infinitealias2 -a infinitealias2/
+infinitealias3 -a infinitealias4/
+infinitealias4 -a aliasmodule infinitealias5
+infinitealias5 -a infinitealias3/
+namedmodule -d nameddir first-dir/subdir
+realmodule first-dir/subdir a
+statusmod -s Mungeable
+topfiles -a first-dir/file1 first-dir/file2
+world -a \.'
+ # There is code in modules.c:save_d which explicitly skips
+ # modules defined with -a, which is why aliasmodule is not
+ # listed.
+ dotest modules-148a1 "${testcvs} co -s" \
+'statusmod Mungeable
+bogusalias NONE first-dir/subdir/a -a
+dirmodule NONE first-dir/subdir
+namedmodule NONE first-dir/subdir
+realmodule NONE first-dir/subdir a'
+
+ # Check that infinite loops are avoided
+ dotest modules-148a1.1 "${testcvs} co infinitealias" \
+"$CPROG checkout: module \`infinitealias' in modules file contains infinite
loop" \
+"$SPROG server: module \`infinitealias' in modules file contains infinite loop
+$SPROG checkout: module \`infinitealias' in modules file contains infinite
loop"
+ # Prior to 1.11.12 & 1.12.6, the inifinte alias loop check did not
+ # strip slashes.
+ dotest modules-148a1.2 "${testcvs} co infinitealias2" \
+"$CPROG checkout: module \`infinitealias2' in modules file contains infinite
loop" \
+"$SPROG server: module \`infinitealias2' in modules file contains infinite loop
+$SPROG checkout: module \`infinitealias2' in modules file contains infinite
loop"
+ # Prior to 1.11.12 & 1.12.6, the inifinte alias loop check did not
+ # notice when A -> B -> A, it only noticed A -> A.
+ dotest modules-148a1.3 "${testcvs} co infinitealias3/" \
+"$CPROG checkout: module \`infinitealias3' in modules file contains infinite
loop" \
+"$SPROG server: module \`infinitealias3' in modules file contains infinite loop
+$SPROG checkout: module \`infinitealias3' in modules file contains infinite
loop"
+
+ # Test that real modules check out to realmodule/a, not subdir/a.
+ dotest modules-149a1 "${testcvs} co realmodule" "U realmodule/a"
+ dotest modules-149a2 "test -d realmodule && test -f realmodule/a" ""
+ dotest_fail modules-149a3 "test -f realmodule/b" ""
+ dotest modules-149a4 "${testcvs} -q co realmodule" ""
+ dotest modules-149a5 "echo yes | ${testcvs} release -d realmodule" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .realmodule.: "
+
+ dotest_fail modules-149b1 "${testcvs} co realmodule/a" \
+"${SPROG}"' checkout: module `realmodule/a'\'' is a request for a file in a
module which is not a directory' \
+"${SPROG}"' server: module `realmodule/a'\'' is a request for a file in a
module which is not a directory
+'"${CPROG}"' \[checkout aborted\]: cannot expand modules'
+
+ # Now test the ability to check out a single file from a directory
+ dotest modules-150c "${testcvs} co dirmodule/a" "U dirmodule/a"
+ dotest modules-150d "test -d dirmodule && test -f dirmodule/a" ""
+ dotest_fail modules-150e "test -f dirmodule/b" ""
+ dotest modules-150f "echo yes | ${testcvs} release -d dirmodule" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .dirmodule.: "
+ # Now test the ability to correctly reject a non-existent filename.
+ # For maximum studliness we would check that an error message is
+ # being output.
+ # We accept a zero exit status because it is what CVS does
+ # (Dec 95). Probably the exit status should be nonzero,
+ # however.
+ dotest modules-150g1 "$testcvs co dirmodule/nonexist" \
+"$SPROG checkout: nothing known about \`dirmodule/nonexist'"
+ # We tolerate the creation of the dirmodule directory, since that
+ # is what CVS does, not because we view that as preferable to not
+ # creating it.
+ dotest_fail modules-150g2 "test -f dirmodule/a || test -f
dirmodule/b" ""
+ rm -r dirmodule
+
+ # Now test that a module using -d checks out to the specified
+ # directory.
+ dotest modules-150h1 "${testcvs} -q co namedmodule" \
+'U nameddir/a
+U nameddir/b'
+ dotest modules-150h2 "test -f nameddir/a && test -f nameddir/b" ""
+ echo add line >>nameddir/a
+ dotest modules-150h3 "${testcvs} -q co namedmodule" 'M nameddir/a'
+ rm nameddir/a
+ dotest modules-150h4 "${testcvs} -q co namedmodule" 'U nameddir/a'
+ dotest modules-150h99 "echo yes | ${testcvs} release -d nameddir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .nameddir.: "
+
+ # Now test that alias modules check out to subdir/a, not
+ # aliasmodule/a.
+ dotest modules-151 "${testcvs} co aliasmodule" ""
+ dotest_fail modules-152 "test -d aliasmodule" ""
+ echo abc >>first-dir/subdir/a
+ dotest modules-153 "${testcvs} -q co aliasmodule" "M
first-dir/subdir/a"
+
+ cd ..
+ rm -r 1
+
+ mkdir 2
+ cd 2
+ dotest modules-155a0 "${testcvs} co aliasnested" \
+"${SPROG} checkout: Updating first-dir/subdir/ssdir"
+ dotest modules-155a1 "test -d first-dir" ''
+ dotest modules-155a2 "test -d first-dir/subdir" ''
+ dotest modules-155a3 "test -d first-dir/subdir/ssdir" ''
+ # Test that nothing extraneous got created.
+ dotest modules-155a4 "ls" "first-dir" \
+"CVS
+first-dir"
+ cd ..
+ rm -r 2
+
+ # Test checking out everything.
+ mkdir 1
+ cd 1
+ dotest modules-155b "${testcvs} -q co world" \
+"U CVSROOT/${DOTSTAR}
+U first-dir/subdir/a
+U first-dir/subdir/b"
+ cd ..
+ rm -r 1
+
+ # Test checking out a module which lists at least two
+ # specific files twice. At one time, this failed over
+ # remote CVS.
+ mkdir 1
+ cd 1
+ dotest modules-155c1 "${testcvs} -q co first-dir" \
+"U first-dir/subdir/a
+U first-dir/subdir/b"
+
+ cd first-dir
+ echo 'first revision' > file1
+ echo 'first revision' > file2
+ dotest modules-155c2 "${testcvs} add file1 file2" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: scheduling file `file2'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add these files permanently'
+ dotest modules-155c3 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ cd ..
+ rm -r first-dir
+ dotest modules-155c4 "${testcvs} -q co topfiles" \
+"U first-dir/file1
+U first-dir/file2"
+ dotest modules-155c5 "${testcvs} -q co topfiles" ""
+
+ # Make sure the right thing happens if we remove a file.
+ cd first-dir
+ dotest modules-155c6 "${testcvs} -q rm -f file1" \
+"${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest modules-155c7 "${testcvs} -q ci -m remove-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.1"
+ cd ..
+ rm -r first-dir
+ dotest modules-155c8 "$testcvs -q co topfiles" \
+"U first-dir/file2"
+
+ dokeep
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ modules2)
+ # More tests of modules, in particular the & feature.
+ mkdir 1; cd 1
+ dotest modules2-setup-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir second-dir third-dir
+ dotest modules2-setup-2 \
+"${testcvs} add first-dir second-dir third-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/second-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/third-dir added to the repository"
+ cd third-dir
+ touch file3
+ dotest modules2-setup-3 "${testcvs} add file3" \
+"${SPROG} add: scheduling file .file3. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest modules2-setup-4 "${testcvs} -q ci -m add file3" \
+"$CVSROOT_DIRNAME/third-dir/file3,v <-- file3
+initial revision: 1\.1"
+ cd ../..
+ rm -r 1
+
+ mkdir 1
+ cd 1
+
+ dotest modules2-1 "${testcvs} -q co CVSROOT/modules" \
+'U CVSROOT/modules'
+ cd CVSROOT
+ cat >> modules << EOF
+ampermodule &first-dir &second-dir
+combmodule third-dir file3 &first-dir
+ampdirmod -d newdir &first-dir &second-dir
+badmod -d newdir
+messymod first-dir &messymodchild
+messymodchild -d sdir/child second-dir
+EOF
+ # Depending on whether the user also ran the modules test
+ # we will be checking in revision 1.2 or 1.3.
+ dotest modules2-2 "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ..
+
+ dotest modules2-3 "${testcvs} -q co ampermodule" ''
+ dotest modules2-4 "test -d ampermodule/first-dir" ''
+ dotest modules2-5 "test -d ampermodule/second-dir" ''
+
+ # Test ability of cvs release to handle multiple arguments
+ # See comment at "release" for list of other cvs release tests.
+ cd ampermodule
+ if ${testcvs} release -d first-dir second-dir <<EOF >>${LOGFILE}
+yes
+yes
+EOF
+ then
+ pass modules2-6
+ else
+ fail modules2-6
+ fi
+ dotest_fail modules2-7 "test -d first-dir" ''
+ dotest_fail modules2-8 "test -d second-dir" ''
+
+ cd ..
+
+ # There used to be a nasty-hack that made CVS skip creation of the
+ # module dir (in this case ampermodule) when -n was specified
+ dotest modules2-ampermod-1 "${testcvs} -q co -n ampermodule" ''
+ dotest modules2-ampermod-2 "test -d ampermodule/first-dir" ''
+ dotest modules2-ampermod-3 "test -d ampermodule/second-dir" ''
+
+ # Test release of a module
+ if echo yes |${testcvs} release -d ampermodule >>${LOGFILE}; then
+ pass modules2-ampermod-release-1
+ else
+ fail modules2-ampermod-release-1
+ fi
+ dotest_fail modules2-ampermod-release-2 "test -d ampermodule" ''
+
+ # and the '-n' test again, but in conjunction with '-d'
+ dotest modules2-ampermod-4 "${testcvs} -q co -n -d newname
ampermodule" ''
+ dotest modules2-ampermod-5 "test -d newname/first-dir" ''
+ dotest modules2-ampermod-6 "test -d newname/second-dir" ''
+ rm -rf newname
+
+ # Now we create another directory named first-dir and make
+ # sure that CVS doesn't get them mixed up.
+ mkdir first-dir
+ # Note that this message should say "Updating ampermodule/first-dir"
+ # I suspect. This is a long-standing behavior/bug....
+ dotest modules2-9 "${testcvs} co ampermodule" \
+"${SPROG} checkout: Updating first-dir
+${SPROG} checkout: Updating second-dir"
+ touch ampermodule/first-dir/amper1
+ cd ampermodule
+ dotest modules2-10 "${testcvs} add first-dir/amper1" \
+"${SPROG} add: scheduling file .first-dir/amper1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ..
+
+ # As with the "Updating xxx" message, the "U first-dir/amper1"
+ # message (instead of "U ampermodule/first-dir/amper1") is
+ # rather fishy.
+ dotest modules2-12 "${testcvs} co ampermodule" \
+"${SPROG} checkout: Updating first-dir
+A first-dir/amper1
+${SPROG} checkout: Updating second-dir"
+
+ if $remote; then
+ dotest modules2-13r "$testcvs -q ci -m add-it ampermodule" \
+"$CVSROOT_DIRNAME/first-dir/amper1,v <-- ampermodule/first-dir/amper1
+initial revision: 1\.1"
+ else
+ # Trying this as above led to a "protocol error" message.
+ # Work around this bug.
+ cd ampermodule
+ dotest modules2-13 "$testcvs -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/amper1,v <-- first-dir/amper1
+initial revision: 1\.1"
+ cd ..
+ fi
+ cd ..
+ rm -r 1
+
+ # Now test the "combmodule" module (combining regular modules
+ # and ampersand modules in the same module definition).
+ mkdir 1; cd 1
+ dotest modules2-14 "${testcvs} co combmodule" \
+"U combmodule/file3
+${SPROG} checkout: Updating first-dir
+U first-dir/amper1"
+ dotest modules2-15 "test -f combmodule/file3" ""
+ dotest modules2-16 "test -f combmodule/first-dir/amper1" ""
+ cd combmodule
+ rm -r first-dir
+ # At least for now there is no way to tell CVS that
+ # some files/subdirectories come from one repository directory,
+ # and others from another.
+ # This seems like a pretty sensible behavior to me, in the
+ # sense that first-dir doesn't "really" exist within
+ # third-dir, so CVS just acts as if there is nothing there
+ # to do.
+ dotest modules2-17 "${testcvs} update -d" \
+"${SPROG} update: Updating \."
+
+ cd ..
+ dotest modules2-18 "${testcvs} -q co combmodule" \
+"U first-dir/amper1"
+ dotest modules2-19 "test -f combmodule/first-dir/amper1" ""
+ cd ..
+ rm -r 1
+
+ # Now test the "ampdirmod" and "badmod" modules to be sure that
+ # options work with ampersand modules but don't prevent the
+ # "missing directory" error message.
+ mkdir 1; cd 1
+ dotest modules2-20 "${testcvs} co ampdirmod" \
+"${SPROG} checkout: Updating first-dir
+U first-dir/amper1
+${SPROG} checkout: Updating second-dir"
+ dotest modules2-21 "test -f newdir/first-dir/amper1" ""
+ dotest modules2-22 "test -d newdir/second-dir" ""
+ dotest_fail modules2-23 "${testcvs} co badmod" \
+"${SPROG} checkout: modules file missing directory for module badmod" \
+"${SPROG} server: modules file missing directory for module badmod
+${CPROG} \[checkout aborted\]: cannot expand modules"
+ cd ..
+ rm -r 1
+
+ # Confirm that a rename with added depth nested in an ampersand
+ # module works.
+ mkdir 1; cd 1
+ dotest modules2-nestedrename-1 "${testcvs} -q co messymod" \
+"U messymod/amper1"
+ dotest modules2-nestedrename-2 "test -d messymod/sdir" ''
+ dotest modules2-nestedrename-3 "test -d messymod/sdir/CVS" ''
+ dotest modules2-nestedrename-4 "test -d messymod/sdir/child" ''
+ dotest modules2-nestedrename-5 "test -d messymod/sdir/child/CVS" ''
+ cd ..; rm -r 1
+
+ # FIXME: client/server has a bug. It should be working like a local
+ # repository in this case, but fails to check out the second module
+ # in the list when a branch is specified.
+ mkdir 1; cd 1
+ dotest modules2-ampertag-setup-1 \
+"${testcvs} -Q rtag tag first-dir second-dir third-dir" \
+''
+ dotest modules2-ampertag-1 "${testcvs} -q co -rtag ampermodule" \
+"U first-dir/amper1"
+ if $remote; then
+ dotest_fail modules2-ampertag-2 "test -d ampermodule/second-dir" ''
+ dotest_fail modules2-ampertag-3 "test -d
ampermodule/second-dir/CVS" ''
+ else
+ dotest modules2-ampertag-2 "test -d ampermodule/second-dir" ''
+ dotest modules2-ampertag-3 "test -d ampermodule/second-dir/CVS" ''
+ fi
+ cd ..; rm -r 1
+
+ # Test for tag files when an ampermod is renamed with more path
+ # elements than it started with.
+ #
+ # FIXME: This is currently broken in the remote case, possibly only
+ # because the messymodchild isn't being checked out at all.
+ mkdir 1; cd 1
+# dotest modules2-tagfiles-setup-1 \
+#"${testcvs} -Q rtag -b branch first-dir second-dir" \
+#''
+ dotest modules2-tagfiles-1 "${testcvs} -q co -rtag messymod" \
+"U messymod/amper1"
+ if $remote; then
+ dotest_fail modules2-tagfiles-2r "test -d messymod/sdir" ''
+ else
+ dotest modules2-tagfiles-2 "cat messymod/sdir/CVS/Tag" 'Ttag'
+ fi
+ cd ..; rm -r 1
+
+ # Test that CVS gives an error if one combines -a with
+ # other options.
+ # Probably would be better to break this out into a separate
+ # test. Although it is short, it shares no files/state with
+ # the rest of the modules2 tests.
+ mkdir 1; cd 1
+ dotest modules2-a0.5 "${testcvs} -q co CVSROOT/modules" \
+'U CVSROOT/modules'
+ cd CVSROOT
+ echo 'aliasopt -a -d onedir first-dir' >modules
+ dotest modules2-a0 "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+ dotest_fail modules2-a1 "${testcvs} -q co aliasopt" \
+"${SPROG} checkout: -a cannot be specified in the modules file along with
other options" \
+"${SPROG} server: -a cannot be specified in the modules file along with other
options
+${CPROG} \[checkout aborted\]: cannot expand modules"
+ cd ..; rm -r 1
+
+ # Clean up.
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir \
+ $CVSROOT_DIRNAME/third-dir
+ ;;
+
+
+
+ modules3)
+ # More tests of modules, in particular what happens if several
+ # modules point to the same file.
+
+ # First just set up a directory first-dir and a file file1 in it.
+ mkdir 1; cd 1
+
+ dotest modules3-0 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest modules3-1 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+
+ cd first-dir
+ echo file1 >file1
+ dotest modules3-2 "${testcvs} add file1" \
+"${SPROG} add: scheduling file \`file1' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest modules3-3 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ cd ..
+
+ dotest modules3-4 "${testcvs} -q update -d CVSROOT" \
+"U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ cat >modules <<EOF
+mod1 -a first-dir/file1
+bigmod -a mod1 first-dir/file1
+namednest -d src/sub/dir first-dir
+nestdeeper -d src/sub1/sub2/sub3/dir first-dir
+nestshallow -d src/dir second-dir/suba/subb
+path/in/modules &mod1
+another/path/test -d another/path/test first-dir
+EOF
+ dotest modules3-5 "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+
+ dotest modules3-6 "${testcvs} -q co bigmod" ''
+ rm -r first-dir
+ dotest modules3-7 "${testcvs} -q co bigmod" 'U first-dir/file1'
+ cd ..
+ rm -r 1
+
+ mkdir 1; cd 1
+ mkdir suba
+ mkdir suba/subb
+ # This fails to work remote (it doesn't notice the directories,
+ # I suppose because they contain no files). Bummer, especially
+ # considering this is a documented technique and everything.
+ dotest modules3-7a \
+"${testcvs} import -m add-dirs second-dir tag1 tag2" \
+"${SPROG} import: Importing ${CVSROOT_DIRNAME}/second-dir/suba
+${SPROG} import: Importing ${CVSROOT_DIRNAME}/second-dir/suba/subb
+
+No conflicts created by this import" "
+No conflicts created by this import"
+ cd ..; rm -r 1
+ mkdir 1; cd 1
+ dotest modules3-7b "${testcvs} co second-dir" \
+"${SPROG} checkout: Updating second-dir
+${SPROG} checkout: Updating second-dir/suba
+${SPROG} checkout: Updating second-dir/suba/subb" \
+"${SPROG} checkout: Updating second-dir"
+
+ if $remote; then
+ cd second-dir
+ mkdir suba
+ dotest modules3-7-workaround1 "${testcvs} add suba" \
+"Directory ${CVSROOT_DIRNAME}/second-dir/suba added to the repository"
+ cd suba
+ mkdir subb
+ dotest modules3-7-workaround2 "${testcvs} add subb" \
+"Directory ${CVSROOT_DIRNAME}/second-dir/suba/subb added to the repository"
+ cd ../..
+ fi
+
+ cd second-dir/suba/subb
+ touch fileb
+ dotest modules3-7c "${testcvs} add fileb" \
+"${SPROG} add: scheduling file .fileb. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest modules3-7d "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/second-dir/suba/subb/fileb,v <-- fileb
+initial revision: 1\.1"
+ cd ../../..
+ cd ..; rm -r 1
+
+ mkdir 1
+ cd 1
+ dotest modules3-8 "${testcvs} -q co namednest" \
+'U src/sub/dir/file1'
+ dotest modules3-9 "test -f src/sub/dir/file1" ''
+ cd ..
+ rm -r 1
+
+ # Try the same thing, but with the directories nested even
+ # deeper (deeply enough so they are nested more deeply than
+ # the number of directories from / to ${TESTDIR}).
+ mkdir 1
+ cd 1
+ dotest modules3-10 "${testcvs} -q co nestdeeper" \
+'U src/sub1/sub2/sub3/dir/file1'
+ dotest modules3-11 "test -f src/sub1/sub2/sub3/dir/file1" ''
+
+ # While we are doing things like twisted uses of '/' (e.g.
+ # modules3-12), try this one.
+ if $remote; then
+ dotest_fail modules3-11b \
+"${testcvs} -q update ${TESTDIR}/1/src/sub1/sub2/sub3/dir/file1" \
+"absolute pathnames invalid for server (specified
.${TESTDIR}/1/src/sub1/sub2/sub3/dir.)"
+ fi # end of remote-only tests
+
+ cd ..
+ rm -r 1
+
+ # This one is almost too twisted for words. The pathname output
+ # in the message from "co" doesn't include the "path/in/modules",
+ # but those directories do get created (with no CVSADM except
+ # in "modules" which has a CVSNULLREPOS).
+ # I'm not sure anyone is relying on this nonsense or whether we
+ # need to keep doing it, but it is what CVS currently does...
+ # Skip it for remote; the remote code has the good sense to
+ # not deal with it (on the minus side it gives
+ # "internal error: repository string too short." (CVS 1.9) or
+ # "warning: server is not creating directories one at a time" (now)
+ # instead of a real error).
+ # I'm tempted to just make it a fatal error to have '/' in a
+ # module name. But see comments at modules3-16.
+ if $remote; then :; else
+ mkdir 1; cd 1
+ dotest modules3-12 "${testcvs} -q co path/in/modules" \
+"U first-dir/file1"
+ dotest modules3-13 "test -f path/in/modules/first-dir/file1" ''
+ cd ..; rm -r 1
+ fi # end of tests skipped for remote
+
+ # Now here is where it used to get seriously bogus.
+ mkdir 1; cd 1
+ dotest modules3-14 \
+"${testcvs} -q rtag tag1 path/in/modules" ''
+ # CVS used to create this even though rtag should *never* affect
+ # the directory current when it is called!
+ dotest_fail modules3-15 "test -d path/in/modules" ''
+ # Just for trivia's sake, rdiff was not similarly vulnerable
+ # because it passed 0 for run_module_prog to do_module.
+ cd ..; rm -r 1
+
+ # Some people seem to want this to work. I still suspect there
+ # are dark corners in slashes in module names. This probably wants
+ # more thought before we start hacking on CVS (one way or the other)
+ # or documenting this.
+ mkdir 2; cd 2
+ dotest modules3-16 "${testcvs} -q co another/path/test" \
+"U another/path/test/file1"
+ dotest modules3-17 "cat another/path/test/file1" 'file1'
+
+ dokeep
+ cd ..; rm -r 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ modules4)
+ # Some tests using the modules file with aliases that
+ # exclude particular directories.
+
+ mkdir 1; cd 1
+
+ dotest modules4-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest modules4-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+
+ cd first-dir
+ mkdir subdir subdir_long
+ dotest modules4-3 "${testcvs} add subdir subdir_long" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir added to the repository
+Directory ${CVSROOT_DIRNAME}/first-dir/subdir_long added to the repository"
+
+ echo file1 > file1
+ dotest modules4-4 "${testcvs} add file1" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+
+ echo file2 > subdir/file2
+ dotest modules4-5 "${testcvs} add subdir/file2" \
+"${SPROG}"' add: scheduling file `subdir/file2'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+
+ echo file3 > subdir_long/file3
+ dotest modules4-6 "${testcvs} add subdir_long/file3" \
+"${SPROG}"' add: scheduling file `subdir_long/file3'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+
+ dotest modules4-7 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/subdir/file2,v <-- subdir/file2
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/subdir_long/file3,v <-- subdir_long/file3
+initial revision: 1\.1"
+
+ cd ..
+
+ dotest modules4-8 "${testcvs} -q update -d CVSROOT" \
+"U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ cat >modules <<EOF
+all -a first-dir
+some -a !first-dir/subdir first-dir
+other -a !first-dir/subdir !first-dir/subdir_long first-dir
+somewhat -a first-dir !first-dir/subdir
+EOF
+ dotest modules4-9 "${testcvs} -q ci -m add-modules" \
+"$CVSROOT_DIRNAME/CVSROOT/modules,v <-- modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+
+ cd ..
+ mkdir 2; cd 2
+
+ dotest modules4-10 "${testcvs} -q co all" \
+"U first-dir/file1
+U first-dir/subdir/file2
+U first-dir/subdir_long/file3"
+ rm -r first-dir
+
+ dotest modules4-11 "${testcvs} -q co some" \
+"U first-dir/file1
+U first-dir/subdir_long/file3"
+ dotest_fail modules4-12 "test -d first-dir/subdir" ''
+ dotest modules4-13 "test -d first-dir/subdir_long" ''
+ rm -r first-dir
+
+ if $remote; then
+ # But remote seems to do it the other way.
+ dotest modules4-14r-1 "${testcvs} -q co somewhat" \
+"U first-dir/file1
+U first-dir/subdir_long/file3"
+ dotest_fail modules4-14r-2 "test -d first-dir/subdir" ''
+ dotest modules4-14r-3 "test -d first-dir/subdir_long" ''
+ else
+ # This is strange behavior, in that the order of the
+ # "!first-dir/subdir" and "first-dir" matter, and it isn't
+ # clear that they should. I suspect it is long-standing
+ # strange behavior but I haven't verified that.
+ dotest modules4-14-1 "${testcvs} -q co somewhat" \
+"U first-dir/file1
+U first-dir/subdir/file2
+U first-dir/subdir_long/file3"
+ dotest modules4-14-2 "test -d first-dir/subdir" ''
+ dotest modules4-14-3 "test -d first-dir/subdir_long" ''
+ fi
+ rm -r first-dir
+
+ dotest modules4-15 "${testcvs} -q co other" \
+"U first-dir/file1"
+ dotest_fail modules4-16 "test -d first-dir/subdir" ''
+ dotest_fail modules4-17 "test -d first-dir/subdir_long" ''
+ rm -r first-dir
+
+ cd ..
+ rm -r 2
+
+ dotest modules4-18 "${testcvs} rtag tag some" \
+"${SPROG} rtag: Tagging first-dir
+${SPROG} rtag: Ignoring first-dir/subdir
+${SPROG} rtag: Tagging first-dir/subdir_long"
+
+ cd 1/first-dir/subdir
+ dotest modules4-19 "${testcvs} log file2" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/subdir/file2,v
+Working file: file2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add-it
+============================================================================="
+
+ dokeep
+ cd ../../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ modules5)
+ # Test module programs
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+ dotest modules5-1 "$testcvs -q co first-dir"
+ cd first-dir
+ mkdir subdir
+ dotest modules5-2 "${testcvs} add subdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir added to the repository"
+ cd subdir
+ mkdir ssdir
+ dotest modules5-3 "${testcvs} add ssdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir/ssdir added to the repository"
+ touch a b
+ dotest modules5-4 "${testcvs} add a b" \
+"${SPROG} add: scheduling file .a. for addition
+${SPROG} add: scheduling file .b. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+
+ dotest modules5-5 "${testcvs} ci -m added" \
+"${CPROG} commit: Examining .
+${CPROG} commit: Examining ssdir
+${CVSROOT_DIRNAME}/first-dir/subdir/a,v <-- a
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/first-dir/subdir/b,v <-- b
+initial revision: 1\.1"
+
+ cd ..
+ dotest modules5-6 "${testcvs} -q co CVSROOT" \
+"U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg"
+
+ # FIXCVS: The sleep in the following script helps avoid out of
+ # order messages, but we really need to figure out how to fix
+ # cvs to prevent them in the first place.
+ for i in checkout export tag; do
+ cat >> ${CVSROOT_DIRNAME}/$i.sh <<EOF
+#! $TESTSHELL
+sleep 1
+echo "$i script invoked in \`pwd\`"
+echo "args: \$@"
+EOF
+ # Cygwin doesn't set premissions correctly over the Samba share.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x ${CVSROOT_DIRNAME}/$i.sh"
+ else
+ chmod +x ${CVSROOT_DIRNAME}/$i.sh
+ fi
+ done
+
+ OPTS="-o${CVSROOT_DIRNAME}/checkout.sh -e
${CVSROOT_DIRNAME}/export.sh -t${CVSROOT_DIRNAME}/tag.sh"
+ cat >CVSROOT/modules <<EOF
+realmodule ${OPTS} first-dir/subdir a
+dirmodule ${OPTS} first-dir/subdir
+namedmodule -d nameddir ${OPTS} first-dir/subdir
+EOF
+
+ dotest modules5-7 "$testcvs -Q ci -m 'add modules' CVSROOT/modules"
+
+ cd ..
+ rm -rf first-dir
+
+ # Test that real modules check out to realmodule/a, not subdir/a.
+ if $remote; then
+ # FIXCVS?
+ # Mac OSX 10.3 (Darwin ppc-osx1 5.5) fails here when $TMPDIR
+ # contains a symlink (it does not fail the local modules5-8).
+ # Since no other platforms are exhibiting the same problem, I
+ # suspect an issue with OSX and fork() or the like dereferencing
+ # the symlink, but it is possible it is something that could be
+ # fixed or worked around in CVS.
+ dotest modules5-8r "$testcvs co realmodule" \
+"U realmodule/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .realmodule..
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: realmodule"
+ else
+ dotest modules5-8 "${testcvs} co realmodule" \
+"U realmodule/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .realmodule..
+checkout script invoked in ${TESTDIR}/1
+args: realmodule"
+ fi
+ dotest modules5-9 "test -d realmodule && test -f realmodule/a" ""
+ dotest_fail modules5-10 "test -f realmodule/b" ""
+ if $remote; then
+ dotest modules5-11 "${testcvs} -q co realmodule" \
+"checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: realmodule"
+ dotest modules5-12 "${testcvs} -q update" ''
+ echo "change" >>realmodule/a
+ dotest modules5-13 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/first-dir/subdir/a,v <-- realmodule/a
+new revision: 1\.2; previous revision: 1\.1"
+ else
+ dotest modules5-11 "${testcvs} -q co realmodule" \
+"checkout script invoked in ${TESTDIR}/1
+args: realmodule"
+ dotest modules5-12 "${testcvs} -q update" ''
+ echo "change" >>realmodule/a
+ dotest modules5-13 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/first-dir/subdir/a,v <-- realmodule/a
+new revision: 1\.2; previous revision: 1\.1"
+ fi
+ dotest modules5-14 "echo yes | ${testcvs} release -d realmodule" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .realmodule.: "
+ dotest modules5-15 "${testcvs} -q rtag -Dnow MYTAG realmodule" \
+"tag script invoked in ${TESTDIR}/1
+args: realmodule MYTAG" \
+"tag script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: realmodule MYTAG"
+ if $remote; then
+ dotest modules5-16 "${testcvs} -q export -r MYTAG realmodule" \
+"U realmodule/a
+export script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: realmodule"
+ else
+ dotest modules5-16 "${testcvs} -q export -r MYTAG realmodule" \
+"U realmodule/a
+export script invoked in ${TESTDIR}/1
+args: realmodule"
+ fi
+ rm -r realmodule
+
+ dotest_fail modules5-17 "${testcvs} co realmodule/a" \
+"${SPROG}"' checkout: module `realmodule/a'\'' is a request for a file in a
module which is not a directory' \
+"${SPROG}"' server: module `realmodule/a'\'' is a request for a file in a
module which is not a directory
+'"${CPROG}"' \[checkout aborted\]: cannot expand modules'
+
+ # Now test the ability to check out a single file from a directory
+ if $remote; then
+ dotest modules5-18 "${testcvs} co dirmodule/a" \
+"U dirmodule/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .dirmodule..
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: dirmodule"
+ else
+ dotest modules5-18 "${testcvs} co dirmodule/a" \
+"U dirmodule/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .dirmodule..
+checkout script invoked in ${TESTDIR}/1
+args: dirmodule"
+ fi
+ dotest modules5-19 "test -d dirmodule && test -f dirmodule/a" ""
+ dotest_fail modules5-20 "test -f dirmodule/b" ""
+ dotest modules5-21 "echo yes | ${testcvs} release -d dirmodule" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .dirmodule.: "
+
+ # Now test the ability to correctly reject a non-existent filename.
+ # For maximum studliness we would check that an error message is
+ # being output.
+ # We accept a zero exit status because it is what CVS does
+ # (Dec 95). Probably the exit status should be nonzero,
+ # however.
+ if $remote; then
+ dotest modules5-22r "$testcvs co dirmodule/nonexist" \
+"$SPROG checkout: nothing known about \`dirmodule/nonexist'
+$SPROG checkout: Executing ..$CVSROOT_DIRNAME/checkout\.sh. .dirmodule..
+checkout script invoked in $TMPDIR/cvs-serv[0-9a-z]*
+args: dirmodule"
+ else
+ dotest modules5-22 "$testcvs co dirmodule/nonexist" \
+"$SPROG checkout: nothing known about \`dirmodule/nonexist'
+$SPROG checkout: Executing ..$CVSROOT_DIRNAME/checkout\.sh. .dirmodule..
+checkout script invoked in $TESTDIR/1
+args: dirmodule"
+ fi
+ # We tolerate the creation of the dirmodule directory, since that
+ # is what CVS does, not because we view that as preferable to not
+ # creating it.
+ dotest_fail modules5-23 "test -f dirmodule/a || test -f dirmodule/b"
""
+ rm -r dirmodule
+
+ # Now test that a module using -d checks out to the specified
+ # directory.
+ if $remote; then
+ dotest modules5-24 "${testcvs} -q co namedmodule" \
+"U nameddir/a
+U nameddir/b
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: nameddir"
+ else
+ dotest modules5-24 "${testcvs} -q co namedmodule" \
+"U nameddir/a
+U nameddir/b
+checkout script invoked in ${TESTDIR}/1
+args: nameddir"
+ fi
+ dotest modules5-25 "test -f nameddir/a && test -f nameddir/b" ""
+ echo add line >>nameddir/a
+ # This seems suspicious: when we checkout an existing directory,
+ # the checkout script gets executed in addition to the update
+ # script. Is that by design or accident?
+ if $remote; then
+ dotest modules5-26 "${testcvs} -q co namedmodule" \
+"M nameddir/a
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: nameddir"
+ else
+ dotest modules5-26 "${testcvs} -q co namedmodule" \
+"M nameddir/a
+checkout script invoked in ${TESTDIR}/1
+args: nameddir"
+ fi
+ rm nameddir/a
+
+ if $remote; then
+ dotest modules5-27 "${testcvs} -q co namedmodule" \
+"U nameddir/a
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: nameddir"
+ else
+ dotest modules5-27 "${testcvs} -q co namedmodule" \
+"U nameddir/a
+checkout script invoked in ${TESTDIR}/1
+args: nameddir"
+ fi
+ dotest modules5-28 "echo yes | ${testcvs} release -d nameddir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .nameddir.: "
+
+ # Now try the same tests with -d on command line
+ # FIXCVS? The manual says the modules programs get the module name,
+ # but they really get the directory name.
+ if $remote; then
+ dotest modules5-29 "${testcvs} co -d mydir realmodule" \
+"U mydir/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .mydir..
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-29 "${testcvs} co -d mydir realmodule" \
+"U mydir/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .mydir..
+checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ dotest modules5-30 "test -d mydir && test -f mydir/a" ""
+ dotest_fail modules5-31 "test -d realmodule || test -f mydir/b" ""
+ if $remote; then
+ dotest modules5-32 "${testcvs} -q co -d mydir realmodule" \
+"checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ dotest modules5-33 "${testcvs} -q update" ''
+ echo "change" >>mydir/a
+ dotest modules5-34 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/first-dir/subdir/a,v <-- mydir/a
+new revision: 1\.3; previous revision: 1\.2"
+ else
+ dotest modules5-32 "${testcvs} -q co -d mydir realmodule" \
+"checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ dotest modules5-33 "${testcvs} -q update" ''
+ echo "change" >>mydir/a
+ dotest modules5-34 "${testcvs} -q ci -m." \
+"$CVSROOT_DIRNAME/first-dir/subdir/a,v <-- mydir/a
+new revision: 1\.3; previous revision: 1\.2"
+ fi
+ dotest modules5-35 "echo yes | ${testcvs} release -d mydir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .mydir.: "
+ if $remote; then
+ dotest modules5-36 "${testcvs} -q rtag -Dnow MYTAG2 realmodule" \
+"tag script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: realmodule MYTAG2"
+ dotest modules5-37 "${testcvs} -q export -r MYTAG2 -d mydir
realmodule" \
+"U mydir/a
+export script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-36 "${testcvs} -q rtag -Dnow MYTAG2 realmodule" \
+"tag script invoked in ${TESTDIR}/1
+args: realmodule MYTAG2"
+ dotest modules5-37 "${testcvs} -q export -r MYTAG2 -d mydir
realmodule" \
+"U mydir/a
+export script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ rm -r mydir
+
+ # Now test the ability to check out a single file from a directory
+ if $remote; then
+ dotest modules5-38 "${testcvs} co -d mydir dirmodule/a" \
+"U mydir/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .mydir..
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-38 "${testcvs} co -d mydir dirmodule/a" \
+"U mydir/a
+${SPROG} checkout: Executing ..${CVSROOT_DIRNAME}/checkout\.sh. .mydir..
+checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ dotest modules5-39 "test -d mydir && test -f mydir/a" ""
+ dotest_fail modules5-40 "test -d dirmodule || test -f mydir/b" ""
+ dotest modules5-41 "echo yes | ${testcvs} release -d mydir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .mydir.: "
+
+ # Now test the ability to correctly reject a non-existent filename.
+ # For maximum studliness we would check that an error message is
+ # being output.
+ # We accept a zero exit status because it is what CVS does
+ # (Dec 95). Probably the exit status should be nonzero,
+ # however.
+ if $remote; then
+ dotest modules5-42r "$testcvs co -d mydir dirmodule/nonexist" \
+"$SPROG checkout: nothing known about \`mydir/nonexist'
+$SPROG checkout: Executing ..$CVSROOT_DIRNAME/checkout\.sh. .mydir..
+checkout script invoked in $TMPDIR/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-42 "$testcvs co -d mydir dirmodule/nonexist" \
+"$SPROG checkout: nothing known about \`mydir/nonexist'
+$SPROG checkout: Executing ..$CVSROOT_DIRNAME/checkout\.sh. .mydir..
+checkout script invoked in $TESTDIR/1
+args: mydir"
+ fi
+ # We tolerate the creation of the mydir directory, since that
+ # is what CVS does, not because we view that as preferable to not
+ # creating it.
+ dotest_fail modules5-43 "test -f mydir/a || test -f mydir/b" ""
+ rm -r mydir
+
+ if $remote; then
+ dotest modules5-44 "${testcvs} -q co -d mydir namedmodule" \
+"U mydir/a
+U mydir/b
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-44 "${testcvs} -q co -d mydir namedmodule" \
+"U mydir/a
+U mydir/b
+checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ dotest modules5-45 "test -f mydir/a && test -f mydir/b" ""
+ dotest_fail modules5-46 "test -d namedir"
+ echo add line >>mydir/a
+ # This seems suspicious: when we checkout an existing directory,
+ # the checkout script gets executed in addition to the update
+ # script. Is that by design or accident?
+ if $remote; then
+ dotest modules5-47 "${testcvs} -q co -d mydir namedmodule" \
+"M mydir/a
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-47 "${testcvs} -q co -d mydir namedmodule" \
+"M mydir/a
+checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ rm mydir/a
+
+ if $remote; then
+ dotest modules5-48 "${testcvs} -q co -d mydir namedmodule" \
+"U mydir/a
+checkout script invoked in ${TMPDIR}/cvs-serv[0-9a-z]*
+args: mydir"
+ else
+ dotest modules5-48 "${testcvs} -q co -d mydir namedmodule" \
+"U mydir/a
+checkout script invoked in ${TESTDIR}/1
+args: mydir"
+ fi
+ dotest modules5-49 "echo yes | ${testcvs} release -d mydir" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory .mydir.: "
+
+ dokeep
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/*.sh
+ ;;
+
+
+
+ modules6)
+ #
+ # Test invalid module definitions
+ #
+ # See the header comment for the `modules' test for an index of
+ # the complete suite of modules tests.
+ #
+
+ #
+ # There was a bug in CVS through 1.11.1p1 where a bad module name
+ # would cause the previous line to be parsed as the module
+ # definition. This test proves this doesn't happen anymore.
+ #
+ mkdir modules6
+ cd modules6
+ dotest module6-setup-1 "${testcvs} -Q co CVSROOT" ""
+ cd CVSROOT
+ echo "longmodulename who cares" >modules
+ echo "badname" >>modules
+ # This test almost isn't setup since it generates the error message
+ # we are looking for if `-Q' isn't specified, but I want to test the
+ # filename in the message later.
+ dotest modules6-setup-2 "$testcvs -Q ci -mbad-modules"
+
+ # Here's where CVS would report not being able to find `lename'
+ cd ..
+ dotest_fail modules6-1 "${testcvs} -q co badname" \
+"${SPROG} checkout: warning: NULL value for key .badname. at line 2 of
.${CVSROOT_DIRNAME}/CVSROOT/modules.
+${SPROG} checkout: cannot find module .badname. - ignored" \
+"${SPROG} server: warning: NULL value for key .badname. at line 2 of
.${CVSROOT_DIRNAME}/CVSROOT/modules.
+${SPROG} server: cannot find module .badname. - ignored
+${CPROG} \[checkout aborted\]: cannot expand modules"
+
+ dokeep
+ restore_adm
+ cd ..
+ rm -r modules6
+ ;;
+
+
+
+ modules7)
+ #
+ # Test tag problems vs an empty CVSROOT/val-tags file
+ #
+ # See the header comment for the `modules' test for an index of
+ # the complete suite of modules tests.
+ #
+ mkdir modules7
+ cd modules7
+ dotest modules7-1 "$testcvs -Q co -d top ."
+ cd top
+ mkdir zero one
+ dotest modules7-2 "$testcvs -Q add zero one"
+ cd one
+ echo 'file1 contents' > file1
+ dotest modules7-2 "$testcvs -Q add file1"
+ dotest modules7-3 "$testcvs -Q ci -mnew file1"
+ dotest modules7-4 "$testcvs -Q tag mytag file1"
+ cd ../CVSROOT
+ echo 'all -a zero one' > modules
+ dotest modules7-5 "$testcvs -Q ci -mall-module"
+ cd ../..
+ mkdir myexport
+ cd myexport
+
+ # This failed prior to CVS version 1.12.10.
+ dotest modules7-7 "$testcvs export -rmytag all" \
+"$SPROG export: Updating zero
+$SPROG export: Updating one
+U one/file1"
+ dotest modules7-8 'cat one/file1' 'file1 contents'
+
+ dokeep
+
+ # cleanup
+ restore_adm
+ cd ../..
+ rm -fr modules7
+ rm -rf $CVSROOT_DIRNAME/zero $CVSROOT_DIRNAME/one
+ ;;
+
+
+
+ mkmodules)
+ # When a file listed in checkoutlist doesn't exist, cvs-1.10.4
+ # would fail to remove the CVSROOT/.#[0-9]* temporary file it
+ # creates while mkmodules is in the process of trying to check
+ # out the missing file.
+
+ mkdir 1; cd 1
+ dotest mkmodules-temp-file-removal-1 "${testcvs} -Q co CVSROOT" ''
+ cd CVSROOT
+ echo no-such-file >> checkoutlist
+ dotest mkmodules-temp-file-removal-2 "$testcvs -Q ci -m. checkoutlist"
+
+ dotest mkmodules-temp-file-removal-3 \
+"echo $CVSROOT_DIRNAME/CVSROOT/.#[0-9]*" \
+"$CVSROOT_DIRNAME/CVSROOT/\.#\[0-9\]\*"
+
+ # Versions 1.11.6 & 1.12.1 and earlier of CVS printed most of the
+ # white space included before error messages in checkoutlist.
+ echo "no-such-file Failed to update no-such-file." >checkoutlist
+ dotest mkmodules-error-message-1 "$testcvs -Q ci -m. checkoutlist" \
+"$SPROG commit: Failed to update no-such-file\."
+
+ # Versions 1.11.6 & 1.12.1 and earlier of CVS used the error string
+ # from the checkoutlist file as the format string passed to error()'s
+ # printf. Check that this is no longer the case by verifying that
+ # printf format patterns remain unchanged.
+ echo "no-such-file Failed to update %s %lx times because %s
happened %d times." >checkoutlist
+ dotest mkmodules-error-message-2 "$testcvs -Q ci -m. checkoutlist" \
+"$SPROG commit: Failed to update %s %lx times because %s happened %d times\."
+
+ dotest mkmodules-cleanup-1 \
+"$testcvs -Q up -pr1.1 checkoutlist >checkoutlist"
+ dotest mkmodules-cleanup-2 "$testcvs -Q ci -m. checkoutlist"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ ;;
+
+
+
+ co-d)
+ # Some tests of various permutations of co-d when directories exist
+ # and checkouts lengthen.
+ #
+ # Interestingly enough, these same tests pass when the directory
+ # lengthening happens via the modules file. Go figure.
+ module=co-d
+ mkdir $module; cd $module
+ mkdir top; cd top
+ dotest co-d-init-1 "$testcvs -Q co -l ."
+ mkdir $module
+ dotest co-d-init-2 "$testcvs -Q add $module"
+ cd $module
+ echo content >file1
+ echo different content >file2
+ dotest co-d-init-3 "$testcvs -Q add file1 file2"
+ dotest co-d-init-4 "$testcvs -Q ci -madd-em"
+ cd ../..
+
+ mkdir 2; cd 2
+ dotest co-d-1 "$testcvs -q co -d dir $module" \
+"U dir/file1
+U dir/file2"
+ dotest co-d-1.2 "cat dir/CVS/Repository" "$module"
+
+ dotest co-d-2 "$testcvs -q co -d dir2/sdir $module" \
+"U dir2/sdir/file1
+U dir2/sdir/file2"
+ dotest co-d-2.2 "cat dir2/CVS/Repository" "."
+ dotest co-d-2.3 "cat dir2/sdir/CVS/Repository" "$module"
+
+ dotest co-d-2.4 "$testcvs -q co -d dir2.4/sdir/sdir2 $module" \
+"U dir2.4/sdir/sdir2/file1
+U dir2.4/sdir/sdir2/file2"
+ dotest co-d-2.4.2 "cat dir2.4/CVS/Repository" "CVSROOT/Emptydir"
+ dotest co-d-2.4.3 "cat dir2.4/sdir/CVS/Repository" "."
+ dotest co-d-2.4.3 "cat dir2.4/sdir/sdir2/CVS/Repository" "$module"
+
+ mkdir dir3
+ dotest co-d-3 "$testcvs -q co -d dir3 $module" \
+"U dir3/file1
+U dir3/file2"
+ dotest co-d-3.2 "cat dir3/CVS/Repository" "$module"
+
+ mkdir dir4
+ dotest co-d-4 "$testcvs -q co -d dir4/sdir $module" \
+"U dir4/sdir/file1
+U dir4/sdir/file2"
+
+ # CVS is only supposed to create administration directories in
+ # directories it also creates, and in the directory specified by
+ # the last portion of the path passed to -d regardless. This is
+ #
+ # FIXCVS:
+ # This is broken in client/server mode because the server does not
+ # know the client's directory structure and has to create
+ # everything.
+ if $remote; then
+ dotest co-d-4.2r "cat dir4/CVS/Repository" "."
+ else
+ dotest_fail co-d-4.2 "test -d dir4/CVS"
+ fi
+
+ dotest co-d-4.3 "cat dir4/sdir/CVS/Repository" "$module"
+
+ mkdir dir5
+ mkdir dir5/sdir
+ dotest co-d-5 "$testcvs -q co -d dir5/sdir $module" \
+"U dir5/sdir/file1
+U dir5/sdir/file2"
+ # FIXCVS as for co-d-4.2r.
+ if $remote; then
+ dotest co-d-5.2 "cat dir5/CVS/Repository" "."
+ else
+ dotest_fail co-d-5.2 "test -d dir5/CVS"
+ fi
+
+ dotest co-d-5.3 "cat dir5/sdir/CVS/Repository" "$module"
+
+ # clean up
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ rm -r $module
+ ;;
+
+
+
+ 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.
+
+ # convenience variables
+ REP=${CVSROOT}
+
+ # First, set TopLevelAdmin=yes so we're sure to get
+ # top-level CVS directories.
+ mkdir 1; cd 1
+ dotest cvsadm-setup-1 "${testcvs} -q co CVSROOT/config" \
+"U CVSROOT/config"
+ cd CVSROOT
+ echo "TopLevelAdmin=yes" >>config
+ dotest cvsadm-setup-2 "${testcvs} -q ci -m yes-top-level" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../..
+ rm -r 1
+
+ # Second, check out the modules file and edit it.
+ mkdir 1; cd 1
+ dotest cvsadm-1 "${testcvs} co CVSROOT/modules" \
+"U CVSROOT/modules"
+
+ # 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" "\."
+ dotest cvsadm-1c "cat CVSROOT/CVS/Root" ${REP}
+ dotest cvsadm-1d "cat CVSROOT/CVS/Repository" "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" \
+"${CPROG} commit: Examining .
+${CPROG} commit: Examining CVSROOT
+${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+${SPROG} commit: Rebuilding administrative file database" \
+"${CPROG} commit: Examining .
+${CPROG} commit: Examining CVSROOT"
+ rm -rf CVS CVSROOT;
+
+ # Create the various modules
+ dotest cvsadm-2 "${testcvs} -q co -l ." ''
+ mkdir mod1
+ mkdir mod1-2
+ mkdir mod2
+ mkdir mod2/sub2
+ mkdir mod2-2
+ mkdir mod2-2/sub2-2
+ dotest cvsadm-2a "${testcvs} add mod1 mod1-2 mod2 mod2/sub2 mod2-2
mod2-2/sub2-2" \
+"Directory ${CVSROOT_DIRNAME}/mod1 added to the repository
+Directory ${CVSROOT_DIRNAME}/mod1-2 added to the repository
+Directory ${CVSROOT_DIRNAME}/mod2 added to the repository
+Directory ${CVSROOT_DIRNAME}/mod2/sub2 added to the repository
+Directory ${CVSROOT_DIRNAME}/mod2-2 added to the repository
+Directory ${CVSROOT_DIRNAME}/mod2-2/sub2-2 added to the repository"
+
+ # 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-2aa "${testcvs} add mod1/file1 mod1-2/file1-2
mod2/sub2/file2 mod2-2/sub2-2/file2-2" \
+"${SPROG} add: scheduling file .mod1/file1. for addition
+${SPROG} add: scheduling file .mod1-2/file1-2. for addition
+${SPROG} add: scheduling file .mod2/sub2/file2. for addition
+${SPROG} add: scheduling file .mod2-2/sub2-2/file2-2. for addition
+${SPROG} add: use \`${SPROG} commit' to add these files permanently"
+
+ dotest cvsadm-2b "${testcvs} ci -m yup mod1 mod1-2 mod2 mod2-2" \
+"${CPROG} commit: Examining mod1
+${CPROG} commit: Examining mod1-2
+${CPROG} commit: Examining mod2
+${CPROG} commit: Examining mod2/sub2
+${CPROG} commit: Examining mod2-2
+${CPROG} commit: Examining mod2-2/sub2-2
+${CVSROOT_DIRNAME}/mod1/file1,v <-- mod1/file1
+initial revision: 1.1
+${CVSROOT_DIRNAME}/mod1-2/file1-2,v <-- mod1-2/file1-2
+initial revision: 1.1
+${CVSROOT_DIRNAME}/mod2/sub2/file2,v <-- mod2/sub2/file2
+initial revision: 1.1
+${CVSROOT_DIRNAME}/mod2-2/sub2-2/file2-2,v <-- mod2-2/sub2-2/file2-2
+initial revision: 1.1"
+ # 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" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1"
+ dotest cvsadm-3b "cat CVS/Repository" "\."
+ dotest cvsadm-3d "cat 1mod/CVS/Repository" "mod1"
+ rm -rf CVS 1mod
+
+ dotest cvsadm-4 "${testcvs} co 2mod" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2"
+ dotest cvsadm-4b "cat CVS/Repository" "\."
+ dotest cvsadm-4d "cat 2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 2mod
+
+ dotest cvsadm-5 "${testcvs} co 1d1mod" \
+"${SPROG} checkout: Updating dir1d1
+U dir1d1/file1"
+ dotest cvsadm-5b "cat CVS/Repository" "\."
+ dotest cvsadm-5d "cat dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir1d1
+
+ dotest cvsadm-6 "${testcvs} co 1d2mod" \
+"${SPROG} checkout: Updating dir1d2
+U dir1d2/file2"
+ dotest cvsadm-6b "cat CVS/Repository" "\."
+ dotest cvsadm-6d "cat dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir1d2
+
+ dotest cvsadm-7 "${testcvs} co 2d1mod" \
+"${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ dotest cvsadm-7b "cat CVS/Repository" "\."
+ dotest cvsadm-7d "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-7f "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir2d1
+
+ dotest cvsadm-8 "${testcvs} co 2d2mod" \
+"${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ dotest cvsadm-8b "cat CVS/Repository" "\."
+ dotest cvsadm-8d "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-8f "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir2d2
+
+ ##################################################
+ ## You are in a shell script of twisted little
+ ## module combination statements, all alike.
+ ##################################################
+
+ ### 1mod
+
+ dotest cvsadm-9 "${testcvs} co 1mod 1mod-2" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating 1mod-2
+U 1mod-2/file1-2"
+ # the usual for the top level
+ dotest cvsadm-9b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-9d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 1mod copy
+ dotest cvsadm-9f "cat 1mod-2/CVS/Repository" "mod1-2"
+ rm -rf CVS 1mod 1mod-2
+
+ # 1mod 2mod redmod bluemod
+ dotest cvsadm-10 "${testcvs} co 1mod 2mod" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating 2mod
+U 2mod/file2"
+ # the usual for the top level
+ dotest cvsadm-10b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-10d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 2dmod
+ dotest cvsadm-10f "cat 2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 1mod 2mod
+
+ dotest cvsadm-11 "${testcvs} co 1mod 1d1mod" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating dir1d1
+U dir1d1/file1"
+ # the usual for the top level
+ dotest cvsadm-11b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-11d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 1d1mod
+ dotest cvsadm-11f "cat dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS 1mod dir1d1
+
+ dotest cvsadm-12 "${testcvs} co 1mod 1d2mod" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating dir1d2
+U dir1d2/file2"
+ # the usual for the top level
+ dotest cvsadm-12b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-12d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 1d2mod
+ dotest cvsadm-12f "cat dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 1mod dir1d2
+
+ dotest cvsadm-13 "${testcvs} co 1mod 2d1mod" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ # the usual for the top level
+ dotest cvsadm-13b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-13d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-13f "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-13h "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS 1mod dir2d1
+
+ dotest cvsadm-14 "${testcvs} co 1mod 2d2mod" \
+"${SPROG} checkout: Updating 1mod
+U 1mod/file1
+${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ # the usual for the top level
+ dotest cvsadm-14b "cat CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-14d "cat 1mod/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-14f "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-14h "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 1mod dir2d2
+
+
+ ### 2mod
+
+ dotest cvsadm-15 "${testcvs} co 2mod 2mod-2" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2
+${SPROG} checkout: Updating 2mod-2
+U 2mod-2/file2-2"
+ # the usual for the top level
+ dotest cvsadm-15b "cat CVS/Repository" "\."
+ # the usual for 2mod
+ dotest cvsadm-15d "cat 2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2mod copy
+ dotest cvsadm-15f "cat 2mod-2/CVS/Repository" "mod2-2/sub2-2"
+ rm -rf CVS 2mod 2mod-2
+
+
+ dotest cvsadm-16 "${testcvs} co 2mod 1d1mod" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2
+${SPROG} checkout: Updating dir1d1
+U dir1d1/file1"
+ # the usual for the top level
+ dotest cvsadm-16b "cat CVS/Repository" "\."
+ # the usual for 2mod
+ dotest cvsadm-16d "cat 2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 1d1mod
+ dotest cvsadm-16f "cat dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS 2mod dir1d1
+
+ dotest cvsadm-17 "${testcvs} co 2mod 1d2mod" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2
+${SPROG} checkout: Updating dir1d2
+U dir1d2/file2"
+ # the usual for the top level
+ dotest cvsadm-17b "cat CVS/Repository" "\."
+ # the usual for 2mod
+ dotest cvsadm-17d "cat 2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 1d2mod
+ dotest cvsadm-17f "cat dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 2mod dir1d2
+
+ dotest cvsadm-18 "${testcvs} co 2mod 2d1mod" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2
+${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ # the usual for the top level
+ dotest cvsadm-18b "cat CVS/Repository" "\."
+ # the usual for 2mod
+ dotest cvsadm-18d "cat 2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2d1mod
+ dotest cvsadm-18f "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-18h "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS 2mod dir2d1
+
+ dotest cvsadm-19 "${testcvs} co 2mod 2d2mod" \
+"${SPROG} checkout: Updating 2mod
+U 2mod/file2
+${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ # the usual for the top level
+ dotest cvsadm-19b "cat CVS/Repository" "\."
+ # the usual for 2mod
+ dotest cvsadm-19d "cat 2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-19f "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-19h "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 2mod dir2d2
+
+
+ ### 1d1mod
+
+ dotest cvsadm-20 "${testcvs} co 1d1mod 1d1mod-2" \
+"${SPROG} checkout: Updating dir1d1
+U dir1d1/file1
+${SPROG} checkout: Updating dir1d1-2
+U dir1d1-2/file1-2"
+ # the usual for the top level
+ dotest cvsadm-20b "cat CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-20d "cat dir1d1/CVS/Repository" "mod1"
+ # the usual for 1d1mod copy
+ dotest cvsadm-20f "cat dir1d1-2/CVS/Repository" "mod1-2"
+ rm -rf CVS dir1d1 dir1d1-2
+
+ dotest cvsadm-21 "${testcvs} co 1d1mod 1d2mod" \
+"${SPROG} checkout: Updating dir1d1
+U dir1d1/file1
+${SPROG} checkout: Updating dir1d2
+U dir1d2/file2"
+ # the usual for the top level
+ dotest cvsadm-21b "cat CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-21d "cat dir1d1/CVS/Repository" "mod1"
+ # the usual for 1d2mod
+ dotest cvsadm-21f "cat dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir1d1 dir1d2
+
+ dotest cvsadm-22 "${testcvs} co 1d1mod 2d1mod" \
+"${SPROG} checkout: Updating dir1d1
+U dir1d1/file1
+${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ # the usual for the top level
+ dotest cvsadm-22b "cat CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-22d "cat dir1d1/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-22f "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-22h "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir1d1 dir2d1
+
+ dotest cvsadm-23 "${testcvs} co 1d1mod 2d2mod" \
+"${SPROG} checkout: Updating dir1d1
+U dir1d1/file1
+${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ # the usual for the top level
+ dotest cvsadm-23b "cat CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-23d "cat dir1d1/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-23f "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-23h "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir1d1 dir2d2
+
+
+ ### 1d2mod
+
+ dotest cvsadm-24 "${testcvs} co 1d2mod 1d2mod-2" \
+"${SPROG} checkout: Updating dir1d2
+U dir1d2/file2
+${SPROG} checkout: Updating dir1d2-2
+U dir1d2-2/file2-2"
+ # the usual for the top level
+ dotest cvsadm-24b "cat CVS/Repository" "\."
+ # the usual for 1d2mod
+ dotest cvsadm-24d "cat dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 1d2mod copy
+ dotest cvsadm-24f "cat dir1d2-2/CVS/Repository" "mod2-2/sub2-2"
+ rm -rf CVS dir1d2 dir1d2-2
+
+ dotest cvsadm-25 "${testcvs} co 1d2mod 2d1mod" \
+"${SPROG} checkout: Updating dir1d2
+U dir1d2/file2
+${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ # the usual for the top level
+ dotest cvsadm-25b "cat CVS/Repository" "\."
+ # the usual for 1d2mod
+ dotest cvsadm-25d "cat dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d1mod
+ dotest cvsadm-25f "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-25h "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir1d2 dir2d1
+
+ dotest cvsadm-26 "${testcvs} co 1d2mod 2d2mod" \
+"${SPROG} checkout: Updating dir1d2
+U dir1d2/file2
+${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ # the usual for the top level
+ dotest cvsadm-26b "cat CVS/Repository" "\."
+ # the usual for 1d2mod
+ dotest cvsadm-26d "cat dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-26f "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-26h "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir1d2 dir2d2
+
+
+ # 2d1mod
+
+ dotest cvsadm-27 "${testcvs} co 2d1mod 2d1mod-2" \
+"${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1
+${SPROG} checkout: Updating dir2d1-2/sub2d1-2
+U dir2d1-2/sub2d1-2/file1-2"
+ # the usual for the top level
+ dotest cvsadm-27b "cat CVS/Repository" "\."
+ # the usual for 2d1mod
+ dotest cvsadm-27d "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-27f "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-27h "cat dir2d1-2/CVS/Repository" "\."
+ dotest cvsadm-27j "cat dir2d1-2/sub2d1-2/CVS/Repository" "mod1-2"
+ rm -rf CVS dir2d1 dir2d1-2
+
+ dotest cvsadm-28 "${testcvs} co 2d1mod 2d2mod" \
+"${SPROG} checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1
+${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ # the usual for the top level
+ dotest cvsadm-28b "cat CVS/Repository" "\."
+ # the usual for 2d1mod
+ dotest cvsadm-28d "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-28f "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-28h "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-28j "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir2d1 dir2d2
+
+
+ # 2d2mod
+
+ dotest cvsadm-29 "${testcvs} co 2d2mod 2d2mod-2" \
+"${SPROG} checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2
+${SPROG} checkout: Updating dir2d2-2/sub2d2-2
+U dir2d2-2/sub2d2-2/file2-2"
+ # the usual for the top level
+ dotest cvsadm-29b "cat CVS/Repository" "\."
+ # the usual for 2d2mod
+ dotest cvsadm-29d "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-29f "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-29h "cat dir2d2-2/CVS/Repository" "mod2-2"
+ dotest cvsadm-29j "cat dir2d2-2/sub2d2-2/CVS/Repository" \
+"mod2-2/sub2-2"
+ rm -rf CVS dir2d2 dir2d2-2
+
+ ##################################################
+ ## And now, all of that again using the "-d" flag
+ ## on the command line.
+ ##################################################
+
+ dotest cvsadm-1d3 "${testcvs} co -d dir 1mod" \
+"${SPROG} checkout: Updating dir
+U dir/file1"
+ dotest cvsadm-1d3b "cat CVS/Repository" "\."
+ dotest cvsadm-1d3d "cat dir/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d4 "${testcvs} co -d dir 2mod" \
+"${SPROG} checkout: Updating dir
+U dir/file2"
+ dotest cvsadm-1d4b "cat CVS/Repository" "\."
+ dotest cvsadm-1d4d "cat dir/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d5 "${testcvs} co -d dir 1d1mod" \
+"${SPROG} checkout: Updating dir
+U dir/file1"
+ dotest cvsadm-1d5b "cat CVS/Repository" "\."
+ dotest cvsadm-1d5d "cat dir/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d6 "${testcvs} co -d dir 1d2mod" \
+"${SPROG} checkout: Updating dir
+U dir/file2"
+ dotest cvsadm-1d6b "cat CVS/Repository" "\."
+ dotest cvsadm-1d6d "cat dir/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d7 "${testcvs} co -d dir 2d1mod" \
+"${SPROG} checkout: Updating dir
+U dir/file1"
+ dotest cvsadm-1d7b "cat CVS/Repository" "\."
+ dotest cvsadm-1d7d "cat dir/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d8 "${testcvs} co -d dir 2d2mod" \
+"${SPROG} checkout: Updating dir
+U dir/file2"
+ dotest cvsadm-1d8b "cat CVS/Repository" "\."
+ dotest cvsadm-1d8d "cat dir/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ ##################################################
+ ## Los Combonaciones
+ ##################################################
+
+ ### 1mod
+
+ dotest cvsadm-1d9 "${testcvs} co -d dir 1mod 1mod-2" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/1mod-2
+U dir/1mod-2/file1-2"
+ # the usual for the top level
+ dotest cvsadm-1d9b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d9d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d9f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 1mod copy
+ dotest cvsadm-1d9h "cat dir/1mod-2/CVS/Repository" "mod1-2"
+ rm -rf CVS dir
+
+ # 1mod 2mod redmod bluemod
+ dotest cvsadm-1d10 "${testcvs} co -d dir 1mod 2mod" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2"
+ dotest cvsadm-1d10b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d10d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d10f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 2dmod
+ dotest cvsadm-1d10h "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d11 "${testcvs} co -d dir 1mod 1d1mod" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1"
+ dotest cvsadm-1d11b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d11d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d11f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 1d1mod
+ dotest cvsadm-1d11h "cat dir/dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d12 "${testcvs} co -d dir 1mod 1d2mod" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2"
+ dotest cvsadm-1d12b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d12d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d12f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 1d2mod
+ dotest cvsadm-1d12h "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d13 "${testcvs} co -d dir 1mod 2d1mod" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1"
+ dotest cvsadm-1d13b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d13d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d13f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-1d13h "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d13j "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d14 "${testcvs} co -d dir 1mod 2d2mod" \
+"${SPROG} checkout: Updating dir/1mod
+U dir/1mod/file1
+${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-1d14b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d14d "cat dir/CVS/Repository" "\."
+ # the usual for 1mod
+ dotest cvsadm-1d14f "cat dir/1mod/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-1d14h "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d14j "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+
+ ### 2mod
+
+ dotest cvsadm-1d15 "${testcvs} co -d dir 2mod 2mod-2" \
+"${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2
+${SPROG} checkout: Updating dir/2mod-2
+U dir/2mod-2/file2-2"
+ dotest cvsadm-1d15b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d15d "cat dir/CVS/Repository" "mod2"
+ # the usual for 2mod
+ dotest cvsadm-1d15f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2mod copy
+ dotest cvsadm-1d15h "cat dir/2mod-2/CVS/Repository" "mod2-2/sub2-2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d16 "${testcvs} co -d dir 2mod 1d1mod" \
+"${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2
+${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1"
+ dotest cvsadm-1d16b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d16d "cat dir/CVS/Repository" "mod2"
+ # the usual for 2mod
+ dotest cvsadm-1d16f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 1d1mod
+ dotest cvsadm-1d16h "cat dir/dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d17 "${testcvs} co -d dir 2mod 1d2mod" \
+"${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2
+${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2"
+ dotest cvsadm-1d17b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d17d "cat dir/CVS/Repository" "mod2"
+ # the usual for 2mod
+ dotest cvsadm-1d17f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 1d2mod
+ dotest cvsadm-1d17h "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d18 "${testcvs} co -d dir 2mod 2d1mod" \
+"${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2
+${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1"
+ dotest cvsadm-1d18b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d18d "cat dir/CVS/Repository" "mod2"
+ # the usual for 2mod
+ dotest cvsadm-1d18f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2d1mod
+ dotest cvsadm-1d18h "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d18j "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d19 "${testcvs} co -d dir 2mod 2d2mod" \
+"${SPROG} checkout: Updating dir/2mod
+U dir/2mod/file2
+${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-1d19b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d19d "cat dir/CVS/Repository" "mod2"
+ # the usual for 2mod
+ dotest cvsadm-1d19f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-1d19h "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d19j "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+
+ ### 1d1mod
+
+ dotest cvsadm-1d20 "${testcvs} co -d dir 1d1mod 1d1mod-2" \
+"${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1
+${SPROG} checkout: Updating dir/dir1d1-2
+U dir/dir1d1-2/file1-2"
+ dotest cvsadm-1d20b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d20d "cat dir/CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-1d20f "cat dir/dir1d1/CVS/Repository" "mod1"
+ # the usual for 1d1mod copy
+ dotest cvsadm-1d20h "cat dir/dir1d1-2/CVS/Repository" "mod1-2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d21 "${testcvs} co -d dir 1d1mod 1d2mod" \
+"${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1
+${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2"
+ dotest cvsadm-1d21b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d21d "cat dir/CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-1d21f "cat dir/dir1d1/CVS/Repository" "mod1"
+ # the usual for 1d2mod
+ dotest cvsadm-1d21h "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d22 "${testcvs} co -d dir 1d1mod 2d1mod" \
+"${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1
+${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1"
+ dotest cvsadm-1d22b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d22d "cat dir/CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-1d22f "cat dir/dir1d1/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-1d22h "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d22j "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d23 "${testcvs} co -d dir 1d1mod 2d2mod" \
+"${SPROG} checkout: Updating dir/dir1d1
+U dir/dir1d1/file1
+${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-1d23b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d23d "cat dir/CVS/Repository" "\."
+ # the usual for 1d1mod
+ dotest cvsadm-1d23f "cat dir/dir1d1/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-1d23h "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d23j "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+
+ ### 1d2mod
+
+ dotest cvsadm-1d24 "${testcvs} co -d dir 1d2mod 1d2mod-2" \
+"${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2
+${SPROG} checkout: Updating dir/dir1d2-2
+U dir/dir1d2-2/file2-2"
+ dotest cvsadm-1d24b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d24d "cat dir/CVS/Repository" "mod2"
+ # the usual for 1d2mod
+ dotest cvsadm-1d24f "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 1d2mod copy
+ dotest cvsadm-1d24h "cat dir/dir1d2-2/CVS/Repository" "mod2-2/sub2-2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d25 "${testcvs} co -d dir 1d2mod 2d1mod" \
+"${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2
+${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1"
+ dotest cvsadm-1d25b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d25d "cat dir/CVS/Repository" "mod2"
+ # the usual for 1d2mod
+ dotest cvsadm-1d25f "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d1mod
+ dotest cvsadm-1d25h "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d25j "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d26 "${testcvs} co -d dir 1d2mod 2d2mod" \
+"${SPROG} checkout: Updating dir/dir1d2
+U dir/dir1d2/file2
+${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-1d26b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d26d "cat dir/CVS/Repository" "mod2"
+ # the usual for 1d2mod
+ dotest cvsadm-1d26f "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-1d26h "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d26j "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+
+ # 2d1mod
+
+ dotest cvsadm-1d27 "${testcvs} co -d dir 2d1mod 2d1mod-2" \
+"${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1
+${SPROG} checkout: Updating dir/dir2d1-2/sub2d1-2
+U dir/dir2d1-2/sub2d1-2/file1-2"
+ dotest cvsadm-1d27b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d27d "cat dir/CVS/Repository" "CVSROOT/Emptydir"
+ # the usual for 2d1mod
+ dotest cvsadm-1d27f "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d27h "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ # the usual for 2d1mod
+ dotest cvsadm-1d27j "cat dir/dir2d1-2/CVS/Repository" "\."
+ dotest cvsadm-1d27l "cat dir/dir2d1-2/sub2d1-2/CVS/Repository" \
+"mod1-2"
+ rm -rf CVS dir
+
+ dotest cvsadm-1d28 "${testcvs} co -d dir 2d1mod 2d2mod" \
+"${SPROG} checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1
+${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-1d28b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d28d "cat dir/CVS/Repository" "CVSROOT/Emptydir"
+ # the usual for 2d1mod
+ dotest cvsadm-1d28f "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-1d28h "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ # the usual for 2d2mod
+ dotest cvsadm-1d28j "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d28l "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+
+ # 2d2mod
+
+ dotest cvsadm-1d29 "${testcvs} co -d dir 2d2mod 2d2mod-2" \
+"${SPROG} checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2
+${SPROG} checkout: Updating dir/dir2d2-2/sub2d2-2
+U dir/dir2d2-2/sub2d2-2/file2-2"
+ dotest cvsadm-1d29b "cat CVS/Repository" "\."
+ # the usual for the dir level
+ dotest cvsadm-1d29d "cat dir/CVS/Repository" "\."
+ # the usual for 2d2mod
+ dotest cvsadm-1d29f "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-1d29h "cat dir/dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ # the usual for 2d2mod
+ dotest cvsadm-1d29j "cat dir/dir2d2-2/CVS/Repository" "mod2-2"
+ dotest cvsadm-1d29l "cat dir/dir2d2-2/sub2d2-2/CVS/Repository" \
+"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.
+ ##################################################
+
+ dotest cvsadm-2d3-1 "$testcvs co -d dir/dir2 1mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file1"
+
+ # Remote couldn't handle this, even with the "mkdir dir", before
+ # CVS 1.11.14.
+ dotest cvsadm-2d3b "cat CVS/Repository" "\."
+ dotest cvsadm-2d3d "cat dir/CVS/Repository" "."
+ dotest cvsadm-2d3f "cat dir/dir2/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-2d4 "$testcvs co -d dir/dir2 2mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file2"
+ dotest cvsadm-2d4b "cat CVS/Repository" "\."
+ dotest cvsadm-2d4f "cat dir/dir2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-2d5 "$testcvs co -d dir/dir2 1d1mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file1"
+ dotest cvsadm-2d5b "cat CVS/Repository" "\."
+ dotest cvsadm-2d5f "cat dir/dir2/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-2d6 "$testcvs co -d dir/dir2 1d2mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file2"
+ dotest cvsadm-2d6b "cat CVS/Repository" "\."
+ dotest cvsadm-2d6f "cat dir/dir2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-2d7 "$testcvs co -d dir/dir2 2d1mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file1"
+ dotest cvsadm-2d7b "cat CVS/Repository" "\."
+ dotest cvsadm-2d7f "cat dir/dir2/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-2d8 "$testcvs co -d dir/dir2 2d2mod" \
+"$SPROG checkout: Updating dir/dir2
+U dir/dir2/file2"
+ dotest cvsadm-2d8b "cat CVS/Repository" "\."
+ dotest cvsadm-2d8f "cat dir/dir2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ ##################################################
+ ## And now, a few of those tests revisited to
+ ## test the behavior of the -N flag.
+ ##################################################
+
+ dotest cvsadm-N3 "$testcvs co -N 1mod" \
+"$SPROG checkout: Updating 1mod
+U 1mod/file1"
+ dotest cvsadm-N3b "cat CVS/Repository" "\."
+ dotest cvsadm-N3d "cat 1mod/CVS/Repository" "mod1"
+ rm -rf CVS 1mod
+
+ dotest cvsadm-N4 "$testcvs co -N 2mod" \
+"$SPROG checkout: Updating 2mod
+U 2mod/file2"
+ dotest cvsadm-N4b "cat CVS/Repository" "\."
+ dotest cvsadm-N4d "cat 2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS 2mod
+
+ dotest cvsadm-N5 "$testcvs co -N 1d1mod" \
+"$SPROG checkout: Updating dir1d1
+U dir1d1/file1"
+ dotest cvsadm-N5b "cat CVS/Repository" "\."
+ dotest cvsadm-N5d "cat dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir1d1
+
+ dotest cvsadm-N6 "$testcvs co -N 1d2mod" \
+"$SPROG checkout: Updating dir1d2
+U dir1d2/file2"
+ dotest cvsadm-N6b "cat CVS/Repository" "\."
+ dotest cvsadm-N6d "cat dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir1d2
+
+ dotest cvsadm-N7 "$testcvs co -N 2d1mod" \
+"$SPROG checkout: Updating dir2d1/sub2d1
+U dir2d1/sub2d1/file1"
+ dotest cvsadm-N7b "cat CVS/Repository" "\."
+ dotest cvsadm-N7d "cat dir2d1/CVS/Repository" "\."
+ dotest cvsadm-N7f "cat dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir2d1
+
+ dotest cvsadm-N8 "$testcvs co -N 2d2mod" \
+"$SPROG checkout: Updating dir2d2/sub2d2
+U dir2d2/sub2d2/file2"
+ dotest cvsadm-N8b "cat CVS/Repository" "\."
+ dotest cvsadm-N8d "cat dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-N8f "cat dir2d2/sub2d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir2d2
+
+ ## the ones in one-deep directories
+
+ dotest cvsadm-N1d3 "$testcvs co -N -d dir 1mod" \
+"$SPROG checkout: Updating dir/1mod
+U dir/1mod/file1"
+ dotest cvsadm-N1d3b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d3d "cat dir/CVS/Repository" "\."
+ dotest cvsadm-N1d3f "cat dir/1mod/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-N1d4 "$testcvs co -N -d dir 2mod" \
+"$SPROG checkout: Updating dir/2mod
+U dir/2mod/file2"
+ dotest cvsadm-N1d4b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d4d "cat dir/CVS/Repository" "mod2"
+ dotest cvsadm-N1d4f "cat dir/2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-N1d5 "$testcvs co -N -d dir 1d1mod" \
+"$SPROG checkout: Updating dir/dir1d1
+U dir/dir1d1/file1"
+ dotest cvsadm-N1d5b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d5d "cat dir/CVS/Repository" "\."
+ dotest cvsadm-N1d5d "cat dir/dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-N1d6 "$testcvs co -N -d dir 1d2mod" \
+"$SPROG checkout: Updating dir/dir1d2
+U dir/dir1d2/file2"
+ dotest cvsadm-N1d6b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d6d "cat dir/CVS/Repository" "mod2"
+ dotest cvsadm-N1d6f "cat dir/dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ dotest cvsadm-N1d7 "$testcvs co -N -d dir 2d1mod" \
+"$SPROG checkout: Updating dir/dir2d1/sub2d1
+U dir/dir2d1/sub2d1/file1"
+ dotest cvsadm-N1d7b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d7d "cat dir/CVS/Repository" "CVSROOT/Emptydir"
+ dotest cvsadm-N1d7f "cat dir/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-N1d7h "cat dir/dir2d1/sub2d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ dotest cvsadm-N1d8 "$testcvs co -N -d dir 2d2mod" \
+"$SPROG checkout: Updating dir/dir2d2/sub2d2
+U dir/dir2d2/sub2d2/file2"
+ dotest cvsadm-N1d8b "cat CVS/Repository" "\."
+ dotest cvsadm-N1d8d "cat dir/CVS/Repository" "\."
+ dotest cvsadm-N1d8d "cat dir/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-N1d8d "cat dir/dir2d2/sub2d2/CVS/Repository" \
+"mod2/sub2"
+ rm -rf CVS dir
+
+ ## the ones in two-deep directories
+
+ mkdir dir
+ dotest cvsadm-N2d3 "$testcvs co -N -d dir/dir2 1mod" \
+"$SPROG checkout: Updating dir/dir2/1mod
+U dir/dir2/1mod/file1"
+ dotest cvsadm-N2d3b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d3f "cat dir/dir2/CVS/Repository" "\."
+ dotest cvsadm-N2d3h "cat dir/dir2/1mod/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-N2d4 "$testcvs co -N -d dir/dir2 2mod" \
+"$SPROG checkout: Updating dir/dir2/2mod
+U dir/dir2/2mod/file2"
+ dotest cvsadm-N2d4b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d4f "cat dir/dir2/CVS/Repository" "mod2"
+ dotest cvsadm-N2d4h "cat dir/dir2/2mod/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-N2d5 "$testcvs co -N -d dir/dir2 1d1mod" \
+"$SPROG checkout: Updating dir/dir2/dir1d1
+U dir/dir2/dir1d1/file1"
+ dotest cvsadm-N2d5b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d5f "cat dir/dir2/CVS/Repository" "\."
+ dotest cvsadm-N2d5h "cat dir/dir2/dir1d1/CVS/Repository" "mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-N2d6 "$testcvs co -N -d dir/dir2 1d2mod" \
+"$SPROG checkout: Updating dir/dir2/dir1d2
+U dir/dir2/dir1d2/file2"
+ dotest cvsadm-N2d6b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d6f "cat dir/dir2/CVS/Repository" "mod2"
+ dotest cvsadm-N2d6h "cat dir/dir2/dir1d2/CVS/Repository" "mod2/sub2"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-N2d7 "$testcvs co -N -d dir/dir2 2d1mod" \
+"$SPROG checkout: Updating dir/dir2/dir2d1/sub2d1
+U dir/dir2/dir2d1/sub2d1/file1"
+ dotest cvsadm-N2d7b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d7f "cat dir/dir2/CVS/Repository" "CVSROOT/Emptydir"
+ dotest cvsadm-N2d7g "cat dir/dir2/dir2d1/CVS/Repository" "\."
+ dotest cvsadm-N2d7h "cat dir/dir2/dir2d1/sub2d1/CVS/Repository" \
+"mod1"
+ rm -rf CVS dir
+
+ mkdir dir
+ dotest cvsadm-N2d8 "$testcvs co -N -d dir/dir2 2d2mod" \
+"$SPROG checkout: Updating dir/dir2/dir2d2/sub2d2
+U dir/dir2/dir2d2/sub2d2/file2"
+ dotest cvsadm-N2d8b "cat CVS/Repository" "\."
+ dotest cvsadm-N2d8f "cat dir/dir2/CVS/Repository" "\."
+ dotest cvsadm-N2d8h "cat dir/dir2/dir2d2/CVS/Repository" "mod2"
+ dotest cvsadm-N2d8j "cat dir/dir2/dir2d2/sub2d2/CVS/Repository" \
+"mod2/sub2"
+ rm -rf CVS dir
+ # End of test that didn't work for remote prior to CVS 1.11.14.
+
+ ##################################################
+ ## That's enough of that, thank you very much.
+ ##################################################
+
+ dokeep
+ restore_adm
+
+ # remove our junk
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/1mod $CVSROOT_DIRNAME/1mod-2 \
+ $CVSROOT_DIRNAME/2mod $CVSROOT_DIRNAME/2mod-2 \
+ $CVSROOT_DIRNAME/mod1 $CVSROOT_DIRNAME/mod1-2 \
+ $CVSROOT_DIRNAME/mod2 $CVSROOT_DIRNAME/mod2-2
+ ;;
+
+
+
+ emptydir)
+ # Various tests of the Emptydir (CVSNULLREPOS) code. See also:
+ # cvsadm: tests of Emptydir in various module definitions
+ # basicb: Test that "Emptydir" is non-special in ordinary contexts
+
+ mkdir 1; cd 1
+ dotest emptydir-1 "${testcvs} co CVSROOT/modules" \
+"U CVSROOT/modules"
+ echo "# Module defs for emptydir tests" > CVSROOT/modules
+ echo "2d1mod -d dir2d1/sub/sub2d1 mod1" >> CVSROOT/modules
+ echo "2d1moda -d dir2d1/suba moda/modasub" >> CVSROOT/modules
+ echo "2d1modb -d dir2d1/suba mod1" >> CVSROOT/modules
+ echo "comb -a 2d1modb 2d1moda" >> CVSROOT/modules
+
+ dotest emptydir-2 "${testcvs} ci -m add-modules" \
+"${CPROG} commit: Examining CVSROOT
+${CVSROOT_DIRNAME}/CVSROOT/modules,v <-- CVSROOT/modules
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+${SPROG} commit: Rebuilding administrative file database" \
+"${CPROG} commit: Examining CVSROOT"
+ rm -rf CVS CVSROOT
+
+ modify_repo mkdir $CVSROOT_DIRNAME/mod1 $CVSROOT_DIRNAME/moda
+ # Populate. Not sure we really need to do this.
+ dotest emptydir-3 "$testcvs -q co -l ."
+ dotest emptydir-3a "${testcvs} co mod1 moda" \
+"${SPROG} checkout: Updating mod1
+${SPROG} checkout: Updating moda"
+ echo "file1" > mod1/file1
+ mkdir moda/modasub
+ dotest emptydir-3b "${testcvs} add moda/modasub" \
+"Directory ${CVSROOT_DIRNAME}/moda/modasub added to the repository"
+ echo "filea" > moda/modasub/filea
+ dotest emptydir-4 "${testcvs} add mod1/file1 moda/modasub/filea" \
+"${SPROG} add: scheduling file .mod1/file1. for addition
+${SPROG} add: scheduling file .moda/modasub/filea. for addition
+${SPROG} add: use \`${SPROG} commit' to add these files permanently"
+ dotest emptydir-5 "${testcvs} -q ci -m yup" \
+"$CVSROOT_DIRNAME/mod1/file1,v <-- mod1/file1
+initial revision: 1\.1
+${CVSROOT_DIRNAME}/moda/modasub/filea,v <-- moda/modasub/filea
+initial revision: 1\.1"
+ rm -rf mod1 moda CVS
+ # End Populate.
+
+ dotest emptydir-6 "${testcvs} co 2d1mod" \
+"${SPROG} checkout: Updating dir2d1/sub/sub2d1
+U dir2d1/sub/sub2d1/file1"
+ cd dir2d1
+ touch emptyfile
+ # It doesn't make any sense to add a file (or do much of anything
+ # else) in Emptydir; Emptydir is a placeholder indicating that
+ # the working directory doesn't correspond to anything in
+ # the repository.
+ dotest_fail emptydir-7 "${testcvs} add emptyfile" \
+"${SPROG} \[add aborted]: cannot add to \`${CVSROOT_DIRNAME}/CVSROOT/Emptydir'"
+ mkdir emptydir
+ dotest_fail emptydir-8 "${testcvs} add emptydir" \
+"${CPROG} \[add aborted]: cannot add to \`${CVSROOT_DIRNAME}/CVSROOT/Emptydir'"
+ cd ..
+ rm -rf CVS dir2d1
+
+ # OK, while we have an Emptydir around, test a few obscure
+ # things about it.
+ mkdir edir; cd edir
+ dotest emptydir-9 "${testcvs} -q co -l CVSROOT" \
+"U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ dotest_fail emptydir-10 "test -d Emptydir" ''
+ # This tests the code in find_dirs which skips Emptydir.
+ dotest emptydir-11 "${testcvs} -q -n update -d -P" ''
+ cd ../..
+ rm -r edir
+ cd ..
+
+ # Now start playing with moda.
+ mkdir 2; cd 2
+ dotest emptydir-12 "${testcvs} -q co 2d1moda" \
+"U dir2d1/suba/filea"
+ # OK, this is the crux of the matter. This used to show "Emptydir",
+ # but everyone seemed to think it should show "moda". This
+ # usually works better, but not always as shown by the following
+ # test.
+ dotest emptydir-13 "cat dir2d1/CVS/Repository" "moda"
+ dotest_fail emptydir-14 "${testcvs} co comb" \
+"${SPROG} checkout: existing repository ${CVSROOT_DIRNAME}/moda/modasub does
not match ${CVSROOT_DIRNAME}/mod1
+${SPROG} checkout: ignoring module 2d1modb
+${SPROG} checkout: Updating dir2d1/suba"
+ dotest emptydir-15 "cat dir2d1/CVS/Repository" "moda"
+ cd ..
+
+ # Test the effect of a non-cvs directory already existing with the
+ # same name as one in the modules file.
+ mkdir 3; cd 3
+ mkdir dir2d1
+ dotest emptydir-16 "${testcvs} co 2d1mod" \
+"${SPROG} checkout: Updating dir2d1/sub/sub2d1
+U dir2d1/sub/sub2d1/file1"
+
+ if $remote; then
+ dotest emptydir-17 "cat dir2d1/CVS/Repository" "CVSROOT/Emptydir"
+ else
+ dotest_fail emptydir-17 "test -d dir2d1/CVS"
+ fi
+
+ dokeep
+ cd ..
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/mod1 $CVSROOT_DIRNAME/moda
+ # I guess for the moment the convention is going to be
+ # that we don't need to remove $CVSROOT_DIRNAME/CVSROOT/Emptydir
+ ;;
+
+
+
+ abspath)
+
+ # These tests test the thituations thin thwitch thoo theck
+ # things thout twith thabsolute thaths. Threally.
+
+ #
+ # CHECKOUTS
+ #
+
+ # Create a few modules to use
+ modify_repo mkdir $CVSROOT_DIRNAME/mod1 $CVSROOT_DIRNAME/mod2
+ dotest abspath-1a "${testcvs} co mod1 mod2" \
+"${SPROG} checkout: Updating mod1
+${SPROG} checkout: Updating mod2"
+
+ # Populate the module
+ echo "file1" > mod1/file1
+ echo "file2" > mod2/file2
+ cd mod1
+ dotest abspath-1ba "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ cd ..
+ cd mod2
+ dotest abspath-1bb "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ cd ..
+
+ dotest abspath-1c "${testcvs} ci -m yup mod1 mod2" \
+"${CPROG} commit: Examining mod1
+${CPROG} commit: Examining mod2
+${CVSROOT_DIRNAME}/mod1/file1,v <-- mod1/file1
+initial revision: 1.1
+${CVSROOT_DIRNAME}/mod2/file2,v <-- mod2/file2
+initial revision: 1.1"
+ # Finished creating the module -- clean up.
+ rm -rf CVS mod1 mod2
+ # Done.
+
+ # Try checking out the module in a local directory
+ if $remote; then
+ dotest_fail abspath-2a "${testcvs} co -d ${TESTDIR}/1 mod1" \
+"${SPROG} \[checkout aborted\]: absolute pathnames invalid for server
(specified .${TESTDIR}/1.)"
+ dotest abspath-2a-try2 "${testcvs} co -d 1 mod1" \
+"${SPROG} checkout: Updating 1
+U 1/file1"
+ else
+ dotest abspath-2a "${testcvs} co -d ${TESTDIR}/1 mod1" \
+"${SPROG} checkout: Updating ${TESTDIR}/1
+U ${TESTDIR}/1/file1"
+ fi # remote workaround
+
+ dotest abspath-2b "cat ${TESTDIR}/1/CVS/Repository" "mod1"
+
+ # Done. Clean up.
+ rm -r $TESTDIR/1
+
+
+ # 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.
+
+ # Since CVS 1.11.14, CVS will create leading directories specified
+ # via co -d.
+ # I am unsure that this wasn't the behavior prior to CVS 1.9, but the
+ # comment that used to be here leads me to believe it was not.
+ if $remote; then :; else
+ dotest abspath-3.1 "$testcvs -q co -d $TESTDIR/1/2 mod1" \
+"U $TESTDIR/1/2/file1"
+ rm -r $TESTDIR/1
+ fi
+ dotest abspath-3.2 "$testcvs -q co -d 1/2 mod1" \
+"U 1/2/file1"
+ rm -r 1
+
+ # We don't to mess with an existing directory just to traverse it,
+ # for example by creating a CVS directory, but currently we can't
+ # avoid this in client/server mode.
+ mkdir 1
+ if $remote; then
+ dotest abspath-3ar "$testcvs co -d 1/2 mod1" \
+"$SPROG checkout: Updating 1/2
+U 1/2/file1"
+ dotest abspath-3br "cat 1/CVS/Repository" .
+ else
+ dotest abspath-3a "$testcvs co -d $TESTDIR/1/2 mod1" \
+"$SPROG checkout: Updating $TESTDIR/1/2
+U $TESTDIR/1/2/file1"
+ dotest_fail abspath-3b "test -d ${TESTDIR}/1/CVS"
+ fi
+
+ dotest abspath-3c "cat ${TESTDIR}/1/2/CVS/Repository" 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-4r "${testcvs} co -d ${TESTDIR}/barf/sub mod1" \
+"${SPROG} \[checkout aborted\]: cannot make directory sub: Permission denied" \
+"${SPROG} \[checkout aborted\]: absolute pathnames invalid for server
(specified .${TESTDIR}/barf/sub.)"
+ chmod +w ${TESTDIR}/barf
+ rmdir ${TESTDIR}/barf
+ # Done. Nothing to clean up.
+
+
+ # Try checking out two modules into the same directory.
+ if $remote; then
+ dotest abspath-5ar "${testcvs} co -d 1 mod1 mod2" \
+"${SPROG} checkout: Updating 1/mod1
+U 1/mod1/file1
+${SPROG} checkout: Updating 1/mod2
+U 1/mod2/file2"
+ else
+ dotest abspath-5a "${testcvs} co -d ${TESTDIR}/1 mod1 mod2" \
+"${SPROG} checkout: Updating ${TESTDIR}/1/mod1
+U ${TESTDIR}/1/mod1/file1
+${SPROG} checkout: Updating ${TESTDIR}/1/mod2
+U ${TESTDIR}/1/mod2/file2"
+ fi # end remote workaround
+ dotest abspath-5b "cat ${TESTDIR}/1/CVS/Repository" "\."
+ dotest abspath-5c "cat ${TESTDIR}/1/mod1/CVS/Repository" "mod1"
+ dotest abspath-5d "cat ${TESTDIR}/1/mod2/CVS/Repository" "mod2"
+ # Done. Clean up.
+ rm -rf $TESTDIR/1
+
+
+ # Try checking out the top-level module.
+ if $remote; then
+ dotest abspath-6ar "$testcvs co -d 1 ." \
+"$SPROG checkout: Updating 1
+$SPROG checkout: Updating 1/CVSROOT
+$DOTSTAR
+$SPROG checkout: Updating 1/mod1
+U 1/mod1/file1
+$SPROG checkout: Updating 1/mod2
+U 1/mod2/file2"
+ else
+ dotest abspath-6a "${testcvs} co -d ${TESTDIR}/1 ." \
+"${SPROG} checkout: Updating ${TESTDIR}/1
+${SPROG} checkout: Updating ${TESTDIR}/1/CVSROOT
+${DOTSTAR}
+${SPROG} checkout: Updating ${TESTDIR}/1/mod1
+U ${TESTDIR}/1/mod1/file1
+${SPROG} checkout: Updating ${TESTDIR}/1/mod2
+U ${TESTDIR}/1/mod2/file2"
+ fi # end of remote workaround
+ dotest abspath-6b "cat ${TESTDIR}/1/CVS/Repository" "\."
+ dotest abspath-6c "cat ${TESTDIR}/1/CVSROOT/CVS/Repository" "CVSROOT"
+ dotest abspath-6c "cat ${TESTDIR}/1/mod1/CVS/Repository" "mod1"
+ dotest abspath-6d "cat ${TESTDIR}/1/mod2/CVS/Repository" "mod2"
+ # Done. Clean up.
+ rm -rf ${TESTDIR}/1
+
+ # Test that an absolute pathname to some other directory
+ # doesn't mess with the current working directory.
+ mkdir 1
+ cd 1
+ if $remote; then
+ dotest_fail abspath-7ar "${testcvs} -q co -d ../2 mod2" \
+"${SPROG} checkout: protocol error: .\.\./2. contains more leading \.\.
+${SPROG} \[checkout aborted\]: than the 0 which Max-dotdot specified"
+ cd ..
+ dotest abspath-7a-try2r "${testcvs} -q co -d 2 mod2" \
+"U 2/file2"
+ cd 1
+ else
+ dotest abspath-7a "${testcvs} -q co -d ${TESTDIR}/2 mod2" \
+"U ${TESTDIR}/2/file2"
+ fi # remote workaround
+ dotest abspath-7b "ls" ""
+ dotest abspath-7c "${testcvs} -q co mod1" \
+"U mod1/file1"
+ cd mod1
+ if $remote; then
+ cd ../..
+ dotest abspath-7dr "${testcvs} -q co -d 3 mod2" \
+"U 3/file2"
+ cd 1/mod1
+ else
+ dotest abspath-7d "${testcvs} -q co -d ${TESTDIR}/3 mod2" \
+"U ${TESTDIR}/3/file2"
+ fi # remote workaround
+ dotest abspath-7e "${testcvs} -q update -d"
+
+ #
+ # FIXME: do other functions here (e.g. update /tmp/foo)
+ #
+
+ # Finished with all tests. Cleanup.
+ dokeep
+ cd ../..
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/mod1 $CVSROOT_DIRNAME/mod2
+ ;;
+
+
+
+ abspath2)
+ # More absolute path checks. The following used to attempt to create
+ # directories in /:
+ #
+ # $ cvs -d:fork:/cvsroot co /foo
+ # cvs checkout: warning: cannot make directory CVS in /: Permission
denied
+ # cvs [checkout aborted]: cannot make directory /foo: Permission
denied
+ # $
+ #
+ # The -z9 in this test also checks for an old server bug where the
+ # server would block indefinitely attempting to read an EOF from the
+ # client in the compression buffer shutdown routine.
+ dotest_fail abspath2-1 "$testcvs -z9 co /foo" \
+"$CPROG \[checkout aborted\]: Absolute module reference invalid: \`/foo'" \
+"$SPROG \[server aborted\]: Absolute module reference invalid: \`/foo'
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ ;;
+
+
+
+ toplevel)
+ # test the feature that cvs creates a CVS subdir also for
+ # the toplevel directory
+
+ # First set the TopLevelAdmin setting.
+ mkdir 1; cd 1
+ dotest toplevel-1a "${testcvs} -q co CVSROOT/config" \
+"U CVSROOT/config"
+ cd CVSROOT
+ echo "TopLevelAdmin=yes" >>config
+ dotest toplevel-1b "${testcvs} -q ci -m yes-top-level" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../..
+ rm -r 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 ${CVSROOT_DIRNAME}/top-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/second-dir added to the repository"
+ cd top-dir
+
+ touch file1
+ dotest toplevel-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest toplevel-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/top-dir/file1,v <-- file1
+initial revision: 1\.1"
+ cd ..
+
+ cd second-dir
+ touch file2
+ dotest toplevel-3s "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest toplevel-4s "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/second-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ cd ../..
+ rm -r 1; mkdir 1; cd 1
+ dotest toplevel-5 "${testcvs} co top-dir" \
+"${SPROG} checkout: Updating top-dir
+U top-dir/file1"
+
+ dotest toplevel-6 "${testcvs} update top-dir" \
+"${SPROG} update: Updating top-dir"
+ dotest toplevel-7 "${testcvs} update" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating top-dir"
+
+ dotest toplevel-8 "${testcvs} update -d top-dir" \
+"${SPROG} update: Updating top-dir"
+ # There is some sentiment that
+ # "${SPROG} update: Updating \.
+ # ${SPROG} update: 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" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating CVSROOT
+${DOTSTAR}
+${SPROG} update: Updating top-dir"
+
+ cd ..
+ rm -r 1; mkdir 1; cd 1
+ dotest toplevel-10 "${testcvs} co top-dir" \
+"${SPROG} checkout: 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"
+
+ # 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.
+ if test -n "$remotehost"; then
+ # Cygwin again.
+ $CVS_RSH $remotehost "chmod -w $TESTDIR/1"
+ else
+ chmod -w ../1
+ fi
+ # Now see whether CVS has trouble because it can't create CVS.
+ # First string is for local, second is for remote.
+ dotest toplevel-12 "${testcvs} co top-dir" \
+"${SPROG} checkout: warning: cannot make directory CVS in \.: Permission denied
+${SPROG} checkout: Updating top-dir" \
+"${CPROG} checkout: warning: cannot make directory CVS in \.: Permission denied
+${CPROG} checkout: in directory \.:
+${CPROG} checkout: cannot open CVS/Entries for reading: No such file or
directory
+${SPROG} checkout: Updating top-dir"
+
+ chmod +w ../1
+
+ dokeep
+ restore_adm
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/top-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ toplevel2)
+ # Similar to toplevel, but test the case where TopLevelAdmin=no.
+
+ # First set the TopLevelAdmin setting.
+ mkdir 1; cd 1
+ dotest toplevel2-1a "${testcvs} -q co CVSROOT/config" \
+"U CVSROOT/config"
+ cd CVSROOT
+ echo "TopLevelAdmin=no" >>config
+ dotest toplevel2-1b "$testcvs -q ci -m no-top-level" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../..
+ rm -r 1
+
+ # Now set up some directories and subdirectories
+ mkdir 1; cd 1
+ dotest toplevel2-1 "${testcvs} -q co -l ." ''
+ mkdir top-dir second-dir
+ dotest toplevel2-2 "${testcvs} add top-dir second-dir" \
+"Directory ${CVSROOT_DIRNAME}/top-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/second-dir added to the repository"
+ cd top-dir
+
+ touch file1
+ dotest toplevel2-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest toplevel2-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/top-dir/file1,v <-- file1
+initial revision: 1\.1"
+ cd ..
+
+ cd second-dir
+ touch file2
+ dotest toplevel2-3s "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest toplevel2-4s "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/second-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ cd ../..
+ rm -r 1; mkdir 1; cd 1
+ dotest toplevel2-5 "${testcvs} co top-dir" \
+"${SPROG} checkout: Updating top-dir
+U top-dir/file1"
+
+ dotest toplevel2-6 "${testcvs} update top-dir" \
+"${SPROG} update: Updating top-dir"
+ dotest toplevel2-7 "${testcvs} update" \
+"${SPROG} update: Updating top-dir"
+
+ dotest toplevel2-8 "${testcvs} update -d top-dir" \
+"${SPROG} update: Updating top-dir"
+ # Contrast this with toplevel-9, which has TopLevelAdmin=yes.
+ dotest toplevel2-9 "${testcvs} update -d" \
+"${SPROG} update: Updating top-dir"
+
+ cd ..
+ rm -r 1; mkdir 1; cd 1
+ dotest toplevel2-10 "${testcvs} co top-dir" \
+"${SPROG} checkout: Updating top-dir
+U top-dir/file1"
+ # This tests more or less the same thing, in a particularly
+ # "real life" example. With TopLevelAdmin=yes, this command
+ # would give us second-dir and CVSROOT directories too.
+ dotest toplevel2-11 "${testcvs} -q update -d" ""
+
+ dokeep
+ cd ..
+ restore_adm
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/top-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ rstar-toplevel)
+ # This test used to confirm a bug that existed in the r* commands
+ # run against the top-level project prior to CVS 1.11.18 & 1.12.10.
+ #
+ # The assertion failure was something like:
+ # do_recursion: Assertion \`strstr (repository, \"/\./\") == ((void
\*)0)' failed\..*"
+ dotest rstar-toplevel-1 "$testcvs -q rlog ." \
+"
+RCS file: $CVSROOT_DIRNAME/CVSROOT$DOTSTAR"
+
+ dokeep
+ ;;
+
+
+
+ trailingslashes)
+ # Some tests of CVS's reactions to path specifications containing
+ # trailing slashes.
+ mkdir trailingslashes; cd trailingslashes
+ dotest trailingslashes-init-1 "$testcvs -Q co -ldt ."
+ dotest trailingslashes-init-2 "$testcvs -Q co -dt2 ."
+ cd t
+ echo "Ahh'll be baaack." >topfile
+ dotest trailingslashes-init-3 "$testcvs -Q add topfile"
+ dotest trailingslashes-init-4 "$testcvs -Q ci -mto-top"
+
+ # First, demonstrate the usual case.
+ cd ../t2
+ dotest trailingslashes-1 "$testcvs -q up CVSROOT"
+ dotest_fail trailingslashes-1a "test -f topfile"
+
+ # FIXCVS:
+ # Now the one that fails in remote mode.
+ # This highlights one of the failure cases mentioned in TODO item
+ # #205.
+ if $remote; then
+ dotest trailingslashes-2 "$testcvs -q up CVSROOT/" \
+"U topfile"
+ dotest trailingslashes-2a "test -f topfile"
+ else
+ dotest trailingslashes-2 "$testcvs -q up CVSROOT/"
+ dotest_fail trailingslashes-2a "test -f topfile"
+ fi
+
+ dokeep
+ cd ../..
+ rm -rf trailingslashes
+ modify_repo rm -rf $CVSROOT_DIRNAME/topfile,v
+ ;;
+
+
+
+ checkout_repository)
+ dotest_fail checkout_repository-1 \
+"${testcvs} co -d ${CVSROOT_DIRNAME} CVSROOT" \
+"${CPROG} \[checkout aborted\]: Cannot check out files into the repository
itself" \
+"${SPROG} \[checkout aborted\]: absolute pathnames invalid for server
(specified \`${CVSROOT_DIRNAME}')"
+
+ # The behavior of the client/server test below should be correct.
+ # The CVS client currently has no way of knowing that the client and
+ # server are the same machine and thus skips the $CVSROOT checks.
+ # I think checking for this case in CVS would be bloat since this
+ # should be a fairly rare occurance.
+ cd ${CVSROOT_DIRNAME}
+ dotest_fail checkout_repository-2 "${testcvs} co CVSROOT" \
+"${CPROG} \[checkout aborted\]: Cannot check out files into the repository
itself" \
+"${SPROG} checkout: Updating CVSROOT
+${CPROG} checkout: move away \`CVSROOT/checkoutlist'; it is in the way
+C CVSROOT/checkoutlist
+${CPROG} checkout: move away \`CVSROOT/commitinfo'; it is in the way
+C CVSROOT/commitinfo
+${CPROG} checkout: move away \`CVSROOT/config'; it is in the way
+C CVSROOT/config
+${CPROG} checkout: move away \`CVSROOT/cvswrappers'; it is in the way
+C CVSROOT/cvswrappers
+${CPROG} checkout: move away \`CVSROOT/loginfo'; it is in the way
+C CVSROOT/loginfo
+${CPROG} checkout: move away \`CVSROOT/modules'; it is in the way
+C CVSROOT/modules
+${CPROG} checkout: move away \`CVSROOT/notify'; it is in the way
+C CVSROOT/notify
+${CPROG} checkout: move away \`CVSROOT/postadmin'; it is in the way
+C CVSROOT/postadmin
+${CPROG} checkout: move away \`CVSROOT/postproxy'; it is in the way
+C CVSROOT/postproxy
+${CPROG} checkout: move away \`CVSROOT/posttag'; it is in the way
+C CVSROOT/posttag
+${CPROG} checkout: move away \`CVSROOT/postwatch'; it is in the way
+C CVSROOT/postwatch
+${CPROG} checkout: move away \`CVSROOT/preproxy'; it is in the way
+C CVSROOT/preproxy
+${CPROG} checkout: move away \`CVSROOT/rcsinfo'; it is in the way
+C CVSROOT/rcsinfo
+${CPROG} checkout: move away \`CVSROOT/taginfo'; it is in the way
+C CVSROOT/taginfo
+${CPROG} checkout: move away \`CVSROOT/verifymsg'; it is in the way
+C CVSROOT/verifymsg"
+
+ dotest checkout_repository-3 \
+"${testcvs} co -p CVSROOT/modules >/dev/null" \
+"===================================================================
+Checking out CVSROOT/modules
+RCS: ${CVSROOT_DIRNAME}/CVSROOT/modules,v
+VERS: 1\.[0-9]*
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*"
+
+ dokeep
+ cd $TESTDIR
+ ;;
+
+
+
+ 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
+
+ dokeep
+ # Clean up
+ cd ..
+ rm -r a-dir
+ modify_repo 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.
+
+ # Our "editor" puts "x" at the start of each line, so we
+ # can see the "CVS:" lines.
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+sed <\$1 -e 's/^/x/' >${TESTDIR}/edit.new
+mv ${TESTDIR}/edit.new \$1
+exit 0
+EOF
+ chmod +x ${TESTDIR}/editme
+
+ mkdir 1; cd 1
+ dotest editor-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest editor-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1 file2
+ dotest editor-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest editor-4 "${testcvs} -e ${TESTDIR}/editme -q ci" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ # 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" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ dotest editor-log-file1 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+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: ----------------------------------------------------------------------
+============================================================================="
+
+ # 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: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+xCVS: ----------------------------------------------------------------------
+xCVS: Enter Log. Lines beginning with .CVS:. are removed automatically
+xCVS:
+xCVS: Modified Files:
+xCVS: Tag: br
+xCVS: file2
+xCVS: ----------------------------------------------------------------------
+============================================================================="
"
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+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: ----------------------------------------------------------------------
+============================================================================="
+
+ # Test CVS's response to an unchanged log message
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+exit 0
+EOF
+ chmod +x ${TESTDIR}/editme
+ dotest_fail editor-emptylog-1 "echo a |${testcvs} -e
${TESTDIR}/editme ci -f file1" \
+"
+Log message unchanged or not specified
+a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs
+Action: (continue) ${CPROG} \[commit aborted\]: aborted by user"
+
+ # Test CVS's response to an empty log message
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+cat /dev/null >\$1
+exit 0
+EOF
+ chmod +x ${TESTDIR}/editme
+ dotest_fail editor-emptylog-1a "echo a |${testcvs} -e
${TESTDIR}/editme ci -f file1" \
+"
+Log message unchanged or not specified
+a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs
+Action: (continue) ${CPROG} \[commit aborted\]: aborted by user"
+
+ # Test CVS's response to a log message with one blank line
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+echo >\$1
+exit 0
+EOF
+ chmod +x ${TESTDIR}/editme
+ dotest_fail editor-emptylog-1b "echo a |${testcvs} -e
${TESTDIR}/editme ci -f file1" \
+"
+Log message unchanged or not specified
+a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs
+Action: (continue) ${CPROG} \[commit aborted\]: aborted by user"
+
+ # Test CVS's response to a log message with only comments
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+cat \$1 >${TESTDIR}/edit.new
+mv ${TESTDIR}/edit.new \$1
+exit 0
+EOF
+ chmod +x ${TESTDIR}/editme
+ dotest_fail editor-emptylog-1c "echo a |${testcvs} -e
${TESTDIR}/editme ci -f file1" \
+"
+Log message unchanged or not specified
+a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs
+Action: (continue) ${CPROG} \[commit aborted\]: aborted by user"
+
+ # Test CVS's response to a log message that is zero bytes
+ # in length. This caused core dumps in cvs 1.11.5 on Solaris
+ # hosts.
+ cd ..
+ dotest editor-emptylog-continue-1 "${testcvs} -q co CVSROOT/loginfo" \
+"U CVSROOT/loginfo"
+
+ cd CVSROOT
+ cat <<\EOF >>loginfo
+DEFAULT (echo Start-Log;cat;echo End-Log) >> $CVSROOT/CVSROOT/commitlog
+EOF
+ dotest editor-emptylog-continue-2 "$testcvs -Q ci -mloggem"
+
+ cd ../first-dir
+ cat >${TESTDIR}/editme <<EOF
+#!${TESTSHELL}
+sleep 1
+cp /dev/null \$1
+exit 1
+EOF
+ chmod +x ${TESTDIR}/editme
+ dotest editor-emptylog-continue-3 "echo c |${testcvs} -e
${TESTDIR}/editme ci -f file1" \
+"${CPROG} commit: warning: editor session failed
+
+Log message unchanged or not specified
+a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs
+Action: (continue) ${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ # The loginfo Log message should be an empty line and not "(null)"
+ # which is what some fprintf() implementations do with "%s"
+ # format and a NULL pointer...
+ if $remote; then
+ dotest editor-emptylog-continue-4r \
+"cat $CVSROOT_DIRNAME/CVSROOT/commitlog" \
+"Start-Log
+Update of $CVSROOT_DIRNAME/CVSROOT
+In directory $hostname:$TMPDIR/cvs-serv[0-9a-z]*
+
+Modified Files:
+ loginfo
+Log Message:
+loggem
+End-Log
+Start-Log
+Update of $CVSROOT_DIRNAME/first-dir
+In directory $hostname:$TMPDIR/cvs-serv[0-9a-z]*
+
+Modified Files:
+ file1
+Log Message:
+
+End-Log"
+ else
+ dotest editor-emptylog-continue-4 \
+"cat $CVSROOT_DIRNAME/CVSROOT/commitlog" \
+"Start-Log
+Update of $CVSROOT_DIRNAME/CVSROOT
+In directory $hostname:$TESTDIR/1/CVSROOT
+
+Modified Files:
+ loginfo
+Log Message:
+loggem
+End-Log
+Start-Log
+Update of $CVSROOT_DIRNAME/first-dir
+In directory $hostname:$TESTDIR/1/first-dir
+
+Modified Files:
+ file1
+Log Message:
+
+End-Log"
+ fi
+ # There should have an empty log message at this point
+ dotest editor-emptylog-continue-5 "${testcvs} log -N -r1.2 file1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.2
+branch:
+locks: strict
+access list:
+keyword substitution: kv
+total revisions: 3; selected revisions: 1
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: +0 -0;
commitid: ${commitid};
+\*\*\* empty log message \*\*\*
+============================================================================="
+
+ # clean up
+ dokeep
+ # restore the default loginfo script
+ restore_adm
+ cd ../..
+ rm -r 1
+ rm $TESTDIR/editme
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ env)
+ # Test to see if the CVS_PID environment variable is being set
+ mkdir ${TESTDIR}/env
+ cd ${TESTDIR}/env
+ dotest env-1 "${testcvs} -Q co . >>${LOGFILE}" ''
+
+ cat > ${TESTDIR}/env/test-cvs-pid <<EOF
+#!${TESTSHELL}
+if test "x\$CVS_PID" != "x"; then
+ # In local mode, there is no directory with the pid in it for use.
+ # In remote mode the CVS_PID will be the parent process of the
+ # cvs process that runs the commitinfo script.
+ if test "x$remote" = "x:" ; then
+ ppid=\`pwd | sed -e 's,.*/cvs-serv,,'\`
+ else
+ # This assumes that the -l switch puts PPID in the banner and does
+ # not run the elements together such that whitespace surrounds the
+ # pid and ppid in the output. This could be made slightly simpler
+ # if all hosts had a 'ps' command that supported the -p switch,
+ # but Solaris 7 /usr/ucb/ps does not and that may be the one we use.
+ # It is because this is so messy that the CVS_PID feature exists.
+ pid=\$\$
+ pidcmd="ps -o pid,ppid -p \$pid || ps -el || ps -al"
+ if echo \$pidcmd | sh >pid.stdout 2> pid.stderr; then
+ ppid=\`cat pid.stdout |\\
+ awk '/PPID/ { for (i=1; i <= NF; i++) {
+ if (\$i == "PPID") ppidx = i;
+ if (\$i == "PID") pidx = i;
+ }
+ next;
+ }
+ { print \$pidx " " \$ppidx }' |\\
+ grep "^\$pid " |\\
+ awk '{ print \$NF }'\`
+ else
+ ppid=unkown
+ fi
+ fi
+ if test "x\$ppid" = "x\${CVS_PID}"; then
+ # The PID looks okay to me
+ # Clean up any temporary files
+ rm -f pid.stdout pid.stderr
+ exit 0
+ else
+ echo The environment variable CVS_PID is not properly set.
+ echo It should have been set to \'\$ppid\' but instead was \'\$CVS_PID\'
+ echo It is possible that this test is broken for your host.
+ echo Current pid: \$pid
+ [ -n "\$pidcmd" ] && echo "Command: \$pidcmd"
+ [ -s pid.stdout ] && echo Standard Out: && cat pid.stdout
+ [ -s pid.stderr ] && echo Standard Error: && cat pid.stderr
+ exit 1
+ fi
+else
+ echo The environment variable CVS_PID is not set.
+ exit 1
+fi
+EOF
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x ${TESTDIR}/env/test-cvs-pid"
+ else
+ chmod +x ${TESTDIR}/env/test-cvs-pid
+ fi
+ cd CVSROOT
+ echo "^env ${TESTDIR}/env/test-cvs-pid %r/%p %s" >>commitinfo
+ dotest env-2 "${testcvs} -q ci -m test-pid commitinfo" \
+"${CVSROOT_DIRNAME}/CVSROOT/commitinfo,v <-- commitinfo
+new revision: 1\.2; previous revision: 1\.1
+${SPROG} commit: Rebuilding administrative file database"
+ cd ..
+ mkdir env
+ dotest env-3 "${testcvs} -q add env" \
+"Directory ${CVSROOT_DIRNAME}/env added to the repository"
+ cd env
+ echo testing >file1
+ dotest env-4 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest env-5 "${testcvs} -q commit -m test-pid" \
+"${CVSROOT_DIRNAME}/env/file1,v <-- file1
+initial revision: 1\.1"
+
+ dokeep
+ # undo commitinfo changes
+ restore_adm
+ cd ../..
+ rm -fr $TESTDIR/env
+ modify_repo rm -rf $CVSROOT_DIRNAME/env
+ ;;
+
+
+
+ errmsg1)
+ modify_repo mkdir $CVSROOT_DIRNAME/1dir
+ mkdir 1
+ cd 1
+ dotest errmsg1-init-1 "$testcvs -Q co 1dir"
+ cd 1dir
+ touch foo
+ dotest errmsg-init-2 "$testcvs -Q add foo"
+ 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
+
+ cd ../../2/1dir
+ # The second case in the local and remote versions of errmsg1-168
+ # below happens on Cygwin under Windows, where write privileges
+ # aren't enforced properly.
+ if $remote; then
+ dotest errmsg1-168r "${testcvs} -q update" \
+"${SPROG} update: \`foo' is no longer in the repository
+$CPROG update: unable to remove \./foo: Permission denied" \
+"${SPROG} update: \`foo' is no longer in the repository"
+ else
+ dotest errmsg1-168 "${testcvs} -q update" \
+"${SPROG} update: \`foo' is no longer in the repository
+${SPROG} update: unable to remove foo: Permission denied" \
+"${SPROG} update: \`foo' is no longer in the repository"
+ fi
+
+ dokeep
+ cd ..
+ chmod u+w 1dir
+ cd ..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/1dir
+ ;;
+
+
+
+ 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.
+
+ # 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ dotest_fail errmsg2-3 "${testcvs} add CVS" \
+"${CPROG} add: 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" \
+"${CPROG} add: cannot add special file .CVS.; skipping
+${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} 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 ." \
+"${CPROG} add: cannot add special file .\..; skipping"
+ dotest_fail errmsg2-6 "${testcvs} add .." \
+"${CPROG} add: 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ mkdir sdir
+ cd ..
+ dotest errmsg2-8 "${testcvs} add first-dir/sdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/sdir added to the repository"
+ # while we're here... check commit with no CVS directory
+ dotest_fail errmsg2-8a "${testcvs} -q ci first-dir nonexistant" \
+"${CPROG} commit: nothing known about .nonexistant'
+${CPROG} \[commit aborted\]: correct above errors first!"
+ dotest_fail errmsg2-8b "$testcvs -q ci nonexistant first-dir" \
+"$CPROG commit: nothing known about .nonexistant'
+$CPROG \[commit aborted\]: correct above errors first!"
+ dotest errmsg2-8c "$testcvs -q ci first-dir"
+
+ cd first-dir
+
+ touch file10
+ mkdir sdir10
+ dotest errmsg2-10 "${testcvs} add file10 sdir10" \
+"${SPROG} add: scheduling file .file10. for addition
+Directory ${CVSROOT_DIRNAME}/first-dir/sdir10 added to the repository
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest errmsg2-11 "${testcvs} -q ci -m add-file10" \
+"$CVSROOT_DIRNAME/first-dir/file10,v <-- file10
+initial revision: 1\.1"
+ # Try to see that there are no droppings left by
+ # any of the previous tests.
+ dotest errmsg2-12 "${testcvs} -q update" ""
+
+ # 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 ${CVSROOT_DIRNAME}/first-dir/sdir10/ssdir added to the repository"
+
+ touch first-dir/sdir10/ssdir/ssfile
+ dotest errmsg2-14 \
+ "${testcvs} add first-dir/sdir10/ssdir/ssfile" \
+"${SPROG} add: scheduling file .first-dir/sdir10/ssdir/ssfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ touch first-dir/file15
+ dotest errmsg2-15 "${testcvs} add first-dir/file15" \
+"${SPROG} add: scheduling file .first-dir/file15. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ # Now the case where we try to give it a directory which is not
+ # under CVS control.
+ mkdir bogus-dir
+ touch bogus-dir/file16
+ # FIXCVS: 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" \
+"${SPROG} add: in directory \`bogus-dir':
+${SPROG} \[add aborted\]: there is no version here; do .${SPROG} checkout.
first" \
+"${CPROG} add: cannot open CVS/Entries for reading: No such file or directory
+${CPROG} \[add aborted\]: no repository"
+ rm -r bogus-dir
+
+ # One error condition we don't test for is trying to add a file
+ # or directory which already is there.
+
+ dotest errmsg2-17 "${testcvs} -q ci -m checkin" \
+"$CVSROOT_DIRNAME/first-dir/file15,v <-- first-dir/file15
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/sdir10/ssdir/ssfile,v <--
first-dir/sdir10/ssdir/ssfile
+initial revision: 1\.1"
+ dotest errmsg2-18 "${testcvs} -Q tag test" ''
+
+ # trying to import the repository
+
+ if $remote; then :; else
+ cd ${CVSROOT_DIRNAME}
+ dotest_fail errmsg2-20 "${testcvs} import -mtest . A B" \
+"${SPROG} \[import aborted\]: attempt to import the repository"
+ dotest_fail errmsg2-21 "${testcvs} import -mtest first-dir A B" \
+"${SPROG} \[import aborted\]: attempt to import the repository"
+ fi
+
+ dokeep
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ errmsg3)
+ # Test the *PANIC* message caused by missing administration files
+ mkdir errmsg3
+ cd errmsg3
+ mkdir CVS
+ dotest_fail errmsg3-1 "${testcvs} -q up" \
+"${CPROG} update: in directory \`.':
+${CPROG} update: CVS directory found without administrative files\.
+${CPROG} update: Use CVS to create the CVS directory, or rename the
+${CPROG} update: directory if it is intended to store something
+${CPROG} update: besides CVS administrative files\.
+${CPROG} \[update aborted\]: \*PANIC\* administration files missing!"
+
+ dokeep
+ cd ..
+ rm -r errmsg3
+ ;;
+
+
+
+ close-stdout)
+ # Ensure that cvs update -p FILE > /dev/full fails
+ # Perform this test IFF /dev/full is a writable character device.
+ if test -w /dev/full && test -c /dev/full; then
+ mkdir close-stdout
+ cd close-stdout
+ echo a > file
+ dotest close-stdout-1 "$testcvs -Q import -m. closeout X Y" ''
+ dotest close-stdout-2 "$testcvs -Q co closeout" ''
+ # Match either a bare `write error' or
+ # `write error: No space left on device',
+ # since closeout.c can produce both.
+ dotest_fail close-stdout-3 \
+ "${testcvs} -Q update -p closeout/file > /dev/full" \
+ "${CPROG} \[update aborted\]: write error.*"
+
+ dokeep
+ cd ..
+ rm -r close-stdout
+ modify_repo rm -rf $CVSROOT_DIRNAME/closeout
+ else
+ skip close-stdout '/dev/full is not available'
+ fi
+ ;;
+
+
+
+ debug-log-nonfatal)
+ # Once upon a time, failure to create the debug log could be fatal.
+ if $remote; then :; else
+ remoteonly debug-log-nonfatal
+ continue
+ fi
+
+ mkdir $TESTDIR/unwritable
+ chmod a-w $TESTDIR/unwritable
+ if test -n "$CVS_CLIENT_LOG"; then
+ save_CVS_CLIENT_LOG=$CVS_CLIENT_LOG
+ fi
+ CVS_CLIENT_LOG=$TESTDIR/unwritable/cvsclientlog
+ export CVS_CLIENT_LOG
+
+ dotest debug-log-nonfatal-1 \
+"$testcvs -Q co -p CVSROOT/config >/dev/null" \
+"$CPROG checkout: opening to-server logfile
$TESTDIR/unwritable/cvsclientlog.in: Permission denied
+$CPROG checkout: opening from-server logfile
$TESTDIR/unwritable/cvsclientlog.out: Permission denied"
+
+ dokeep
+ rm -rf $TESTDIR/unwritable
+ unset CVS_CLIENT_LOG
+ if test -n "$save_CVS_CLIENT_LOG"; then
+ CVS_CLIENT_LOG=$save_CVS_CLIENT_LOG
+ fi
+ ;;
+
+
+
+ adderrmsg)
+ # Test some of the error messages the 'add' command can return and
+ # their reactions to '-q'.
+
+ # First the usual setup; create a directory first-dir.
+ mkdir 1; cd 1
+ dotest adderrmsg-init1 "${testcvs} -q co -l ." ''
+ mkdir adderrmsg-dir
+ dotest adderrmsg-init2 "${testcvs} add adderrmsg-dir" \
+"Directory ${CVSROOT_DIRNAME}/adderrmsg-dir added to the repository"
+ cd adderrmsg-dir
+
+ # try to add the admin dir
+ dotest_fail adderrmsg-1 "${testcvs} add CVS" \
+"${CPROG} add: cannot add special file .CVS.; skipping"
+ # might not want to see this message when you 'cvs add *'
+ dotest_fail adderrmsg-2 "${testcvs} -q add CVS" ""
+
+ # to test some other messages
+ touch file1
+ dotest adderrmsg-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ # add it twice
+ dotest_fail adderrmsg-4 "${testcvs} add file1" \
+"${SPROG} add: \`file1' has already been entered"
+ dotest_fail adderrmsg-5 "${testcvs} -q add file1" ""
+
+ dotest adderrmsg-6 "${testcvs} -q ci -madd" \
+"$CVSROOT_DIRNAME/adderrmsg-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ # file in Entries & repository
+ dotest_fail adderrmsg-7 "${testcvs} add file1" \
+"${SPROG} add: \`file1' already exists, with version number 1\.1"
+ dotest_fail adderrmsg-8 "${testcvs} -q add file1" ""
+
+ # clean up
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/adderrmsg-dir
+ ;;
+
+
+
+ opterrmsg)
+ # Test some option parsing error messages
+
+ # No init is necessary since these error messages are printed b4
+ # CVS looks for a sandbox or repository
+
+ # -z used to accept non-numeric arguments. This bit someone who
+ # attempted `cvs -z -n up' when the -n was read as the argument to
+ # -z.
+ dotest_fail opterrmsg-1 "${testcvs} -z -n up" \
+"${CPROG}: gzip compression level must be between 0 and 9"
+
+ # Some general -z checks
+ dotest_fail opterrmsg-2 "${testcvs} -z -1 up" \
+"${CPROG}: gzip compression level must be between 0 and 9"
+ dotest_fail opterrmsg-3 "${testcvs} -z10 up" \
+"${CPROG}: gzip compression level must be between 0 and 9"
+ ;;
+
+
+
+ devcom)
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+ dotest devcom-1 "$testcvs -q co first-dir"
+
+ cd first-dir
+ echo abb >abb
+ dotest devcom-2 "$testcvs add abb" \
+"$SPROG add: scheduling file \`abb' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+
+ dotest devcom-3 "$testcvs -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/abb,v <-- abb
+initial revision: 1\.1"
+
+ dotest_fail devcom-4 "$testcvs watch" "Usage$DOTSTAR"
+
+ dotest devcom-5 "$testcvs watch on"
+
+ echo abc >abc
+ dotest devcom-6 "$testcvs add abc" \
+"$SPROG add: scheduling file \`abc' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+
+ dotest devcom-7 "$testcvs -q ci -m added" \
+"$CVSROOT_DIRNAME/first-dir/abc,v <-- abc
+initial revision: 1\.1"
+
+ cd ../..
+ mkdir 2
+ cd 2
+
+ dotest devcom-8 "$testcvs -q co first-dir" \
+"U first-dir/abb
+U first-dir/abc"
+
+ cd first-dir
+ dotest_fail devcom-9 "test -w abb"
+ dotest_fail devcom-9b "test -w abc"
+
+ dotest devcom-10 "$testcvs editors"
+ dotest devcom-11 "$testcvs edit abb"
+
+ # Here we test for the traditional ISO C ctime() date format.
+ # We assume the C locale; I guess that works provided we set
+ # LC_ALL at the start of this script but whether these
+ # strings should vary based on locale does not strike me as
+ # self-evident.
+ dotest devcom-12 "$testcvs editors" \
+"abb ${username} [SMTWF][uoehra][neduit] [JFAMSOND][aepuco][nbrylgptvc]
[0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000 [-a-zA-Z_.0-9]*
${TESTDIR}/2/first-dir"
+
+ echo aaaa >>abb
+ dotest devcom-13 "$testcvs ci -m modify abb" \
+"${CVSROOT_DIRNAME}/first-dir/abb,v <-- abb
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Unedit of a file not being edited should be a noop.
+ dotest devcom-14 "$testcvs unedit abb" ''
+
+ dotest devcom-15 "$testcvs editors" ""
+
+ dotest_fail devcom-16 "test -w abb"
+
+ dotest devcom-17 "$testcvs edit abc"
+
+ # Unedit of an unmodified file.
+ dotest devcom-18 "$testcvs unedit abc"
+ dotest devcom-19 "$testcvs edit abc"
+
+ echo changedabc >abc
+ # Try to unedit a modified file; cvs should ask for confirmation
+ dotest devcom-20 "echo no | $testcvs unedit abc" \
+"abc has been modified; revert changes? "
+
+ dotest devcom-21 "echo changedabc |$diff_u - abc"
+
+ # OK, now confirm the unedit
+ dotest devcom-22 "echo yes |$testcvs unedit abc" \
+"abc has been modified; revert changes? "
+
+ dotest devcom-23 "echo abc |$diff_u - abc"
+
+ dotest devcom-24 "$testcvs watchers" ''
+
+ # FIXME: This probably should be an error message instead
+ # of silently succeeding and printing nothing.
+ dotest devcom-a-nonexist "$testcvs watchers nonexist" ''
+
+ dotest devcom-a1 "$testcvs watch add" ''
+ dotest devcom-a2 "$testcvs watchers" \
+"abb $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
+
+ # Test commented out because the bug it tests for is not fixed
+ # The error is:
+ # cvs watchers: cannot open CVS/Entries for reading: No such file or
directory
+ # cvs: ../../work/ccvs/src/fileattr.c:75: fileattr_read: Assertion
`fileattr_stored_repos != ((void *)0)' failed.
+: dotest devcom-t-nonexist "${testcvs} watchers nonexist" fixme
+
+ dotest devcom-t1 "${testcvs} -q co -rtag first-dir/abb" \
+'U first-dir/abb'
+ cd ..
+ # Since first-dir/abb is readonly, use -f.
+ rm -rf 3
+
+ # 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 ../..
+ 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" ''
+
+ dokeep
+ cd ..
+ # Use -f because of the readonly files.
+ rm -rf 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ devcom2)
+ # More watch tests, most notably setting watches on
+ # files in various different states.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+ dotest devcom2-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+
+ # This should probably be an error; setting a watch on a totally
+ # unknown file is more likely to be a typo than intentional.
+ # But that isn't the currently implemented behavior.
+ dotest devcom2-2 "${testcvs} watch on w1" ''
+
+ touch w1 w2 w3 nw1
+ dotest devcom2-3 "${testcvs} add w1 w2 w3 nw1" "${DOTSTAR}"
+ # Letting the user set the watch here probably can be considered
+ # a feature--although it leads to a few potentially strange
+ # consequences like one user can set the watch and another actually
+ # adds the file.
+ dotest devcom2-4 "${testcvs} watch on w2" ''
+ dotest devcom2-5 "${testcvs} -Q ci -m add-them"
+
+ # 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_fail devcom2-12 "$testcvs edit w4" \
+"${CPROG} edit: no such file w4; ignored"
+ # Try the same thing with a per-directory watch set.
+ dotest devcom2-13 "${testcvs} watch on" ''
+ dotest_fail devcom2-14 "$testcvs edit w5" \
+"${CPROG} 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 ..
+
+ # Do a little error testing
+ dotest devcom2-18 "${testcvs} -q co -d first+dir first-dir" \
+"U first${PLUS}dir/nw1
+U first${PLUS}dir/w1
+U first${PLUS}dir/w2
+U first${PLUS}dir/w3"
+ cd first+dir
+ dotest_fail devcom2-19 "${testcvs} edit" \
+"${CPROG} \[edit aborted\]: current directory (${TESTDIR}/2/first${PLUS}dir)
contains an invalid character (${PLUS},>;=\\\\t\\\\n)"
+
+ # Make sure there are no droppings lying around
+ dotest devcom2-20 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \
+"Fw1 _watched=
+Fw2 _watched=
+Fw3 _watched=
+Fnw1 _watched=
+D _watched="
+
+ dokeep
+ cd ../..
+ # Use -f because of the readonly files.
+ rm -rf 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ devcom3)
+ # More watch tests, most notably handling of features designed
+ # for future expansion.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1
+ cd 1
+
+ # Set up logging via the postwatch script hook. See the `info' test
+ # for a list of tests where other script hooks are tested.
+ dotest devcom3-init-1 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ echo "ALL $TESTDIR/1/loggit %r %p %c" >>postwatch
+ dotest devcom3-init-2 "$testcvs -Q ci -mlog-watch"
+ cd .. # 1
+
+ cat >loggit <<EOF
+#!$TESTSHELL
+echo \${1+"\$@"} >>$TESTDIR/1/watch-log
+EOF
+ # #^@&!^@ Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x $TESTDIR/1/loggit"
+ else
+ chmod +x loggit
+ fi
+
+
+
+ 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"
+
+ # OK, since we are about to delve into CVS's internals, make
+ # sure that we seem to be correct about how they work.
+ dotest devcom3-5 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \
+"Fw1 _watched=
+Fw2 _watched="
+ # Now write a few more lines, just as if we were a newer version
+ # of CVS implementing some new feature.
+ cat <<'EOF' >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr
+Enew line here
address@hidden@#=&
+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=
address@hidden@#=&
+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
address@hidden@#=&"
+
+ # Now test disconnected "cvs edit" and the format of the
+ # CVS/Notify file.
+ if $remote; then
+ CVS_SERVER_save=$CVS_SERVER
+ CVS_SERVER=$TESTDIR/cvs-none; export CVS_SERVER
+
+ # The ${DOTSTAR} below matches the exact CVS server error message,
+ # which in :fork: mode is:
+ # "$SPROG \[edit aborted\]: cannot exec $TESTDIR/cvs-none:
${DOTSTAR}",
+ # but which is:
+ # "bash2: line 1: $TESTDIR/cvs-none: No such file or directory"
+ # when testing across an :ext:/ssh link to my Linux 2.4 box.
+ #
+ # I can't even test for the second part of the error message,
+ # from the client, which varies more consistently, usually either
+ # "end of file from server" (if the process doing the exec exits
+ # before the parent gets around to sending data to it) or
+ # "received broken pipe signal" (if it is the other way around),
+ # since HP-UX fails to output it.
+ dotest_fail devcom3-9ar "$testcvs edit w1 2>/dev/null"
+ dotest devcom3-9br "test -w w1"
+ dotest devcom3-9cr "cat CVS/Notify" \
+"Ew1 [SMTWF][uoehra][neduit] [JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9]
[0-9:]* [0-9][0-9][0-9][0-9] -0000 [-a-zA-Z_.0-9]* ${TESTDIR}/1/first-dir
EUC"
+ CVS_SERVER=${CVS_SERVER_save}; export CVS_SERVER
+ if $proxy; then
+ dotest_fail devcom3-9dp "$testcvs -q update" \
+"This CVS server does not support disconnected \`cvs edit'\. For now, remove
all \`CVS/Notify' files in your workspace and try your command again\."
+ dotest devcom3-9ep "test -f CVS/Notify"
+ rm CVS/Notify
+ dotest devcom3-9hp "$testcvs watchers w1"
+ else
+ dotest devcom3-9dr "$testcvs -q update"
+ dotest_fail devcom3-9er "test -f CVS/Notify"
+ dotest devcom3-9fr "$testcvs watchers w1" \
+"w1 $username tedit tunedit tcommit"
+ fi
+ dotest devcom3-9gr "$testcvs unedit w1"
+ dotest devcom3-9hr "$testcvs watchers w1"
+ fi
+
+ cd ../..
+ # OK, now change the tab to a space, and see that CVS gives
+ # a reasonable error (this is database corruption but CVS should
+ # not lose its mind).
+ sed -e 's/Fw2 /Fw2 /' <$CVSROOT_DIRNAME/first-dir/CVS/fileattr \
+ >$CVSROOT_DIRNAME/first-dir/CVS/fileattr.new
+ modify_repo mv $CVSROOT_DIRNAME/first-dir/CVS/fileattr.new \
+ $CVSROOT_DIRNAME/first-dir/CVS/fileattr
+ mkdir 2; cd 2
+ dotest_fail devcom3-10 "${testcvs} -Q co ." \
+"${SPROG} \[checkout aborted\]: file attribute database corruption: tab
missing in ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr"
+
+ notifyworks=false
+ if $remote; then
+ if $proxy; then :; else
+ notifyworks=:
+ fi
+ fi
+ if $notifyworks; then
+ dotest devcom3-postwatch-examine-1r "cat $TESTDIR/1/watch-log" \
+"$CVSROOT_DIRNAME first-dir watch
+$CVSROOT_DIRNAME first-dir watch
+$CVSROOT_DIRNAME first-dir watch
+$CVSROOT_DIRNAME first-dir update
+$CVSROOT_DIRNAME first-dir server"
+ else
+ dotest devcom3-postwatch-examine-1 "cat $TESTDIR/1/watch-log" \
+"$CVSROOT_DIRNAME first-dir watch
+$CVSROOT_DIRNAME first-dir watch
+$CVSROOT_DIRNAME first-dir watch"
+ fi
+
+ dokeep
+ restore_adm
+ cd ..
+ # Use -f because of the readonly files.
+ rm -rf 1 2
+ modify_repo 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 ${CVSROOT_DIRNAME}/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" \
+"$SPROG add: scheduling file .file1. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest watch4-3 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ # Now test the analogous behavior for directories.
+ mkdir subdir
+ dotest watch4-4 "${testcvs} add subdir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/subdir added to the repository"
+ cd subdir
+ touch sfile
+ dotest watch4-5 "${testcvs} add sfile" \
+"${SPROG} add: scheduling file .sfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest watch4-6 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/subdir/sfile,v <-- sfile
+initial revision: 1\.1"
+ 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 ../..
+
+ cd 1/first-dir
+
+ # NOTE: I'm leaving in '' as acceptable
+ # to maintain partial compatibility with CVS versions
+ # prior to the edit check patch.
+ editorsLineRE="file1 $username [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR/2/first-dir"
+ dotest watch4-11 "$testcvs edit file1" "$editorsLineRE"
+
+ echo 'edited in 1' >file1
+ dotest watch4-12 "${testcvs} -q ci -m edit-in-1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../..
+ cd 2/first-dir
+ dotest watch4-13 "${testcvs} -q update" \
+"RCS file: ${CVSROOT_DIRNAME}/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
+${SPROG} update: 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 ../..
+
+ # 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"
+
+ dokeep
+ # Specify -f because of the readonly files.
+ rm -rf 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ watch5)
+ # This test was designed to catch a problem in server
+ # mode where an 'cvs edit'd file disappeared from the
+ # CVS/Base directory when 'cvs status' or 'cvs update'
+ # was called on the file after the file was touched.
+ #
+ # This test is still here to prevent the bug from
+ # being reintroduced.
+ #
+ # The rationale for having CVS/Base stay around is that
+ # CVS/Base should be there if "cvs edit" has been run (this
+ # may be helpful as a "cvs editors" analogue, it is
+ # client-side and based on working directory not username;
+ # but more importantly, it isn't clear why a "cvs status"
+ # would act like an unedit, and even if it does, it would
+ # need to make the file read-only again).
+
+ mkdir watch5; cd watch5
+ dotest watch5-0a "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest watch5-0b "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+
+ cd first-dir
+ dotest watch5-1 "${testcvs} watch on" ''
+ # This is just like the 173 test
+ touch file1
+ dotest watch5-2 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest watch5-3 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest watch5-4 "${testcvs} edit file1" ''
+ dotest watch5-5 "test -f CVS/Base/file1" ''
+ if ${testcvs} status file1 >>${LOGFILE} 2>&1; then
+ pass watch5-6
+ else
+ fail watch5-6
+ fi
+ dotest watch5-7 "test -f CVS/Base/file1" ''
+
+ # Here's where the file used to dissappear
+ touch file1
+ if ${testcvs} status file1 >>${LOGFILE} 2>&1; then
+ pass watch5-8
+ else
+ fail watch5-8
+ fi
+ dotest watch5-10 "test -f CVS/Base/file1" ''
+
+ # Make sure update won't remove the file either
+ touch file1
+ dotest watch5-11 "${testcvs} -q up" ''
+ dotest watch5-12 "test -f CVS/Base/file1" ''
+
+ dokeep
+ cd ../..
+ rm -r watch5
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ watch6-0)
+
+ # Make sure that default attributes are being set properly.
+ # Specifying a directory has, it seems, never worked,
+ # and 1.12.10 broke it completely.
+ mkdir watch6-0; cd watch6-0
+
+ dotest watch6-0-setup-1 "$testcvs -Q co -ldtop ."
+ cd top
+ mkdir watch6-0
+ dotest watch6-0-setup-2 "$testcvs -Q add watch6-0"
+ cd watch6-0
+ dotest watch6-0-1 "$testcvs watch add"
+ dotest watch6-0-2 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr >/dev/null"
+ dotest watch6-0-3 "$testcvs watch remove"
+ dotest_fail watch6-0-4 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr 2>/dev/null >/dev/null"
+
+ dotest watch6-0-5 "$testcvs watch add ."
+ dotest watch6-0-6 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr >/dev/null"
+ dotest watch6-0-7 "$testcvs watch remove ."
+ dotest_fail watch6-0-8 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr 2>/dev/null >/dev/null"
+
+ # OK, basic add/remove work. Now, make sure it works with
+ # named directories.
+ mkdir dir1
+ mkdir dir2
+ mkdir dir3
+ echo afile>afile
+ $testcvs -Q add afile dir1 dir2 dir3
+ $testcvs -Q ci -m "Adding test files"
+
+ # Current directory should not be watched, but there should
+ # be a watch on the file, and on dir1 & dir2, but not on
+ # dir3.
+ dotest watch6-0-9 "$testcvs -Q watch add afile dir1 dir2"
+ dotest_fail watch6-0-10 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr 2>/dev/null >/dev/null"
+ dotest watch6-0-11 \
+"grep '^Fafile' $CVSROOT_DIRNAME/watch6-0/CVS/fileattr >/dev/null"
+ dotest watch6-0-12 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/dir1/CVS/fileattr >/dev/null"
+ dotest watch6-0-13 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/dir2/CVS/fileattr >/dev/null"
+ dotest_fail watch6-0-12 \
+"grep '^D' $CVSROOT_DIRNAME/watch6-0/dir3/CVS/fileattr 2>/dev/null >/dev/null"
+
+ dokeep
+ cd ../../..
+ rm -rf watch6-0
+ modify_repo rm -rf $CVSROOT_DIRNAME/watch6-0
+ ;;
+
+
+
+ watch6)
+ # Check that `cvs watch on' does not reset the fileattr file.
+ mkdir watch6; cd watch6
+
+ dotest watch6-setup-1 "$testcvs -Q co -ldtop ."
+ cd top
+ mkdir watch6
+ dotest watch6-setup-2 "$testcvs -Q add watch6"
+
+ # I don't recall why I had these next 3 lines.
+ cd ..
+ dotest watch6-setup-3 "$testcvs -Q co watch6"
+ cd watch6
+
+ mkdir subdir
+ dotest watch6-setup-4 "$testcvs -Q add subdir"
+ cd subdir
+
+ # START watch add/remove sequence
+ dotest watch6-1 "$testcvs -Q watch add"
+ dotest watch6-2 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-3 "$testcvs watch on"
+ dotest watch6-4 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+ dotest watch6-5 \
+"grep '_watched' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-6 "$testcvs watch off"
+ dotest watch6-7 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+ dotest_fail watch6-8 \
+"grep '_watched' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-9 "$testcvs watch remove"
+ dotest_fail watch6-10 \
+"test -d $CVSROOT_DIRNAME/test-directory/subdir/CVS"
+ dotest_fail watch6-11 \
+"test -f $CVSROOT_DIRNAME/test-directory/subdir/CVS/fileattr"
+ # END watch add/remove sequence
+
+ echo Hi there >afile
+ dotest watch6-12 "$testcvs -Q add afile"
+ dotest watch6-13 "$testcvs ci -m 'A file' afile" \
+"$CVSROOT_DIRNAME/watch6/subdir/afile,v <-- afile
+initial revision: 1.1"
+
+ # START watch add/remove sequence
+ dotest watch6-14 "$testcvs -Q watch add"
+ dotest watch6-15 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-16 "$testcvs watch on"
+ dotest watch6-17 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+ dotest watch6-18 \
+"grep '_watched' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-19 "$testcvs watch off"
+ dotest watch6-20 \
+"grep '_watchers' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+ dotest_fail watch6-21 \
+"grep '_watched' $CVSROOT_DIRNAME/watch6/subdir/CVS/fileattr >/dev/null"
+
+ dotest watch6-22 "$testcvs watch remove"
+ dotest_fail watch6-23 \
+"test -d $CVSROOT_DIRNAME/test-directory/subdir/CVS"
+ dotest_fail watch6-24 \
+"test -f $CVSROOT_DIRNAME/test-directory/subdir/CVS/fileattr"
+ # END watch add/remove sequence
+
+ if $keep; then
+ echo Keeping $TESTDIR and exiting due to --keep
+ exit 0
+ fi
+ cd ../../..
+ rm -r watch6
+ modify_repo rm -rf $CVSROOT_DIRNAME/watch6
+ ;;
+
+
+
+ edit-check)
+ # This tests the edit -c/-f and related features.
+
+ mkdir edit-check; cd edit-check
+ dotest edit-check-0a "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest edit-check-0b "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+
+ cd first-dir
+ dotest edit-check-1 "$testcvs watch on"
+
+ echo foo > file1
+ dotest edit-check-2a "$testcvs add -minitial file1" \
+"$SPROG [a-z]*: scheduling file .file1. for addition
+$SPROG [a-z]*: use .$SPROG commit. to add this file permanently"
+
+ dotest edit-check-2b "$testcvs commit -m 'c1' file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ editorsLineRE="file1 $username [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR/edit-check/first-dir"
+
+ R_editorsLineRE="first-dir/file1 $username
[SMTWF][uoehra][neduit] [JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]*
[0-9][0-9][0-9][0-9] -0000 $hostname $TESTDIR/edit-check"
+ F3_editorsLineRE="second-dir/file3 $username
[SMTWF][uoehra][neduit] [JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]*
[0-9][0-9][0-9][0-9] -0000 $hostname $TESTDIR/edit-check/first-dir"
+
+ A_editorsLineRE="file1 [-a-zA-Z0-9_]* [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR[0-9]*/edit-check/first-dir"
+
+ AF_editorsLineRE="file[12] [-a-zA-Z0-9_]* [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR/edit-check/first-dir"
+
+ NF_editorsLineRE=" [-a-zA-Z0-9_]* [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR/edit-check/first-dir"
+
+ dotest edit-check-3 "$testcvs edit file1"
+ dotest edit-check-4 "$testcvs edit file1" "$editorsLineRE"
+
+ dotest_fail edit-check-5a "$testcvs edit -c file1" \
+"$editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+
+ dotest edit-check-5b "$testcvs editors" "$editorsLineRE"
+
+ dotest edit-check-6a "$testcvs edit -c -f file1" "$editorsLineRE"
+ dotest edit-check-6b "$testcvs editors" "$editorsLineRE"
+
+ dotest edit-check-7a "cat file1" "foo"
+ echo "bar" > file1
+ dotest_fail edit-check-7b "$testcvs edit -c file1" \
+"$editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+ dotest edit-check-7c "cat file1" "bar"
+
+ # edit-check-8a has issues. It copies the current (modified)
+ # version of the file into CVS/Base, so that edit-check-9a and
+ # edit-check-9b don't get the expected results.
+ # Maybe unedit is *supposed* to return it to the state
+ # it was in before the edit (even if it was modified),
+ # but while that has a certain symetry, it doesn't seem
+ # to pass the intuitive-usability test.
+ # This aspect of the general problem could
+ # be fixed by not overwriting pre-existing Base versions,
+ # but it still wouldn't fix it if the user manually
+ # modified the file before doing the first edit.
+ # Because of the possibility that this is working as
+ # intended, I'm just commenting out the test, not fixing
+ # the issue.
+ #dotest edit-check-8a "${testcvs} edit -c -f file1" \
+ # "${editorsLineRE}"
+ dotest edit-check-8b "$testcvs editors" "$editorsLineRE"
+
+ dotest edit-check-9a "echo yes | $testcvs unedit file1" \
+"file1 has been modified; revert changes? "
+ dotest edit-check-9b "$testcvs editors"
+ dotest edit-check-9c "cat file1" "foo"
+
+ dotest edit-check-10 "$testcvs edit -c file1"
+ dotest_fail edit-check-11 "$testcvs edit -c file1" \
+"$editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+
+ echo "morefoo" > file1
+ dotest edit-check-12a "$testcvs commit -m 'c2' -c file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest edit-check-12b "$testcvs editors file1"
+
+ chmod u+w file1
+ echo "morebar" > file1
+ dotest_fail edit-check-13a "$testcvs commit -m 'c3' -c file1" \
+"$SPROG [a-z]*: Valid edit does not exist for file1
+$SPROG \[[a-z]* aborted\]: correct above errors first!"
+ dotest edit-check-13b "$testcvs editors file1"
+
+ dotest edit-check-14a "$testcvs commit -m 'c4' -c -f file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ dotest edit-check-14b "$testcvs editors file1"
+
+ dotest edit-check-15 "$testcvs edit -c file1"
+ cd ..
+
+ dotest edit-check-16a "echo yes | $testcvs release -d first-dir" \
+"You have \[0\] altered files in this repository.
+Are you sure you want to release (and delete) directory \`first-dir': "
+ dotest edit-check-16b "$testcvs -q update -d first-dir" \
+ "U first-dir/file1"
+ cd first-dir
+ dotest edit-check-16c "$testcvs editors file1"
+
+ cd ..
+ dotest edit-check-17a "$testcvs edit -c"
+ dotest_fail edit-check-17b "$testcvs edit -c" \
+"$R_editorsLineRE
+$SPROG edit: Skipping file \`first-dir/file1' due to existing editors\."
+ dotest edit-check-17c "$testcvs edit -c -f" "$R_editorsLineRE"
+
+ echo "more changes" > first-dir/file1
+ dotest edit-check-18a "$testcvs -q commit -m 'c5' -c" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- first-dir/file1
+new revision: 1\.4; previous revision: 1\.3"
+ dotest edit-check-18b "$testcvs editors"
+
+ cd first-dir
+
+ # Manually fake another editor:
+
+ # Try to gaurantee a seperate name for an "other" user editting
+ # the file.
+ otherUser="dummyUser"
+ if [ x"$USER" = x"$otherUser" ] ; then
+ otherUser="dummyUser2"
+ fi
+ if [ x"$LOGNAME" = x"$otherUser" ] ; then
+ otherUser="dummyUser3"
+ fi
+ tabChar=' '
+
+ backupFileattrName="$CVSROOT_DIRNAME/first-dir/CVS/bak.fileattr.$$"
+ mv $CVSROOT_DIRNAME/first-dir/CVS/fileattr $backupFileattrName
+
+ otherDir="`pwd | sed 's%/edit-check/%2/edit-check/%'`"
+ echo \
+"Ffile1${tabChar}_watched=;_editors=$otherUser>Sat Oct 6 04:25:00 2001
-0000+`hostname`+$otherDir;_watchers=$otherUser>tedit+tunedit+tcommit
+D${tabChar}_watched=" > $CVSROOT_DIRNAME/first-dir/CVS/fileattr
+
+ editFileattrName="$CVSROOT_DIRNAME/first-dir/CVS/edit.fileattr.$$"
+ cp $CVSROOT_DIRNAME/first-dir/CVS/fileattr $editFileattrName
+
+ O_editorsLineRE="file1 $otherUser [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR[0-9]/edit-check/first-dir"
+
+ dotest edit-check-19a "$testcvs edit file1" "$O_editorsLineRE"
+ dotest edit-check-19b "$testcvs editors" \
+"$A_editorsLineRE
+$NF_editorsLineRE"
+
+ dotest edit-check-20a "$testcvs unedit file1"
+ dotest edit-check-20b "$testcvs editors" "$O_editorsLineRE"
+
+ dotest_fail edit-check-21a "$testcvs edit -c file1" \
+"$O_editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+ dotest edit-check-21b "$testcvs editors" "$O_editorsLineRE"
+
+ dotest edit-check-22a "$testcvs edit -c -f file1" "$O_editorsLineRE"
+ dotest edit-check-22b "$testcvs editors" \
+"$A_editorsLineRE
+$NF_editorsLineRE"
+
+ echo "Yet another change" >file1
+
+ dotest_fail edit-check-23a "$testcvs edit -c" \
+"$A_editorsLineRE
+$NF_editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+
+ dotest edit-check-23b "$testcvs editors" \
+"$A_editorsLineRE
+$NF_editorsLineRE"
+
+ dotest edit-check-24a "echo y | $testcvs unedit" \
+ "file1 has been modified; revert changes? "
+ dotest edit-check-24b "$testcvs editors" "$O_editorsLineRE"
+ dotest edit-check-24c "cat file1" "more changes"
+
+ dotest edit-check-25a "$testcvs unedit"
+ dotest edit-check-25b "$testcvs editors" "$O_editorsLineRE"
+ dotest_fail edit-check-25c "test -w file1"
+
+ dotest edit-check-26a "$testcvs edit file1" "$O_editorsLineRE"
+ dotest edit-check-26b "$testcvs editors file1" \
+"$A_editorsLineRE
+$NF_editorsLineRE"
+ dotest edit-check-26c "test -w file1"
+
+ echo "Yet more changes" >file1
+ dotest edit-check-27a "$testcvs -q commit -mmsg -c file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4"
+ dotest edit-check-27b "$testcvs editors" "$O_editorsLineRE"
+
+ chmod u+w file1
+ echo "unofficial change" >file1
+
+ dotest_fail edit-check-28a "$testcvs -q commit -mmsg -c" \
+"$SPROG commit: Valid edit does not exist for file1
+$SPROG \[commit aborted\]: correct above errors first!"
+ dotest edit-check-28b "$testcvs editors" "$O_editorsLineRE"
+
+ dotest edit-check-29a "$testcvs -q commit -mmsg -c -f" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.6; previous revision: 1\.5"
+ dotest edit-check-29b "$testcvs editors" "$O_editorsLineRE"
+ dotest edit-check-29c "cat file1" "unofficial change"
+
+ modify_repo cp "$backupFileattrName" \
+ $CVSROOT_DIRNAME/first-dir/CVS/fileattr
+ dotest edit-check-30 "$testcvs editors"
+
+ # Make sure earlier unreported editors are reported properly
+ # with the edit-check code running.
+ if $remote; then
+ CVS_SERVER_SAVED=$CVS_SERVER
+ CVS_SERVER=$TESTDIR/cvs-none; export CVS_SERVER
+
+ # The $DOTSTAR matches the exact exec error message
+ # (which varies) and either "end of file from server"
+ # (if the process doing the exec exits before the parent
+ # gets around to sending data to it) or "broken pipe" (if it
+ # is the other way around).
+ dotest_fail edit-check-31ar "$testcvs edit file1" \
+"$SPROG \[edit aborted\]: cannot exec $TESTDIR/cvs-none: $DOTSTAR"
+ dotest edit-check-31br "test -w file1"
+ dotest edit-check-31cr "cat CVS/Notify" \
+"Efile1 [SMTWF][uoehra][neduit] [JFAMSOND][aepuco][nbrylgptvc] [0-9
][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000 [-a-zA-Z_.0-9]*
$TESTDIR/edit-check/first-dir EUC"
+ CVS_SERVER=$CVS_SERVER_SAVED; export CVS_SERVER
+
+ dotest_fail edit-check-31dr "$testcvs edit -c file1" \
+"$editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\."
+ dotest edit-check-31er "$testcvs editors file1" "$editorsLineRE"
+ dotest edit-check-31fr "$testcvs unedit file1"
+ fi
+
+ # Make sure it isn't confused by handling multiple files at
+ # the same time:
+ echo file2Data >file2
+
+ dotest edit-check-32a "$testcvs add file2" \
+"$SPROG [a-z]*: scheduling file .file2. for addition
+$SPROG [a-z]*: use .$SPROG commit. to add this file permanently"
+
+ dotest edit-check-32b "$testcvs commit -m 'c1' file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ mkdir second-dir
+ dotest edit-check-32c "$testcvs add second-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir/second-dir added to the repository"
+ cd second-dir
+ echo ThirdFile >file3
+
+ dotest edit-check-32d "$testcvs add file3" \
+"$SPROG [a-z]*: scheduling file .file3. for addition
+$SPROG [a-z]*: use .$SPROG commit. to add this file permanently"
+
+ dotest edit-check-32f "$testcvs commit -m 'c1' file3" \
+"$CVSROOT_DIRNAME/first-dir/second-dir/file3,v <-- file3
+initial revision: 1\.1"
+ dotest_fail edit-check-32g "test -w file3"
+
+ cd ..
+
+ dotest edit-check-33a "$testcvs edit -c"
+
+ dotest edit-check-33b "$testcvs editors" \
+"$AF_editorsLineRE
+$AF_editorsLineRE
+$F3_editorsLineRE"
+ dotest edit-check-33c "test -w second-dir/file3"
+
+ dotest_fail edit-check-34a "$testcvs edit -c file1 file2" \
+"$AF_editorsLineRE
+$SPROG edit: Skipping file \`file1' due to existing editors\.
+$AF_editorsLineRE
+$SPROG edit: Skipping file \`file2' due to existing editors\."
+
+ dotest edit-check-34b "$testcvs editors file1 file2" \
+"$editorsLineRE
+$AF_editorsLineRE"
+
+ dotest edit-check-35a "$testcvs unedit file1"
+ dotest edit-check-35b "$testcvs editors" \
+"$AF_editorsLineRE
+$F3_editorsLineRE"
+ dotest edit-check-35c "test -w second-dir/file3"
+
+ dotest edit-check-36a "$testcvs unedit"
+ dotest edit-check-36b "$testcvs editors"
+ dotest_fail edit-check-36c "test -w second-dir/file3"
+
+ dokeep
+ cd ../..
+ rm -rf edit-check
+ rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ unedit-without-baserev)
+ mkdir 1; cd 1
+ module=x
+
+ file=m
+ echo foo > $file
+ dotest unedit-without-baserev-1 \
+ "$testcvs -Q import -m . $module X Y" ''
+ dotest unedit-without-baserev-2 "$testcvs -Q co $module" ''
+ cd $module
+
+ dotest unedit-without-baserev-3 "$testcvs -Q edit $file" ''
+
+ echo add a line >> $file
+ rm -f CVS/Baserev
+
+ # This will fail on most systems.
+ dotest unedit-without-baserev-4 "echo yes |${testcvs} -Q unedit
$file" \
+"m has been modified; revert changes${QUESTION} ${CPROG} unedit: m not
mentioned in CVS/Baserev
+${CPROG} unedit: run update to complete the unedit"
+
+ # SunOS4.1.4 systems make it this far, but with a corrupted
+ # CVS/Entries file. Demonstrate the corruption!
+ dotest unedit-without-baserev-5 "cat CVS/Entries" \
+ "/$file/1\.1\.1\.1/${DOTSTAR}"
+
+ dotest unedit-without-baserev-6 "${testcvs} -q update" \
+"$SPROG update: warning: \`m' was lost
+U m"
+
+ # OK, those were the easy cases. Now tackle the hard one
+ # (the reason that CVS/Baserev was invented rather than just
+ # getting the revision from CVS/Entries). This is very
+ # similar to watch4-10 through watch4-18 but with Baserev
+ # missing.
+ cd ../..
+ mkdir 2; cd 2
+ dotest unedit-without-baserev-7 "${testcvs} -Q co x" ''
+ cd x
+
+ dotest unedit-without-baserev-10 "${testcvs} edit m" ''
+ echo 'edited in 2' >m
+ cd ../..
+
+ cd 1/x
+
+ editorsLineRE="m $username [SMTWF][uoehra][neduit]
[JFAMSOND][aepuco][nbrylgptvc] [0-9 ][0-9] [0-9:]* [0-9][0-9][0-9][0-9] -0000
$hostname $TESTDIR/2/x"
+ dotest unedit-without-baserev-11 "$testcvs edit m" "$editorsLineRE"
+
+ echo 'edited in 1' >m
+ dotest unedit-without-baserev-12 "${testcvs} -q ci -m edit-in-1" \
+"$CVSROOT_DIRNAME/x/m,v <-- m
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../..
+ cd 2/x
+ dotest unedit-without-baserev-13 "${testcvs} -q update" \
+"RCS file: ${CVSROOT_DIRNAME}/x/m,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.2
+Merging differences between 1\.1\.1\.1 and 1\.2 into m
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in m
+C m"
+ rm CVS/Baserev
+ dotest unedit-without-baserev-14 "echo yes |${testcvs} unedit m" \
+"m has been modified; revert changes${QUESTION} ${CPROG} unedit: m not
mentioned in CVS/Baserev
+${CPROG} unedit: run update to complete the unedit"
+ dotest unedit-without-baserev-15 "${testcvs} -q update" \
+"$SPROG update: warning: \`m' was lost
+U m"
+ # The following tests are kind of degenerate compared with
+ # watch4-16 through watch4-18 but might as well make sure that
+ # nothing seriously wrong has happened to the working directory.
+ dotest unedit-without-baserev-16 "cat m" 'edited in 1'
+ # Make sure CVS really thinks we are at 1.2.
+ dotest unedit-without-baserev-17 "${testcvs} -q update" ""
+ dotest unedit-without-baserev-18 "cat m" "edited in 1"
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ rm -r 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ ignore)
+ # On Windows, we can't check out CVSROOT, because the case
+ # insensitivity means that this conflicts with cvsroot.
+ mkdir ignore
+ cd ignore
+
+ dotest ignore-1 "${testcvs} -q co CVSROOT" "U CVSROOT/${DOTSTAR}"
+ cd CVSROOT
+ echo rootig.c >cvsignore
+ dotest ignore-2 "${testcvs} add cvsignore" "${SPROG}"' add:
scheduling file `cvsignore'"'"' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+
+ dotest ignore-3 " ${testcvs} ci -m added" \
+"${CPROG} commit: Examining \.
+${CVSROOT_DIRNAME}/CVSROOT/cvsignore,v <-- cvsignore
+initial revision: 1\.1
+${SPROG} commit: Rebuilding administrative file database"
+
+ cd ..
+ if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then
+ pass ignore-4
+ else
+ fail ignore-4
+ fi
+
+ # 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 ignore-5 "${testcvs} import -m m -I optig.c
ignore/first-dir tag1 tag2" \
+'
+
+I ignore/first-dir/defig.o
+I ignore/first-dir/envig.c
+I ignore/first-dir/optig.c
+I ignore/first-dir/rootig.c
+N ignore/first-dir/bar.c
+N ignore/first-dir/foobar.c
+No conflicts created by this import'
+ dotest_sort ignore-6 "${testcvs} import -m m -I ! ignore/second-dir
tag3 tag4" \
+'
+
+N ignore/second-dir/bar.c
+N ignore/second-dir/defig.o
+N ignore/second-dir/envig.c
+N ignore/second-dir/foobar.c
+N ignore/second-dir/optig.c
+N ignore/second-dir/rootig.c
+No conflicts created by this import'
+ cd ..
+ rm -r dir-to-import
+
+ mkdir 1
+ cd 1
+ dotest ignore-7 "${testcvs} -q co -dsecond-dir ignore/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 ignore-8 "${testcvs} -q co -dfirst-dir ignore/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 ignore-9 "${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 ignore-10 "${testcvs} -q update -I ! -I CVS" \
+"${QUESTION} defig.o
+${QUESTION} envig.c
+${QUESTION} notig.c
+${QUESTION} optig.c
+${QUESTION} rootig.c"
+
+ # 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 $remote; then
+ dotest ignore-11r "$testcvs -q diff" "$QUESTION notig.c"
+
+ # Force the server to be contacted. Ugh. Having CVS
+ # contact the server for the sole purpose of checking
+ # the CVSROOT/cvsignore file does not seem like such a
+ # good idea, so I imagine this will continue to be
+ # necessary. Oh well, at least we test CVS's ablity to
+ # handle a file with a modified timestamp but unmodified
+ # contents.
+ touch bar.c
+
+ dotest ignore-11ar "$testcvs -q ci -m commit-it" \
+"$QUESTION notig.c"
+ fi
+
+ # now test .cvsignore files
+ cd ..
+ echo notig.c >first-dir/.cvsignore
+ echo foobar.c >second-dir/.cvsignore
+ touch first-dir/notig.c second-dir/notig.c second-dir/foobar.c
+ dotest_sort ignore-12 "${testcvs} -qn update" \
+"${QUESTION} first-dir/.cvsignore
+${QUESTION} second-dir/.cvsignore
+${QUESTION} second-dir/notig.c"
+ dotest_sort ignore-13 "${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"
+
+ echo yes | dotest ignore-14 "${testcvs} release -d first-dir" \
+"${QUESTION} \.cvsignore
+You have \[0\] altered files in this repository.
+Are you sure you want to release (and delete) directory .first-dir': "
+
+ echo add a line >>second-dir/foobar.c
+ rm second-dir/notig.c second-dir/.cvsignore
+ echo yes | dotest ignore-15 "${testcvs} release -d second-dir" \
+"M foobar.c
+You have \[1\] altered files in this repository.
+Are you sure you want to release (and delete) directory .second-dir': "
+
+ dokeep
+ cd ../..
+ rm -r ignore
+ modify_repo rm -rf $CVSROOT_DIRNAME/ignore
+ ;;
+
+
+
+ ignore-on-branch)
+ # Test that CVS _doesn't_ ignore files on branches because they were
+ # added to the trunk.
+ mkdir ignore-on-branch; cd ignore-on-branch
+ modify_repo mkdir $CVSROOT_DIRNAME/ignore-on-branch
+
+ # create file1 & file2 on trunk
+ dotest ignore-on-branch-setup-1 "$testcvs -q co -dsetup
ignore-on-branch" ''
+ cd setup
+ echo file1 >file1
+ dotest ignore-on-branch-setup-2 "$testcvs -q add file1" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest ignore-on-branch-setup-3 "$testcvs -q ci -mfile1 file1" \
+"$CVSROOT_DIRNAME/ignore-on-branch/file1,v <-- file1
+initial revision: 1\.1"
+ dotest ignore-on-branch-setup-4 "$testcvs -q tag -b branch" 'T file1'
+ echo file2 >file2
+ dotest ignore-on-branch-setup-5 "$testcvs -q add file2" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest ignore-on-branch-setup-6 "$testcvs -q ci -mtrunk file2" \
+"$CVSROOT_DIRNAME/ignore-on-branch/file2,v <-- file2
+initial revision: 1\.1"
+
+ cd ..
+
+ # Check out branch.
+ #
+ # - This was the original failure case - file2 would not be flagged
+ # with a '?'
+ dotest ignore-on-branch-1 "$testcvs -q co -rbranch ignore-on-branch" \
+'U ignore-on-branch/file1'
+ cd ignore-on-branch
+ echo file2 on branch >file2
+ dotest ignore-on-branch-2 "$testcvs -nq update" '? file2'
+
+ # Now set up for a join. One of the original fixes for this would
+ # print out a 'U' and a '?' during a join which added a file.
+ if $remote; then
+ dotest ignore-on-branch-3 "$testcvs -q tag -b branch2" \
+'? file2
+T file1'
+ else
+ dotest ignore-on-branch-3 "$testcvs -q tag -b branch2" 'T file1'
+ fi
+ dotest ignore-on-branch-4 "$testcvs -q add file2" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest ignore-on-branch-5 "$testcvs -q ci -mbranch file2" \
+"$CVSROOT_DIRNAME/ignore-on-branch/file2,v <-- file2
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+ dotest ignore-on-branch-6 "$testcvs -q up -rbranch2" \
+"${SPROG} update: \`file2' is no longer in the repository"
+ dotest ignore-on-branch-7 "$testcvs -q up -jbranch" 'U file2'
+
+ dokeep
+ cd ../..
+ rm -r ignore-on-branch
+ modify_repo rm -rf $CVSROOT_DIRNAME/ignore-on-branch
+ ;;
+
+
+
+ binfiles)
+ # Test cvs's ability to handle binary files.
+ # List of binary file tests:
+ # * conflicts, "cvs admin": binfiles
+ # * branching and joining: binfiles2
+ # * adding and removing files: binfiles3
+ # * -k wrappers: binwrap, binwrap2, binwrap3
+ # * "cvs import" and wrappers: binwrap, binwrap2, binwrap3
+ # * -k option to "cvs import": none yet, as far as I know.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1; cd 1
+ dotest binfiles-1 "${testcvs} -q co first-dir" ''
+ ${AWK} 'BEGIN { printf "address@hidden", 2, 10, 137, 13, 10 }' \
+ </dev/null | ${TR} '@' '\000' >binfile.dat
+ cat binfile.dat binfile.dat >binfile2.dat
+ cd first-dir
+ cp ../binfile.dat binfile
+ dotest binfiles-2 "${testcvs} add -kb binfile" \
+"${SPROG}"' add: scheduling file `binfile'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest binfiles-3 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/binfile,v <-- binfile
+initial revision: 1\.1"
+ 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
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ # Test that "-kk" does not override "-kb"
+ cd ../..
+ mkdir 2a; cd 2a
+ dotest binfiles-4 "${testcvs} -q co -kk first-dir" 'U
first-dir/binfile'
+ cd first-dir
+ # 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
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ # Test whether the default options from the RCS file are
+ # also used when operating on files instead of whole
+ # directories
+ cd ../..
+ rm -r 2a
+ mkdir 3; cd 3
+ dotest binfiles-5.5b0 "${testcvs} -q co first-dir/binfile" \
+'U first-dir/binfile'
+ cd first-dir
+ dotest binfiles-5.5b1 "${testcvs} status binfile" \
+"===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+ cd ../..
+ rm -r 3
+ # test that "-kk" does not override "-kb"
+ mkdir 3; cd 3
+ dotest binfiles-5.5b0 "${testcvs} -q co -kk first-dir/binfile" \
+'U first-dir/binfile'
+ cd first-dir
+ dotest binfiles-5.5b1 "${testcvs} status binfile" \
+"===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+ cd ../..
+ rm -r 3
+ cd 2/first-dir
+
+ cp ../../1/binfile2.dat binfile
+ dotest binfiles-6 "${testcvs} -q ci -m modify-it" \
+"$CVSROOT_DIRNAME/first-dir/binfile,v <-- binfile
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../../1/first-dir
+ dotest binfiles-7 "${testcvs} -q update" 'U 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" \
+"$CVSROOT_DIRNAME/first-dir/binfile,v <-- binfile
+new revision: 1\.3; previous revision: 1\.2"
+ cd ../../2/first-dir
+ echo 'edits in dir 2' >binfile
+ dotest binfiles-con1 "${testcvs} -q update" \
+"$SPROG update: nonmergeable file needs merge
+$SPROG update: revision 1\.3 from repository is now in binfile
+$SPROG update: file from working directory is now in \.#binfile\.1\.2
+C binfile"
+
+ dotest_fail binfiles-con1b "$testcvs -q up" "C binfile"
+
+ dotest binfiles-con2 "cmp binfile ../../1/binfile.dat" ''
+ dotest binfiles-con3 "cat .#binfile.1.2" 'edits in dir 2'
+
+ cp ../../1/binfile2.dat binfile
+ dotest binfiles-con4 "$testcvs -q ci -m resolve-it" \
+"$CVSROOT_DIRNAME/first-dir/binfile,v <-- binfile
+new revision: 1\.4; previous revision: 1\.3"
+ cd ../../1/first-dir
+ dotest binfiles-con5 "${testcvs} -q update" 'U binfile'
+
+ dotest binfiles-9 "${testcvs} -q update -A" ''
+ # "-kk" no longer does anything with "-kb"
+ dotest binfiles-10 "${testcvs} -q update -kk" ''
+ dotest binfiles-11 "${testcvs} -q update" ''
+ # "-kk" no longer does anything with "-kb"
+ dotest binfiles-12 "${testcvs} -q update -A" ''
+ dotest binfiles-13 "${testcvs} -q update -A" ''
+
+ cd ../..
+
+ 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 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/binfile,v <-- binfile
+new revision: 1\.5; previous revision: 1\.4"
+ 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 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+ dotest binfiles-14d "${testcvs} admin -kv binfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/binfile,v
+done"
+ # cvs admin doesn't change the checked-out file or its sticky
+ # kopts. There probably should be a way which does (but
+ # what if the file is modified? And do we try to version
+ # control the kopt setting?)
+ dotest binfiles-14e "cat binfile" 'this file is $''RCSfile$'
+ dotest binfiles-14f "${testcvs} status binfile" \
+"===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.5.*
+ Repository revision: 1\.5 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+ dotest binfiles-14g "${testcvs} -q update -A" 'U binfile'
+ dotest binfiles-14h "cat binfile" 'this file is binfile,v'
+ dotest binfiles-14i "${testcvs} status binfile" \
+"===================================================================
+File: binfile Status: Up-to-date
+
+ Working revision: 1\.5.*
+ Repository revision: 1\.5 ${CVSROOT_DIRNAME}/first-dir/binfile,v
+ Commit Identifier: ${commitid}
+ 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" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest binfiles-sticky2 "${testcvs} -q ci -m add-it nibfile" \
+"$CVSROOT_DIRNAME/first-dir/nibfile,v <-- nibfile
+initial revision: 1\.1"
+ dotest binfiles-sticky3 "${testcvs} -q update -kb nibfile" \
+ 'U nibfile'
+ dotest binfiles-sticky4 "${testcvs} -q status nibfile" \
+"===================================================================
+File: nibfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/nibfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ # Now test that -A can clear the sticky option.
+ dotest binfiles-sticky5 "${testcvs} -q update -A nibfile" \
+"U nibfile"
+ dotest binfiles-sticky6 "${testcvs} -q status nibfile" \
+"===================================================================
+File: nibfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/nibfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest binfiles-15 "${testcvs} -q admin -kb nibfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/nibfile,v
+done"
+ dotest binfiles-16 "${testcvs} -q update nibfile" "U nibfile"
+ dotest binfiles-17 "${testcvs} -q status nibfile" \
+"===================================================================
+File: nibfile Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/nibfile,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ dotest binfiles-o1 "${testcvs} admin -o1.3:: binfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/binfile,v
+deleting revision 1\.5
+deleting revision 1\.4
+done"
+ dotest binfiles-o2 "${testcvs} admin -o::1.3 binfile" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/binfile,v
+deleting revision 1\.2
+deleting revision 1\.1
+done"
+ dotest binfiles-o3 "${testcvs} -q log -h -N binfile" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/binfile,v
+Working file: binfile
+head: 1\.3
+branch:
+locks: strict
+access list:
+keyword substitution: v
+total revisions: 1
+============================================================================="
+
+ # Check that the contents were right. This isn't the hard case
+ # (in which RCS_delete_revs does a diff), but might as well.
+ dotest binfiles-o4 "${testcvs} -q update binfile" "U binfile"
+ dotest binfiles-o5 "cmp binfile ../../1/binfile.dat" ""
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r 1 2
+ ;;
+
+
+
+ binfiles2)
+ # Test cvs's ability to handle binary files, particularly branching
+ # and joining. The key thing we are worrying about is that CVS
+ # doesn't print "cannot merge binary files" or some such, in
+ # situations where no merging is required.
+ # See also "join" which does this with non-binary files.
+ #
+ # 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.
+
+ modify_repo mkdir ${CVSROOT_DIRNAME}/first-dir
+ mkdir 1; cd 1
+ dotest binfiles2-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+
+ # 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 "address@hidden", 2, 10, 137, 13, 10 }' \
+ </dev/null | ${TR} '@' '\000' >../binfile
+ # Use binfl2 rather than binfile2 because of a problem with Cygwin
+ # and Samba. that causes cat to report that the input and output file
+ # are the same when outputting to binfile3. Why? I don't know, but
+ # it is consistently reproducible.
+ cat ../binfile ../binfile >../binfl2
+ cat ../binfl2 ../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" \
+"${SPROG} add: scheduling file .brmod. for addition
+${SPROG} add: scheduling file .brmod-trmod. for addition
+${SPROG} add: scheduling file .brmod-wdmod. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest binfiles2-1b "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+initial revision: 1\.1"
+ 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" \
+"${SPROG} add: scheduling file .binfile\.dat. for addition on branch .br.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cp ../binfl2 brmod
+ cp ../binfl2 brmod-trmod
+ cp ../binfl2 brmod-wdmod
+ dotest binfiles2-5 "${testcvs} -q ci -m br-changes" \
+"$CVSROOT_DIRNAME/first-dir/Attic/binfile\.dat,v <-- binfile\.dat
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest binfiles2-6 "${testcvs} -q update -A" \
+"${SPROG} update: \`binfile\.dat' is no longer in the repository
+U brmod
+U brmod-trmod
+U 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" \
+"$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.2; previous revision: 1\.1"
+ cp ../binfile3 brmod-wdmod
+
+ dotest binfiles2-8 "${testcvs} -q update -j br" \
+"U binfile\.dat
+U brmod
+${SPROG} update: nonmergeable file needs merge
+${SPROG} update: revision 1.1.2.1 from repository is now in brmod-trmod
+${SPROG} update: file from working directory is now in .#brmod-trmod.1.2
+C brmod-trmod
+M brmod-wdmod
+${SPROG} update: nonmergeable file needs merge
+${SPROG} update: revision 1.1.2.1 from repository is now in brmod-wdmod
+${SPROG} update: file from working directory is now in .#brmod-wdmod.1.1
+C brmod-wdmod"
+
+ dotest binfiles2-9 "cmp ../binfile binfile.dat"
+ dotest binfiles2-9-brmod "cmp ../binfl2 brmod"
+ dotest binfiles2-9-brmod-trmod "cmp ../binfl2 brmod-trmod"
+ dotest binfiles2-9-brmod-trmod "cmp ../binfl2 brmod-wdmod"
+ dotest binfiles2-9a-brmod-trmod "cmp ../binfile3 .#brmod-trmod.1.2"
+ dotest binfiles2-9a-brmod-wdmod "cmp ../binfile3 .#brmod-wdmod.1.1"
+
+ # Test that everything was properly scheduled.
+ dotest binfiles2-10 "${testcvs} -q ci -m checkin" \
+"$CVSROOT_DIRNAME/first-dir/binfile\.dat,v <-- binfile\.dat
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.3; previous revision: 1\.2
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+new revision: 1\.2; previous revision: 1\.1"
+
+ dotest_fail binfiles2-o1 "${testcvs} -q admin -o :1.2 brmod-trmod" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/brmod-trmod,v
+deleting revision 1\.2
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/brmod-trmod,v: can't remove
branch point 1\.1
+${SPROG} admin: RCS file for .brmod-trmod. not modified\."
+ dotest binfiles2-o2 "${testcvs} -q admin -o 1.1.2.1: brmod-trmod" \
+"RCS file: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+checkin
+============================================================================="
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r 1
+ ;;
+
+
+
+ binfiles3)
+ # More binary file tests, especially removing, adding, &c.
+ # See "binfiles" for a list of binary file tests.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ mkdir 1; cd 1
+ dotest binfiles3-1 "${testcvs} -q co first-dir" ''
+ ${AWK} 'BEGIN { printf "address@hidden", 2, 10, 137, 13, 10 }' \
+ </dev/null | ${TR} '@' '\000' >binfile.dat
+ cd first-dir
+ echo hello >file1
+ dotest binfiles3-2 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest binfiles3-3 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ rm file1
+ dotest binfiles3-4 "${testcvs} rm file1" \
+"${SPROG} remove: scheduling .file1. for removal
+${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest binfiles3-5 "${testcvs} -q ci -m remove-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.1"
+ cp ../binfile.dat file1
+ dotest binfiles3-6 "${testcvs} add -kb file1" \
+"$SPROG add: Re-adding file .file1. after dead revision 1\.2\.
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ # The idea behind this test is to make sure that the file
+ # gets opened in binary mode to send to "cvs ci".
+ dotest binfiles3-6a "cat CVS/Entries" \
+"/file1/0/[A-Za-z0-9 :]*/-kb/
+D"
+ # TODO: This just tests the case where the old keyword
+ # expansion mode is the default (RCS_getexpand == NULL
+ # in checkaddfile()); should also test the case in which
+ # we are changing it from one non-default value to another.
+ dotest binfiles3-7 "$testcvs -q ci -m readd-it" \
+"$SPROG commit: changing keyword expansion mode to -kb
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ dotest binfiles3-8 "${testcvs} -q log -h -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.3
+branch:
+locks: strict
+access list:
+keyword substitution: b
+total revisions: 3
+============================================================================="
+
+ # OK, now test admin -o on a binary file. See "admin"
+ # test for a more complete list of admin -o tests.
+ cp ${TESTDIR}/1/binfile.dat ${TESTDIR}/1/binfile4.dat
+ echo '%%$$##@@!!jjiiuull' | ${TR} j '\000' >>${TESTDIR}/1/binfile4.dat
+ cp ${TESTDIR}/1/binfile4.dat ${TESTDIR}/1/binfile5.dat
+ echo 'aawwee%$$##@@!!jjil' | ${TR} w '\000'
>>${TESTDIR}/1/binfile5.dat
+
+ cp ../binfile4.dat file1
+ dotest binfiles3-9 "${testcvs} -q ci -m change" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3"
+ cp ../binfile5.dat file1
+ dotest binfiles3-10 "${testcvs} -q ci -m change" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4"
+ dotest binfiles3-11 "${testcvs} admin -o 1.3::1.5 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+deleting revision 1\.4
+done"
+ dotest binfiles3-12 "${testcvs} -q update -r 1.3 file1" "U file1"
+ dotest binfiles3-13 "cmp file1 ${TESTDIR}/1/binfile.dat" ""
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-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.
+
+ # For the moment, remote CVS can't pass wrappers from CVSWRAPPERS
+ # (see wrap_send). So skip these tests for remote.
+ if $remote; then :; else
+
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ mkdir 1; cd 1
+ dotest mcopy-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+
+ # FIXCVS: unless a branch has at least one file on it,
+ # tag_check_valid won't know it exists. So 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" \
+"${SPROG} add: scheduling file .\.cvswrappers. for addition
+${SPROG} add: scheduling file .brmod. for addition
+${SPROG} add: scheduling file .brmod-trmod. for addition
+${SPROG} add: scheduling file .brmod-wdmod. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest mcopy-1b "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/\.cvswrappers,v <-- \.cvswrappers
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+initial revision: 1\.1"
+
+ # 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" \
+"$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest mcopy-6 "${testcvs} -q update -A" \
+"U brmod
+U brmod-trmod
+U brmod-wdmod"
+ dotest mcopy-7 "cat brmod brmod-trmod brmod-wdmod" \
+"brmod initial contents
+brmod-trmod initial contents
+brmod-wdmod initial contents"
+
+ echo 'modify brmod-trmod again on trunk' >brmod-trmod
+ dotest mcopy-7a "${testcvs} -q ci -m tr-modify" \
+"$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.2; previous revision: 1\.1"
+ echo 'modify brmod-wdmod in working dir' >brmod-wdmod
+
+ dotest mcopy-8 "${testcvs} -q update -j br" \
+"U brmod
+${SPROG} update: nonmergeable file needs merge
+${SPROG} update: revision 1.1.2.1 from repository is now in brmod-trmod
+${SPROG} update: file from working directory is now in .#brmod-trmod.1.2
+C brmod-trmod
+M brmod-wdmod
+${SPROG} update: nonmergeable file needs merge
+${SPROG} update: revision 1.1.2.1 from repository is now in brmod-wdmod
+${SPROG} update: 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" \
+"$CVSROOT_DIRNAME/first-dir/brmod,v <-- brmod
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/brmod-trmod,v <-- brmod-trmod
+new revision: 1\.3; previous revision: 1\.2
+$CVSROOT_DIRNAME/first-dir/brmod-wdmod,v <-- brmod-wdmod
+new revision: 1\.2; previous revision: 1\.1"
+
+ dokeep
+ cd ../..
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -r 1
+ unset CVSWRAPPERS
+ fi # end of tests to be skipped for remote
+ ;;
+
+
+
+ binwrap)
+ # 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
+
+ # While we're here, test for rejection of duplicate tag names.
+ dotest_fail binwrap-0 \
+ "${testcvs} import -m msg -I ! first-dir dup dup" \
+"${CPROG} \[import aborted\]: tag .dup. was specified more than once"
+
+ if ${testcvs} import -m message -I ! -W "*.exe -k 'b'" \
+ first-dir tag1 tag2 >>${LOGFILE}; then
+ pass binwrap-1
+ else
+ fail binwrap-1
+ fi
+ cd ..
+ rm -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
+
+ Working revision: 1\.1\.1\.1.*
+ Repository revision: 1\.1\.1\.1
${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: foo\.exe Status: Up-to-date
+
+ Working revision: 1\.1\.1\.1.*
+ Repository revision: 1\.1\.1\.1
${CVSROOT_DIRNAME}/first-dir/foo\.exe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ dokeep
+ rm -r first-dir
+ modify_repo 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.
+
+ mkdir dir-to-import
+ cd dir-to-import
+ touch foo.c foo.exe
+
+ # 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
+ 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
${CVSROOT_DIRNAME}/first-dir/foo\.c,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -ko
+
+===================================================================
+File: foo\.exe Status: Up-to-date
+
+ Working revision: 1\.1\.1\.1.*
+ Repository revision: 1\.1\.1\.1
${CVSROOT_DIRNAME}/first-dir/foo\.exe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: -kb"
+
+ dokeep
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-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
+
+ binwrap3_line1="This is a test file "
+ binwrap3_line2="containing little of use "
+ binwrap3_line3="except this non-haiku"
+
+ binwrap3_text="${binwrap3_line1}${binwrap3_line2}${binwrap3_line3}"
+
+ cd ${TESTDIR}
+
+ # On Windows, we can't check out CVSROOT, because the case
+ # insensitivity means that this conflicts with cvsroot.
+ mkdir wnt
+ cd wnt
+
+ mkdir binwrap3 # the 0th dir
+ mkdir binwrap3/sub1
+ mkdir binwrap3/sub2
+ mkdir binwrap3/sub2/subsub
+
+ echo "bar*" > binwrap3/.cvswrappers
+ echo "*.c0 -k 'b'" >> binwrap3/.cvswrappers
+ echo "whatever -k 'b'" >> binwrap3/.cvswrappers
+ echo ${binwrap3_text} > binwrap3/foo-b.c0
+ echo ${binwrap3_text} > binwrap3/bar-t.c0
+ echo ${binwrap3_text} > binwrap3/foo-b.sb
+ echo ${binwrap3_text} > binwrap3/foo-t.sb
+ echo ${binwrap3_text} > binwrap3/foo-t.c1
+ echo ${binwrap3_text} > binwrap3/foo-t.st
+
+ echo "bar* -k 'kv'" > binwrap3/sub1/.cvswrappers
+ 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/bar-t.c1
+ echo ${binwrap3_text} > binwrap3/sub1/foo-b.sb
+ echo ${binwrap3_text} > binwrap3/sub1/foo-t.sb
+ echo ${binwrap3_text} > binwrap3/sub1/foo-t.c0
+ echo ${binwrap3_text} > binwrap3/sub1/foo-t.st
+
+ echo "bar*" > binwrap3/sub2/.cvswrappers
+ echo "*.st -k 'b'" >> binwrap3/sub2/.cvswrappers
+ echo ${binwrap3_text} > binwrap3/sub2/foo-b.sb
+ echo ${binwrap3_text} > binwrap3/sub2/foo-t.sb
+ echo ${binwrap3_text} > binwrap3/sub2/foo-b.st
+ echo ${binwrap3_text} > binwrap3/sub2/bar-t.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 "bar* -k 'kv'" > binwrap3/sub2/subsub/.cvswrappers
+ 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/bar-t.c3
+ echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-b.sb
+ echo ${binwrap3_text} > binwrap3/sub2/subsub/foo-t.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" "U 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-t.sb" > cvswrappers
+ echo "foo*.sb -k 'b'" >> cvswrappers
+ dotest binwrap3-2 "${testcvs} -q ci -m cvswrappers-mod" \
+"$CVSROOT_DIRNAME/CVSROOT/cvswrappers,v <-- cvswrappers
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+
+ # Avoid environmental interference
+ CVSWRAPPERS_save=${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}"
+
+ # 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" \
+"${SPROG} add: scheduling file .file1\.newbin. for addition
+${SPROG} add: scheduling file .file1\.txt. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest binwrap3-2d "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/binwrap3/sub2/file1\.newbin,v <-- file1\.newbin
+initial revision: 1\.1
+$CVSROOT_DIRNAME/binwrap3/sub2/file1\.txt,v <-- file1\.txt
+initial revision: 1\.1"
+ 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 binwrap3-top4 "grep foo-t.st ./CVS/Entries" \
+ "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-top5 "grep foo-t.sb ./CVS/Entries" \
+ "/foo-t.sb/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-top6 "grep bar-t.c0 ./CVS/Entries" \
+ "/bar-t.c0/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/"
+
+ 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-sub1-5 "grep foo-t.sb sub1/CVS/Entries" \
+ "/foo-t.sb/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-sub1-6 "grep bar-t.c1 sub1/CVS/Entries" \
+ "/bar-t.c1/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-sub2-7 "grep foo-t.sb sub2/CVS/Entries" \
+ "/foo-t.sb/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-sub2-8 "grep bar-t.st sub2/CVS/Entries" \
+ "/bar-t.st/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/"
+
+ dotest binwrap3-subsub2 "grep foo-b.sb sub2/subsub/CVS/Entries" \
+ "/foo-b.sb/1.1.1.1/[A-Za-z0-9 :]*/-kb/"
+
+ dotest binwrap3-subsub3 "grep foo-t.c0 sub2/subsub/CVS/Entries" \
+ "/foo-t.c0/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-subsub4 "grep foo-t.c1 sub2/subsub/CVS/Entries" \
+ "/foo-t.c1/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-subsub5 "grep foo-t.c2 sub2/subsub/CVS/Entries" \
+ "/foo-t.c2/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-subsub6 "grep foo-t.st sub2/subsub/CVS/Entries" \
+ "/foo-t.st/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-subsub7 "grep foo-t.sb sub2/subsub/CVS/Entries" \
+ "/foo-t.sb/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ dotest binwrap3-subsub8 "grep bar-t.c3 sub2/subsub/CVS/Entries" \
+ "/bar-t.c3/1.1.1.1/[A-Za-z0-9 :]*//"
+
+ 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 :]*//"
+
+ # Restore and clean up
+ dokeep
+ cd ..
+ rm -r binwrap3 CVSROOT
+ cd ..
+ rm -r wnt
+ modify_repo rm -rf $CVSROOT_DIRNAME/binwrap3
+ CVSWRAPPERS=${CVSWRAPPERS_save}
+ ;;
+
+
+
+ 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" "U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ echo "* -m 'COPY'" >>cvswrappers
+ dotest mwrap-c2 "${testcvs} -q ci -m wrapper-mod" \
+"$CVSROOT_DIRNAME/CVSROOT/cvswrappers,v <-- cvswrappers
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch aa
+ dotest mwrap-3 "${testcvs} add aa" \
+"${SPROG} add: scheduling file .aa. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest mwrap-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+initial revision: 1\.1"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+new revision: 1\.2; previous revision: 1\.1"
+ cd ../..
+ cd m1/first-dir
+ echo "changed in m1" >aa
+ dotest mwrap-7 "$testcvs -nq update" \
+"$SPROG update: nonmergeable file needs merge
+$SPROG update: revision 1\.2 from repository is now in aa
+$SPROG update: file from working directory is now in \.#aa\.1\.1
+C aa"
+ dotest mwrap-8 "$testcvs -q update" \
+"$SPROG update: nonmergeable file needs merge
+$SPROG update: revision 1\.2 from repository is now in aa
+$SPROG update: 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"
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r CVSROOT
+ rm -r m1 m2
+ cd ..
+ rm -r wnt
+ modify_repo 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
+ # taginfo: taginfo
+ # posttag: posttag
+ # postadmin: admin
+ # postwatch: devcom3
+ # config: config
+ # config2: MinCompressionLevel and MaxCompressionLevel in 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" "U CVSROOT${DOTSTAR}"
+ cd CVSROOT
+ dotest info-2 "$testcvs -Q tag info-start"
+ sed -e's/%p/ALL/' <loginfo >tmploginfo
+ mv tmploginfo loginfo
+ echo "ALL sh -c \"echo
x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$COMMITID=\$SESSIONID=\$CVSROOT=
>>$TESTDIR/testlog; cat >/dev/null\"" >> loginfo
+ # The following cases test the format string substitution
+ echo "ALL echo %{} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %x >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo % >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %{sxVv} >>$TESTDIR/testlog2; cat >/dev/null" >>
loginfo
+ echo "ALL echo %{v} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %s %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
+ sed -e's/^UseNewInfoFmtStrings=yes$/#&/' <config >tmpconfig
+ mv tmpconfig config
+
+ # 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" \
+"$SPROG add: scheduling file \`loginfo' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+
+ dotest_fail info-3 "$testcvs -q ci -m new-loginfo" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$CVSROOT_DIRNAME/CVSROOT/loginfo,v <-- loginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=MYENV}
+$SPROG \[commit aborted\]: Unknown format character in info file ('').
+Info files are the hook files, verifymsg, taginfo, commitinfo, etc\."
+ cd ..
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest info-5 "$testcvs -q co first-dir" ''
+ cd first-dir
+ touch file1
+ dotest info-6 "$testcvs add file1" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc
+ dotest info-6b "$testcvs -q -s OTHER=value ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\." \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\.
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}"
+ echo line0 >>file1
+ dotest info-6c "$testcvs -q -sOTHER=foo ci -m mod-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\." \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\.
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}"
+ echo line1 >>file1
+ dotest info-7 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m mod-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+
+ cd ..
+ dotest info-9 "cat $TESTDIR/testlog" \
+"xenv-valueyz=${username}=${commitid}=${commitid}=${CVSROOT_DIRNAME}="
+ dotest info-10 "cat $TESTDIR/testlog2" \
+'first-dir
+first-dir
+first-dir
+first-dir file1,,NONE,1.1
+first-dir 1.1
+first-dir file1 %s
+first-dir NONEAX
+first-dir file1ux
+first-dir
+first-dir
+first-dir
+first-dir file1,,1.1,1.2
+first-dir 1.2
+first-dir file1 %s
+first-dir 1.1AX
+first-dir file1ux
+first-dir
+first-dir
+first-dir
+first-dir file1,,1.2,1.3
+first-dir 1.3
+first-dir file1 %s
+first-dir 1.2AX
+first-dir file1ux'
+
+ # and make sure adding a '1' in the format strings really does ensure
+ # ensure backwards compatibility.
+ #
+ # these tests are identical to the above except for the loginfo setup
+ # and the project name
+ cd CVSROOT
+ dotest info-setup-intfmt-1 "$testcvs -q up -prinfo-start config
>config"
+ dotest info-setup-intfmt-2 "$testcvs -q up -prinfo-start loginfo
>loginfo"
+ sed -e's/%p/ALL/' <loginfo >tmploginfo
+ mv tmploginfo loginfo
+ 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 %1{} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %1x >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %1 >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %1{sxVv} >>$TESTDIR/testlog2; cat >/dev/null" >>
loginfo
+ echo "ALL echo %1{v} >>$TESTDIR/testlog2; cat >/dev/null" >> loginfo
+ echo "ALL echo %1s %%s >>$TESTDIR/testlog2; cat >/dev/null" >>
loginfo
+ echo "ALL echo %1{V}AX >>$TESTDIR/testlog2; cat >/dev/null" >>
loginfo
+ echo "third-dir echo %1sux >>$TESTDIR/testlog2; cat >/dev/null" \
+ >> loginfo
+
+ dotest info-setup-intfmt-2 "${testcvs} -q -s ZEE=garbage ci -m
nuke-admin-for-info-intfmt" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$TESTDIR/cvsroot/CVSROOT/loginfo,v <-- loginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary .1. in
+all info files after each .%. which doesn.t represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+ cd ..
+
+ # delete the logs now so the results look more like the last tests
+ # (they won't include the config file update)
+ rm ${TESTDIR}/testlog ${TESTDIR}/testlog2
+
+ modify_repo mkdir $CVSROOT_DIRNAME/third-dir
+ dotest info-intfmt-5 "${testcvs} -q co third-dir" ''
+ cd third-dir
+ touch file1
+ dotest info-intfmt-6 "${testcvs} add file1" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc
+ dotest info-intfmt-6b "${testcvs} -q -s OTHER=value ci -m add-it" \
+"${TESTDIR}/cvsroot/third-dir/file1,v <-- file1
+initial revision: 1\.1
+${SPROG} commit: loginfo:[0-9]*: no such user variable \${=ZEE}
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\."
+ echo line0 >>file1
+ dotest info-intfmt-6c "${testcvs} -q -sOTHER=foo ci -m mod-it" \
+"${TESTDIR}/cvsroot/third-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+${SPROG} commit: loginfo:[0-9]*: no such user variable \${=ZEE}
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\."
+ echo line1 >>file1
+ dotest info-intfmt-7 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m
mod-it" \
+"${TESTDIR}/cvsroot/third-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\.
+${SPROG} commit: Using deprecated info format strings\. Convert your scripts
to use
+the new argument format and remove '1's from your info file format strings\."
+
+ cd ..
+ dotest info-intfmt-9 "cat $TESTDIR/testlog"
"xenv-valueyz=${username}=${TESTDIR}/cvsroot="
+ dotest info-intfmt-10 "cat $TESTDIR/testlog2" \
+'third-dir
+third-dir
+third-dir
+third-dir file1,,NONE,1.1
+third-dir 1.1
+third-dir file1 %s
+third-dir NONEAX
+third-dir file1ux
+third-dir
+third-dir
+third-dir
+third-dir file1,,1.1,1.2
+third-dir 1.2
+third-dir file1 %s
+third-dir 1.1AX
+third-dir file1ux
+third-dir
+third-dir
+third-dir
+third-dir file1,,1.2,1.3
+third-dir 1.3
+third-dir file1 %s
+third-dir 1.2AX
+third-dir file1ux'
+
+ rm ${TESTDIR}/testlog ${TESTDIR}/testlog2
+
+ # test the new format strings too
+ cd CVSROOT
+ dotest info-setup-newfmt-1 "$testcvs -q up -prinfo-start loginfo
>loginfo"
+ echo "ALL sh -c \"echo
x\${=MYENV}\${=OTHER}y\${=ZEE}=\$USER=\$CVSROOT= >>$TESTDIR/testlog; cat
>/dev/null\" %{sVv}" >> loginfo
+ # The following cases test the format string substitution
+ echo "ALL echo %p \"%{sTVv}\" >>$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 "second-dir echo %sux >>$TESTDIR/testlog2; cat >/dev/null" >>
loginfo
+ dotest info-setup-newfmt-2 "$testcvs -q -s ZEE=garbage ci -m
nuke-admin-for-info-newfmt" \
+"${CVSROOT_DIRNAME}/CVSROOT/loginfo,v <-- loginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+${SPROG} commit: Rebuilding administrative file database"
+ cd ..
+
+ # delete the logs now so the results look more like the last tests
+ # (they won't include the config file update)
+ rm ${TESTDIR}/testlog ${TESTDIR}/testlog2
+
+ modify_repo mkdir $CVSROOT_DIRNAME/fourth-dir
+ dotest info-newfmt-1 "${testcvs} -q co fourth-dir" ''
+ cd fourth-dir
+ touch file1
+ dotest info-newfmt-2 "${testcvs} add file1" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ echo "cvs -s OTHER=not-this -s MYENV=env-" >>$HOME/.cvsrc
+ dotest info-newfmt-3 "$testcvs -q -s OTHER=value ci -m add-it" \
+"$TESTDIR/cvsroot/fourth-dir/file1,v <-- file1
+initial revision: 1\.1
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}"
+ echo line0 >>file1
+ dotest info-newfmt-4 "$testcvs -q -sOTHER=foo ci -m mod-it" \
+"$TESTDIR/cvsroot/fourth-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$SPROG commit: loginfo:[0-9]*: no such user variable \${=ZEE}"
+ echo line1 >>file1
+ dotest info-newfmt-5 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m
mod-it" \
+"${TESTDIR}/cvsroot/fourth-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+
+ cd ..
+ dotest info-newfmt-6 "cat $TESTDIR/testlog" \
+"xenv-valueyz=${username}=${TESTDIR}/cvsroot="
+ dotest info-newfmt-7 "cat $TESTDIR/testlog2" \
+'fourth-dir file1 NONE 1\.1
+1\.1
+file1
+NONEAX
+fourth-dir file1 1\.1 1\.2
+1\.2
+file1
+1\.1AX
+fourth-dir file1 1\.2 1\.3
+1\.3
+file1
+1\.2AX'
+
+ # clean up after newfmt tests
+ cd CVSROOT
+ dotest info-cleanup-newfmt-1 "$testcvs -q up -prinfo-start loginfo
>loginfo"
+ dotest info-cleanup-newfmt-2 "$testcvs -q ci -m nuke-loginfo" \
+"$CVSROOT_DIRNAME/CVSROOT/loginfo,v <-- loginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ # clean up the logs
+ rm ${TESTDIR}/testlog ${TESTDIR}/testlog2
+
+ # Now test verifymsg
+ cat >${TESTDIR}/vscript <<EOF
+#!${TESTSHELL}
+echo vscript "\$@"
+if sed 1q < \$1 | grep '^BugId:[ ]*[0-9][0-9]*$' > /dev/null; then
+ exit 0
+elif sed 1q < \$1 | grep '^BugId:[ ]*new$' > /dev/null; then
+ echo A new bugid was found. >> \$1
+ exit 0
+else
+ echo "No BugId found."
+ sleep 1
+ exit 1
+fi
+EOF
+ cat >${TESTDIR}/vscript2 <<EOF
+#!${TESTSHELL}
+echo vscript2 "\$@"
+if test -f CVS/Repository; then
+ repo=\`cat CVS/Repository\`
+else
+ repo=\`pwd\`
+fi
+echo \$repo
+if echo "\$repo" |grep yet-another/ >/dev/null 2>&1; then
+ exit 1
+else
+ exit 0
+fi
+EOF
+ # Grumble, grumble, mumble, search for "Cygwin".
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x ${TESTDIR}/vscript*"
+ else
+ chmod +x ${TESTDIR}/vscript*
+ fi
+ echo "^first-dir/yet-another\\(/\\|\$\\) ${TESTDIR}/vscript2 %l
%{sV}" >verifymsg
+ echo "^first-dir\\(/\\|\$\\) ${TESTDIR}/vscript %l %{sV}" >>verifymsg
+ echo "^missing-script\$ ${TESTDIR}/bogus %l" >>verifymsg
+ echo "^missing-var\$ ${TESTDIR}/vscript %l \${=Bogus}" >>verifymsg
+ # first test the directory independant verifymsg
+ dotest info-v1 "${testcvs} -q ci -m add-verification" \
+"$CVSROOT_DIRNAME/CVSROOT/verifymsg,v <-- verifymsg
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ../first-dir
+ echo line2 >>file1
+ dotest_fail info-v2 "${testcvs} -q ci -m bogus" \
+"vscript $tempname file1 1\.3
+No BugId found\.
+${SPROG} \[commit aborted\]: Message verification failed"
+
+ cat >${TESTDIR}/comment.tmp <<EOF
+BugId: 42
+and many more lines after it
+EOF
+ dotest info-v3 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \
+"vscript $tempname file1 1\.3
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3"
+ rm ${TESTDIR}/comment.tmp
+
+ cd ..
+ mkdir another-dir
+ cd another-dir
+ touch file2
+ dotest_fail info-v4 \
+ "${testcvs} import -m bogus first-dir/another x y" \
+"vscript $tempname - Imported sources NONE
+No BugId found\.
+${SPROG} \[import aborted\]: Message verification failed"
+
+ # now verify that directory dependent verifymsgs work
+ dotest info-v5 \
+ "${testcvs} import -m bogus first-dir/yet-another x y" \
+"vscript2 $tempname - Imported sources NONE
+$TESTDIR/wnt/another-dir
+N first-dir/yet-another/file2
+
+No conflicts created by this import" \
+"vscript2 $tempname - Imported sources NONE
+$CVSROOT_DIRNAME/first-dir/yet-another
+N first-dir/yet-another/file2
+
+No conflicts created by this import"
+
+ # FIXCVS
+ #
+ # note that in the local case the error message is the same as
+ # info-v5
+ #
+ # This means that the verifymsg scripts cannot reliably and
+ # consistantly obtain information on which directory is being
+ # committed to. Thus it is currently useless for them to be
+ # running in every dir. They should either be run once or
+ # directory information should be passed.
+ if $remote; then
+ dotest_fail info-v6r \
+ "${testcvs} import -m bogus first-dir/yet-another/and-another x
y" \
+"vscript2 $tempname - Imported sources NONE
+$CVSROOT_DIRNAME/first-dir/yet-another/and-another
+$SPROG \[import aborted\]: Message verification failed"
+ else
+ dotest info-v6 \
+ "${testcvs} import -m bogus first-dir/yet-another/and-another x
y" \
+"vscript2 $tempname - Imported sources NONE
+$TESTDIR/wnt/another-dir
+N first-dir/yet-another/and-another/file2
+
+No conflicts created by this import"
+ fi
+
+ # check that errors invoking the script cause verification failure
+ #
+ # The second text below occurs on Cygwin, where I assume execvp
+ # does not return to let CVS print the error message when its
+ # argument does not exist.
+ dotest_fail info-v7 "${testcvs} import -m bogus missing-script x y" \
+"${SPROG} import: cannot exec ${TESTDIR}/bogus: No such file or directory
+${SPROG} \[import aborted\]: Message verification failed" \
+"${SPROG} \[import aborted\]: Message verification failed"
+
+ dotest_fail info-v8 "${testcvs} import -m bogus missing-var x y" \
+"${SPROG} import: verifymsg:4: no such user variable \${=Bogus}
+${SPROG} \[import aborted\]: Message verification failed"
+
+ rm file2
+ cd ..
+ rmdir another-dir
+
+ cd CVSROOT
+ echo "RereadLogAfterVerify=always" >>config
+ dotest info-rereadlog-1 "${testcvs} -q ci -m
add-RereadLogAfterVerify=always" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../first-dir
+ echo line3 >>file1
+ cat >${TESTDIR}/comment.tmp <<EOF
+BugId: new
+See what happens next.
+EOF
+ dotest info-reread-2 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \
+"vscript $tempname file1 1\.4
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4"
+ dotest info-reread-3 "${testcvs} -q log -N -r1.5 file1" "
+.*
+BugId: new
+See what happens next.
+A new bugid was found.
+============================================================================="
+
+ cd ../CVSROOT
+ grep -v "RereadLogAfterVerify" config > config.new
+ mv config.new config
+ echo "RereadLogAfterVerify=stat" >>config
+ dotest info-reread-4 \
+"$testcvs -q ci -m add-RereadLogAfterVerify=stat" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../first-dir
+ echo line4 >>file1
+ cat >${TESTDIR}/comment.tmp <<EOF
+BugId: new
+See what happens next with stat.
+EOF
+ dotest info-reread-5 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \
+"vscript $tempname file1 1\.5
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.6; previous revision: 1\.5"
+ dotest info-reread-6 "${testcvs} -q log -N -r1.6 file1" "
+.*
+BugId: new
+See what happens next with stat.
+A new bugid was found.
+============================================================================="
+
+ cd ../CVSROOT
+ grep -v "RereadLogAfterVerify" config > config.new
+ mv config.new config
+ echo "RereadLogAfterVerify=never" >>config
+ dotest info-reread-7 \
+"$testcvs -q ci -m add-RereadLogAfterVerify=never" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../first-dir
+ echo line5 >>file1
+ cat >${TESTDIR}/comment.tmp <<EOF
+BugId: new
+See what happens next.
+EOF
+ dotest info-reread-8 "${testcvs} -q ci -F ${TESTDIR}/comment.tmp" \
+"vscript $tempname file1 1\.6
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.7; previous revision: 1\.6"
+ dotest info-reread-6 "${testcvs} -q log -N -r1.7 file1" "
+.*
+BugId: new
+See what happens next.
+============================================================================="
+
+ cd ../CVSROOT
+ dotest info-reread-cleanup-1 "$testcvs -q up -prinfo-start config
>config"
+ # Append the NULL format string until we remove the deprecation
+ # warning for lack of format strings.
+ echo 'DEFAULT false %n' >verifymsg
+ echo 'DEFAULT true %n' >>verifymsg
+ dotest info-multdef "${testcvs} -q ci -m multdef" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$CVSROOT_DIRNAME/CVSROOT/verifymsg,v <-- verifymsg
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ../CVSROOT
+ dotest info-reread--cleanup-1 \
+"$testcvs -q up -prinfo-start verifymsg >verifymsg"
+ dotest info-cleanup-verifymsg "$testcvs -q ci -m nuke-verifymsg" \
+"$SPROG commit: Multiple .DEFAULT. lines (1 and 2) in verifymsg file
+$CVSROOT_DIRNAME/CVSROOT/verifymsg,v <-- verifymsg
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ dokeep
+ rm ${TESTDIR}/vscript*
+ cd ..
+
+ dotest info-cleanup-0 "$testcvs -n release -d CVSROOT" \
+"You have \[0\] altered files in this repository\."
+
+ dotest info-cleanup-1 \
+"echo yes |${testcvs} -q release -d CVSROOT >/dev/null"
+ dotest info-cleanup-2 \
+"echo yes |${testcvs} -q release -d first-dir >/dev/null"
+ dotest info-cleanup-3 \
+"echo yes |${testcvs} -q release -d third-dir >/dev/null"
+ dotest info-cleanup-4 \
+"echo yes |${testcvs} -q release -d fourth-dir >/dev/null"
+
+ dokeep
+ cd ..
+ rm -r wnt
+ rm $HOME/.cvsrc
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/third-dir \
+ $CVSROOT_DIRNAME/fourth-dir
+ ;;
+
+
+
+ taginfo)
+ # Tests of the CVSROOT/taginfo file. See the comment at the
+ # "info" tests for a full list of administrative file tests.
+
+ # all the oldfmt stuff can come out once we finish deprecating
+ # the old info file command line format stuff.
+ #
+ # grep the code for SUPPORT_OLD_INFO_FMT_STRINGS and see the stuff
+ # in configure.in about oldinfoformatsupport
+
+ mkdir 1; cd 1
+ dotest taginfo-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+ cd CVSROOT
+ dotest taginfo-init-2 "$testcvs -Q tag taginfo-start"
+ cat >$TESTDIR/1/loggit <<EOF
+#!$TESTSHELL
+if test "\$1" = rejectme; then
+ exit 1
+else
+ echo "\$@" >>$TESTDIR/1/taglog
+ exit 0
+fi
+EOF
+ # #^@&!^@ Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x ${TESTDIR}/1/loggit"
+ else
+ chmod +x ${TESTDIR}/1/loggit
+ fi
+ echo "ALL ${TESTDIR}/1/loggit" >>taginfo
+ sed -e's/^UseNewInfoFmtStrings=yes$/#&/' <config >tmpconfig
+ mv tmpconfig config
+ sed -e's/%p/ALL/' <loginfo >tmploginfo
+ mv tmploginfo loginfo
+ dotest taginfo-2 "${testcvs} -q ci -m check-in-taginfo" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$CVSROOT_DIRNAME/CVSROOT/loginfo,v <-- loginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$CVSROOT_DIRNAME/CVSROOT/taginfo,v <-- taginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+
+ # taginfo-3 used to rely on the top-level CVS directory
+ # being created to add "first-dir" to the repository. Since
+ # that won't happen anymore, we create the directory in the
+ # repository.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest taginfo-3 "$testcvs -q co first-dir"
+
+ cd first-dir
+ echo first >file1
+ dotest taginfo-4 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest taginfo-5 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary '1' in
+all info files after each '%' which doesn't represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+ dotest taginfo-6 "${testcvs} -q tag tag1" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+T file1"
+ dotest taginfo-7 "${testcvs} -q tag -b br" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+T file1"
+ dotest taginfo-8 "$testcvs -q update -r br"
+ echo add text on branch >>file1
+ dotest taginfo-9 "${testcvs} -q ci -m modify-on-br" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary '1' in
+all info files after each '%' which doesn't represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+ dotest taginfo-10 "${testcvs} -q tag -F -c brtag" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+T file1"
+
+ dotest_fail taginfo-11 "${testcvs} -q tag rejectme" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+${SPROG} tag: Pre-tag check failed
+${SPROG} \[tag aborted\]: correct the above errors first!"
+
+ # When we are using taginfo to allow/disallow, it would be
+ # convenient to be able to use "cvs -n tag" to test whether
+ # the allow/disallow functionality is working as expected.
+ dotest taginfo-12 "${testcvs} -nq tag rejectme" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+T file1"
+
+ # But when taginfo is used for logging, it is a pain for -n
+ # to call taginfo, since taginfo doesn't know whether -n was
+ # specified or not.
+ dotest taginfo-13 "${testcvs} -nq tag would-be-tag" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+T file1"
+
+ # Deleting: the cases are basically either the tag existed,
+ # or it didn't exist.
+ dotest taginfo-14 "${testcvs} -q tag -d tag1" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\.
+D file1"
+ dotest taginfo-15 "${testcvs} -q tag -d tag1" \
+"${SPROG} tag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\."
+
+ # Likewise with rtag.
+ dotest taginfo-16 "${testcvs} -q rtag tag1 first-dir" \
+"${SPROG} rtag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\."
+ dotest taginfo-17 "${testcvs} -q rtag -d tag1 first-dir" \
+"${SPROG} rtag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\."
+ dotest taginfo-18 "${testcvs} -q rtag -d tag1 first-dir" \
+"${SPROG} rtag: warning: taginfo line contains no format strings:
+ \"${TESTDIR}/1/loggit\"
+Filling in old defaults ('%t %o %p %{sv}'), but please be aware that this
+usage is deprecated\."
+
+ # The "br" example should be passing 1.1.2 or 1.1.0.2.
+ # But it turns out that is very hard to implement, since
+ # check_fileproc doesn't know what branch number it will
+ # get. Probably the whole thing should be re-architected
+ # so that taginfo only allows/denies tagging, and a new
+ # hook, which is done from tag_fileproc, does logging.
+ # That would solve this, some more subtle races, and also
+ # the fact that it is nice for users to run "-n tag foo" to
+ # see whether a tag would be allowed. Failing that,
+ # I suppose passing "1.1.branch" or "branch" for "br"
+ # would be an improvement.
+ dotest taginfo-examine-1 "cat ${TESTDIR}/1/taglog" \
+"tag1 add first-dir file1 1\.1
+br add first-dir file1 1\.1
+brtag mov first-dir file1 1\.1\.2\.1
+tag1 del first-dir file1 1\.1
+tag1 del first-dir
+tag1 add first-dir file1 1\.1
+tag1 del first-dir file1 1\.1
+tag1 del first-dir"
+
+ # now that we've tested the default operation, try a new
+ # style fmt string.
+ rm $TESTDIR/1/taglog
+ cd ..
+ cd CVSROOT
+ dotest taginfo-newfmt-init-1 \
+"$testcvs -q up -prtaginfo-start taginfo >taginfo"
+ echo "ALL $TESTDIR/1/loggit %r %t %o %b %p %{sTVv}" >>taginfo
+ dotest taginfo-newfmt-init-2 "$testcvs -q ci -m check-in-taginfo" \
+"$TESTDIR/cvsroot/CVSROOT/taginfo,v <-- taginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database" \
+"$TESTDIR/cvsroot/CVSROOT/taginfo,v <-- taginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary '1' in
+all info files after each '%' which doesn't represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+
+ cat >${TESTDIR}/1/loggit <<EOF
+#!${TESTSHELL}
+if test "\$1" = rejectme; then
+ exit 1
+else
+ while test "\$#" -gt 0; do
+ echo "\$1" >>${TESTDIR}/1/taglog
+ shift
+ done
+ exit 0
+fi
+EOF
+
+ cd ..
+ cd first-dir
+ dotest taginfo-newfmt-2 "${testcvs} -q update -A" "U file1"
+ echo "bull pucky" >'file 2'
+ dotest taginfo-newfmt-2b "${testcvs} add 'file 2'" \
+"${SPROG} add: scheduling file .file 2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest taginfo-newfmt-2c "$testcvs -q ci -m add-it" \
+"$TESTDIR/cvsroot/first-dir/file 2,v <-- file 2
+initial revision: 1\.1" \
+"$TESTDIR/cvsroot/first-dir/file 2,v <-- file 2
+initial revision: 1\.1
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary '1' in
+all info files after each '%' which doesn't represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+
+ dotest taginfo-newfmt-3 "${testcvs} -q tag tag1" \
+"T file 2
+T file1"
+ dotest taginfo-newfmt-4 "${testcvs} -q tag tag3" \
+"T file 2
+T file1"
+ dotest taginfo-newfmt-5 "$testcvs -q tag -rtag1 tag4" \
+"T file 2
+T file1"
+
+ dotest taginfo-newfmt-examine-1 "cat ${TESTDIR}/1/taglog" \
+"$TESTDIR/cvsroot
+tag1
+add
+N
+first-dir
+file 2
+
+NONE
+1\.1
+file1
+
+NONE
+1\.1
+$TESTDIR/cvsroot
+tag3
+add
+N
+first-dir
+file 2
+
+NONE
+1\.1
+file1
+
+NONE
+1\.1
+$TESTDIR/cvsroot
+tag4
+add
+N
+first-dir
+file 2
+tag1
+NONE
+1\.1
+file1
+tag1
+NONE
+1\.1"
+
+ # now update to use the new format strings (really, disable support
+ # of the old format) and run the whole gamut of tests again.
+ rm ${TESTDIR}/1/taglog
+ cd ..
+ cd CVSROOT
+ cat >${TESTDIR}/1/loggit <<EOF
+#!${TESTSHELL}
+if test "\$1" = rejectme; then
+ exit 1
+else
+ echo "\$@" >>${TESTDIR}/1/taglog
+ exit 0
+fi
+EOF
+ dotest taginfo-newfmt-init-7 \
+"$testcvs -q up -prtaginfo-start taginfo >taginfo"
+ echo "ALL ${TESTDIR}/1/loggit %{t} %b %{o} %p %{sTVv}" >>taginfo
+ echo "UseNewInfoFmtStrings=yes" >>config
+ dotest taginfo-newfmt-7 "$testcvs -q ci -m check-in-taginfo" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$TESTDIR/cvsroot/CVSROOT/taginfo,v <-- taginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database" \
+"$TESTDIR/cvsroot/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$TESTDIR/cvsroot/CVSROOT/taginfo,v <-- taginfo
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database
+$SPROG commit: warning: Set to use deprecated info format strings\. Establish
+compatibility with the new info file format strings (add a temporary '1' in
+all info files after each '%' which doesn't represent a literal percent)
+and set UseNewInfoFmtStrings=yes in CVSROOT/config\. After that, convert
+individual command lines and scripts to handle the new format at your
+leisure\."
+
+ cd ../first-dir
+ dotest taginfo-newfmt-8 "${testcvs} -q tag tag1" ""
+ mkdir sdir
+ dotest taginfo-newfmt-8b "${testcvs} -q add sdir" \
+"Directory ${TESTDIR}/cvsroot/first-dir/sdir added to the repository"
+ touch sdir/file3
+ dotest taginfo-newfmt-8c "${testcvs} -q add sdir/file3" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest taginfo-newfmt-8d "${testcvs} -q ci -m added-sdir" \
+"${TESTDIR}/cvsroot/first-dir/sdir/file3,v <-- sdir/file3
+initial revision: 1\.1"
+ dotest taginfo-newfmt-9 "${testcvs} -q tag -b br" \
+"T file 2
+W file1 : br already exists on branch 1\.1\.2\.1 : NOT MOVING tag to branch
1\.1\.0\.4
+T sdir/file3"
+ dotest taginfo-newfmt-10 "${testcvs} -q update -r br" "U file1"
+ echo add more text on branch >>file1
+ dotest taginfo-newfmt-11 "${testcvs} -q ci -m modify-on-br" \
+"${TESTDIR}/cvsroot/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1"
+ dotest taginfo-newfmt-12 "${testcvs} -q tag -F -c brtag" \
+"T file 2
+T file1
+T sdir/file3"
+
+ # we are being called once for each directory. I'm not sure
+ # I like this, but I'm also not sure how hard it would be to change,
+ # It seems like it would be more trouble than it is really worth
+ # to let a partial tag go through...
+ dotest_fail taginfo-newfmt-13 "${testcvs} -q tag rejectme" \
+"${SPROG} tag: Pre-tag check failed
+${SPROG} tag: Pre-tag check failed
+${SPROG} \[tag aborted\]: correct the above errors first!"
+
+ # When we are using taginfo to allow/disallow, it would be
+ # convenient to be able to use "cvs -n tag" to test whether
+ # the allow/disallow functionality is working as expected.
+ # see the comment before taginfo-newfmt-15 for notes on
+ # pretag and posttag proc
+ dotest taginfo-newfmt-14 "${testcvs} -nq tag rejectme" \
+"T file 2
+T file1
+T sdir/file3"
+
+ # But when taginfo is used for logging, it is a pain for -n
+ # to call taginfo, since taginfo doesn't know whether -n was
+ # specified or not. (this could be fixed pretty easily now
+ # with a new fmt string. i suppose it would be better to
+ # have a pretag proc and a posttag proc, though.)
+ dotest taginfo-newfmt-15 "${testcvs} -nq tag would-be-tag" \
+"T file 2
+T file1
+T sdir/file3"
+
+ # Deleting: the cases are basically either the tag existed,
+ # or it didn't exist.
+ dotest taginfo-newfmt-16 "${testcvs} -q tag -d tag1" \
+"D file 2
+D file1"
+ dotest taginfo-newfmt-17 "${testcvs} -q tag -d tag1" ""
+
+ # Likewise with rtag.
+ dotest taginfo-newfmt-18 "${testcvs} -q rtag tag1 first-dir" ""
+ dotest taginfo-newfmt-19 "${testcvs} -q rtag -d tag1 first-dir" ""
+ dotest taginfo-newfmt-20 "${testcvs} -q rtag -d tag1 first-dir" ""
+
+ # The "br" example should be passing 1.1.2 or 1.1.0.2.
+ # But it turns out that is very hard to implement, since
+ # check_fileproc doesn't know what branch number it will
+ # get. Probably the whole thing should be re-architected
+ # so that taginfo only allows/denies tagging, and a new
+ # hook, which is done from tag_fileproc, does logging.
+ # That would solve this, some more subtle races, and also
+ # the fact that it is nice for users to run "-n tag foo" to
+ # see whether a tag would be allowed. Failing that,
+ # I suppose passing "1.1.branch" or "branch" for "br"
+ # would be an improvement.
+ dotest taginfo-newfmt-examine-2 "cat ${TESTDIR}/1/taglog" \
+"tag1 N add first-dir
+br T add first-dir file 2 NONE 1\.1
+br T add first-dir/sdir file3 NONE 1\.1
+brtag N mov first-dir file 2 br NONE 1\.1 file1 br 1\.1\.2\.1 1\.1\.2\.2
+brtag N mov first-dir/sdir file3 br NONE 1\.1
+tag1 ? del first-dir file 2 br 1\.1 1\.1 file1 br 1\.1 1\.1
+tag1 ? del first-dir/sdir
+tag1 ? del first-dir
+tag1 ? del first-dir/sdir
+tag1 N add first-dir file 2 NONE 1\.1 file1 NONE 1\.1
+tag1 N add first-dir/sdir file3 NONE 1\.1
+tag1 ? del first-dir file 2 1\.1 1\.1 file1 1\.1 1\.1
+tag1 ? del first-dir/sdir file3 1\.1 1\.1
+tag1 ? del first-dir
+tag1 ? del first-dir/sdir"
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ posttag)
+ # Tests of the CVSROOT/taginfo file. See the comment at the
+ # "info" tests for a full list of administrative file tests.
+
+ mkdir 1; cd 1
+
+ dotest posttag-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+
+ # now that we've tested the default operation, try a new
+ # style fmt string.
+ cd CVSROOT
+ echo "ALL $TESTDIR/1/loggit %r %t %o %b %p %{sVv}" >posttag
+ dotest posttag-init-2 "$testcvs -q ci -m check-in-taginfo" \
+"$TESTDIR/cvsroot/CVSROOT/posttag,v <-- posttag
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ..
+
+ cat >$TESTDIR/1/loggit <<EOF
+#!$TESTSHELL
+if test "\$1" = rejectme; then
+ error=:
+else
+ error=false
+fi
+
+while [ -n "\$1" ]; do
+ echo "\$1" >>$TESTDIR/1/taglog
+ shift
+done
+
+if \$error; then
+ exit 1
+fi
+exit 0
+EOF
+ # #^@&!^@ Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x $TESTDIR/1/loggit"
+ else
+ chmod +x $TESTDIR/1/loggit
+ fi
+
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest posttag-init-3 "$testcvs -q co first-dir"
+
+ cd first-dir
+ echo first >file1
+ echo "bull pucky" >'file 2'
+ dotest posttag-init-4 "$testcvs add file1 'file 2'" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: scheduling file \`file 2' for addition
+$SPROG add: use \`$SPROG commit' to add these files permanently"
+ dotest posttag-init-5 "$testcvs -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file 2,v <-- file 2
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ dotest posttag-1 "$testcvs -q tag tag1" \
+"T file 2
+T file1"
+ dotest posttag-2 "$testcvs -q tag tag3" \
+"T file 2
+T file1"
+
+ dotest posttag-3 "$testcvs -q tag rejectme" \
+"T file 2
+T file1"
+
+ dotest posttag-4 "$testcvs -q tag -d rejectme" \
+"D file 2
+D file1"
+
+ dotest posttag-examine-1 "cat $TESTDIR/1/taglog" \
+"$TESTDIR/cvsroot
+tag1
+add
+N
+first-dir
+file 2
+NONE
+1\.1
+file1
+NONE
+1\.1
+$TESTDIR/cvsroot
+tag3
+add
+N
+first-dir
+file 2
+NONE
+1\.1
+file1
+NONE
+1\.1
+$TESTDIR/cvsroot
+rejectme
+add
+N
+first-dir
+file 2
+NONE
+1.1
+file1
+NONE
+1.1
+$TESTDIR/cvsroot
+rejectme
+del
+?
+first-dir
+file 2
+1.1
+1.1
+file1
+1.1
+1.1"
+
+ dokeep
+ cd ../..
+ restore_adm
+ rm -r 1
+ modify_repo 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.
+
+ # See note in keywordexpand about config errors from a proxied
+ # primary.
+ if $noredirect; then
+ notnoredirect config
+ continue
+ fi
+
+ # On Windows, we can't check out CVSROOT, because the case
+ # insensitivity means that this conflicts with cvsroot.
+ mkdir wnt
+ cd wnt
+
+ dotest config-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+ cd CVSROOT
+ dotest config-init-2 "$testcvs -Q tag config-start"
+ echo 'bogus line' >>config
+ dotest config-3 "$testcvs -q ci -m change-to-bogus-line" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ dotest config-3a "$testcvs -Q update -jHEAD -jconfig-start" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
syntax error: missing \`=' between keyword and value
+RCS file: $CVSROOT_DIRNAME/CVSROOT/config,v
+retrieving revision 1.[0-9]*
+retrieving revision 1.[0-9]*
+Merging differences between 1.[0-9]* and 1.[0-9]* into config"
+ echo 'BogusOption=yes' >>config
+ if $proxy; then
+ dotest config-4p "$testcvs -q ci -m change-to-bogus-opt" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[99\]: syntax
error: missing \`=' between keyword and value
+$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[99\]: syntax error: missing
\`=' between keyword and value
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ else
+ dotest config-4 "$testcvs -q ci -m change-to-bogus-opt" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[1-9][0-9]*\]:
syntax error: missing \`=' between keyword and value
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ fi
+
+ if $proxy; then
+ : # FIXME: don't try in proxy mode
+ else
+ # Now test the HistoryLogPath and HistorySearchPath options.
+ mkdir $TESTDIR/historylogs
+ echo >config \
+ 'HistoryLogPath=$CVSROOT/../historylogs/%Y-%m-%d-%H-%M-%S'
+ echo 'HistorySearchPath=$CVSROOT/../historylogs/*' >>config
+
+ # The warning is left over from the previous test.
+ dotest config-5 "$testcvs -q ci -m set-HistoryLogPath" \
+"$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[98\]: unrecognized keyword
\`BogusOption'
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ echo '# noop' >> config
+ dotest config-6 "$testcvs -q ci -mlog-commit" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ sleep 1
+ echo '# noop' >> config
+ dotest config-7 "$testcvs -q ci -mlog-commit" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ # The log entry was intentionally split across multiple files.
+ dotest config-8 "ls -l $TESTDIR/historylogs/*" \
+"-rw-rw-r--.*$TESTDIR/historylogs/2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]-[0-2][0-9]-[0-5][0-9]-[0-5][0-9]
+-rw-rw-r--.*$TESTDIR/historylogs/2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]-[0-2][0-9]-[0-5][0-9]-[0-5][0-9]"
+
+ # Should still see both commits.
+ if $remote; then
+ dotest config-9r "$testcvs history -ea" \
+"M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT == <remote>
+M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT == <remote>"
+ else
+ dotest config-9 "$testcvs history -ea" \
+"M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT ==
$TESTDIR/wnt/CVSROOT
+M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT ==
$TESTDIR/wnt/CVSROOT"
+ fi
+
+ # Remove this now to see what kind of error messages we get.
+ rm -r $TESTDIR/historylogs
+ fi
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r wnt
+ ;;
+
+
+
+ config2)
+ # Tests of the CVSROOT/config file. See the comment at the
+ # "info" tests for a full list of administrative file tests.
+
+ # No point in testing compression effects in local mode.
+ if $remote; then :; else
+ remoteonly config2
+ continue
+ fi
+
+ # On Windows, we can't check out CVSROOT, because the case
+ # insensitivity means that this conflicts with cvsroot.
+ mkdir wnt
+ cd wnt
+
+ # Set MinCompressionLevel and MaxCompressionLevel in config.
+ dotest config2-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+ dotest config2-init-1b "$testcvs -Q tag initial"
+ cd CVSROOT
+ cat << EOF >> config
+MinCompressionLevel=5
+MaxCompressionLevel=6
+EOF
+ dotest config2-init-2 \
+"$testcvs -q ci -m set-compression-constraints" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ # Verify that the server reports forcing compression to an allowed
+ # level.
+
+ # Too high.
+ dotest config2-1 "$testcvs -z9 update" \
+"$SPROG server: Forcing compression level 6 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+ # Too low.
+ dotest config2-2 "$testcvs -z1 update" \
+"$SPROG server: Forcing compression level 5 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+ # From zero.
+ dotest config2-3 "$testcvs update" \
+"$SPROG server: Forcing compression level 5 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+ # Just right.
+ dotest config2-3 "$testcvs -z5 update" \
+"$SPROG update: Updating \."
+
+ # Check that compression may be forced to 0.
+ dotest config2-init-2b "$testcvs -z5 up -jHEAD -jinitial" "$DOTSTAR"
+ cat << EOF >> config
+MaxCompressionLevel=0
+EOF
+ dotest config2-init-3 "$testcvs -qz5 ci -m no-compression" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ # Too high.
+ dotest config2-5 "$testcvs -z9 update" \
+"$SPROG server: Forcing compression level 0 (allowed: 0 <= z <= 0)\.
+$SPROG update: Updating \."
+ # Just right.
+ dotest config2-6 "$testcvs update" \
+"$SPROG update: Updating \."
+
+ # And verify effect without restrictions.
+ dotest config2-init-3b "$testcvs up -jHEAD -jinitial" "$DOTSTAR"
+ dotest config2-init-4 "$testcvs -q ci -m change-to-comment" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ dotest config2-7 "$testcvs update" \
+"$SPROG update: Updating \."
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r wnt
+ ;;
+
+ config3)
+ # Verify comments, white space, & [rootspecs] in CVSROOT/config
+ #
+ # `cvs server' `-c' option tested in `server' test
+ modify_repo mkdir $CVSROOT_DIRNAME/config3
+ mkdir config3
+ cd config3
+
+ dotest config3-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+ cd CVSROOT
+
+ # I break the usual sanity.sh indentation standard for here-docs
+ # mostly to test that leading white-space is now ignored.
+ dotest config3-init-1b "$testcvs -Q tag initial-config"
+
+ cat <<EOF >>config
+ # Ignore a comment with leading spaces.
+ GLOBAL-BAD-OPTION=WWW
+
+ [/ignore/this/root]
+ [/and/this/one]
+ IGNORED-BAD-OPTION=YYY
+EOF
+ dotest config3-init-2 \
+"$testcvs -q ci -m test-root-specs" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ..
+ dotest config3-1 "$testcvs co config3" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]:
unrecognized keyword \`GLOBAL-BAD-OPTION'
+$SPROG checkout: Updating config3"
+
+ cd CVSROOT
+ dotest config3-init-2a "$testcvs -Q up -jHEAD -jinitial-config" \
+"$DOTSTAR
+Merging differences between 1\.[0-9]* and 1\.[0-9]* into config"
+
+ cat <<EOF >>config
+ # Ignore a comment with leading spaces.
+
+ [/ignore/this/root]
+ [/and/this/one]
+ IGNORED-BAD-OPTION=YYY
+ # Ignore a comment with leading spaces.
+
+ [/some/other/root]
+
+ # Comments and blank lines do not affect fall-through behavior.
+
+ [$CVSROOT_DIRNAME]
+ [$SECONDARY_CVSROOT_DIRNAME]
+
+ # Comments and blank lines do not affect fall-through behavior.
+
+ [/yet/another/root]
+ # Ignore a comment with leading spaces.
+ PROCESS-BAD-OPTION=XXX
+EOF
+ dotest config3-init-3 \
+"$testcvs -q ci -m test-root-specs" \
+"$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]: unrecognized
keyword \`GLOBAL-BAD-OPTION'
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]:
unrecognized keyword \`GLOBAL-BAD-OPTION'
+$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]: unrecognized
keyword \`GLOBAL-BAD-OPTION'
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ..
+ dotest config3-2 "$testcvs co config3" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]:
unrecognized keyword \`PROCESS-BAD-OPTION'
+$SPROG checkout: Updating config3"
+
+ # The next few tests make sure both global options and root
+ # specific options are processed by setting the history log and
+ # search paths in different locations and then verifying that
+ # both registered. It also verifies that a key for a different
+ # root is ignored.
+ cd CVSROOT
+ dotest config3-init-3a "$testcvs -Q up -jHEAD -jinitial-config" \
+"$DOTSTAR
+Merging differences between 1\.[0-9]* and 1\.[0-9]* into config"
+
+ cat <<EOF >>config
+ HistoryLogPath=$TESTDIR/historylog
+
+ [/ignore/this/root]
+ [/and/this/one]
+ IGNORED-BAD-OPTION=YYY
+
+ [/some/other/root]
+ [$CVSROOT_DIRNAME]
+ [$SECONDARY_CVSROOT_DIRNAME]
+ [/yet/another/root]
+ HistorySearchPath=$TESTDIR/historylog
+
+ [/ignore/another/root]
+ [/and/this/one/too]
+ ANOTHER-IGNORED-BAD-OPTION=ZZZ
+
+ [$CVSROOT_DIRNAME]
+ [$SECONDARY_CVSROOT_DIRNAME]
+ LogHistory=TMAR
+EOF
+ dotest config3-init-4 \
+"$testcvs -q ci -m test-root-specs" \
+"$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]: unrecognized
keyword \`PROCESS-BAD-OPTION'
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database" \
+"$SPROG [a-z]*: $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]:
unrecognized keyword \`PROCESS-BAD-OPTION'
+$SPROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config \[[0-9]*\]: unrecognized
keyword \`PROCESS-BAD-OPTION'
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cd ..
+ dotest config3-3 "$testcvs co -d config3-2 config3" \
+"$SPROG checkout: Updating config3-2"
+
+ cd config3-2
+ touch newfile
+ dotest config3-4 "$testcvs -Q add newfile"
+ dotest config3-5 "$testcvs -q ci -madd-file" \
+"$CVSROOT_DIRNAME/config3/newfile,v <-- newfile
+initial revision: 1\.1"
+
+ dotest config3-6 "$testcvs rtag testtag config3" \
+"$SPROG rtag: Tagging config3"
+
+ cd ..
+ dotest config3-7 "$testcvs history -ea" \
+"A [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.1 newfile config3 ==
[-_/a-zA-Z0-9<>.]*
+T [0-9-]* [0-9:]* ${PLUS}0000 $username config3 \[testtag:A\]"
+
+ dokeep
+ restore_adm
+ cd ..
+ rm -r config3
+ modify_repo rm -rf $CVSROOT_DIRNAME/config3
+ ;;
+
+
+
+ config4)
+ # TmpDir
+ mkdir config4
+ cd config4
+
+ dotest config4-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+ cd CVSROOT
+ mkdir $TESTDIR/config4/tmp
+ echo "TmpDir=$TESTDIR/config4/tmp" >>config
+ echo "DEFAULT $TESTDIR/config4/verify %l" >>verifymsg
+ dotest config4-init-2 "$testcvs -q ci -m change-tmpdir" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$CVSROOT_DIRNAME/CVSROOT/verifymsg,v <-- verifymsg
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ cat >$TESTDIR/config4/verify <<EOF
+#! /bin/sh
+echo \$1
+exit 0
+EOF
+ chmod a+x $TESTDIR/config4/verify
+ dotest config4-1 \
+"$testcvs -q ci -fmtest-tmpdir config" \
+"$TESTDIR/config4/tmp/$tempfile
+$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r config4
+ modify_repo rm -rf $CVSROOT_DIRNAME/config4
+ ;;
+
+
+
+ compression)
+ # Try to reproduce some old compression buffer problems.
+
+ # No point in testing compression effects in local mode.
+ if $remote; then :; else
+ remoteonly config2
+ continue
+ fi
+
+ mkdir compression; cd compression
+ dotest compression-init1 "$testcvs -z6 -Q co -l -d toplevel ."
+ cd toplevel
+ mkdir compression
+ dotest compression-init2 "$testcvs -z6 -Q add compression"
+ cd ..
+
+ dotest compression-init3 "$testcvs -z6 -q co compression"
+ cd compression
+
+ # Want a big file
+ cat >big_file <<EOF
+a lot of data on a line to make a really big file once it is copied, copied,
+copied, the digital equivalent of a mile.
+EOF
+ # 1..14 creates about a 1MB file, the minimum required on the system
+ # I initially tested this on. 1..16 creates about a 4MB file.
+ for a in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+ cat big_file >tmp
+ cat big_file >>tmp
+ mv tmp big_file
+ done
+
+ dotest compression-1 "$testcvs -z6 -Q add big_file"
+
+ # The following command hung due to a bug in CVS 1.12.13. This was
+ # because the command that grabbed more data to uncompress from the
+ # underlying buffer in zlib.c:compress_buffer_input would try to read
+ # the amount of data actually needed by the caller, even though this
+ # should be shorter in the underlying buffer (since it is
+ # compressed). e.g., if the caller wanted 5 bytes, there may only be
+ # 3 bytes in the underlying buffer though it will uncompress to 5
+ # bytes, and a blocking read looking for 5 bytes will block
+ # indefinitely since the data will never become available.
+ #
+ # The only odd thing is that it worked at all, sometimes. For
+ # example, I needed big_file to be at least 1MB in size to reproduce
+ # this here (for a in 1..14 - I used 1..16 above just 4 good measure
+ # - this compresses to c. 26k and should handle up 2 a c. 16k pg sz).
+ # My guess is that this has something to do with the amount the file
+ # gets compressed and how much other data preceded it in the data
+ # stream - the current buffer read will read as much data as is
+ # available, up to the system page size, in blocking or nonblocking
+ # modes. So, when the compressed data is much less than the system
+ # page size, it is cached in full from previous reads and the
+ # blocking read request for more than the available data is never
+ # made. The smallest file I could reproduce this with above
+ # compressed to just under 7k, on a system with a 4k page size. In
+ # this case, the call to compress_buffer_input decompressed all the
+ # available buffered data, causing a read request for maybe half a
+ # megabyte, with only 3k left to read, and the server blocked
+ # waiting for the nonexistent data. The same could happen with a
+ # smaller file if its compressed data happened to cross a page
+ # boundry or if timing issues caused a similar effect.
+ dotest compression-2 "$testcvs -z6 -q ci -mForce-zerror." \
+"$CVSROOT_DIRNAME/compression/big_file,v <-- big_file
+initial revision: 1\.1"
+
+ dokeep
+ cd ../..
+ rm -r compression
+ modify_repo rm -rf $CVSROOT_DIRNAME/compression
+ ;;
+
+
+
+ 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.
+ modify_repo 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" \
+"$SPROG add: scheduling file \`file1' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+
+ dotest serverpatch-3 "${testcvs} -q commit -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ # Tag the file.
+ dotest serverpatch-4 "${testcvs} -q tag tag file1" 'T file1'
+
+ # Check out a tagged copy of the file.
+ cd ../..
+ mkdir 2
+ cd 2
+ dotest serverpatch-5 "${testcvs} -q co -r tag first-dir" \
+'U first-dir/file1'
+
+ # Remove the tag. This will leave the tag string in the
+ # expansion of the Name keyword.
+ dotest serverpatch-6 "${testcvs} -q update -A first-dir" ''
+
+ # Modify and check in the first copy.
+ cd ../1/first-dir
+ echo '2' >> file1
+ dotest serverpatch-7 "${testcvs} -q ci -mx file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+
+ # 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' \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo 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: logopt, rcs
+ # -s: logopt, rcs3
+ # -R: logopt, rcs3
+ # -w, -t: not tested yet (TODO)
+
+ # Check in a file with a few revisions and branches.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest log-1 "$testcvs -q co first-dir"
+ cd first-dir
+ echo 'first revision' > file1
+ echo 'first revision' > file2
+ dotest log-2 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ rm -f ${TESTDIR}/comment.tmp
+
+ echo 'second revision' > file1
+ echo 'second revision' > file2
+ dotest log-4 "${testcvs} -q ci -m2 file1 file2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.2; previous revision: 1\.1"
+
+ dotest log-5 "${testcvs} -q tag -b branch file1" 'T file1'
+ dotest log-5a "${testcvs} -q tag tag1 file2" 'T file2'
+
+ echo 'third revision' > file1
+ echo 'third revision' > file2
+ dotest log-6 "${testcvs} -q ci -m3 file1 file2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.3; previous revision: 1\.2"
+
+ dotest log-6a "${testcvs} -q tag tag2 file2" 'T file2'
+
+ dotest log-7 "${testcvs} -q update -r branch" \
+"U file1
+${SPROG} update: \`file2' is no longer in the repository"
+
+ echo 'first branch revision' > file1
+ dotest log-8 "${testcvs} -q ci -m1b file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2\.2\.1; previous revision: 1\.2"
+
+ dotest log-9 "${testcvs} -q tag tag file1" 'T file1'
+
+ echo 'second branch revision' > file1
+ dotest log-10 "${testcvs} -q ci -m2b file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1"
+
+ # Set up a bunch of shell variables to make the later tests
+ # easier to describe.=
+ log_header1="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.3
+branch:
+locks: strict
+access list:"
+ rlog_header1="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+head: 1\.3
+branch:
+locks: strict
+access list:"
+ log_tags1='symbolic names:
+ tag: 1\.2\.2\.1
+ branch: 1\.2\.0\.2'
+ log_keyword='keyword substitution: kv'
+ log_dash='----------------------------
+revision'
+ log_date="date: ${ISO8601DATE}; author: ${username}; state: Exp;"
+ log_lines=" lines: ${PLUS}1 -1;"
+ log_commitid=" commitid: ${commitid};"
+ log_rev1="${log_dash} 1\.1
+${log_date}${log_commitid}
+line 1
+
+line 2"
+ log_rev2="${log_dash} 1\.2
+${log_date}${log_lines}${log_commitid}
+branches: 1\.2\.2;
+2"
+ log_rev3="${log_dash} 1\.3
+${log_date}${log_lines}${log_commitid}
+3"
+ log_rev1b="${log_dash} 1\.2\.2\.1
+${log_date}${log_lines}${log_commitid}
+1b"
+ log_rev2b="${log_dash} 1\.2\.2\.2
+${log_date}${log_lines}${log_commitid}
+2b"
+
log_trailer='============================================================================='
+
+ # Now, finally, test the log output.
+
+ dotest log-11 "${testcvs} log file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-12 "${testcvs} log -N file1" \
+"${log_header1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-13 "${testcvs} log -b file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 3
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-14 "${testcvs} log -r file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest log-14a "${testcvs} log -rHEAD file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ # The user might not realize that "-r" must not take a space.
+ # In the error message, HEAD is a file name, not a tag name (which
+ # might be confusing itself).
+ dotest_fail log-14b "${testcvs} log -r HEAD file1" \
+"${SPROG} log: nothing known about HEAD
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+# Check that unusual syntax works correctly.
+
+ dotest log-14c "${testcvs} log -r: file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-14d "${testcvs} log -r, file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-14e "${testcvs} log -r. file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-14f "${testcvs} log -r:: file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-15 "${testcvs} log -r1.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2}
+${log_trailer}"
+
+ dotest log-16 "${testcvs} log -r1.2.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ # This test would fail with the old invocation of rlog, but it
+ # works with the builtin log support.
+ dotest log-17 "${testcvs} log -rbranch file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-18 "${testcvs} log -r1.2.2. file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2b}
+${log_trailer}"
+
+ # Multiple -r options are undocumented; see comments in
+ # cvs.texinfo about whether they should be deprecated.
+ dotest log-18a "${testcvs} log -r1.2.2.2 -r1.3:1.3 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2b}
+${log_trailer}"
+
+ # This test would fail with the old invocation of rlog, but it
+ # works with the builtin log support.
+ dotest log-19 "${testcvs} log -rbranch. file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2b}
+${log_trailer}"
+
+ dotest log-20 "${testcvs} log -r1.2: file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+
+ dotest log-20a "${testcvs} log -r1.2:: file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest log-21 "${testcvs} log -r:1.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-21a "${testcvs} log -r::1.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-22 "${testcvs} log -r1.1:1.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-22a "${testcvs} log -r1.1::1.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2}
+${log_trailer}"
+
+ dotest log-22b "${testcvs} log -r1.1::1.3 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+
+ dotest log-23 "${testcvs} log -rfoo:: file1" \
+"${SPROG} log: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-24 "${testcvs} log -rfoo::1.3 file1" \
+"${SPROG} log: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-25 "${testcvs} log -r::foo file1" \
+"${SPROG} log: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-26 "${testcvs} log -r1.1::foo file1" \
+"${SPROG} log: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ # Test BASE pseudotag
+ dotest log-27 "${testcvs} log -rBASE file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2b}
+${log_trailer}"
+
+ dotest log-28 "${testcvs} -q up -r1.2 file1" "U file1"
+ dotest log-29 "${testcvs} log -rBASE file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2}
+${log_trailer}"
+
+ dotest log-30 "${testcvs} -q up -rbranch file1" "U file1"
+
+ # Now the same tests but with rlog
+
+ dotest log-r11 "${testcvs} rlog first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-r12 "${testcvs} rlog -N first-dir/file1" \
+"${rlog_header1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-r13 "${testcvs} rlog -b first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 3
+description:
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-r14 "${testcvs} rlog -r first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest log-r14a "${testcvs} rlog -rHEAD first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest_fail log-r14b "${testcvs} rlog -r HEAD first-dir/file1" \
+"${SPROG} rlog: cannot find module .HEAD. - ignored
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest log-r14c "${testcvs} rlog -r: first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-r14d "${testcvs} rlog -r, first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-r14e "${testcvs} rlog -r. first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+ dotest log-r14f "${testcvs} rlog -r:: first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-r15 "${testcvs} rlog -r1.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2}
+${log_trailer}"
+
+ dotest log-r16 "${testcvs} rlog -r1.2.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-r17 "${testcvs} rlog -rbranch first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+
+ dotest log-r18 "${testcvs} rlog -r1.2.2. first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2b}
+${log_trailer}"
+
+ dotest log-r18a "${testcvs} rlog -r1.2.2.2 -r1.3:1.3 first-dir/file1"
\
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2b}
+${log_trailer}"
+
+ dotest log-r19 "${testcvs} rlog -rbranch. first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2b}
+${log_trailer}"
+
+ dotest log-r20 "${testcvs} rlog -r1.2: first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+
+ dotest log-r20a "${testcvs} rlog -r1.2:: first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev3}
+${log_trailer}"
+
+ dotest log-r21 "${testcvs} rlog -r:1.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-r21a "${testcvs} rlog -r::1.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-r22 "${testcvs} rlog -r1.1:1.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev2}
+${log_rev1}
+${log_trailer}"
+
+ dotest log-r22a "${testcvs} rlog -r1.1::1.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 1
+description:
+${log_rev2}
+${log_trailer}"
+
+ dotest log-r22b "${testcvs} rlog -r1.1::1.3 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+
+ dotest log-r23 "${testcvs} rlog -rfoo:: first-dir/file1" \
+"${SPROG} rlog: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-r24 "${testcvs} rlog -rfoo::1.3 first-dir/file1" \
+"${SPROG} rlog: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-r25 "${testcvs} rlog -r::foo first-dir/file1" \
+"${SPROG} rlog: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-r26 "${testcvs} rlog -r1.1::foo first-dir/file1" \
+"${SPROG} rlog: warning: no revision .foo. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ # Test BASE pseudotag
+ dotest log-r27 "${testcvs} rlog -rBASE first-dir/file1" \
+"${SPROG} rlog: warning: no revision .BASE. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ dotest log-r28 "${testcvs} -q up -r1.2 file1" "U file1"
+ dotest log-r29 "${testcvs} rlog -rBASE first-dir/file1" \
+"${SPROG} rlog: warning: no revision .BASE. in
.${CVSROOT_DIRNAME}/first-dir/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 0
+description:
+${log_trailer}"
+
+ # Test when head is dead
+
+ dotest log-d0 "${testcvs} -q up -A" \
+"U file1
+U file2"
+ dotest log-d1 "${testcvs} -q rm -f file1" \
+"${SPROG} remove: use .${SPROG} commit. to remove this file permanently"
+ dotest log-d2 "${testcvs} -q ci -m4" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: delete; previous revision: 1\.3"
+
+ log_header1="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+Working file: file1
+head: 1\.4
+branch:
+locks: strict
+access list:"
+ rlog_header1="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+head: 1\.4
+branch:
+locks: strict
+access list:"
+ log_header2="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+Working file: file2
+head: 1\.3
+branch:
+locks: strict
+access list:"
+ rlog_header2="
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+head: 1\.3
+branch:
+locks: strict
+access list:"
+ log_tags2='symbolic names:
+ tag2: 1\.3
+ tag1: 1\.2'
+ log_rev4="${log_dash} 1\.4
+date: ${ISO8601DATE}; author: ${username}; state: dead; lines: ${PLUS}0 -0;
commitid: ${commitid};
+4"
+ log_rev22="${log_dash} 1\.2
+${log_date}${log_lines}${log_commitid}
+2"
+
+ dotest log-d3 "${testcvs} log -rbranch file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+ dotest log-rd3 "${testcvs} rlog -rbranch first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+ dotest log-d4 "${testcvs} -q log -rbranch" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 0
+description:
+${log_trailer}"
+ dotest log-d4a "${testcvs} -q log -t -rbranch" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6
+description:
+${log_trailer}
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3
+description:
+${log_trailer}"
+ dotest log-d4b "${testcvs} -q log -tS -rbranch" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_trailer}
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-d4c "${testcvs} -q log -h -rbranch" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6
+${log_trailer}
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3
+${log_trailer}"
+ dotest log-d4d "${testcvs} -q log -hS -rbranch" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+${log_trailer}
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-d4e "$testcvs -q log -R -rbranch" \
+"$CVSROOT_DIRNAME/first-dir/Attic/file1,v
+$CVSROOT_DIRNAME/first-dir/file2,v"
+ dotest log-d4f "${testcvs} -q log -R -S -rbranch" \
+"${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+${SPROG} log: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-rd4 "${testcvs} -q rlog -rbranch first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 0
+description:
+${log_trailer}"
+ dotest log-rd4a "${testcvs} -q rlog -t -rbranch first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6
+description:
+${log_trailer}
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3
+description:
+${log_trailer}"
+ dotest log-rd4b "${testcvs} -q rlog -St -rbranch first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_trailer}
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-rd4c "${testcvs} -q rlog -h -rbranch first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6
+${log_trailer}
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v.
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3
+${log_trailer}"
+ dotest log-rd4d "${testcvs} -q rlog -Sh -rbranch first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+${log_trailer}
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-rd4e "${testcvs} -q rlog -R -rbranch first-dir" \
+"${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+${CVSROOT_DIRNAME}/first-dir/file2,v"
+ dotest log-rd4f "${testcvs} -q rlog -R -S -rbranch first-dir" \
+"${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+${SPROG} rlog: warning: no revision .branch. in
.${CVSROOT_DIRNAME}/first-dir/file2,v."
+ dotest log-d5 "${testcvs} log -r1.2.2.1:1.2.2.2 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+ dotest log-rd5 "${testcvs} rlog -r1.2.2.1:1.2.2.2 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}"
+ dotest log-d6 "${testcvs} -q log -r1.2.2.1:1.2.2.2" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 0
+description:
+${log_trailer}"
+ dotest log-rd6 "${testcvs} -q rlog -r1.2.2.1:1.2.2.2 first-dir" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev2b}
+${log_rev1b}
+${log_trailer}
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 0
+description:
+${log_trailer}"
+ dotest log-d7 "${testcvs} log -r1.2:1.3 file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+ dotest log-rd7 "${testcvs} -q rlog -r1.2:1.3 first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 2
+description:
+${log_rev3}
+${log_rev2}
+${log_trailer}"
+ dotest log-d8 "${testcvs} -q log -rtag1:tag2" \
+"${SPROG} log: warning: no revision .tag1. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${SPROG} log: warning: no revision .tag2. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 0
+description:
+${log_trailer}
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 2
+description:
+${log_rev3}
+${log_rev22}
+${log_trailer}"
+ dotest log-d8a "${testcvs} -q log -rtag1:tag2 -S" \
+"${SPROG} log: warning: no revision .tag1. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${SPROG} log: warning: no revision .tag2. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${log_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 2
+description:
+${log_rev3}
+${log_rev22}
+${log_trailer}"
+ dotest log-rd8 "${testcvs} -q rlog -rtag1:tag2 first-dir" \
+"${SPROG} rlog: warning: no revision .tag1. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${SPROG} rlog: warning: no revision .tag2. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 6; selected revisions: 0
+description:
+${log_trailer}
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 2
+description:
+${log_rev3}
+${log_rev22}
+${log_trailer}"
+ dotest log-rd8a "${testcvs} -q rlog -rtag1:tag2 -S first-dir" \
+"${SPROG} rlog: warning: no revision .tag1. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${SPROG} rlog: warning: no revision .tag2. in
.${CVSROOT_DIRNAME}/first-dir/Attic/file1,v.
+${rlog_header2}
+${log_tags2}
+${log_keyword}
+total revisions: 3; selected revisions: 2
+description:
+${log_rev3}
+${log_rev22}
+${log_trailer}"
+
+ dotest log-d99 "${testcvs} -q up -rbranch" \
+"U file1
+${SPROG} update: \`file2' is no longer in the repository"
+
+ # Now test outdating revisions
+
+ dotest log-o0 "${testcvs} admin -o 1.2.2.2:: file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+done"
+ dotest log-o1 "${testcvs} admin -o ::1.2.2.1 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+done"
+ dotest log-o2 "${testcvs} admin -o 1.2.2.1:: file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file1,v
+deleting revision 1\.2\.2\.2
+done"
+ dotest log-o3 "${testcvs} log file1" \
+"${log_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev4}
+${log_rev3}
+${log_rev2}
+${log_rev1}
+${log_rev1b}
+${log_trailer}"
+ dotest log-ro3 "${testcvs} rlog first-dir/file1" \
+"${rlog_header1}
+${log_tags1}
+${log_keyword}
+total revisions: 5; selected revisions: 5
+description:
+${log_rev4}
+${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"
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ log2)
+ # More "cvs log" tests, for example the file description.
+
+ # Check in a file
+ modify_repo 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" \
+"${SPROG}"' add: scheduling file `file1'\'' for addition
+'"${SPROG}"' add: use .'"${SPROG}"' commit. to add this file permanently'
+ dotest log2-3 "${testcvs} -q commit -m 1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ # Setting the file description with add -m doesn't yet work
+ # client/server, so skip log2-4 for remote.
+ if $remote; then :; else
+
+ dotest log2-4 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+1
+============================================================================="
+
+ fi # end of tests skipped for remote
+
+ dotest log2-5 "${testcvs} admin -t-change-description file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest log2-6 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+1
+============================================================================="
+
+ echo 'longer description' >${TESTDIR}/descrip
+ echo 'with two lines' >>${TESTDIR}/descrip
+ dotest log2-7 "${testcvs} admin -t${TESTDIR}/descrip file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest_fail log2-7a "${testcvs} admin -t${TESTDIR}/nonexist file1" \
+"${CPROG} \[admin aborted\]: can't stat ${TESTDIR}/nonexist: No such file or
directory"
+ dotest log2-8 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+1
+============================================================================="
+
+ # TODO: `cvs admin -t "my message" file1' is a request to
+ # read the message from stdin and to operate on two files.
+ # Should test that there is an error because "my message"
+ # doesn't exist.
+
+ dotest log2-9 "echo change from stdin | ${testcvs} admin -t -q file1"
""
+ dotest log2-10 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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 from stdin
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+1
+============================================================================="
+
+ dokeep
+ cd ..
+ rm $TESTDIR/descrip
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ logopt)
+ # Some tests of log.c's option parsing and such things.
+ mkdir 1; cd 1
+ dotest logopt-1 "$testcvs -q co -l ." ''
+ mkdir first-dir
+ dotest logopt-2 "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+ echo hi >file1
+ dotest logopt-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest logopt-4 "${testcvs} -q ci -m add file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ cd ..
+
+ dotest logopt-5 "${testcvs} log -R -d 2038-01-01" \
+"${SPROG} log: Logging \.
+${SPROG} log: Logging first-dir
+${CVSROOT_DIRNAME}/first-dir/file1,v"
+ dotest logopt-6 "${testcvs} log -d 2038-01-01 -R" \
+"${SPROG} log: Logging \.
+${SPROG} log: Logging first-dir
+${CVSROOT_DIRNAME}/first-dir/file1,v"
+ dotest logopt-6a "${testcvs} log -Rd 2038-01-01" \
+"${SPROG} log: Logging \.
+${SPROG} log: Logging first-dir
+${CVSROOT_DIRNAME}/first-dir/file1,v"
+ dotest logopt-7 "${testcvs} log -s Exp -R" \
+"${SPROG} log: Logging \.
+${SPROG} log: Logging first-dir
+${CVSROOT_DIRNAME}/first-dir/file1,v"
+
+ dokeep
+ cd ..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ ann)
+ # Tests of "cvs annotate". See also:
+ # basica-10 A simple annotate test
+ # rcs Annotate and the year 2000
+ # keywordlog Annotate and $Log.
+ mkdir 1; cd 1
+ dotest ann-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest ann-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ cat >file1 <<EOF
+this
+is
+the
+ancestral
+file
+EOF
+ dotest ann-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest ann-4 "${testcvs} -q ci -m add file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ cat >file1 <<EOF
+this
+is
+a
+file
+
+with
+a
+blank
+line
+EOF
+ dotest ann-5 "${testcvs} -q ci -m modify file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest ann-6 "${testcvs} -q tag -b br" "T file1"
+ cat >file1 <<EOF
+this
+is
+a
+trunk file
+
+with
+a
+blank
+line
+EOF
+ dotest ann-7 "${testcvs} -q ci -m modify file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ dotest ann-8 "${testcvs} -q update -r br" "U file1"
+ cat >file1 <<EOF
+this
+is
+a
+file
+
+with
+a
+blank
+line
+and some
+branched content
+EOF
+ dotest ann-9 "${testcvs} -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2\.2\.1; previous revision: 1\.2"
+ # Note that this annotates the trunk despite the presence
+ # of a sticky tag in the current directory. This is
+ # fairly bogus, but it is the longstanding behavior for
+ # whatever that is worth.
+ dotest ann-10 "${testcvs} ann" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.3 ($username8 *[0-9a-zA-Z-]*): trunk file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line"
+ dotest ann-10w1 "${testcvs} ann -w 1" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username1 *[0-9a-zA-Z-]*): this
+1\.1 ($username1 *[0-9a-zA-Z-]*): is
+1\.2 ($username1 *[0-9a-zA-Z-]*): a
+1\.3 ($username1 *[0-9a-zA-Z-]*): trunk file
+1\.2 ($username1 *[0-9a-zA-Z-]*):
+1\.2 ($username1 *[0-9a-zA-Z-]*): with
+1\.2 ($username1 *[0-9a-zA-Z-]*): a
+1\.2 ($username1 *[0-9a-zA-Z-]*): blank
+1\.2 ($username1 *[0-9a-zA-Z-]*): line"
+ if test $userlen -lt 80; then
+ dotest ann-10wmax "${testcvs} ann -w $userlen" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username *[0-9a-zA-Z-]*): this
+1\.1 ($username *[0-9a-zA-Z-]*): is
+1\.2 ($username *[0-9a-zA-Z-]*): a
+1\.3 ($username *[0-9a-zA-Z-]*): trunk file
+1\.2 ($username *[0-9a-zA-Z-]*):
+1\.2 ($username *[0-9a-zA-Z-]*): with
+1\.2 ($username *[0-9a-zA-Z-]*): a
+1\.2 ($username *[0-9a-zA-Z-]*): blank
+1\.2 ($username *[0-9a-zA-Z-]*): line"
+ fi
+ dotest ann-11 "${testcvs} ann -r br" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.1 ($username8 *[0-9a-zA-Z-]*): file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): and some
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): branched content"
+ # FIXCVS: shouldn't "-r 1.2.0.2" be the same as "-r br"?
+ dotest ann-12 "${testcvs} ann -r 1.2.0.2 file1" ""
+ dotest ann-13 "${testcvs} ann -r 1.2.2 file1" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.1 ($username8 *[0-9a-zA-Z-]*): file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): and some
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): branched content"
+ dotest_fail ann-14 "$testcvs ann -r bill-clintons-chastity file1" \
+"$SPROG \[annotate aborted\]: no such tag \`bill-clintons-chastity'"
+
+ # Now get rid of the working directory and test rannotate
+
+ cd ../..
+ rm -r 1
+ dotest ann-r10 "${testcvs} rann first-dir" \
+"
+Annotations for first-dir/file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.3 ($username8 *[0-9a-zA-Z-]*): trunk file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line"
+ dotest ann-r11 "${testcvs} rann -r br first-dir" \
+"
+Annotations for first-dir/file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.1 ($username8 *[0-9a-zA-Z-]*): file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): and some
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): branched content"
+ dotest ann-r12 "${testcvs} rann -r 1.2.0.2 first-dir/file1" ""
+ dotest ann-r13 "${testcvs} rann -r 1.2.2 first-dir/file1" \
+"
+Annotations for first-dir/file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 ($username8 *[0-9a-zA-Z-]*): this
+1\.1 ($username8 *[0-9a-zA-Z-]*): is
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.1 ($username8 *[0-9a-zA-Z-]*): file
+1\.2 ($username8 *[0-9a-zA-Z-]*):
+1\.2 ($username8 *[0-9a-zA-Z-]*): with
+1\.2 ($username8 *[0-9a-zA-Z-]*): a
+1\.2 ($username8 *[0-9a-zA-Z-]*): blank
+1\.2 ($username8 *[0-9a-zA-Z-]*): line
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): and some
+1\.2\.2\.1 ($username8 *[0-9a-zA-Z-]*): branched content"
+ dotest_fail ann-r14 "$testcvs rann -r bill-clintons-chastity
first-dir/file1" \
+"$SPROG \[rannotate aborted\]: no such tag \`bill-clintons-chastity'"
+
+ dokeep
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ ann-id)
+ # Demonstrate that cvs-1.9.28.1 improperly expands rcs keywords in
+ # the output of `cvs annotate' -- it uses values from the previous
+ # delta. In this case, `1.1' instead of `1.2', even though it puts
+ # the proper version number on the prefix to each line of output.
+ mkdir 1; cd 1
+ dotest ann-id-1 "$testcvs -q co -l ."
+ module=x
+ mkdir $module
+ dotest ann-id-2 "${testcvs} add $module" \
+"Directory ${CVSROOT_DIRNAME}/$module added to the repository"
+ cd $module
+
+ file=m
+ echo '$Id''$' > $file
+
+ dotest ann-id-3 "$testcvs add $file" \
+"$SPROG add: scheduling file .$file. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest ann-id-4 "$testcvs -Q ci -m . $file"
+
+ echo line2 >> $file
+ dotest ann-id-5 "$testcvs -Q ci -m . $file"
+
+ # The version number after $file,v should be `1.2'.
+ # 1.9.28.1 puts `1.1' there.
+ dotest ann-id-6 "$testcvs -Q ann $file" \
+"
+Annotations for $file
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1.2 ($username8 *[0-9a-zA-Z-]*): "'\$'"Id: $file,v 1.1 [0-9/]*
[0-9:]* $username Exp "'\$'"
+1.2 ($username8 *[0-9a-zA-Z-]*): line2"
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ crerepos)
+ # Various tests relating to creating repositories, operating
+ # on repositories created with old versions of CVS, etc.
+
+ CVS_SERVER_save=$CVS_SERVER
+
+ # Because this test is all about -d options and such, it
+ # at least to some extent needs to be different for remote vs.
+ # local.
+ if $remote; then
+
+ # Use :ext: rather than :fork:. Most of the tests use :fork:,
+ # so we want to make sure that we test :ext: _somewhere_.
+ # Make sure 'rsh' works first.
+ require_rsh "$CVS_RSH"
+ if test $? -eq 77; then
+ skip crerepos "$skipreason"
+ continue
+ fi
+
+ # Make sure server ignores real $HOME/.cvsrc:
+ cat >$TESTDIR/cvs-setHome <<EOF
+#!$TESTSHELL
+HOME=$HOME
+export HOME
+exec $CVS_SERVER "\$@"
+EOF
+ chmod a+x $TESTDIR/cvs-setHome
+
+ # Note that we set CVS_SERVER at the beginning.
+ CVS_SERVER=$TESTDIR/cvs-setHome; export CVS_SERVER
+ CREREPOS_ROOT=:ext:$host$TESTDIR/crerepos
+ else # local
+ CREREPOS_ROOT=$TESTDIR/crerepos
+ fi
+
+ # First, if the repository doesn't exist at all...
+ dotest_fail crerepos-1 \
+"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
+"${SPROG} \[checkout aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*"
+ mkdir crerepos
+
+ # The repository exists but CVSROOT doesn't.
+ dotest_fail crerepos-2 \
+"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
+"${SPROG} \[checkout aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*"
+ mkdir crerepos/CVSROOT
+
+ # Checkout of nonexistent module
+ dotest_fail crerepos-3 \
+"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
+"${SPROG} checkout: cannot find module .cvs-sanity. - ignored"
+
+ # Now test that CVS works correctly without a modules file
+ # or any of that other stuff. In particular, it *must*
+ # function if administrative files added to CVS recently (since
+ # CVS 1.3) do not exist, because the repository might have
+ # been created with an old version of CVS.
+ mkdir 1; cd 1
+ dotest crerepos-4 \
+"${testcvs} -q -d ${TESTDIR}/crerepos co CVSROOT" \
+''
+ dotest crerepos-5 \
+"echo yes | $testcvs -d $TESTDIR/crerepos release -d CVSROOT" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory \`CVSROOT': "
+ rm -rf CVS
+ cd ..
+ # The directory 1 should be empty
+ dotest crerepos-6 "rmdir 1"
+
+ if $remote; then
+ # Test that CVS rejects a relative path in CVSROOT.
+ mkdir 1; cd 1
+ # Note that having the client reject the pathname (as :fork:
+ # does), does _not_ test for the bugs we are trying to catch
+ # here. The point is that malicious clients might send all
+ # manner of things and the server better protect itself.
+ dotest_fail crerepos-6a-r \
+"${testcvs} -q -d :ext:`hostname`:../crerepos get ." \
+"${CPROG} checkout: CVSROOT may only specify a positive, non-zero, integer
port (not .\.\..)\.
+${CPROG} checkout: Perhaps you entered a relative pathname${QUESTION}
+${CPROG} \[checkout aborted\]: Bad CVSROOT: .:ext:${hostname}:\.\./crerepos.\."
+ cd ..
+ rm -r 1
+
+ mkdir 1; cd 1
+ dotest_fail crerepos-6b-r \
+"${testcvs} -d :ext:`hostname`:crerepos init" \
+"${CPROG} init: CVSROOT requires a path spec:
+${CPROG} init:
:(gserver|kserver|pserver):\[\[user\]\[:address@hidden:\[port\]\]/path
+${CPROG} init: \[:(ext|server):address@hidden:\]/path
+${CPROG} \[init aborted\]: Bad CVSROOT: .:ext:${hostname}:crerepos.\."
+ cd ..
+ rm -r 1
+ else # local
+ # Test that CVS rejects a relative path in CVSROOT.
+
+ mkdir 1; cd 1
+ # Set CVS_RSH=false since ocassionally (e.g. when CVS_RSH=ssh on
+ # some systems) some rsh implementations will block because they
+ # can look up '..' and want to ask the user about the unknown host
+ # key or somesuch. Which error message we get depends on whether
+ # false finishes running before we try to talk to it or not.
+ dotest_fail crerepos-6a "CVS_RSH=false ${testcvs} -q -d ../crerepos
get ." \
+"${SPROG} \[checkout aborted\]: end of file from server (consult above
messages if any)" \
+"${SPROG} \[checkout aborted\]: received broken pipe signal"
+ cd ..
+ rm -r 1
+
+ mkdir 1; cd 1
+ dotest_fail crerepos-6b "${testcvs} -d crerepos init" \
+"${SPROG} init: CVSROOT must be an absolute pathname (not .crerepos.)
+${SPROG} init: when using local access method\.
+${SPROG} \[init aborted\]: Bad CVSROOT: .crerepos.\."
+ cd ..
+ rm -r 1
+ fi # end of tests to be skipped for remote
+
+ # CVS should have created a history file. If the administrator
+ # doesn't need it and wants to save on disk space, they just
+ # delete it and set LogHistory = the empty string in config.
+ dotest 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1
+ dotest crerepos-10 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest crerepos-11 "${testcvs} -q ci -m add-it" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ 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" \
+"${SPROG} add: scheduling file .cfile. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest crerepos-15 "${testcvs} -q ci -m add-it" \
+"$TESTDIR/crerepos/crerepos-dir/cfile,v <-- cfile
+initial revision: 1\.1"
+ cd ../..
+ rm -r 1
+
+ mkdir 1; cd 1
+ dotest crerepos-16 "${testcvs} co first-dir" \
+"${SPROG} checkout: Updating first-dir
+U first-dir/file1"
+ dotest crerepos-17 "${testcvs} -d ${CREREPOS_ROOT} co crerepos-dir" \
+"${SPROG} checkout: Updating crerepos-dir
+U crerepos-dir/cfile"
+ dotest crerepos-18 "${testcvs} update" \
+"${SPROG} update: Updating first-dir
+${SPROG} update: Updating crerepos-dir"
+
+ cd ..
+
+ CVS_SERVER=$CVS_SERVER_save; export CVS_SERVER
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ dokeep
+ rm -f $TESTDIR/cvs-setHome
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ rm -rf $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.
+
+ # Save the timezone and set it to UTC for these tests to make the
+ # value more predicatable.
+ save_TZ=$TZ
+ TZ=UTC0; export TZ
+
+ modify_repo 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 <<EOF >$TESTDIR/file1,v
+head 1.3;
+access;
+symbols;
+locks; strict;
+comment @# @;
+
+
+1.3
+date ${RAWRCSDATE2000A}; author kingdon; state Exp;
+branches;
+next 1.2;
+
+1.2
+date ${RAWRCSDATE1996A}; author kingdon; state Exp;
+branches;
+next 1.1;
+
+1.1
+date ${RAWRCSDATE1996B}; author kingdon; state Exp;
+branches;
+next ;
+
+
+desc
address@hidden is for testing CVS
+@
+
+
+1.3
+log
address@hidden second line; modify twelfth line
+@
+text
address@hidden 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
address@hidden more lines
+@
+text
address@hidden 1
+This is the second line
+d11 1
+a11 1
+This is the twelfth line
+@
+
+
+1.1
+log
address@hidden file1
+@
+text
address@hidden 12
+@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+
+ dotest rcs-1 "$testcvs -q co first-dir" 'U first-dir/file1'
+ cd first-dir
+ dotest rcs-2 "$testcvs -q log" "
+RCS file: $CVSROOT_DIRNAME/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: ${ISO8601DATE2000A}; author: kingdon; state: Exp; lines: ${PLUS}1 -2;
+delete second line; modify twelfth line
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE1996A}; author: kingdon; state: Exp; lines: ${PLUS}12 -0;
+add more lines
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE1996B}; 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: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE2000A}; author: kingdon; state: Exp; lines: ${PLUS}1 -2;
+delete second line; modify twelfth line
+============================================================================="
+
+ # RFC822 format (as amended by RFC1123).
+ dotest rcs-4 "${testcvs} -q log -d '<3 Apr 2000 00:00'" \
+"
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE1996A}; author: kingdon; state: Exp; lines: ${PLUS}12 -0;
+add more lines
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE1996B}; author: kingdon; state: Exp;
+add file1
+============================================================================="
+
+ # Intended behavior for "cvs annotate" is that it displays the
+ # last two digits of the year. Make sure it does that rather
+ # than some bogosity like "100".
+ dotest rcs-4a "${testcvs} annotate file1" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1 (kingdon 24-Nov-96): This is the first line
+1\.2 (kingdon 24-Nov-96): This is the third line
+1\.2 (kingdon 24-Nov-96): This is the fourth line
+1\.2 (kingdon 24-Nov-96): This is the fifth line
+1\.2 (kingdon 24-Nov-96): This is the sixth line
+1\.2 (kingdon 24-Nov-96): This is the seventh line
+1\.2 (kingdon 24-Nov-96): This is the eighth line
+1\.2 (kingdon 24-Nov-96): This is the ninth line
+1\.2 (kingdon 24-Nov-96): This is the tenth line
+1\.2 (kingdon 24-Nov-96): This is the eleventh line
+1\.3 (kingdon 24-Nov-00): This is the twelfth line (and what a line
it is)
+1\.2 (kingdon 24-Nov-96): This is the thirteenth line"
+
+ # Probably should split this test into two at this point (file1
+ # above this line and file2 below), as the two share little
+ # data/setup.
+
+ # OK, here is another one. This one was written by hand based on
+ # doc/RCSFILES and friends. One subtle point is that none of
+ # the lines end with newlines; that is a feature which we
+ # should be testing.
+ cat <<EOF >$TESTDIR/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
+
+log @@ text @d1 1
+a1 1
+start revision@
+1.2.6.1 log @@ text @d1 1
+a1 1
+branch revision@
+EOF
+ modify_repo mv $TESTDIR/file2,v $CVSROOT_DIRNAME/first-dir/file2,v
+ # ' Match the single quote in above here doc -- for font-lock mode.
+
+ # First test the default branch.
+ dotest rcs-5 "${testcvs} -q update file2" "U file2"
+ dotest rcs-6 "cat file2" "branch revision"
+
+ # 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" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.2\.6\.2; previous revision: 1\.2\.6\.1"
+
+ # Now get rid of the default branch, it will get in the way.
+ dotest rcs-7 "${testcvs} admin -b file2" \
+"RCS file: ${CVSROOT_DIRNAME}/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[ ][ address@hidden newphrase we'd have trouble
extending @@ address@hidden ]*;"
+ # 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\.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 1\.2\.6\.2;
+
+1\.2\.6\.2
+date [0-9.]*; author ${username}; state Exp;
+branches;
+next ;
+commitid ${commitid};
+
+
+desc
+@@
+
+
+1\.5
+log
+@@
+newphrase1 ;
+newphrase2 42;
+text
address@hidden revision@
+
+
+1\.4
+log
+@@
+text
address@hidden 1
+a1 1
+new year revision@
+
+
+1\.3
+log
+@@
+text
address@hidden 1
+a1 1
+old year revision@
+
+
+1\.2
+log
+@@
+text
address@hidden 1
+a1 1
+mid revision@
+
+
+1\.1
+log
+@@
+text
address@hidden 1
+a1 1
+start revision@
+
+
+1\.2\.6\.1
+log
+@@
+text
address@hidden 1
+a1 1
+branch revision@
+
+
+1\.2\.6\.2
+log
address@hidden
+@
+text
address@hidden 1
+a1 1
+next branch revision
+@"
+
+ dotest rcs-9 "${testcvs} -q update -p -D '1970-12-31 11:30 UT' file2"
\
+"start revision"
+
+ dotest rcs-10 "${testcvs} -q update -p -D '1970-12-31 12:30 UT'
file2" \
+"mid revision"
+
+ dotest rcs-11 "${testcvs} -q update -p -D '1971-01-01 00:30 UT'
file2" \
+"new year revision"
+
+ # Same test as rcs-10, but with am/pm.
+ dotest rcs-12 "${testcvs} -q update -p -D 'December 31, 1970 12:30pm
UT' file2" \
+"mid revision"
+
+ # Same test as rcs-11, but with am/pm.
+ dotest rcs-13 "${testcvs} -q update -p -D 'January 1, 1971 12:30am
UT' file2" \
+"new year revision"
+
+ # OK, now make sure cvs log doesn't have any trouble with the
+ # newphrases and such.
+ dotest rcs-14 "${testcvs} -q log file2" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+Working file: file2
+head: 1\.5
+branch:
+locks:
+access list:
+symbolic names:
+ branch: 1\.2\.6
+keyword substitution: kv
+total revisions: 7; selected revisions: 7
+description:
+----------------------------
+revision 1\.5
+date: 1971-01-01 01:00:00 [+-]0000; author: joe; state: bogus; lines:
${PLUS}1 -1;
+\*\*\* empty log message \*\*\*
+----------------------------
+revision 1\.4
+date: 1971-01-01 00:00:05 [+-]0000; author: joe; state: bogus; lines:
${PLUS}1 -1;
+\*\*\* empty log message \*\*\*
+----------------------------
+revision 1\.3
+date: 1970-12-31 15:00:05 [+-]0000; author: joe; state: bogus; lines:
${PLUS}1 -1;
+\*\*\* empty log message \*\*\*
+----------------------------
+revision 1\.2
+date: 1970-12-31 12:15:05 [+-]0000; 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 [+-]0000; author: joe; state: bogus;
+\*\*\* empty log message \*\*\*
+----------------------------
+revision 1\.2\.6\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+mod
+----------------------------
+revision 1\.2\.6\.1
+date: 1971-01-01 08:00:05 [+-]0000; author: joe; state: Exp; lines:
${PLUS}1 -1;
+\*\*\* empty log message \*\*\*
+============================================================================="
+ # Now test each date format for "cvs log -d".
+ # Earlier than 1971-01-01
+ dotest rcs-15 "${testcvs} -q log -d '<1971-01-01 00:00 GMT' file2 \
+ | grep revision" \
+"total revisions: 7; selected revisions: 3
+revision 1\.3
+revision 1\.2
+revision 1\.1"
+ # Later than 1971-01-01
+ dotest rcs-16 "${testcvs} -q log -d '1971-01-01 00:00 GMT<' file2 \
+ | grep revision" \
+"total revisions: 7; selected revisions: 4
+revision 1\.5
+revision 1\.4
+revision 1\.2\.6\.2
+revision 1\.2\.6\.1"
+ # Alternate syntaxes for later and earlier; multiple -d options
+ dotest rcs-17 "${testcvs} -q log -d '>1971-01-01 00:00 GMT' \
+ -d '1970-12-31 12:15 GMT>' file2 | grep revision" \
+"total revisions: 7; selected revisions: 5
+revision 1\.5
+revision 1\.4
+revision 1\.1
+revision 1\.2\.6\.2
+revision 1\.2\.6\.1"
+ # Range, and single date
+ dotest rcs-18 "${testcvs} -q log -d '1970-12-31 11:30 GMT' \
+ -d '1971-01-01 00:00:05 GMT<1971-01-01 01:00:01 GMT' \
+ file2 | grep revision" \
+"total revisions: 7; selected revisions: 2
+revision 1\.5
+revision 1\.1"
+ # Alternate range syntax; equality
+ dotest rcs-19 "${testcvs} -q log \
+ -d '1971-01-01 01:00:01 GMT>=1971-01-01 00:00:05 GMT' \
+ file2 | grep revision" \
+"total revisions: 7; selected revisions: 2
+revision 1\.5
+revision 1\.4"
+
+ dokeep
+ TZ=$save_TZ
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rcs2)
+ # More date tests. Might as well do this as a separate
+ # test from "rcs", so that we don't need to perturb the
+ # "written by RCS 5.7" RCS file.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ # Significance of various dates:
+ # * At least one Y2K standard refers to recognizing 9 Sep 1999
+ # (as an example of a pre-2000 date, I guess).
+ # * At least one Y2K standard refers to recognizing 1 Jan 2001
+ # (as an example of a post-2000 date, I guess).
+ # * Many Y2K standards refer to 2000 being a leap year.
+ cat <<EOF >$TESTDIR/file1,v
+head 1.7; access; symbols; locks; strict;
+1.7 date 2004.08.31.01.01.01; author sue; state; branches; next 1.6;
+1.6 date 2004.02.29.01.01.01; author sue; state; branches; next 1.5;
+1.5 date 2003.02.28.01.01.01; author sue; state; branches; next 1.4;
+1.4 date 2001.01.01.01.01.01; author sue; state; branches; next 1.3;
+1.3 date 2000.02.29.01.01.01; author sue; state; branches; next 1.2;
+1.2 date 99.09.09.01.01.01; author sue; state; branches; next 1.1;
+1.1 date 98.09.10.01.01.01; author sue; state; branches; next;
+desc @a test file@
+1.7 log @@ text @head revision@
+1.6 log @@ text @d1 1
+a1 1
+2004 was a great year for leaping@
+1.5 log @@ text @d1 1
+a1 1
+2003 wasn't@
+1.4 log @@ text @d1 1
+a1 1
+two year hiatus@
+1.3 log @@ text @d1 1
+a1 1
+2000 is also a good year for leaping@
+1.2 log @@ text @d1 1
+a1 1
+Tonight we're going to party like it's a certain year@
+1.1 log @@ text @d1 1
+a1 1
+Need to start somewhere@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ # ' Match the 3rd single quote in the here doc -- for font-lock mode.
+
+ dotest rcs2-1 "${testcvs} -q co first-dir" 'U first-dir/file1'
+ cd first-dir
+
+ # 9 Sep 1999
+ dotest rcs2-2 "${testcvs} -q update -p -D '1999-09-09 11:30 UT'
file1" \
+"Tonight we're going to party like it's a certain year"
+ # 1 Jan 2001.
+ dotest rcs2-3 "${testcvs} -q update -p -D '2001-01-01 11:30 UT'
file1" \
+"two year hiatus"
+ # 29 Feb 2000
+ dotest rcs2-4 "${testcvs} -q update -p -D '2000-02-29 11:30 UT'
file1" \
+"2000 is also a good year for leaping"
+ # 29 Feb 2003 is invalid
+ dotest_fail rcs2-5 "${testcvs} -q update -p -D '2003-02-29 11:30 UT'
file1" \
+"$CPROG \[update aborted\]: Can't parse date/time: \`2003-02-29 11:30 UT'"
+
+ dotest rcs2-6 "${testcvs} -q update -p -D 2007-01-07 file1" \
+"head revision"
+ # This assumes that the clock of the machine running the tests
+ # is set to at least the year 1998 or so. There don't seem
+ # to be a lot of ways to test the relative date code (short
+ # of something like LD_LIBRARY_PRELOAD'ing in our own
+ # getttimeofday, or hacking the CVS source with testing
+ # features, which always seems to be problematic since then
+ # someone feels like documenting them and things go downhill
+ # from there).
+ #
+ # These tests can be expected to fail 3 times every 400 years
+ # starting Feb. 29, 2096 (because 8 years from that date would
+ # be Feb. 29, 2100, which is an invalid date -- 2100 isn't a
+ # leap year because it's divisible by 100 but not by 400).
+
+ dotest rcs2-7 "${testcvs} -q update -p -D '96 months' file1" \
+"head revision"
+ dotest rcs2-8 "${testcvs} -q update -p -D '8 years' file1" \
+"head revision"
+
+ dokeep
+ cd ..
+ rm -r first-dir
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rcs3)
+ # More RCS file tests, in particular at least some of the
+ # error handling issues.
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ cat <<EOF >$TESTDIR/file1,v
+head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02
+; author jeremiah ;state ; branches; next;desc@@1.1log@@address@hidden@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ mkdir 1; cd 1
+ # CVS requires whitespace between "desc" and its value.
+ # The rcsfile(5) manpage doesn't really seem to answer the
+ # question one way or the other (it has a grammar but almost
+ # nothing about lexical analysis).
+ dotest_fail rcs3-1 "${testcvs} -q co first-dir" \
+"${SPROG} \[checkout aborted\]: EOF while looking for value in RCS file
${CVSROOT_DIRNAME}/first-dir/file1,v"
+ cat <<EOF >$TESTDIR/file1,v
+head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02
+; author jeremiah ;state ; branches; next;desc @@1.1log@@address@hidden@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ # Whitespace issues, likewise.
+ dotest_fail rcs3-2 "${testcvs} -q co first-dir" \
+"${SPROG} \[checkout aborted\]: unexpected '.x6c' reading revision number in
RCS file ${CVSROOT_DIRNAME}/first-dir/file1,v"
+ cat <<EOF >$TESTDIR/file1,v
+head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02
+; author jeremiah ;state ; branches; next;desc @@1.1 log@@address@hidden@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ # Charming array of different messages for similar
+ # whitespace issues (depending on where the whitespace is).
+ dotest_fail rcs3-3 "${testcvs} -q co first-dir" \
+"${SPROG} \[checkout aborted\]: EOF while looking for value in RCS file
${CVSROOT_DIRNAME}/first-dir/file1,v"
+ cat <<EOF >$TESTDIR/file1,v
+head 1.1; access; symbols; locks; expand o; 1.1 date 2007.03.20.04.03.02
+; author jeremiah ;state ; branches; next;desc @@1.1 log @@text @head@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ dotest rcs3-4 "${testcvs} -q co first-dir" 'U first-dir/file1'
+
+ # Ouch, didn't expect this one. FIXCVS. Or maybe just remove
+ # the feature, if this is a -s problem?
+ dotest_fail rcs3-5 "${testcvs} log -s nostate first-dir/file1" \
+"${DOTSTAR}ssertion.*failed${DOTSTAR}" "${DOTSTAR}failed assertion${DOTSTAR}"
+ cd first-dir
+ dotest_fail rcs3-5a "${testcvs} log -s nostate file1" \
+"${DOTSTAR}ssertion.*failed${DOTSTAR}" "${DOTSTAR}failed assertion${DOTSTAR}"
+ cd ..
+
+ # See remote code above for rationale for cd.
+ cd first-dir
+ dotest rcs3-6 "${testcvs} log -R file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v"
+
+ # OK, now put an extraneous '\0' at the end.
+ mv $CVSROOT_DIRNAME/first-dir/file1,v $TESTDIR/file1,v
+ ${AWK} </dev/null 'BEGIN { printf "@%c", 10 }' | ${TR} '@' '\000' \
+ >>$TESTDIR/file1,v
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/first-dir/file1,v
+ dotest_fail rcs3-7 "${testcvs} log -s nostate file1" \
+"${SPROG} \[log aborted\]: unexpected '.x0' reading revision number in RCS
file ${CVSROOT_DIRNAME}/first-dir/file1,v"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ rcs4)
+ # Fix a bug that shows up when checking out files by date with the
+ # "-D date" command line option. There is code in the original to
+ # handle a special case. If the date search finds revision 1.1 it
+ # is supposed to check whether revision 1.1.1.1 has the same date
+ # stamp, which would indicate that the file was originally brought
+ # in with "cvs import". In that case it is supposed to return the
+ # vendor branch version 1.1.1.1.
+ #
+ # However, there is a bug in the code. It actually compares
+ # the date of revision 1.1 for equality with the date given
+ # on the command line -- clearly wrong. This commit fixes
+ # the coding bug.
+ #
+ # There is an additional bug which is _not_ fixed yet.
+ # The date comparison should not be a strict
+ # equality test. It should allow a fudge factor of, say, 2-3
+ # seconds. Old versions of CVS created the two revisions
+ # with two separate invocations of the RCS "ci" command. We
+ # have many old files in the tree in which the dates of
+ # revisions 1.1 and 1.1.1.1 differ by 1 second.
+
+ # Need a predictable time zone.
+ save_TZ=$TZ
+ TZ=UTC0; export TZ
+
+ mkdir rcs4
+ cd rcs4
+
+ mkdir imp-dir
+ cd imp-dir
+ echo 'OpenMunger sources' >file1
+
+ # choose a time in the past to demonstrate the problem
+ touch -t 200012010123 file1
+
+ dotest_sort rcs4-1 \
+"${testcvs} import -d -m add rcs4-dir openmunger openmunger-1_0" \
+'
+
+N rcs4-dir/file1
+No conflicts created by this import'
+ echo 'OpenMunger sources release 1.1 extras' >>file1
+ touch -t 200112011234 file1
+ dotest_sort rcs4-2 \
+"${testcvs} import -d -m add rcs4-dir openmunger openmunger-1_1" \
+'
+
+No conflicts created by this import
+U rcs4-dir/file1'
+ cd ..
+ # Next checkout the new module
+ dotest rcs4-3 \
+"${testcvs} -q co rcs4-dir" \
+'U rcs4-dir/file1'
+ cd rcs4-dir
+ echo 'local change' >> file1
+
+ # commit a local change
+ dotest rcs4-4 "${testcvs} -q commit -m hack file1" \
+"$CVSROOT_DIRNAME/rcs4-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ # now see if we get version 1.1 or 1.1.1.1 when we ask for
+ # a checkout by time... it really should be 1.1.1.1 as
+ # that was indeed the version that was visible at the target
+ # time.
+ dotest rcs4-5 \
+"${testcvs} -q update -D 'October 1, 2001 UTC' file1" \
+'U file1'
+ dotest rcs4-6 \
+"${testcvs} -q status file1" \
+'===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.1\.1.*
+ Repository revision: 1\.1\.1\.1
'${CVSROOT_DIRNAME}'/rcs4-dir/file1,v
+ Commit Identifier: '${commitid}'
+ Sticky Tag: (none)
+ Sticky Date: 2001\.10\.01\.00\.00\.00
+ Sticky Options: (none)'
+
+ dokeep
+ TZ=$save_TZ
+ cd ../..
+ rm -r rcs4
+ modify_repo rm -rf $CVSROOT_DIRNAME/rcs4-dir
+ ;;
+
+
+
+ rcs5)
+ # Some tests of the $Log keyword and log message without a trailing
+ # EOL. This used to look ugly and, in the worst case, could cause
+ # a seg fault due to a buffer overflow.
+ #
+ # Note that it should not be possible to create this situation via a
+ # CVS server (and any client), since the server itself inserts the
+ # trailing EOL onto log messages that are missing one. Still, we
+ # shouldn't segfault due to a corrupt RCS file and I think that a log
+ # message without the trailing EOL doesn't actually violate the RCS
+ # spec, though it doesn't appear to be possible to create such a log
+ # message using RCS 5.7.
+
+ modify_repo mkdir $CVSROOT_DIRNAME/rcs5
+ cat <<\EOF >$TESTDIR/file1,v
+head 1.1;
+access;
+symbols;
+locks;
+expand kv;
+
+1.1 date 2007.03.20.04.03.02; author jeremiah; state Ext; branches; next;
+
+desc
+@@
+
+1.1
+log
address@hidden always had very fine wine@
+text
address@hidden
+/*
+EOF
+echo ' * History: $''Log$' >>$TESTDIR/file1,v
+ cat <<\EOF >>$TESTDIR/file1,v
+ */
+line5
+@
+EOF
+ modify_repo mv $TESTDIR/file1,v $CVSROOT_DIRNAME/rcs5/file1,v
+
+ mkdir rcs5
+ cd rcs5
+ dotest rcs5-1 "$testcvs -Q co rcs5"
+ dotest rcs5-2 "cat rcs5/file1" \
+"line1
+/\\*
+ \\* History: "'\$'"Log: file1,v "'\$'"
+ \\* History: Revision 1\.1 2007/03/20 04:03:02 jeremiah
+ \\* History: he always had very fine wine
+ \\* History:
+ \\*/
+line5"
+
+ cd ..
+ rm -r rcs5
+ modify_repo rm -rf $CVSROOT_DIRNAME/rcs5
+ ;;
+
+
+
+ lockfiles)
+ # Tests of CVS lock files.
+ # TODO-maybe: Add a test where we arrange for a loginfo
+ # script (or some such) to ensure that locks are in place
+ # so then we can see how they are behaving.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ mkdir 1; cd 1
+ mkdir sdir
+ mkdir sdir/ssdir
+ echo file >sdir/ssdir/file1
+ dotest lockfiles-1 \
+"${testcvs} -Q import -m import-it first-dir bar baz" ""
+ cd ..
+
+ mkdir 2; cd 2
+ dotest lockfiles-2 "${testcvs} -q co first-dir" \
+"U first-dir/sdir/ssdir/file1"
+ dotest lockfiles-3 "${testcvs} -Q co CVSROOT" ""
+ cd CVSROOT
+ echo "LockDir=${TESTDIR}/locks" >>config
+ dotest lockfiles-4 "${testcvs} -q ci -m config-it" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cd ../first-dir/sdir/ssdir
+ # The error message appears twice because Lock_Cleanup only
+ # stops recursing after the first attempt.
+ dotest_fail lockfiles-5 "${testcvs} -q update" \
+"${SPROG} \[update aborted\]: cannot stat ${TESTDIR}/locks: No such file or
directory"
+ mkdir ${TESTDIR}/locks
+ # Grumble, mumble. Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod u=rwx,g=r,o= ${TESTDIR}/locks"
+ else
+ chmod u=rwx,g=r,o= ${TESTDIR}/locks
+ fi
+ save_umask=`umask`
+ umask 0077
+ CVSUMASK=0077; export CVSUMASK
+ dotest lockfiles-6 "${testcvs} -q update" ""
+ # TODO: should also be testing that CVS continues to honor the
+ # umask and CVSUMASK normally. In the case of the umask, CVS
+ # doesn't seem to use it for much (although it perhaps should).
+ dotest lockfiles-7 "ls ${TESTDIR}/locks/first-dir/sdir/ssdir" ""
+
+ # The policy is that when CVS creates new lock directories, they
+ # inherit the permissions from the parent directory. CVSUMASK
+ # isn't right, because typically the reason for LockDir is to
+ # use a different set of permissions.
+ #
+ # Bah! Cygwin!
+ if test -n "$remotehost"; then
+ dotest lockfiles-7a "$CVS_RSH $remotehost 'ls -ld
${TESTDIR}/locks/first-dir'" \
+"drwxr-----.*first-dir"
+ dotest lockfiles-7b "$CVS_RSH $remotehost 'ls -ld
${TESTDIR}/locks/first-dir/sdir/ssdir'" \
+"drwxr-----.*first-dir/sdir/ssdir"
+ else
+ dotest lockfiles-7a "ls -ld ${TESTDIR}/locks/first-dir" \
+"drwxr-----.*first-dir"
+ dotest lockfiles-7b "ls -ld ${TESTDIR}/locks/first-dir/sdir/ssdir" \
+"drwxr-----.*first-dir/sdir/ssdir"
+ fi
+
+ cd ../../..
+ dotest lockfiles-8 "${testcvs} -q update" ""
+ dotest lockfiles-9 "${testcvs} -q co -l ." ""
+
+ ###
+ ### There are race conditions in the following tests, but hopefully
+ ### the 5 seconds the first process waits to remove the lockdir and
+ ### the 30 seconds CVS waits betweens checks will be significant
+ ### enough to render the case moot.
+ ###
+ # Considers the following cases:
+ #
+ # Lock Present
+ # Operation Allowed (case #)
+ #
+ # Read Promotable Write
+ # _______ __________ ______
+ # Read |Yes (1) Yes (2) No (3)
+ # Promotable Read |Yes (4) No (5) No (6)
+ # Write |No (7) No (8) No (9)
+ #
+ # Tests do not appear in same ordering as table:
+ # 1. Read when read locks are present...
+ # 2. Read when promotable locks are present...
+ # 3. Don't read when write locks present...
+ # 4. Read but don't write when read locks are present... (fail
+ # commit up-to-date check with promotable lock present).
+ # 5. Don't allow promotable read when promotable locks are present...
+ # (fail to perform commit up-to-date check with promotable lock
+ # present).
+ # 6. Don't allow promotable read when write locks are present...
+ # (fail to perform commit up-to-date check with promotable lock
+ # present).
+ # 7. Don't write when read locks are present...
+ # 8. Don't write when promotable locks are present...
+ # 9. Don't write when write locks are present...
+
+ # 3. Don't read when write locks present...
+ mkdir "$TESTDIR/locks/first-dir/#cvs.lock"
+ (sleep 5; rmdir "$TESTDIR/locks/first-dir/#cvs.lock")&
+ dotest lockfiles-10 "$testcvs -q co -l first-dir" \
+"$SPROG checkout: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir
+$SPROG checkout: \[[0-9:]*\] obtained lock in $CVSROOT_DIRNAME/first-dir"
+
+ # 1. Read when read locks are present...
+ touch "$TESTDIR/locks/first-dir/#cvs.rfl.test.lock"
+ dotest lockfiles-11 "$testcvs -q co -l first-dir"
+ rm "$TESTDIR/locks/first-dir/#cvs.rfl.test.lock"
+
+ # 2. Read when promotable locks are present...
+ cd ..
+ mkdir 3; cd 3
+ touch "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock"
+ dotest lockfiles-12 "$testcvs -q co first-dir" \
+"U first-dir/sdir/ssdir/file1"
+ rm "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock"
+
+ # 7. Don't write when read locks are present...
+ echo I always have trouble coming up with witty text for the test
files >>first-dir/sdir/ssdir/file1
+ touch "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.rfl.test.lock"
+ (sleep 5; rm
"$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.rfl.test.lock")&
+ dotest lockfiles-13 "$testcvs -q ci -mconflict first-dir" \
+"$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: \[[0-9:]*\] obtained lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$CVSROOT_DIRNAME/first-dir/sdir/ssdir/file1,v <-- first-dir/sdir/ssdir/file1
+new revision: 1\.2; previous revision: 1\.1"
+
+ # 4. Read but don't write when read locks are present... (fail
+ # commit up-to-date check with promotable lock present).
+ cd ../2
+ echo something that would render readers all full of smiles
>>first-dir/sdir/ssdir/file1
+ touch "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.rfl.test.lock"
+ dotest_fail lockfiles-14 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$SPROG commit: Up-to-date check failed for \`first-dir/sdir/ssdir/file1'
+$SPROG \[commit aborted\]: correct above errors first!"
+ rm "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.rfl.test.lock"
+
+ # 5. Don't allow promotable read when promotable locks are present...
+ # (fail to perform commit up-to-date check with promotable lock
+ # present).
+ touch "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock"
+ (sleep 5; rm
"$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock")&
+ dotest_fail lockfiles-15 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: \[[0-9:]*\] obtained lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: Up-to-date check failed for \`first-dir/sdir/ssdir/file1'
+$SPROG \[commit aborted\]: correct above errors first!"
+
+ # 6. Don't allow promotable read when write locks are present...
+ # (fail to perform commit up-to-date check with promotable lock
+ # present).
+ mkdir "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.lock"
+ (sleep 5; rmdir "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.lock")&
+ dotest_fail lockfiles-16 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: \[[0-9:]*\] obtained lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: Up-to-date check failed for \`first-dir/sdir/ssdir/file1'
+$SPROG \[commit aborted\]: correct above errors first!"
+
+ # 8. Don't write when promotable locks are present...
+ dotest lockfiles-17 "$testcvs -Q up -C first-dir/sdir/ssdir"
+ echo the kinds of smiles that light faces for miles
>>first-dir/sdir/ssdir/file1
+ touch "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock"
+ (sleep 5; rm
"$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.pfl.test.lock")&
+ dotest lockfiles-18 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: \[[0-9:]*\] obtained lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$CVSROOT_DIRNAME/first-dir/sdir/ssdir/file1,v <-- first-dir/sdir/ssdir/file1
+new revision: 1\.3; previous revision: 1\.2"
+
+ # 9. Don't write when write locks are present...
+ echo yet this poem would probably only give longfellow bile
>>first-dir/sdir/ssdir/file1
+ mkdir "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.lock"
+ (sleep 5; rmdir "$TESTDIR/locks/first-dir/sdir/ssdir/#cvs.lock")&
+ dotest lockfiles-19 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$SPROG commit: \[[0-9:]*\] obtained lock in
$CVSROOT_DIRNAME/first-dir/sdir/ssdir
+$CVSROOT_DIRNAME/first-dir/sdir/ssdir/file1,v <-- first-dir/sdir/ssdir/file1
+new revision: 1\.4; previous revision: 1\.3"
+
+ # 10. Don't write when history locks are present...
+ echo have you ever heard a poem quite so vile\?
>>first-dir/sdir/ssdir/file1
+ mkdir "$TESTDIR/locks/CVSROOT/#cvs.history.lock"
+ (sleep 5; rmdir "$TESTDIR/locks/CVSROOT/#cvs.history.lock")&
+ dotest lockfiles-20 "$testcvs -q ci -mnot-up-to-date first-dir" \
+"$CVSROOT_DIRNAME/first-dir/sdir/ssdir/file1,v <-- first-dir/sdir/ssdir/file1
+new revision: 1\.5; previous revision: 1\.4
+$SPROG commit: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/CVSROOT
+$SPROG commit: \[[0-9:]*\] obtained lock in $CVSROOT_DIRNAME/CVSROOT"
+
+ dotest lockfiles-21 "$testcvs -Q tag newtag first-dir"
+
+ rm $CVSROOT_DIRNAME/CVSROOT/val-tags
+ mkdir "$TESTDIR/locks/CVSROOT/#cvs.val-tags.lock"
+ (sleep 5; rmdir "$TESTDIR/locks/CVSROOT/#cvs.val-tags.lock")&
+ dotest lockfiles-22 "$testcvs -q up -r newtag first-dir" \
+"$SPROG update: \[[0-9:]*\] waiting for $username's lock in
$CVSROOT_DIRNAME/CVSROOT
+$SPROG update: \[[0-9:]*\] obtained lock in $CVSROOT_DIRNAME/CVSROOT"
+
+ cd CVSROOT
+ dotest lockfiles-cleanup-1 "$testcvs -q up -pr1.1 config >config" ""
+ dotest lockfiles-cleanup-2 "$testcvs -q ci -m config-it" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ dokeep
+ cd ../..
+ # Restore umask.
+ umask $save_umask
+ unset CVSUMASK
+ rm -r $TESTDIR/locks
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ backuprecover)
+ # Tests to make sure we get the expected behavior
+ # when we recover a repository from an old backup
+ #
+ # Details:
+ # Backup will be older than some developer's workspaces
+ # This means the first attempt at an update will fail
+ # The workaround for this is to replace the CVS
+ # directories with those from a "new" checkout from
+ # the recovered repository. Due to this, multiple
+ # merges should cause conflicts (the same data
+ # will be merged more than once).
+ # A workspace updated before the date of the recovered
+ # copy will not need any extra attention
+ #
+ # Note that backuprecover-15 is probably a failure case
+ # If nobody else had a more recent update, the data would be lost
+ # permanently
+ # Granted, the developer should have been notified not to do this
+ # by now, but still...
+ #
+ mkdir backuprecover; cd backuprecover
+ mkdir 1; cd 1
+ dotest backuprecover-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest backuprecover-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ mkdir dir
+ dotest backuprecover-3 "${testcvs} add dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir added to the repository"
+ touch file1 dir/file2
+ dotest backuprecover-4 "${testcvs} -q add file1 dir/file2" \
+"${SPROG} add: use \`${SPROG} commit' to add these files permanently"
+ dotest backuprecover-5 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+initial revision: 1\.1"
+ echo "Line one" >>file1
+ echo " is the place" >>file1
+ echo " we like to begin" >>file1
+ echo "Anything else" >>file1
+ echo " looks like" >>file1
+ echo " a sin" >>file1
+ echo "File 2" >>dir/file2
+ echo " is the place" >>dir/file2
+ echo " the rest of it goes" >>dir/file2
+ echo "Why I don't use" >>dir/file2
+ echo " something like 'foo'" >>dir/file2
+ echo " God only knows" >>dir/file2
+ dotest backuprecover-6 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Simulate the lazy developer
+ # (he did some work but didn't check it in...)
+ cd ../..
+ mkdir 2; cd 2
+ dotest backuprecover-7 "${testcvs} -Q co first-dir" ''
+ cd first-dir
+ sed -e "s/looks like/just looks like/" file1 >tmp; mv tmp file1
+ sed -e "s/don't use/don't just use/" dir/file2 >tmp; mv tmp dir/file2
+
+ # developer 1 is on a roll
+ cd ../../1/first-dir
+ echo "I need some more words" >>file1
+ echo " to fill up this space" >>file1
+ echo " anything else would be a disgrace" >>file1
+ echo "My rhymes cross many boundries" >>dir/file2
+ echo " this time it's files" >>dir/file2
+ echo " a word that fits here would be something like dials"
>>dir/file2
+ dotest backuprecover-8 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.3; previous revision: 1\.2"
+
+ # Save a backup copy
+ cp -R $CVSROOT_DIRNAME/first-dir $TESTDIR/backup
+
+ # Simulate developer 3
+ cd ../..
+ mkdir 3; cd 3
+ dotest backuprecover-9a "${testcvs} -Q co first-dir" ''
+ cd first-dir
+ echo >>file1
+ echo >>dir/file2
+ echo "Developer 1 makes very lame rhymes" >>file1
+ echo " I think he should quit and become a mime" >>file1
+ echo "What the %*^# kind of rhyme crosses a boundry?" >>dir/file2
+ echo " I think you should quit and get a job in the foundry"
>>dir/file2
+ dotest backuprecover-9b "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.4; previous revision: 1\.3"
+
+ # Developer 4 so we can simulate a conflict later...
+ cd ../..
+ mkdir 4; cd 4
+ dotest backuprecover-10 "${testcvs} -Q co first-dir" ''
+ cd first-dir
+ sed -e "s/quit and/be fired so he can/" dir/file2 >tmp; mv tmp
dir/file2
+
+ # And back to developer 1
+ cd ../../1/first-dir
+ dotest backuprecover-11 "${testcvs} -Q update" ''
+ echo >>file1
+ echo >>dir/file2
+ echo "Oh yeah, well rhyme this" >>file1
+ echo " developer three" >>file1
+ echo " you want opposition" >>file1
+ echo " you found some in me!" >>file1
+ echo "I'll give you mimes" >>dir/file2
+ echo " and foundries galore!" >>dir/file2
+ echo " your head will spin" >>dir/file2
+ echo " once you find what's in store!" >>dir/file2
+ dotest backuprecover-12 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.5; previous revision: 1\.4"
+
+ # developer 3'll do a bit of work that never gets checked in
+ cd ../../3/first-dir
+ dotest backuprecover-13 "${testcvs} -Q update" ''
+ sed -e "s/very/some extremely/" file1 >tmp; mv tmp file1
+ dotest backuprecover-14 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.6; previous revision: 1\.5"
+ echo >>file1
+ echo "Tee hee hee hee" >>file1
+ echo >>dir/file2
+ echo "Find what's in store?" >>dir/file2
+ echo " Oh, I'm so sure!" >>dir/file2
+ echo " You've got an ill, and I have the cure!" >>dir/file2
+
+ # Slag the original and restore it a few revisions back
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ modify_repo mv $TESTDIR/backup $CVSROOT_DIRNAME/first-dir
+
+ # Have developer 1 try an update and lose some data
+ #
+ # Feel free to imagine the horrific scream of despair
+ cd ../../1/first-dir
+ dotest backuprecover-15 "${testcvs} update" \
+"${SPROG} update: Updating .
+U file1
+${SPROG} update: Updating dir
+U dir/file2"
+
+ # Developer 3 tries the same thing (he has an office)
+ # but fails without losing data since all of his files have
+ # uncommitted changes
+ cd ../../3/first-dir
+ dotest_fail backuprecover-16 "${testcvs} update" \
+"${SPROG} update: Updating \.
+${SPROG} \[update aborted\]: could not find desired version 1\.6 in
${CVSROOT_DIRNAME}/first-dir/file1,v"
+
+ # create our workspace fixin' script
+ cd ../..
+ echo \
+"#!$TESTSHELL
+
+# This script will copy the CVS database dirs from the checked out
+# version of a newly recovered repository and replace the CVS
+# database dirs in a workspace with later revisions than those in the
+# recovered repository
+cd repos-first-dir
+DATADIRS=\`find . -name CVS -print\`
+cd ../first-dir
+find . -name CVS -print | xargs rm -rf
+for file in \${DATADIRS}; do
+ cp -R ../repos-first-dir/\${file} \${file}
+done" >fixit
+
+ # We only need to fix the workspaces of developers 3 and 4
+ # (1 lost all her data and 2 has an update date from
+ # before the date the backup was made)
+ cd 3
+ dotest backuprecover-17 \
+ "${testcvs} -Q co -d repos-first-dir first-dir" ''
+ cd ../4
+ dotest backuprecover-18 \
+ "${testcvs} -Q co -d repos-first-dir first-dir" ''
+ sh ../fixit
+ cd ../3; sh ../fixit
+
+ # (re)commit developer 3's stuff
+ cd first-dir
+ dotest backuprecover-19 "${testcvs} -q ci -mrecover/merge" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.4; previous revision: 1\.3"
+
+ # and we should get a conflict on developer 4's stuff
+ cd ../../4/first-dir
+ dotest backuprecover-20 "${testcvs} update" \
+"${SPROG} update: Updating \.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.3
+retrieving revision 1\.4
+Merging differences between 1\.3 and 1\.4 into file1
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in file1
+C file1
+${SPROG} update: Updating dir
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir/file2,v
+retrieving revision 1\.3
+retrieving revision 1\.4
+Merging differences between 1\.3 and 1\.4 into file2
+rcsmerge: warning: conflicts during merge
+${SPROG} update: conflicts found in dir/file2
+C dir/file2"
+ sed -e \
+"/^<<<<<<</,/^=======/d
+/^>>>>>>>/d" file1 >tmp; mv tmp file1
+ sed -e \
+"/^<<<<<<</,/^=======/d
+/^>>>>>>>/d
+s/quit and/be fired so he can/" dir/file2 >tmp; mv tmp dir/file2
+ dotest backuprecover-21 "${testcvs} -q ci -mrecover/merge" \
+"$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.5; previous revision: 1\.4"
+
+ # go back and commit developer 2's stuff to prove it can still be done
+ cd ../../2/first-dir
+ dotest backuprecover-22 "${testcvs} -Q update" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.2
+retrieving revision 1\.4
+Merging differences between 1\.2 and 1\.4 into file1
+RCS file: ${CVSROOT_DIRNAME}/first-dir/dir/file2,v
+retrieving revision 1\.2
+retrieving revision 1\.5
+Merging differences between 1\.2 and 1\.5 into file2"
+ dotest backuprecover-23 "${testcvs} -q ci -mtest" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4
+$CVSROOT_DIRNAME/first-dir/dir/file2,v <-- dir/file2
+new revision: 1\.6; previous revision: 1\.5"
+
+ # and restore the data to developer 1
+ cd ../../1/first-dir
+ dotest backuprecover-24 "${testcvs} -Q update" ''
+
+ dokeep
+ cd ../../..
+ rm -r backuprecover
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ sshstdio)
+ # CVS_RSH=ssh can have a problem with a non-blocking stdio
+ # in some cases. So, this test is all about testing :ext:
+ # with CVS_RSH=ssh. The problem is that not all machines
+ # will necessarily have ssh available, so be prepared to
+ # skip this test.
+
+ if $proxy; then
+ notproxy sshstdio
+ continue
+ fi
+
+ if $remote; then :; else
+ remoteonly sshstdio
+ continue
+ fi
+
+ require_ssh
+ if test $? -eq 77; then
+ skip sshstdio "$skipreason"
+ continue
+ fi
+
+ SSHSTDIO_ROOT=:ext:$host$CVSROOT_DIRNAME
+
+ mkdir sshstdio; cd sshstdio
+ dotest sshstdio-1 "$testcvs -d $SSHSTDIO_ROOT -q co -l ."
+ mkdir first-dir
+ dotest sshstdio-2 "$testcvs add first-dir" \
+ "Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+
a='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+
c='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+ # Generate 1024 lines of $a
+ cnt=0
+ echo $a > aaa
+ while [ $cnt -lt 5 ] ; do
+ cnt=`expr $cnt + 1` ;
+ mv aaa aaa.old
+ cat aaa.old aaa.old aaa.old aaa.old > aaa
+ done
+ dotest sshstdio-3 "$testcvs -q add aaa" \
+"$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest sshstdio-4 "$testcvs -q ci -mcreate aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+initial revision: 1\.1"
+ # replace lines 1, 512, 513, 1024 with $c
+ sed 510q < aaa > aaa.old
+ (echo $c; cat aaa.old; echo $c; \
+ echo $c; cat aaa.old; echo $c) > aaa
+ dotest sshstdio-5 "$testcvs -q ci -mmodify-it aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.2; previous revision: 1\.1"
+ cat > wrapper.sh <<EOF
+#!$TESTSHELL
+exec "\$@" 2>&1 < /dev/null | cat
+EOF
+ chmod +x wrapper.sh
+ ./wrapper.sh \
+ $testcvs -z5 -Q diff --side-by-side -W 500 -r 1.1 -r 1.2 \
+ aaa > wrapper.dif
+
+ $testcvs -z5 -Q diff --side-by-side -W 500 -r 1.1 -r 1.2 \
+ aaa > good.dif
+
+ dotest sshstdio-6 "$diff_u wrapper.dif good.dif"
+
+ dokeep
+ cd ../..
+ CVS_RSH=$save_CVS_RSH; export CVS_RSH
+ rm -r sshstdio
+ rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ parseroot2)
+ # Test some :ext: roots for consistancy.
+ if $remote; then :; else
+ remoteonly parseroot2
+ continue
+ fi
+
+ require_rsh "$CVS_RSH"
+ if test $? -eq 77; then
+ skip parseroot2 "$skipreason"
+ continue
+ fi
+
+ # Test checking out and subsequently updating with some different
+ # CVSROOTs.
+
+ # A standard case, hostname:dirname.
+ mkdir parseroot2; cd parseroot2
+ save_CVSROOT=$CVSROOT
+ CVSROOT=$host:$CVSROOT_DIRNAME
+ dotest parseroot2-1 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ dotest parseroot2-2 "$testcvs -Q up"
+ cd ..
+
+ # A degenerate remote case, just the server name and the directory
+ # name, with no :'s to help parsing. It can be mistaken for a
+ # relative directory name.
+ rm -r CVSROOT
+ CVSROOT=$host$CVSROOT_DIRNAME
+ dotest parseroot2-3 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ dotest parseroot2-4 "$testcvs -Q up"
+
+ dokeep
+ cd ../..
+ CVSROOT=$save_CVSROOT
+ rm -r parseroot2
+ ;;
+
+
+
+ parseroot3)
+ # Test some :ext: roots for consistancy.
+ if $remote; then :; else
+ remoteonly parseroot3
+ continue
+ fi
+
+ require_rsh "$CVS_RSH"
+ if test $? -eq 77; then
+ skip parseroot3 "$skipreason"
+ continue
+ fi
+
+ # Test checking out and subsequently updating with some different
+ # CVSROOTs.
+
+ # A standard case, hostname:dirname.
+ mkdir parseroot3; cd parseroot3
+ save_CVSROOT=$CVSROOT
+ save_CVS_RSH=$CVS_RSH
+ save_CVS_SERVER=$CVS_SERVER
+ unset CVS_RSH
+ unset CVS_SERVER
+
CVSROOT=":ext;CVS_RSH=$save_CVS_RSH;CVS_SERVER=$save_CVS_SERVER:$host:$CVSROOT_DIRNAME"
+ dotest parseroot3-1 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ dotest parseroot3-2 "$testcvs -Q up"
+ cd ..
+
+ # Initial checkout.
+ rm -r CVSROOT
+
CVSROOT=":ext;cvs_RSH=$save_CVS_RSH;CVS_Server=$save_CVS_SERVER:$host$CVSROOT_DIRNAME"
+ dotest parseroot3-3 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ dotest parseroot3-4 "$testcvs -Q up"
+ cd ..
+
+ # Checkout bogus values for Redirect
+ rm -r CVSROOT
+
CVSROOT=":ext;Redirect=bogus;CVS_RSH=$save_CVS_RSH;CVS_SERVER=$save_CVS_SERVER:$host$CVSROOT_DIRNAME"
+ dotest parseroot3-5 "$testcvs -Q co CVSROOT" \
+"$SPROG checkout: CVSROOT: unrecognized value \`bogus' for \`Redirect'"
+ cd CVSROOT
+ # FIXCVS: parse_cvsroot is called more often that is
+ # desirable.
+ dotest parseroot3-6 "$testcvs -Q up" \
+"$SPROG update: CVSROOT: unrecognized value \`bogus' for \`Redirect'"
+ cd ..
+
+ # Checkout good values for Redirect
+ rm -r CVSROOT
+
CVSROOT=":EXT;Redirect=no;CVS_RSH=$save_CVS_RSH;CVS_SERVER=$save_CVS_SERVER:$host$CVSROOT_DIRNAME"
+ dotest parseroot3-7 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ dotest parseroot3-8 "$testcvs -Q up"
+ cd ..
+
+ dotest parseroot3-9 "$testcvs -Q co -ldtop ."
+ dotest parseroot3-10 "test -d top"
+ dotest parseroot3-11 "test -d top/CVS"
+ dotest parseroot3-10 "cat top/CVS/Root" "$CVSROOT"
+
+ dokeep
+ cd ..
+ CVSROOT=$save_CVSROOT
+ CVS_RSH=$save_CVS_RSH
+ CVS_SERVER=$save_CVS_SERVER
+ export CVS_RSH CVS_SERVER
+ rm -r parseroot3
+ ;;
+
+
+
+ history)
+ # CVSROOT/history tests:
+ # history: various "cvs history" invocations
+ # basic2: Generating the CVSROOT/history file via CVS commands.
+
+ # Put in some data for the history file (discarding what was
+ # there before). Note that this file format is fixed; the
+ # user may wish to analyze data from a previous version of
+ # CVS. If we phase out this format, it should be done
+ # slowly and carefully.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ cat <<EOF >$CVSROOT_DIRNAME/CVSROOT/history
+O3395c677|anonymous|<remote>/*0|ccvs||ccvs
+O3396c677|anonymous|<remote>/src|ccvs||src
+O3397c677|kingdon|<remote>/*0|ccvs||ccvs
+M339cafae|nk|<remote>|ccvs/src|1.229|sanity.sh
+M339cafff|anonymous|<remote>|ccvs/src|1.23|Makefile
+M339dc339|kingdon|~/work/*0|ccvs/src|1.231|sanity.sh
+W33a6eada|anonymous|<remote>*4|ccvs/emx||Makefile.in
+C3b235f50|kingdon|<remote>|ccvs/emx|1.3|README
+M3b23af50|kingdon|~/work/*0|ccvs/doc|1.281|cvs.texinfo
+EOF
+
+ dotest history-1 "${testcvs} history -e -a" \
+"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\*
+O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\*
+M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src ==
<remote>
+W 1997-06-17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx ==
<remote>/emx
+O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\*
+M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx ==
<remote>
+M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc ==
~/work/ccvs/doc
+M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src ==
<remote>"
+
+ dotest history-2 "${testcvs} history -e -a -D '10 Jun 1997 13:00 UT'"
\
+"W 1997-06-17 19:51 ${PLUS}0000 anonymous Makefile\.in ccvs/emx ==
<remote>/emx
+M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx ==
<remote>
+M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc ==
~/work/ccvs/doc"
+
+ dotest history-3 "${testcvs} history -e -a -D '10 Jun 2001 13:00 UT'"
\
+"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs\.texinfo ccvs/doc ==
~/work/ccvs/doc"
+
+ dotest history-4 "${testcvs} history -ac sanity.sh" \
+"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>"
+
+ dotest history-5 "${testcvs} history -a -xCGUWAMR README sanity.sh" \
+"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote>
+M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>"
+
+ dotest history-6 "${testcvs} history -xCGUWAMR -a -f README -f
sanity.sh" \
+"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote>
+M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>"
+
+ dotest history-7 "${testcvs} history -xCGUWAMR -a -f sanity.sh
README" \
+"M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity\.sh ccvs/src ==
~/work/ccvs/src
+C 2001-06-10 11:51 ${PLUS}0000 kingdon 1\.3 README ccvs/emx == <remote>
+M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity\.sh ccvs/src == <remote>"
+
+ dotest history-8 "${testcvs} history -ca -D '1970-01-01 00:00 UT'" \
+"M 1997-06-10 01:36 ${PLUS}0000 nk 1\.229 sanity.sh ccvs/src ==
<remote>
+M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src ==
<remote>
+M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src ==
~/work/ccvs/src
+M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc ==
~/work/ccvs/doc"
+
+ dotest history-9 "${testcvs} history -acl" \
+"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc ==
~/work/ccvs/doc
+M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src ==
<remote>
+M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src ==
~/work/ccvs/src"
+
+ dotest history-10 "${testcvs} history -lca -D '1970-01-01 00:00 UT'" \
+"M 2001-06-10 17:33 ${PLUS}0000 kingdon 1\.281 cvs.texinfo ccvs/doc ==
~/work/ccvs/doc
+M 1997-06-10 01:38 ${PLUS}0000 anonymous 1\.23 Makefile ccvs/src ==
<remote>
+M 1997-06-10 21:12 ${PLUS}0000 kingdon 1\.231 sanity.sh ccvs/src ==
~/work/ccvs/src"
+
+ dotest history-11 "${testcvs} history -aw" \
+"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\*
+O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\*
+O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\*"
+
+ dotest history-12 "${testcvs} history -aw -D'1970-01-01 00:00 UT'" \
+"O 1997-06-04 19:48 ${PLUS}0000 anonymous ccvs =ccvs= <remote>/\*
+O 1997-06-05 14:00 ${PLUS}0000 anonymous ccvs =src= <remote>/\*
+O 1997-06-06 08:12 ${PLUS}0000 kingdon ccvs =ccvs= <remote>/\*"
+ ;;
+
+
+
+ big)
+
+ # 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.
+
+ modify_repo 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" \
+"$SPROG add: scheduling file .file1. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest big-3 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ 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_fail big-4b "$testcvs -q diff -u" \
+"Index: file1
+===================================================================
+RCS file: $CVSROOT_DIRNAME/first-dir/file1,v
+retrieving revision 1\.1
+diff -u -r1\.1 file1
+--- file1 $RFCDATE 1\.1
+$PLUS$PLUS$PLUS file1 $RFCDATE
+@@ -998,3 ${PLUS}998,4 @@
+ This is line (9,9,7) which goes into the file file1 for testing
+ This is line (9,9,8) which goes into the file file1 for testing
+ This is line (9,9,9) which goes into the file file1 for testing
+${PLUS}add a line to the end"
+
+ dotest big-5 "$testcvs -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ 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" "U file1"
+
+ dokeep
+ cd ../..
+ rm -r first-dir 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ modes)
+ # Test repository permissions (CVSUMASK and so on).
+ # Although the tests in this section "cheat" by testing
+ # repository permissions, which are sort of not a user-visible
+ # sort of thing, the modes do have user-visible consequences,
+ # such as whether a second user can check out the files. But
+ # it would be awkward to test the consequences, so we don't.
+
+ # Solaris /bin/sh doesn't support export -n. I'm not sure
+ # what we can do about this, other than hope that whoever
+ # is running the tests doesn't have CVSUMASK set.
+ #export -n CVSUMASK # if unset, defaults to 002
+
+ save_umask=`umask`
+ umask 077
+ mkdir 1; cd 1
+ dotest modes-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest modes-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch aa
+ dotest modes-3 "${testcvs} add aa" \
+"${SPROG} add: scheduling file .aa. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest modes-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+initial revision: 1\.1"
+ # Yawn. Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-5remotehost "$CVS_RSH $remotehost 'ls -l
${CVSROOT_DIRNAME}/first-dir/aa,v'" \
+"-r--r--r-- .*"
+ else
+ dotest modes-5 "ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v" \
+"-r--r--r-- .*"
+ fi
+
+ # 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" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+new revision: 1\.2; previous revision: 1\.1"
+ # 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.
+ #
+ # Yeah, yeah. Search for "Cygwin".
+ if test -n "$remotehost"; then
+ dotest modes-7remotehost "$CVS_RSH $remotehost 'ls -l
${CVSROOT_DIRNAME}/first-dir/aa,v'" \
+"-r--r--r-- .*"
+ else
+ dotest modes-7 "ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v" \
+"-r--r--r-- .*"
+ fi
+
+ # OK, now manually change the modes and see what happens.
+ #
+ # Cygwin, already.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod g=r,o=
${CVSROOT_DIRNAME}/first-dir/aa,v"
+ else
+ chmod g=r,o= ${CVSROOT_DIRNAME}/first-dir/aa,v
+ fi
+ echo second line >>aa
+ dotest modes-7a "${testcvs} -q ci -m set-execute-bit" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+new revision: 1\.3; previous revision: 1\.2"
+ # Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-7bremotehost "$CVS_RSH $remotehost 'ls -l
${CVSROOT_DIRNAME}/first-dir/aa,v'" \
+"-r--r----- .*"
+ else
+ dotest modes-7b "ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v" \
+"-r--r----- .*"
+ fi
+
+ # Check admin --execute
+ # Cygwin, already.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost \
+"chmod ugo=r ${CVSROOT_DIRNAME}/first-dir/aa,v"
+ else
+ chmod ugo=r ${CVSROOT_DIRNAME}/first-dir/aa,v
+ fi
+ dotest modes-execute-1 "${testcvs} admin --execute aa" \
+"RCS file: $CVSROOT_DIRNAME/first-dir/aa,v
+done"
+ # Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-execute-2r \
+"$CVS_RSH $remotehost 'ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v'" \
+"-r-xr-xr-x .*"
+ else
+ dotest modes-execute-2 "ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v" \
+"-r-xr-xr-x .*"
+ fi
+
+ # Test if admin --no-execute removes the execute bit:
+ dotest modes-execute-3 "${testcvs} admin --no-execute aa" \
+"RCS file: $CVSROOT_DIRNAME/first-dir/aa,v
+done"
+ # Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-execue-4r \
+"$CVS_RSH $remotehost 'ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v'" \
+"-r--r--r-- .*"
+ else
+ dotest modes-execute-4 \
+"ls -l ${CVSROOT_DIRNAME}/first-dir/aa,v" \
+"-r--r--r-- .*"
+ fi
+
+ CVSUMASK=007
+ export CVSUMASK
+ touch ab
+ # Might as well test the execute bit too.
+ chmod +x ab
+ dotest modes-8 "$testcvs add ab" \
+"$SPROG add: scheduling file .ab. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest modes-9 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/ab,v <-- ab
+initial revision: 1\.1"
+
+ # The ssh-wrapper script set up by this script forwards CVSUMASK to
+ # the server. In practice it would be set on the server in some
+ # other manner (for instance, by the `env' command, or as an option
+ # in the xinted.conf file).
+ #
+ # I don't recall why, but I used to look for:
+ #
+ # dotest modes-10remotehost \
+ # "$CVS_RSH $remotehost 'ls -l $CVSROOT_DIRNAME/first-dir/ab,v'" \
+ # "-r--r--r--.*"
+ #
+ # here when $remotehost was set. I'm not sure why. Maybe this was
+ # one of the innumerable Cygwin issues?
+ dotest modes-10 "ls -l $CVSROOT_DIRNAME/first-dir/ab,v" \
+"-r-xr-x---.*"
+
+ # Checkout --no-execute
+ # Test if admin --no-execute removes the execute bit:
+ dotest modes-execute-5 "${testcvs} admin --no-execute ab" \
+"RCS file: $CVSROOT_DIRNAME/first-dir/ab,v
+done"
+ # Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-execue-6r \
+"$CVS_RSH $remotehost 'ls -l ${CVSROOT_DIRNAME}/first-dir/ab,v'" \
+"-r--r----- .*"
+ else
+ dotest modes-execute-6 \
+"ls -l ${CVSROOT_DIRNAME}/first-dir/ab,v" \
+"-r--r----- .*"
+ dotest modes-execute-7 "${testcvs} admin --execute ab" \
+"RCS file: $CVSROOT_DIRNAME/first-dir/ab,v
+done"
+ fi
+ # Cygwin.
+ if test -n "$remotehost"; then
+ dotest modes-execue-8r \
+"$CVS_RSH $remotehost 'ls -l ${CVSROOT_DIRNAME}/first-dir/ab,v'" \
+"-r-xr-x--- .*"
+ else
+ dotest modes-execute-8 "ls -l ${CVSROOT_DIRNAME}/first-dir/ab,v" \
+"-r-xr-x--- .*"
+ fi
+
+ # OK, now add a file on a branch. Check that the mode gets
+ # set the same way (it is a different code path in CVS).
+ dotest modes-11 "${testcvs} -q tag -b br" 'T aa
+T ab'
+ dotest modes-12 "${testcvs} -q update -r br" ''
+ touch ac
+ dotest modes-13 "${testcvs} add ac" \
+"${SPROG} add: scheduling file .ac. for addition on branch .br.
+${SPROG} add: use .${SPROG} 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" \
+"$CVSROOT_DIRNAME/first-dir/Attic/ac,v <-- ac
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # ssh-wrapper forwards CVSUMASK. See modes-10 for notes.
+ dotest modes-15 \
+"ls -l ${CVSROOT_DIRNAME}/first-dir/Attic/ac,v" \
+"-r--r-----.*"
+
+ dokeep
+ cd ../..
+ # Restore umask.
+ umask $save_umask
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ modes2)
+ # More tests of file permissions in the working directory
+ # and that sort of thing.
+
+ # The usual setup, file first-dir/aa with two revisions.
+ mkdir 1; cd 1
+ dotest modes2-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest modes2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch aa
+ dotest modes2-3 "${testcvs} add aa" \
+"${SPROG} add: scheduling file .aa. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest modes2-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+initial revision: 1\.1"
+ echo "more money" >> aa
+ dotest modes2-5 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+new revision: 1\.2; previous revision: 1\.1"
+
+ # OK, here is the test. The idea is to see what
+ # No_Difference does if it can't open the file.
+ # If we don't change the st_mtime, CVS doesn't even try to read
+ # the file. Note that some versions of "touch" require that we
+ # do this while the file is still writable.
+ touch aa
+ chmod a= aa
+ # Don't try this when permissions are broken, as with Cygwin.
+ if ${LS} ${CVSROOT_DIRNAME}/first-dir >/dev/null 2>&1; then :; else
+ dotest_fail modes2-6 "${testcvs} -q update -r 1.1 aa" \
+"${CPROG} \[update aborted\]: cannot open file aa for comparing: Permission
denied" \
+"${CPROG} \[update aborted\]: reading aa: Permission denied"
+ fi
+
+ dokeep
+ chmod u+rwx aa
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ modes3)
+ # Repository permissions. Particularly, what happens if we
+ # can't read/write in the repository.
+ # TODO: the case where we can access the repository, just not
+ # the attic (may that one can remain a fatal error, seems less
+ # useful for access control).
+ mkdir 1; cd 1
+ dotest modes3-1 "$testcvs -q co -l ."
+ mkdir first-dir second-dir
+ dotest modes3-2 "${testcvs} add first-dir second-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository
+Directory ${CVSROOT_DIRNAME}/second-dir added to the repository"
+ touch first-dir/aa second-dir/ab
+ dotest modes3-3 "${testcvs} add first-dir/aa second-dir/ab" \
+"${SPROG} add: scheduling file .first-dir/aa. for addition
+${SPROG} add: scheduling file .second-dir/ab. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest modes3-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- first-dir/aa
+initial revision: 1\.1
+$CVSROOT_DIRNAME/second-dir/ab,v <-- second-dir/ab
+initial revision: 1\.1"
+ # quiet down this one as it will be noisy in proxy mode
+ modify_repo chmod a= $CVSROOT_DIRNAME/first-dir >/dev/null 2>&1
+ if ${LS} ${CVSROOT_DIRNAME}/first-dir >/dev/null 2>&1; then
+ # Avoid this test under Cygwin since permissions work differently
+ # there.
+ #
+ # This test also gets avoided under Mac OS X since the system `ls'
+ # is broken and exits with a 0 status despite the permission
+ # denied error.
+ if test -n "$remotehost"; then
+ cygwin_hack=false
+ else
+ cygwin_hack=:
+ fi
+ else
+ cygwin_hack=false
+ fi
+
+ cd $TESTDIR/1
+ if $cygwin_hack; then :; else
+ dotest modes3-5 "${testcvs} update" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating first-dir
+${SPROG} update: cannot open directory ${CVSROOT_DIRNAME}/first-dir:
Permission denied
+${SPROG} update: skipping directory first-dir
+${SPROG} update: Updating second-dir"
+ fi
+
+ # OK, I can see why one might say the above case could be a
+ # fatal error, because normally users without access to first-dir
+ # won't have it in their working directory. But the next
+ # one is more of a problem if it is fatal.
+ #
+ # The second text string below is for Cygwin again, and again it
+ # should really be XFAIL under Cygwin, but for now deal with the
+ # passing opendir by accepting the alternate string.
+ rm -r first-dir
+ dotest modes3-6 "${testcvs} update -dP" \
+"${SPROG} update: Updating .
+${SPROG} update: Updating CVSROOT
+U ${DOTSTAR}
+${SPROG} update: Updating first-dir
+${SPROG} update: cannot open directory ${CVSROOT_DIRNAME}/first-dir:
Permission denied
+${SPROG} update: skipping directory first-dir
+${SPROG} update: Updating second-dir" \
+"${SPROG} update: Updating .
+${SPROG} update: Updating CVSROOT
+U ${DOTSTAR}
+${SPROG} update: Updating first-dir
+${SPROG} update: Updating second-dir"
+
+ dokeep
+ cd ..
+ rm -r 1
+ # quiet down this one as it will be noisy in proxy mode
+ modify_repo chmod u+rwx $CVSROOT_DIRNAME/first-dir 2>/dev/null
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir \
+ $CVSROOT_DIRNAME/second-dir
+ ;;
+
+
+
+ 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch aa
+ echo '$''Id$' >kw
+ # Cygwin, *cough*, puts the year in the time column until the minute
+ # is no longer the current minute. Sleep 60 seconds to avoid this
+ # problem.
+ sleep 60
+ 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" \
+"${SPROG} add: scheduling file .aa. for addition
+${SPROG} add: scheduling file .kw. for addition
+${SPROG} add: use .${SPROG} 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 \
+"$diff_u $TESTDIR/1/stamp.aa.touch $TESTDIR/1/stamp.aa.add"
+ dotest stamps-4kw \
+"$diff_u $TESTDIR/1/stamp.kw.touch $TESTDIR/1/stamp.kw.add"
+ sleep 60
+ dotest stamps-5 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/kw,v <-- kw
+initial revision: 1\.1"
+ # Cygwin, *cough*, puts the year in the time column until the minute
+ # is no longer the current minute. Sleep 60 seconds to avoid this
+ # problem.
+ sleep 60
+ 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 \
+"$diff_u $TESTDIR/1/stamp.aa.add $TESTDIR/1/stamp.aa.ci"
+ dotest_fail stamps-6kw \
+"cmp $TESTDIR/1/stamp.kw.add $TESTDIR/1/stamp.kw.ci >/dev/null"
+ 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.
+ dotest_fail stamps-8aa \
+"cmp $TESTDIR/1/stamp.aa.ci $TESTDIR/1/stamp.aa.get >/dev/null"
+ dotest stamps-8kw \
+"$diff_u $TESTDIR/1/stamp.kw.ci $TESTDIR/1/stamp.kw.get"
+
+ # 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" \
+"$CVSROOT_DIRNAME/first-dir/aa,v <-- aa
+new revision: 1\.2; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/kw,v <-- kw
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Cygwin, *cough*, puts the year in the time column until the minute
+ # is no longer the current minute. Sleep 60 seconds to avoid this
+ # problem.
+ sleep 60
+ 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" 'U aa
+U 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.
+ dotest_fail stamps-11aa \
+"cmp $TESTDIR/1/stamp.aa.update $TESTDIR/1/stamp.aa.ci2 >/dev/null"
+ dotest_fail stamps-11kw \
+"cmp $TESTDIR/1/stamp.kw.update $TESTDIR/1/stamp.kw.ci2 >/dev/null"
+
+ dokeep
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ perms)
+ mkdir 1; cd 1
+ dotest perms-init-1 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ echo 'PreservePermissions=yes' >> ${CVSROOT_DIRNAME}/CVSROOT/config
+ dotest perms-init-2 "$testcvs -Q ci -mperms"
+ cd ..
+
+ dotest perms-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest perms-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ touch foo
+ chmod 431 foo
+ dotest perms-3 "${testcvs} add foo" \
+"${SPROG} add: scheduling file .foo. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest perms-4 "${testcvs} -q ci -m ''" \
+"$CVSROOT_DIRNAME/first-dir/foo,v <-- foo
+initial revision: 1\.1"
+
+ # Test checking out files with different permissions.
+ cd ../..
+ mkdir 2; cd 2
+ dotest perms-5 "${testcvs} -q co first-dir" "U first-dir/foo"
+ cd first-dir
+ if $remote; then :; else
+ # PreservePermissions not yet implemented for remote.
+ dotest perms-6 "ls -l foo" "-r---wx--x .* foo"
+ fi
+
+ dokeep
+ cd ../1/CVSROOT
+ restore_adm
+ cd ../..
+ rm -rf 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ symlinks)
+ # short cut around checking out and committing CVSROOT
+ rm -f $CVSROOT_DIRNAME/CVSROOT/config
+ echo 'PreservePermissions=yes' >> $CVSROOT_DIRNAME/CVSROOT/config
+ chmod 444 $CVSROOT_DIRNAME/CVSROOT/config
+
+ mkdir 1; cd 1
+ dotest symlinks-1 "$testcvs -q co -l ."
+ mkdir first-dir
+ dotest symlinks-2 "$testcvs add first-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+
+ dotest symlinks-2.1 "ln -s $TESTDIR/fumble slink"
+ dotest symlinks-3 "$testcvs add slink" \
+"$SPROG add: scheduling file .slink. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ if $remote; then
+ # Remote doesn't implement PreservePermissions, and in its
+ # absence the correct behavior is to follow the symlink.
+ dotest_fail symlinks-4r "$testcvs -q ci -m ''" \
+"$SPROG \[commit aborted\]: reading slink: No such file or directory"
+ else
+ dotest symlinks-4 "$testcvs -q ci -m ''" \
+"$CVSROOT_DIRNAME/first-dir/slink,v <-- slink
+initial revision: 1\.1"
+
+ # Test checking out symbolic links.
+ cd ../..
+ mkdir 2; cd 2
+ dotest symlinks-5 "$testcvs -q co first-dir" "U first-dir/slink"
+ cd first-dir
+ dotest symlinks-6 "ls -l slink" \
+"l[rwx\-]* .* slink -> $TESTDIR/fumble"
+ fi
+
+ dokeep
+ cd ../..
+ rm -rf 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ restore_adm
+ ;;
+
+
+
+ symlinks2)
+ # Symlinks in working directory without PreservePermissions.
+ # Also see: symlinks: with PreservePermissions
+ # rcslib-symlink-*: symlinks in repository.
+ mkdir 1; cd 1
+ dotest symlinks2-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest symlinks2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo nonsymlink > slink
+ dotest symlinks2-3 "${testcvs} add slink" \
+"${SPROG} add: scheduling file .slink. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest symlinks2-4 "${testcvs} -q ci -m ''" \
+"$CVSROOT_DIRNAME/first-dir/slink,v <-- slink
+initial revision: 1\.1"
+ rm slink
+ # Choose name cvslog.* so it is in default ignore list.
+ echo second file >cvslog.file2
+ dotest symlinks2-5 "ln -s cvslog.file2 slink" ""
+ dotest symlinks2-6 "${testcvs} -q ci -m linkify" \
+"$CVSROOT_DIRNAME/first-dir/slink,v <-- slink
+new revision: 1\.2; previous revision: 1\.1"
+ dotest symlinks2-7 "${testcvs} -q update -r 1.1 slink" "U slink"
+ dotest symlinks2-8 "cat slink" "nonsymlink"
+ dotest symlinks2-9 "ls -l slink" "-[-rwx]* .* slink"
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ hardlinks)
+ # short cut around checking out and committing CVSROOT
+ rm -f ${CVSROOT_DIRNAME}/CVSROOT/config
+ echo 'PreservePermissions=yes' > ${CVSROOT_DIRNAME}/CVSROOT/config
+ chmod 444 ${CVSROOT_DIRNAME}/CVSROOT/config
+
+ mkdir 1; cd 1
+ dotest hardlinks-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest hardlinks-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ # Make up some ugly filenames, to test that they get
+ # encoded properly in the delta nodes. Note that `dotest' screws
+ # up if some arguments have embedded spaces.
+ if touch aaaa
+ then
+ pass hardlinks-2.1
+ else
+ fail hardlinks-2.1
+ fi
+
+ if ln aaaa b.b.b.b
+ then
+ pass hardlinks-2.2
+ else
+ fail hardlinks-2.2
+ fi
+
+ if ln aaaa 'dd dd dd'
+ then
+ pass hardlinks-2.3
+ else
+ fail hardlinks-2.3
+ fi
+
+ dotest hardlinks-3 "${testcvs} add [abd]*" \
+"${SPROG} add: scheduling file .aaaa. for addition
+${SPROG} add: scheduling file .b\.b\.b\.b. for addition
+${SPROG} add: scheduling file .dd dd dd. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest hardlinks-4 "${testcvs} -q ci -m ''" \
+"$CVSROOT_DIRNAME/first-dir/aaaa,v <-- aaaa
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/b\.b\.b\.b,v <-- b\.b\.b\.b
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/dd dd dd,v <-- dd dd dd
+initial revision: 1\.1"
+ # Test checking out hardlinked files.
+ cd ../..
+ mkdir 2; cd 2
+ if $remote; then
+ # Remote does not implement PreservePermissions.
+ dotest hardlinks-5r "${testcvs} -q co first-dir" \
+"U first-dir/aaaa
+U first-dir/b\.b\.b\.b
+U first-dir/dd dd dd"
+ cd first-dir
+ dotest hardlinks-6r "ls -l [abd]*" \
+"-[rwx\-]* *1 .* aaaa
+-[rwx\-]* *1 .* b\.b\.b\.b
+-[rwx\-]* *1 .* dd dd dd"
+ else
+ dotest hardlinks-5 "${testcvs} -q co first-dir" \
+"U first-dir/aaaa
+U first-dir/b\.b\.b\.b
+U first-dir/dd dd dd"
+ cd first-dir
+ # To make sure that the files are properly hardlinked, it
+ # would be nice to do `ls -i' and make sure all the inodes
+ # match. But I think that would require expr to support
+ # tagged regexps, and I don't think we can rely on that.
+ # So instead we just see that each file has the right
+ # number of links. -twp
+ dotest hardlinks-6 "ls -l [abd]*" \
+"-[rwx\-]* *3 .* aaaa
+-[rwx\-]* *3 .* b\.b\.b\.b
+-[rwx\-]* *3 .* dd dd dd"
+ fi
+
+ dokeep
+ cd ../..
+ rm -rf 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ restore_adm
+ ;;
+
+
+
+ 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 $CVSROOT_DIRNAME/first-dir added to the repository"
+ cd first-dir
+
+ touch file1
+ dotest sticky-3 "$testcvs add file1" \
+"$SPROG add: scheduling file .file1. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest sticky-4 "$testcvs -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest sticky-5 "$testcvs -q tag tag1" "T file1"
+ echo add a line >>file1
+ dotest sticky-6 "$testcvs -q ci -m modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest sticky-7 "$testcvs -q update -r tag1" "U 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" \
+"$SPROG add: cannot add file on non-branch tag \`tag1'"
+ dotest sticky-12 "$testcvs -q update -A" "U file1
+$QUESTION file2" "$QUESTION file2
+U file1"
+ dotest sticky-13 "${testcvs} add file2" \
+"$SPROG add: scheduling file .file2. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest sticky-14 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+
+ # Now back to tag1
+ dotest sticky-15 "${testcvs} -q update -r tag1" "U file1
+$SPROG update: \`file2' is no longer in the repository"
+
+ rm file1
+ dotest sticky-16 "${testcvs} rm file1" \
+"$SPROG remove: scheduling .file1. for removal
+$SPROG remove: use .$SPROG 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" \
+"${SPROG} update: \`file1' is no longer in the repository
+${SPROG} update: \`file2' is no longer in the repository"
+ dotest sticky-20 "$testcvs -q update -A" "U file1
+U file2"
+
+ # Now try with a numeric revision.
+ dotest sticky-21 "$testcvs -q update -r 1.1 file1" "U file1"
+ dotest sticky-22 "$testcvs rm -f file1" \
+"$SPROG remove: 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.
+
+ # Ditto with a sticky date.
+ #
+ # 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 -Dnow file1" \
+"$SPROG update: warning: \`file1' was lost
+U file1" "U file1"
+ dotest sticky-24 "$testcvs rm -f file1" \
+"$SPROG remove: cannot remove file .file1. which has a sticky date of
.[0-9.]*."
+
+ dotest sticky-25 "$testcvs -q update -A" \
+"$SPROG update: warning: \`file1' was lost
+U file1" "U file1"
+
+ dokeep
+ restore_adm
+ cd ../..
+ rm -r 1
+ modify_repo 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.
+ # "keyword2" tests "cvs update -kk -j" with text and binary files
+ # I don't think any test is testing "cvs import -k".
+ # Other keyword expansion tests:
+ # keywordlog - $Log.
+ mkdir 1; cd 1
+ dotest keyword-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest keyword-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo '$''Author$' > file1
+ echo '$''Date$' >> file1
+ echo '$''Header$' >> file1
+ echo '$''Id$' >> file1
+ echo '$''Locker$' >> file1
+ echo '$''Name$' >> file1
+ echo '$''RCSfile$' >> file1
+ echo '$''Revision$' >> file1
+ echo '$''Source$' >> file1
+ echo '$''State$' >> file1
+ echo '$''Nonkey$' >> file1
+ # Omit the trailing dollar sign
+ echo '$''Date' >> file1
+ # Put two keywords on one line
+ echo '$''State$' '$''State$' >> file1
+ # Use a header for Log
+ echo 'xx $''Log$' >> file1
+
+ dotest keyword-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest keyword-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ 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: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/first-dir/file1,v
+1\.1 locked
+done"
+
+ dotest keyword-7 "${testcvs} update -kkv file1" "U file1"
+ dotest keyword-8 "cat file1" \
+'\$'"Author: ${username} "'\$'"
+"'\$'"Date: [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]
[0-9][0-9]:[0-9][0-9]:[0-9][0-9] "'\$'"
+"'\$'"Header: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/first-dir/file1,v "'\$'"
+"'\$'"State: Exp "'\$'"
+"'\$'"Nonkey"'\$'"
+"'\$'"Date
+"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'"
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.1 [0-9/]* [0-9:]* ${username}
+xx add
+xx"
+
+ dotest keyword-9 "${testcvs} update -kkvl file1" "U file1"
+ dotest keyword-10 "cat file1" \
+'\$'"Author: ${username} "'\$'"
+"'\$'"Date: ${RCSKEYDATE} "'\$'"
+"'\$'"Header: ${CVSROOT_DIRNAME}/first-dir/file1,v 1\.1 ${RCSKEYDATE}
${username} Exp ${username} "'\$'"
+"'\$'"Id: file1,v 1\.1 ${RCSKEYDATE} ${username} Exp ${username} "'\$'"
+"'\$'"Locker: ${username} "'\$'"
+"'\$'"Name: "'\$'"
+"'\$'"RCSfile: file1,v "'\$'"
+"'\$'"Revision: 1\.1 "'\$'"
+"'\$'"Source: ${CVSROOT_DIRNAME}/first-dir/file1,v "'\$'"
+"'\$'"State: Exp "'\$'"
+"'\$'"Nonkey"'\$'"
+"'\$'"Date
+"'\$'"State: Exp "'\$'" "'\$'"State: Exp "'\$'"
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.1 ${RCSKEYDATE} ${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 ${RCSKEYDATE} ${username}
+xx add
+xx"
+
+ dotest keyword-13 "${testcvs} update -kv file1" "U file1"
+ dotest keyword-14 "cat file1" \
+"${username}
+${RCSKEYDATE}
+${CVSROOT_DIRNAME}/first-dir/file1,v 1\.1 ${RCSKEYDATE} ${username} Exp
+file1,v 1\.1 ${RCSKEYDATE} ${username} Exp
+
+
+file1,v
+1\.1
+${CVSROOT_DIRNAME}/first-dir/file1,v
+Exp
+"'\$'"Nonkey"'\$'"
+"'\$'"Date
+Exp Exp
+xx file1,v
+xx Revision 1\.1 ${RCSKEYDATE} ${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"
+
+ echo '$''Name$' > file1
+ dotest keyword-18 "${testcvs} ci -m modify file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ dotest keyword-19 "${testcvs} -q tag tag1" "T file1"
+ echo "change" >> file1
+ dotest keyword-20 "${testcvs} -q ci -m mod2 file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ # FIXCVS - These unpatchable files are happening because the tag
+ # associated with the current base version of the file in the
+ # sandbox is not available in these cases. See the note in the
+ # patch_file function in update.c.
+ dotest keyword-21 "${testcvs} -q update -r tag1" "U file1" \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+
+ dotest keyword-22 "cat file1" '\$'"Name: tag1 "'\$'
+
+ if $remote; then
+ # Like serverpatch-8. Not sure there is anything much we
+ # can or should do about this.
+ dotest keyword-23r "${testcvs} update -A file1" \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+ else
+ dotest keyword-23 "${testcvs} update -A file1" "U file1"
+ fi
+ dotest keyword-24 "cat file1" '\$'"Name: "'\$'"
+change"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ echo initial >file1
+ dotest keywordlog-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ # See "rmadd" for a list of other tests of cvs ci -r.
+ dotest keywordlog-4 "${testcvs} -q ci -r 1.3 -m add file1" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.3"
+
+ 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 <<EOF
+First log line
+Second log line
+EOF
+ # As with rmadd-25, "cvs ci -r" sets a sticky tag.
+ dotest_fail keywordlog-4b \
+"${testcvs} ci -F ${TESTDIR}/comment.tmp file1" \
+"${SPROG} commit: sticky tag .1\.3. for file .file1. is not a branch
+${SPROG} \[commit aborted\]: correct above errors first!"
+ dotest keywordlog-4c "${testcvs} -q update -A" "M file1"
+
+ dotest keywordlog-5 "${testcvs} ci -F ${TESTDIR}/comment.tmp file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3"
+ rm -f ${TESTDIR}/comment.tmp
+ dotest keywordlog-6 "${testcvs} -q tag -b br" "T file1"
+ dotest keywordlog-7 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx"
+
+ cd ../../2/first-dir
+ dotest keywordlog-8 "${testcvs} -q update" "U file1"
+ dotest keywordlog-9 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx"
+ cd ../../1/first-dir
+
+ echo "change" >> file1
+ dotest keywordlog-10 "${testcvs} ci -m modify file1" \
+"${CVSROOT_DIRNAME}/first-dir/file1,v <-- file1
+new revision: 1\.5; previous revision: 1\.4"
+ dotest keywordlog-11 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.5 ${RCSKEYDATE} ${username}
+xx modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx
+change"
+
+ cd ../../2/first-dir
+ dotest keywordlog-12 "${testcvs} -q update" "U file1"
+ dotest keywordlog-13 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.5 ${RCSKEYDATE} ${username}
+xx modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx
+change"
+
+ cd ../../1/first-dir
+ dotest keywordlog-14 "${testcvs} -q update -r br" "U file1"
+ echo br-change >>file1
+ dotest keywordlog-15 "${testcvs} -q ci -m br-modify" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4\.2\.1; previous revision: 1\.4"
+ dotest keywordlog-16 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4\.2\.1 ${RCSKEYDATE} ${username}
+xx br-modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx
+br-change"
+ cd ../../2/first-dir
+ dotest keywordlog-17 "${testcvs} -q update -r br" "U file1"
+ dotest keywordlog-18 "cat file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4\.2\.1 ${RCSKEYDATE} ${username}
+xx br-modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${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" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4\.2\.1 ${RCSKEYDATE} ${username}
+xx br-modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx
+br-change"
+ dotest keywordlog-20 "${testcvs} -q co -p first-dir/file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.5 ${RCSKEYDATE} ${username}
+xx modify
+xx
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx
+change"
+ dotest keywordlog-21 "${testcvs} -q co -p -r 1.4 first-dir/file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx"
+
+ cd 2/first-dir
+ # OK, the basic rule for keyword expansion is that it
+ # happens on checkout. And the rule for annotate is that
+ # it annotates a checked-in revision, rather than a checked-out
+ # file. So, although it is kind of confusing that the latest
+ # revision does not appear in the annotated output, and the
+ # annotated output does not quite match what you'd get with
+ # update or checkout, the behavior is more or less logical.
+ # The same issue occurs with annotate and other keywords,
+ # I think, although it is particularly noticeable for $Log.
+ dotest keywordlog-22 "${testcvs} ann -r br file1" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.3 ($username8 *[0-9a-zA-Z-]*): initial
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): xx "'\$'"Log: file1,v "'\$'"
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): xx Revision 1\.4 ${RCSKEYDATE}
$username
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): xx First log line
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): xx Second log line
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): xx
+1\.4\.2\.1 ($username8 *[0-9a-zA-Z-]*): br-change"
+ dotest keywordlog-23 "${testcvs} ann -r HEAD file1" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.3 ($username8 *[0-9a-zA-Z-]*): initial
+1\.5 ($username8 *[0-9a-zA-Z-]*): xx "'\$'"Log: file1,v "'\$'"
+1\.5 ($username8 *[0-9a-zA-Z-]*): xx Revision 1\.4 ${RCSKEYDATE}
$username
+1\.5 ($username8 *[0-9a-zA-Z-]*): xx First log line
+1\.5 ($username8 *[0-9a-zA-Z-]*): xx Second log line
+1\.5 ($username8 *[0-9a-zA-Z-]*): xx
+1\.5 ($username8 *[0-9a-zA-Z-]*): change"
+ cd ../..
+
+ #
+ # test the operation of 'admin -o' in conjunction with keywords
+ # (especially Log - this used to munge the RCS file for all time)
+ #
+
+ dotest keywordlog-24 \
+"${testcvs} admin -oHEAD 1/first-dir/file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+deleting revision 1\.5
+done"
+
+ dotest keywordlog-25 \
+"${testcvs} -q co -p first-dir/file1" \
+"initial
+xx "'\$'"Log: file1,v "'\$'"
+xx Revision 1\.4 ${RCSKEYDATE} ${username}
+xx First log line
+xx Second log line
+xx"
+
+ # Now test the behavior when the comment leader exceeds the
+ # configured maximum.
+ mkdir 3; cd 3
+ dotest keywordlog-26 "$testcvs -Q co first-dir"
+
+ cd first-dir
+ sed 's/xx \$/1234567890123456789 $/' <file1 >tmp
+ mv tmp file1
+ dotest keywordlog-27 "$testcvs -Q ci -mrevision-5"
+ dotest keywordlog-28 "cat file1" \
+"initial
+1234567890123456789 "'\$'"Log: file1,v "'\$'"
+1234567890123456789 Revision 1\.5 $RCSKEYDATE $username
+1234567890123456789 revision-5
+1234567890123456789
+xx Revision 1\.4 $RCSKEYDATE $username
+xx First log line
+xx Second log line
+xx"
+
+ sed 's/1234567890123456789 \$/12345678901234567890 $/' <file1 >tmp
+ mv tmp file1
+ dotest keywordlog-29 "$testcvs -Q ci -mrevision-6" \
+"$SPROG commit: Skipping "'`$''Log$'"' keyword due to excessive comment
leader\."
+ dotest keywordlog-30 "cat file1" \
+"initial
+12345678901234567890 "'\$'"Log: file1,v "'\$'"
+1234567890123456789 Revision 1\.5 $RCSKEYDATE $username
+1234567890123456789 revision-5
+1234567890123456789
+xx Revision 1\.4 $RCSKEYDATE $username
+xx First log line
+xx Second log line
+xx"
+
+ # Check that the Log-related config options work.
+ cd ..
+ dotest keywordlog-31 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ echo "UseArchiveCommentLeader=TrUe" >>config
+ dotest keywordlog-32 "$testcvs -Q ci -mset-UseArchiveCommentLeader"
+
+ cd ../first-dir
+ dotest keywordlog-33 "$testcvs -Q ci -fmrevision-7 file1"
+ dotest keywordlog-34 "cat file1" \
+"initial
+12345678901234567890 "'\$'"Log: file1,v "'\$'"
+# Revision 1\.7 $RCSKEYDATE $username
+# revision-7
+#
+1234567890123456789 Revision 1\.5 $RCSKEYDATE $username
+1234567890123456789 revision-5
+1234567890123456789
+xx Revision 1\.4 $RCSKEYDATE $username
+xx First log line
+xx Second log line
+xx"
+
+ cd ../CVSROOT
+ echo "MaxCommentLeaderLength=1k" >>config
+ dotest keywordlog-35 "$testcvs -Q ci -mset-MaxCommentLeaderLength"
+
+ cd ../first-dir
+ dotest keywordlog-36 "$testcvs -Q ci -fmrevision-8 file1"
+ dotest keywordlog-37 "cat file1" \
+"initial
+12345678901234567890 "'\$'"Log: file1,v "'\$'"
+12345678901234567890 Revision 1\.8 $RCSKEYDATE $username
+12345678901234567890 revision-8
+12345678901234567890
+# Revision 1\.7 $RCSKEYDATE $username
+# revision-7
+#
+1234567890123456789 Revision 1\.5 $RCSKEYDATE $username
+1234567890123456789 revision-5
+1234567890123456789
+xx Revision 1\.4 $RCSKEYDATE $username
+xx First log line
+xx Second log line
+xx"
+
+ dokeep
+ cd ../..
+ restore_adm
+ rm -r 1 2 3
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ keywordname)
+ # Test the Name keyword.
+ # See the keyword test for a descriptions of some other tests that
+ # test keyword expansion modes.
+ mkdir keywordname; cd keywordname
+ mkdir 1; cd 1
+ dotest keywordname-init-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest keywordname-init-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo '$'"Name$" >file1
+ echo '$'"Name$" >file2
+ dotest keywordname-init-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+
+ # See "rmadd" for a list of other tests of cvs ci -r.
+ dotest keywordname-init-4 "${testcvs} -q ci -r 1.3 -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.3
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.3"
+
+ dotest keywordname-init-6 "${testcvs} -q up -A"
+ dotest keywordname-init-7 "${testcvs} -q tag -b br" \
+"T file1
+T file2"
+
+ echo new data >>file1
+ dotest keywordname-init-8 "${testcvs} -q ci -mchange" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.4; previous revision: 1\.3"
+
+ # First check out a branch.
+ #
+ # There used to be a bug where static tags would be substituted for
+ # Name keywords but not branch tags.
+ #
+ # FIXCVS - BUG
+ # Why shouldn't the non-update case not cause a substitution?
+ # An update -kk or -A will unsub and sub keywords without updates
+ # being required.
+ # FIXCVS - see note above keyword-21
+ dotest keywordname-update-1 "${testcvs} -q up -rbr" "U file1" \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+ dotest keywordname-update-2 "cat file1" '\$'"Name: br "'\$'
+ dotest keywordname-update-3 "cat file2" '\$'"Name: "'\$'
+
+ # Now verify that updating to the trunk leaves no substitution for
+ # $Name
+ dotest keywordname-update-4 "${testcvs} -q tag firsttag" \
+"T file1
+T file2"
+ # FIXCVS - see note above keyword-21
+ dotest keywordname-update-5 "${testcvs} -q up -A" "U file1" \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+ dotest keywordname-update-6 "cat file1" \
+'\$'"Name: "'\$'"
+new data"
+ dotest keywordname-update-7 "cat file2" '\$'"Name: "'\$'
+
+ # But updating to a static tag does cause a substitution
+ # FIXCVS - see same note above
+ dotest keywordname-update-8 "${testcvs} -q up -rfirsttag" "U file1" \
+"$CPROG update: checksum failure after patch to \`\./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+ dotest keywordname-update-9 "cat file1" '\$'"Name: firsttag "'\$'
+ dotest keywordname-update-10 "cat file2" '\$'"Name: "'\$'
+
+ # And reverify the trunk update when the change is actually removed.
+ dotest keywordname-update-11 "${testcvs} -q up -A" "U file1" \
+"$CPROG update: checksum failure after patch to \`./file1'; will refetch
+$CPROG client: refetching unpatchable files
+U file1"
+ dotest keywordname-update-12 "cat file1" \
+'\$'"Name: "'\$'"
+new data"
+ dotest keywordname-update-13 "cat file2" '\$'"Name: "'\$'
+
+ cd ../..
+
+ # now verify that a fresh checkout substitutes all the $Name fields
+ mkdir 2; cd 2
+ dotest keywordname-checkout-1 \
+"${testcvs} -q co -rfirsttag first-dir" \
+"U first-dir/file1
+U first-dir/file2"
+ cd first-dir
+ dotest keywordname-checkout-2 "cat file1" '\$'"Name: firsttag "'\$'
+ dotest keywordname-checkout-3 "cat file2" '\$'"Name: firsttag "'\$'
+
+ dokeep
+ cd ../../..
+ rm -r keywordname
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ keyword2)
+ # Test merging on files with keywords:
+ # without -kk
+ # with -kk
+ # on text files
+ # on binary files
+ # Note: This test assumes that CVS has already passed the binfiles
+ # test sequence
+ # Note2: We are testing positive on binary corruption here
+ # we probably really DON'T want to 'cvs update -kk' a binary
file...
+ mkdir 1; cd 1
+ dotest keyword2-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest keyword2-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo '$''Revision$' >> file1
+ echo "I" >>file1
+ echo "like" >>file1
+ echo "long" >>file1
+ echo "files!" >>file1
+ echo "" >>file1
+ echo "a test line for our times" >>file1
+ echo "" >>file1
+ echo "They" >>file1
+ echo "make" >>file1
+ echo "diff" >>file1
+ echo "look like it" >>file1
+ echo "did a much better" >>file1
+ echo "job." >>file1
+ dotest keyword2-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ ${AWK} 'BEGIN { printf "%c%c%c%sRevision: 1.1 address@hidden", \
+ 2, 10, 137, "$", 13, 10 }' \
+ </dev/null | ${TR} '@' '\000' >../binfile.dat
+ cp ../binfile.dat .
+ dotest keyword2-5 "${testcvs} add -kb binfile.dat" \
+"${SPROG} add: scheduling file .binfile\.dat. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+
+ dotest keyword2-6 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/binfile\.dat,v <-- binfile\.dat
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ dotest keyword2-7 "${testcvs} -q tag -b branch" \
+"T binfile\.dat
+T file1"
+
+ sed -e 's/our/the best of and the worst of/' file1 >f; mv f file1
+ dotest keyword2-8 "${testcvs} -q ci -m change" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+
+ dotest keyword2-9 "${testcvs} -q update -r branch" 'U file1'
+
+ echo "what else do we have?" >>file1
+ dotest keyword2-10 "${testcvs} -q ci -m change" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ # Okay, first a conflict in file1 - should be okay with binfile.dat
+ dotest keyword2-11 "${testcvs} -q update -A -j branch" \
+"U file1
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.1
+Merging differences between 1\.1 and 1\.1\.2\.1 into file1
+rcsmerge: warning: conflicts during merge"
+
+ dotest_fail keyword2-12 "${testcvs} diff file1" \
+"Index: file1
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.2
+diff -r1\.2 file1
+0a1
+> <<<<<<< file1
+1a3,5
+> =======
+> \\\$""Revision: 1\.1\.2\.1 \\\$
+> >>>>>>> 1\.1\.2\.1
+14a19
+> what else do we have${QUESTION}"
+
+ # Here's the problem... shouldn't -kk a binary file...
+ rm file1
+ dotest keyword2-13 "${testcvs} -q update -A -kk -j branch" \
+"${SPROG} update: warning: \`file1' was lost
+U file1
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.1
+retrieving revision 1\.1\.2\.1
+Merging differences between 1\.1 and 1\.1\.2\.1 into file1"
+
+ # binfile won't get checked in, but it is now corrupt and could
+ # have been checked in if it had changed on the branch...
+ dotest keyword2-14 "${testcvs} -q ci -m change" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+
+ # "-kk" no longer corrupts binary files
+ dotest keyword2-15 "cmp binfile.dat ../binfile.dat" ''
+
+ # Okay, restore everything and make CVS try and merge a binary file...
+ # "-kk" no longer affects binary files
+ dotest keyword2-16 "${testcvs} -q update -A" \
+"U file1"
+ dotest keyword2-17 "${testcvs} -q tag -b branch2" \
+"T binfile\.dat
+T file1"
+ dotest keyword2-18 "${testcvs} -q update -r branch2" ''
+
+ ${AWK} 'BEGIN { printf "address@hidden", 2, 10, 137, 13, 10 }' \
+ </dev/null | ${TR} '@' '\000' >>binfile.dat
+ dotest keyword2-19 "$testcvs -q ci -m badbadbad" \
+"$CVSROOT_DIRNAME/first-dir/binfile\.dat,v <-- binfile\.dat
+new revision: 1\.1\.4\.1; previous revision: 1\.1"
+ # "-kk" no longer affects binary files
+
+ # XXXX: do not ask, why we get the "U binfile.dat" line twice
+ # looks like a bug!
+ dotest keyword2-20 "${testcvs} -q update -A -kk -j branch2" \
+"U binfile\.dat
+U binfile\.dat
+U file1"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-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" \
+"
+
+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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3; previous revision: 1\.2"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3\.2\.1; previous revision: 1\.3"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1"
+ # With no sticky tags, HEAD is the head of the trunk.
+ dotest head-trunk-setup "${testcvs} -q update -A" "U 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: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.3
+retrieving revision 1\.3\.2\.2
+diff -c -r1\.3 -r1\.3\.2\.2
+\*\*\* file1 ${RFCDATE} 1\.3
+--- file1 ${RFCDATE} 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" "U 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" ""
+
+ # With a nonbranch sticky tag on a branch,
+ # HEAD is the head of the trunk
+ dotest head-brtag-setup "${testcvs} -q update -r brtag" "U 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"
+
+ # CVS 1.9 and older thought that HEAD is "brtag" (this was
+ # noted as "strange, maybe accidental"). But "br1" makes a
+ # whole lot more sense.
+ dotest head-brtag-diff "${testcvs} -q diff -c -r HEAD -r br1" ""
+
+ # 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" \
+"U 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, there is a non-branch sticky tag.
+ dotest_fail head-trunktag-diff \
+ "${testcvs} -q diff -c -r HEAD -r br1" \
+"Index: file1
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+retrieving revision 1\.3
+retrieving revision 1\.3\.2\.2
+diff -c -r1\.3 -r1\.3\.2\.2
+\*\*\* file1 ${RFCDATE} 1\.3
+--- file1 ${RFCDATE} 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"
+
+ # Also might test what happens if we setup with update -r
+ # HEAD. In general, if sticky tags matter, does the
+ # behavior of "update -r <foo>" (without -p) depend on the
+ # sticky tags before or after the update?
+
+ # 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" \
+"${SPROG} admin: Administrating \.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+${SPROG} admin: cannot remove revision 1\.3\.2\.1 because it has tags
+${SPROG} admin: RCS file for .file1. not modified\.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+ dotest head-o0b "${testcvs} tag -d brtag" \
+"${SPROG} tag: Untagging \.
+D file1
+D file2"
+ dotest head-o1 "${testcvs} admin -o ::br1" \
+"${SPROG} admin: Administrating \.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+deleting revision 1\.3\.2\.1
+done
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ tagdate)
+ # Test combining -r and -D.
+ #
+ # Note that this is not a complete test. It relies on the fact
+ # that update, checkout and export have a LOT of shared code.
+ # Notice:
+ # 1) checkout is never tested at all with -r -D
+ # 2) update never uses an argument to '-D' besides 'now'
+ # (this test does not provide enough data to prove
+ # that 'cvs update' with both a '-r' and a '-D'
+ # specified does not ignore '-D': a 'cvs up
+ # -r<branch> -Dnow' and a 'cvs up -r<branch>'
+ # should specify the same file revision).
+ # 3) export uses '-r<branch> -D<when there was a different
+ # revision>', hopefully completing this behavior test
+ # for checkout and update as well.
+ #
+ mkdir 1; cd 1
+ save_TZ=$TZ
+ TZ=UTC0; export TZ
+ dotest tagdate-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest tagdate-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo trunk-1 >file1
+ dotest tagdate-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest tagdate-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+ date_T1=`getrlogdate -r1.1 first-dir/file1`
+
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1"
+ date_T2=`getrlogdate -r1.2 first-dir/file1`
+
+ # 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" "U file1"
+ echo br2-1 >file1
+ dotest tagdate-10 "${testcvs} -q ci -m modify-on-br2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.4\.1; previous revision: 1\.1"
+ date_T3=`getrlogdate -r1.1.4.1 first-dir/file1`
+
+ # Then the case where br2 does have revisions:
+ dotest tagdate-11 "${testcvs} -q update -p -r br1 -D now" "trunk-1"
+
+ # Joins from dates on the head used to be prohibited.
+ dotest tagdate-12 "$testcvs -q update -j:yesterday -j:now"
+ dotest tagdate-12b "$testcvs -Q update -C"
+ # And check export
+
+ echo br2-2 >file1
+ dotest tagdate-13 "${testcvs} -q ci -m modify-2-on-br2" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.4\.2; previous revision: 1\.1\.4\.1"
+ date_T4=`getrlogdate -r1.1.4.2 first-dir/file1`
+
+ # Test diff -r<tag>:<date> with two revisions specified.
+ dotest_fail tagdate-13b \
+"$testcvs -q diff -u -rbr2:'$date_T3' -rbr2:now file1" \
+"Index: file1
+===================================================================
+RCS file: $CVSROOT_DIRNAME/first-dir/file1,v
+retrieving revision 1\.1\.4\.1
+retrieving revision 1\.1\.4\.2
+diff -u -r1\.1\.4\.1 -r1\.1\.4\.2
+--- file1 $RFCDATE 1\.1\.4\.1
++++ file1 $RFCDATE 1\.1\.4\.2
+@@ -1 ${PLUS}1 @@
+-br2-1
+${PLUS}br2-2"
+
+ # Tag a date on a branch.
+ dotest tagdate-13c "$testcvs -q tag -rbr2:'$date_T3' tagdate" \
+"T file1"
+ dotest tagdate-13d "$testcvs -q update -rtagdate" "U file1"
+ dotest tagdate-13e "cat file1" "br2-1"
+
+ # This one should fail, though currently without an error message,
+ # since a date on a static tag is meaningless.
+ dotest tagdate-13f "$testcvs -q tag -rtagdate:'$date_T3' tagdate"
+
+ # and restore to using the trunk for future tests.
+ dotest tagdate-13g "$testcvs -q up -rbr2" "U file1"
+
+ cd ../..
+ mkdir 2; cd 2
+ dotest tagdate-14 \
+"$testcvs -q export -r br2 -D'$date_T3' first-dir" \
+"U first-dir/file1"
+ dotest tagdate-14b "cat first-dir/file1" "br2-1"
+ dotest tagdate-15 \
+"$testcvs -q export -rbr2:'$date_T3' -dsecond-dir first-dir" \
+"U second-dir/file1"
+ dotest tagdate-15b "cat second-dir/file1" "br2-1"
+
+ # Now for annotate
+ cd ../1/first-dir
+ dotest tagdate-16 "${testcvs} annotate -rbr2 -D'$date_T3'" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1\.4\.1 ($username8 *[0-9a-zA-Z-]*): br2-1"
+
+ dotest tagdate-17 "${testcvs} annotate -rbr2 -Dnow" \
+"
+Annotations for file1
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+1\.1\.4\.2 ($username8 *[0-9a-zA-Z-]*): br2-2"
+
+ # Now check to see what happens when we add files to br2 and trunk
+ echo br2-1 > file3
+ dotest tagdate-18 "${testcvs} add file3" \
+"${SPROG} add: scheduling file \`file3' for addition on branch \`br2'
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest tagdate-19 "${testcvs} -q ci -m add file3" \
+"$CVSROOT_DIRNAME/first-dir/Attic/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ date_T5=`getrlogdate -r1.1 first-dir/file3`
+ date_T6=`getrlogdate -r1.1.2.1 first-dir/file3`
+
+ cd ../..
+ mkdir 3; cd 3
+ dotest tagdate-20 "${testcvs} -Q co first-dir" ''
+ cd first-dir
+ echo trunk-1 > file2
+ dotest tagdate-21 "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest tagdate-22 "${testcvs} -q ci -m add file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ date_T7=`getrlogdate -r1.1 first-dir/file2`
+ echo "trunk-2" >file2
+ dotest tagdate-23 "${testcvs} -q ci -m update file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.2; previous revision: 1\.1"
+ date_T8=`getrlogdate -r1.2 first-dir/file2`
+
+ cd ../../1/first-dir
+ echo br2-1 > file2
+ dotest tagdate-24 "${testcvs} add file2" \
+"${SPROG} add: scheduling file \`file2' for addition on branch \`br2'
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest tagdate-25 "${testcvs} -q ci -m add file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1"
+ date_T9=`getrlogdate -r1.2.2.2 first-dir/file2`
+ cd ../..
+
+ # Time Rev Branch Comments
+ # T0 trunk first-dir created
+ # T1 1.1 trunk first-dir/file1 committed "trunk-1"
+ # br1 branch created
+ # br2 branch created
+ # T2 1.2 trunk first-dir/file1 committed "trunk-2"
+ # T3 1.1.4.1 br2 first-dir/file1 committed "br2-1"
+ # +60s
+ # T4 1.1.4.2 br2 first-dir/file1 committed "br2-2"
+ # T5 1.1 trunk first-dir/file3 dead
+ # T6 1.1.2.1 br2 first-dir/file3 committed "br2-1"
+ # T7 1.1 trunk first-dir/file2 committed "trunk-1"
+ # T8 1.2 trunk first-dir/file2 committed "trunk-2"
+ # T8 1.2.2.1 br2 first-dir/file2 dead
+ # T9 1.2.2.2 br2 first-dir/file2 committed "br2-1"
+ #
+
+ mkdir 4; cd 4
+ (echo Dates for tagdate-26-* are:;\
+ echo " date_T1='$date_T1'";\
+ echo " date_T2='$date_T2'";\
+ echo " date_T3='$date_T3'";\
+ echo " date_T4='$date_T4'";\
+ echo " date_T5='$date_T5'";\
+ echo " date_T6='$date_T6'";\
+ echo " date_T7='$date_T7'";\
+ echo " date_T8='$date_T8'";\
+ echo " date_T9='$date_T9'") >>$LOGFILE
+ dotest tagdate-26-trunk-t1 \
+"${testcvs} co -D'$date_T1' -d first-dir-trunk-t1 first-dir" \
+"${SPROG} checkout: Updating first-dir-trunk-t1
+U first-dir-trunk-t1/file1"
+ dotest tagdate-26-br2-t1 \
+"${testcvs} co -r br2 -D'$date_T1' -d first-dir-br2-t1 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t1
+U first-dir-br2-t1/file1"
+ dotest tagdate-26-trunk-t2 \
+"${testcvs} co -D'$date_T2' -d first-dir-trunk-t2 first-dir" \
+"${SPROG} checkout: Updating first-dir-trunk-t2
+U first-dir-trunk-t2/file1"
+ dotest tagdate-26-br2-t2 \
+"${testcvs} co -r br2 -D'$date_T2' -d first-dir-br2-t2 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t2
+U first-dir-br2-t2/file1"
+ dotest tagdate-26-br2-t3 \
+"${testcvs} co -r br2 -D'$date_T3' -d first-dir-br2-t3 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t3
+U first-dir-br2-t3/file1"
+ dotest tagdate-26-br2-t4 \
+"${testcvs} co -r br2 -D'$date_T4' -d first-dir-br2-t4 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t4
+U first-dir-br2-t4/file1"
+ dotest tagdate-26-br2-t6 \
+"${testcvs} co -r br2 -D'$date_T6' -d first-dir-br2-t6 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t6
+U first-dir-br2-t6/file1
+U first-dir-br2-t6/file3"
+ dotest tagdate-26-trunk-t7 \
+"${testcvs} co -D'$date_T7' -d first-dir-trunk-t7 first-dir" \
+"${SPROG} checkout: Updating first-dir-trunk-t7
+U first-dir-trunk-t7/file1
+U first-dir-trunk-t7/file2"
+ dotest tagdate-26-br2-t7 \
+"${testcvs} co -r br2 -D'$date_T7' -d first-dir-br2-t7 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t7
+U first-dir-br2-t7/file1
+U first-dir-br2-t7/file3"
+ dotest tagdate-26-trunk-t8 \
+"${testcvs} co -D'$date_T8' -d first-dir-trunk-t8 first-dir" \
+"${SPROG} checkout: Updating first-dir-trunk-t8
+U first-dir-trunk-t8/file1
+U first-dir-trunk-t8/file2"
+ dotest tagdate-26-br2-t8 \
+"${testcvs} co -r br2 -D'$date_T8' -d first-dir-br2-t8 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t8
+U first-dir-br2-t8/file1
+U first-dir-br2-t8/file3"
+ dotest tagdate-26-br2-t9 \
+"${testcvs} co -r br2 -D'$date_T9' -d first-dir-br2-t9 first-dir" \
+"${SPROG} checkout: Updating first-dir-br2-t9
+U first-dir-br2-t9/file1
+U first-dir-br2-t9/file2
+U first-dir-br2-t9/file3"
+ dotest tagdate-27-trunk-t1 \
+"${testcvs} status first-dir-trunk-t1" \
+"${SPROG} status: Examining first-dir-trunk-t1
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1[^.]*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t1 \
+"${testcvs} status first-dir-br2-t1" \
+"${SPROG} status: Examining first-dir-br2-t1
+===================================================================
+File: file1 Status: Needs Patch
+
+ Working revision: 1\.1[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.4)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-trunk-t2 \
+"${testcvs} status first-dir-trunk-t2" \
+"${SPROG} status: Examining first-dir-trunk-t2
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.2[^.]*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t2 \
+"${testcvs} status first-dir-br2-t2" \
+"${SPROG} status: Examining first-dir-br2-t2
+===================================================================
+File: file1 Status: Needs Patch
+
+ Working revision: 1\.1[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.4)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t3 \
+"${testcvs} status first-dir-br2-t3" \
+"${SPROG} status: Examining first-dir-br2-t3
+===================================================================
+File: file1 Status: Needs Patch
+
+ Working revision: 1\.1\.4\.1[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.4)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t4 \
+"${testcvs} status first-dir-br2-t4" \
+"${SPROG} status: Examining first-dir-br2-t4
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.4\.2[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.4)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t6 \
+"${testcvs} status first-dir-br2-t6" \
+"${SPROG} status: Examining first-dir-br2-t6
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.4\.2[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (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
${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-trunk-t7 \
+"${testcvs} status first-dir-trunk-t7" \
+"${SPROG} status: Examining first-dir-trunk-t7
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.2[^.]*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)
+
+===================================================================
+File: file2 Status: Up-to-date
+
+ Working revision: 1\.1[^.]*
+ Repository revision: 1\.1 ${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t7 \
+"${testcvs} status first-dir-br2-t7" \
+"${SPROG} status: Examining first-dir-br2-t7
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.4\.2[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (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
${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-trunk-t8 \
+"${testcvs} status first-dir-trunk-t8" \
+"${SPROG} status: Examining first-dir-trunk-t8
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.2[^.]*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)
+
+===================================================================
+File: file2 Status: Up-to-date
+
+ Working revision: 1\.2[^.]*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: ${RCSDELTADATE}
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t8 \
+"${testcvs} status first-dir-br2-t8" \
+"${SPROG} status: Examining first-dir-br2-t8
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.4\.2[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (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
${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest tagdate-27-br2-t9 \
+"${testcvs} status first-dir-br2-t9" \
+"${SPROG} status: Examining first-dir-br2-t9
+===================================================================
+File: file1 Status: Up-to-date
+
+ Working revision: 1\.1\.4\.2[^.]*
+ Repository revision: 1\.1\.4\.2
${CVSROOT_DIRNAME}/first-dir/file1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.4)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file2 Status: Up-to-date
+
+ Working revision: 1\.2\.2\.2[^.]*
+ Repository revision: 1\.2\.2\.2
${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.2\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file3 Status: Up-to-date
+
+ Working revision: 1\.1\.2\.1[^.]*
+ Repository revision: 1\.1\.2\.1
${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: br2 (branch: 1\.1\.2)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+
+ # Now check the contents of the files
+ dotest tagdate-28-trunk-t1 'cat first-dir-trunk-t1/file1' 'trunk-1'
+ dotest tagdate-28-br2-t1 'cat first-dir-br2-t1/file1' 'trunk-1'
+ dotest tagdate-28-trunk-t2 'cat first-dir-trunk-t2/file1' 'trunk-2'
+ dotest tagdate-28-br2-t2 'cat first-dir-br2-t2/file1' 'trunk-1'
+ dotest tagdate-28-br2-t3 'cat first-dir-br2-t3/file1' 'br2-1'
+ dotest tagdate-28-br2-t4 'cat first-dir-br2-t4/file1' 'br2-2'
+ dotest tagdate-28-br2-t6a 'cat first-dir-br2-t6/file1' "br2-2"
+ dotest tagdate-28-br2-t6b 'cat first-dir-br2-t6/file3' "br2-1"
+ dotest tagdate-28-trunk-t7a 'cat first-dir-trunk-t7/file1' "trunk-2"
+ dotest tagdate-28-trunk-t7b 'cat first-dir-trunk-t7/file2' "trunk-1"
+ dotest tagdate-28-br2-t7a 'cat first-dir-br2-t7/file1' "br2-2"
+ dotest tagdate-28-br2-t7b 'cat first-dir-br2-t7/file3' "br2-1"
+ dotest tagdate-28-trunk-t8a 'cat first-dir-trunk-t8/file1' "trunk-2"
+ dotest tagdate-28-trunk-t8b 'cat first-dir-trunk-t8/file2' "trunk-2"
+ dotest tagdate-28-br2-t8a 'cat first-dir-br2-t8/file1' "br2-2"
+ dotest tagdate-28-br2-t8c 'cat first-dir-br2-t8/file3' "br2-1"
+ dotest tagdate-28-br2-t9a 'cat first-dir-br2-t9/file1' "br2-2"
+ dotest tagdate-28-br2-t9b 'cat first-dir-br2-t9/file2' "br2-1"
+ dotest tagdate-28-br2-t9c 'cat first-dir-br2-t9/file3' "br2-1"
+ cd ..
+
+ unset date_T1 date_T2 date_T3 date_T4 date_T5
+ unset date_T6 date_T7 date_T8 date_T9
+ TZ=$save_TZ
+
+ dokeep
+ rm -r 1 2 3 4
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ echo trunk-1 >file1
+ echo trunk-1 >file2
+ dotest multibranch2-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest multibranch2-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.4\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.4\.1; previous revision: 1\.1"
+
+ dotest multibranch2-9 "${testcvs} -q update -r A" 'U file1
+U 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" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ dotest multibranch2-11 "${testcvs} -q log file1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+branches: 1\.1\.2; 1\.1\.4;
+add
+----------------------------
+revision 1\.1\.4\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+modify-on-B
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; lines: ${PLUS}1 -1;
commitid: ${commitid};
+modify-on-A
+============================================================================="
+
+ # This one is more concise.
+ dotest multibranch2-12 "${testcvs} -q log -r1.1 file1" \
+"
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+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.
+
+ dotest multibranch2-13 "${testcvs} -q update -r B" "U file1
+U file2"
+ dotest multibranch2-14 "${testcvs} -q update -r A -j B file2" \
+"U file2
+RCS file: ${CVSROOT_DIRNAME}/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" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ tag8k)
+ # In cvs-1.9.27, there is a bug that can cause an abort.
+ # It happens when you commit a change to a ,v file that has
+ # just the right amount of tag/branch info to align one of the
+ # semicolons in the branch info to be on a 8k-byte boundary.
+ # The result: rcsbuf_getkey got an abort. This failure doesn't
+ # corrupt the ,v file -- that would be really serious. But it
+ # does leave stale write locks that have to be removed manually.
+
+ mkdir 1
+ cd 1
+
+ module=x
+
+ : > junk
+ dotest tag8k-1 "$testcvs -Q import -m . $module X Y" ''
+ dotest tag8k-2 "$testcvs -Q co $module" ''
+ cd $module
+
+ file=m
+ : > $file
+ dotest tag8k-3 "$testcvs add $file" \
+"$SPROG add: scheduling file .$file. for addition
+$SPROG add: use .$SPROG commit. to add this file permanently"
+ dotest tag8k-4 "$testcvs -Q ci -m . $file"
+
+ # It seems there have to be at least two versions.
+ echo a > $file
+ dotest tag8k-5 "$testcvs -Q ci -m . $file"
+
+ # Add just under 8K worth of tags.
+
t=TAG---------------------------------------------------------------------
+ t=$t$t
+ t=$t$t$t$t$t
+ # Now $t is 720 bytes long.
+
+ # Apply some tags with that long prefix.
+ dotest tag8k-6 "$testcvs -Q tag $t-0 $file" ''
+ dotest tag8k-7 "$testcvs -Q tag $t-1 $file" ''
+ dotest tag8k-8 "$testcvs -Q tag $t-2 $file" ''
+ dotest tag8k-9 "$testcvs -Q tag $t-3 $file" ''
+ dotest tag8k-10 "$testcvs -Q tag $t-4 $file" ''
+ dotest tag8k-11 "$testcvs -Q tag $t-5 $file" ''
+ dotest tag8k-12 "$testcvs -Q tag $t-6 $file" ''
+ dotest tag8k-13 "$testcvs -Q tag $t-7 $file" ''
+ dotest tag8k-14 "$testcvs -Q tag $t-8 $file" ''
+ dotest tag8k-15 "$testcvs -Q tag $t-9 $file" ''
+ dotest tag8k-16 "$testcvs -Q tag $t-a $file" ''
+
+ # Extract the author value.
+ name=`sed -n 's/.*; author \([^;]*\);.*/\1/p'
${CVSROOT_DIRNAME}/$module/$file,v|sed 1q`
+
+ # Form a suffix string of length (16 - length($name)).
+ # CAREFUL: this will lose if $name is longer than 16.
+ sed_pattern=`echo $name|sed s/././g`
+ suffix=`echo 1234567890123456|sed s/$sed_pattern//`
+
+ # Add a final tag with length chosen so that it will push the
+ # offset of the `;' in the 2nd occurrence of `;\tauthor' in the
+ # ,v file to exactly 8192.
+ dotest tag8k-17 "$testcvs -Q tag "x8bytes-$suffix" $file" ''
+
+ # This commit would fail with 1.9.27.
+ echo a >> $file
+ dotest tag8k-18 "$testcvs -Q ci -m . $file"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ admin)
+ # More "cvs admin" tests.
+ # The basicb-21 test tests rejecting an invalid 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)
+ # basicb-o* (attempt to delete all revisions)
+ # 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; binary files)
+ # binfiles3-9 (binary files)
+ # 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?)
+ # -n: admin, tagf tests.
+
+ # Test the postadmin hook as a side effect of the rest of the tests.
+ # See the `info' test for notes on where other script hooks are
+ # tested.
+ mkdir 2; cd 2
+ dotest admin-init-1 "$testcvs -Q co CVSROOT"
+ cd CVSROOT
+ echo "ALL $TESTDIR/2/loggit %r %p %c" >>postadmin
+ dotest admin-init-2 "$testcvs -Q ci -mlog-admin"
+ cd .. # 2
+
+ cat >loggit <<EOF
+#!$TESTSHELL
+echo \${1+"\$@"} >>$TESTDIR/2/admin-log
+EOF
+ # #^@&!^@ Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x $TESTDIR/2/loggit"
+ else
+ chmod +x loggit
+ fi
+ cd .. # $TESTDIR
+
+
+ mkdir 1; cd 1
+ dotest admin-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest admin-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+
+ dotest_fail admin-3 "${testcvs} -q admin -i file1" \
+"${CPROG} admin: the -i option to admin is not supported
+${CPROG} admin: run add or import to create an RCS file
+${CPROG} \[admin aborted\]: specify ${CPROG} -H admin for usage information"
+ dotest_fail admin-4 "${testcvs} -q log file1" \
+"${SPROG} log: nothing known about file1"
+ dotest_fail admin-4a "${testcvs} -q admin file1" \
+"${SPROG} admin: 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" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest admin-6 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ 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
+ echo 'add a file on the branch' >> file3
+ dotest admin-9a "${testcvs} -q add file3" \
+"${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest admin-9b "${testcvs} -q ci -m modify-on-branch" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+new revision: 1\.1\.2\.1; previous revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/Attic/file3,v <-- file3
+new revision: 1\.1\.2\.1; previous revision: 1\.1"
+ dotest admin-10 "${testcvs} -q update -A" \
+"U file1
+${SPROG} update: \`file3' is no longer in the repository"
+
+ # Check that we can administer files in the repository that
+ # aren't in the working directory.
+ dotest admin-10-1 "${testcvs} admin ." \
+"${SPROG} admin: Administrating .
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+ dotest admin-10-2 "${testcvs} -q admin file3" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+done"
+
+ # 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" \
+"${CPROG} admin: while processing more than one file:
+${CPROG} \[admin aborted\]: attempt to specify a numeric revision"
+ dotest_fail admin-10b "${testcvs} -q admin -m1.1:bogus file1 file2" \
+"${CPROG} admin: while processing more than one file:
+${CPROG} \[admin aborted\]: attempt to specify a numeric revision"
+
+ # try a bad symbolic revision
+ dotest_fail admin-10c "${testcvs} -q admin -bBOGUS" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file1,v: Symbolic name BOGUS is
undefined.
+${SPROG} admin: RCS file for .file1. not modified\.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file2,v: Symbolic name BOGUS is
undefined.
+${SPROG} admin: RCS file for .file2. not modified\."
+
+ # 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: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-11a "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+add
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: foo; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-branch
+============================================================================="
+ dotest admin-12 "${testcvs} -q admin -bbr file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-12a "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+add
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: foo; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-branch
+============================================================================="
+
+ # "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 valid, 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).
+ # Note that we must accept either 2 or 4 digit year.
+ 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@;
+
+
+1\.1
+date ${RCSDELTADATE}; author ${username}; state Exp;
+branches
+ 1\.1\.2\.1;
+next ;
+commitid ${commitid};
+
+1\.1\.2\.1
+date ${RCSDELTADATE}; author ${username}; state foo;
+branches;
+next ;
+commitid ${commitid};
+
+
+desc
+@@
+
+
+1\.1
+log
address@hidden
+@
+text
+@@
+
+
+1\.1\.2\.1
+log
address@hidden
+@
+text
address@hidden 1
+add a line on the branch
+@"
+ dotest_fail admin-14-1 "${testcvs} -q admin \
+-m1.1.1.1:changed-bogus-log-message file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+$SPROG admin: $CVSROOT_DIRNAME/first-dir/file2,v: no such revision 1\.1\.1\.1
+$SPROG admin: RCS file for .file2. not modified."
+ dotest admin-14-2 "${testcvs} -q log file2" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+Working file: file2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ br: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add
+============================================================================="
+
+ dotest admin-14-3 "${testcvs} -q admin -aauth3 -aauth2,foo \
+-soneone:1.1 -m1.1:changed-log-message -ntagone: file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+ dotest admin-15 "${testcvs} -q log file2" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: oneone; commitid:
${commitid};
+changed-log-message
+============================================================================="
+
+ dotest admin-16 "${testcvs} -q admin \
+-A${CVSROOT_DIRNAME}/first-dir/file2,v -b -L -Nbr:1.1 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-17 "${testcvs} -q log file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+add
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: foo; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-branch
+============================================================================="
+
+ dotest_fail admin-18 "${testcvs} -q admin -nbr:1.1.2 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file1,v: symbolic name br already
bound to 1\.1
+${SPROG} admin: RCS file for .file1. not modified\."
+ dotest admin-19 "${testcvs} -q admin -ebaz -ebar,auth3 -nbr file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-20 "${testcvs} -q log file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+add
+----------------------------
+revision 1.1.2.1
+date: ${ISO8601DATE}; author: ${username}; state: foo; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-branch
+============================================================================="
+
+ # 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: ${CVSROOT_DIRNAME}/first-dir/file1,v
+${SPROG} admin: Couldn't open rcs file .${TESTDIR}/foo/bar.: No such file or
directory
+${SPROG} \[admin aborted\]: cannot continue"
+
+ # 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 $remote; then :; else
+ dotest admin-19a-admin "${testcvs} -q admin
-A../../cvsroot/first-dir/file2,v file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-19a-log "${testcvs} -q log -h -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch:
+locks: strict
+access list:
+ foo
+ auth2
+ auth3
+keyword substitution: kv
+total revisions: 2
+============================================================================="
+ fi # end of tests skipped for remote
+
+ # Now test that plain -e works right.
+ dotest admin-19a-2 "${testcvs} -q admin -e file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+ dotest admin-19a-3 "${testcvs} -q log -h -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch:
+locks: strict
+access list:
+keyword substitution: kv
+total revisions: 2
+============================================================================="
+
+ # Put the access list back, to avoid special cases later.
+ dotest admin-19a-4 "${testcvs} -q admin -afoo,auth2 file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+
+ # Add another revision to file2, so we can delete one.
+ echo 'add a line' >> file2
+ dotest admin-21 "${testcvs} -q ci -m modify file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.2; previous revision: 1\.1"
+ dotest admin-22 "${testcvs} -q admin -o1.1 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/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" \
+"${SPROG} add: scheduling file .aaa. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest admin-22-o2 "${testcvs} -q ci -m first aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+initial revision: 1\.1"
+ echo second rev >> aaa
+ dotest admin-22-o3 "${testcvs} -q ci -m second aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.2; previous revision: 1\.1"
+ echo third rev >> aaa
+ dotest admin-22-o4 "${testcvs} -q ci -m third aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.3; previous revision: 1\.2"
+ echo fourth rev >> aaa
+ dotest admin-22-o5 "${testcvs} -q ci -m fourth aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.4; previous revision: 1\.3"
+ echo fifth rev >>aaa
+ dotest admin-22-o6 "${testcvs} -q ci -m fifth aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.5; previous revision: 1\.4"
+ echo sixth rev >> aaa
+ dotest admin-22-o7 "${testcvs} -q ci -m sixth aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.6; previous revision: 1\.5"
+ dotest admin-22-o8 "${testcvs} admin -l1.6 aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+1\.6 locked
+done"
+ dotest admin-22-o9 "${testcvs} log -r1.6 aaa" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+sixth
+============================================================================="
+ dotest_fail admin-22-o10 "${testcvs} admin -o1.5: aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/aaa,v: can't remove locked
revision 1\.6
+${SPROG} admin: RCS file for .aaa. not modified\."
+ dotest admin-22-o11 "${testcvs} admin -u aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+1\.6 unlocked
+done"
+ dotest admin-22-o12 "${testcvs} admin -o1.5: aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+deleting revision 1\.6
+deleting revision 1\.5
+done"
+ dotest admin-22-o13 "${testcvs} log aaa" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+fourth
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+third
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+second
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+first
+============================================================================="
+
+ 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" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.3\.2\.1; previous revision: 1\.3"
+ dotest_fail admin-22-o17 "${testcvs} admin -o1.2:1.4 aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+deleting revision 1\.4
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/aaa,v: can't remove branch point
1\.3
+${SPROG} admin: RCS file for .aaa. not modified\."
+ dotest admin-22-o18 "${testcvs} update -p -r1.4 aaa" \
+"===================================================================
+Checking out aaa
+RCS: ${CVSROOT_DIRNAME}/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" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.3\.2\.2; previous revision: 1\.3\.2\.1"
+ echo third branch rev >> aaa
+ dotest admin-22-o20 "${testcvs} ci -m branch-three aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.3\.2\.3; previous revision: 1\.3\.2\.2"
+ echo fourth branch rev >> aaa
+ dotest admin-22-o21 "${testcvs} ci -m branch-four aaa" \
+"$CVSROOT_DIRNAME/first-dir/aaa,v <-- aaa
+new revision: 1\.3\.2\.4; previous revision: 1\.3\.2\.3"
+ dotest admin-22-o22 "${testcvs} admin -o:1.3.2.3 aaa" \
+"RCS file: ${CVSROOT_DIRNAME}/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: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+fourth
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+branches: 1\.3\.2;
+third
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+second
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+first
+----------------------------
+revision 1\.3\.2\.4
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}4 -0;
commitid: ${commitid};
+branch-four
+============================================================================="
+
+ 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"
+
+ # 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: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+modify
+============================================================================="
+
+ dotest admin-25 "cat ${CVSROOT_DIRNAME}/first-dir/file1,v" \
+"head 1\.1;
+access
+ foo
+ auth2;
+symbols;
+locks; strict;
+comment @xx@;
+
+
+1\.1
+date ${RCSDELTADATE}; author ${username}; state Exp;
+branches
+ 1\.1\.2\.1;
+next ;
+commitid ${commitid};
+
+1\.1\.2\.1
+date ${RCSDELTADATE}; author ${username}; state foo;
+branches;
+next ;
+commitid ${commitid};
+
+
+desc
+@@
+
+
+1\.1
+log
address@hidden
+@
+text
+@@
+
+
+1\.1\.2\.1
+log
address@hidden
+@
+text
address@hidden 1
+add a line on the branch
+@"
+
+ # Tests of cvs admin -n. Make use of the results of
+ # admin-1 through admin-25.
+ # FIXME: We probably shouldn't make use of those results;
+ # this test is way too long as it is.
+
+ # tagtwo should be a revision
+ #
+ dotest admin-26-1 "${testcvs} admin -ntagtwo:tagone file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ # br1 should be a branch
+ #
+ dotest admin-26-2 "${testcvs} admin -nbr1:br file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ # Attach some tags using RCS versions
+ #
+ dotest admin-26-3 "${testcvs} admin -ntagthree:1.1 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ dotest admin-26-4 "${testcvs} admin -nbr2:1.1.2 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ dotest admin-26-5 "${testcvs} admin -nbr4:1.1.0.2 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ # Check results so far
+ #
+ dotest admin-26-6 "${testcvs} status -v file2" \
+"===================================================================
+File: file2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT_DIRNAME}/first-dir/file2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ br4 (branch: 1\.1\.2)
+ br2 (branch: 1\.1\.2)
+ tagthree (revision: 1\.1)
+ br1 (branch: 1\.1\.2)
+ tagtwo (revision: 1\.1)
+ tagone (revision: 1\.1)
+ br (branch: 1\.1\.2)"
+
+
+ # Add a couple more revisions
+ #
+ echo "nuthr_line" >> file2
+ dotest admin-27-1 "${testcvs} commit -m nuthr_line file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.3; previous revision: 1\.2"
+
+ echo "yet_another" >> file2
+ dotest admin-27-2 "${testcvs} commit -m yet_another file2" \
+"$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+new revision: 1\.4; previous revision: 1\.3"
+
+ # Fail trying to reattach existing tag with -n
+ #
+ dotest admin-27-3 "${testcvs} admin -ntagfour:1.1 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ dotest_fail admin-27-4 "${testcvs} admin -ntagfour:1.3 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file2,v: symbolic name tagfour
already bound to 1\.1
+${SPROG} admin: RCS file for .file2. not modified\."
+
+ # Succeed at reattaching existing tag, using -N
+ #
+ dotest admin-27-5 "${testcvs} admin -Ntagfour:1.3 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+done"
+
+ # Fail on some bogus operations
+ # Try to attach to nonexistant tag
+ #
+ dotest_fail admin-28-1 "${testcvs} admin -ntagsix:tagfive file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file2,v: Symbolic name or
revision tagfive is undefined\.
+${SPROG} admin: RCS file for .file2. not modified\."
+
+ # Try a some nonexisting numeric target tags
+ #
+ dotest_fail admin-28-2 "${testcvs} admin -ntagseven:2.1 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} \[admin aborted\]: revision .2\.1. does not exist"
+
+ dotest_fail admin-28-3 "${testcvs} admin -ntageight:2.1.2 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} \[admin aborted\]: revision .2\.1\.2. does not exist"
+
+ # Try some invalid targets
+ #
+ dotest_fail admin-28-4 "${testcvs} admin -ntagnine:1.a.2 file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} \[admin aborted\]: Tag .1\.a\.2. invalid. Cannot resolve extension:
\`a'"
+
+ # Confirm that a missing tag is not a fatal error.
+ dotest admin-28-5.1 "${testcvs} -Q tag BO+GUS file1" ''
+ dotest_fail admin-28-5.2 "${testcvs} admin -ntagten:BO+GUS file2
file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file2,v: Symbolic name or
revision BO${PLUS}GUS is undefined\.
+${SPROG} admin: RCS file for .file2. not modified\.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+done"
+
+ dotest_fail admin-28-6 "${testcvs} admin -nq.werty:tagfour file2" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} \[admin aborted\]: tag .q\.werty. must not contain the characters ..*"
+
+ # Verify the archive
+ #
+ dotest admin-29 "cat ${CVSROOT_DIRNAME}/first-dir/file2,v" \
+"head 1\.4;
+access
+ auth3
+ auth2
+ foo;
+symbols
+ tagfour:1\.3
+ br4:1\.1\.0\.2
+ br2:1\.1\.0\.2
+ tagthree:1\.1
+ br1:1\.1\.0\.2
+ tagtwo:1\.1
+ tagone:1\.1
+ br:1\.1\.0\.2;
+locks; strict;
+comment @# @;
+
+
+1\.4
+date ${RCSDELTADATE}; author ${username}; state Exp;
+branches;
+next 1\.3;
+commitid ${commitid};
+
+1\.3
+date ${RCSDELTADATE}; author ${username}; state Exp;
+branches;
+next 1\.2;
+commitid ${commitid};
+
+1\.2
+date ${RCSDELTADATE}; author ${username}; state Exp;
+branches;
+next ;
+commitid ${commitid};
+
+
+desc
+@@
+
+
+1\.4
+log
address@hidden
+@
+text
address@hidden a line
+nuthr_line
+yet_another
+@
+
+
+1\.3
+log
address@hidden
+@
+text
address@hidden 1
+@
+
+
+1\.2
+log
address@hidden
+@
+text
address@hidden 1
+@"
+
+ dotest_fail admin-30 "${testcvs} admin -mbr:another-log-message \
+file2 aaa file3" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/file2,v: no such revision br: 1\.1
+${SPROG} admin: RCS file for .file2. not modified.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/aaa,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/aaa,v: no such revision br
+${SPROG} admin: RCS file for .aaa. not modified.
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+done"
+ dotest admin-31 "${testcvs} log" \
+"${SPROG} log: Logging \.
+
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+fourth
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+branches: 1\.3\.2;
+third
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+second
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+first
+----------------------------
+revision 1\.3\.2\.4
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}4 -0;
commitid: ${commitid};
+branch-four
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+Working file: file1
+head: 1\.1
+branch:
+locks: strict
+access list:
+ foo
+ auth2
+symbolic names:
+ tagten: 1\.1
+ BO${PLUS}GUS: 1\.1
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+branches: 1\.1\.2;
+add
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: foo; lines: ${PLUS}1 -0;
commitid: ${commitid};
+modify-on-branch
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
+Working file: file2
+head: 1\.4
+branch:
+locks: strict
+access list:
+ auth3
+ auth2
+ foo
+symbolic names:
+ tagfour: 1\.3
+ br4: 1\.1\.0\.2
+ br2: 1\.1\.0\.2
+ tagthree: 1\.1
+ br1: 1\.1\.0\.2
+ tagtwo: 1\.1
+ tagone: 1\.1
+ br: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.4
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+yet_another
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+nuthr_line
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+modify
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/first-dir/Attic/file3,v
+Working file: file3
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+ br: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: dead; commitid:
${commitid};
+branches: 1\.1\.2;
+file file3 was initially added on branch br\.
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+another-log-message
+============================================================================="
+
+ # Currently, this test outputs 36 identical lines, so I am just
+ # checking $DOTSTAR for brevity.
+ dotest admin-postadmin-examine-1 "cat $TESTDIR/2/admin-log" \
+"$CVSROOT_DIRNAME first-dir admin$DOTSTAR"
+
+ dokeep
+
+ # clean up our after ourselves
+ restore_adm
+ cd ../..
+ rm -r 1 2
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-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.
+
+ # 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 ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1
+ dotest reserved-3 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest reserved-4 "${testcvs} -q ci -m add" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1"
+
+ dotest reserved-5 "${testcvs} -q admin -l file1" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/file1,v
+1\.1 locked
+done"
+ dotest reserved-6 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+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: ${CVSROOT_DIRNAME}/first-dir/file1,v
+1\.1 unlocked
+done"
+
+ dotest reserved-8 "${testcvs} log -N file1" "
+RCS file: ${CVSROOT_DIRNAME}/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: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+add
+============================================================================="
+
+ # 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.
+
+ cat >${TESTDIR}/lockme <<EOF
+#!${TESTSHELL}
+line=\`grep <\$1/\$2,v 'locks $anyusername:1\.[0-9];'\`
+if test -z "\$line"; then
+ # It isn't locked
+ exit 0
+else
+ user=\`echo \$line | sed -e 's/locks \\($anyusername\\):[0-9.]*;.*/\\1/'\`
+ version=\`echo \$line | sed -e 's/locks $anyusername:\\([0-9.]*\\);.*/\\1/'\`
+ echo "\$user has file a-lock locked for version \$version" >&2
+ exit 1
+fi
+EOF
+ # Cygwin. Blaaarg.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x ${TESTDIR}/lockme"
+ else
+ chmod +x ${TESTDIR}/lockme
+ fi
+
+ echo stuff > a-lock
+ dotest reserved-9 "${testcvs} add a-lock" \
+"${SPROG} add: scheduling file .a-lock. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest reserved-10 "${testcvs} -q ci -m new a-lock" \
+"$CVSROOT_DIRNAME/first-dir/a-lock,v <-- a-lock
+initial revision: 1\.1"
+ # 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" \
+"$CVSROOT_DIRNAME/CVSROOT/commitinfo,v <-- commitinfo
+new revision: 1\.2; previous revision: 1\.1
+$SPROG commit: 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;/'
${CVSROOT_DIRNAME}/first-dir/a-lock,v > a-lock,v
+ # Cygwin.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod 644
${CVSROOT_DIRNAME}/first-dir/a-lock,v"
+ else
+ chmod 644 ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+ fi
+ dotest reserved-13 "mv a-lock,v ${CVSROOT_DIRNAME}/first-dir/a-lock,v"
+ # Cygwin. Blah.
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod 444
${CVSROOT_DIRNAME}/first-dir/a-lock,v"
+ else
+ chmod 444 ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+ fi
+ echo more stuff >> a-lock
+ dotest_fail_sort reserved-13b "$testcvs ci -m '' a-lock" \
+" \"$TESTDIR/lockme\"
+Appending defaults (\" %r/%p %s\"), but please be aware that this usage is
+$SPROG \[commit aborted\]: correct above errors first!
+$SPROG commit: Pre-commit check failed
+$SPROG commit: warning: commitinfo line contains no format strings:
+deprecated\.
+fred has file a-lock locked for version 1\.1"
+ # OK, now test "cvs admin -l" in the case where someone
+ # else has the file locked.
+ dotest_fail reserved-13c "${testcvs} admin -l a-lock" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+${SPROG} \[admin aborted\]: Revision 1\.1 is already locked by fred"
+
+ dotest reserved-14 "${testcvs} admin -u1.1 a-lock" \
+"RCS file: ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+${SPROG} admin: ${CVSROOT_DIRNAME}/first-dir/a-lock,v: revision 1\.1 locked by
fred; breaking lock
+1\.1 unlocked
+done"
+ dotest reserved-15 "$testcvs -q ci -m success a-lock" \
+"$SPROG commit: warning: commitinfo line contains no format strings:
+ \"$TESTDIR/lockme\"
+Appending defaults (\" %r/%p %s\"), but please be aware that this usage is
+deprecated\.
+$CVSROOT_DIRNAME/first-dir/a-lock,v <-- a-lock
+new revision: 1\.2; previous revision: 1\.1"
+
+ # Now test for a bug involving branches and locks
+ sed -e 's/locks; strict;/locks fred:1.2; strict;/'
${CVSROOT_DIRNAME}/first-dir/a-lock,v > a-lock,v
+ chmod 644 ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+ dotest reserved-16 \
+"mv a-lock,v ${CVSROOT_DIRNAME}/first-dir/a-lock,v" ""
+ chmod 444 ${CVSROOT_DIRNAME}/first-dir/a-lock,v
+ dotest reserved-17 "${testcvs} -q tag -b br a-lock" "T a-lock"
+ dotest reserved-18 "${testcvs} -q update -r br a-lock" ""
+ echo edit it >>a-lock
+ dotest reserved-19 "${testcvs} -q ci -m modify a-lock" \
+"$SPROG commit: warning: commitinfo line contains no format strings:
+ \"$TESTDIR/lockme\"
+Appending defaults (\" %r/%p %s\"), but please be aware that this usage is
+deprecated\.
+$CVSROOT_DIRNAME/first-dir/a-lock,v <-- a-lock
+new revision: 1\.2\.2\.1; previous revision: 1\.2"
+
+ # undo commitinfo changes
+ cd ../CVSROOT
+ echo '# vanilla commitinfo' >commitinfo
+ dotest reserved-cleanup-1 "${testcvs} -q ci -m back commitinfo" \
+"$SPROG commit: warning: commitinfo line contains no format strings:
+ \"$TESTDIR/lockme\"
+Appending defaults (\" %r/%p %s\"), but please be aware that this usage is
+deprecated\.
+$CVSROOT_DIRNAME/CVSROOT/commitinfo,v <-- commitinfo
+new revision: 1\.3; previous revision: 1\.2
+$SPROG commit: Rebuilding administrative file database"
+
+ dokeep
+ cd ..; rm -r CVSROOT
+ cd ..
+ rm -r 1
+ rm $TESTDIR/lockme
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ diffmerge1)
+ # Make sure CVS can merge correctly in circumstances where it
+ # used to mess up (due to a bug which existed in diffutils 2.7
+ # and 2.6, but not 2.5, and which has been fixed in CVS's diff
+ # lib by Paul Eggert, bless his bitty heart).
+
+ # This first test involves two working copies, "mine" and
+ # "yours", checked out from the same repository at the same
+ # time. In yours, you remove some text from the end of the
+ # file and check it in; meanwhile, "me" has commented out some
+ # lines earlier in the file, and I go to check it in right
+ # after you checked yours in. CVS naturally tells me the file
+ # is not up-to-date, so I run cvs update, but it updates
+ # incorrectly, leaving in the lines of text you just deleted.
+ # Bad! I'm in too much of a hurry to actually look at the
+ # file, so I check it in and go home, and so your changes have
+ # been lost. Later you discover this, and you suspect me of
+ # deliberately sabotaging your work, so you let all the air
+ # out of my tires. Only after a series of expensive lawsuits
+ # and countersuits do we discover that this was all CVS's
+ # fault.
+ #
+ # Luckily, this problem has been fixed now, as our test will
+ # handily confirm, no doubt:
+
+ # First make a repository containing the original text:
+
+ # We should be here anyway, but cd to it just in case:
+ cd ${TESTDIR}
+
+ mkdir diffmerge1
+ cd diffmerge1
+
+ # These are the files we both start out with:
+ mkdir import
+ cd import
+ diffmerge_create_older_files
+
+ dotest diffmerge1_import \
+ "${testcvs} import -m import diffmerge1 tag1 tag2" \
+ "${DOTSTAR}No conflicts created by this import"
+ cd ..
+
+ # Check out two working copies, one for "you" and one for
+ # "me". If no branch is used and cvs detects that only one
+ # of the two people made changes, then cvs does not run the
+ # merge algorithm. But if a branch is used, then cvs does run
+ # the merge algorithm (even in this case of only one of the two
+ # people having made changes). CVS used to have a bug in this
+ # case. Therefore, it is important to test this case by
+ # using a branch:
+ ${testcvs} rtag -b tag diffmerge1 >/dev/null 2>&1
+ ${testcvs} checkout -r tag diffmerge1 >/dev/null 2>&1
+ mv diffmerge1 yours
+ ${testcvs} checkout diffmerge1 >/dev/null 2>&1
+ mv diffmerge1 mine
+
+ # In your working copy, you'll make changes, and
+ # then check in your changes before I check in mine:
+ cd yours
+ diffmerge_create_your_files
+ dotest diffmerge1_yours "${testcvs} -q ci -m yours" \
+"$CVSROOT_DIRNAME/diffmerge1/testcase01,v <-- testcase01
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase02,v <-- testcase02
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase03,v <-- testcase03
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase04,v <-- testcase04
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase05,v <-- testcase05
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase06,v <-- testcase06
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase07,v <-- testcase07
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase08,v <-- testcase08
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase09,v <-- testcase09
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1
+$CVSROOT_DIRNAME/diffmerge1/testcase10,v <-- testcase10
+new revision: 1\.1\.1\.1\.2\.1; previous revision: 1\.1\.1\.1"
+
+ # Change my copy. Then I
+ # update, after both my modifications and your checkin:
+ cd ../mine
+ diffmerge_create_my_files
+ dotest diffmerge1_mine "${testcvs} -q update -j tag" \
+"M testcase01
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase01,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase01
+M testcase02
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase02,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase02
+M testcase03
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase03,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase03
+M testcase04
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase04,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase04
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase05,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase05
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase06,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase06
+M testcase07
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase07,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase07
+testcase07 already contains the differences between 1\.1\.1\.1 and
1\.1\.1\.1\.2\.1
+M testcase08
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase08,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase08
+M testcase09
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase09,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase09
+M testcase10
+RCS file: ${CVSROOT_DIRNAME}/diffmerge1/testcase10,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.1\.1\.1\.2\.1
+Merging differences between 1\.1\.1\.1 and 1\.1\.1\.1\.2\.1 into testcase10"
+
+ # So if your changes didn't make it into my working copy, or
+ # in any case if the files do not look like the final text
+ # in the files in directory comp_me, then the test flunks:
+ cd ..
+ mkdir comp_me
+ cd comp_me
+ diffmerge_create_expected_files
+ cd ..
+ rm mine/.#*
+
+ dotest diffmerge1_cmp "directory_cmp comp_me mine"
+
+ # Clean up after ourselves:
+ dokeep
+ cd ..
+ rm -r diffmerge1
+ modify_repo rm -rf $CVSROOT_DIRNAME/diffmerge1
+ ;;
+
+
+
+ diffmerge2)
+
+ # FIXME: This test should be rewritten to be much more concise.
+ # It currently weighs in at something like 600 lines, but the
+ # same thing could probably be tested in more like 50-100 lines.
+ mkdir diffmerge2
+
+ # This tests for another diffmerge bug reported by Martin
+ # Tomes; actually, his bug was probably caused by an initial
+ # fix for the bug in test diffmerge1, and likely wasn't ever
+ # a problem in CVS as long as one was using a normal
+ # distribution of diff or a version of CVS that has the diff
+ # lib in it.
+ #
+ # Nevertheless, once burned twice cautious, so we test for his
+ # bug here.
+ #
+ # Here is his report, more or less verbatim:
+ # ------------------------------------------
+ #
+ # Put the attached file (sgrid.h,v) into your repository
+ # somewhere, check out the module and do this:
+ #
+ # cvs update -j Review_Phase_2_Enhancements sgrid.h
+ # cvs diff -r Review_V1p3 sgrid.h
+ #
+ # As there have been no changes made on the trunk there
+ # should be no differences, however this is output:
+ #
+ # % cvs diff -r Review_V1p3 sgrid.h
+ # Index: sgrid.h
+ # ===================================================================
+ # RCS file: /usr/local/repository/play/fred/sgrid.h,v
+ # retrieving revision 1.1.2.1
+ # diff -r1.1.2.1 sgrid.h
+ # 178a179,184
+ # > /*--------------------------------------------------------------
+ # > INLINE FUNCTION : HORIZONTALLINES
+ # > NOTES : Description at the end of the file
+ # > ----------------------------------------------------------------*/
+ # > uint16 horizontalLines( void );
+ # >
+ #
+ # I did a cvs diff -c -r 1.1 -r 1.1.2.1 sgrid.h and patched those
+ # differences to sgrid.h version 1.1 and got the correct result
+ # so it looks like the built in patch is faulty.
+ # -------------------------------------------------------------------
+ #
+ # This is the RCS file, sgrid.h,v, that he sent:
+
+ echo "head 1.1;
+access;
+symbols
+ Review_V1p3:1.1.2.1
+ Review_V1p3C:1.1.2.1
+ Review_1p3A:1.1.2.1
+ Review_V1p3A:1.1.2.1
+ Review_Phase_2_Enhancements:1.1.0.2
+ Review_V1p2:1.1
+ Review_V1p2B:1.1
+ Review_V1p2A:1.1
+ Review_V1p1:1.1
+ Review_1p1:1.1;
+locks; strict;
+comment @ * @;
+
+
+1.1
+date 97.04.02.11.20.05; author colinl; state Exp;
+branches
+ 1.1.2.1;
+next ;
+
+1.1.2.1
+date 97.06.09.10.00.07; author colinl; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
address@hidden: DEV1175
+DCN:
+Tested By: Colin Law
+Reviewed By:
+Reason for Change: Initial Revision of all files
+
+Design Change Details:
+
+Implications:
+@
+text
+@/* \$""Header: L:/gpanels/dis/sgrid.h_v 1.1.1.0 24 Jan 1996 14:59:20
PAULT \$ */
+/*
+ * \$""Log: L:/gpanels/dis/sgrid.h_v \$
+ *
+ * Rev 1.1.1.0 24 Jan 1996 14:59:20 PAULT
+ * Branched
+ *
+ * Rev 1.1 24 Jan 1996 12:09:52 PAULT
+ * Consolidated 4100 code merged to trunk
+ *
+ * Rev 1.0.2.0 01 Jun 1995 14:18:58 DAVEH
+ * Branched
+ *
+ * Rev 1.0 19 Apr 1995 16:32:48 COLINL
+ * Initial revision.
+*/
+/*****************************************************************************
+FILE : SGRID.H
+VERSION : 2.1
+AUTHOR : Dave Hartley
+SYSTEM : Borland C++
+DESCRIPTION : The declaration of the scrolling grid class
+
+*****************************************************************************/
+#if !defined(__SGRID_H)
+#define __SGRID_H
+
+#if !defined(__SCROLL_H)
+#include <scroll.h>
+#endif
+
+#if !defined(__GKI_H)
+#include \"gki.h\"
+#endif
+
+#if defined PRINTING_SUPPORT
+class Printer;
+#endif
+
+/*****************************************************************************
+CLASS : ScrollingGrid
+DESCRIPTION: This class inherits from a grid and a scrollable, and
+ can therefore use all the PUBLIC services provided by these
+ classes. A description of these can be found in
+ GRID.H and SCROLL.H.
+ A scrolling grid is a set of horizontal and vertical lines
+ that scroll and continually update to provide a complete grid
+
+*****************************************************************************/
+
+class ScrollingGrid : public Scrollable
+{
+ public:
+#if defined _WINDOWS
+/*---------------------------------------------------------------------------
+FUNCTION : CONSTRUCTOR
+DESCRIPTION : sets up the details of the grid, ready for painting
+ARGUMENTS : name : sgColour
+ - the colour of the grid
+ sgLineType
+ - the syle of line
+ sgHorizontalTotal
+ - the total number of horizontal grid lines
+ verticalSpacingMin
+ - the min distance between the vertical grid lines
+ on the scrolling axis
+ currentTimestamp
+ - timestamp value now
+ ticksPerSecond
+ - number of timestamp ticks per second
+ ticksPerPixel
+ - number of timestamp ticks per pixel required
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ ScrollingGrid( GkiColour sgColour, GkiLineType sgLineType,
+ uint16 sgHorizontalTotal,
+ uint16 verticalSpacingMin, uint32 currentTimestamp,
+ uint16 ticksPerSecond, uint32 ticksPerPixel );
+#else
+/*---------------------------------------------------------------------------
+FUNCTION : CONSTRUCTOR
+DESCRIPTION : sets up the details of the grid, ready for painting
+ARGUMENTS : name : sgColour
+ - the colour of the grid
+ sgLineType
+ - the syle of line
+ sgHorizontalTotal ( THE MAX NUMBER OF LINES IS 100 )
+ - the total number of horizontal grid lines
+ sgVerticalSpacing
+ - the distance between the vertical grid lines
+ on the scrolling axis
+
+RETURN : None
+NOTES : If the caller does not get the total grid lines value, synced
+ with the overall size of the viewport, the spacing between
+ grid lines will not be consistent.
+
+---------------------------------------------------------------------------*/
+ ScrollingGrid( GkiColour sgColour, GkiLineType sgLineType
+ , uint16 sgHorizontalTotal, uint16 sgVerticalSpacing );
+#endif
+/*---------------------------------------------------------------------------
+FUNCTION : DESTRUCTOR
+DESCRIPTION : tidies it all up
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ ~ScrollingGrid( void );
+
+/*---------------------------------------------------------------------------
+FUNCTION : ATTACH
+DESCRIPTION : This service overloads the base class service, as it does
+ additional work at the time of attachment.
+
+ARGUMENTS : name : tDrawingArea
+ - the scrolled viewport to attach this trend to
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void attach( SViewport *tDrawingArea );
+
+#if defined _WINDOWS
+/*---------------------------------------------------------------------------
+FUNCTION : calculateVerticalSpacing
+DESCRIPTION : determines optimum spacing along time axis
+ARGUMENTS :
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void calculateVerticalSpacing();
+
+/*---------------------------------------------------------------------------
+FUNCTION : gridSpacingTicks
+DESCRIPTION : Provides the grid spacing in the time axis in ticks
+ARGUMENTS :
+RETURN : Number of ticks
+NOTES :
+---------------------------------------------------------------------------*/
+ uint32 gridSpacingTicks();
+
+#endif
+
+/*---------------------------------------------------------------------------
+INLINE FUNCTION : HORIZONTALLINES
+NOTES : Description at the end of the file
+---------------------------------------------------------------------------*/
+ uint16 horizontalLines( void );
+
+#if defined _WINDOWS
+// In Windows the OnDraw() function replaces paint()
+/*---------------------------------------------------------------------------
+FUNCTION : ScrollingGrid OnDraw
+DESCRIPTION : Paints the given area of the grid.
+ Pure virtual
+ARGUMENTS : pDC pointer to the device context to use for display
+ Note that the device context operates in the coords
+ of the window owning the viewport
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ virtual void OnDraw( CDC *pDC );
+
+#else // not Windows
+
+/*---------------------------------------------------------------------------
+FUNCTION : PAINT
+DESCRIPTION : This extends the standard grid paint method to paint the
+ viewport relative to its current position.
+
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paint( void );
+#endif
+
+/*---------------------------------------------------------------------------
+FUNCTION : P A I N T T E X T M A R K E R S
+DESCRIPTION : this service allow the text markers to be painted seperatley
+ from the grid lines
+
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paintTextMarkers();
+
+#if defined PRINTING_SUPPORT
+/*---------------------------------------------------------------------------
+FUNCTION : P R I N T
+DESCRIPTION : This print service prints a grid marker ( being either a
+ timestamp or a date, IF there is one at the plot position
+ given
+
+ARGUMENTS : name :
+ displayPosition
+ - Where in the log to look to see if there is an
+ entry to print
+
+ - printerPtr
+ the printer to print to
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void print( uint16 currentPrintPos, Printer *printerPtr );
+#endif
+
+/*---------------------------------------------------------------------------
+FUNCTION : S E T D R I V E D I R E C T I O N
+DESCRIPTION : Sets direction for update and scrolling forwards or backwards
+ARGUMENTS : direction - required direction
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void setDriveDirection( ScrollDirection direction );
+
+/*---------------------------------------------------------------------------
+FUNCTION : S E T U P
+DESCRIPTION : service that will setup the grid prior to a paint
+
+ARGUMENTS : name :
+ - newTimestamp
+
+
+ - newTimeBase
+ the number of ticks that represent a plot point on
+ the trendgraph.
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void setup( uint32 newTimestamp, uint32 newTimeBase );
+
+#if defined PRINTING_SUPPORT
+/*---------------------------------------------------------------------------
+FUNCTION : S E T U P F O R P R I N T
+DESCRIPTION : This service iis to be called prior to printing. It allows
+ the grid to prepare its markers ready for the print
+ commands
+
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void setupForPrint();
+#endif
+
+/*---------------------------------------------------------------------------
+FUNCTION : UPDATE
+DESCRIPTION : When this service is called it will calculate what needs to
+ be painted and fill in the display again.
+
+ARGUMENTS : name : timeStamp
+ - the reference time of this update.
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void update( uint32 timeStamp );
+
+/*---------------------------------------------------------------------------
+FUNCTION : U P D A T E B U F F E R
+DESCRIPTION : When a display update is not required, use this method. It
+ updates the internal data ready for a call to paint that
+ will then show the grid in the right position
+
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void updateBuffer( void );
+
+ private:
+
+/*---------------------------------------------------------------------------
+FUNCTION : M A K E G R I D M A R K E R
+DESCRIPTION : service that perpares a string for display. The string will
+ either be a short date, or short time. this is determined
+ by the current setting of the dateMarker flag
+
+ARGUMENTS : name : timestampVal
+ - the value to convert
+
+ storePtr
+ - the place to put the string
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void makeGridMarker( uint32 timestampVal, char *storePtr );
+
+/*---------------------------------------------------------------------------
+FUNCTION : P A I N T G R I D M A R K E R
+DESCRIPTION : given a position will put the string on the display
+
+ARGUMENTS : name :
+ yPos
+ - were it goes on the Y-axis
+
+ gridMarkerPtr
+ - what it is
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paintGridMarker( uint16 yPos, char *gridMarkerPtr );
+
+#if defined _WINDOWS
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTHORIZONTALLINES
+DESCRIPTION : responsible for painting the grids horizontal lines
+ARGUMENTS : pRectToDraw pointer to rectangle that needs refreshing.
+ in viewport coords
+ pDC pointer to device context to use
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paintHorizontalLines(RectCoords* pRectToDraw, CDC* pDC );
+#else
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTHORIZONTALLINES
+DESCRIPTION : responsible for painting the grids horizontal lines
+ARGUMENTS : name: xStart
+ - the starting X co-ordinate for the horizontal line
+ xEnd
+ - the ending X co-ordinate for the horizontal line
+
+RETURN : None
+NOTES : Remember lines are drawn from origin. The origin in a
+ horizontal viewport will be the top.
+---------------------------------------------------------------------------*/
+ void paintHorizontalLines( uint16 xStart, uint16 xEnd );
+#endif
+
+#if defined _WINDOWS
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTVERTICALLINES
+DESCRIPTION : responsible for painting the grids vertical lines
+ARGUMENTS : pRectToDraw pointer to rectangle that needs refreshing.
+ in viewport coords
+ offset offset from rhs that rightmost line would be
+ drawn if rectangle included whole viewport
+ pDC pointer to device context to use
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paintVerticalLines( RectCoords* pRectToDraw, uint16 offset,
+ CDC* pDC );
+#else
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTVERTICALLINES
+DESCRIPTION : responsible for painting the grids vertical lines
+ARGUMENTS : name : yStart
+ - the starting Y co-ordinate for the vertical line
+ yEnd
+ - the ending Y co-ordinate for the vertical line
+ offset
+ - a starting point offset that determines at what X
+ position the first line will be drawn
+
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void paintVerticalLines( uint16 yStart, uint16 yEnd, uint16 offset );
+#endif
+
+#if defined _WINDOWS
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTVERTICALLINE
+DESCRIPTION : paints one line at the position specified, and length
+ARGUMENTS : name : yStart
+ - the starting point on the y axis for the line
+ yEnd
+ - the end point on the y axis for the line
+ xPosition
+ - The horizontal offset from the start of the viewport
+ pDC pointer to device context to use
+
+RETURN : None
+NOTES : There is not an equivalent horizontal method as yet. This
+ is a seperate method because the service is useful to a
+ derivation of this class
+---------------------------------------------------------------------------*/
+ void paintVerticalLine( uint16 yStart, uint16 yEnd
+ , uint16 xPosition, CDC *pDC );
+#else
+/*---------------------------------------------------------------------------
+FUNCTION : PAINTVERTICALLINE
+DESCRIPTION : paints one line at the position specified, and length
+ARGUMENTS : name : yStart
+ - the starting point on the y axis for the line
+ yEnd
+ - the end point on the y axis for the line
+ xPosition
+ - The horizontal offset from the start of the viewport
+
+RETURN : None
+NOTES : There is not an equivalent horizontal method as yet. This
+ is a seperate method because the service is useful to a
+ derivation of this class
+---------------------------------------------------------------------------*/
+ void paintVerticalLine( uint16 yStart, uint16 yEnd
+ , uint16 xPosition );
+#endif
+
+/*---------------------------------------------------------------------------
+INLINE FUNCTION : VERTICALSPACING
+NOTES : Description at the end of the file
+---------------------------------------------------------------------------*/
+ uint16 verticalSpacing( void );
+
+
+ // Position in viewport that we are now writing to if going forwards
+ // Note that if this is greater than viewport length then we have
+ // just scrolled and value must be adjusted before use.
+ sint16 forwardsOutputPosition;
+
+ // Position in viewport that we are now writing to if going backwards
+ // Note that if this is less than zero then we have
+ // just scrolled and value must be adjusted before use.
+ sint16 backwardsOutputPosition;
+
+ // position in grid cycle of forwards output position.
+ // if zero then it is time to output a grid line
+ sint16 forwardsIntervalCount;
+
+ // position in grid cycle of forwards output position.
+ // if zero then it is time to output a grid line
+ sint16 backwardsIntervalCount;
+
+ uint32 lastUpdateTimestamp;
+ uint32 timeBase; // ticks per pixel
+ uint16 currentOutputPosition;
+ uint16 gridTimestampSpacing;
+ uint16 intervalCount;
+ uint16 horizontalTotal;
+ uint16 vSpacing;
+#if defined PRINTING_SUPPORT
+ uint16 numberOfGridMarkersPrinted;
+#endif
+ bool firstTime; // indicates first time through
+ bool dateMarker;
+
+ GkiLineType lineType;
+ GkiColour gridColour;
+
+ #if defined _WINDOWS
+ uint16 ticksPerSec; // number of time ticks per second
+ uint16 vSpacingMin; // minimum pixels per division along time axis
+ CPen *pPen; // the pen to use for drawing in windows
+ #endif
+
+};
+
+
+/*****************************************************************************
+ I N L I N E F U N C T I O N S
+*****************************************************************************/
+
+/*---------------------------------------------------------------------------
+FUNCTION : HORIZONTALLINES
+DESCRIPTION : supplies the number of horizontal lines in the grid
+ARGUMENTS : name :
+
+RETURN :
+NOTES :
+---------------------------------------------------------------------------*/
+inline uint16 ScrollingGrid::horizontalLines( void )
+{
+ return( horizontalTotal );
+}
+/*---------------------------------------------------------------------------
+FUNCTION : VERTICALSPACING
+DESCRIPTION : returns the distance between adjacent vertical lines
+ARGUMENTS : name :
+
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+inline uint16 ScrollingGrid::verticalSpacing( void )
+{
+ return( vSpacing );
+}
+
+#endif
+@
+
+
+1.1.2.1
+log
address@hidden:DS4 Provision of major and minor grid lines
+@
+text
address@hidden 1
+a1 1
+/* \$""Header: /usr/local/repository/cmnsrc/review/src/sgrid.h,v 1.1
1997/04/02 11:20:05 colinl Exp \$ */
+d3 1
+a3 12
+ * \$""Log: sgrid.h,v \$
+ * Revision 1.1 1997/04/02 11:20:05 colinl
+ * Project: DEV1175
+ * DCN:
+ * Tested By: Colin Law
+ * Reviewed By:
+ * Reason for Change: Initial Revision of all files
+ *
+ * Design Change Details:
+ *
+ * Implications:
+ *
+d58 6
+a63 5
+ARGUMENTS : name : majorColour colour for major grid lines
+ minorColour colour for minor grid lines
+ sgLineType line type for minor grid lines
+ yMajorGridLines number of major y lines on grid
+ yMinorGridLines number of major y lines on grid
+d77 2
+a78 3
+ ScrollingGrid( GkiColour majorColour, GkiColour minorColour,
+ GkiLineType sgLineType,
+ uint16 yMajorGridLines, uint16 yMinorGridLines,
+a137 17
+FUNCTION : DrawHorizontalGridLines
+
+DESCRIPTION : Draws major or minor grid lines
+ARGUMENTS : pDC device context
+ pPen pen to use
+ numLines total lines required
+ yLow, yHigh, xLow, xHigh rectangle to draw in
+ yMax max y value
+RETURN : None
+NOTES :
+---------------------------------------------------------------------------*/
+ void DrawHorizontalGridLines( CDC* pDC, CPen* pPen,
+ uint16 numLines,
+ uint16 yLow, uint16 yHigh, uint16 xLow, uint16 xHigh,
+ uint16 yMax );
+
+/*---------------------------------------------------------------------------
+d148 6
+d448 1
+a448 2
+ uint16 m_yMajorGridLines;
+ uint16 m_yMinorGridLines;
+d456 2
+a457 3
+ GkiLineType lineType; // line type for minor grid lines
+ GkiColour m_majorColour;
+ GkiColour m_minorColour;
+d462 1
+a462 2
+ CPen *pMajorPen; // pen to use for drawing major grid lines
+ CPen *pMinorPen; // pen to use for drawing minor grid lines
+d472 12
+@" > diffmerge2/sgrid.h,v
+
+ # We have to put the RCS file in the repository by hand for
+ # this test:
+ modify_repo mkdir $CVSROOT_DIRNAME/diffmerge2
+ modify_repo cp diffmerge2/sgrid.h,v \
+ $CVSROOT_DIRNAME/diffmerge2/sgrid.h,v
+ rm -rf diffmerge2
+ dotest diffmerge2_co \
+ "$testcvs co diffmerge2" "${DOTSTAR}U $DOTSTAR"
+ cd diffmerge2
+ dotest diffmerge2_update \
+ "${testcvs} update -j Review_Phase_2_Enhancements sgrid.h" \
+ "${DOTSTAR}erging ${DOTSTAR}"
+ # This is the one that counts -- there should be no output:
+ dotest diffmerge2_diff \
+ "${testcvs} diff -r Review_V1p3 sgrid.h" ''
+
+ dokeep
+ cd ..
+ rm -rf diffmerge2
+ modify_repo rm -rf $CVSROOT_DIRNAME/diffmerge2
+ ;;
+
+
+
+ release)
+ # Tests of "cvs release", particularly multiple arguments.
+ # Other CVS release tests:
+ # info-cleanup-0 for "cvs -n release".
+ # ignore-193 for the text of the question that cvs release asks.
+ # Also for interactions with cvsignore.
+ # basicc: "-d .", global -Q, no arguments (is a noop),
+ # "cvs release" without -d, multiple arguments.
+ # dirs-4: repository directory has been deleted.
+ # modules2-6: multiple arguments.
+
+ # First the usual setup; create a directory first-dir.
+ mkdir 1; cd 1
+ dotest release-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest release-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ mkdir dir1
+ dotest release-3 "${testcvs} add dir1" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir1 added to the repository"
+ mkdir dir2
+ dotest release-4 "${testcvs} add dir2" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir2 added to the repository"
+ cd dir2
+ mkdir dir3
+ dotest release-5 "${testcvs} add dir3" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir2/dir3 added to the repository"
+
+ cd ../..
+ dotest release-6 "${testcvs} release -d first-dir/dir2/dir3
first-dir/dir1" \
+"You have .0. altered files in this repository.
+Are you sure you want to release (and delete) directory .first-dir/dir2/dir3.:
\
+You have .0. altered files in this repository.
+Are you sure you want to release (and delete) directory .first-dir/dir1.: "
<<EOF
+yes
+yes
+EOF
+ dotest_fail release-7 "test -d first-dir/dir1" ''
+ dotest_fail release-8 "test -d first-dir/dir2/dir3" ''
+ dotest release-9 "${testcvs} update" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating first-dir
+${SPROG} update: Updating first-dir/dir2"
+
+ cd first-dir
+ mkdir dir1
+ dotest release-10 "${testcvs} add dir1" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir1 added to the repository"
+ cd dir2
+ mkdir dir3
+ dotest release-11 "${testcvs} add dir3" \
+"Directory ${CVSROOT_DIRNAME}/first-dir/dir2/dir3 added to the repository"
+
+ cd ../..
+ dotest release-12 "${testcvs} release first-dir/dir2/dir3
first-dir/dir1" \
+"You have .0. altered files in this repository.
+Are you sure you want to release directory .first-dir/dir2/dir3.: .. .release.
aborted by user choice.
+You have .0. altered files in this repository.
+Are you sure you want to release directory .first-dir/dir1.: " <<EOF
+no
+yes
+EOF
+ dotest release-13 "${testcvs} release first-dir/dir2/dir3
first-dir/dir2" \
+"You have .0. altered files in this repository.
+Are you sure you want to release directory .first-dir/dir2/dir3.: \
+You have .0. altered files in this repository.
+Are you sure you want to release directory .first-dir/dir2.: " <<EOF
+yes
+yes
+EOF
+ dotest release-14 "test -d first-dir/dir1" ''
+ dotest release-15 "test -d first-dir/dir2/dir3" ''
+
+ mkdir first-dir/dir1/dir4
+ # FIXCVS: There should be a path showing in front of dir below,
+ # I believe.
+ dotest release-unrecognized-dir-1 \
+"${testcvs} release -d first-dir/dir1" \
+"${QUESTION} dir4
+You have .0. altered files in this repository.
+Are you sure you want to release (and delete) directory \`first-dir/dir1': "
<<EOF
+yes
+EOF
+
+ rm -rf first-dir/dir2
+
+ dotest release-16 "${testcvs} update" \
+"$SPROG update: Updating \.
+$SPROG update: Updating first-dir"
+
+ # Check to make sure release isn't overwriting a
+ # CVS/Entries file in the current directory (using data
+ # from the released directory).
+
+ # cvs 1.11 (remote) fails on release-21 (a message about
+ # chdir into the removed directory), although it seemingly
+ # unedits and removes the directory correctly. If
+ # you manually continue, it then fails on release-22 do
+ # to the messed up CVS/Entries file from release-21.
+ cd first-dir
+ mkdir second-dir
+ dotest release-18 "$testcvs add second-dir" \
+"Directory $CVSROOT_DIRNAME/first-dir/second-dir added to the repository"
+
+ cd second-dir
+ touch file1
+ dotest release-19 "$testcvs -Q add file1"
+ dotest release-20 '$testcvs -q ci -m add' \
+"$CVSROOT_DIRNAME/first-dir/second-dir/file1,v <-- file1
+initial revision: 1\.1"
+ dotest release-21 "$testcvs edit file1"
+ cd ..
+ dotest release-22 "echo yes | $testcvs release -d second-dir" \
+"You have \[0\] altered files in this repository.
+Are you sure you want to release (and delete) directory \`second-dir': "
+ dotest release-23 "$testcvs -q update -d" "U second-dir/file1"
+ dotest release-24 "$testcvs edit"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ modify_repo rm -rf 1 $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ recase)
+ #
+ # Some tests of behavior which broke at one time or another when run
+ # from case insensitive clients against case sensitive servers.
+ #
+ # These tests are namned according to the following convention:
+ #
+ # ci Client (sandbox filesystem) case Insensitive
+ # cs Client (sandbox filesystem) case Sensitive
+ # si Server (repository filesystem) case Insensitive
+ # ss Server (repository filesystem) case Sensitive
+ #
+
+ mkdir 1; cd 1
+
+ # First, we will expect different results for a few of these tests
+ # based on whether the repository is on a case sensitive filesystem
+ # or not and whether the sandbox is on a case sensitive filesystem or
+ # not, so determine which cases we are dealing with:
+ echo file >file
+ echo FiLe >FiLe
+ if cmp file FiLe >/dev/null; then
+ client_sensitive=false
+ else
+ client_sensitive=:
+ fi
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost 'echo file >file'
+ $CVS_RSH $remotehost 'echo FiLe >FiLe'
+ if $CVS_RSH $remotehost 'cmp file FiLe >/dev/null'; then
+ server_sensitive=false
+ else
+ server_sensitive=:
+ fi
+ else
+ server_sensitive=$client_sensitive
+ fi
+
+ # The first test (recase-1 & recase-2) is for a remove of a file then
+ # a readd in a different case.
+ modify_repo mkdir $CVSROOT_DIRNAME/first-dir
+ dotest recase-init-1 "$testcvs -Q co first-dir"
+ cd first-dir
+
+ echo this file has no content >file
+ dotest recase-init-2 "$testcvs -Q add file"
+ dotest recase-init-3 "$testcvs -Q ci -madd"
+ dotest recase-init-4 "$testcvs -Q tag first"
+
+ # Now remove the file.
+ dotest recase-init-5 "$testcvs -Q rm -f file"
+ dotest recase-init-6 "$testcvs -Q ci -mrm"
+
+ # Now the test - readd in a different case.
+ echo this file needs some content >FiLe
+ if $server_sensitive; then
+ dotest recase-1ss "$testcvs add FiLe" \
+"$SPROG add: scheduling file \`FiLe' for addition
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ dotest recase-2ss "$testcvs -q ci -mrecase" \
+"$CVSROOT_DIRNAME/first-dir/FiLe,v <-- FiLe
+initial revision: 1\.1"
+ else # server insensitive
+ dotest recase-1si "$testcvs add FiLe" \
+"$SPROG add: Re-adding file \`FiLe' after dead revision 1\.2\.
+$SPROG add: use \`$SPROG commit' to add this file permanently"
+ dotest recase-2si "$testcvs -q ci -mrecase" \
+"$CVSROOT_DIRNAME/first-dir/FiLe,v <-- FiLe
+new revision: 1\.3; previous revision: 1\.2"
+ fi
+
+ # Now verify that a checkout will still work
+ cd ../..
+ mkdir 2; cd 2
+ dotest recase-3 "$testcvs -q co first-dir" \
+"U first-dir/FiLe"
+
+ cd first-dir
+ # Prove that we can still get status and log information on
+ # conflicting case files (1 in Attic, one in parent).
+ if $remote; then
+ if $client_sensitive; then
+ file=file
+ fIlE=fIlE
+ else # client insensitive
+ # Because FiLe is present on a case insensitive client, it is the
+ # only one ever found and queried or altered.
+ file=FiLe
+ fIlE=FiLe
+ fi
+ else # ! $remote
+ file=file
+ fIlE=fIlE
+ fi
+ if $server_sensitive; then
+ if $client_sensitive; then
+ # Client finds Entry only for FiLe. Others returned by server.
+ dotest recase-4sscs "$testcvs status file" \
+"===================================================================
+File: no file file Status: Up-to-date
+
+ Working revision: No entry for file
+ Repository revision: 1\.2 $CVSROOT_DIRNAME/first-dir/Attic/file,v
+ Commit Identifier: ${commitid}"
+ dotest recase-5sscs "$testcvs log file" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/Attic/file,v
+Working file: file
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ first: 1\.1
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: $username; state: dead; lines: +0 -0;
commitid: ${commitid};
+rm
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+add
+============================================================================="
+ dotest recase-6sscs "$testcvs status FiLe" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-7sscs "$testcvs log FiLe" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ else # server sensitive && client insensitive
+ # Client finds same Entry for file & FiLe.
+ dotest recase-4ssci "$testcvs status file" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-5ssci "$testcvs log file" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ dotest recase-6ss "$testcvs status FiLe" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-7ss "$testcvs log FiLe" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ fi
+ else # server insensitive
+ # There is only one archive when the server is insensitive, but the
+ # printed file/archive name can vary.
+ dotest recase-4si "$testcvs status file" \
+"===================================================================
+File: $file Status: Up-to-date
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 $CVSROOT_DIRNAME/first-dir/$file,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-5si "$testcvs log file" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/$file,v
+Working file: $file
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ first: 1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: $username; state: Exp; lines: +1 -1;
commitid: ${commitid};
+recase
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: $username; state: dead; lines: +0 -0;
commitid: ${commitid};
+rm
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+add
+============================================================================="
+ dotest recase-6si "$testcvs status FiLe" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.3.*
+ Repository revision: 1\.3 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-7si "$testcvs log FiLe" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+ first: 1\.1
+keyword substitution: kv
+total revisions: 3; selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE}; author: $username; state: Exp; lines: +1 -1;
commitid: ${commitid};
+recase
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: $username; state: dead; lines: +0 -0;
commitid: ${commitid};
+rm
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+add
+============================================================================="
+ fi
+
+ # And when the file does not exist on the client, we go with the
+ # client Entries match.
+ if $client_sensitive && $server_sensitive; then
+ dotest recase-8sscs "$testcvs status fIlE" \
+"$SPROG status: nothing known about \`fIlE'
+===================================================================
+File: no file fIlE Status: Unknown
+
+ Working revision: No entry for fIlE
+ Repository revision: No revision control file"
+ else # !$client_sensitive || !$server_sensitive
+ dotest recase-8anyi "$testcvs status fIlE" \
+"===================================================================
+File: $fIlE Status: Up-to-date
+
+ Working revision: 1\.[0-9]*.*
+ Repository revision: 1\.[0-9]*
$CVSROOT_DIRNAME/first-dir/$fIlE,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ fi
+
+ # and an update
+ if $server_sensitive; then
+ dotest recase-9ss "$testcvs -q up -rfirst" \
+"$SPROG update: \`FiLe' is no longer in the repository
+U file"
+
+ if $client_sensitive; then
+ dotest recase-10sscs "$testcvs -q up -A" \
+"U FiLe
+$SPROG update: \`file' is no longer in the repository"
+ else # client insensitive
+ # FIXCVS: This should remove the offending file first.
+ dotest_fail recase-10ssci "$testcvs -q up -A" \
+"$SPROG update: move away \`\./FiLe'; it is in the way
+C FiLe
+$SPROG update: \`file' is no longer in the repository"
+
+ cd ..
+ rm -r first-dir
+ dotest recase-11ssci "$testcvs -q co first-dir" \
+"U first-dir/FiLe"
+ cd first-dir
+ fi
+
+ #
+ # See what happens when cased names clash.
+ #
+
+ # Copy the archive
+ if test -n "$remotehost"; then
+ modify_repo $CVS_RSH $remotehost \
+ "cp $CVSROOT_DIRNAME/first-dir/FiLe,v \
+ $CVSROOT_DIRNAME/first-dir/FILE,v"
+ else
+ modify_repo cp $CVSROOT_DIRNAME/first-dir/FiLe,v \
+ $CVSROOT_DIRNAME/first-dir/FILE,v
+ fi
+
+ if $client_sensitive; then
+ dotest recase-12sscs "$testcvs -q up" "U FILE"
+ else # client insensitive
+ dotest_fail recase-12ssci "$testcvs -q up" \
+"$SPROG update: move away \`\./FILE'; it is in the way
+C FILE"
+ fi
+ else # server insensitive
+ dotest recase-9si "$testcvs -q up -rfirst" "U FiLe"
+ dotest recase-10si "$testcvs -q up -A" "U FiLe"
+ fi
+
+ # Prove that we can still get status and log information on
+ # conflicting case files (1 in Attic, two in parent).
+ if $server_sensitive; then
+ if $client_sensitive; then
+ # Client finds Entry only for FiLe. Others returned by server.
+ dotest recase-13sscs "$testcvs status file" \
+"===================================================================
+File: no file file Status: Up-to-date
+
+ Working revision: No entry for file
+ Repository revision: 1\.2 $CVSROOT_DIRNAME/first-dir/Attic/file,v
+ Commit Identifier: ${commitid}"
+ dotest recase-14sscs "$testcvs log file" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/Attic/file,v
+Working file: file
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ first: 1\.1
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: $username; state: dead; lines: +0 -0;
commitid: ${commitid};
+rm
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+add
+============================================================================="
+ dotest recase-15sscs "$testcvs status FiLe" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-16sscs "$testcvs log FiLe" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ dotest recase-17sscs "$testcvs status FILE" \
+"===================================================================
+File: FILE Status: Up-to-date
+
+ Working revision: 1.1.*
+ Repository revision: 1.1 ${CVSROOT_DIRNAME}/first-dir/FILE,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-18sscs "$testcvs log FILE" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FILE,v
+Working file: FILE
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ else # $server_sensitive && !$client_sensitive
+ # Client finds same Entry for file & FiLe.
+ dotest recase-13ssci "$testcvs status file" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-16ssci "$testcvs log FiLe" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ dotest recase-17ssci "$testcvs status FILE" \
+"===================================================================
+File: FiLe Status: Up-to-date
+
+ Working revision: 1\.1.*
+ Repository revision: 1\.1 $CVSROOT_DIRNAME/first-dir/FiLe,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ dotest recase-18ssci "$testcvs log FILE" \
+"
+RCS file: $CVSROOT_DIRNAME/first-dir/FiLe,v
+Working file: FiLe
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: $username; state: Exp; commitid: ${commitid};
+recase
+============================================================================="
+ fi
+ else # !$server_sensitive
+ # Skip these when the server is case insensitive - nothing
+ # has changed since recase-[4-7]si
+ :
+ fi
+
+ if $client_sensitive && $server_sensitive; then
+ dotest recase-19sscs "$testcvs status fIlE" \
+"$SPROG status: nothing known about \`fIlE'
+===================================================================
+File: no file fIlE Status: Unknown
+
+ Working revision: No entry for fIlE
+ Repository revision: No revision control file"
+ else # !$client_sensitive || !$server_sensitive
+ dotest recase-19anyi "$testcvs status fIlE" \
+"===================================================================
+File: $fIlE Status: Up-to-date
+
+ Working revision: 1\.[0-9]*.*
+ Repository revision: 1\.[0-9]*
$CVSROOT_DIRNAME/first-dir/$fIlE,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)"
+ fi
+
+ # And last but not least, prove that a checkout is still possible.
+ cd ../..
+ mkdir 3; cd 3
+ if $server_sensitive; then
+ if $client_sensitive; then
+ dotest recase-20sscs "$testcvs -q co first-dir" \
+"U first-dir/FILE
+U first-dir/FiLe"
+ else # $server_senstive && !$client_sensitive
+ dotest_fail recase-20ssci "$testcvs -q co first-dir" \
+"U first-dir/FILE
+$SPROG checkout: move away \`first-dir/FiLe'; it is in the way
+C first-dir/FiLe"
+ fi
+ else # !$server_sensitive
+ # Skip these since nothing has changed.
+ :
+ fi
+
+ dokeep
+ cd ..
+ rm -r 1 2 3
+ if $server_sensitive && test -n "$remotehost"; then
+ # It is necessary to remove one of the case-conflicted files before
+ # recursively removing the rest under Cygwin on a Samba share or
+ # Samba returns a permission denied error due to its case
+ # confusion.
+ $CVS_RSH $remotehost "rm -f $CVSROOT_DIRNAME/first-dir/FILE,v"
+ fi
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ multiroot)
+ #
+ # set up two repositories
+ #
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT1_DIRNAME=${TESTDIR}/root.1
+ CVSROOT2_DIRNAME=${TESTDIR}/root.2
+ CVSROOT1=`newroot $CVSROOT1_DIRNAME`
+ CVSROOT2=`newroot $CVSROOT2_DIRNAME`
+ testcvs1="$testcvs -d '$CVSROOT1'"
+ testcvs2="$testcvs -d '$CVSROOT2'"
+
+ dotest multiroot-setup-1 "mkdir $CVSROOT1_DIRNAME $CVSROOT2_DIRNAME"
+ dotest multiroot-setup-2 "$testcvs1 init"
+ dotest multiroot-setup-3 "$testcvs2 init"
+
+ #
+ # create some directories in ${CVSROOT1_DIRNAME}
+ #
+ mkdir 1; cd 1
+ dotest multiroot-setup-4 "${testcvs1} co -l ." "${SPROG} checkout:
Updating ."
+ mkdir mod1-1 mod1-2
+ dotest multiroot-setup-5 "${testcvs1} add mod1-1 mod1-2" \
+"Directory ${CVSROOT1_DIRNAME}/mod1-1 added to the repository
+Directory ${CVSROOT1_DIRNAME}/mod1-2 added to the repository"
+ echo file1-1 > mod1-1/file1-1
+ echo file1-2 > mod1-2/file1-2
+ dotest multiroot-setup-6 "${testcvs1} add mod1-1/file1-1
mod1-2/file1-2" \
+"${SPROG} add: scheduling file .mod1-1/file1-1. for addition
+${SPROG} add: scheduling file .mod1-2/file1-2. for addition
+${SPROG} add: use \`${SPROG} commit' to add these files permanently"
+ dotest multiroot-setup-7 "${testcvs1} commit -m is" \
+"${CPROG} commit: Examining \.
+${CPROG} commit: Examining mod1-1
+${CPROG} commit: Examining mod1-2
+${CVSROOT1_DIRNAME}/mod1-1/file1-1,v <-- mod1-1/file1-1
+initial revision: 1.1
+${CVSROOT1_DIRNAME}/mod1-2/file1-2,v <-- mod1-2/file1-2
+initial revision: 1.1"
+ cd ..
+ rm -rf 1
+
+ #
+ # create some directories in ${CVSROOT2_DIRNAME}
+ #
+ mkdir 1; cd 1
+ dotest multiroot-setup-8 "${testcvs2} co -l ." "${SPROG} checkout:
Updating ."
+ mkdir mod2-1 mod2-2
+ dotest multiroot-setup-9 "${testcvs2} add mod2-1 mod2-2" \
+"Directory ${CVSROOT2_DIRNAME}/mod2-1 added to the repository
+Directory ${CVSROOT2_DIRNAME}/mod2-2 added to the repository"
+ echo file2-1 > mod2-1/file2-1
+ echo file2-2 > mod2-2/file2-2
+ dotest multiroot-setup-6 "${testcvs2} add mod2-1/file2-1
mod2-2/file2-2" \
+"${SPROG} add: scheduling file .mod2-1/file2-1. for addition
+${SPROG} add: scheduling file .mod2-2/file2-2. for addition
+${SPROG} add: use \`${SPROG} commit' to add these files permanently"
+ dotest multiroot-setup-10 "${testcvs2} commit -m anyone" \
+"${CPROG} commit: Examining \.
+${CPROG} commit: Examining mod2-1
+${CPROG} commit: Examining mod2-2
+${CVSROOT2_DIRNAME}/mod2-1/file2-1,v <-- mod2-1/file2-1
+initial revision: 1.1
+${CVSROOT2_DIRNAME}/mod2-2/file2-2,v <-- mod2-2/file2-2
+initial revision: 1.1"
+ cd ..
+ rm -rf 1
+
+ # check out a few directories, from simple/shallow to
+ # complex/deep
+ mkdir 1; cd 1
+
+ # OK, this case is kind of weird. If we just run things from
+ # here, without CVS/Root, then CVS will contact the server
+ # mentioned in CVSROOT (which is irrelevant) which will print
+ # some messages. Our workaround is to make sure we have a
+ # CVS/Root file at top level. In the future, it is possible
+ # the best behavior will be to extend the existing behavior
+ # ("being called from a directory without CVS administration
+ # has always meant to process each of the sub-dirs") to also
+ # do that if there is no CVSROOT, CVS/Root, or -d at top level.
+ #
+ # The local case could stumble through the tests without creating
+ # the top-level CVS/Root, but we create it for local and for
+ # remote to reduce special cases later in the test.
+ dotest multiroot-workaround "${testcvs1} -q co -l ." ""
+
+ dotest multiroot-setup-11 "${testcvs1} co mod1-1 mod1-2" \
+"${SPROG} checkout: Updating mod1-1
+U mod1-1/file1-1
+${SPROG} checkout: Updating mod1-2
+U mod1-2/file1-2"
+ dotest multiroot-setup-12 "${testcvs2} co mod2-1 mod2-2" \
+"${SPROG} checkout: Updating mod2-1
+U mod2-1/file2-1
+${SPROG} checkout: Updating mod2-2
+U mod2-2/file2-2"
+ cd mod1-2
+ dotest multiroot-setup-13 "${testcvs2} co mod2-2" \
+"${SPROG} checkout: Updating mod2-2
+U mod2-2/file2-2"
+ cd ..
+ cd mod2-2
+ dotest multiroot-setup-14 "${testcvs1} co mod1-2" \
+"${SPROG} checkout: Updating mod1-2
+U mod1-2/file1-2"
+ cd ..
+
+ #
+ # Make sure that the Root and Repository files contain the
+ # correct information.
+ #
+ dotest multiroot-cvsadm-1a "cat mod1-1/CVS/Root" "${CVSROOT1}"
+ dotest multiroot-cvsadm-1b "cat mod1-1/CVS/Repository" "mod1-1"
+ dotest multiroot-cvsadm-2a "cat mod2-1/CVS/Root" "${CVSROOT2}"
+ dotest multiroot-cvsadm-2b "cat mod2-1/CVS/Repository" "mod2-1"
+ dotest multiroot-cvsadm-3a "cat mod1-2/CVS/Root" "${CVSROOT1}"
+ dotest multiroot-cvsadm-3b "cat mod1-2/CVS/Repository" "mod1-2"
+ dotest multiroot-cvsadm-3c "cat mod1-2/mod2-2/CVS/Root" "${CVSROOT2}"
+ dotest multiroot-cvsadm-3d "cat mod1-2/mod2-2/CVS/Repository" "mod2-2"
+ dotest multiroot-cvsadm-4a "cat mod2-2/CVS/Root" "${CVSROOT2}"
+ dotest multiroot-cvsadm-4b "cat mod2-2/CVS/Repository" "mod2-2"
+ dotest multiroot-cvsadm-4c "cat mod2-2/mod1-2/CVS/Root" "${CVSROOT1}"
+ dotest multiroot-cvsadm-4d "cat mod2-2/mod1-2/CVS/Repository" "mod1-2"
+
+ #
+ # Start testing various cvs commands. Begin with commands
+ # without extra arguments (e.g. "cvs update", "cvs diff",
+ # etc.
+ #
+
+ # Do at least one command with both CVSROOTs to make sure
+ # that there's not some kind of unexpected dependency on the
+ # choice of which CVSROOT is specified on the command line.
+
+ dotest multiroot-update-1a "${testcvs1} update" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating mod1-1
+${SPROG} update: Updating mod1-2
+${SPROG} update: Updating mod1-2/mod2-2
+${SPROG} update: cannot open directory ${CVSROOT1_DIRNAME}/mod2-2: No such
file or directory
+${SPROG} update: skipping directory mod1-2/mod2-2
+${SPROG} update: Updating mod2-1
+${SPROG} update: cannot open directory ${CVSROOT1_DIRNAME}/mod2-1: No such
file or directory
+${SPROG} update: skipping directory mod2-1
+${SPROG} update: Updating mod2-2
+${SPROG} update: cannot open directory ${CVSROOT1_DIRNAME}/mod2-2: No such
file or directory
+${SPROG} update: skipping directory mod2-2"
+
+ # Same deal but with -d ${CVSROOT2}.
+ dotest multiroot-update-1b "${testcvs2} update" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating mod1-1
+${SPROG} update: cannot open directory ${CVSROOT2_DIRNAME}/mod1-1: No such
file or directory
+${SPROG} update: skipping directory mod1-1
+${SPROG} update: Updating mod1-2
+${SPROG} update: cannot open directory ${CVSROOT2_DIRNAME}/mod1-2: No such
file or directory
+${SPROG} update: skipping directory mod1-2
+${SPROG} update: Updating mod2-1
+${SPROG} update: Updating mod2-2
+${SPROG} update: Updating mod2-2/mod1-2
+${SPROG} update: cannot open directory ${CVSROOT2_DIRNAME}/mod1-2: No such
file or directory
+${SPROG} update: skipping directory mod2-2/mod1-2"
+
+ # modify all files and do a diff
+
+ echo bobby >> mod1-1/file1-1
+ echo brown >> mod1-2/file1-2
+ echo goes >> mod2-1/file2-1
+ echo down >> mod2-2/file2-2
+
+ dotest_fail multiroot-diff-1 "${testcvs} diff" \
+"${SPROG} diff: Diffing \.
+${SPROG} diff: Diffing mod1-1
+Index: mod1-1/file1-1
+===================================================================
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+retrieving revision 1\.1
+diff -r1\.1 file1-1
+1a2
+> bobby
+${SPROG} diff: Diffing mod1-2
+Index: mod1-2/file1-2
+===================================================================
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+retrieving revision 1\.1
+diff -r1\.1 file1-2
+1a2
+> brown
+${SPROG} diff: Diffing mod2-2/mod1-2
+${SPROG} diff: Diffing mod1-2/mod2-2
+${SPROG} diff: Diffing mod2-1
+Index: mod2-1/file2-1
+===================================================================
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+retrieving revision 1\.1
+diff -r1\.1 file2-1
+1a2
+> goes
+${SPROG} diff: Diffing mod2-2
+Index: mod2-2/file2-2
+===================================================================
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+retrieving revision 1\.1
+diff -r1\.1 file2-2
+1a2
+> down" \
+"${SPROG} diff: Diffing \.
+${SPROG} diff: Diffing mod1-1
+Index: mod1-1/file1-1
+===================================================================
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+retrieving revision 1\.1
+diff -r1\.1 file1-1
+1a2
+> bobby
+${SPROG} diff: Diffing mod1-2
+Index: mod1-2/file1-2
+===================================================================
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+retrieving revision 1\.1
+diff -r1\.1 file1-2
+1a2
+> brown
+${SPROG} diff: Diffing mod2-2
+${SPROG} diff: Diffing mod2-2/mod1-2
+${SPROG} diff: Diffing mod1-2
+${SPROG} diff: Diffing mod1-2/mod2-2
+${SPROG} diff: Diffing mod2-1
+Index: mod2-1/file2-1
+===================================================================
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+retrieving revision 1\.1
+diff -r1\.1 file2-1
+1a2
+> goes
+${SPROG} diff: Diffing mod2-2
+Index: mod2-2/file2-2
+===================================================================
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+retrieving revision 1\.1
+diff -r1\.1 file2-2
+1a2
+> down"
+
+ dotest multiroot-commit-1 "${testcvs} commit -m actually" \
+"${CPROG} commit: Examining \.
+${CPROG} commit: Examining mod1-1
+${CPROG} commit: Examining mod1-2
+${CPROG} commit: Examining mod2-2/mod1-2
+${CVSROOT1_DIRNAME}/mod1-1/file1-1,v <-- mod1-1/file1-1
+new revision: 1.2; previous revision: 1.1
+${CVSROOT1_DIRNAME}/mod1-2/file1-2,v <-- mod1-2/file1-2
+new revision: 1.2; previous revision: 1.1
+${CPROG} commit: Examining mod1-2/mod2-2
+${CPROG} commit: Examining mod2-1
+${CPROG} commit: Examining mod2-2
+${CVSROOT2_DIRNAME}/mod2-1/file2-1,v <-- mod2-1/file2-1
+new revision: 1.2; previous revision: 1.1
+${CVSROOT2_DIRNAME}/mod2-2/file2-2,v <-- mod2-2/file2-2
+new revision: 1.2; previous revision: 1.1"
+
+ dotest multiroot-update-2 "${testcvs} update" \
+"${CPROG} update: Updating \.
+${CPROG} update: Updating mod1-1
+${CPROG} update: Updating mod1-2
+${CPROG} update: Updating mod2-2/mod1-2
+U mod2-2/mod1-2/file1-2
+${CPROG} update: Updating mod1-2/mod2-2
+U mod1-2/mod2-2/file2-2
+${CPROG} update: Updating mod2-1
+${CPROG} update: Updating mod2-2" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating mod1-1
+${SPROG} update: Updating mod1-2
+${SPROG} update: Updating mod2-2
+${SPROG} update: Updating mod2-2/mod1-2
+U mod2-2/mod1-2/file1-2
+${SPROG} update: Updating mod1-2
+${SPROG} update: Updating mod1-2/mod2-2
+U mod1-2/mod2-2/file2-2
+${SPROG} update: Updating mod2-1
+${SPROG} update: Updating mod2-2"
+
+ dotest multiroot-tag-1 "${testcvs} tag cattle" \
+"${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging mod1-1
+T mod1-1/file1-1
+${SPROG} tag: Tagging mod1-2
+T mod1-2/file1-2
+${SPROG} tag: Tagging mod2-2/mod1-2
+${SPROG} tag: Tagging mod1-2/mod2-2
+T mod1-2/mod2-2/file2-2
+${SPROG} tag: Tagging mod2-1
+T mod2-1/file2-1
+${SPROG} tag: Tagging mod2-2" \
+"${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging mod1-1
+T mod1-1/file1-1
+${SPROG} tag: Tagging mod1-2
+T mod1-2/file1-2
+${SPROG} tag: Tagging mod2-2
+${SPROG} tag: Tagging mod2-2/mod1-2
+${SPROG} tag: Tagging mod1-2
+${SPROG} tag: Tagging mod1-2/mod2-2
+T mod1-2/mod2-2/file2-2
+${SPROG} tag: Tagging mod2-1
+T mod2-1/file2-1
+${SPROG} tag: Tagging mod2-2"
+
+ echo anotherfile1-1 > mod1-1/anotherfile1-1
+ echo anotherfile2-1 > mod2-1/anotherfile2-1
+ echo anotherfile1-2 > mod2-2/mod1-2/anotherfile1-2
+ echo anotherfile2-2 > mod1-2/mod2-2/anotherfile2-2
+
+ if $remote; then
+ cd mod1-1
+ dotest multiroot-add-1ar "${testcvs} add anotherfile1-1" \
+"${SPROG} add: scheduling file .anotherfile1-1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ../mod2-1
+ dotest multiroot-add-1br "${testcvs} add anotherfile2-1" \
+"${SPROG} add: scheduling file .anotherfile2-1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ../mod2-2/mod1-2
+ dotest multiroot-add-1cr "${testcvs} add anotherfile1-2" \
+"${SPROG} add: scheduling file .anotherfile1-2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ../../mod1-2/mod2-2
+ dotest multiroot-add-1dr "${testcvs} add anotherfile2-2" \
+"${SPROG} add: scheduling file .anotherfile2-2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ../..
+ else
+ dotest multiroot-add-1 "${testcvs} add mod1-1/anotherfile1-1
mod2-1/anotherfile2-1 mod2-2/mod1-2/anotherfile1-2
mod1-2/mod2-2/anotherfile2-2" \
+"${SPROG} add: scheduling file .mod1-1/anotherfile1-1. for addition
+${SPROG} add: scheduling file .mod2-1/anotherfile2-1. for addition
+${SPROG} add: scheduling file .mod2-2/mod1-2/anotherfile1-2. for addition
+${SPROG} add: scheduling file .mod1-2/mod2-2/anotherfile2-2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ fi
+
+ dotest multiroot-status-1 "${testcvs} status -v" \
+"${SPROG} status: Examining \.
+${SPROG} status: Examining mod1-1
+===================================================================
+File: anotherfile1-1 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file1-1 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod1-2
+===================================================================
+File: file1-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-2/mod1-2
+===================================================================
+File: anotherfile1-2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file1-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod1-2/mod2-2
+===================================================================
+File: anotherfile2-2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file2-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-1
+===================================================================
+File: anotherfile2-1 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file2-1 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-2
+===================================================================
+File: file2-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)" \
+"${SPROG} status: Examining \.
+${SPROG} status: Examining mod1-1
+===================================================================
+File: anotherfile1-1 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file1-1 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod1-2
+===================================================================
+File: file1-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-2
+${SPROG} status: Examining mod2-2/mod1-2
+===================================================================
+File: anotherfile1-2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file1-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod1-2
+${SPROG} status: Examining mod1-2/mod2-2
+===================================================================
+File: anotherfile2-2 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file2-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-1
+===================================================================
+File: anotherfile2-1 Status: Locally Added
+
+ Working revision: New file!
+ Repository revision: No revision control file
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+===================================================================
+File: file2-1 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)
+
+${SPROG} status: Examining mod2-2
+===================================================================
+File: file2-2 Status: Up-to-date
+
+ Working revision: 1\.2.*
+ Repository revision: 1\.2 ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+ Commit Identifier: ${commitid}
+ Sticky Tag: (none)
+ Sticky Date: (none)
+ Sticky Options: (none)
+
+ Existing Tags:
+ cattle (revision: 1\.2)"
+
+ dotest multiroot-commit-2 "${testcvs} commit -m reading" \
+"${CPROG} commit: Examining \.
+${CPROG} commit: Examining mod1-1
+${CPROG} commit: Examining mod1-2
+${CPROG} commit: Examining mod2-2/mod1-2
+${CVSROOT1_DIRNAME}/mod1-1/anotherfile1-1,v <-- mod1-1/anotherfile1-1
+initial revision: 1\.1
+${CVSROOT1_DIRNAME}/mod1-2/anotherfile1-2,v <-- mod2-2/mod1-2/anotherfile1-2
+initial revision: 1\.1
+${CPROG} commit: Examining mod1-2/mod2-2
+${CPROG} commit: Examining mod2-1
+${CPROG} commit: Examining mod2-2
+${CVSROOT2_DIRNAME}/mod2-2/anotherfile2-2,v <-- mod1-2/mod2-2/anotherfile2-2
+initial revision: 1\.1
+${CVSROOT2_DIRNAME}/mod2-1/anotherfile2-1,v <-- mod2-1/anotherfile2-1
+initial revision: 1\.1"
+
+ dotest multiroot-update-3 "${testcvs} update" \
+"${CPROG} update: Updating \.
+${CPROG} update: Updating mod1-1
+${CPROG} update: Updating mod1-2
+U mod1-2/anotherfile1-2
+${CPROG} update: Updating mod2-2/mod1-2
+${CPROG} update: Updating mod1-2/mod2-2
+${CPROG} update: Updating mod2-1
+${CPROG} update: Updating mod2-2
+U mod2-2/anotherfile2-2" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating mod1-1
+${SPROG} update: Updating mod1-2
+U mod1-2/anotherfile1-2
+${SPROG} update: Updating mod2-2
+${SPROG} update: Updating mod2-2/mod1-2
+${SPROG} update: Updating mod1-2
+${SPROG} update: Updating mod1-2/mod2-2
+${SPROG} update: Updating mod2-1
+${SPROG} update: Updating mod2-2
+U mod2-2/anotherfile2-2"
+
+ dotest multiroot-log-1 "${testcvs} log" \
+"${SPROG} log: Logging \.
+${SPROG} log: Logging mod1-1
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/anotherfile1-1,v
+Working file: mod1-1/anotherfile1-1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+Working file: mod1-1/file1-1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod1-2
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/anotherfile1-2,v
+Working file: mod1-2/anotherfile1-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+Working file: mod1-2/file1-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod2-2/mod1-2
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/anotherfile1-2,v
+Working file: mod2-2/mod1-2/anotherfile1-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+Working file: mod2-2/mod1-2/file1-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod1-2/mod2-2
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/anotherfile2-2,v
+Working file: mod1-2/mod2-2/anotherfile2-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+Working file: mod1-2/mod2-2/file2-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+=============================================================================
+${SPROG} log: Logging mod2-1
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/anotherfile2-1,v
+Working file: mod2-1/anotherfile2-1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+Working file: mod2-1/file2-1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+=============================================================================
+${SPROG} log: Logging mod2-2
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/anotherfile2-2,v
+Working file: mod2-2/anotherfile2-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+Working file: mod2-2/file2-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+============================================================================="
\
+"${SPROG} log: Logging \.
+${SPROG} log: Logging mod1-1
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/anotherfile1-1,v
+Working file: mod1-1/anotherfile1-1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-1/file1-1,v
+Working file: mod1-1/file1-1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod1-2
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/anotherfile1-2,v
+Working file: mod1-2/anotherfile1-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+Working file: mod1-2/file1-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod2-2
+${SPROG} log: Logging mod2-2/mod1-2
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/anotherfile1-2,v
+Working file: mod2-2/mod1-2/anotherfile1-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT1_DIRNAME}/mod1-2/file1-2,v
+Working file: mod2-2/mod1-2/file1-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+is
+=============================================================================
+${SPROG} log: Logging mod1-2
+${SPROG} log: Logging mod1-2/mod2-2
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/anotherfile2-2,v
+Working file: mod1-2/mod2-2/anotherfile2-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+Working file: mod1-2/mod2-2/file2-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+=============================================================================
+${SPROG} log: Logging mod2-1
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/anotherfile2-1,v
+Working file: mod2-1/anotherfile2-1
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-1/file2-1,v
+Working file: mod2-1/file2-1
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+=============================================================================
+${SPROG} log: Logging mod2-2
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/anotherfile2-2,v
+Working file: mod2-2/anotherfile2-2
+head: 1\.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+reading
+=============================================================================
+
+RCS file: ${CVSROOT2_DIRNAME}/mod2-2/file2-2,v
+Working file: mod2-2/file2-2
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ cattle: 1\.2
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}1 -0;
commitid: ${commitid};
+actually
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+anyone
+============================================================================="
+
+
+ # After the simple cases, let's execute some commands which
+ # refer to parts of our checked-out tree (e.g. "cvs update
+ # mod1-1 mod2-2")
+
+ dokeep
+
+ # clean up after ourselves
+ cd ..
+ rm -r 1
+
+ # clean up our repositories
+ rm -rf ${CVSROOT1_DIRNAME} ${CVSROOT2_DIRNAME}
+ ;;
+
+
+
+ multiroot2)
+ # More multiroot tests. In particular, nested directories.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT1_DIRNAME=${TESTDIR}/root1
+ CVSROOT2_DIRNAME=${TESTDIR}/root2
+ CVSROOT1=`newroot $CVSROOT1_DIRNAME`
+ CVSROOT2=`newroot $CVSROOT2_DIRNAME`
+
+ dotest multiroot2-1 "${testcvs} -d ${CVSROOT1} init" ""
+ dotest multiroot2-2 "${testcvs} -d ${CVSROOT2} init" ""
+
+ mkdir imp-dir; cd imp-dir
+ echo file1 >file1
+ mkdir sdir
+ echo sfile >sdir/sfile
+ mkdir sdir/ssdir
+ echo ssfile >sdir/ssdir/ssfile
+ dotest_sort multiroot2-3 \
+"${testcvs} -d ${CVSROOT1} import -m import-to-root1 dir1 vend rel" "
+
+N dir1/file1
+N dir1/sdir/sfile
+N dir1/sdir/ssdir/ssfile
+No conflicts created by this import
+${SPROG} import: Importing ${TESTDIR}/root1/dir1/sdir
+${SPROG} import: Importing ${TESTDIR}/root1/dir1/sdir/ssdir"
+ cd sdir
+ dotest_sort multiroot2-4 \
+"${testcvs} -d ${CVSROOT2} import -m import-to-root2 sdir vend2 rel2" "
+
+N sdir/sfile
+N sdir/ssdir/ssfile
+No conflicts created by this import
+${SPROG} import: Importing ${TESTDIR}/root2/sdir/ssdir"
+ cd ../..
+
+ mkdir 1; cd 1
+ # Get TopLevelAdmin-like behavior.
+ dotest multiroot2-5 "${testcvs} -d ${CVSROOT1} -q co -l ."
+ dotest multiroot2-5 "${testcvs} -d ${CVSROOT1} -q co dir1" \
+"U dir1/file1
+U dir1/sdir/sfile
+U dir1/sdir/ssdir/ssfile"
+ cd dir1
+ dotest multiroot2-6 "${testcvs} -Q release -d sdir" ""
+ dotest multiroot2-7 "${testcvs} -d ${CVSROOT2} -q co sdir" \
+"U sdir/sfile
+U sdir/ssdir/ssfile"
+ cd ..
+ # This has one subtle effect - it deals with Entries.Log
+ # so that the next test doesn't get trace messages for
+ # Entries.Log
+ dotest multiroot2-8 "${testcvs} update" \
+"${CPROG} update: Updating \.
+${CPROG} update: Updating dir1
+${CPROG} update: Updating dir1/sdir
+${CPROG} update: Updating dir1/sdir/ssdir" \
+"${SPROG} update: Updating \.
+${SPROG} update: Updating dir1
+${SPROG} update: Updating dir1
+${SPROG} update: Updating dir1/sdir
+${SPROG} update: Updating dir1/sdir/ssdir"
+ # Two reasons we don't run this on the server: (1) the server
+ # also prints some trace messages, and (2) the server trace
+ # messages are subject to out-of-order bugs (this one is hard
+ # to work around).
+ if $remote; then :; else
+ dotest multiroot2-9a "${testcvs} -t update" \
+" *-> main: Session ID is ${commitid}
+ *-> main loop with CVSROOT=${TESTDIR}/root1
+ *-> parse_config ($TESTDIR/root1)
+ *-> do_update ((null), (null), (null), 1, 0, 0, 0, 0, 0, 3, (null), (null),
(null), (null), (null), 1, (null))
+ *-> Write_Template (\., ${TESTDIR}/root1)
+${CPROG} update: Updating \.
+ *-> Reader_Lock(${TESTDIR}/root1)
+ *-> Simple_Lock_Cleanup()
+ *-> Write_Template (dir1, ${TESTDIR}/root1/dir1)
+${CPROG} update: Updating dir1
+ *-> Reader_Lock(${TESTDIR}/root1/dir1)
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${TESTDIR}/root2
+ *-> parse_config ($TESTDIR/root2)
+ *-> do_update ((null), (null), (null), 1, 0, 0, 0, 0, 0, 3, (null), (null),
(null), (null), (null), 1, (null))
+ *-> Write_Template (dir1/sdir, ${TESTDIR}/root2/dir1/sdir)
+${CPROG} update: Updating dir1/sdir
+ *-> Reader_Lock(${TESTDIR}/root2/sdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Write_Template (dir1/sdir/ssdir, ${TESTDIR}/root2/sdir/ssdir)
+${CPROG} update: Updating dir1/sdir/ssdir
+ *-> Reader_Lock(${TESTDIR}/root2/sdir/ssdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()"
+ fi
+
+ dotest multiroot2-9 "${testcvs} -q tag tag1" \
+"T dir1/file1
+T dir1/sdir/sfile
+T dir1/sdir/ssdir/ssfile"
+ echo "change it" >>dir1/file1
+ echo "change him too" >>dir1/sdir/sfile
+ dotest multiroot2-10 "${testcvs} -q ci -m modify" \
+"$TESTDIR/root1/dir1/file1,v <-- dir1/file1
+new revision: 1\.2; previous revision: 1\.1
+$TESTDIR/root2/sdir/sfile,v <-- dir1/sdir/sfile
+new revision: 1\.2; previous revision: 1\.1"
+ dotest multiroot2-11 "${testcvs} -q tag tag2" \
+"T dir1/file1
+T dir1/sdir/sfile
+T dir1/sdir/ssdir/ssfile"
+ dotest_fail multiroot2-12 \
+"${testcvs} -q diff -u -r tag1 -r tag2" \
+"Index: dir1/file1
+===================================================================
+RCS file: ${TESTDIR}/root1/dir1/file1,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.2
+diff -u -r1\.1\.1\.1 -r1\.2
+--- dir1/file1 ${RFCDATE} 1\.1\.1\.1
+${PLUS}${PLUS}${PLUS} dir1/file1 ${RFCDATE} 1\.2
+@@ -1 ${PLUS}1,2 @@
+ file1
+${PLUS}change it
+Index: dir1/sdir/sfile
+===================================================================
+RCS file: ${TESTDIR}/root2/sdir/sfile,v
+retrieving revision 1\.1\.1\.1
+retrieving revision 1\.2
+diff -u -r1\.1\.1\.1 -r1\.2
+--- dir1/sdir/sfile ${RFCDATE} 1\.1\.1\.1
+${PLUS}${PLUS}${PLUS} dir1/sdir/sfile ${RFCDATE} 1\.2
+@@ -1 ${PLUS}1,2 @@
+ sfile
+${PLUS}change him too"
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ # clean up after ourselves
+ cd ..
+ rm -r imp-dir 1
+
+ # clean up our repositories
+ rm -rf root1 root2
+ ;;
+
+
+
+ multiroot3)
+ # More multiroot tests. Directories are side-by-side, not nested.
+ # Not drastically different from multiroot but it covers somewhat
+ # different stuff.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT1=`newroot ${TESTDIR}/root1`
+ CVSROOT2=`newroot ${TESTDIR}/root2`
+
+ mkdir 1; cd 1
+ dotest multiroot3-1 "${testcvs} -d ${CVSROOT1} init" ""
+ dotest multiroot3-2 "${testcvs} -d ${CVSROOT1} -q co -l ." ""
+ mkdir dir1
+ dotest multiroot3-3 "${testcvs} add dir1" \
+"Directory ${TESTDIR}/root1/dir1 added to the repository"
+ dotest multiroot3-4 "${testcvs} -d ${CVSROOT2} init" ""
+ rm -r CVS
+ dotest multiroot3-5 "${testcvs} -d ${CVSROOT2} -q co -l ." ""
+ mkdir dir2
+
+ # OK, the problem is that CVS/Entries doesn't look quite right,
+ # I suppose because of the "rm -r". Then again, why *should* it
+ # look right? CVS/Root can only point to a single location, but
+ # we expect CVS/Entries to hold entries for two repositories? It
+ # just plain isn't part of the filespec yet.
+ #
+ # Use the quick and dirty fix.
+ echo "D/dir1////" >CVS/Entries
+ echo "D/dir2////" >>CVS/Entries
+
+ dotest multiroot3-7 "${testcvs} add dir2" \
+"Directory ${TESTDIR}/root2/dir2 added to the repository"
+
+ touch dir1/file1 dir2/file2
+ if $remote; then
+ # Trying to add them both in one command doesn't work,
+ # because add.c doesn't do multiroot (it doesn't use recurse.c).
+ # Furthermore, it can't deal with the parent directory
+ # having a different root from the child, hence the cd.
+ cd dir1
+ dotest multiroot3-8 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ cd ..
+ dotest multiroot3-8a "${testcvs} add dir2/file2" \
+"${SPROG} add: scheduling file .dir2/file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ else
+ dotest multiroot3-8 "${testcvs} add dir1/file1 dir2/file2" \
+"${SPROG} add: scheduling file .dir1/file1. for addition
+${SPROG} add: scheduling file .dir2/file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ fi
+
+ dotest multiroot3-9 "${testcvs} -q ci -m add-them" \
+"$TESTDIR/root2/dir2/file2,v <-- dir2/file2
+initial revision: 1\.1
+$TESTDIR/root1/dir1/file1,v <-- dir1/file1
+initial revision: 1\.1"
+
+ # That this is an error is good - we are asking CVS to do
+ # something which doesn't make sense.
+ dotest_fail multiroot3-10 \
+"${testcvs} -q -d ${CVSROOT1} diff dir1/file1 dir2/file2" \
+"${SPROG} diff: failed to create lock directory for .${TESTDIR}/root1/dir2'
(${TESTDIR}/root1/dir2/#cvs.lock): No such file or directory
+${SPROG} diff: failed to obtain dir lock in repository .${TESTDIR}/root1/dir2'
+${SPROG} \[diff aborted\]: read lock failed - giving up"
+
+ # This one is supposed to work.
+ dotest multiroot3-11 "${testcvs} -q diff dir1/file1 dir2/file2" ""
+
+ # make sure we can't access across repositories
+ # FIXCVS: we probably shouldn't even create the local directories
+ # in this case, but we do, so deal with it.
+ mkdir 1a
+ cd 1a
+ dotest_fail multiroot3-12 \
+"$testcvs -d $CVSROOT1 -q co ../root2/dir2" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root2/dir2'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root2/dir2'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ dotest_fail multiroot3-13 \
+"$testcvs -d $CVSROOT2 -q co ../root1/dir1" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root1/dir1'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root1/dir1'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ dotest_fail multiroot3-14 \
+"$testcvs -d $CVSROOT1 -q co ./../root2/dir2" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root2/dir2'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root2/dir2'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ dotest_fail multiroot3-15 \
+"$testcvs -d $CVSROOT2 -q co ./../root1/dir1" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root1/dir1'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root1/dir1'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ dotest_fail multiroot3-16 \
+"$testcvs -d $CVSROOT1 -q co -p ../root2/dir2" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root2/dir2'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\.\./root2/dir2'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+ dotest_fail multiroot3-17 \
+"$testcvs -d $CVSROOT1 -q co -p ./../root1/dir1" \
+"$CPROG \[checkout aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root1/dir1'\." \
+"$SPROG \[server aborted\]: up-level in module reference (\`..') invalid:
\`\./\.\./root1/dir1'\.
+$CPROG \[checkout aborted\]: end of file from server (consult above messages
if any)"
+
+ cd ../..
+
+ if $keep; then
+ echo Keeping ${TESTDIR} and exiting due to --keep
+ exit 0
+ fi
+
+ rm -r 1
+ rm -rf ${TESTDIR}/root1 ${TESTDIR}/root2
+ unset CVSROOT1
+ unset CVSROOT2
+ ;;
+
+
+
+ multiroot4)
+ # More multiroot tests, in particular we have two roots with
+ # similarly-named directories and we try to see that CVS can
+ # keep them separate.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT1=`newroot ${TESTDIR}/root1`
+ CVSROOT2=`newroot ${TESTDIR}/root2`
+
+ mkdir 1; cd 1
+ dotest multiroot4-1 "${testcvs} -d ${CVSROOT1} init" ""
+ dotest multiroot4-2 "${testcvs} -d ${CVSROOT1} -q co -l ." ""
+ mkdir dircom
+ dotest multiroot4-3 "${testcvs} add dircom" \
+"Directory ${TESTDIR}/root1/dircom added to the repository"
+ cd dircom
+ touch file1
+ dotest multiroot4-4 "${testcvs} add file1" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest multiroot4-5 "${testcvs} -q ci -m add" \
+"$TESTDIR/root1/dircom/file1,v <-- file1
+initial revision: 1\.1"
+ cd ../..
+ mkdir 2; cd 2
+ dotest multiroot4-6 "${testcvs} -d ${CVSROOT2} init" ""
+ dotest multiroot4-7 "${testcvs} -d ${CVSROOT2} -q co -l ." ""
+ mkdir dircom
+ dotest multiroot4-8 "${testcvs} add dircom" \
+"Directory ${TESTDIR}/root2/dircom added to the repository"
+ cd dircom
+ touch file2
+ dotest multiroot4-9 "${testcvs} add file2" \
+"${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add this file permanently"
+ dotest multiroot4-10 "${testcvs} -q ci -m add" \
+"$TESTDIR/root2/dircom/file2,v <-- file2
+initial revision: 1\.1"
+
+ cd ../..
+ cd 1/dircom
+ # This may look contrived; the real world example which inspired
+ # it was that a user was changing from local to remote. Cases
+ # like switching servers (among those mounting the same
+ # repository) and so on would also look the same.
+ mkdir sdir2
+ dotest multiroot4-11 "${testcvs} -d ${CVSROOT2} add sdir2" \
+"Directory ${TESTDIR}/root2/dircom/sdir2 added to the repository"
+
+ dotest multiroot4-12 "${testcvs} -q update" ""
+ cd ..
+ dotest multiroot4-13 "${testcvs} -q update dircom" ""
+ cd ..
+
+ rm -r 1 2
+ rm -rf ${TESTDIR}/root1 ${TESTDIR}/root2
+ unset CVSROOT1
+ unset CVSROOT2
+ ;;
+
+
+
+ rmroot)
+ # When the Entries/Root file is removed from an existing
+ # workspace, CVS should assume $CVSROOT instead
+ #
+ # Right now only checking that CVS exits normally on an
+ # update once CVS/Root is deleted
+ #
+ # There was a time when this would core dump when run in
+ # client/server mode
+
+ mkdir 1; cd 1
+ dotest rmroot-setup-1 "${testcvs} -q co -l ." ''
+ mkdir first-dir
+ dotest rmroot-setup-2 "${testcvs} add first-dir" \
+"Directory ${CVSROOT_DIRNAME}/first-dir added to the repository"
+ cd first-dir
+ touch file1 file2
+ dotest rmroot-setup-3 "${testcvs} add file1 file2" \
+"${SPROG} add: scheduling file .file1. for addition
+${SPROG} add: scheduling file .file2. for addition
+${SPROG} add: use .${SPROG} commit. to add these files permanently"
+ dotest rmroot-setup-4 "${testcvs} -q commit -minit" \
+"$CVSROOT_DIRNAME/first-dir/file1,v <-- file1
+initial revision: 1\.1
+$CVSROOT_DIRNAME/first-dir/file2,v <-- file2
+initial revision: 1\.1"
+ rm CVS/Root
+ dotest rmroot-1 "${testcvs} -q update" ''
+
+ dokeep
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/first-dir
+ ;;
+
+
+
+ reposmv)
+ # More tests of repositories and specifying them.
+ # Similar to crerepos but that test is probably getting big
+ # enough.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT1=`newroot ${TESTDIR}/root1`
+ CVSROOT_MOVED=`newroot ${TESTDIR}/root-moved`
+
+ dotest reposmv-setup-1 "${testcvs} -d ${CVSROOT1} init" ""
+ mkdir imp-dir; cd imp-dir
+ echo file1 >file1
+ dotest reposmv-setup-2 \
+"${testcvs} -d ${CVSROOT1} import -m add dir1 vendor release" \
+"N dir1/file1
+
+No conflicts created by this import"
+ cd ..
+
+ mkdir 1; cd 1
+ dotest reposmv-1 "${testcvs} -d ${CVSROOT1} -Q co dir1" ""
+ mv ${TESTDIR}/root1 ${TESTDIR}/root-moved
+ cd dir1
+
+ # If we didn't have a relative repository, get one now.
+ dotest reposmv-1a "cat CVS/Repository" \
+"${TESTDIR}/root1/dir1" "dir1"
+ echo dir1 >CVS/Repository
+
+ # There were some duplicated warnings and such; only test
+ # for the part of the error message which makes sense.
+ #
+ # FIXCVS then FIXME
+ # Now the duplicated error messages only occur on some platforms,
+ # including, apparently, NetBSD 1.6.1, RedHat Linux 7.3, whatever
+ # kernel that is using, and Solaris 9. These platforms somehow
+ # decide to call Name_Root() up to four times, via do_recursion, but
+ # I'm not sure of the rest of the details. Other platforms,
+ # including Fedora Core 1 (Linux 2.4.22-1.2199.nptl), RH Linux 9
+ # (Linux 2.4.20-37.9.legacy), and probably AIX 3.4, Solaris 8,
+ # BSD/OS 4.2, & IRIX 6.5 only call Name_Root() once as a result of
+ # this test.
+ #
+ # Bug: "skipping directory " without filename.
+ if $remote; then
+ dotest_fail reposmv-2r "${testcvs} update" \
+"Cannot access ${TESTDIR}/root1/CVSROOT
+No such file or directory"
+ else
+ dotest reposmv-2 "$testcvs update" \
+"$DOTSTAR$CPROG update: in directory \.:
+$CPROG update: ignoring CVS/Root because it specifies a non-existent
repository $TESTDIR/root1
+$CPROG update: Updating \.
+$DOTSTAR$CPROG update: cannot open directory $CVSROOT_DIRNAME/dir1: No such
file or directory
+$CPROG update: skipping directory "
+ fi
+
+ # CVS/Root overrides $CVSROOT
+ if $remote; then
+ CVSROOT_save=${CVSROOT}
+ CVSROOT=:fork:${TESTDIR}/root-moved; export CVSROOT
+ dotest_fail reposmv-3r "${testcvs} update" \
+"Cannot access ${TESTDIR}/root1/CVSROOT
+No such file or directory"
+ CVSROOT=${CVSROOT_save}; export CVSROOT
+ else
+ CVSROOT_save=$CVSROOT
+ CVSROOT=$TESTDIR/root-moved; export CVSROOT
+ dotest reposmv-3 "$testcvs update" \
+"$DOTSTAR$CPROG update: in directory \.:
+$CPROG update: ignoring CVS/Root because it specifies a non-existent
repository $TESTDIR/root1
+$CPROG update: Updating \.$DOTSTAR"
+ CVSROOT=$CVSROOT_save; export CVSROOT
+ fi
+
+ if $remote; then
+ CVSROOT_save=${CVSROOT}
+ CVSROOT=:fork:${TESTDIR}/root-none; export CVSROOT
+ dotest_fail reposmv-4r "${testcvs} update" \
+"Cannot access ${TESTDIR}/root1/CVSROOT
+No such file or directory"
+ CVSROOT=${CVSROOT_save}; export CVSROOT
+ else
+ # CVS/Root doesn't seem to quite completely override $CVSROOT
+ # Bug? Not necessarily a big deal if it only affects error
+ # messages.
+ CVSROOT_save=${CVSROOT}
+ CVSROOT=${TESTDIR}/root-none; export CVSROOT
+ dotest_fail reposmv-4 "${testcvs} update" \
+"${CPROG} update: in directory \.:
+${CPROG} update: ignoring CVS/Root because it specifies a non-existent
repository ${TESTDIR}/root1
+${CPROG} \[update aborted\]: ${TESTDIR}/root-none/CVSROOT: No such file or
directory"
+ CVSROOT=${CVSROOT_save}; export CVSROOT
+ fi
+
+ # -d overrides CVS/Root
+ #
+ # Oddly enough, with CVS 1.10 I think this didn't work for
+ # local (that is, it would appear that CVS/Root would not
+ # get used, but would produce an error if it didn't exist).
+ dotest reposmv-5 "${testcvs} -d ${CVSROOT_MOVED} update" \
+"${SPROG} update: Updating \."
+
+ # TODO: could also test various other things, like what if the
+ # user removes CVS/Root (which is legit). Or another set of
+ # tests would be if both repositories exist but we want to make
+ # sure that CVS is using the correct one.
+
+ cd ../..
+ rm -r imp-dir 1
+ rm -rf root1 root2
+ unset CVSROOT1
+ ;;
+
+
+
+ pserver)
+ # Test basic pserver functionality.
+ if $remote; then
+ if test -n "$remotehost"; then
+ # Don't even try. (The issue is getting servercvs & testcvs
+ # set correctly for the following tests. Some expect one access
+ # method and some another, which in $remotehost mode, means that
+ # sometimes the executables must run on one platform and
+ # sometimes another.)
+ continue
+ fi
+ save_servercvs=$servercvs
+ servercvs=$testcvs
+ # First set SystemAuth=no. Not really necessary, I don't
+ # think, but somehow it seems like the clean thing for
+ # the testsuite.
+ mkdir 1; cd 1
+ dotest pserver-1 "$testcvs -Q co CVSROOT" ""
+ cd CVSROOT
+ echo "SystemAuth=no" >>config
+ dotest pserver-2 "$testcvs -q ci -m config-it" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+ cat >$CVSROOT_DIRNAME/CVSROOT/passwd <<EOF
+testme:q6WV9d2t848B2:$username
+dontroot:q6WV9d2t848B2:root
+anonymous::$username
+$username:
+willfail: :whocares
+EOF
+ dotest_fail pserver-3 "$servercvs pserver" \
+"error 0 Server configuration missing --allow-root in inetd.conf" <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+testme
+Ay::'d
+END AUTH REQUEST
+EOF
+
+ # Confirm that not sending a newline during auth cannot constitute
+ # a denial-of-service attack. This assumes that PATH_MAX is less
+ # than 65536 bytes. If PATH_MAX is larger than 65535 bytes, this
+ # test could hang indefinitely.
+ ${AWK} 'BEGIN { printf "0123456789abcdef" }' </dev/null >garbageseg
+ echo "BEGIN AUTH REQUEST" >garbageinput
+ i=0
+ while test $i -lt 64; do
+ cat <garbageseg >>garbageseg2
+ i=`expr $i + 1`
+ done
+ i=0
+ while test $i -lt 64; do
+ cat <garbageseg2 >>garbageinput
+ i=`expr $i + 1`
+ done
+ dotest_fail pserver-auth-no-dos \
+"${servercvs} --allow-root=${CVSROOT_DIRNAME} pserver" \
+"$CPROG \\[pserver aborted\\]: error reading from net while validating
pserver: Not enough space" \
+"$CPROG \\[pserver aborted\\]: error reading from net while validating
pserver: Cannot allocate memory" <garbageinput
+ unset i
+ rm garbageseg garbageseg2 garbageinput
+
+ # Sending the Root and noop before waiting for the
+ # "I LOVE YOU" is bogus, but hopefully we can get
+ # away with it.
+ dotest pserver-4 "$servercvs --allow-root=$CVSROOT_DIRNAME pserver"
\
+"$DOTSTAR LOVE YOU
+ok" <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+testme
+Ay::'d
+END AUTH REQUEST
+Root $CVSROOT_DIRNAME
+noop
+EOF
+
+ # The "no such system user" error is occurring on at least one of
+ # our BSD 2.0.2 nightly test platforms.
+ dotest_fail pserver-4.2 \
+"$servercvs --allow-root=$CVSROOT_DIRNAME pserver" \
+"error 0: root not allowed" \
+"E Fatal error, aborting\.
+error 0 root: no such system user" <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+dontroot
+Ay::'d
+END AUTH REQUEST
+EOF
+
+ dotest pserver-5 "$servercvs --allow-root=$CVSROOT_DIRNAME pserver"
\
+"$DOTSTAR LOVE YOU
+E Protocol error: Root says \"$TESTDIR/1\" but pserver says
\"$CVSROOT_DIRNAME\"
+error " <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+testme
+Ay::'d
+END AUTH REQUEST
+Root $TESTDIR/1
+noop
+EOF
+
+ dotest pserver-5a "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E Protocol error: init says \"${TESTDIR}/2\" but pserver says
\"${CVSROOT_DIRNAME}\"
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+init ${TESTDIR}/2
+EOF
+ dotest_fail pserver-5b "test -d ${TESTDIR}/2" ''
+
+ dotest pserver-5c "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E init xxx must be an absolute pathname
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+init xxx
+EOF
+ dotest_fail pserver-5d "test -d xxx" ''
+
+ dotest_fail pserver-6 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"I HATE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d^b?hd
+END AUTH REQUEST
+EOF
+
+ dotest_fail pserver-7 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"I HATE YOU" <<EOF
+BEGIN VERIFICATION REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d^b?hd
+END VERIFICATION REQUEST
+EOF
+
+ dotest pserver-8 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU" <<EOF
+BEGIN VERIFICATION REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END VERIFICATION REQUEST
+EOF
+
+# Tests pserver-9 through pserver-13 are about empty passwords
+
+ # Test empty password (both sides) for aliased user
+ dotest pserver-9 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+A
+END AUTH REQUEST
+EOF
+
+ # Test empty password (server side only) for aliased user
+ dotest pserver-10 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+Aanythingwouldworkhereittrulydoesnotmatter
+END AUTH REQUEST
+EOF
+
+ # Test empty (both sides) password for non-aliased user
+ dotest pserver-11 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+A
+END AUTH REQUEST
+EOF
+
+ # Test empty (server side only) password for non-aliased user
+ dotest pserver-12 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Anypasswordwouldworkwhynotthisonethen
+END AUTH REQUEST
+EOF
+
+ # Test failure of whitespace password
+ dotest_fail pserver-13 "${servercvs}
--allow-root=${CVSROOT_DIRNAME} pserver" \
+"${DOTSTAR} HATE YOU" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+willfail
+Amquiteunabletocomeupwithinterestingpasswordsanymore
+END AUTH REQUEST
+EOF
+
+ # The following tests are for read-only access
+
+ # Check that readers can only read, everyone else can write
+
+ echo anonymous >$CVSROOT_DIRNAME/CVSROOT/readers
+
+ dotest pserver-14 "$servercvs --allow-root=$CVSROOT_DIRNAME
pserver" \
+"$DOTSTAR LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+anonymous
+Ay::'d
+END AUTH REQUEST
+Root $CVSROOT_DIRNAME
+version
+EOF
+
+ dotest pserver-15 "$servercvs --allow-root=$CVSROOT_DIRNAME
pserver" \
+"$DOTSTAR LOVE YOU
+E $CPROG \\[server aborted\\]: .init. requires write access to the repository
+error " <<EOF
+BEGIN AUTH REQUEST
+$CVSROOT_DIRNAME
+anonymous
+Ay::'d
+END AUTH REQUEST
+init $CVSROOT_DIRNAME
+EOF
+
+ dotest pserver-16 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-17 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ dotest pserver-18 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-19 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Anything
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ # Check that writers can write, everyone else can only read
+ # even if not listed in readers
+
+ cat >${CVSROOT_DIRNAME}/CVSROOT/writers <<EOF
+testme
+EOF
+
+ dotest pserver-20 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-21 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E $CPROG \\[server aborted\\]: .init. requires write access to the repository
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+Ay::'d
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ dotest pserver-22 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-23 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ dotest pserver-24 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-25 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E $CPROG \\[server aborted\\]: .init. requires write access to the repository
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Anything
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ # Should work the same without readers
+
+ rm ${CVSROOT_DIRNAME}/CVSROOT/readers
+
+ dotest pserver-26 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-27 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E $CPROG \\[server aborted\\]: .init. requires write access to the repository
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+anonymous
+Ay::'d
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ dotest pserver-28 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-29 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+testme
+Ay::'d
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ dotest pserver-30 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+M Concurrent Versions System (CVS) .*
+ok" <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Ay::'d
+END AUTH REQUEST
+Root ${CVSROOT_DIRNAME}
+version
+EOF
+
+ dotest pserver-31 "${servercvs} --allow-root=${CVSROOT_DIRNAME}
pserver" \
+"${DOTSTAR} LOVE YOU
+E $CPROG \\[server aborted\\]: .init. requires write access to the repository
+error " <<EOF
+BEGIN AUTH REQUEST
+${CVSROOT_DIRNAME}
+${username}
+Anything
+END AUTH REQUEST
+init ${CVSROOT_DIRNAME}
+EOF
+
+ # pserver used to try and print from the NULL pointer
+ # in this error message in this case
+ dotest_fail pserver-bufinit "${servercvs} pserver" \
+"$CPROG \[pserver aborted\]: unexpected EOF encountered during authentication"
</dev/null
+
+ # Clean up.
+ dotest pserver-cleanup-1 "${testcvs} -q up -pr1.1 config >config" ""
+ dotest pserver-cleanup-2 "${testcvs} -q ci -m config-it" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v <-- config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ restore_adm
+ servercvs=$save_servercvs
+ fi # skip the whole thing for local
+ ;;
+
+
+
+ server)
+ # Some tests of the server (independent of the client).
+ if $remote; then
+ save_servercvs=$servercvs
+ servercvs=$testcvs
+ dotest server-1 "${servercvs} server" \
+"E Protocol error: Root request missing
+error " <<EOF
+Directory bogus
+mumble/bar
+update
+EOF
+
+ # Could also test for relative pathnames here (so that crerepos-6a
+ # and crerepos-6b can use :fork:).
+ dotest server-2 "${servercvs} server" "ok" <<EOF
+Set OTHER=variable
+Set MYENV=env-value
+init ${TESTDIR}/crerepos
+EOF
+ dotest server-3 "test -d ${TESTDIR}/crerepos/CVSROOT" ""
+
+ # Now some tests of gzip-file-contents (used by jCVS).
+ ${AWK} 'BEGIN { \
+printf "%c%c%c%c%c%c.6%c%c+I-.%c%c%c%c5%c;%c%c%c%c", \
+31, 139, 8, 64, 5, 7, 64, 3, 225, 2, 64, 198, 185, 5, 64, 64, 64}' \
+ </dev/null | ${TR} '\100' '\000' >gzipped.dat
+ # Note that the CVS client sends "-b 1.1.1", and this
+ # test doesn't. But the server also defaults to that.
+ cat <<EOF >session.dat
+Root ${TESTDIR}/crerepos
+UseUnchanged
+gzip-file-contents 3
+Argument -m
+Argument msg
+Argumentx
+Argument dir1
+Argument tag1
+Argument tag2
+Directory .
+${TESTDIR}/crerepos
+Modified file1
+u=rw,g=r,o=r
+z25
+EOF
+ cat gzipped.dat >>session.dat
+ echo import >>session.dat
+ dotest server-4 "${servercvs} server" \
+"M N dir1/file1
+M
+M No conflicts created by this import
+M
+ok" <session.dat
+ dotest server-5 \
+"${testcvs} -q -d ${TESTDIR}/crerepos co -p dir1/file1" "test"
+
+ # OK, here are some notify tests.
+ dotest server-6 "${servercvs} server" \
+"Notified \./
+${TESTDIR}/crerepos/dir1/file1
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Notify file1
+E Fri May 7 13:21:09 1999 -0000 myhost some-work-dir EUC
+noop
+EOF
+ # Sending the second "noop" before waiting for the output
+ # from the first is bogus but hopefully we can get away
+ # with it.
+ dotest server-7 "${servercvs} server" \
+"M file1 $username Fri May 7 13:21:09 1999 -0000 myhost
some-work-dir
+Notified \./
+${TESTDIR}/crerepos/dir1/file1
+ok
+M file1 $username Fri May 7 13:21:09 1999 -0000 myhost
some-work-dir
+Notified \./
+${TESTDIR}/crerepos/dir1/file1
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Notify file1
+E Fri May 7 13:21:09 1999 -0000 myhost some-work-dir EUC
+noop
+Notify file1
+E The 57th day of Discord in the YOLD 3165 myhost some-work-dir
EUC
+noop
+EOF
+
+ # OK, now test a few error conditions.
+ # FIXCVS: should give "error" and no "Notified", like server-9
+ dotest server-8 "${servercvs} server" \
+"M file1 $username The 57th day of Discord in the YOLD 3165
myhost some-work-dir
+E $CPROG server: invalid character in editor value
+Notified \./
+${TESTDIR}/crerepos/dir1/file1
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Notify file1
+E Setting Orange, the 52th day of Discord in the YOLD 3165 myhost
some-work-dir EUC
+noop
+EOF
+
+ dotest server-9 "${servercvs} server" \
+"E Protocol error; misformed Notify request
+error " <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Notify file1
+E Setting Orange+57th day of Discord myhost some-work-dir EUC
+noop
+EOF
+
+ # First demonstrate an interesting quirk in the protocol.
+ # The "watchers" request selects the files to operate based
+ # on files which exist in the working directory. So if we
+ # don't send "Entry" or the like, it won't do anything.
+ # Wants to be documented in cvsclient.texi...
+ dotest server-10 "${servercvs} server" "ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+watchers
+EOF
+ # See if "watchers" and "editors" display the right thing.
+ dotest server-11 "${servercvs} server" \
+"M file1 ${username} tedit tunedit tcommit
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Entry /file1/1.1////
+watchers
+EOF
+ dotest server-12 "${servercvs} server" \
+"M file1 ${username} The 57th day of Discord in the YOLD 3165
myhost some-work-dir
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Entry /file1/1.1////
+editors
+EOF
+
+ # Now do an unedit.
+ dotest server-13 "${servercvs} server" \
+"Notified \./
+${TESTDIR}/crerepos/dir1/file1
+ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+Notify file1
+U 7 May 1999 15:00 -0000 myhost some-work-dir EUC
+noop
+EOF
+
+ # Now try "watchers" and "editors" again.
+ dotest server-14 "${servercvs} server" "ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+watchers
+EOF
+ dotest server-15 "${servercvs} server" "ok" <<EOF
+Root ${TESTDIR}/crerepos
+Directory .
+${TESTDIR}/crerepos/dir1
+editors
+EOF
+
+ # Test that the global `-l' option is ignored nonfatally.
+ dotest server-16 "${testcvs} server" \
+"E $CPROG server: WARNING: global \`-l' option ignored\.
+ok" <<EOF
+Global_option -l
+noop
+EOF
+
+ # There used to be some exploits based on malformed Entry requests
+ dotest server-17 "$testcvs server" \
+"E protocol error: Malformed Entry
+error " <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos/dir1
+Entry X/file1/1.1////
+noop
+EOF
+
+ dotest server-18 "$testcvs server" \
+"E protocol error: Malformed Entry
+error " <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos/dir1
+Entry /CC/CC/CC
+noop
+EOF
+
+ # Check that the config file may be set from the command line.
+ # But first verify the default config produces no error messages.
+ dotest server-19 "$testcvs server" \
+"ok" <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos
+noop
+EOF
+ echo THIS-CONFIG-OPTION-IS-BAD=XXX >$TESTDIR/newconfig
+ dotest_fail server-20 "$testcvs server -c $TESTDIR/newconfig" \
+"E $SPROG \[server aborted\]: Invalid path to config file specified:
\`$TESTDIR/newconfig'" <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos
+noop
+EOF
+ dotest_fail server-21 \
+"$testcvs server -c /etc/cvs/this-shouldnt-exist" \
+"E $SPROG \[server aborted\]: Failed to resolve path:
\`/etc/cvs/this-shouldnt-exist': No such file or directory" <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos
+noop
+EOF
+
+ # Now make sure that the config file can't be set via the user's
+ # .cvsrc.
+ echo server -c $TESTDIR/newconfig >$HOME/.cvsrc
+ dotest server-22 "$testcvs server" \
+"ok" <<EOF
+Root $TESTDIR/crerepos
+Directory .
+$TESTDIR/crerepos
+noop
+EOF
+
+ dokeep
+ rm -rf $TESTDIR/crerepos
+ rm gzipped.dat session.dat
+ rm $TESTDIR/newconfig $HOME/.cvsrc
+ servercvs=$save_servercvs
+ fi # skip the whole thing for local
+ ;;
+
+
+
+ server2)
+ # More server tests, in particular testing that various
+ # possible security holes are plugged.
+ if $remote; then
+ if test -n "$remotehost"; then
+ # Don't even try. (The issue is getting servercvs & testcvs
+ # set correctly for the following tests. Some expect one access
+ # method and some another, which in $remotehost mode, means that
+ # sometimes the executables must run on one platform and
+ # sometimes another.)
+ continue
+ fi
+ save_servercvs=$servercvs
+ servercvs=$testcvs
+ dotest server2-1 "${servercvs} server" \
+"E protocol error: directory '${CVSROOT_DIRNAME}/\.\./dir1' not within root
'${CVSROOT_DIRNAME}'
+error " <<EOF
+Root ${CVSROOT_DIRNAME}
+Directory .
+${CVSROOT_DIRNAME}/../dir1
+noop
+EOF
+
+ dotest server2-2 "${servercvs} server" \
+"E protocol error: directory '${CVSROOT_DIRNAME}dir1' not within root
'${CVSROOT_DIRNAME}'
+error " <<EOF
+Root ${CVSROOT_DIRNAME}
+Directory .
+${CVSROOT_DIRNAME}dir1
+noop
+EOF
+
+ dotest server2-3 "${servercvs} server" \
+"E protocol error: directory '${TESTDIR}' not within root '${CVSROOT_DIRNAME}'
+error " <<EOF
+Root ${CVSROOT_DIRNAME}
+Directory .
+${TESTDIR}
+noop
+EOF
+
+ # OK, now a few tests for the rule that one cannot pass a
+ # filename containing a slash to Modified, Is-modified,
+ # Notify, Questionable, or Unchanged. For completeness
+ # we'd try them all. For lazyness/conciseness we don't.
+ dotest server2-4 "${servercvs} server" \
+"E protocol error: directory 'foo/bar' not within current directory
+error " <<EOF
+Root ${CVSROOT_DIRNAME}
+Directory .
+${CVSROOT_DIRNAME}
+Unchanged foo/bar
+noop
+EOF
+
+ dotest server2-5 \
+"${servercvs} --allow-root=${CVSROOT_DIRNAME}.bad server" \
+"E Bad root ${CVSROOT_DIRNAME}
+error " <<EOF
+Root ${CVSROOT_DIRNAME}
+noop
+EOF
+ dotest server2-6 \
+"${servercvs} --allow-root=${CVSROOT_DIRNAME} server" \
+"ok" <<EOF
+Root ${CVSROOT_DIRNAME}
+noop
+EOF
+ servercvs=$save_servercvs
+ fi
+ ;;
+
+
+
+ client)
+ # Some tests of the client (independent of the server).
+ if $remote; then :; else
+ remoteonly client
+ continue
+ fi
+
+ if $proxy; then
+ # Skip these tests in proxy mode since they assume we are not
+ # writing through a proxy server. There is no writeproxy-client
+ # test currently. The writeproxy & writeproxy-noredirect tests
+ # test the writeproxy server.
+ notproxy client
+ continue
+ fi
+
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+# This is admittedly a bit cheezy, in the sense that we make lots
+# of assumptions about what the client is going to send us.
+# We don't mention Repository, because current clients don't require it.
+# Sending these at our own pace, rather than waiting for the client to
+# make the requests, is bogus, but hopefully we can get away with it.
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "M special message"
+echo "Created first-dir/"
+echo "$CVSROOT_DIRNAME/first-dir/file1"
+echo "/file1/1.1///"
+echo "u=rw,g=rw,o=rw"
+echo "4"
+echo "xyz"
+echo "ok"
+cat >/dev/null
+EOF
+ # Cygwin. Pthffffffffft!
+ if test -n "$remotehost"; then
+ $CVS_RSH $remotehost "chmod +x $TESTDIR/serveme"
+ else
+ chmod +x $TESTDIR/serveme
+ fi
+ save_CVS_SERVER=$CVS_SERVER
+ CVS_SERVER=$TESTDIR/serveme; export CVS_SERVER
+ mkdir 1; cd 1
+ dotest_fail client-1 "$testcvs -q co first-dir" \
+"$CPROG \[checkout aborted\]: This server does not support the global -q
option$DOTSTAR"
+ dotest client-2 "$testcvs co first-dir" "special message"
+
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "M merge-it"
+echo "Copy-file ./"
+echo "$CVSROOT_DIRNAME/first-dir/file1"
+echo "$TESTDIR/bogus/.#file1.1.1"
+echo "Merged ./"
+echo "$CVSROOT_DIRNAME/first-dir/file1"
+echo "/file1/1.2///"
+echo "u=rw,g=rw,o=rw"
+echo "4"
+echo "abd"
+echo "ok"
+cat >/dev/null
+EOF
+ cd first-dir
+ mkdir $TESTDIR/bogus
+ # The ${DOTSTAR} is to match a potential "broken pipe" if the
+ # client exits before the server script sends everything
+ dotest_fail client-3 "$testcvs update" \
+"merge-it
+$CPROG \[update aborted\]: protocol error: Copy-file tried to specify
director$DOTSTAR"
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "M merge-it"
+echo "Copy-file ./"
+echo "$CVSROOT_DIRNAME/first-dir/file1"
+echo ".#file1.1.1"
+echo "Merged ./"
+echo "$CVSROOT_DIRNAME/first-dir/file1"
+echo "/file1/1.2///"
+echo "u=rw,g=rw,o=rw"
+echo "4"
+echo "abc"
+echo "ok"
+cat >/dev/null
+EOF
+ dotest client-4 "$testcvs update" "merge-it"
+ dotest client-5 "cat .#file1.1.1" "xyz"
+ dotest client-6 "cat CVS/Entries" "/file1/1.2/[A-Za-z0-9 :]*//
+D"
+ dotest client-7 "cat file1" "abc"
+
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "M OK, whatever"
+echo "ok"
+cat >$TESTDIR/client.tmp
+EOF
+ chmod u=rw,go= file1
+ # By specifying the time zone in local time, we don't
+ # know exactly how that will translate to GMT.
+ dotest client-8 "$testcvs update -D 99-10-04" "OK, whatever"
+ # String 2 below is Cygwin again - ptoooey.
+ dotest client-9 "cat $TESTDIR/client.tmp" \
+"Root $CVSROOT_DIRNAME
+Valid-responses [-a-zA-Z ]*
+valid-requests
+Argument -D
+Argument [34] Oct 1999 [0-9][0-9]:00:00 -0000
+Argument --
+Directory \.
+$CVSROOT_DIRNAME/first-dir
+Entry /file1/1\.2///
+Modified file1
+u=rw,g=,o=
+4
+abc
+update" \
+"Root $CVSROOT_DIRNAME
+Valid-responses [-a-zA-Z ]*
+valid-requests
+Argument -D
+Argument [34] Oct 1999 [0-9][0-9]:00:00 -0000
+Argument --
+Directory \.
+$CVSROOT_DIRNAME/first-dir
+Entry /file1/1\.2///
+Modified file1
+u=rw,g=r,o=r
+4
+abc
+update"
+
+ # The following test tests what was a potential client exploit in
+ # CVS versions 1.11.14 and CVS versions 1.12.6 and earlier. This
+ # exploit would allow a trojan server to create arbitrary files,
+ # anywhere the user had write permissions, even outside of the
+ # user's sandbox.
+ cat >$HOME/.bashrc <<EOF
+#!$TESTSHELL
+# This is where login scripts would usually be
+# stored.
+EOF
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Rcs-diff $HOME/"
+echo "$HOME/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "20"
+echo "a1 1"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+
+ # If I don't run the following sleep between the above cat and
+ # the following calls to dotest, sometimes the serveme file isn't
+ # completely written yet by the time CVS tries to execute it,
+ # causing the shell to intermittantly report syntax errors (usually
+ # early EOF). There's probably a new race condition here, but this
+ # works.
+ #
+ # Incidentally, I can reproduce this behavior with Linux 2.4.20 and
+ # Bash 2.05 or Bash 2.05b.
+ sleep 1
+ dotest_fail client-10 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`$HOME/.bashrc'\."
+
+ # A second try at a client exploit. This one never actually
+ # failed in the past, but I thought it wouldn't hurt to add a test.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Rcs-diff ./"
+echo "$HOME/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "20"
+echo "a1 1"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-11 "$testcvs update" \
+"$CPROG \[update aborted\]: patch original file \./\.bashrc does not exist"
+
+ # A third try at a client exploit. This one did used to fail like
+ # client-10.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Rcs-diff ../../home/"
+echo "../../.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "20"
+echo "a1 1"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-12 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`\.\./\.\./home/.bashrc'\."
+
+ # Try the same exploit using the Created response.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Created $HOME/"
+echo "$HOME/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "26"
+echo "#! $TESTSHELL"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-13 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`$HOME/.bashrc'\."
+
+ # Now try using the Update-existing response
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Update-existing ../../home/"
+echo "../../home/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "26"
+echo "#! $TESTSHELL"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-14 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`\.\./\.\./home/.bashrc'\."
+
+ # Try the same exploit using the Merged response.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Merged $HOME/"
+echo "$HOME/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "26"
+echo "#! $TESTSHELL"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-15 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`$HOME/.bashrc'\."
+
+ # Now try using the Updated response
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Updated ../../home/"
+echo "../../home/.bashrc"
+echo "/.bashrc/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "26"
+echo "#! $TESTSHELL"
+echo "echo 'gotcha!'"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-16 "$testcvs update" \
+"$CPROG update: Server attempted to update a file via an invalid pathname:
+$CPROG \[update aborted\]: \`\.\./\.\./home/.bashrc'\."
+
+ # Try the same exploit using the Copy-file response.
+ # As far as I know, Copy-file was never exploitable either.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Directory Entry
Modified Unchanged Argument Argumentx ci co update"
+echo "ok"
+echo "Created ."
+echo "./innocuous"
+echo "/innocuous/73.50///"
+echo "u=rw,g=rw,o=rw"
+echo "26"
+echo "#! $TESTSHELL"
+echo "echo 'gotcha!'"
+echo "Copy-file ."
+echo "./innocuous"
+echo "$HOME/innocuous"
+echo "ok"
+cat >/dev/null
+EOF
+ sleep 1
+ dotest_fail client-18 "$testcvs update" \
+"$CPROG \[update aborted\]: protocol error: Copy-file tried to specify
directory"
+
+ # And verify that none of the exploits was successful.
+ dotest client-19 "cat $HOME/.bashrc" \
+"#!$TESTSHELL
+# This is where login scripts would usually be
+# stored\."
+
+ # Check that the client detects redirect loops.
+ cat >$TESTDIR/serveme <<EOF
+#!$TESTSHELL
+echo "Valid-requests Root Valid-responses valid-requests Command-prep Referrer
Repository Directory Relative-directory Max-dotdot Static-directory Sticky
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged Notify
Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream
wrapper-sendme-rcsOptions Set Gssapi-authenticate expand-modules ci co update
diff log rlog list rlist global-list-quiet ls add remove update-patches
gzip-file-contents status rdiff tag rtag import admin export history release
watch-on watch-off watch-add watch-remove watchers editors edit init annotate
rannotate noop version"
+echo "ok"
+echo "Redirect $CVSROOT"
+
+# Eat up data from the client to avoid broken pipe errors.
+cat >/dev/null
+EOF
+ echo newstuff >file1
+ sleep 1
+ dotest_fail client-20 "$testcvs ci" \
+"$CPROG commit: Examining \.
+$CPROG \[commit aborted\]: \`Redirect' loop detected\. Server
misconfiguration$QUESTION"
+
+ dokeep
+ cd ../..
+ rm -r 1
+ rmdir $TESTDIR/bogus
+ rm $TESTDIR/serveme $HOME/.bashrc
+ CVS_SERVER=$save_CVS_SERVER; export CVS_SERVER
+ ;;
+
+
+
+ dottedroot)
+ # Check that a CVSROOT with a "." in the name will work.
+
+ if $proxy; then
+ # don't even try
+ continue
+ fi
+
+ CVSROOT_save=${CVSROOT}
+ CVSROOT_DIRNAME_save=${CVSROOT_DIRNAME}
+ CVSROOT_DIRNAME=${TESTDIR}/cvs.root
+ CVSROOT=`newroot ${CVSROOT_DIRNAME}`
+
+ dotest dottedroot-init-1 "${testcvs} init" ""
+ mkdir dir1
+ mkdir dir1/dir2
+ echo version1 >dir1/dir2/file1
+ cd dir1
+ dotest dottedroot-1 "${testcvs} import -m '' module1 AUTHOR INITIAL" \
+"${SPROG} import: Importing ${CVSROOT_DIRNAME}/module1/dir2
+N module1/dir2/file1
+
+No conflicts created by this import"
+ cd ..
+
+ # This is the test that used to cause an assertion failure
+ # in recurse.c:do_recursion().
+ dotest dottedroot-2 "${testcvs} co -rINITIAL module1" \
+"${SPROG} checkout: Updating module1
+${SPROG} checkout: Updating module1/dir2
+U module1/dir2/file1"
+
+ dokeep
+
+ rm -rf ${CVSROOT_DIRNAME}
+ rm -r dir1 module1
+ CVSROOT_DIRNAME=${CVSROOT_DIRNAME_save}
+ CVSROOT=${CVSROOT_save}
+ ;;
+
+
+
+ fork)
+ # Test that the server defaults to the correct executable in :fork:
+ # mode. See the note in the TODO at the end of this file about this.
+ #
+ # This test and client should be left after all other references to
+ # CVS_SERVER are removed from this script.
+ #
+ # The client series of tests already tests that CVS_SERVER is
+ # working, but that test might be better here.
+ if $remote; then
+ if test -n "$remotehost"; then
+ # Don't even try. If our caller specified a remotehost, our
+ # access method has been determined anyhow.
+ continue
+ fi
+ mkdir fork; cd fork
+ save_CVS_SERVER=$CVS_SERVER
+ unset CVS_SERVER
+ # So looking through $PATH for cvs won't work...
+ echo "echo junk" >cvs
+ chmod a+x cvs
+ save_PATH=$PATH; PATH=.:$PATH
+ # The second error message below is for testing clients without
+ # server support.
+ if ${testcvs_server_support}; then
+ dotest fork-1 "$testcvs -d:fork:$CVSROOT_DIRNAME version" \
+'Client: \(.*\)
+Server: \1'
+ else
+ dotest_fail fork-1-noss \
+"$testcvs -d:fork:$CVSROOT_DIRNAME version" \
+"Client: .*
+Server: ${CPROG} version: You must set the CVS_SERVER environment variable when
+${CPROG} version: using the :fork: access method\.
+${CPROG} \[version aborted\]: This CVS was not compiled with server support\."
+ fi
+
+ CVS_SERVER=${save_CVS_SERVER}; export CVS_SERVER
+ unset save_CVS_SERVER
+ PATH=$save_PATH; unset save_PATH
+
+ dokeep
+ cd ..
+ rm -r fork
+ fi
+ ;;
+
+
+
+ commit-add-missing)
+ # Make sure that a commit fails when a `cvs add'ed file has
+ # been removed from the working directory.
+
+ mkdir 1; cd 1
+ module=c-a-m
+ echo > unused-file
+ dotest commit-add-missing-1 \
+ "$testcvs -Q import -m. $module X Y" ''
+
+ file=F
+ # Check it out and tag it.
+ dotest commit-add-missing-2 "$testcvs -Q co $module" ''
+ cd $module
+ dotest commit-add-missing-3 "$testcvs -Q tag -b B" ''
+ echo v1 > $file
+ dotest commit-add-missing-4 "$testcvs -Q add $file" ''
+ rm -f $file
+ dotest_fail commit-add-missing-5 "$testcvs -Q ci -m. $file" \
+"${SPROG} commit: Up-to-date check failed for .$file'
+${SPROG} \[commit aborted\]: correct above errors first!"
+
+ dotest
+ cd ../..
+ rm -rf 1
+ modify_repo rm -rf $CVSROOT_DIRNAME/$module
+ ;;
+
+
+
+ commit-d)
+ # Check that top-level commits work when CVS/Root
+ # is overridden by cvs -d.
+
+ mkdir -p 1/subdir; cd 1
+ touch file1 subdir/file2
+ dotest commit-d-1 "$testcvs -Q import -m. c-d-c X Y" ""
+ dotest commit-d-2 "$testcvs -Q co c-d-c" ""
+ cd c-d-c
+ echo change >>file1; echo another change >>subdir/file2
+ # Changing working root, then override with -d
+ echo nosuchhost:/cvs > CVS/Root
+ dotest commit-d-3 "$testcvs -q -d '$CVSROOT' commit -m." \
+"$CVSROOT_DIRNAME/c-d-c/file1,v <-- file1
+new revision: 1.2; previous revision: 1.1
+$CVSROOT_DIRNAME/c-d-c/subdir/file2,v <-- subdir/file2
+new revision: 1.2; previous revision: 1.1"
+
+ dokeep
+ cd ../..
+ rm -rf 1 cvsroot/c-d-c
+ ;;
+
+
+
+ template)
+ # Check that the CVS/Template directory is being
+ # properly created.
+ modify_repo mkdir -p $CVSROOT_DIRNAME/first/subdir
+ modify_repo mkdir $CVSROOT_DIRNAME/second
+ mkdir template; cd template
+
+ # check that no CVS/Template is created for an empty rcsinfo
+ # Note: For cvs clients with no Clear-template response, the
+ # CVS/Template file will exist and be zero bytes in length.
+ dotest template-empty-1 "${testcvs} -Q co first" ''
+ dotest template-empty-2 \
+"test ! -s first/CVS/Template" ''
+ dotest template-empty-3 \
+"test ! -s first/subdir/CVS/Template" ''
+ rm -fr first
+
+ # create some template files
+ echo 'CVS: the default template' > ${TESTDIR}/template/temp.def
+ echo 'CVS: the first template' > ${TESTDIR}/template/temp.first
+ echo 'CVS: the subdir template' > ${TESTDIR}/template/temp.subdir
+
+ dotest template-rcsinfo-1 "${testcvs} -Q co CVSROOT" ''
+ cd CVSROOT
+ echo DEFAULT ${TESTDIR}/template/temp.def >>rcsinfo
+ dotest template-rcsinfo-2 "$testcvs -Q ci -m."
+ # Make sure we get the update without a commit.
+ dotest template-rcsinfo-3 "${testcvs} -Q ci -m." ''
+ # Did the CVSROOT/CVS/Template file get the updated version?
+ if $remote; then
+ dotest template-rcsinfo-4r \
+"$diff_u CVS/Template $TESTDIR/template/temp.def"
+ else
+ dotest template-rcsinfo-4 \
+"test ! -f CVS/Template" ''
+ fi
+ echo "^first/subdir ${TESTDIR}/template/temp.subdir" >>rcsinfo
+ echo "^first ${TESTDIR}/template/temp.first" >>rcsinfo
+ dotest template-rcsinfo-4.1 "${testcvs} -Q ci -m. rcsinfo"
+ # Did the CVSROOT/CVS/Template file get the updated version?
+ if $remote; then
+ dotest template-rcsinfo-5r \
+"$diff_u CVS/Template $TESTDIR/template/temp.def"
+ else
+ dotest template-rcsinfo-5 \
+"test ! -f CVS/Template" ''
+ fi
+ cd ..
+
+ # Now checkout the first and second modules and see
+ # if the proper template has been provided for each
+ dotest template-first "${testcvs} co first second" \
+"${SPROG} checkout: Updating first
+${SPROG} checkout: Updating first/subdir
+${SPROG} checkout: Updating second"
+
+ if $remote; then
+ # When in client/server CVS/Template must exist
+ dotest template-first-r-1 "test -f first/CVS/Template" ''
+ dotest template-first-r-2 "test -f first/subdir/CVS/Template" ''
+ dotest template-first-r-3 "test -f second/CVS/Template" ''
+ # The value of the CVS/Template should be equal to the
+ # file called out in the rcsinfo file.
+ dotest template-first-r-4 \
+"$diff_u first/CVS/Template $TESTDIR/template/temp.first"
+ dotest template-first-r-5 \
+"$diff_u first/subdir/CVS/Template $TESTDIR/template/temp.subdir"
+ dotest template-first-r-6 \
+"$diff_u second/CVS/Template $TESTDIR/template/temp.def"
+ else
+ # When in local mode CVS/Template must NOT exist
+ dotest_fail template-first-1 "test -f first/CVS/Template" ''
+ dotest_fail template-first-2 "test -f first/subdir/CVS/Template" ''
+ dotest_fail template-first-3 "test -f second/CVS/Template" ''
+ fi
+
+ # Next, create a new subdirectory and see if it gets the
+ # correct template or not
+ cd second
+ mkdir otherdir
+ dotest template-add-1 "${testcvs} add otherdir" \
+"Directory ${CVSROOT_DIRNAME}/second/otherdir added to the repository"
+ if $remote; then
+ dotest template-add-2r \
+"$diff_u otherdir/CVS/Template $TESTDIR/template/temp.def"
+ else
+ dotest_fail template-add-2 "test -f otherdir/CVS/Template" ''
+ fi
+ cd ..
+
+ # Update the remote template. Then see if doing an
+ # update of a checked out tree will properly update
+ # the CVS/Template files.
+ echo 'CVS: Line two' >> ${TESTDIR}/template/temp.def
+ echo 'CVS: Line two' >> ${TESTDIR}/template/temp.first
+ echo 'CVS: Line two' >> ${TESTDIR}/template/temp.subdir
+ dotest template-second "${testcvs} update first second" \
+"${SPROG} update: Updating first
+${SPROG} update: Updating first/subdir
+${SPROG} update: Updating second
+${SPROG} update: Updating second/otherdir"
+
+ if $remote; then
+ dotest template-second-r-1 \
+"$diff_u first/CVS/Template $TESTDIR/template/temp.first"
+ dotest template-second-r-2 \
+"$diff_u first/subdir/CVS/Template $TESTDIR/template/temp.subdir"
+ dotest template-second-r-3 \
+"$diff_u second/CVS/Template $TESTDIR/template/temp.def"
+ dotest template-second-r-4 \
+"$diff_u second/otherdir/CVS/Template $TESTDIR/template/temp.def"
+ else
+ # When in local mode CVS/Template must NOT exist
+ dotest_fail template-second-1 "test -f CVS/Template" ''
+ dotest_fail template-second-2 "test -f subdir/CVS/Template" ''
+ dotest_fail template-second-3 "test -f second/CVS/Template" ''
+ dotest_fail template-second-4 \
+"test -f second/otherdir/CVS/Template" ''
+ fi
+ # Update the remote template with a zero-length template
+ : > ${TESTDIR}/template/temp.def
+ dotest template-third-1 "${testcvs} update second" \
+"${SPROG} update: Updating second
+${SPROG} update: Updating second/otherdir"
+
+ if $remote; then
+ dotest_fail template-third-r-2 "test -s second/CVS/Template" ''
+ dotest_fail template-third-r-3 "test -s
second/otherdir/CVS/Template" ''
+ else
+ dotest_fail template-third-2 "test -f second/CVS/Template" ''
+ dotest_fail template-third-3 \
+"test -f second/otherdir/CVS/Template" ''
+ fi
+
+ # fun with remote protocols and tags
+ if $remote; then
+ cd second
+ echo hello > file1
+ dotest template-tag-r-1 "${testcvs} -Q add file1" ''
+ dotest template-tag-r-2 "${testcvs} -Q commit -madd file1"
+ dotest template-tag-r-3 "${testcvs} -q tag tag" 'T file1'
+ rm ${CVSROOT_DIRNAME}/CVSROOT/val-tags
+ cd ..
+ rm -fr second
+ dotest template-tag-r-4 "${testcvs} -Q co -rtag second" ''
+ fi
+
+ cd CVSROOT
+ dotest template-norcsinfo-1 "${testcvs} up" \
+"${SPROG} update: Updating \."
+ # Did the CVSROOT/CVS/Template file get the updated version?
+ if $remote; then
+ dotest template-norcsinfo-r-2 \
+"$diff_u CVS/Template $TESTDIR/template/temp.def"
+ else
+ dotest_fail template-norcsinfo-2 "test -f CVS/Template" ''
+ fi
+
+ : > rcsinfo
+ dotest template-norcsinfo-3 "${testcvs} -Q ci -m. rcsinfo"
+ # Did the CVSROOT/CVS/Template file get the updated version?
+ # The file should be gone or of zero length.
+ dotest template-norcsinfo-4 \
+"test ! -s CVS/Template" ''
+ cd ..
+
+ dotest template-norcsinfo-5 "${testcvs} update first" \
+"${SPROG} update: Updating first
+${SPROG} update: Updating first/subdir"
+
+ # Note: For cvs clients with no Clear-template response, the
+ # CVS/Template file will exist and be zero bytes in length.
+ dotest template-norcsinfo-6 \
+"test ! -s first/CVS/Template" ''
+ dotest template-norcsinfo-7 \
+"test ! -s first/subdir/CVS/Template" ''
+
+ dokeep
+
+ # cleanup
+ modify_repo rm -rf $CVSROOT_DIRNAME/first $CVSROOT_DIRNAME/second
+ restore_adm
+ cd ..
+ rm -rf template
+ ;;
+
+
+
+ writeproxy)
+ # Various tests for a read-only CVS mirror set up as a write-proxy
+ # for a central server.
+ #
+ # These tests are only meaningful in client/server mode.
+ if $remote; then :; else
+ remoteonly writeproxy
+ continue
+ fi
+
+ if $noredirect; then
+ notnoredirect writeproxy
+ continue
+ fi
+
+ require_rsync
+ if test $? -eq 77; then
+ skip writeproxy "$skipreason"
+ continue
+ fi
+
+ PRIMARY_CVSROOT_DIRNAME_save=$PRIMARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_save=$PRIMARY_CVSROOT
+ PRIMARY_CVSROOT_DIRNAME=$TESTDIR/primary_cvsroot
+ PRIMARY_CVSROOT=`newroot $PRIMARY_CVSROOT_DIRNAME`
+ SECONDARY_CVSROOT_DIRNAME_save=$SECONDARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_save=$SECONDARY_CVSROOT
+ SECONDARY_CVSROOT_DIRNAME=$TESTDIR/writeproxy_cvsroot
+ SECONDARY_CVSROOT=`newroot $SECONDARY_CVSROOT_DIRNAME`
+
+ # Initialize the primary repository
+ dotest writeproxy-init-1 "$testcvs -d$PRIMARY_CVSROOT init"
+ mkdir writeproxy; cd writeproxy
+ mkdir primary; cd primary
+ dotest writeproxy-init-2 "$testcvs -Qd$PRIMARY_CVSROOT co CVSROOT"
+ cd CVSROOT
+ cat >>loginfo <<EOF
+ALL (cat >/dev/null; echo %R) >$TESTDIR/referrer
+ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+EOF
+ cat >>config <<EOF
+PrimaryServer=$PRIMARY_CVSROOT
+EOF
+ dotest writeproxy-init-3 \
+"$testcvs -Q ci -mconfigure-writeproxy"
+
+ # Quickly verify that the server can resolve symlinks when
+ # determining whether it is the primary.
+ # This shouldn't actually change the repository.
+ save_CVS_SERVER=$CVS_SERVER
+ ln -s $PRIMARY_CVSROOT_DIRNAME $TESTDIR/primary_link
+ dotest writeproxy-0 "$CVS_SERVER server" \
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer
Repository Directory Relative-directory Max-dotdot Static-directory Sticky
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged Notify
Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream
wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log
rlog list rlist global-list-quiet ls add remove update-patches
gzip-file-contents status rdiff tag rtag import admin export history release
watch-on watch-off watch-add watch-remove watchers editors edit init annotate
rannotate noop version
+ok
+ok
+ok" \
+<< EOF
+Root $TESTDIR/primary_link
+Valid-responses ok error Valid-requests Redirect Checked-in New-entry Checksum
Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time
Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky
Clear-sticky Edit-file Template Clear-template Notified Module-expansion
Wrapper-rcsOption M Mbinary E F MT
+valid-requests
+UseUnchanged
+Command-prep commit
+Global_option -q
+Global_option -Q
+Argument -m
+Argument configure-writeproxy
+Argument --
+Directory .
+CVSROOT
+Entry /checkoutlist/1.1///
+Modified checkoutlist
+u=rw,g=rw,o=r
+495
+# The "checkoutlist" file is used to support additional version controlled
+# administrative files in \$CVSROOT/CVSROOT, such as template files.
+#
+# The first entry on a line is a filename which will be checked out from
+# the corresponding RCS file in the \$CVSROOT/CVSROOT directory.
+# The remainder of the line is an error message to use if the file cannot
+# be checked out.
+#
+# File format:
+#
+# [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>
+#
+# comment lines begin with '#'
+ci
+EOF
+ rm $TESTDIR/primary_link
+
+ # And now the secondary.
+ $RSYNC -gopr $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+
+ # Checkout from secondary
+ #
+ # For now, move the primary root out of the way to satisfy
+ # ourselves that the data is coming from the secondary.
+ mv $PRIMARY_CVSROOT_DIRNAME $TESTDIR/save-root
+ cd ../..
+ mkdir secondary; cd secondary
+ dotest writeproxy-1 "$testcvs -qd$SECONDARY_CVSROOT co CVSROOT" \
+"U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg"
+
+ # Confirm data present
+ cd CVSROOT
+ dotest writeproxy-2 "grep rsync loginfo" \
+"ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/
$SECONDARY_CVSROOT_DIRNAME"
+ dotest writeproxy-3 "grep PrimaryServer config" \
+"${DOTSTAR}
+PrimaryServer=$PRIMARY_CVSROOT"
+
+ # Checkin to secondary
+ cd ..
+ dotest writeproxy-4 "$testcvs -Qd$SECONDARY_CVSROOT co -ldtop ."
+ cd top
+ mkdir firstdir
+
+ # Have to move the primary root back before we can perform write
+ # operations.
+ mv $TESTDIR/save-root $PRIMARY_CVSROOT_DIRNAME
+
+ dotest writeproxy-5 "$testcvs -Q add firstdir"
+ cd firstdir
+ echo now you see me >file1
+ dotest writeproxy-6 "$testcvs -Q add file1"
+ dotest writeproxy-6a "grep file1 CVS/Entries >/dev/null"
+ dotest writeproxy-7 "$testcvs -Q ci -mfirst-file file1"
+
+ # Verify that the server got the correct referrer.
+ #
+ # This happens even when using a :fork:ed server because CVS is
+ # hardcoded to support only :ext: servers.
+ #
+ # This test meaningfully detects that a referrer was passed in fork
+ # mode because the only time the referrer string can be altered from
+ # its original state is when the server sends a Referrer response.
+ # If the client were not parsing and resending the referrer, this
+ # string would still match $SECONDARY_CVSROOT_DIRNAME.
+ dotest writeproxy-7a "cat $TESTDIR/referrer" \
+":ext:address@hidden"
+
+ # Make sure the sync took place
+ dotest writeproxy-7b "$testcvs -Q up"
+
+ # Checkout from primary
+ cd ../../../primary
+ dotest writeproxy-8 "$testcvs -qd$PRIMARY_CVSROOT co firstdir" \
+"U firstdir/file1"
+
+ # Confirm data present
+ # - This test indirectly confirms that the commit did not take
+ # place on the secondary.
+ cd firstdir
+ dotest writeproxy-9 "cat file1" "now you see me"
+
+ # Commit to primary
+ echo now you see me again >file1
+ dotest writeproxy-10 "$testcvs -Q ci -medit file1"
+
+ # Update from secondary
+ cd ../../secondary/top/firstdir
+ dotest writeproxy-11 "$testcvs -q up" \
+"U file1"
+
+ # Confirm data present
+ dotest writeproxy-12 "cat file1" "now you see me again"
+
+ # Test a failing rsync
+ cd ../../CVSROOT
+ sed \$d <loginfo >tmp
+ mv tmp loginfo
+ echo >>loginfo \
+"ALL echo >&2 'Im rsync and I encountered an error!'; cat >/dev/null; exit 1"
+ dotest writeproxy-init-13 "$testcvs -Q ci -mbreak-rsync" \
+"Im rsync and I encountered an error!"
+ echo "# a comment" >>loginfo
+ dotest writeproxy-13 "$testcvs -Q ci -mtest-broken-rsync" \
+"Im rsync and I encountered an error!"
+ touch loginfo
+ dotest_fail writeproxy-14 "$testcvs up" \
+"$SPROG update: Updating \.
+$SPROG \[update aborted\]: could not find desired version 1\.4 in
$PRIMARY_CVSROOT_DIRNAME/CVSROOT/loginfo,v"
+
+ dokeep
+ cd ../../..
+ rm -r writeproxy $TESTDIR/referrer
+ rm -rf $PRIMARY_CVSROOT_DIRNAME $SECONDARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_DIRNAME=$PRIMARY_CVSROOT_DIRNAME_save
+ PRIMARY_CVSROOT=$PRIMARY_CVSROOT_save
+ SECONDARY_CVSROOT_DIRNAME=$SECONDARY_CVSROOT_DIRNAME_save
+ SECONDARY_CVSROOT=$SECONDARY_CVSROOT_save
+ ;;
+
+
+
+ writeproxy-noredirect)
+ # Various tests for a read-only CVS mirror set up as a write-proxy
+ # for a central server.
+ #
+ # These tests are only meaningful in client/server mode.
+ #
+ # These tests are a few simple tests for a writeproxy setup with a
+ # client that can't handle the `Redirect' response. Mostly they
+ # parallel the "writeproxy" tests but, in the style of the "server",
+ # "server2", "pserver", and related tests, they bypass the CVS client
+ # for write commands by piping data into a server on STDIN to mimic
+ # a client that cannot handle the `Redirect' response.
+ if $remote; then :; else
+ remoteonly writeproxy-noredirect
+ continue
+ fi
+
+ require_rsync
+ if test $? -eq 77; then
+ skip writeproxy-noredirect "$skipreason"
+ continue
+ fi
+
+ PRIMARY_CVSROOT_DIRNAME_save=$PRIMARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_save=$PRIMARY_CVSROOT
+ PRIMARY_CVSROOT_DIRNAME=$TESTDIR/primary_cvsroot
+ PRIMARY_CVSROOT=`newroot $PRIMARY_CVSROOT_DIRNAME`
+ SECONDARY_CVSROOT_DIRNAME_save=$SECONDARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_DIRNAME=$TESTDIR/writeproxy_cvsroot
+
+ # Initialize the primary repository
+ dotest writeproxy-noredirect-init-1 \
+"$testcvs -d'$PRIMARY_CVSROOT' init"
+ mkdir writeproxy-noredirect; cd writeproxy-noredirect
+ mkdir primary; cd primary
+ dotest writeproxy-noredirect-init-2 \
+"$testcvs -Qd'$PRIMARY_CVSROOT' co CVSROOT"
+ cd CVSROOT
+ cat >>loginfo <<EOF
+ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+EOF
+ cat >>config <<EOF
+PrimaryServer=$PRIMARY_CVSROOT
+EOF
+ dotest writeproxy-noredirect-init-3 \
+"$testcvs -Q ci -mconfigure-writeproxy"
+
+ # And now the secondary.
+ $RSYNC -gopr $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+
+ CVS_SERVER_save=$CVS_SERVER
+ CVS_SERVER_secondary=$TESTDIR/writeproxy-secondary-wrapper
+ CVS_SERVER=$CVS_SERVER_secondary
+
+ # Wrap the CVS server to allow --primary-root to be set by the
+ # secondary.
+ cat <<EOF >$TESTDIR/writeproxy-secondary-wrapper
+#! $TESTSHELL
+CVS_SERVER=$TESTDIR/writeproxy-primary-wrapper
+export CVS_SERVER
+
+# No need to check the PID of the last client since we are testing with
+# Redirect disabled.
+proot_arg="--allow-root $SECONDARY_CVSROOT_DIRNAME --allow-root
$PRIMARY_CVSROOT_DIRNAME"
+exec $servercvs \$proot_arg "\$@"
+EOF
+ cat <<EOF >$TESTDIR/writeproxy-primary-wrapper
+#! $TESTSHELL
+#CVS_SERVER_LOG=/tmp/cvsprimarylog
+exec $servercvs "\$@"
+EOF
+
+ chmod a+x $TESTDIR/writeproxy-secondary-wrapper \
+ $TESTDIR/writeproxy-primary-wrapper
+
+ # Checkout from secondary
+ #
+ # It may look like we are checking out from the primary here, but
+ # in fork mode, the deciding factor is the PrimaryServer translation
+ # above.
+ #
+ # When the primary and secondary hostname were different, the server
+ # the client is talking directly to is more obvious.
+ #
+ # For now, move the primary root out of the way to satisfy
+ # ourselves that the data is coming from the secondary.
+ mv $PRIMARY_CVSROOT_DIRNAME $TESTDIR/save-root
+ cd ../..
+ mkdir secondary; cd secondary
+ dotest writeproxy-noredirect-1 \
+"$testcvs -qd'$PRIMARY_CVSROOT' co CVSROOT" \
+"U CVSROOT/checkoutlist
+U CVSROOT/commitinfo
+U CVSROOT/config
+U CVSROOT/cvswrappers
+U CVSROOT/loginfo
+U CVSROOT/modules
+U CVSROOT/notify
+U CVSROOT/postadmin
+U CVSROOT/postproxy
+U CVSROOT/posttag
+U CVSROOT/postwatch
+U CVSROOT/preproxy
+U CVSROOT/rcsinfo
+U CVSROOT/taginfo
+U CVSROOT/verifymsg"
+
+ # Confirm data present
+ cd CVSROOT
+ dotest writeproxy-noredirect-2 "grep rsync loginfo" \
+"ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/
$SECONDARY_CVSROOT_DIRNAME"
+ dotest writeproxy-noredirect-3 "grep PrimaryServer config" \
+"${DOTSTAR}
+PrimaryServer=$PRIMARY_CVSROOT"
+
+ # Checkin to secondary
+ cd ..
+ dotest writeproxy-noredirect-4 \
+"$testcvs -Qd'$PRIMARY_CVSROOT' co -ldtop ."
+ cd top
+ mkdir firstdir
+
+ # Have to move the primary root back before we can perform write
+ # operations.
+ mv $TESTDIR/save-root $PRIMARY_CVSROOT_DIRNAME
+
+ dotest writeproxy-noredirect-5 "$CVS_SERVER server" \
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer
Repository Directory Relative-directory Max-dotdot Static-directory Sticky
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged Notify
Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream
wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log
rlog list rlist global-list-quiet ls add remove update-patches
gzip-file-contents status rdiff tag rtag import admin export history release
watch-on watch-off watch-add watch-remove watchers editors edit init annotate
rannotate noop version
+ok
+ok
+ok
+Clear-template firstdir/
+firstdir/
+ok" \
+<< EOF
+Root $PRIMARY_CVSROOT_DIRNAME
+Valid-responses ok error Valid-requests Checked-in New-entry Checksum
Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time
Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky
Clear-sticky Template Clear-template Notified Module-expansion
Wrapper-rcsOption M Mbinary E F MT
+valid-requests
+UseUnchanged
+Command-prep add
+Global_option -q
+Global_option -Q
+wrapper-sendme-rcsOptions
+Argument --
+Directory firstdir
+firstdir
+Directory .
+
+Argument firstdir
+add
+EOF
+
+ # Gotta update the workspace ourselves since we bypassed the client.
+ cp -R CVS firstdir/CVS
+ echo "firstdir" >firstdir/CVS/Repository
+
+ cd firstdir
+ echo now you see me >file1
+ dotest writeproxy-noredirect-6 "$CVS_SERVER server" \
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer
Repository Directory Relative-directory Max-dotdot Static-directory Sticky
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged Notify
Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream
wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log
rlog list rlist global-list-quiet ls add remove update-patches
gzip-file-contents status rdiff tag rtag import admin export history release
watch-on watch-off watch-add watch-remove watchers editors edit init annotate
rannotate noop version
+ok
+ok
+ok
+Checked-in \./
+firstdir/file1
+/file1/0///
+ok" \
+<< EOF
+Root $PRIMARY_CVSROOT_DIRNAME
+Valid-responses ok error Valid-requests Checked-in New-entry Checksum
Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time
Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky
Clear-sticky Template Clear-template Notified Module-expansion
Wrapper-rcsOption M Mbinary E F MT
+valid-requests
+UseUnchanged
+Command-prep add
+Global_option -q
+Global_option -Q
+wrapper-sendme-rcsOptions
+Argument --
+Directory .
+firstdir
+Is-modified file1
+Argument file1
+add
+EOF
+
+ # Have to add it to the workspace ourselves again since we are
+ # bypassing the client.
+ echo /file1/0/dummy+timestamp// >>CVS/Entries
+
+ dotest writeproxy-noredirect-7 "$CVS_SERVER server" \
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer
Repository Directory Relative-directory Max-dotdot Static-directory Sticky
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged Notify
Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream
wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log
rlog list rlist global-list-quiet ls add remove update-patches
gzip-file-contents status rdiff tag rtag import admin export history release
watch-on watch-off watch-add watch-remove watchers editors edit init annotate
rannotate noop version
+ok
+ok
+Mode u=rw,g=rw,o=r
+Checked-in \./
+firstdir/file1
+/file1/1\.1///
+ok" \
+<< EOF
+Root $PRIMARY_CVSROOT_DIRNAME
+Valid-responses ok error Valid-requests Checked-in New-entry Checksum
Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time
Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky
Clear-sticky Template Clear-template Notified Module-expansion
Wrapper-rcsOption M Mbinary E F MT
+valid-requests
+UseUnchanged
+Command-prep commit
+Global_option -q
+Global_option -Q
+Argument -m
+Argument first-file
+Argument --
+Directory .
+firstdir
+Entry /file1/0/+modified//
+Modified file1
+u=rw,g=rw,o=r
+15
+now you see me
+Argument file1
+ci
+EOF
+
+ # Have to add it to the workspace ourselves again since we are
+ # bypassing the client.
+ echo D >CVS/Entries
+ echo /file1/1.1/dummy+timestamp// >>CVS/Entries
+
+ # Make sure the sync took place
+ dotest writeproxy-noredirect-7a "$testcvs -Q up"
+
+ CVS_SERVER=$servercvs
+ # Checkout from primary
+ cd ../../../primary
+ dotest writeproxy-noredirect-8 \
+"$testcvs -qd'$PRIMARY_CVSROOT' co firstdir" \
+"U firstdir/file1"
+
+ # Confirm data present
+ # - This test indirectly confirms that the commit did not take
+ # place on the secondary.
+ cd firstdir
+ dotest writeproxy-noredirect-9 "cat file1" "now you see me"
+
+ # Commit to primary
+ echo now you see me again >file1
+ dotest writeproxy-noredirect-10 "$testcvs -Q ci -medit file1"
+
+ CVS_SERVER=$CVS_SERVER_secondary
+ # Update from secondary
+ cd ../../secondary/top/firstdir
+ dotest writeproxy-noredirect-11 "$testcvs -q up" "U file1"
+
+ # Confirm data present
+ dotest writeproxy-noredirect-12 "cat file1" "now you see me again"
+
+ dokeep
+ cd ../../../..
+ rm -r writeproxy-noredirect
+ rm -rf $PRIMARY_CVSROOT_DIRNAME $SECONDARY_CVSROOT_DIRNAME
+ rm $TESTDIR/writeproxy-secondary-wrapper \
+ $TESTDIR/writeproxy-primary-wrapper
+ CVS_SERVER=$CVS_SERVER_save
+ PRIMARY_CVSROOT_DIRNAME=$PRIMARY_CVSROOT_DIRNAME_save
+ PRIMARY_CVSROOT=$PRIMARY_CVSROOT_save
+ SECONDARY_CVSROOT_DIRNAME=$SECONDARY_CVSROOT_DIRNAME_save
+ ;;
+
+
+
+ writeproxy-ssh)
+ # Various tests for a read-only CVS mirror set up as a write-proxy
+ # for a central server accessed via the :ext: method.
+ #
+ # Mostly these tests are intended to set up for the final test which
+ # verifies that the server registers the referrer.
+ if $remote; then :; else
+ remoteonly writeproxy-ssh
+ continue
+ fi
+
+ if $noredirect; then
+ notnoredirect writeproxy-ssh
+ continue
+ fi
+
+ require_rsh "$CVS_RSH"
+ if test $? -eq 77; then
+ skip writeproxy-ssh "$skipreason"
+ continue
+ fi
+
+ require_rsync
+ if test $? -eq 77; then
+ skip writeproxy-ssh "$skipreason"
+ continue
+ fi
+
+ # Save old roots.
+ PRIMARY_CVSROOT_DIRNAME_save=$PRIMARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_save=$PRIMARY_CVSROOT
+ SECONDARY_CVSROOT_DIRNAME_save=$SECONDARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_save=$SECONDARY_CVSROOT
+
+ # Set new roots.
+ PRIMARY_CVSROOT_DIRNAME=$TESTDIR/primary_cvsroot
+ PRIMARY_CVSROOT=:ext:$host$PRIMARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_DIRNAME=$TESTDIR/writeproxy_cvsroot
+ SECONDARY_CVSROOT=":ext;Redirect=yes:$host$SECONDARY_CVSROOT_DIRNAME"
+
+ # Initialize the primary repository
+ dotest writeproxy-ssh-init-1 "$testcvs -d$PRIMARY_CVSROOT init"
+ mkdir writeproxy-ssh; cd writeproxy-ssh
+ mkdir primary; cd primary
+ dotest writeproxy-ssh-init-2 "$testcvs -Qd$PRIMARY_CVSROOT co CVSROOT"
+ cd CVSROOT
+ cat >>loginfo <<EOF
+ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+EOF
+ cat >>loginfo <<EOF
+ALL echo Referrer=%R; cat >/dev/null
+EOF
+ cat >>config <<EOF
+PrimaryServer=$PRIMARY_CVSROOT
+EOF
+ dotest writeproxy-ssh-init-3 \
+"$testcvs -Q ci -mconfigure-writeproxy-ssh" \
+"Referrer=NONE"
+
+ # And now the secondary.
+ $RSYNC -gopr $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+
+ # Checkout from secondary
+ #
+ # For now, move the primary root out of the way to satisfy
+ # ourselves that the data is coming from the secondary.
+ mv $PRIMARY_CVSROOT_DIRNAME $TESTDIR/save-root
+
+ # Checkin to secondary
+ cd ../..
+ save_CVSROOT=$CVSROOT
+ CVSROOT=$SECONDARY_CVSROOT
+ export CVSROOT
+ dotest writeproxy-ssh-1 "$testcvs -Q co -ldtop ."
+ CVSROOT=$save_CVSROOT
+ export CVSROOT
+ cd top
+ mkdir firstdir
+
+ # Have to move the primary root back before we can perform write
+ # operations.
+ mv $TESTDIR/save-root $PRIMARY_CVSROOT_DIRNAME
+
+ dotest writeproxy-ssh-2 "$testcvs -Q add firstdir" \
+"Referrer=:ext:address@hidden"
+
+ cd firstdir
+ echo now you see me >file1
+ dotest writeproxy-ssh-3 "$testcvs -Q add file1"
+ dotest writeproxy-ssh-4 "$testcvs -Q ci -mfirst-file file1" \
+"Referrer=:ext:address@hidden"
+
+ dokeep
+ cd ../../..
+ rm -r writeproxy-ssh
+ rm -rf $PRIMARY_CVSROOT_DIRNAME $SECONDARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_DIRNAME=$PRIMARY_CVSROOT_DIRNAME_save
+ PRIMARY_CVSROOT=$PRIMARY_CVSROOT_save
+ SECONDARY_CVSROOT_DIRNAME=$SECONDARY_CVSROOT_DIRNAME_save
+ SECONDARY_CVSROOT=$SECONDARY_CVSROOT_save
+ ;;
+
+
+
+ writeproxy-ssh-noredirect)
+ # Various tests for a read-only CVS mirror set up as a write-proxy
+ # for a central server accessed via the :ext: method.
+ #
+ # Mostly these tests are intended to set up for the final test which
+ # verifies that the server registers the referrer.
+ if $remote; then :; else
+ remoteonly writeproxy-ssh-noredirect
+ continue
+ fi
+
+ require_rsh "$CVS_RSH"
+ if test $? -eq 77; then
+ skip writeproxy-ssh-noredirect "$skipreason"
+ continue
+ fi
+
+ require_rsync
+ if test $? -eq 77; then
+ skip writeproxy-ssh-noredirect "$skipreason"
+ continue
+ fi
+
+ # Save old roots.
+ PRIMARY_CVSROOT_DIRNAME_save=$PRIMARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_save=$PRIMARY_CVSROOT
+ SECONDARY_CVSROOT_DIRNAME_save=$SECONDARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_save=$SECONDARY_CVSROOT
+
+ # Set new roots.
+ PRIMARY_CVSROOT_DIRNAME=$TESTDIR/primary_cvsroot
+ PRIMARY_CVSROOT=:ext:$host$PRIMARY_CVSROOT_DIRNAME
+ SECONDARY_CVSROOT_DIRNAME=$TESTDIR/writeproxy_cvsroot
+ SECONDARY_CVSROOT=":ext;Redirect=no:$host$PRIMARY_CVSROOT_DIRNAME"
+
+ # Initialize the primary repository
+ dotest writeproxy-ssh-noredirect-init-1 \
+"$testcvs -d$PRIMARY_CVSROOT init"
+ mkdir writeproxy-ssh-noredirect; cd writeproxy-ssh-noredirect
+ mkdir primary; cd primary
+ dotest writeproxy-ssh-noredirect-init-2 \
+"$testcvs -Qd$PRIMARY_CVSROOT co CVSROOT"
+ cd CVSROOT
+ cat >>loginfo <<EOF
+ALL $RSYNC -gopr --delete $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+EOF
+ cat >>loginfo <<EOF
+ALL echo Referrer=%R; cat >/dev/null
+EOF
+ cat >>config <<EOF
+PrimaryServer=$PRIMARY_CVSROOT
+EOF
+ dotest writeproxy-ssh-noredirect-init-3 \
+"$testcvs -Q ci -mconfigure-writeproxy-ssh-noredirect" \
+"Referrer=NONE"
+
+ # And now the secondary.
+ $RSYNC -gopr $PRIMARY_CVSROOT_DIRNAME/ $SECONDARY_CVSROOT_DIRNAME
+
+ # Wrap the CVS server to allow --primary-root to be set by the
+ # secondary.
+ cat <<EOF >$TESTDIR/writeproxy-secondary-wrapper
+#! $TESTSHELL
+CVS_SERVER=$TESTDIR/writeproxy-primary-wrapper
+export CVS_SERVER
+
+# No need to check the PID of the last client since we are testing with
+# Redirect disabled.
+proot_arg="--allow-root=$SECONDARY_CVSROOT_DIRNAME
--allow-root=$PRIMARY_CVSROOT_DIRNAME"
+exec $CVS_SERVER \$proot_arg "\$@"
+EOF
+ cat <<EOF >$TESTDIR/writeproxy-primary-wrapper
+#! $TESTSHELL
+if test -n "\$CVS_SERVER_LOG"; then
+ CVS_SERVER_LOG=$TMPDIR/cvsprimarylog; export CVS_SERVER_LOG
+fi
+exec $CVS_SERVER "\$@"
+EOF
+
+ CVS_SERVER_save=$CVS_SERVER
+ CVS_SERVER_secondary=$TESTDIR/writeproxy-secondary-wrapper
+ CVS_SERVER=$CVS_SERVER_secondary
+
+ chmod a+x $TESTDIR/writeproxy-secondary-wrapper \
+ $TESTDIR/writeproxy-primary-wrapper
+
+ # Checkout from secondary
+ #
+ # For now, move the primary root out of the way to satisfy
+ # ourselves that the data is coming from the secondary.
+ mv $PRIMARY_CVSROOT_DIRNAME $TESTDIR/save-root
+
+ # Checkin to secondary
+ cd ../..
+ dotest writeproxy-ssh-noredirect-1 \
+"$testcvs -qd '$SECONDARY_CVSROOT' co -ldtop ."
+
+ cd top
+ mkdir firstdir
+
+ # Have to move the primary root back before we can perform write
+ # operations.
+ mv $TESTDIR/save-root $PRIMARY_CVSROOT_DIRNAME
+
+ dotest writeproxy-ssh-noredirect-2 "$testcvs -Q add firstdir" \
+"Referrer=NONE"
+
+ cd firstdir
+ echo now you see me >file1
+ dotest writeproxy-ssh-noredirect-3 "$testcvs -Q add file1"
+ dotest writeproxy-ssh-noredirect-4 \
+"$testcvs -Q ci -mfirst-file file1" \
+"Referrer=NONE"
+
+ dokeep
+ cd ../../..
+ rm -r writeproxy-ssh-noredirect
+ rm -rf $PRIMARY_CVSROOT_DIRNAME $SECONDARY_CVSROOT_DIRNAME
+ PRIMARY_CVSROOT_DIRNAME=$PRIMARY_CVSROOT_DIRNAME_save
+ PRIMARY_CVSROOT=$PRIMARY_CVSROOT_save
+ SECONDARY_CVSROOT_DIRNAME=$SECONDARY_CVSROOT_DIRNAME_save
+ SECONDARY_CVSROOT=$SECONDARY_CVSROOT_save
+ rm $TESTDIR/writeproxy-secondary-wrapper \
+ $TESTDIR/writeproxy-primary-wrapper
+ CVS_SERVER=$CVS_SERVER_save
+ ;;
+
+
+
+ trace)
+ # Check that there are no core dumps lurking in the trace
+ # options.
+
+ # Perform some cleanup for normalized testing...
+ rm ${CVSROOT_DIRNAME}/CVSROOT/history
+ rm -f ${CVSROOT_DIRNAME}/CVSROOT/cvsignore
+ rm -f ${CVSROOT_DIRNAME}/CVSROOT/cvsignore,v
+
+ # checkout the trace option
+
+ mkdir trace && cd trace
+ mkdir imp && cd imp
+ touch file1
+
+ dotest_sort trace-1 "${testcvs} -t -t -t init" \
+" *-> Lock_Cleanup()
+ *-> RCS_checkout (checkoutlist,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (commitinfo,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (config,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (cvswrappers,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (loginfo,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (modules,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (notify,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (postadmin,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (postproxy,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (posttag,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (postwatch,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (preproxy,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (rcsinfo,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (taginfo,v, , , , \.#[0-9][0-9]*)
+ *-> RCS_checkout (verifymsg,v, , , , \.#[0-9][0-9]*)
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#[0-9][0-9]*)
+ *-> unlink_file(\.#checkoutlist)
+ *-> unlink_file(\.#commitinfo)
+ *-> unlink_file(\.#config)
+ *-> unlink_file(\.#cvswrappers)
+ *-> unlink_file(\.#loginfo)
+ *-> unlink_file(\.#modules)
+ *-> unlink_file(\.#notify)
+ *-> unlink_file(\.#postadmin)
+ *-> unlink_file(\.#postproxy)
+ *-> unlink_file(\.#posttag)
+ *-> unlink_file(\.#postwatch)
+ *-> unlink_file(\.#preproxy)
+ *-> unlink_file(\.#rcsinfo)
+ *-> unlink_file(\.#taginfo)
+ *-> unlink_file(\.#verifymsg)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )" \
+"
+ *-> Forking server: ${CVS_SERVER} server
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> RCS_checkout (checkoutlist,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (commitinfo,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (config,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (cvswrappers,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (loginfo,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (modules,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (notify,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (postadmin,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (postproxy,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (posttag,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (postwatch,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (preproxy,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (rcsinfo,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (taginfo,v, , , , \.#[0-9][0-9]*)
+S -> RCS_checkout (verifymsg,v, , , , \.#[0-9][0-9]*)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> do_cvs_command (init)
+S -> remove_locks()
+S -> remove_locks()
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#[0-9][0-9]*)
+S -> unlink_file(\.#checkoutlist)
+S -> unlink_file(\.#commitinfo)
+S -> unlink_file(\.#config)
+S -> unlink_file(\.#cvswrappers)
+S -> unlink_file(\.#loginfo)
+S -> unlink_file(\.#modules)
+S -> unlink_file(\.#notify)
+S -> unlink_file(\.#postadmin)
+S -> unlink_file(\.#postproxy)
+S -> unlink_file(\.#posttag)
+S -> unlink_file(\.#postwatch)
+S -> unlink_file(\.#preproxy)
+S -> unlink_file(\.#rcsinfo)
+S -> unlink_file(\.#taginfo)
+S -> unlink_file(\.#verifymsg)" \
+
+ dotest_sort trace-2 \
+"${testcvs} -t -t -t import -mimport trace MYVENDOR version-1" \
+"
+
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> safe_location( where=(null) )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+N trace/file1
+No conflicts created by this import" \
+"
+
+
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Sending file \`file1' to server
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+N trace/file1
+No conflicts created by this import
+S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (import)
+S -> remove_locks()
+S -> remove_locks()
+S -> safe_location( where=(null) )
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()"
+
+ cd ..
+ rm -fr imp
+
+ dotest_sort trace-3 "${testcvs} -t -t -t co trace" \
+" *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=3, aflag=0,
+ *locktype=1, update_preload=trace
+ *-> Create_Admin
+ *-> Create_Admin (\., trace, ${CVSROOT_DIRNAME}/trace, , , 0, 0, 1)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , , file1)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Register(file1, 1\.1\.1\.1, ${DATE}, , )
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Write_Template (trace, ${CVSROOT_DIRNAME}/trace)
+ *-> chmod(file1,[0-7][0-7]*)
+ *-> do_module (trace, Updating, NULL, NULL)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> safe_location( where=(null) )
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(\./CVS/Entries\.Static)
+ *-> unlink_file(\./CVS/Tag)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> unlink_file_dir(CVS/,,file1)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+U trace/file1
+${SPROG} checkout: Updating trace" \
+"
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=3, aflag=0,
+ *locktype=1, update_preload=trace
+ *-> Create_Admin
+ *-> Create_Admin (trace, trace, ${CVSROOT_DIRNAME}/trace, , , 0, 0, 1)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Register(file1, 1\.1\.1\.1, ${DATE}, , )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(\.new\.file1,file1)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> safe_location( where=(null) )
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> unlink_file(CVS/Entries\.Static)
+ *-> unlink_file(CVS/Tag)
+ *-> unlink_file(CVS/Template)
+ *-> unlink_file(trace/CVS/Tag)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Create_Admin
+S -> Create_Admin (\., trace, ${CVSROOT_DIRNAME}/trace, , , 0, 0, 1)
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/rcsinfo, trace, ALL)
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , ,
(function))
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Register(file1, 1\.1\.1\.1, , , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Write_Template (trace, ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (\., ${CVSROOT_DIRNAME})
+S -> dirswitch (\., ${CVSROOT_DIRNAME})
+S -> do_cvs_command (checkout)
+S -> do_module (trace, Updating, NULL, NULL)
+S -> do_module (trace, Updating, NULL, NULL)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> safe_location( where=(null) )
+S -> serve_directory (\.)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_register(file1, 1\.1\.1\.1, , , , , )
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(\./CVS/Entries\.Static)
+S -> unlink_file(\./CVS/Tag)
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+U trace/file1
+${SPROG} checkout: Updating trace"
+
+ cd trace
+ mkdir subdir
+ dotest_sort trace-4 "${testcvs} -t -t -t add subdir" \
+" *-> Create_Admin
+ *-> Create_Admin (\., subdir, ${CVSROOT_DIRNAME}/trace/subdir, , , 0, 0, 1)
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace/subdir, ALL)
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> unlink_file(\./CVS/Tag)
+ *-> unlink_file(${CVSROOT_DIRNAME}/trace/subdir/CVS/fileattr)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+Directory ${CVSROOT_DIRNAME}/trace/subdir added to the repository" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *-> Create_Admin
+ *-> Create_Admin (subdir, subdir, ${CVSROOT_DIRNAME}/trace/subdir, , , 0, 0,
1)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+${DOTSTAR} *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+${DOTSTAR} *-> unlink_file(CVS/Template)
+ *-> unlink_file(subdir/CVS/Tag)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${DOTSTAR}Directory ${CVSROOT_DIRNAME}/trace/subdir added to the repository
+S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace/subdir, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/rcsinfo, trace/subdir, ALL)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Write_Template (subdir, ${CVSROOT_DIRNAME}/trace/subdir)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (subdir, ${CVSROOT_DIRNAME}/trace/subdir)
+S -> do_cvs_command (add)
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> serve_directory (subdir)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> unlink_file(${CVSROOT_DIRNAME}/trace/subdir/CVS/fileattr)
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )"
+ touch file2
+ dotest_sort trace-5 "${testcvs} -t -t -t add file2" \
+" *-> Lock_Cleanup()
+ *-> Register(file2, 0, Initial file2, , )
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} add: scheduling file \`file2' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Register(file2, 0, dummy timestamp, , )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+${DOTSTAR} *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+${DOTSTAR} *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${DOTSTAR}S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Register(file2, 0, Initial file2, , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (add)
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_register(file2, 0, Initial file2, , , , )
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} add: scheduling file \`file2' for addition
+${SPROG} add: use \`${SPROG} commit' to add this file permanently"
+ dotest_sort trace-6 "${testcvs} -t -t -t ci -mnew-file file2" \
+" *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+ *-> Promotable_Lock ()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file2,v, 1, , , (function))
+ *-> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file2,v, 1, (null), , file2 )
+ *-> Register(file2, 1\.1, ${DATE}, , )
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_tree_promotably (1, argv, 0, 1, 0)
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_lock(${CVSROOT_DIRNAME}/trace)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> remove_locks()
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Base/file2)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> unlink_file(CVS/file2,t)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file2,v <-- file2
+initial revision: 1\.1" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Register(file2, 1\.1, ${DATE}, , )
+ *-> Sending file \`file2' to server
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Base/file2)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file2,v <-- file2
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+S -> Promotable_Lock ()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file2,v, 1, , , (function))
+S -> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file2,v, 1, (null), , file2 )
+S -> Register(file2, 1\.1, ${DATE}, , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (commit)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_tree_promotably (1, argv, 0, 1, 0)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_lock(${CVSROOT_DIRNAME}/trace)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> remove_locks()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file2)
+S -> server_pathname_check (file2)
+S -> server_pathname_check (file2)
+S -> server_register(file2, 1\.1, ${DATE}, , , , )
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(CVS/file2,t)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+initial revision: 1\.1"
+ dotest_sort trace-7 "${testcvs} -t -t -t tag bp" \
+" *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in= )
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=1, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *local_specified=0, mname=(null), msg=(null) )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+T file1
+T file2
+${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging subdir" \
+"
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in= )
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *local_specified=0, mname=(null), msg=(null) )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (subdir, ${CVSROOT_DIRNAME}/trace/subdir)
+S -> do_cvs_command (tag)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+S -> serve_directory (\.)
+S -> serve_directory (\.)
+S -> serve_directory (subdir)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+T file1
+T file2
+${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging subdir"
+
+ dotest_sort trace-8 "${testcvs} -t -t -t tag -b branch1" \
+" *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in= )
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=1, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *local_specified=0, mname=(null), msg=(null) )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+ *-> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+T file1
+T file2
+${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging subdir" \
+"
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in= )
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *local_specified=0, mname=(null), msg=(null) )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (subdir, ${CVSROOT_DIRNAME}/trace/subdir)
+S -> do_cvs_command (tag)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+S -> serve_directory (\.)
+S -> serve_directory (\.)
+S -> serve_directory (subdir)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+T file1
+T file2
+${SPROG} tag: Tagging \.
+${SPROG} tag: Tagging subdir"
+ dotest_sort trace-9 "${testcvs} -t -t -t log" \
+"
+
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=7, aflag=0,
+ *locktype=1, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ MYVENDOR: 1\.1\.1
+ bp: 1\.1
+ bp: 1\.1\.1\.1
+ branch1: 1\.1\.0\.2
+ branch1: 1\.1\.1\.1\.0\.2
+ version-1: 1\.1\.1\.1
+----------------------------
+----------------------------
+----------------------------
+=============================================================================
+=============================================================================
+Initial revision
+RCS file: ${CVSROOT_DIRNAME}/trace/file1,v
+RCS file: ${CVSROOT_DIRNAME}/trace/file2,v
+Working file: file1
+Working file: file2
+access list:
+access list:
+branch:
+branch: 1\.1\.1
+branches: 1\.1\.1;
+${SPROG} log: Logging \.
+${SPROG} log: Logging subdir
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+description:
+description:
+head: 1\.1
+head: 1\.1
+import
+keyword substitution: kv
+keyword substitution: kv
+locks: strict
+locks: strict
+new-file
+revision 1\.1
+revision 1\.1
+revision 1\.1\.1\.1
+symbolic names:
+symbolic names:
+total revisions: 1; selected revisions: 1
+total revisions: 2; selected revisions: 2" \
+"
+
+
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=7, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ MYVENDOR: 1\.1\.1
+ bp: 1\.1
+ bp: 1\.1\.1\.1
+ branch1: 1\.1\.0\.2
+ branch1: 1\.1\.1\.1\.0\.2
+ version-1: 1\.1\.1\.1
+----------------------------
+----------------------------
+----------------------------
+=============================================================================
+=============================================================================
+Initial revision
+RCS file: ${CVSROOT_DIRNAME}/trace/file1,v
+RCS file: ${CVSROOT_DIRNAME}/trace/file2,v
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> dirswitch (subdir, ${CVSROOT_DIRNAME}/trace/subdir)
+S -> do_cvs_command (log)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> serve_directory (\.)
+S -> serve_directory (subdir)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+Working file: file1
+Working file: file2
+access list:
+access list:
+branch:
+branch: 1\.1\.1
+branches: 1\.1\.1;
+${SPROG} log: Logging \.
+${SPROG} log: Logging subdir
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+date: ${ISO8601DATE}; author: ${username}; state: Exp; lines: ${PLUS}0 -0;
commitid: ${commitid};
+date: ${ISO8601DATE}; author: ${username}; state: Exp; commitid:
${commitid};
+description:
+description:
+head: 1\.1
+head: 1\.1
+import
+keyword substitution: kv
+keyword substitution: kv
+locks: strict
+locks: strict
+new-file
+revision 1\.1
+revision 1\.1
+revision 1\.1\.1\.1
+symbolic names:
+symbolic names:
+total revisions: 1; selected revisions: 1
+total revisions: 2; selected revisions: 2"
+
+ dotest_sort trace-10 "${testcvs} -t -t -t annotate file1" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *locktype=1, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+Annotations for file1" \
+"
+
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in= )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+Annotations for file1
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (annotate)
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )"
+
+ dotest_sort \
+trace-11 "${testcvs} -t -t -t rtag -r bp -b branch2 trace" \
+" *aflag=0, repository=${CVSROOT_DIRNAME}/trace )
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=6, aflag=0,
+ *local=0, which=6, aflag=0,
+ *local=0, which=6, aflag=0,
+ *locktype=1, update_preload=(null)
+ *locktype=1, update_preload=trace
+ *locktype=2, update_preload=trace
+ *local_specified=0, mname=trace, msg=Tagging )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, trace/file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+ *-> check_fileproc ( ${CVSROOT_DIRNAME}/trace, trace/file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+ *-> do_module (trace, Tagging, NULL, branch2)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+ *-> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> tag_check_valid ( name=bp, argc=0, argv=${PFMT}, local=0,
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} rtag: Tagging trace
+${SPROG} rtag: Tagging trace/subdir" \
+"
+ *aflag=0, repository=${CVSROOT_DIRNAME}/trace )
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=6, aflag=0,
+ *local=0, which=6, aflag=0,
+ *local=0, which=6, aflag=0,
+ *locktype=1, update_preload=(null)
+ *locktype=1, update_preload=trace
+ *locktype=2, update_preload=trace
+ *local_specified=0, mname=trace, msg=Tagging )
+ *mwhere=(null), mfile=(null), shorten=0,
+ *-> Forking server: ${CVS_SERVER} server
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/taginfo, trace, ALL)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace/subdir)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*, (null))
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*, (null))
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, trace/file1,
${CVSROOT_DIRNAME}/trace/file1,v )
+S -> check_fileproc ( ${CVSROOT_DIRNAME}/trace, trace/file2,
${CVSROOT_DIRNAME}/trace/file2,v )
+S -> do_cvs_command (rtag)
+S -> do_module (trace, Tagging, NULL, branch2)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file2,,${CVSROOT_DIRNAME}/trace/file2,v)
+S -> rtag_proc ( argc=1, argv=${PFMT}, xwhere=(null),
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> tag_check_valid ( name=bp, argc=0, argv=${PFMT}, local=0,
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} rtag: Tagging trace
+${SPROG} rtag: Tagging trace/subdir"
+
+ dotest_sort trace-12 "${testcvs} -t -t -t status file1" \
+"
+
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *locktype=1, update_preload=(null)
+ Repository revision: 1\.1\.1\.1 ${CVSROOT_DIRNAME}/trace/file1,v
+ Sticky Date: (none)
+ Sticky Options: (none)
+ Sticky Tag: (none)
+ Working revision: 1\.1\.1\.1 ${DATE}
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+===================================================================
+File: file1 *Status: Up-to-date" \
+"
+
+
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ Repository revision: 1\.1\.1\.1 ${CVSROOT_DIRNAME}/trace/file1,v
+ Sticky Date: (none)
+ Sticky Options: (none)
+ Sticky Tag: (none)
+ Working revision: 1\.1\.1\.1
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+===================================================================
+File: file1 *Status: Up-to-date
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (status)
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )"
+
+ echo foo >> file1
+ dotest_sort trace-13 "${testcvs} -t -t -t up -C file1" \
+" *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=3, aflag=0,
+ *locktype=1, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , ,
(function))
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , , file1)
+ *-> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, (null), ,
file1 )
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Register(file1, 1\.1\.1\.1, ${DATE}, , )
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> chmod(file1,[0-7][0-7]*)
+ *-> copy(file1,\.#file1\.1\.1\.1\.1)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> rename(file1,CVS/,,file1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> unlink_file_dir(CVS/,,file1)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+(Locally modified file1 moved to \.#file1\.1\.1\.1\.1)
+U file1" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=3, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Register(file1, 1\.1\.1\.1, ${DATE}, , )
+ *-> copy(file1,\.#file1\.1\.1\.1\.1)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(\.new\.file1,file1)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+(Locally modified file1 moved to \.#file1\.1\.1\.1\.1)
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , ,
(function))
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Register(file1, 1\.1\.1\.1, M, , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (update)
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> server_register(file1, 1\.1\.1\.1, M, , , , )
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+U file1"
+ echo foo >> file1
+ dotest_sort trace-14 "${testcvs} -t -t -t ci -madd-data file1" \
+" *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+ *-> Promotable_Lock ()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, , , , (function))
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, , -ko, ${tempname})
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , ,
(function))
+ *-> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, (null), (null), , file1 )
+ *-> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, (null), ,
file1 )
+ *-> Register(file1, 1\.2, ${DATE}, , )
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_tree_promotably (1, argv, 0, 1, 0)
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_lock(${CVSROOT_DIRNAME}/trace)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> remove_locks()
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(${tempname})
+ *-> unlink_file(${tempname})
+ *-> unlink_file(CVS/Base/file1)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file1,v <-- file1
+new revision: 1\.2; previous revision: 1\.1" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Register(file1, 1\.2, ${DATE}, , )
+ *-> Sending file \`file1' to server
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Base/file1)
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file1,v <-- file1
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+S -> Promotable_Lock ()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, , , , (function))
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, , -ko, ${tempname})
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, , ,
(function))
+S -> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, (null), (null), , file1 )
+S -> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, (null), ,
file1 )
+S -> Register(file1, 1\.2, ${DATE}, , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (commit)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_tree_promotably (1, argv, 0, 1, 0)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_lock(${CVSROOT_DIRNAME}/trace)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> remove_locks()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> server_pathname_check (file1)
+S -> server_pathname_check (file1)
+S -> server_register(file1, 1\.2, ${DATE}, , , , )
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(${tempname})
+S -> unlink_file(${tempname})
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+new revision: 1\.2; previous revision: 1\.1"
+
+ dotest_fail_sort trace-15 "${testcvs} -t -t -t diff -r1.1 file1" \
+" *aflag=0, repository= )
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=7, aflag=0,
+ *locktype=1, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, , , ${tempname})
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , (function))
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , ${tempname})
+ *-> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, 1\.2, , file1 )
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> diff_file_nodiff (file1, 3)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> tag_check_valid ( name=1\.1, argc=1, argv=${PFMT}, local=0,
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+0a1
+===================================================================
+> foo
+Index: file1
+RCS file: ${CVSROOT_DIRNAME}/trace/file1,v
+diff -r1\.1 -r1\.2
+retrieving revision 1\.1
+retrieving revision 1\.2" \
+"
+ *aflag=0, repository= )
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=7, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+0a1
+===================================================================
+> foo
+Index: file1
+RCS file: ${CVSROOT_DIRNAME}/trace/file1,v
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, , , ${tempname})
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , (function))
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , ${tempname})
+S -> RCS_cmp_file( ${CVSROOT_DIRNAME}/trace/file1,v, 1\.1, 1\.2, , file1 )
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> diff_file_nodiff (file1, 3)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (diff)
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> tag_check_valid ( name=1\.1, argc=1, argv=${PFMT}, local=0,
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+diff -r1\.1 -r1\.2
+retrieving revision 1\.1
+retrieving revision 1\.2"
+
+ dotest_sort trace-16 "${testcvs} -t -t -t rdiff -rbp trace/file1" \
+" *aflag=0, repository=${CVSROOT_DIRNAME}/trace )
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=6, aflag=0,
+ *locktype=1, update_preload=trace
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, bp, ,
${tempname})
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , ${tempname})
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> do_module (trace/file1, Patching, NULL, NULL)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> patch_proc ( (null), (null), (null), 0, 0, trace/file1, Patching )
+ *-> remove_locks()
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> tag_check_valid ( name=bp, argc=1, argv=${PFMT}, local=0,
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+\*\*\* 0 \*\*\*\*
+\*\*\* trace/file1:1\.1\.1\.1 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+${PLUS} foo
+--- 1 ----
+--- trace/file1 ${DATE}
+Index: trace/file1
+diff -c trace/file1:1\.1\.1\.1 trace/file1:1\.2" \
+"
+ *aflag=0, repository=${CVSROOT_DIRNAME}/trace )
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=${CVSROOT_DIRNAME}/trace )
+ *local=0, which=6, aflag=0,
+ *locktype=1, update_preload=trace
+ *-> Forking server: ${CVS_SERVER} server
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+\*\*\* 0 \*\*\*\*
+\*\*\* trace/file1:1\.1\.1\.1 ${DATE}
+\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
+${PLUS} foo
+--- 1 ----
+--- trace/file1 ${DATE}
+Index: trace/file1
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.1\.1\.1, bp, ,
${tempname})
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , , ${tempname})
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> do_cvs_command (rdiff)
+S -> do_module (trace/file1, Patching, NULL, NULL)
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> patch_proc ( (null), (null), (null), 0, 0, trace/file1, Patching )
+S -> remove_locks()
+S -> remove_locks()
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> tag_check_valid ( name=bp, argc=1, argv=${PFMT}, local=0,
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+diff -c trace/file1:1\.1\.1\.1 trace/file1:1\.2"
+
+ dotest_sort trace-17 "${testcvs} -t -t -t rm -f file1" \
+" *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *locktype=1, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+ *-> Register(file1, -1\.2, ${DATE}, , )
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> do_recursion ( frame=${PFMT} )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_simple_remove()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} remove: scheduling \`file1' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=1, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Register(file1, -1\.2, dummy timestamp, , )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Reader_Lock(${CVSROOT_DIRNAME}/trace)
+S -> Register(file1, -1\.2, , , )
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (remove)
+S -> do_recursion ( frame=${PFMT} )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_simple_remove()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> server_register(file1, -1\.2, , , , , )
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(CVS/Entries\.Log)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${SPROG} remove: scheduling \`file1' for removal
+${SPROG} remove: use \`${SPROG} commit' to remove this file permanently"
+
+ dotest_sort trace-18 "${testcvs} -t -t -t ci -mremove file1" \
+" *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Lock_Cleanup()
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+ *-> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+ *-> Promotable_Lock ()
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, , , , file1)
+ *-> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , -ko, ${tempname})
+ *-> Scratch_Entry(file1)
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_tree_promotably (1, argv, 0, 1, 0)
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_lock(${CVSROOT_DIRNAME}/trace)
+ *-> rcs_cleanup()
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> remove_locks()
+ *-> remove_locks()
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(${tempname})
+ *-> unlink_file(${tempname})
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> unlink_file(file1)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file1,v <-- file1
+new revision: delete; previous revision: 1\.2" \
+"
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *callerdat=${PFMT}, argc=1, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *dosrcs=1, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Scratch_Entry(file1)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> rename(CVS/Entries\.Backup,CVS/Entries)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file(CVS/Entries\.Log)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+${CVSROOT_DIRNAME}/trace/file1,v <-- file1
+S -> CVS_SERVER_SLEEP not set\.
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Leaving do_recursion ( frame=${PFMT} )
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/commitinfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/loginfo, trace, ALL)
+S -> Parse_Info (${CVSROOT_DIRNAME}/CVSROOT/verifymsg, trace, not ALL)
+S -> Promotable_Lock ()
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, , , , file1)
+S -> RCS_checkout (${CVSROOT_DIRNAME}/trace/file1,v, 1\.2, , -ko, ${tempname})
+S -> Scratch_Entry(file1)
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs.pfl\.${hostname}\.[0-9][0-9]*)
+S -> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs.rfl\.${hostname}\.[0-9][0-9]*)
+S -> dirswitch (\., ${CVSROOT_DIRNAME}/trace)
+S -> do_cvs_command (commit)
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> do_recursion ( frame=${PFMT} )
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_name (${CVSROOT_DIRNAME}/trace, )
+S -> lock_simple_remove()
+S -> lock_simple_remove()
+S -> lock_tree_promotably (1, argv, 0, 1, 0)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_exists (${CVSROOT_DIRNAME}/trace)
+S -> promotable_lock(${CVSROOT_DIRNAME}/trace)
+S -> rcs_cleanup()
+S -> readers_exist (${CVSROOT_DIRNAME}/trace)
+S -> remove_locks()
+S -> remove_locks()
+S -> remove_locks()
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(${CVSROOT_DIRNAME}/trace/,file1,,${CVSROOT_DIRNAME}/trace/file1,v)
+S -> rename(CVS/Entries\.Backup,CVS/Entries)
+S -> serve_directory (\.)
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_pathname_check (file1)
+S -> server_pathname_check (file1)
+S -> server_pathname_check (file1)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+S -> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> unlink_file(${tempname})
+S -> unlink_file(${tempname})
+S -> unlink_file(CVS/Entries\.Log)
+S -> unlink_file(file1)
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+done
+new revision: delete; previous revision: 1\.2"
+
+ # SGI IRIX seems to have problems with the stdout and stderr
+ # mix for this test, so separate them.
+ dotest_sort trace-19 "${testcvs} -t -t -t history file1 2>stderr19" \
+"O ${ISODATE} ${username} trace =trace= ${TESTDIR}/trace/\*" \
+"O ${ISODATE} ${username} trace =trace= <remote>/\*"
+ dotest_sort trace-19stderr "sort < stderr19" \
+" *-> Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> remove_locks()
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )" \
+"
+ *-> Forking server: ${CVS_SERVER} server
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> do_cvs_command (history)
+S -> remove_locks()
+S -> remove_locks()
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()"
+ rm stderr19
+
+ cd ..
+ dotest_sort \
+trace-20 "echo yes | ${testcvs} -t -t -t release -d trace" \
+" *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *locktype=2, update_preload=(null)
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Lock_Cleanup()
+ *-> Lock_Cleanup()
+ *-> Promotable_Lock ()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> Simple_Lock_Cleanup()
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.pfl\.\*,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> _lock_exists (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.rfl\.\*,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace)
+ *-> lock_dir_for_write (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, #cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, #cvs\.lock)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.pfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.rfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir,
#cvs\.wfl\.${hostname}\.[0-9][0-9]*)
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_name (${CVSROOT_DIRNAME}/trace/subdir, )
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_simple_remove()
+ *-> lock_tree_promotably (0, argv, 0, 1, 0)
+ *-> main loop with CVSROOT=${CVSROOT_DIRNAME}
+ *-> parse_cvsroot ( ${CVSROOT_DIRNAME} )
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> promotable_exists (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> promotable_lock(${CVSROOT_DIRNAME}/trace)
+ *-> promotable_lock(${CVSROOT_DIRNAME}/trace/subdir)
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace)
+ *-> readers_exist (${CVSROOT_DIRNAME}/trace/subdir)
+ *-> remove_locks()
+ *-> remove_locks()
+ *-> run_popen(${testcvs} -n -q -d ${CVSROOT_DIRNAME} update,r)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 0)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace, 1)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 0)
+ *-> set_lock (${CVSROOT_DIRNAME}/trace/subdir, 1)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file_dir(trace)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+Are you sure you want to release (and delete) directory \`trace': *->
start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+You have \[0\] altered files in this repository\." \
+"
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *callerdat=${PFMT}, argc=0, argv=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *direntproc=${PFMT}, dirleavproc=${PFMT},
+ *dosrcs=0, repository_in=(null) )
+ *dosrcs=0, repository_in=(null) )
+ *local=0, which=1, aflag=0,
+ *local=0, which=1, aflag=0,
+ *locktype=0, update_preload=(null)
+ *locktype=0, update_preload=(null)
+ *-> Forking server: ${CVS_SERVER} server
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> Leaving do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> do_recursion ( frame=${PFMT} )
+ *-> main loop with CVSROOT=${CVSROOT}
+ *-> parse_cvsroot ( ${CVSROOT} )
+ *-> run_popen(${testcvs} -n -q -d ${CVSROOT} update,r)
+ *-> start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+ *-> unlink_file_dir(trace)
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+ *-> walklist ( list=${PFMT}, proc=${PFMT}, closure=${PFMT} )
+Are you sure you want to release (and delete) directory \`trace': *->
start_recursion ( fileproc=${PFMT}, filesdoneproc=${PFMT},
+S -> CVS_SERVER_SLEEP not set\.
+S -> Lock_Cleanup()
+S -> Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> Simple_Lock_Cleanup()
+S -> do_cvs_command (release)
+S -> fopen(${CVSROOT_DIRNAME}/CVSROOT/history,a)
+S -> remove_locks()
+S -> remove_locks()
+S -> server_cleanup()
+S -> server_cleanup()
+S -> server_notify()
+S -> server_notify()
+S -> server_notify()
+You have \[0\] altered files in this repository\."
+
+ dokeep
+ cd ..
+ rm -fr trace
+ modify_repo rm -fr $CVSROOT_DIRNAME/trace
+ ;;
+
+
+
+ *)
+ echo $what is not the name of a test -- ignored
+ ;;
+ esac
+
+ # Sanity check sanity.sh. :)
+ #
+ # Test our exit directory so that tests that exit in an incorrect directory
+ # are noticed during single test runs.
+ #
+ # FIXME?
+ # Sparc Solaris 9 is dereferencing paths here as if /bin/pwd were
+ # called when /tmp is a symlink. This might be a new problem with this
+ # test, but since this was recently tested I think it more likely to be
+ # A Solaris issue.
+ if test "x$TESTDIR" != "x`pwd`"; then
+ fail "cleanup: PWD != TESTDIR (\``pwd`' != \`$TESTDIR')"
+ fi
+
+ # Test that the last test didn't overwrite any write proxy configuration
+ # which may be in place.
+ if $proxy; then
+ problem=false
+ for file in \
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT/config \
+ $CVSROOT_DIRNAME/CVSROOT/config \
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT/loginfo \
+ $CVSROOT_DIRNAME/CVSROOT/loginfo \
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT/postadmin \
+ $CVSROOT_DIRNAME/CVSROOT/postadmin \
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT/posttag \
+ $CVSROOT_DIRNAME/CVSROOT/posttag \
+ $SECONDARY_CVSROOT_DIRNAME/CVSROOT/postwatch \
+ $CVSROOT_DIRNAME/CVSROOT/postwatch; do
+ if $diff_u $file $TESTDIR/`basename $file`-clean >>$LOGFILE 2>&1;
then
+ :;
+ else
+ echo "\`$file' and \`$TESTDIR/`basename $file`-clean' differ." \
+ >>$LOGFILE
+ problem=:
+ fi
+ done
+ if $problem; then
+ fail "cleanup: write proxy configuration not preserved"
+ fi
+ fi
+
+ if $remote && test "$servercvs_orig" != "$servercvs" >/dev/null 2>&1; then
+ fail "test slagged \$servercvs"
+ fi
+
+ # Reset val-tags to a pristine state.
+ if test -s $CVSROOT_DIRNAME/CVSROOT/val-tags; then
+ modify_repo ":" > $CVSROOT_DIRNAME/CVSROOT/val-tags
+ fi
+ verify_tmp_empty "post $what"
+
+done # The big loop
+
+# Set up summary data for output.
+skippedoutput=
+warningsoutput=
+extendedinfo=
+if test $skipped -ne 0; then
+ skippedoutput="$skipped test group"
+ if test $skipped -ne 1; then
+ skippedoutput="${skippedoutput}s"
+ fi
+ skippedoutput="$skippedoutput skipped"
+fi
+if test $warnings -ne 0; then
+ warningsoutput="$warnings test"
+ if test $warnings -ne 1; then
+ warningsoutput="${warningsoutput}s"
+ fi
+ warningsoutput="$warningsoutput passed with warnings"
+fi
+if test -n "$skippedoutput" || test -n "$warningsoutput"; then
+ extendedinfo=" ("
+ if test -n "$skippedoutput"; then
+ extendedinfo="$extendedinfo$skippedoutput"
+ fi
+ if test -n "$skippedoutput" && test -n "$warningsoutput"; then
+ extendedinfo="$extendedinfo and "
+ fi
+ if test -n "$warningsoutput"; then
+ extendedinfo="$extendedinfo$warningsoutput"
+ fi
+ extendedinfo="$extendedinfo)"
+fi
+
+echo "OK, all $passed tests passed$extendedinfo."
+
+# TODO:
+# * Test `cvs update -d foo' (where foo does not exist).
+# * Test `cvs update foo bar' (where foo and bar are both from the
+# same directory in the repository). Suppose one is a branch--make
+# sure that both directories get updated with the respective correct
+# thing.
+# * `cvs update ../foo'. Also ../../foo ./../foo foo/../../bar /foo/bar
+# foo/.././../bar foo/../bar etc.
+# * Test all flags in modules file.
+# Test that ciprog gets run both on checkin in that directory, or a
+# higher-level checkin which recurses into it.
+# * Test operations on a directory that contains other directories but has
+# no files of its own.
+# * -t global option
+# * cvs rm followed by cvs add or vice versa (with no checkin in between).
+# * cvs rm twice (should be a nice error message).
+# * -P option to checkout--(a) refrains from checking out new empty dirs,
+# (b) prunes empty dirs already there.
+# * Test that cvs -d `hostname`:${TESTDIR}/non/existent co foo
+# gives an appropriate error (e.g.
+# Cannot access ${TESTDIR}/non-existent/CVSROOT
+# No such file or directory).
+# (like basica-9, but for remote).
+# * Test ability to send notifications in response to watches. (currently
+# hard to test because CVS doesn't send notifications if username is the
+# same).
+# * Test the contents of adm files other than Root and Repository.
+# Entries seems the next most important thing.
+# * Test the following compatibility issues:
+# - The filler fields in "D" entries in CVS/Entries get preserved
+# (per cvs.texinfo).
+# - Unrecognized entry types in CVS/Entries get ignored (looks like
+# this needs to be documented in cvs.texinfo, but is not)
+# - Test that unrecognized files in CVS directories (e.g. CVS/Foobar)
+# are ignored (per cvs.texinfo).
+# - Test 'cvs history' with symlinks in the path to the working directory.
+# - Remove most of the CVS_SERVER stuff after a reasonable amount of time.
+# The "fork" & "client" series of tests should be left. 4/2/00, CVS
+# 1.11.0.1 was altered so that it would default to program_name (set from
+# argv[0]) rather than "cvs", but I'd like this script to work on legacy
+# versions of CVS for awhile.
+# - Testsuite doesn't work with usernames over eight characters in length.
+# Fix it.
+# End of TODO list.
+
+# Exit if keep set
+dokeep
+
+# Remove the test directory, but first change out of it.
+if $TIMING; then
+ echo "exiting without removing test dir in order to preserve timing
information."
+else
+ cd `dirname $TESTDIR`
+ rm -rf $TESTDIR
+fi
+
+# end of sanity.sh
Index: ccvs/src/status.c
diff -u /dev/null ccvs/src/status.c:1.68.8.1
--- /dev/null Tue Jan 17 15:41:25 2006
+++ ccvs/src/status.c Tue Jan 17 15:41:24 2006
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Status Information
+ */
+
+#include "cvs.h"
+
+static Dtype status_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int status_fileproc (void *callerdat, struct file_info *finfo);
+static int tag_list_proc (Node * p, void *closure);
+
+static int local = 0;
+static int long_format = 0;
+static RCSNode *xrcsnode;
+
+static const char *const status_usage[] =
+{
+ "Usage: %s %s [-vlR] [files...]\n",
+ "\t-v\tVerbose format; includes tag information for the file\n",
+ "\t-l\tProcess this directory only (not recursive).\n",
+ "\t-R\tProcess directories recursively.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+int
+cvsstatus (int argc, char **argv)
+{
+ int c;
+ int err = 0;
+
+ if (argc == -1)
+ usage (status_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+vlR")) != -1)
+ {
+ switch (c)
+ {
+ case 'v':
+ long_format = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case '?':
+ default:
+ usage (status_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ wrap_setup ();
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ ign_setup ();
+
+ if (long_format)
+ send_arg("-v");
+ if (local)
+ send_arg("-l");
+ send_arg ("--");
+
+ /* For a while, we tried setting SEND_NO_CONTENTS here so this
+ could be a fast operation. That prevents the
+ server from updating our timestamp if the timestamp is
+ changed but the file is unmodified. Worse, it is user-visible
+ (shows "locally modified" instead of "up to date" if
+ timestamp is changed but file is not). And there is no good
+ workaround (you might not want to run "cvs update"; "cvs -n
+ update" doesn't update CVS/Entries; "cvs diff --brief" or
+ something perhaps could be made to work but somehow that
+ seems nonintuitive to me even if so). Given that timestamps
+ seem to have the potential to get munged for any number of
+ reasons, it seems better to not rely too much on them. */
+
+ send_files (argc, argv, local, 0, 0);
+
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+
+ send_to_server ("status\012", 0);
+ err = get_responses_and_close ();
+
+ return err;
+ }
+#endif
+
+ /* start the recursion processor */
+ err = start_recursion (status_fileproc, NULL, status_dirproc,
+ NULL, NULL, argc, argv, local, W_LOCAL,
+ 0, CVS_LOCK_READ, NULL, 1, NULL);
+
+ return (err);
+}
+
+/*
+ * display the status of a file
+ */
+/* ARGSUSED */
+static int
+status_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Ctype status;
+ char *sstat;
+ Vers_TS *vers;
+ Node *node;
+
+ status = Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0);
+ sstat = "Classify Error";
+ switch (status)
+ {
+ case T_UNKNOWN:
+ sstat = "Unknown";
+ break;
+ case T_CHECKOUT:
+ sstat = "Needs Checkout";
+ break;
+ case T_PATCH:
+ sstat = "Needs Patch";
+ break;
+ case T_CONFLICT:
+ /* FIXME - This message could be clearer. It comes up
+ * when a file exists or has been added in the local sandbox
+ * and a file of the same name has been committed indepenently to
+ * the repository from a different sandbox, as well as when a
+ * timestamp hasn't changed since a merge resulted in conflicts.
+ * It also comes up whether an update has been attempted or not, so
+ * technically, I think the double-add case is not actually a
+ * conflict yet.
+ */
+ sstat = "Unresolved Conflict";
+ break;
+ case T_ADDED:
+ sstat = "Locally Added";
+ break;
+ case T_REMOVED:
+ sstat = "Locally Removed";
+ break;
+ case T_MODIFIED:
+ if (file_has_markers (finfo))
+ sstat = "File had conflicts on merge";
+ else
+ /* Note that we do not re Register() the file when we spot
+ * a resolved conflict like update_fileproc() does on the
+ * premise that status should not alter the sandbox.
+ */
+ sstat = "Locally Modified";
+ break;
+ case T_REMOVE_ENTRY:
+ sstat = "Entry Invalid";
+ break;
+ case T_UPTODATE:
+ sstat = "Up-to-date";
+ break;
+ case T_NEEDS_MERGE:
+ sstat = "Needs Merge";
+ break;
+ case T_TITLE:
+ /* I don't think this case can occur here. Just print
+ "Classify Error". */
+ break;
+ }
+
+ cvs_output ("\
+===================================================================\n", 0);
+ if (vers->ts_user == NULL)
+ {
+ cvs_output ("File: no file ", 0);
+ cvs_output (finfo->file, 0);
+ cvs_output ("\t\tStatus: ", 0);
+ cvs_output (sstat, 0);
+ cvs_output ("\n\n", 0);
+ }
+ else
+ {
+ char *buf;
+ buf = Xasprintf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
+ cvs_output (buf, 0);
+ free (buf);
+ }
+
+ if (vers->vn_user == NULL)
+ {
+ cvs_output (" Working revision:\tNo entry for ", 0);
+ cvs_output (finfo->file, 0);
+ cvs_output ("\n", 0);
+ }
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ cvs_output (" Working revision:\tNew file!\n", 0);
+ else
+ {
+ cvs_output (" Working revision:\t", 0);
+ cvs_output (vers->vn_user, 0);
+
+ /* Only add the UTC timezone if there is a time to use. */
+ if (!server_active && strlen (vers->ts_rcs) > 0)
+ {
+ /* Convert from the asctime() format to ISO 8601 */
+ char *buf;
+
+ cvs_output ("\t", 0);
+
+ /* Allow conversion from CVS/Entries asctime() to ISO 8601 */
+ buf = Xasprintf ("%s UTC", vers->ts_rcs);
+ cvs_output_tagged ("date", buf);
+ free (buf);
+ }
+ cvs_output ("\n", 0);
+ }
+
+ if (vers->vn_rcs == NULL)
+ cvs_output (" Repository revision:\tNo revision control file\n", 0);
+ else
+ {
+ cvs_output (" Repository revision:\t", 0);
+ cvs_output (vers->vn_rcs, 0);
+ cvs_output ("\t", 0);
+ cvs_output (vers->srcfile->print_path, 0);
+ cvs_output ("\n", 0);
+
+ node = findnode(vers->srcfile->versions,vers->vn_rcs);
+ if (node)
+ {
+ RCSVers *v;
+ v=(RCSVers*)node->data;
+ node = findnode(v->other_delta,"commitid");
+ cvs_output(" Commit Identifier:\t", 0);
+ if(node && node->data)
+ cvs_output(node->data, 0);
+ else
+ cvs_output("(none)",0);
+ cvs_output("\n",0);
+ }
+ }
+
+ if (vers->entdata)
+ {
+ Entnode *edata;
+
+ edata = vers->entdata;
+ if (edata->tag)
+ {
+ if (vers->vn_rcs == NULL)
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output (" - MISSING from RCS file!\n", 0);
+ }
+ else
+ {
+ if (isdigit ((unsigned char) edata->tag[0]))
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output ("\n", 0);
+ }
+ else
+ {
+ char *branch = NULL;
+
+ if (RCS_nodeisbranch (finfo->rcs, edata->tag))
+ branch = RCS_whatbranch (finfo->rcs, edata->tag);
+
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output (" (", 0);
+ cvs_output (branch ? "branch" : "revision", 0);
+ cvs_output (": ", 0);
+ cvs_output (branch ? branch : vers->vn_rcs, 0);
+ cvs_output (")\n", 0);
+
+ if (branch)
+ free (branch);
+ }
+ }
+ }
+ else if (!really_quiet)
+ cvs_output (" Sticky Tag:\t\t(none)\n", 0);
+
+ if (edata->date)
+ {
+ cvs_output (" Sticky Date:\t\t", 0);
+ cvs_output (edata->date, 0);
+ cvs_output ("\n", 0);
+ }
+ else if (!really_quiet)
+ cvs_output (" Sticky Date:\t\t(none)\n", 0);
+
+ if (edata->options && edata->options[0])
+ {
+ cvs_output (" Sticky Options:\t", 0);
+ cvs_output (edata->options, 0);
+ cvs_output ("\n", 0);
+ }
+ else if (!really_quiet)
+ cvs_output (" Sticky Options:\t(none)\n", 0);
+ }
+
+ if (long_format && vers->srcfile)
+ {
+ List *symbols = RCS_symbols(vers->srcfile);
+
+ cvs_output ("\n Existing Tags:\n", 0);
+ if (symbols)
+ {
+ xrcsnode = finfo->rcs;
+ (void) walklist (symbols, tag_list_proc, NULL);
+ }
+ else
+ cvs_output ("\tNo Tags Exist\n", 0);
+ }
+
+ cvs_output ("\n", 0);
+ freevers_ts (&vers);
+ return (0);
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+status_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+ return (R_PROCESS);
+}
+
+
+
+/*
+ * Print out a tag and its type
+ */
+static int
+tag_list_proc (Node *p, void *closure)
+{
+ char *branch = NULL;
+ char *buf;
+
+ if (RCS_nodeisbranch (xrcsnode, p->key))
+ branch = RCS_whatbranch (xrcsnode, p->key);
+
+ buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key,
+ branch ? "branch" : "revision",
+ branch ? branch : (char *)p->data);
+ cvs_output (buf, 0);
+ free (buf);
+
+ if (branch)
+ free (branch);
+
+ return (0);
+}
Index: ccvs/src/tag.c
diff -u /dev/null ccvs/src/tag.c:1.142.8.1
--- /dev/null Tue Jan 17 15:41:25 2006
+++ ccvs/src/tag.c Tue Jan 17 15:41:24 2006
@@ -0,0 +1,1717 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Tag and Rtag
+ *
+ * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
+ * Tag uses the checked out revision in the current directory, rtag uses
+ * the modules database, if necessary.
+ */
+
+#include "cvs.h"
+#include "save-cwd.h"
+
+static int rtag_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+static int check_fileproc (void *callerdat, struct file_info *finfo);
+static int check_filesdoneproc (void *callerdat, int err,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int pretag_proc (const char *_repository, const char *_filter,
+ void *_closure);
+static void masterlist_delproc (Node *_p);
+static void tag_delproc (Node *_p);
+static int pretag_list_to_args_proc (Node *_p, void *_closure);
+
+static Dtype tag_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int rtag_fileproc (void *callerdat, struct file_info *finfo);
+static int rtag_delete (RCSNode *rcsfile);
+static int tag_fileproc (void *callerdat, struct file_info *finfo);
+
+static char *numtag; /* specific revision to tag */
+static bool numtag_validated = false;
+static char *date = NULL;
+static char *symtag; /* tag to add or delete */
+static bool delete_flag; /* adding a tag by default */
+static bool branch_mode; /* make an automagic "branch" tag */
+static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags
*/
+static bool force_tag_match = true; /* force tag to match by default */
+static bool force_tag_move; /* don't force tag to move by default */
+static bool check_uptodate; /* no uptodate-check by default */
+static bool attic_too; /* remove tag from Attic files */
+static bool is_rtag;
+
+struct tag_info
+{
+ Ctype status;
+ char *oldrev;
+ char *rev;
+ char *tag;
+ char *options;
+};
+
+struct master_lists
+{
+ List *tlist;
+};
+
+static List *mtlist;
+
+static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
+static const char *const rtag_usage[] =
+{
+ "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
+ "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
+ "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
+ "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
+ "\t-d\tDelete the given tag.\n",
+ "\t-F\tMove tag if it already exists.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive.\n",
+ "\t-n\tNo execution of 'tag program'.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-r rev\tExisting revision/tag.\n",
+ "\t-D\tExisting date.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+static const char tag_opts[] = "+BbcdFflQqRr:D:";
+static const char *const tag_usage[] =
+{
+ "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
+ "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
+ "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
+ "\t-c\tCheck that working files are unmodified.\n",
+ "\t-d\tDelete the given tag.\n",
+ "\t-F\tMove tag if it already exists.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-r rev\tExisting revision/tag.\n",
+ "\t-D\tExisting date.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+
+
+int
+cvstag (int argc, char **argv)
+{
+ bool local = false; /* recursive by default */
+ int c;
+ int err = 0;
+ bool run_module_prog = true;
+
+ is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
+
+ if (argc == -1)
+ usage (is_rtag ? rtag_usage : tag_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ attic_too = true;
+ break;
+ case 'b':
+ branch_mode = true;
+ break;
+ case 'B':
+ disturb_branch_tags = true;
+ break;
+ case 'c':
+ check_uptodate = true;
+ break;
+ case 'd':
+ delete_flag = true;
+ break;
+ case 'F':
+ force_tag_move = true;
+ break;
+ case 'f':
+ force_tag_match = false;
+ break;
+ case 'l':
+ local = true;
+ break;
+ case 'n':
+ run_module_prog = false;
+ break;
+ case 'Q':
+ case 'q':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_name);
+ break;
+ case 'R':
+ local = false;
+ break;
+ case 'r':
+ parse_tagdate (&numtag, &date, optarg);
+ break;
+ case 'D':
+ if (date) free (date);
+ date = Make_Date (optarg);
+ break;
+ case '?':
+ default:
+ usage (is_rtag ? rtag_usage : tag_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < (is_rtag ? 2 : 1))
+ usage (is_rtag ? rtag_usage : tag_usage);
+ symtag = argv[0];
+ argc--;
+ argv++;
+
+ if (date && delete_flag)
+ error (1, 0, "-d makes no sense with a date specification.");
+ if (delete_flag && branch_mode)
+ error (0, 0, "warning: -b ignored with -d options");
+ RCS_check_tag (symtag);
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ /* We're the client side. Fire up the remote server. */
+ start_server ();
+
+ ign_setup ();
+
+ if (attic_too)
+ send_arg ("-a");
+ if (branch_mode)
+ send_arg ("-b");
+ if (disturb_branch_tags)
+ send_arg ("-B");
+ if (check_uptodate)
+ send_arg ("-c");
+ if (delete_flag)
+ send_arg ("-d");
+ if (force_tag_move)
+ send_arg ("-F");
+ if (!force_tag_match)
+ send_arg ("-f");
+ if (local)
+ send_arg ("-l");
+ if (!run_module_prog)
+ send_arg ("-n");
+
+ if (numtag)
+ option_with_arg ("-r", numtag);
+ if (date)
+ client_senddate (date);
+
+ send_arg ("--");
+
+ send_arg (symtag);
+
+ if (is_rtag)
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ send_to_server ("rtag\012", 0);
+ }
+ else
+ {
+ send_files (argc, argv, local, 0,
+
+ /* I think the -c case is like "cvs status", in
+ which we really better be correct rather than
+ being fast; it is just too confusing otherwise. */
+ check_uptodate ? 0 : SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("tag\012", 0);
+ }
+
+ return get_responses_and_close ();
+ }
+#endif
+
+ if (is_rtag)
+ {
+ DBM *db;
+ int i;
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ {
+ /* XXX last arg should be repository, but doesn't make sense here */
+ history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
+ (date ? date : "A"))), symtag, argv[i], "");
+ err += do_module (db, argv[i], TAG,
+ delete_flag ? "Untagging" : "Tagging",
+ rtag_proc, NULL, 0, local, run_module_prog,
+ 0, symtag);
+ }
+ close_module (db);
+ }
+ else
+ {
+ err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
+ NULL);
+ }
+
+ return err;
+}
+
+
+
+struct pretag_proc_data {
+ List *tlist;
+ bool delete_flag;
+ bool force_tag_move;
+ char *symtag;
+};
+
+/*
+ * called from Parse_Info, this routine processes a line that came out
+ * of the posttag file and turns it into a command and executes it.
+ *
+ * RETURNS
+ * the absolute value of the return value of run_exec, which may or
+ * may not be the return value of the child process. this is
+ * contrained to return positive values because Parse_Info is summing
+ * return values and testing for non-zeroness to signify one or more
+ * of its callbacks having returned an error.
+ */
+static int
+posttag_proc (const char *repository, const char *filter, void *closure)
+{
+ char *cmdline;
+ const char *srepos = Short_Repository (repository);
+ struct pretag_proc_data *ppd = closure;
+
+ /* %t = tag being added/moved/removed
+ * %o = operation = "add" | "mov" | "del"
+ * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
+ * | "N" (not branch)
+ * %c = cvs_cmd_name
+ * %p = path from $CVSROOT
+ * %r = path from root
+ * %{sVv} = attribute list = file name, old version tag will be deleted
+ * from, new version tag will be added to (or
+ * deleted from until
+ * SUPPORT_OLD_INFO_FMT_STRINGS is undefined).
+ */
+ /*
+ * Cast any NULL arguments as appropriate pointers as this is an
+ * stdarg function and we need to be certain the caller gets what
+ * is expected.
+ */
+ cmdline = format_cmdline (
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ false, srepos,
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ filter,
+ "t", "s", ppd->symtag,
+ "o", "s", ppd->delete_flag
+ ? "del" : ppd->force_tag_move ? "mov" : "add",
+ "b", "c", delete_flag
+ ? '?' : branch_mode ? 'T' : 'N',
+ "c", "s", cvs_cmd_name,
+#ifdef SERVER_SUPPORT
+ "R", "s", referrer ? referrer->original : "NONE",
+#endif /* SERVER_SUPPORT */
+ "p", "s", srepos,
+ "r", "s", current_parsed_root->directory,
+ "sVv", ",", ppd->tlist,
+ pretag_list_to_args_proc, (void *) NULL,
+ (char *) NULL);
+
+ if (!cmdline || !strlen (cmdline))
+ {
+ if (cmdline) free (cmdline);
+ error (0, 0, "pretag proc resolved to the empty string!");
+ return 1;
+ }
+
+ run_setup (cmdline);
+
+ free (cmdline);
+ return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
+}
+
+
+
+/*
+ * Call any postadmin procs.
+ */
+static int
+tag_filesdoneproc (void *callerdat, int err, const char *repository,
+ const char *update_dir, List *entries)
+{
+ Node *p;
+ List *mtlist, *tlist;
+ struct pretag_proc_data ppd;
+
+ TRACE (TRACE_FUNCTION, "tag_filesdoneproc (%d, %s, %s)", err, repository,
+ update_dir);
+
+ mtlist = callerdat;
+ p = findnode (mtlist, update_dir);
+ if (p != NULL)
+ tlist = ((struct master_lists *) p->data)->tlist;
+ else
+ tlist = NULL;
+ if (tlist == NULL || tlist->list->next == tlist->list)
+ return err;
+
+ ppd.tlist = tlist;
+ ppd.delete_flag = delete_flag;
+ ppd.force_tag_move = force_tag_move;
+ ppd.symtag = symtag;
+ Parse_Info (CVSROOTADM_POSTTAG, repository, posttag_proc,
+ PIOPT_ALL, &ppd);
+
+ return err;
+}
+
+
+
+/*
+ * callback proc for doing the real work of tagging
+ */
+/* ARGSUSED */
+static int
+rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
+ int shorten, int local_specified, char *mname, char *msg)
+{
+ /* Begin section which is identical to patch_proc--should this
+ be abstracted out somehow? */
+ char *myargv[2];
+ int err = 0;
+ int which;
+ char *repository;
+ char *where;
+
+#ifdef HAVE_PRINTF_PTR
+ TRACE (TRACE_FUNCTION,
+ "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
+ " mwhere=%s, mfile=%s, shorten=%d,\n"
+ " local_specified=%d, mname=%s, msg=%s)",
+ argc, (void *)argv, xwhere ? xwhere : "(null)",
+ mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
+ shorten, local_specified,
+ mname ? mname : "(null)", msg ? msg : "(null)" );
+#else
+ TRACE (TRACE_FUNCTION,
+ "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
+ " mwhere=%s, mfile=%s, shorten=%d,\n"
+ " local_specified=%d, mname=%s, msg=%s )",
+ argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
+ mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
+ shorten, local_specified,
+ mname ? mname : "(null)", msg ? msg : "(null)" );
+#endif
+
+ if (is_rtag)
+ {
+ repository = xmalloc (strlen (current_parsed_root->directory)
+ + strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1)
+ + 2);
+ (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
+ argv[0]);
+ where = xmalloc (strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1)
+ + 1);
+ (void) strcpy (where, argv[0]);
+
+ /* If MFILE isn't null, we need to set up to do only part of the
+ * module.
+ */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char *path;
+
+ /* If the portion of the module is a path, put the dir part on
+ * REPOS.
+ */
+ if ((cp = strrchr (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ path = xmalloc (strlen (repository) + strlen (mfile) + 5);
+ (void) sprintf (path, "%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void) strcpy (repository, path);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ }
+ else
+ {
+ myargv[0] = argv[0];
+ myargv[1] = mfile;
+ argc = 2;
+ argv = myargv;
+ }
+ free (path);
+ }
+
+ /* cd to the starting repository */
+ if (CVS_CHDIR (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (repository);
+ free (where);
+ return 1;
+ }
+ /* End section which is identical to patch_proc. */
+
+ if (delete_flag || attic_too || (force_tag_match && numtag))
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+ }
+ else
+ {
+ where = NULL;
+ which = W_LOCAL;
+ repository = "";
+ }
+
+ if (numtag != NULL && !numtag_validated)
+ {
+ tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
+ repository, false);
+ numtag_validated = true;
+ }
+
+ /* check to make sure they are authorized to tag all the
+ specified files in the repository */
+
+ mtlist = getlist ();
+ err = start_recursion (check_fileproc, check_filesdoneproc,
+ NULL, NULL, NULL,
+ argc - 1, argv + 1, local_specified, which, 0,
+ CVS_LOCK_READ, where, 1, repository);
+
+ if (err)
+ {
+ error (1, 0, "correct the above errors first!");
+ }
+
+ /* It would be nice to provide consistency with respect to
+ commits; however CVS lacks the infrastructure to do that (see
+ Concurrency in cvs.texinfo and comment in do_recursion). */
+
+ /* start the recursion processor */
+ err = start_recursion
+ (is_rtag ? rtag_fileproc : tag_fileproc,
+ tag_filesdoneproc, tag_dirproc, NULL, mtlist, argc - 1, argv + 1,
+ local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
+ repository);
+ dellist (&mtlist);
+ if (which & W_REPOS) free (repository);
+ if (where != NULL)
+ free (where);
+ return err;
+}
+
+
+
+/* check file that is to be tagged */
+/* All we do here is add it to our list */
+static int
+check_fileproc (void *callerdat, struct file_info *finfo)
+{
+ const char *xdir;
+ Node *p;
+ Vers_TS *vers;
+ List *tlist;
+ struct tag_info *ti;
+ int addit = 1;
+
+ TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
+ finfo->repository ? finfo->repository : "(null)",
+ finfo->fullname ? finfo->fullname : "(null)",
+ finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
+ : "NULL");
+
+ if (check_uptodate)
+ {
+ switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
+ {
+ case T_UPTODATE:
+ case T_CHECKOUT:
+ case T_PATCH:
+ case T_REMOVE_ENTRY:
+ break;
+ case T_UNKNOWN:
+ case T_CONFLICT:
+ case T_NEEDS_MERGE:
+ case T_MODIFIED:
+ case T_ADDED:
+ case T_REMOVED:
+ default:
+ error (0, 0, "%s is locally modified", finfo->fullname);
+ freevers_ts (&vers);
+ return 1;
+ }
+ }
+ else
+ vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
+
+ if (finfo->update_dir[0] == '\0')
+ xdir = ".";
+ else
+ xdir = finfo->update_dir;
+ if ((p = findnode (mtlist, xdir)) != NULL)
+ {
+ tlist = ((struct master_lists *) p->data)->tlist;
+ }
+ else
+ {
+ struct master_lists *ml;
+
+ tlist = getlist ();
+ p = getnode ();
+ p->key = xstrdup (xdir);
+ p->type = UPDATE;
+ ml = xmalloc (sizeof (struct master_lists));
+ ml->tlist = tlist;
+ p->data = ml;
+ p->delproc = masterlist_delproc;
+ (void) addnode (mtlist, p);
+ }
+ /* do tlist */
+ p = getnode ();
+ p->key = xstrdup (finfo->file);
+ p->type = UPDATE;
+ p->delproc = tag_delproc;
+ if (vers->srcfile == NULL)
+ {
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", finfo->file);
+ freevers_ts (&vers);
+ freenode (p);
+ return 1;
+ }
+
+ /* Here we duplicate the calculation in tag_fileproc about which
+ version we are going to tag. There probably are some subtle races
+ (e.g. numtag is "foo" which gets moved between here and
+ tag_fileproc). */
+ p->data = ti = xmalloc (sizeof (struct tag_info));
+ ti->tag = xstrdup (numtag ? numtag : vers->tag);
+ if (!is_rtag && numtag == NULL && date == NULL)
+ ti->rev = xstrdup (vers->vn_user);
+ else
+ {
+ char *tmp;
+ if (RCS_is_relative (numtag))
+ {
+ tmp = Version_resolve_relTag (finfo, numtag, !is_rtag);
+ if (!tmp)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.", numtag);
+ return 1;
+ }
+ }
+ else
+ {
+ tmp = xstrdup (numtag);
+ }
+ ti->rev = RCS_getversion (vers->srcfile, tmp, date,
+ force_tag_match, NULL);
+ free (tmp);
+ }
+ if (ti->rev != NULL)
+ {
+ ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
+
+ if (ti->oldrev == NULL)
+ {
+ if (delete_flag)
+ {
+ /* Deleting a tag which did not exist is a noop and
+ should not be logged. */
+ addit = 0;
+ }
+ }
+ else if (delete_flag)
+ {
+ free (ti->rev);
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ /* a hack since %v used to mean old or new rev */
+ ti->rev = xstrdup (ti->oldrev);
+#else /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ ti->rev = NULL;
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ }
+ else if (strcmp(ti->oldrev, p->data) == 0)
+ addit = 0;
+ else if (!force_tag_move)
+ addit = 0;
+ }
+ else
+ addit = 0;
+ if (!addit)
+ {
+ free(p->data);
+ p->data = NULL;
+ }
+ freevers_ts (&vers);
+ (void)addnode (tlist, p);
+ return 0;
+}
+
+
+
+static int
+check_filesdoneproc (void *callerdat, int err, const char *repos,
+ const char *update_dir, List *entries)
+{
+ int n;
+ Node *p;
+ List *tlist;
+ struct pretag_proc_data ppd;
+
+ p = findnode (mtlist, update_dir);
+ if (p != NULL)
+ tlist = ((struct master_lists *) p->data)->tlist;
+ else
+ tlist = NULL;
+ if (tlist == NULL || tlist->list->next == tlist->list)
+ return err;
+
+ ppd.tlist = tlist;
+ ppd.delete_flag = delete_flag;
+ ppd.force_tag_move = force_tag_move;
+ ppd.symtag = symtag;
+ if ((n = Parse_Info (CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL,
+ &ppd)) > 0)
+ {
+ error (0, 0, "Pre-tag check failed");
+ err += n;
+ }
+ return err;
+}
+
+
+
+/*
+ * called from Parse_Info, this routine processes a line that came out
+ * of a taginfo file and turns it into a command and executes it.
+ *
+ * RETURNS
+ * the absolute value of the return value of run_exec, which may or
+ * may not be the return value of the child process. this is
+ * contrained to return positive values because Parse_Info is adding up
+ * return values and testing for non-zeroness to signify one or more
+ * of its callbacks having returned an error.
+ */
+static int
+pretag_proc (const char *repository, const char *filter, void *closure)
+{
+ char *newfilter = NULL;
+ char *cmdline;
+ const char *srepos = Short_Repository (repository);
+ struct pretag_proc_data *ppd = closure;
+
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ if (!strchr (filter, '%'))
+ {
+ error (0,0,
+ "warning: taginfo line contains no format strings:\n"
+ " \"%s\"\n"
+ "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be
aware that this\n"
+ "usage is deprecated.", filter);
+ newfilter = xmalloc (strlen (filter) + 16);
+ strcpy (newfilter, filter);
+ strcat (newfilter, " %t %o %p %{sv}");
+ filter = newfilter;
+ }
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+
+ /* %t = tag being added/moved/removed
+ * %o = operation = "add" | "mov" | "del"
+ * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
+ * | "N" (not branch)
+ * %c = cvs_cmd_name
+ * %p = path from $CVSROOT
+ * %r = path from root
+ * %{sVv} = attribute list = file name, old version tag will be deleted
+ * from, new version tag will be added to (or
+ * deleted from until
+ * SUPPORT_OLD_INFO_FMT_STRINGS is undefined)
+ */
+ /*
+ * Cast any NULL arguments as appropriate pointers as this is an
+ * stdarg function and we need to be certain the caller gets what
+ * is expected.
+ */
+ cmdline = format_cmdline (
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ false, srepos,
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ filter,
+ "t", "s", ppd->symtag,
+ "o", "s", ppd->delete_flag ? "del" :
+ ppd->force_tag_move ? "mov" : "add",
+ "b", "c", delete_flag
+ ? '?' : branch_mode ? 'T' : 'N',
+ "c", "s", cvs_cmd_name,
+#ifdef SERVER_SUPPORT
+ "R", "s", referrer ? referrer->original : "NONE",
+#endif /* SERVER_SUPPORT */
+ "p", "s", srepos,
+ "r", "s", current_parsed_root->directory,
+ "sVv", ",", ppd->tlist,
+ pretag_list_to_args_proc, (void *) NULL,
+ (char *) NULL);
+
+ if (newfilter) free (newfilter);
+
+ if (!cmdline || !strlen (cmdline))
+ {
+ if (cmdline) free (cmdline);
+ error (0, 0, "pretag proc resolved to the empty string!");
+ return 1;
+ }
+
+ run_setup (cmdline);
+
+ /* FIXME - the old code used to run the following here:
+ *
+ * if (!isfile(s))
+ * {
+ * error (0, errno, "cannot find pre-tag filter '%s'", s);
+ * free(s);
+ * return (1);
+ * }
+ *
+ * not sure this is really necessary. it might give a little finer grained
+ * error than letting the execution attempt fail but i'm not sure. in any
+ * case it should be easy enough to add a function in run.c to test its
+ * first arg for fileness & executability.
+ */
+
+ free (cmdline);
+ return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
+}
+
+
+
+static void
+masterlist_delproc (Node *p)
+{
+ struct master_lists *ml = p->data;
+
+ dellist (&ml->tlist);
+ free (ml);
+ return;
+}
+
+
+
+static void
+tag_delproc (Node *p)
+{
+ struct tag_info *ti;
+ if (p->data)
+ {
+ ti = (struct tag_info *) p->data;
+ if (ti->oldrev) free (ti->oldrev);
+ if (ti->rev) free (ti->rev);
+ free (ti->tag);
+ free (p->data);
+ p->data = NULL;
+ }
+ return;
+}
+
+
+
+/* to be passed into walklist with a list of tags
+ * p->key = tagname
+ * p->data = struct tag_info *
+ * p->data->oldrev = rev tag will be deleted from
+ * p->data->rev = rev tag will be added to
+ * p->data->tag = tag oldrev is attached to, if any
+ *
+ * closure will be a struct format_cmdline_walklist_closure
+ * where closure is undefined
+ */
+static int
+pretag_list_to_args_proc (Node *p, void *closure)
+{
+ struct tag_info *taginfo = (struct tag_info *)p->data;
+ struct format_cmdline_walklist_closure *c =
+ (struct format_cmdline_walklist_closure *)closure;
+ char *arg = NULL;
+ const char *f;
+ char *d;
+ size_t doff;
+
+ if (!p->data) return 1;
+
+ f = c->format;
+ d = *c->d;
+ /* foreach requested attribute */
+ while (*f)
+ {
+ switch (*f++)
+ {
+ case 's':
+ arg = p->key;
+ break;
+ case 'T':
+ arg = taginfo->tag ? taginfo->tag : "";
+ break;
+ case 'v':
+ arg = taginfo->rev ? taginfo->rev : "NONE";
+ break;
+ case 'V':
+ arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
+ break;
+ default:
+ error(1,0,
+ "Unknown format character or not a list attribute: %c",
+ f[-1]);
+ break;
+ }
+ /* copy the attribute into an argument */
+ if (c->quotes)
+ {
+ arg = cmdlineescape (c->quotes, arg);
+ }
+ else
+ {
+ arg = cmdlinequote ('"', arg);
+ }
+
+ doff = d - *c->buf;
+ expand_string (c->buf, c->length, doff + strlen (arg));
+ d = *c->buf + doff;
+ strncpy (d, arg, strlen (arg));
+ d += strlen (arg);
+
+ free (arg);
+
+ /* and always put the extra space on. we'll have to back up a char
when we're
+ * done, but that seems most efficient
+ */
+ doff = d - *c->buf;
+ expand_string (c->buf, c->length, doff + 1);
+ d = *c->buf + doff;
+ *d++ = ' ';
+ }
+ /* correct our original pointer into the buff */
+ *c->d = d;
+ return 0;
+}
+
+
+/*
+ * Called to rtag a particular file, as appropriate with the options that were
+ * set above.
+ */
+/* ARGSUSED */
+static int
+rtag_fileproc (void *callerdat, struct file_info *finfo)
+{
+ RCSNode *rcsfile;
+ char *version = NULL, *rev = NULL;
+ int retcode = 0;
+ int retval = 0;
+ static bool valtagged = false;
+
+ /* find the parsed RCS data */
+ if ((rcsfile = finfo->rcs) == NULL)
+ {
+ retval = 1;
+ goto free_vars_and_return;
+ }
+
+ /*
+ * For tagging an RCS file which is a symbolic link, you'd best be
+ * running with RCS 5.6, since it knows how to handle symbolic links
+ * correctly without breaking your link!
+ */
+
+ if (delete_flag)
+ {
+ retval = rtag_delete (rcsfile);
+ goto free_vars_and_return;
+ }
+
+ /*
+ * If we get here, we are adding a tag. But, if -a was specified, we
+ * need to check to see if a -r or -D option was specified. If neither
+ * was specified and the file is in the Attic, remove the tag.
+ */
+ if (attic_too && (!numtag && !date))
+ {
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ {
+ retval = rtag_delete (rcsfile);
+ goto free_vars_and_return;
+ }
+ }
+ version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
+ if (version == NULL)
+ {
+ /* If -a specified, clean up any old tags */
+ if (attic_too)
+ (void)rtag_delete (rcsfile);
+
+ if (!quiet && !force_tag_match)
+ {
+ error (0, 0, "cannot find tag `%s' in `%s'",
+ numtag ? numtag : "head", rcsfile->path);
+ retval = 1;
+ }
+ goto free_vars_and_return;
+ }
+ if (numtag
+ && isdigit ((unsigned char)*numtag)
+ && strcmp (numtag, version) != 0)
+ {
+
+ /*
+ * We didn't find a match for the numeric tag that was specified, but
+ * that's OK. just pass the numeric tag on to rcs, to be tagged as
+ * specified. Could get here if one tried to tag "1.1.1" and there
+ * was a 1.1.1 branch with some head revision. In this case, we want
+ * the tag to reference "1.1.1" and not the revision at the head of
+ * the branch. Use a symbolic tag for that.
+ */
+ rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
+ retcode = RCS_settag(rcsfile, symtag, numtag);
+ if (retcode == 0)
+ RCS_rewrite (rcsfile, NULL, NULL);
+ }
+ else
+ {
+ char *oversion;
+
+ /*
+ * As an enhancement for the case where a tag is being re-applied to
+ * a large body of a module, make one extra call to RCS_getversion to
+ * see if the tag is already set in the RCS file. If so, check to
+ * see if it needs to be moved. If not, do nothing. This will
+ * likely save a lot of time when simply moving the tag to the
+ * "current" head revisions of a module -- which I have found to be a
+ * typical tagging operation.
+ */
+ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
+ oversion = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
+ if (oversion != NULL)
+ {
+ int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
+
+ /*
+ * if versions the same and neither old or new are branches don't
+ * have to do anything
+ */
+ if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
+ {
+ free (oversion);
+ goto free_vars_and_return;
+ }
+
+ if (!force_tag_move)
+ {
+ /* we're NOT going to move the tag */
+ (void)printf ("W %s", finfo->fullname);
+
+ (void)printf (" : %s already exists on %s %s",
+ symtag, isbranch ? "branch" : "version",
+ oversion);
+ (void)printf (" : NOT MOVING tag to %s %s\n",
+ branch_mode ? "branch" : "version", rev);
+ free (oversion);
+ goto free_vars_and_return;
+ }
+ else /* force_tag_move is set and... */
+ if ((isbranch && !disturb_branch_tags) ||
+ (!isbranch && disturb_branch_tags))
+ {
+ error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
+ finfo->fullname,
+ isbranch ? "branch" : "non-branch",
+ symtag, oversion, rev,
+ isbranch ? "" : " due to `-B' option");
+ free (oversion);
+ goto free_vars_and_return;
+ }
+ free (oversion);
+ }
+ retcode = RCS_settag (rcsfile, symtag, rev);
+ if (retcode == 0)
+ RCS_rewrite (rcsfile, NULL, NULL);
+ }
+
+ if (retcode != 0)
+ {
+ error (1, retcode == -1 ? errno : 0,
+ "failed to set tag `%s' to revision `%s' in `%s'",
+ symtag, rev, rcsfile->path);
+ retval = 1;
+ goto free_vars_and_return;
+ }
+
+free_vars_and_return:
+ if (branch_mode && rev) free (rev);
+ if (version) free (version);
+ if (!delete_flag && !retval && !valtagged)
+ {
+ tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
+ valtagged = true;
+ }
+ return retval;
+}
+
+
+
+/*
+ * If -d is specified, "force_tag_match" is set, so that this call to
+ * RCS_getversion() will return a NULL version string if the symbolic
+ * tag does not exist in the RCS file.
+ *
+ * If the -r flag was used, numtag is set, and we only delete the
+ * symtag from files that have numtag.
+ *
+ * This is done here because it's MUCH faster than just blindly calling
+ * "rcs" to remove the tag... trust me.
+ */
+static int
+rtag_delete (RCSNode *rcsfile)
+{
+ char *version;
+ int retcode, isbranch;
+
+ if (numtag)
+ {
+ version = RCS_getversion (rcsfile, numtag, NULL, 1, NULL);
+ if (version == NULL)
+ return (0);
+ free (version);
+ }
+
+ version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
+ if (version == NULL)
+ return 0;
+ free (version);
+
+
+ isbranch = RCS_nodeisbranch (rcsfile, symtag);
+ if ((isbranch && !disturb_branch_tags) ||
+ (!isbranch && disturb_branch_tags))
+ {
+ if (!quiet)
+ error (0, 0,
+ "Not removing %s tag `%s' from `%s'%s.",
+ isbranch ? "branch" : "non-branch",
+ symtag, rcsfile->path,
+ isbranch ? "" : " due to `-B' option");
+ return 1;
+ }
+
+ if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", symtag,
+ rcsfile->path);
+ return 1;
+ }
+ RCS_rewrite (rcsfile, NULL, NULL);
+ return 0;
+}
+
+
+
+/*
+ * Called to tag a particular file (the currently checked out version is
+ * tagged with the specified tag - or the specified tag is deleted).
+ */
+/* ARGSUSED */
+static int
+tag_fileproc (void *callerdat, struct file_info *finfo)
+{
+ char *version, *oversion;
+ char *nversion = NULL;
+ char *rev;
+ Vers_TS *vers;
+ int retcode = 0;
+ int retval = 0;
+ static bool valtagged = false;
+
+ vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
+
+ if (numtag || date)
+ {
+ char *tmp;
+ if (RCS_is_relative (numtag))
+ {
+ tmp = Version_resolve_relTag (finfo, numtag, true);
+ if (!tmp)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.", numtag);
+ return 1;
+ }
+ }
+ else
+ {
+ tmp = xstrdup (numtag);
+ }
+ nversion = RCS_getversion (vers->srcfile, tmp, date,
+ force_tag_match, NULL);
+ free (tmp);
+ if (!nversion)
+ goto free_vars_and_return;
+ }
+ if (delete_flag)
+ {
+
+ int isbranch;
+ /*
+ * If -d is specified, "force_tag_match" is set, so that this call to
+ * RCS_getversion() will return a NULL version string if the symbolic
+ * tag does not exist in the RCS file.
+ *
+ * This is done here because it's MUCH faster than just blindly calling
+ * "rcs" to remove the tag... trust me.
+ */
+
+ version = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
+ if (version == NULL || vers->srcfile == NULL)
+ goto free_vars_and_return;
+
+ free (version);
+
+ isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
+ if ((isbranch && !disturb_branch_tags) ||
+ (!isbranch && disturb_branch_tags))
+ {
+ if (!quiet)
+ error(0, 0,
+ "Not removing %s tag `%s' from `%s'%s.",
+ isbranch ? "branch" : "non-branch",
+ symtag, vers->srcfile->path,
+ isbranch ? "" : " due to `-B' option");
+ retval = 1;
+ goto free_vars_and_return;
+ }
+
+ if ((retcode = RCS_deltag (vers->srcfile, symtag)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag %s from %s", symtag,
+ vers->srcfile->path);
+ retval = 1;
+ goto free_vars_and_return;
+ }
+ RCS_rewrite (vers->srcfile, NULL, NULL);
+
+ /* warm fuzzies */
+ if (!really_quiet)
+ {
+ cvs_output ("D ", 2);
+ cvs_output (finfo->fullname, 0);
+ cvs_output ("\n", 1);
+ }
+
+ goto free_vars_and_return;
+ }
+
+ /*
+ * If we are adding a tag, we need to know which version we have checked
+ * out and we'll tag that version.
+ */
+ if (!nversion)
+ version = vers->vn_user;
+ else
+ version = nversion;
+ if (!version)
+ goto free_vars_and_return;
+ else if (strcmp (version, "0") == 0)
+ {
+ if (!quiet)
+ error (0, 0, "couldn't tag added but un-commited file `%s'",
+ finfo->file);
+ goto free_vars_and_return;
+ }
+ else if (version[0] == '-')
+ {
+ if (!quiet)
+ error (0, 0, "skipping removed but un-commited file `%s'",
+ finfo->file);
+ goto free_vars_and_return;
+ }
+ else if (vers->srcfile == NULL)
+ {
+ if (!quiet)
+ error (0, 0, "cannot find revision control file for `%s'",
+ finfo->file);
+ goto free_vars_and_return;
+ }
+
+ /*
+ * As an enhancement for the case where a tag is being re-applied to a
+ * large number of files, make one extra call to RCS_getversion to see
+ * if the tag is already set in the RCS file. If so, check to see if it
+ * needs to be moved. If not, do nothing. This will likely save a lot of
+ * time when simply moving the tag to the "current" head revisions of a
+ * module -- which I have found to be a typical tagging operation.
+ */
+ rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
+ oversion = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
+ if (oversion != NULL)
+ {
+ int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
+
+ /*
+ * if versions the same and neither old or new are branches don't have
+ * to do anything
+ */
+ if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
+ {
+ free (oversion);
+ if (branch_mode)
+ free (rev);
+ goto free_vars_and_return;
+ }
+
+ if (!force_tag_move)
+ {
+ /* we're NOT going to move the tag */
+ cvs_output ("W ", 2);
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" : ", 0);
+ cvs_output (symtag, 0);
+ cvs_output (" already exists on ", 0);
+ cvs_output (isbranch ? "branch" : "version", 0);
+ cvs_output (" ", 0);
+ cvs_output (oversion, 0);
+ cvs_output (" : NOT MOVING tag to ", 0);
+ cvs_output (branch_mode ? "branch" : "version", 0);
+ cvs_output (" ", 0);
+ cvs_output (rev, 0);
+ cvs_output ("\n", 1);
+ free (oversion);
+ if (branch_mode)
+ free (rev);
+ goto free_vars_and_return;
+ }
+ else /* force_tag_move == 1 and... */
+ if ((isbranch && !disturb_branch_tags) ||
+ (!isbranch && disturb_branch_tags))
+ {
+ error (0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
+ finfo->fullname,
+ isbranch ? "branch" : "non-branch",
+ symtag, oversion, rev,
+ isbranch ? "" : " due to `-B' option");
+ free (oversion);
+ if (branch_mode)
+ free (rev);
+ goto free_vars_and_return;
+ }
+ free (oversion);
+ }
+
+ if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
+ {
+ error (1, retcode == -1 ? errno : 0,
+ "failed to set tag %s to revision %s in %s",
+ symtag, rev, vers->srcfile->path);
+ if (branch_mode)
+ free (rev);
+ retval = 1;
+ goto free_vars_and_return;
+ }
+ if (branch_mode)
+ free (rev);
+ RCS_rewrite (vers->srcfile, NULL, NULL);
+
+ /* more warm fuzzies */
+ if (!really_quiet)
+ {
+ cvs_output ("T ", 2);
+ cvs_output (finfo->fullname, 0);
+ cvs_output ("\n", 1);
+ }
+
+ free_vars_and_return:
+ if (nversion != NULL)
+ free (nversion);
+ freevers_ts (&vers);
+ if (!delete_flag && !retval && !valtagged)
+ {
+ tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
+ valtagged = true;
+ }
+ return retval;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+tag_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+
+ if (ignore_directory (update_dir))
+ {
+ /* print the warm fuzzy message */
+ if (!quiet)
+ error (0, 0, "Ignoring %s", update_dir);
+ return R_SKIP_ALL;
+ }
+
+ if (!quiet)
+ error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
+ update_dir);
+ return R_PROCESS;
+}
+
+
+
+/* Code relating to the val-tags file. Note that this file has no way
+ of knowing when a tag has been deleted. The problem is that there
+ is no way of knowing whether a tag still exists somewhere, when we
+ delete it some places. Using per-directory val-tags files (in
+ CVSREP) might be better, but that might slow down the process of
+ verifying that a tag is correct (maybe not, for the likely cases,
+ if carefully done), and/or be harder to implement correctly. */
+
+struct val_args {
+ const char *name;
+ int found;
+};
+
+static int
+val_fileproc (void *callerdat, struct file_info *finfo)
+{
+ RCSNode *rcsdata;
+ struct val_args *args = callerdat;
+ char *tag;
+
+ if ((rcsdata = finfo->rcs) == NULL)
+ /* Not sure this can happen, after all we passed only
+ W_REPOS | W_ATTIC. */
+ return 0;
+
+ tag = RCS_gettag (rcsdata, args->name, 1, NULL);
+ if (tag != NULL)
+ {
+ /* FIXME: should find out a way to stop the search at this point. */
+ args->found = 1;
+ free (tag);
+ }
+ return 0;
+}
+
+
+
+/* This routine determines whether a tag appears in CVSROOT/val-tags.
+ *
+ * The val-tags file will be open read-only when IDB is NULL. Since writes to
+ * val-tags always append to it, the lack of locking is okay. The worst case
+ * race condition might misinterpret a partially written "foobar" matched, for
+ * instance, a request for "f", "foo", of "foob". Such a mismatch would be
+ * caught harmlessly later.
+ *
+ * Before CVS adds a tag to val-tags, it will lock val-tags for write and
+ * verify that the tag is still not present to avoid adding it twice.
+ *
+ * NOTES
+ * This function expects its parent to handle any necessary locking of the
+ * val-tags file.
+ *
+ * INPUTS
+ * idb When this value is NULL, the val-tags file is opened in
+ * in read-only mode. When present, the val-tags file is opened
+ * in read-write mode and the DBM handle is stored in *IDB.
+ * name The tag to search for.
+ *
+ * OUTPUTS
+ * *idb The val-tags file opened for read/write, or NULL if it couldn't
+ * be opened.
+ *
+ * ERRORS
+ * Exits with an error message if the val-tags file cannot be opened for
+ * read (failure to open val-tags read/write is harmless - see below).
+ *
+ * RETURNS
+ * true 1. If NAME exists in val-tags.
+ * 2. If IDB is non-NULL and val-tags cannot be opened for write.
+ * This allows callers to ignore the harmless inability to
+ * update the val-tags cache.
+ * false If the file could be opened and the tag is not present.
+ */
+static int is_in_val_tags (DBM **idb, const char *name)
+{
+ DBM *db = NULL;
+ char *valtags_filename;
+ datum mytag;
+ int status;
+
+ /* Casting out const should be safe here - input datums are not
+ * written to by the myndbm functions.
+ */
+ mytag.dptr = (char *)name;
+ mytag.dsize = strlen (name);
+
+ valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
+ CVSROOTADM, CVSROOTADM_VALTAGS);
+
+ if (idb)
+ {
+ mode_t omask;
+
+ omask = umask (cvsumask);
+ db = dbm_open (valtags_filename, O_RDWR | O_CREAT, 0666);
+ umask (omask);
+
+ if (!db)
+ {
+
+ error (0, errno, "warning: cannot open `%s' read/write",
+ valtags_filename);
+ *idb = NULL;
+ return 1;
+ }
+
+ *idb = db;
+ }
+ else
+ {
+ db = dbm_open (valtags_filename, O_RDONLY, 0444);
+ if (!db && !existence_error (errno))
+ error (1, errno, "cannot read %s", valtags_filename);
+ }
+
+ /* If the file merely fails to exist, we just keep going and create
+ it later if need be. */
+
+ status = 0;
+ if (db)
+ {
+ datum val;
+
+ val = dbm_fetch (db, mytag);
+ if (val.dptr != NULL)
+ /* Found. The tag is valid. */
+ status = 1;
+
+ /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
+
+ if (!idb) dbm_close (db);
+ }
+
+ free (valtags_filename);
+ return status;
+}
+
+
+
+/* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and
+ * reverifies that the tag does not exist before adding it.
+ */
+static void add_to_val_tags (const char *name)
+{
+ DBM *db;
+ datum mytag;
+ datum value;
+
+ if (noexec) return;
+
+ val_tags_lock (current_parsed_root->directory);
+
+ /* Check for presence again since we have a lock now. */
+ if (is_in_val_tags (&db, name)) return;
+
+ /* Casting out const should be safe here - input datums are not
+ * written to by the myndbm functions.
+ */
+ mytag.dptr = (char *)name;
+ mytag.dsize = strlen (name);
+ value.dptr = "y";
+ value.dsize = 1;
+
+ if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
+ error (0, errno, "failed to store %s into val-tags", name);
+ dbm_close (db);
+
+ clear_val_tags_lock ();
+}
+
+
+
+static Dtype
+val_direntproc (void *callerdat, const char *dir, const char *repository,
+ const char *update_dir, List *entries)
+{
+ /* This is not quite right--it doesn't get right the case of "cvs
+ update -d -r foobar" where foobar is a tag which exists only in
+ files in a directory which does not exist yet, but which is
+ about to be created. */
+ if (isdir (dir))
+ return R_PROCESS;
+ return R_SKIP_ALL;
+}
+
+
+
+/* With VALID set, insert NAME into val-tags if it is not already present
+ * there.
+ *
+ * Without VALID set, check to see whether NAME is a valid tag. If so, return.
+ * If not print an error message and exit.
+ *
+ * INPUTS
+ *
+ * ARGC, ARGV, LOCAL, and AFLAG specify which files we will be operating on.
+ *
+ * REPOSITORY is the repository if we need to cd into it, or NULL if
+ * we are already there, or "" if we should do a W_LOCAL recursion.
+ * Sorry for three cases, but the "" case is needed in case the
+ * working directories come from diverse parts of the repository, the
+ * NULL case avoids an unneccesary chdir, and the non-NULL, non-""
+ * case is needed for checkout, where we don't want to chdir if the
+ * tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
+ * local directory.
+ *
+ * ERRORS
+ * Errors may be encountered opening and accessing the DBM file. Write
+ * errors generate warnings and read errors are fatal. When !VALID and NAME
+ * is not in val-tags, errors may also be generated as per start_recursion.
+ * When !VALID, non-existance of tags both in val-tags and in the archive
+ * files also causes a fatal error.
+ *
+ * RETURNS
+ * Nothing.
+ */
+void
+tag_check_valid (const char *oriname, int argc, char **argv, int local, int
aflag,
+ char *repository, bool valid)
+{
+ char *name;
+ struct val_args the_val_args;
+ struct saved_cwd cwd;
+ int which;
+
+#ifdef HAVE_PRINTF_PTR
+ TRACE (TRACE_FUNCTION,
+ "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
+ " aflag=%d, repository=%s, valid=%s)",
+ oriname ? oriname : "(name)", argc, (void *)argv, local, aflag,
+ repository ? repository : "(null)",
+ valid ? "true" : "false");
+#else
+ TRACE (TRACE_FUNCTION,
+ "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
+ " aflag=%d, repository=%s, valid=%s)",
+ oriname ? oriname : "(name)", argc, (unsigned long)argv, local,
aflag,
+ repository ? repository : "(null)",
+ valid ? "true" : "false");
+#endif
+
+ /* validate tag and return symbolic tag if any */
+ bool files = (argc > 0);
+ int i;
+ for (i=0; i<argc; ++i)
+ if (isdir (argv[i]))
+ {
+ files = false;
+ break;
+ }
+ if (!(name = RCS_extract_tag (oriname, files)))
+ {
+ assert (!valid);
+ return;
+ }
+
+ if (is_in_val_tags (NULL, name))
+ {
+ free (name);
+ return;
+ }
+
+ if (!valid)
+ {
+ /* We didn't find the tag in val-tags, so look through all the RCS files
+ * to see whether it exists there. Yes, this is expensive, but there
+ * is no other way to cope with a tag which might have been created
+ * by an old version of CVS, from before val-tags was invented
+ */
+
+ the_val_args.name = name;
+ the_val_args.found = 0;
+ which = W_REPOS | W_ATTIC;
+
+ if (repository == NULL || repository[0] == '\0')
+ which |= W_LOCAL;
+ else
+ {
+ if (save_cwd (&cwd))
+ error (1, errno, "Failed to save current directory.");
+ if (CVS_CHDIR (repository) < 0)
+ error (1, errno, "cannot change to %s directory", repository);
+ }
+
+ start_recursion
+ (val_fileproc, NULL, val_direntproc, NULL,
+ &the_val_args, argc, argv, local, which, aflag,
+ CVS_LOCK_READ, NULL, 1, repository);
+ if (repository != NULL && repository[0] != '\0')
+ {
+ if (restore_cwd (&cwd))
+ error (1, errno, "Failed to restore current directory, `%s'.",
+ cwd.name);
+ free_cwd (&cwd);
+ }
+
+ if (!the_val_args.found)
+ error (1, 0, "no such tag `%s'", name);
+ }
+
+ /* The tags is valid but not mentioned in val-tags. Add it. */
+ add_to_val_tags (name);
+ free (name);
+}
Index: ccvs/src/update.c
diff -u /dev/null ccvs/src/update.c:1.260.2.1
--- /dev/null Tue Jan 17 15:41:25 2006
+++ ccvs/src/update.c Tue Jan 17 15:41:24 2006
@@ -0,0 +1,2939 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * "update" updates the version in the present directory with respect to the
RCS
+ * repository. The present version must have been created by "checkout". The
+ * user can keep up-to-date by calling "update" whenever he feels like it.
+ *
+ * The present version can be committed by "commit", but this keeps the version
+ * in tact.
+ *
+ * Arguments following the options are taken to be file names to be updated,
+ * rather than updating the entire directory.
+ *
+ * Modified or non-existent RCS files are checked out and reported as U
+ * <user_file>
+ *
+ * Modified user files are reported as M <user_file>. If both the RCS file and
+ * the user file have been modified, the user file is replaced by the result
+ * of rcsmerge, and a backup file is written for the user in .#file.version.
+ * If this throws up irreconcilable differences, the file is reported as C
+ * <user_file>, and as M <user_file> otherwise.
+ *
+ * Files added but not yet committed are reported as A <user_file>. Files
+ * removed but not yet committed are reported as R <user_file>.
+ *
+ * If the current directory contains subdirectories that hold concurrent
+ * versions, these are updated too. If the -d option was specified, new
+ * directories added to the repository are automatically created and updated
+ * as well.
+ */
+
+#include "cvs.h"
+#include <assert.h>
+#include "save-cwd.h"
+#ifdef SERVER_SUPPORT
+# include "md5.h"
+#endif
+#include "watch.h"
+#include "fileattr.h"
+#include "edit.h"
+#include "getline.h"
+#include "buffer.h"
+#include "hardlink.h"
+
+static int checkout_file (struct file_info *finfo, Vers_TS *vers_ts,
+ int adding, int merging, int update_server);
+#ifdef SERVER_SUPPORT
+static void checkout_to_buffer (void *, const char *, size_t);
+static int patch_file (struct file_info *finfo,
+ Vers_TS *vers_ts,
+ int *docheckout, struct stat *file_info,
+ unsigned char *checksum);
+static void patch_file_write (void *, const char *, size_t);
+#endif
+static int merge_file (struct file_info *finfo, Vers_TS *vers);
+static int scratch_file (struct file_info *finfo, Vers_TS *vers);
+static Dtype update_dirent_proc (void *callerdat, const char *dir,
+ const char *repository,
+ const char *update_dir,
+ List *entries);
+static int update_dirleave_proc (void *callerdat, const char *dir,
+ int err, const char *update_dir,
+ List *entries);
+static int update_fileproc (void *callerdat, struct file_info *);
+static int update_filesdone_proc (void *callerdat, int err,
+ const char *repository,
+ const char *update_dir, List *entries);
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+static int get_linkinfo_proc( void *_callerdat, struct _finfo * );
+#endif
+static void join_file (struct file_info *finfo, Vers_TS *vers_ts,
+ const char *j1, const char *j2);
+
+static char *options = NULL;
+static char *tag = NULL;
+static char *date = NULL;
+/* This is a bit of a kludge. We call WriteTag at the beginning
+ before we know whether nonbranch is set or not. And then at the
+ end, once we have the right value for nonbranch, we call WriteTag
+ again. I don't know whether the first call is necessary or not.
+ rewrite_tag is nonzero if we are going to have to make that second
+ call. warned is nonzero if we've already warned the user that the
+ tag occurs as both a revision tag and a branch tag. */
+static int rewrite_tag;
+static int nonbranch;
+static int warned;
+
+/* If we set the tag or date for a subdirectory, we use this to undo
+ the setting. See update_dirent_proc. */
+static char *tag_update_dir;
+
+static char *join_rev1, *join_date1;
+static char *join_rev2, *join_date2;
+static int aflag = 0;
+static int toss_local_changes = 0;
+static int force_tag_match = 1;
+static int update_build_dirs = 0;
+static int update_prune_dirs = 0;
+static int pipeout = 0;
+static int dotemplate = 0;
+#ifdef SERVER_SUPPORT
+static int patches = 0;
+static int rcs_diff_patches = 0;
+#endif
+static List *ignlist = NULL;
+static time_t last_register_time;
+static const char *const update_usage[] =
+{
+ "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n",
+ " [-I ign] [-W spec] [files...]\n",
+ "\t-A\tReset any sticky tags/date/kopts.\n",
+ "\t-P\tPrune empty directories.\n",
+ "\t-C\tOverwrite locally modified files with clean repository copies.\n",
+ "\t-d\tBuild directories, like checkout does.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-p\tSend updates to standard output (avoids stickiness).\n",
+ "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
+ "\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
+ "\t-D date\tSet date to update from (is sticky).\n",
+ "\t-j rev\tMerge in changes made between current revision and rev.\n",
+ "\t-I ign\tMore files to ignore (! to reset).\n",
+ "\t-W spec\tWrappers specification line.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+
+
+/*
+ * update is the argv,argc based front end for arg parsing
+ */
+int
+update (int argc, char **argv)
+{
+ int c, err;
+ int local = 0; /* recursive by default */
+ int which; /* where to look for files and dirs */
+ char *xjoin_rev1, *xjoin_date1,
+ *xjoin_rev2, *xjoin_date2,
+ *join_orig1, *join_orig2;
+
+ if (argc == -1)
+ usage (update_usage);
+
+ xjoin_rev1 = xjoin_date1 = xjoin_rev2 = xjoin_date2 = join_orig1 =
+ join_orig2 = NULL;
+
+ ign_setup ();
+ wrap_setup ();
+
+ /* parse the args */
+ optind = 0;
+ while ((c = getopt (argc, argv, "+ApCPflRQqduk:r:D:j:I:W:")) != -1)
+ {
+ switch (c)
+ {
+ case 'A':
+ aflag = 1;
+ break;
+ case 'C':
+ toss_local_changes = 1;
+ break;
+ case 'I':
+ ign_add (optarg, 0);
+ break;
+ case 'W':
+ wrap_add (optarg, 0);
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'Q':
+ case 'q':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_name);
+ break;
+ case 'd':
+ update_build_dirs = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'r':
+ parse_tagdate (&tag, &date, optarg);
+ break;
+ case 'D':
+ if (date) free (date);
+ date = Make_Date (optarg);
+ break;
+ case 'P':
+ update_prune_dirs = 1;
+ break;
+ case 'p':
+ pipeout = 1;
+ noexec = 1; /* so no locks will be created */
+ break;
+ case 'j':
+ if (join_orig2)
+ error (1, 0, "only two -j options can be specified");
+ if (join_orig1)
+ {
+ join_orig2 = xstrdup (optarg);
+ parse_tagdate (&xjoin_rev2, &xjoin_date2, optarg);
+ }
+ else
+ {
+ join_orig1 = xstrdup (optarg);
+ parse_tagdate (&xjoin_rev1, &xjoin_date1, optarg);
+ }
+ break;
+ case 'u':
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ patches = 1;
+ rcs_diff_patches = server_use_rcs_diff ();
+ }
+ else
+#endif
+ usage (update_usage);
+ break;
+ case '?':
+ default:
+ usage (update_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ int pass;
+ unsigned int flags = 0;
+
+ /* The first pass does the regular update. If we receive at least
+ one patch which failed, we do a second pass and just fetch
+ those files whose patches failed. */
+ pass = 1;
+ do
+ {
+ int status;
+
+ start_server ();
+
+ if (local)
+ send_arg("-l");
+ if (update_build_dirs)
+ send_arg("-d");
+ if (pipeout)
+ send_arg("-p");
+ if (!force_tag_match)
+ send_arg("-f");
+ if (aflag)
+ send_arg("-A");
+ if (toss_local_changes)
+ send_arg("-C");
+ if (update_prune_dirs)
+ send_arg("-P");
+ client_prune_dirs = update_prune_dirs;
+ option_with_arg ("-r", tag);
+ if (options && options[0] != '\0')
+ send_arg (options);
+ if (date)
+ client_senddate (date);
+ if (join_orig1)
+ option_with_arg ("-j", join_orig1);
+ if (join_orig2)
+ option_with_arg ("-j", join_orig2);
+ wrap_send ();
+
+ if (failed_patches_count == 0)
+ {
+ /* If the server supports the command "update-patches", that
+ means that it knows how to handle the -u argument to update,
+ which means to send patches instead of complete files.
+
+ We don't send -u if failed_patches != NULL, so that the
+ server doesn't try to send patches which will just fail
+ again. At least currently, the client also clobbers the
+ file and tells the server it is lost, which also will get
+ a full file instead of a patch, but it seems clean to omit
+ -u. */
+ if (supported_request ("update-patches"))
+ send_arg ("-u");
+
+ send_arg ("--");
+
+ if (update_build_dirs)
+ flags |= SEND_BUILD_DIRS;
+
+ if (toss_local_changes) {
+ flags |= SEND_NO_CONTENTS;
+ flags |= BACKUP_MODIFIED_FILES;
+ }
+
+ /* If noexec, probably could be setting SEND_NO_CONTENTS.
+ Same caveats as for "cvs status" apply. */
+
+ send_files (argc, argv, local, aflag, flags);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ }
+ else
+ {
+ fprintf (stderr, "%s client: refetching unpatchable files\n",
+ program_name);
+
+ if (toplevel_wd != NULL
+ && CVS_CHDIR (toplevel_wd) < 0)
+ {
+ error (1, errno, "could not chdir to %s", toplevel_wd);
+ }
+
+ send_arg ("--");
+
+ /* Failed patches should only occur with files that were not
+ * locally modified.
+ */
+ flags |= SEND_NO_CONTENTS;
+
+ send_files (failed_patches_count, failed_patches, local,
+ aflag, flags);
+ send_file_names (failed_patches_count, failed_patches, 0);
+ free_names (&failed_patches_count, failed_patches);
+ }
+
+ send_to_server ("update\012", 0);
+
+ status = get_responses_and_close ();
+
+ /* If there are any conflicts, the server will return a
+ non-zero exit status. If any patches failed, we still
+ want to run the update again. We use a pass count to
+ avoid an endless loop. */
+
+ /* Notes: (1) assuming that status != 0 implies a
+ potential conflict is the best we can cleanly do given
+ the current protocol. I suppose that trying to
+ re-fetch in cases where there was a more serious error
+ is probably more or less harmless, but it isn't really
+ ideal. (2) it would be nice to have a testsuite case for the
+ conflict-and-patch-failed case. */
+
+ if (status != 0
+ && (failed_patches_count == 0 || pass > 1))
+ {
+ if (failed_patches_count > 0)
+ free_names (&failed_patches_count, failed_patches);
+ return status;
+ }
+
+ ++pass;
+ } while (failed_patches_count > 0);
+
+ return 0;
+ }
+#endif
+
+ if (tag != NULL)
+ tag_check_valid (tag, argc, argv, local, aflag, "", false);
+ if (xjoin_rev1 != NULL)
+ tag_check_valid (xjoin_rev1, argc, argv, local, aflag, "", false);
+ if (xjoin_rev2 != NULL)
+ tag_check_valid (xjoin_rev2, argc, argv, local, aflag, "", false);
+
+ /*
+ * If we are updating the entire directory (for real) and building dirs
+ * as we go, we make sure there is no static entries file and write the
+ * tag file as appropriate
+ */
+ if (argc <= 0 && !pipeout)
+ {
+ if (update_build_dirs)
+ {
+ if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
+ error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ char *repos = Name_Repository (NULL, NULL);
+ server_clear_entstat (".", repos);
+ free (repos);
+ }
+#endif
+ }
+
+ /* keep the CVS/Tag file current with the specified arguments */
+ if (aflag || tag || date)
+ {
+ char *repos = Name_Repository (NULL, NULL);
+ WriteTag (NULL, tag, date, 0, ".", repos);
+ free (repos);
+ rewrite_tag = 1;
+ nonbranch = -1;
+ warned = 0;
+ }
+ }
+
+ /* look for files/dirs locally and in the repository */
+ which = W_LOCAL | W_REPOS;
+
+ /* look in the attic too if a tag or date is specified */
+ if (tag || date || join_orig1)
+ {
+ TRACE (TRACE_DATA, "update: searching attic");
+ which |= W_ATTIC;
+ }
+
+ /* call the command line interface */
+ err = do_update (argc, argv, options, tag, date, force_tag_match,
+ local, update_build_dirs, aflag, update_prune_dirs,
+ pipeout, which, xjoin_rev1, xjoin_date1, xjoin_rev2,
+ xjoin_date2, NULL, 1, NULL);
+
+ /* Free the space allocated for tags and dates, if necessary. */
+ if (tag) free (tag);
+ if (date) free (date);
+
+ return err;
+}
+
+
+
+/*
+ * Command line interface to update (used by checkout)
+ *
+ * repository = cvsroot->repository + update_dir. This is necessary for
+ * checkout so that start_recursion can determine our repository. In the
+ * update case, start_recursion can use the CVS/Root & CVS/Repository file
+ * to determine this value.
+ */
+int
+do_update (int argc, char **argv, char *xoptions, char *xtag, char *xdate,
+ int xforce, int local, int xbuild, int xaflag, int xprune,
+ int xpipeout, int which, char *xjoin_rev1, char *xjoin_date1,
+ char *xjoin_rev2, char *xjoin_date2,
+ char *preload_update_dir, int xdotemplate, char *repository)
+{
+ int err = 0;
+
+ TRACE (TRACE_FUNCTION,
+"do_update (%s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s, %d,
%s)",
+ xoptions ? xoptions : "(null)", xtag ? xtag : "(null)",
+ xdate ? xdate : "(null)", xforce, local, xbuild, xaflag, xprune,
+ xpipeout, which, xjoin_rev1 ? xjoin_rev1 : "(null)",
+ xjoin_date1 ? xjoin_date1 : "(null)",
+ xjoin_rev2 ? xjoin_rev2 : "(null)",
+ xjoin_date2 ? xjoin_date2 : "(null)",
+ preload_update_dir ? preload_update_dir : "(null)", xdotemplate,
+ repository ? repository : "(null)");
+
+ /* fill in the statics */
+ options = xoptions;
+ tag = xtag;
+ date = xdate;
+ force_tag_match = xforce;
+ update_build_dirs = xbuild;
+ aflag = xaflag;
+ update_prune_dirs = xprune;
+ pipeout = xpipeout;
+ dotemplate = xdotemplate;
+
+ /* setup the join support */
+ join_rev1 = xjoin_rev1;
+ join_date1 = xjoin_date1;
+ join_rev2 = xjoin_rev2;
+ join_date2 = xjoin_date2;
+
+#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 = xgetcwd (); /* save top-level working dir */
+
+ /* FIXME-twp: the arguments to start_recursion make me dizzy. This
+ function call was copied from the update_fileproc call that
+ follows it; someone should make sure that I did it right. */
+ err = start_recursion
+ (get_linkinfo_proc, NULL, NULL, NULL, NULL,
+ argc, argv, local, which, aflag, CVS_LOCK_READ,
+ preload_update_dir, 1, NULL);
+ if (err)
+ return err;
+
+ /* FIXME-twp: at this point we should walk the hardlist
+ and update the `links' field of each hardlink_info struct
+ to list the files that are linked on dist. That would make
+ it easier & more efficient to compare the disk linkage with
+ the repository linkage (a simple strcmp). */
+ }
+#endif
+
+ /* call the recursion processor */
+ err = start_recursion (update_fileproc, update_filesdone_proc,
+ update_dirent_proc, update_dirleave_proc, NULL,
+ argc, argv, local, which, aflag, CVS_LOCK_READ,
+ preload_update_dir, 1, repository);
+
+ /* see if we need to sleep before returning to avoid time-stamp races */
+ if (!server_active && last_register_time)
+ {
+ sleep_past (last_register_time);
+ }
+
+ return err;
+}
+
+
+
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+/*
+ * The get_linkinfo_proc callback adds each file to the hardlist
+ * (see hardlink.c).
+ */
+
+static int
+get_linkinfo_proc (void *callerdat, struct file_info *finfo)
+{
+ char *fullpath;
+ Node *linkp;
+ struct hardlink_info *hlinfo;
+
+ /* Get the full pathname of the current file. */
+ fullpath = Xasprintf ("%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 = xmalloc (sizeof (struct hardlink_info));
+
+ hlinfo->status = (Ctype) 0; /* is this dumb? */
+ hlinfo->checked_out = 0;
+
+ linkp->data = hlinfo;
+
+ return 0;
+}
+#endif
+
+
+
+/*
+ * This is the callback proc for update. It is called for each file in each
+ * directory by the recursion code. The current directory is the local
+ * instantiation. file is the file name we are to operate on. update_dir is
+ * set to the path relative to where we started (for pretty printing).
+ * repository is the repository. entries and srcfiles are the pre-parsed
+ * entries and source control files.
+ *
+ * This routine decides what needs to be done for each file and does the
+ * appropriate magic for checkout
+ */
+static int
+update_fileproc (void *callerdat, struct file_info *finfo)
+{
+ int retval, nb;
+ Ctype status;
+ Vers_TS *vers;
+
+ status = Classify_File (finfo, tag, date, options, force_tag_match,
+ aflag, &vers, pipeout);
+
+ /* Keep track of whether TAG is a branch tag.
+ Note that if it is a branch tag in some files and a nonbranch tag
+ in others, treat it as a nonbranch tag. */
+ if (rewrite_tag
+ && tag != NULL
+ && finfo->rcs != NULL)
+ {
+ char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL);
+ if (rev != NULL
+ && nonbranch != (nb = !RCS_nodeisbranch (finfo->rcs, tag)))
+ {
+ if (nonbranch >= 0 && !warned && !quiet)
+ {
+ error (0, 0,
+"warning: %s is a branch tag in some files and a revision tag in others.",
+ tag);
+ warned = 1;
+ }
+ if (nonbranch < nb) nonbranch = nb;
+ }
+ if (rev != NULL)
+ free (rev);
+ }
+
+ if (pipeout)
+ {
+ /*
+ * We just return success without doing anything if any of the really
+ * funky cases occur
+ *
+ * If there is still a valid RCS file, do a regular checkout type
+ * operation
+ */
+ switch (status)
+ {
+ case T_UNKNOWN: /* unknown file was explicitly asked
+ * about */
+ case T_REMOVE_ENTRY: /* needs to be un-registered */
+ case T_ADDED: /* added but not committed */
+ retval = 0;
+ break;
+ case T_CONFLICT: /* old punt-type errors */
+ retval = 1;
+ break;
+ case T_UPTODATE: /* file was already up-to-date */
+ case T_NEEDS_MERGE: /* needs merging */
+ case T_MODIFIED: /* locally modified */
+ case T_REMOVED: /* removed but not committed */
+ case T_CHECKOUT: /* needs checkout */
+ case T_PATCH: /* needs patch */
+ retval = checkout_file (finfo, vers, 0, 0, 0);
+ break;
+
+ default: /* can't ever happen :-) */
+ error (0, 0,
+ "unknown file status %d for file %s", status,
finfo->file);
+ retval = 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (status)
+ {
+ case T_UNKNOWN: /* unknown file was explicitly asked
+ * about */
+ case T_UPTODATE: /* file was already up-to-date */
+ retval = 0;
+ break;
+ case T_CONFLICT: /* old punt-type errors */
+ retval = 1;
+ write_letter (finfo, 'C');
+ break;
+ case T_NEEDS_MERGE: /* needs merging */
+ if (! toss_local_changes)
+ {
+ retval = merge_file (finfo, vers);
+ break;
+ }
+ /* else FALL THROUGH */
+ case T_MODIFIED: /* locally modified */
+ retval = 0;
+ if (toss_local_changes)
+ {
+ char *bakname;
+ bakname = backup_file (finfo->file, vers->vn_user);
+ /* This behavior is sufficiently unexpected to
+ justify overinformativeness, I think. */
+ if (!really_quiet && !server_active)
+ (void) printf ("(Locally modified %s moved to %s)\n",
+ finfo->file, bakname);
+ free (bakname);
+
+ /* The locally modified file is still present, but
+ it will be overwritten by the repository copy
+ after this. */
+ status = T_CHECKOUT;
+ retval = checkout_file (finfo, vers, 0, 0, 1);
+ }
+ else
+ {
+ if (vers->ts_conflict)
+ {
+ if (file_has_markers (finfo))
+ {
+ write_letter (finfo, 'C');
+ retval = 1;
+ }
+ else
+ {
+ /* Reregister to clear conflict flag. */
+ Register (finfo->entries, finfo->file,
+ vers->vn_rcs, vers->ts_rcs,
+ vers->options, vers->tag,
+ vers->date, NULL);
+ }
+ }
+ if (!retval)
+ write_letter (finfo, 'M');
+ }
+ break;
+ case T_PATCH: /* needs patch */
+#ifdef SERVER_SUPPORT
+ if (patches)
+ {
+ int docheckout;
+ struct stat file_info;
+ unsigned char checksum[16];
+
+ retval = patch_file (finfo,
+ vers, &docheckout,
+ &file_info, checksum);
+ if (! docheckout)
+ {
+ if (server_active && retval == 0)
+ server_updated (finfo, vers,
+ (rcs_diff_patches
+ ? SERVER_RCS_DIFF
+ : SERVER_PATCHED),
+ file_info.st_mode, checksum,
+ NULL);
+ break;
+ }
+ }
+#endif
+ /* If we're not running as a server, just check the
+ file out. It's simpler and faster than producing
+ and applying patches. */
+ /* Fall through. */
+ case T_CHECKOUT: /* needs checkout */
+ retval = checkout_file (finfo, vers, 0, 0, 1);
+ break;
+ case T_ADDED: /* added but not committed */
+ write_letter (finfo, 'A');
+ retval = 0;
+ break;
+ case T_REMOVED: /* removed but not committed */
+ write_letter (finfo, 'R');
+ retval = 0;
+ break;
+ case T_REMOVE_ENTRY: /* needs to be un-registered */
+ retval = scratch_file (finfo, vers);
+ break;
+ default: /* can't ever happen :-) */
+ error (0, 0,
+ "unknown file status %d for file %s", status,
finfo->file);
+ retval = 0;
+ break;
+ }
+ }
+
+ /* only try to join if things have gone well thus far */
+ if (retval == 0 && join_rev1)
+ {
+ char *j1;
+ char *j2;
+ bool invalid_tag = false;
+ if (RCS_is_relative (join_rev1))
+ {
+ j1 = Version_resolve_relTag (finfo, join_rev1, false);
+ if (!j1)
+ invalid_tag = true;
+ }
+ else
+ j1 = xstrdup (join_rev1);
+ if (join_rev2 && RCS_is_relative (join_rev2))
+ {
+ j2 = Version_resolve_relTag (finfo, join_rev2, false);
+ if (!j2)
+ invalid_tag = true;
+ }
+ else
+ j2 = xstrdup (join_rev2);
+ if (invalid_tag)
+ {
+ if (!really_quiet)
+ error (0, 0, "Cannot resolve relative tag: `%s'.", j1);
+ }
+ else
+ join_file (finfo, vers, j1, j2);
+ free (j1);
+ free (j2);
+ }
+
+ /* if this directory has an ignore list, add this file to it */
+ if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL))
+ {
+ Node *p;
+
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (finfo->file);
+ if (addnode (ignlist, p) != 0)
+ freenode (p);
+ }
+
+ freevers_ts (&vers);
+ return retval;
+}
+
+
+
+static void
+update_ignproc (const char *file, const char *dir)
+{
+ struct file_info finfo;
+ char *tmp;
+
+ memset (&finfo, 0, sizeof (finfo));
+ finfo.file = file;
+ finfo.update_dir = dir;
+
+ finfo.fullname = tmp = Xasprintf ("%s%s%s",
+ dir[0] == '\0' ? "" : dir,
+ dir[0] == '\0' ? "" : "/",
+ file);
+ write_letter (&finfo, '?');
+ free (tmp);
+}
+
+
+
+/* ARGSUSED */
+static int
+update_filesdone_proc (void *callerdat, int err, const char *repository,
+ const char *update_dir, List *entries)
+{
+ if (nonbranch < 0) nonbranch = 0;
+ if (rewrite_tag)
+ {
+ WriteTag (NULL, tag, date, nonbranch, update_dir, repository);
+ rewrite_tag = 0;
+ }
+
+ /* if this directory has an ignore list, process it then free it */
+ if (ignlist)
+ {
+ ignore_files (ignlist, entries, update_dir, update_ignproc);
+ dellist (&ignlist);
+ }
+
+ /* Clean up CVS admin dirs if we are export */
+ if (strcmp (cvs_cmd_name, "export") == 0)
+ {
+ /* I'm not sure the existence_error is actually possible (except
+ in cases where we really should print a message), but since
+ this code used to ignore all errors, I'll play it safe. */
+ if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
+ error (0, errno, "cannot remove %s directory", CVSADM);
+ }
+ else if (!server_active && !pipeout)
+ {
+ /* If there is no CVS/Root file, add one */
+ if (!isfile (CVSADM_ROOT))
+ Create_Root (NULL, original_parsed_root->original);
+ }
+
+ return err;
+}
+
+
+
+/*
+ * update_dirent_proc () is called back by the recursion processor before a
+ * sub-directory is processed for update. In this case, update_dirent proc
+ * will probably create the directory unless -d isn't specified and this is a
+ * new directory. A return code of 0 indicates the directory should be
+ * processed by the recursion code. A return of non-zero indicates the
+ * recursion code should skip this directory.
+ */
+static Dtype
+update_dirent_proc (void *callerdat, const char *dir, const char *repository,
+ const char *update_dir, List *entries)
+{
+ if (ignore_directory (update_dir))
+ {
+ /* print the warm fuzzy message */
+ if (!quiet)
+ error (0, 0, "Ignoring %s", update_dir);
+ return R_SKIP_ALL;
+ }
+
+ if (!isdir (dir))
+ {
+ /* if we aren't building dirs, blow it off */
+ if (!update_build_dirs)
+ return R_SKIP_ALL;
+
+ /* Various CVS administrators are in the habit of removing
+ the repository directory for things they don't want any
+ more. I've even been known to do it myself (on rare
+ occasions). Not the usual recommended practice, but we
+ want to try to come up with some kind of
+ reasonable/documented/sensible behavior. Generally
+ the behavior is to just skip over that directory (see
+ dirs test in sanity.sh; the case which reaches here
+ is when update -d is specified, and the working directory
+ is gone but the subdirectory is still mentioned in
+ CVS/Entries). */
+ /* In the remote case, the client should refrain from
+ sending us the directory in the first place. So we
+ want to continue to give an error, so clients make
+ sure to do this. */
+ if (!server_active && !isdir (repository))
+ return R_SKIP_ALL;
+
+ if (noexec)
+ {
+ /* ignore the missing dir if -n is specified */
+ error (0, 0, "New directory `%s' -- ignored", update_dir);
+ return R_SKIP_ALL;
+ }
+ else
+ {
+ /* otherwise, create the dir and appropriate adm files */
+
+ /* If no tag or date were specified on the command line,
+ and we're not using -A, we want the subdirectory to use
+ the tag and date, if any, of the current directory.
+ That way, update -d will work correctly when working on
+ a branch.
+
+ We use TAG_UPDATE_DIR to undo the tag setting in
+ update_dirleave_proc. If we did not do this, we would
+ not correctly handle a working directory with multiple
+ tags (and maybe we should prohibit such working
+ directories, but they work now and we shouldn't make
+ them stop working without more thought). */
+ if ((tag == NULL && date == NULL) && ! aflag)
+ {
+ ParseTag (&tag, &date, &nonbranch);
+ if (tag != NULL || date != NULL)
+ tag_update_dir = xstrdup (update_dir);
+ }
+
+ make_directory (dir);
+ Create_Admin (dir, update_dir, repository, tag, date,
+ /* This is a guess. We will rewrite it later
+ via WriteTag. */
+ 0,
+ 0,
+ dotemplate);
+ rewrite_tag = 1;
+ nonbranch = -1;
+ warned = 0;
+ Subdir_Register (entries, NULL, dir);
+ }
+ }
+ /* Do we need to check noexec here? */
+ else if (!pipeout)
+ {
+ char *cvsadmdir;
+
+ /* The directory exists. Check to see if it has a CVS
+ subdirectory. */
+
+ cvsadmdir = Xasprintf ("%s/%s", dir, CVSADM);
+
+ if (!isdir (cvsadmdir))
+ {
+ /* We cannot successfully recurse into a directory without a CVS
+ subdirectory. Generally we will have already printed
+ "? foo". */
+ free (cvsadmdir);
+ return R_SKIP_ALL;
+ }
+ free (cvsadmdir);
+ }
+
+ /*
+ * If we are building dirs and not going to stdout, we make sure there is
+ * no static entries file and write the tag file as appropriate
+ */
+ if (!pipeout)
+ {
+ if (update_build_dirs)
+ {
+ char *tmp = Xasprintf ("%s/%s", dir, CVSADM_ENTSTAT);
+
+ if (unlink_file (tmp) < 0 && ! existence_error (errno))
+ error (1, errno, "cannot remove file %s", tmp);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ server_clear_entstat (update_dir, repository);
+#endif
+ free (tmp);
+ }
+
+ /* keep the CVS/Tag file current with the specified arguments */
+ if (aflag || tag || date)
+ {
+ WriteTag (dir, tag, date, 0, update_dir, repository);
+ rewrite_tag = 1;
+ nonbranch = -1;
+ warned = 0;
+ }
+
+ WriteTemplate (update_dir, dotemplate, repository);
+
+ /* initialize the ignore list for this directory */
+ ignlist = getlist ();
+ }
+
+ /* print the warm fuzzy message */
+ if (!quiet)
+ error (0, 0, "Updating %s", update_dir);
+
+ return R_PROCESS;
+}
+
+
+
+/*
+ * update_dirleave_proc () is called back by the recursion code upon leaving
+ * a directory. It will prune empty directories if needed and will execute
+ * any appropriate update programs.
+ */
+/* ARGSUSED */
+static int
+update_dirleave_proc (void *callerdat, const char *dir, int err,
+ const char *update_dir, List *entries)
+{
+ /* Delete the ignore list if it hasn't already been done. */
+ if (ignlist)
+ dellist (&ignlist);
+
+ /* If we set the tag or date for a new subdirectory in
+ update_dirent_proc, and we're now done with that subdirectory,
+ undo the tag/date setting. Note that we know that the tag and
+ date were both originally NULL in this case. */
+ if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
+ {
+ if (tag != NULL)
+ {
+ free (tag);
+ tag = NULL;
+ }
+ if (date != NULL)
+ {
+ free (date);
+ date = NULL;
+ }
+ nonbranch = -1;
+ warned = 0;
+ free (tag_update_dir);
+ tag_update_dir = NULL;
+ }
+
+ if (strchr (dir, '/') == NULL)
+ {
+ /* FIXME: chdir ("..") loses with symlinks. */
+ /* Prune empty dirs on the way out - if necessary */
+ (void) CVS_CHDIR ("..");
+ if (update_prune_dirs && isemptydir (dir, 0))
+ {
+ /* I'm not sure the existence_error is actually possible (except
+ in cases where we really should print a message), but since
+ this code used to ignore all errors, I'll play it safe. */
+ if (unlink_file_dir (dir) < 0 && !existence_error (errno))
+ error (0, errno, "cannot remove %s directory", dir);
+ Subdir_Deregister (entries, NULL, dir);
+ }
+ }
+
+ return err;
+}
+
+
+
+/* Returns 1 if the file indicated by node has been removed. */
+static int
+isremoved (Node *node, void *closure)
+{
+ Entnode *entdata = node->data;
+
+ /* If the first character of the version is a '-', the file has been
+ removed. */
+ return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
+}
+
+
+
+/* Returns 1 if the argument directory is completely empty, other than the
+ existence of the CVS directory entry. Zero otherwise. If MIGHT_NOT_EXIST
+ and the directory doesn't exist, then just return 0. */
+int
+isemptydir (const char *dir, int might_not_exist)
+{
+ DIR *dirp;
+ struct dirent *dp;
+
+ if ((dirp = CVS_OPENDIR (dir)) == NULL)
+ {
+ if (might_not_exist && existence_error (errno))
+ return 0;
+ error (0, errno, "cannot open directory %s for empty check", dir);
+ return 0;
+ }
+ errno = 0;
+ while ((dp = CVS_READDIR (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") != 0
+ && strcmp (dp->d_name, "..") != 0)
+ {
+ if (strcmp (dp->d_name, CVSADM) != 0)
+ {
+ /* An entry other than the CVS directory. The directory
+ is certainly not empty. */
+ (void) CVS_CLOSEDIR (dirp);
+ return 0;
+ }
+ else
+ {
+ /* The CVS directory entry. We don't have to worry about
+ this unless the Entries file indicates that files have
+ been removed, but not committed, in this directory.
+ (Removing the directory would prevent people from
+ comitting the fact that they removed the files!) */
+ List *l;
+ int files_removed;
+ struct saved_cwd cwd;
+
+ if (save_cwd (&cwd))
+ error (1, errno, "Failed to save current directory.");
+
+ if (CVS_CHDIR (dir) < 0)
+ error (1, errno, "cannot change directory to %s", dir);
+ l = Entries_Open (0, NULL);
+ files_removed = walklist (l, isremoved, 0);
+ Entries_Close (l);
+
+ if (restore_cwd (&cwd))
+ error (1, errno,
+ "Failed to restore current directory, `%s'.",
+ cwd.name);
+ free_cwd (&cwd);
+
+ if (files_removed != 0)
+ {
+ /* There are files that have been removed, but not
+ committed! Do not consider the directory empty. */
+ (void) CVS_CLOSEDIR (dirp);
+ return 0;
+ }
+ }
+ }
+ errno = 0;
+ }
+ if (errno != 0)
+ {
+ error (0, errno, "cannot read directory %s", dir);
+ (void) CVS_CLOSEDIR (dirp);
+ return 0;
+ }
+ (void) CVS_CLOSEDIR (dirp);
+ return 1;
+}
+
+
+
+/*
+ * scratch the Entries file entry associated with a file
+ */
+static int
+scratch_file (struct file_info *finfo, Vers_TS *vers)
+{
+ history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
+ Scratch_Entry (finfo->entries, finfo->file);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ if (vers->ts_user == NULL)
+ server_scratch_entry_only ();
+ server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, NULL, NULL);
+ }
+#endif
+ if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
+ error (0, errno, "unable to remove %s", finfo->fullname);
+ else if (!server_active)
+ {
+ /* skip this step when the server is running since
+ * server_updated should have handled it */
+ /* keep the vers structure up to date in case we do a join
+ * - if there isn't a file, it can't very well have a version number,
can it?
+ */
+ if (vers->vn_user != NULL)
+ {
+ free (vers->vn_user);
+ vers->vn_user = NULL;
+ }
+ if (vers->ts_user != NULL)
+ {
+ free (vers->ts_user);
+ vers->ts_user = NULL;
+ }
+ }
+ return 0;
+}
+
+
+
+/*
+ * Check out a file.
+ */
+static int
+checkout_file (struct file_info *finfo, Vers_TS *vers_ts, int adding,
+ int merging, int update_server)
+{
+ char *backup;
+ int set_time, retval = 0;
+ int status;
+ int file_is_dead;
+ struct buffer *revbuf;
+
+ backup = NULL;
+ revbuf = NULL;
+
+ /* Don't screw with backup files if we're going to stdout, or if
+ we are the server. */
+ if (!pipeout && !server_active)
+ {
+ backup = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
+ if (isfile (finfo->file))
+ rename_file (finfo->file, backup);
+ else /* FIXME: -f/-t has been disabled for so long it should probably
+ * just be stripped out to reduce clutter.
+ */
+ {
+ /* If -f/-t wrappers are being used to wrap up a directory,
+ then backup might be a directory instead of just a file. */
+ if (unlink_file_dir (backup) < 0)
+ {
+ /* Not sure if the existence_error check is needed here. */
+ if (!existence_error (errno))
+ /* FIXME: should include update_dir in message. */
+ error (0, errno, "error removing %s", backup);
+ }
+ free (backup);
+ backup = NULL;
+ }
+ }
+
+ file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
+
+ if (!file_is_dead)
+ {
+ /*
+ * if we are checking out to stdout, print a nice message to
+ * stderr, and add the -p flag to the command */
+ if (pipeout)
+ {
+ if (!quiet)
+ {
+ cvs_outerr ("\
+===================================================================\n\
+Checking out ", 0);
+ cvs_outerr (finfo->fullname, 0);
+ cvs_outerr ("\n\
+RCS: ", 0);
+ cvs_outerr (vers_ts->srcfile->print_path, 0);
+ cvs_outerr ("\n\
+VERS: ", 0);
+ cvs_outerr (vers_ts->vn_rcs, 0);
+ cvs_outerr ("\n***************\n", 0);
+ }
+ }
+
+#ifdef SERVER_SUPPORT
+ if (update_server
+ && server_active
+ && ! pipeout
+ && ! file_gzip_level
+ && ! joining ()
+ && ! wrap_name_has (finfo->file, WRAP_FROMCVS))
+ {
+ revbuf = buf_nonio_initialize (NULL);
+ status = RCS_checkout (vers_ts->srcfile, NULL,
+ vers_ts->vn_rcs, vers_ts->tag,
+ vers_ts->options, RUN_TTY,
+ checkout_to_buffer, revbuf);
+ }
+ else
+#endif
+ status = RCS_checkout (vers_ts->srcfile,
+ pipeout ? NULL : finfo->file,
+ vers_ts->vn_rcs, vers_ts->tag,
+ vers_ts->options, RUN_TTY, NULL, NULL);
+ }
+ if (file_is_dead || status == 0)
+ {
+ mode_t mode;
+
+ mode = (mode_t) -1;
+
+ if (!pipeout)
+ {
+ Vers_TS *xvers_ts;
+
+ if (revbuf != NULL && !noexec)
+ {
+ struct stat sb;
+
+ /* FIXME: We should have RCS_checkout return the mode.
+ That would also fix the kludge with noexec, above, which
+ is here only because noexec doesn't write srcfile->path
+ for us to stat. */
+ if (stat (vers_ts->srcfile->path, &sb) < 0)
+ {
+#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
+ buf_free (revbuf);
+#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
+ error (1, errno, "cannot stat %s",
+ vers_ts->srcfile->path);
+ }
+ mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
+ }
+
+ if (cvswrite
+ && !file_is_dead
+ && !fileattr_get (finfo->file, "_watched"))
+ {
+ if (revbuf == NULL)
+ xchmod (finfo->file, 1);
+ else
+ {
+ /* We know that we are the server here, so
+ although xchmod checks umask, we don't bother. */
+ mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
+ | ((mode & S_IRGRP) ? S_IWGRP : 0)
+ | ((mode & S_IROTH) ? S_IWOTH : 0));
+ }
+ }
+
+ {
+ /* A newly checked out file is never under the spell
+ of "cvs edit". If we think we were editing it
+ from a previous life, clean up. Would be better to
+ check for same the working directory instead of
+ same user, but that is hairy. */
+
+ struct addremove_args args;
+
+ editor_set (finfo->file, getcaller (), NULL);
+
+ memset (&args, 0, sizeof args);
+ args.remove_temp = 1;
+ watch_modify_watchers (finfo->file, &args);
+ }
+
+ /* set the time from the RCS file iff it was unknown before */
+ set_time =
+ (!noexec
+ && (vers_ts->vn_user == NULL ||
+ strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
+ && !file_is_dead);
+
+ wrap_fromcvs_process_file (finfo->file);
+
+ xvers_ts = Version_TS (finfo, options, tag, date,
+ force_tag_match, set_time);
+ if (strcmp (xvers_ts->options, "-V4") == 0)
+ xvers_ts->options[0] = '\0';
+
+ if (revbuf != NULL)
+ {
+ /* If we stored the file data into a buffer, then we
+ didn't create a file at all, so xvers_ts->ts_user
+ is wrong. The correct value is to have it be the
+ same as xvers_ts->ts_rcs, meaning that the working
+ file is unchanged from the RCS file.
+
+ FIXME: We should tell Version_TS not to waste time
+ statting the nonexistent file.
+
+ FIXME: Actually, I don't think the ts_user value
+ matters at all here. The only use I know of is
+ that it is printed in a trace message by
+ Server_Register. */
+
+ if (xvers_ts->ts_user != NULL)
+ free (xvers_ts->ts_user);
+ xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
+ }
+
+ (void) time (&last_register_time);
+
+ if (file_is_dead)
+ {
+ if (xvers_ts->vn_user != NULL)
+ {
+ error (0, 0,
+ "warning: %s is not (any longer) pertinent",
+ finfo->fullname);
+ }
+ Scratch_Entry (finfo->entries, finfo->file);
+#ifdef SERVER_SUPPORT
+ if (server_active && xvers_ts->ts_user == NULL)
+ server_scratch_entry_only ();
+#endif
+ /* FIXME: Rather than always unlink'ing, and ignoring the
+ existence_error, we should do the unlink only if
+ vers_ts->ts_user is non-NULL. Then there would be no
+ need to ignore an existence_error (for example, if the
+ user removes the file while we are running). */
+ if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
+ {
+ error (0, errno, "cannot remove %s", finfo->fullname);
+ }
+ }
+ else
+ Register (finfo->entries, finfo->file,
+ adding ? "0" : xvers_ts->vn_rcs,
+ xvers_ts->ts_user, xvers_ts->options,
+ xvers_ts->tag, xvers_ts->date,
+ NULL); /* Clear conflict flag on fresh checkout */
+
+ /* fix up the vers structure, in case it is used by join */
+ if (join_rev1)
+ {
+ /* FIXME: Throwing away the original revision info is almost
+ certainly wrong -- what if join_rev1 is "BASE"? */
+ if (vers_ts->vn_user != NULL)
+ free (vers_ts->vn_user);
+ if (vers_ts->vn_rcs != NULL)
+ free (vers_ts->vn_rcs);
+ vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
+ vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
+ }
+
+ /* If this is really Update and not Checkout, recode history */
+ if (strcmp (cvs_cmd_name, "update") == 0)
+ history_write ('U', finfo->update_dir, xvers_ts->vn_rcs,
finfo->file,
+ finfo->repository);
+
+ freevers_ts (&xvers_ts);
+
+ if (!really_quiet && !file_is_dead)
+ {
+ write_letter (finfo, 'U');
+ }
+ }
+
+#ifdef SERVER_SUPPORT
+ if (update_server && server_active)
+ server_updated (finfo, vers_ts,
+ merging ? SERVER_MERGED : SERVER_UPDATED,
+ mode, NULL, revbuf);
+#endif
+ }
+ else
+ {
+ if (backup != NULL)
+ {
+ rename_file (backup, finfo->file);
+ free (backup);
+ backup = NULL;
+ }
+
+ error (0, 0, "could not check out %s", finfo->fullname);
+
+ retval = status;
+ }
+
+ if (backup != NULL)
+ {
+ /* If -f/-t wrappers are being used to wrap up a directory,
+ then backup might be a directory instead of just a file. */
+ if (unlink_file_dir (backup) < 0)
+ {
+ /* Not sure if the existence_error check is needed here. */
+ if (!existence_error (errno))
+ /* FIXME: should include update_dir in message. */
+ error (0, errno, "error removing %s", backup);
+ }
+ free (backup);
+ }
+
+#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
+ if (revbuf != NULL)
+ buf_free (revbuf);
+#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
+ return retval;
+}
+
+
+
+#ifdef SERVER_SUPPORT
+
+/* This function is used to write data from a file being checked out
+ into a buffer. */
+
+static void
+checkout_to_buffer (void *callerdat, const char *data, size_t len)
+{
+ struct buffer *buf = (struct buffer *) callerdat;
+
+ buf_output (buf, data, len);
+}
+
+#endif /* SERVER_SUPPORT */
+
+#ifdef SERVER_SUPPORT
+
+/* This structure is used to pass information between patch_file and
+ patch_file_write. */
+
+struct patch_file_data
+{
+ /* File name, for error messages. */
+ const char *filename;
+ /* File to which to write. */
+ FILE *fp;
+ /* Whether to compute the MD5 checksum. */
+ int compute_checksum;
+ /* Data structure for computing the MD5 checksum. */
+ struct md5_ctx context;
+ /* Set if the file has a final newline. */
+ int final_nl;
+};
+
+/* Patch a file. Runs diff. This is only done when running as the
+ * server. The hope is that the diff will be smaller than the file
+ * itself.
+ */
+static int
+patch_file (struct file_info *finfo, Vers_TS *vers_ts, int *docheckout,
+ struct stat *file_info, unsigned char *checksum)
+{
+ char *backup;
+ char *file1;
+ char *file2;
+ int retval = 0;
+ int retcode = 0;
+ int fail;
+ FILE *e;
+ struct patch_file_data data;
+
+ *docheckout = 0;
+
+ if (noexec || pipeout || joining ())
+ {
+ *docheckout = 1;
+ return 0;
+ }
+
+ /* If this file has been marked as being binary, then never send a
+ patch. */
+ if (strcmp (vers_ts->options, "-kb") == 0)
+ {
+ *docheckout = 1;
+ return 0;
+ }
+
+ /* First check that the first revision exists. If it has been nuked
+ by cvs admin -o, then just fall back to checking out entire
+ revisions. In some sense maybe we don't have to do this; after
+ all cvs.texinfo says "Make sure that no-one has checked out a
+ copy of the revision you outdate" but then again, that advice
+ doesn't really make complete sense, because "cvs admin" operates
+ on a working directory and so _someone_ will almost always have
+ _some_ revision checked out. */
+ {
+ char *rev;
+
+ rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
+ if (rev == NULL)
+ {
+ *docheckout = 1;
+ return 0;
+ }
+ else
+ free (rev);
+ }
+
+ /* If the revision is dead, let checkout_file handle it rather
+ than duplicating the processing here. */
+ if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
+ {
+ *docheckout = 1;
+ return 0;
+ }
+
+ backup = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
+ if (isfile (finfo->file))
+ rename_file (finfo->file, backup);
+ else
+ {
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
+ }
+
+ file1 = Xasprintf ("%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
+ file2 = Xasprintf ("%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
+
+ fail = 0;
+
+ /* We need to check out both revisions first, to see if either one
+ has a trailing newline. Because of this, we don't use rcsdiff,
+ but just use diff. */
+
+ e = CVS_FOPEN (file1, "w");
+ if (e == NULL)
+ error (1, errno, "cannot open %s", file1);
+
+ data.filename = file1;
+ data.fp = e;
+ data.final_nl = 0;
+ data.compute_checksum = 0;
+
+ /* FIXME - Passing vers_ts->tag here is wrong in the least number
+ * of cases. Since we don't know whether vn_user was checked out
+ * using a tag, we pass vers_ts->tag, which, assuming the user did
+ * not specify a new TAG to -r, will be the branch we are on.
+ *
+ * The only thing it is used for is to substitute in for the Name
+ * RCS keyword, so in the error case, the patch fails to apply on
+ * the client end and we end up resending the whole file.
+ *
+ * At least, if we are keeping track of the tag vn_user came from,
+ * I don't know where yet. -DRP
+ */
+ retcode = RCS_checkout (vers_ts->srcfile, NULL,
+ vers_ts->vn_user, vers_ts->tag,
+ vers_ts->options, RUN_TTY,
+ patch_file_write, (void *) &data);
+
+ if (fclose (e) < 0)
+ error (1, errno, "cannot close %s", file1);
+
+ if (retcode != 0 || ! data.final_nl)
+ fail = 1;
+
+ if (! fail)
+ {
+ e = CVS_FOPEN (file2, "w");
+ if (e == NULL)
+ error (1, errno, "cannot open %s", file2);
+
+ data.filename = file2;
+ data.fp = e;
+ data.final_nl = 0;
+ data.compute_checksum = 1;
+ md5_init_ctx (&data.context);
+
+ retcode = RCS_checkout (vers_ts->srcfile, NULL,
+ vers_ts->vn_rcs, vers_ts->tag,
+ vers_ts->options, RUN_TTY,
+ patch_file_write, (void *) &data);
+
+ if (fclose (e) < 0)
+ error (1, errno, "cannot close %s", file2);
+
+ if (retcode != 0 || ! data.final_nl)
+ fail = 1;
+ else
+ md5_finish_ctx (&data.context, checksum);
+ }
+
+ retcode = 0;
+ if (! fail)
+ {
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+
+ /* If the client does not support the Rcs-diff command, we
+ send a context diff, and the client must invoke patch.
+ That approach was problematical for various reasons. The
+ new approach only requires running diff in the server; the
+ client can handle everything without invoking an external
+ program. */
+ if (!rcs_diff_patches)
+ /* We use -c, not -u, because that is what CVS has
+ traditionally used. Kind of a moot point, now that
+ Rcs-diff is preferred, so there is no point in making
+ the compatibility issues worse. */
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
+ else
+ /* Now that diff is librarified, we could be passing -a if
+ we wanted to. However, it is unclear to me whether we
+ would want to. Does diff -a, in any significant
+ percentage of cases, produce patches which are smaller
+ than the files it is patching? I guess maybe text
+ files with character sets which diff regards as
+ 'binary'. Conversely, do they tend to be much larger
+ in the bad cases? This needs some more
+ thought/investigation, I suspect. */
+ run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
+ retcode = diff_exec (file1, file2, NULL, NULL, dargc, dargv,
+ finfo->file);
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+
+ /* A retcode of 0 means no differences. 1 means some differences. */
+ if (retcode != 0 && retcode != 1)
+ fail = 1;
+ }
+
+ if (!fail)
+ {
+ struct stat file2_info;
+
+ /* Check to make sure the patch is really shorter */
+ if (stat (file2, &file2_info) < 0)
+ error (1, errno, "could not stat %s", file2);
+ if (stat (finfo->file, file_info) < 0)
+ error (1, errno, "could not stat %s", finfo->file);
+ if (file2_info.st_size <= file_info->st_size)
+ fail = 1;
+ }
+
+ if (! fail)
+ {
+# define BINARY "Binary"
+ char buf[sizeof BINARY];
+ unsigned int c;
+
+ /* Check the diff output to make sure patch will be handle it. */
+ e = CVS_FOPEN (finfo->file, "r");
+ if (e == NULL)
+ error (1, errno, "could not open diff output file %s",
+ finfo->fullname);
+ c = fread (buf, 1, sizeof BINARY - 1, e);
+ buf[c] = '\0';
+ if (strcmp (buf, BINARY) == 0)
+ {
+ /* These are binary files. We could use diff -a, but
+ patch can't handle that. */
+ fail = 1;
+ }
+ fclose (e);
+ }
+
+ if (! fail)
+ {
+ Vers_TS *xvers_ts;
+
+ /* Stat the original RCS file, and then adjust it the way
+ that RCS_checkout would. FIXME: This is an abstraction
+ violation. */
+ if (stat (vers_ts->srcfile->path, file_info) < 0)
+ error (1, errno, "could not stat %s", vers_ts->srcfile->path);
+ if (chmod (finfo->file,
+ file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
+ < 0)
+ error (0, errno, "cannot change mode of file %s", finfo->file);
+ if (cvswrite
+ && !fileattr_get (finfo->file, "_watched"))
+ xchmod (finfo->file, 1);
+
+ /* This stuff is just copied blindly from checkout_file. I
+ don't really know what it does. */
+ xvers_ts = Version_TS (finfo, options, tag, date,
+ force_tag_match, 0);
+ if (strcmp (xvers_ts->options, "-V4") == 0)
+ xvers_ts->options[0] = '\0';
+
+ Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
+ xvers_ts->ts_user, xvers_ts->options,
+ xvers_ts->tag, xvers_ts->date, NULL);
+
+ if (stat (finfo->file, file_info) < 0)
+ error (1, errno, "could not stat %s", finfo->file);
+
+ /* If this is really Update and not Checkout, record history. */
+ if (strcmp (cvs_cmd_name, "update") == 0)
+ history_write ('P', finfo->update_dir, xvers_ts->vn_rcs,
+ finfo->file, finfo->repository);
+
+ freevers_ts (&xvers_ts);
+
+ if (!really_quiet)
+ {
+ if (trace)
+ write_letter (finfo, 'P');
+ else
+ write_letter (finfo, 'U');
+ }
+ }
+ else
+ {
+ int old_errno = errno; /* save errno value over the rename */
+
+ if (isfile (backup))
+ rename_file (backup, finfo->file);
+
+ if (retcode != 0 && retcode != 1)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
+ "could not diff %s", finfo->fullname);
+
+ *docheckout = 1;
+ retval = retcode;
+ }
+
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
+ if (unlink_file (file1) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", file1);
+ if (unlink_file (file2) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", file2);
+
+ free (backup);
+ free (file1);
+ free (file2);
+ return retval;
+}
+
+
+
+/* Write data to a file. Record whether the last byte written was a
+ newline. Optionally compute a checksum. This is called by
+ patch_file via RCS_checkout. */
+
+static void
+patch_file_write (void *callerdat, const char *buffer, size_t len)
+{
+ struct patch_file_data *data = (struct patch_file_data *) callerdat;
+
+ if (fwrite (buffer, 1, len, data->fp) != len)
+ error (1, errno, "cannot write %s", data->filename);
+
+ data->final_nl = (buffer[len - 1] == '\n');
+
+ if (data->compute_checksum)
+ md5_process_bytes (buffer, len, &data->context);
+}
+
+#endif /* SERVER_SUPPORT */
+
+/*
+ * Several of the types we process only print a bit of information consisting
+ * of a single letter and the name.
+ */
+void
+write_letter (struct file_info *finfo, int letter)
+{
+ if (!really_quiet)
+ {
+ char *tag = NULL;
+ /* Big enough for "+updated" or any of its ilk. */
+ char buf[80];
+
+ switch (letter)
+ {
+ case 'U':
+ tag = "updated";
+ break;
+ default:
+ /* We don't yet support tagged output except for "U". */
+ break;
+ }
+
+ if (tag != NULL)
+ {
+ sprintf (buf, "+%s", tag);
+ cvs_output_tagged (buf, NULL);
+ }
+ buf[0] = letter;
+ buf[1] = ' ';
+ buf[2] = '\0';
+ cvs_output_tagged ("text", buf);
+ cvs_output_tagged ("fname", finfo->fullname);
+ cvs_output_tagged ("newline", NULL);
+ if (tag != NULL)
+ {
+ sprintf (buf, "-%s", tag);
+ cvs_output_tagged (buf, NULL);
+ }
+ }
+ return;
+}
+
+
+
+/* Reregister a file after a merge. */
+static void
+RegisterMerge (struct file_info *finfo, Vers_TS *vers,
+ const char *backup, int has_conflicts)
+{
+ /* This file is the result of a merge, which means that it has
+ been modified. We use a special timestamp string which will
+ not compare equal to any actual timestamp. */
+ char *cp = NULL;
+
+ if (has_conflicts)
+ {
+ time (&last_register_time);
+ cp = time_stamp (finfo->file);
+ }
+ Register (finfo->entries, finfo->file, vers->vn_rcs ? vers->vn_rcs : "0",
+ "Result of merge", vers->options, vers->tag, vers->date, cp);
+ if (cp)
+ free (cp);
+
+#ifdef SERVER_SUPPORT
+ /* Send the new contents of the file before the message. If we
+ wanted to be totally correct, we would have the client write
+ the message only after the file has safely been written. */
+ if (server_active)
+ {
+ server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
+ backup);
+ server_updated (finfo, vers, SERVER_MERGED, (mode_t) -1, NULL, NULL);
+ }
+#endif
+}
+
+
+
+/*
+ * Do all the magic associated with a file which needs to be merged
+ */
+static int
+merge_file (struct file_info *finfo, Vers_TS *vers)
+{
+ char *backup;
+ int status;
+ int retval;
+
+ assert (vers->vn_user);
+
+ /*
+ * The users currently modified file is moved to a backup file name
+ * ".#filename.version", so that it will stay around for a few days
+ * before being automatically removed by some cron daemon. The "version"
+ * is the version of the file that the user was most up-to-date with
+ * before the merge.
+ */
+ backup = Xasprintf ("%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
+
+ if (unlink_file (backup) && !existence_error (errno))
+ error (0, errno, "unable to remove %s", backup);
+ copy_file (finfo->file, backup);
+ xchmod (finfo->file, 1);
+
+ if (strcmp (vers->options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file)
+ || special_file_mismatch (finfo, NULL, vers->vn_rcs))
+ {
+ /* For binary files, a merge is always a conflict. Same for
+ files whose permissions or linkage do not match. We give the
+ user the two files, and let them resolve it. It is possible
+ that we should require a "touch foo" or similar step before
+ we allow a checkin. */
+
+ /* TODO: it may not always be necessary to regard a permission
+ mismatch as a conflict. The working file and the RCS file
+ have a common ancestor `A'; if the working file's permissions
+ match A's, then it's probably safe to overwrite them with the
+ RCS permissions. Only if the working file, the RCS file, and
+ A all disagree should this be considered a conflict. But more
+ thought needs to go into this, and in the meantime it is safe
+ to treat any such mismatch as an automatic conflict. -twp */
+
+ status = RCS_checkout (finfo->rcs, finfo->file, vers->vn_rcs,
+ vers->tag, vers->options, NULL, NULL, NULL);
+ if (status)
+ {
+ error (0, 0, "failed to check out `%s' file", finfo->fullname);
+ error (0, 0, "restoring `%s' from backup file `%s'",
+ finfo->fullname, backup);
+ rename_file (backup, finfo->file);
+ retval = 1;
+ goto out;
+ }
+
+ xchmod (finfo->file, 1);
+
+ RegisterMerge (finfo, vers, backup, 1);
+
+ /* Is there a better term than "nonmergeable file"? What we
+ really mean is, not something that CVS cannot or does not
+ want to merge (there might be an external manual or
+ automatic merge process). */
+ error (0, 0, "nonmergeable file needs merge");
+ error (0, 0, "revision %s from repository is now in %s",
+ vers->vn_rcs, finfo->fullname);
+ error (0, 0, "file from working directory is now in %s", backup);
+ write_letter (finfo, 'C');
+
+ history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
+ finfo->repository);
+ retval = 0;
+ goto out;
+ }
+
+ status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
+ vers->options, vers->vn_user, vers->vn_rcs);
+ if (status != 0 && status != 1)
+ {
+ error (0, status == -1 ? errno : 0,
+ "could not merge revision %s of %s", vers->vn_user,
finfo->fullname);
+ error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
+ finfo->fullname, backup);
+ rename_file (backup, finfo->file);
+ retval = 1;
+ goto out;
+ }
+
+ if (strcmp (vers->options, "-V4") == 0)
+ vers->options[0] = '\0';
+
+ /* fix up the vers structure, in case it is used by join */
+ if (join_rev1)
+ {
+ /* FIXME: Throwing away the original revision info is almost
+ certainly wrong -- what if join_rev1 is "BASE"? */
+ if (vers->vn_user != NULL)
+ free (vers->vn_user);
+ vers->vn_user = xstrdup (vers->vn_rcs);
+ }
+
+ RegisterMerge (finfo, vers, backup, status);
+
+ if (status == 1)
+ {
+ error (0, 0, "conflicts found in %s", finfo->fullname);
+
+ write_letter (finfo, 'C');
+
+ history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
+ finfo->repository);
+
+ }
+ else /* status == 0 */
+ {
+ history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
+ finfo->repository);
+
+ /* FIXME: the noexec case is broken. RCS_merge could be doing the
+ xcmp on the temporary files without much hassle, I think. */
+ if (!noexec && !xcmp (backup, finfo->file))
+ {
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" already contains the differences between ", 0);
+ cvs_output (vers->vn_user, 0);
+ cvs_output (" and ", 0);
+ cvs_output (vers->vn_rcs, 0);
+ cvs_output ("\n", 1);
+
+ retval = 0;
+ goto out;
+ }
+
+ write_letter (finfo, 'M');
+ }
+ retval = 0;
+ out:
+ free (backup);
+ return retval;
+}
+
+
+
+/*
+ * Do all the magic associated with a file which needs to be joined
+ * (reached via the -j option to checkout or update).
+ *
+ * INPUTS
+ * finfo File information about the destination file.
+ * vers The Vers_TS structure for finfo.
+ *
+ * GLOBALS
+ * join_rev1 From the command line.
+ * join_rev2 From the command line.
+ * server_active Natch.
+ *
+ * ASSUMPTIONS
+ * 1. Is not called in client mode.
+ */
+static void
+join_file (struct file_info *finfo, Vers_TS *vers, const char *j1, const char
*j2)
+{
+ char *backup;
+ char *t_options;
+ int status;
+
+ char *rev1;
+ char *rev2;
+ char *jrev1;
+ char *jrev2;
+ char *jdate1;
+ char *jdate2;
+
+ TRACE (TRACE_FUNCTION, "join_file(%s, %s%s%s%s, %s, %s)",
+ finfo->file,
+ vers->tag ? vers->tag : "",
+ vers->tag ? " (" : "",
+ vers->vn_rcs ? vers->vn_rcs : "",
+ vers->tag ? ")" : "",
+ j1 ? j1 : "",
+ j2 ? j2 : "");
+
+ jrev1 = j1;
+ jrev2 = j2;
+ jdate1 = join_date1;
+ jdate2 = join_date2;
+
+ /* Determine if we need to do anything at all. */
+ if (vers->srcfile == NULL ||
+ vers->srcfile->path == NULL)
+ {
+ return;
+ }
+
+ /* If only one join revision is specified, it becomes the second
+ revision. */
+ if (jrev2 == NULL)
+ {
+ jrev2 = jrev1;
+ jrev1 = NULL;
+ jdate2 = jdate1;
+ jdate1 = NULL;
+ }
+
+ /* FIXME: Need to handle "BASE" for jrev1 and/or jrev2. Note caveat
+ below about vn_user. */
+
+ /* Convert the second revision, walking branches and dates. */
+ rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, NULL);
+
+ /* If this is a merge of two revisions, get the first revision.
+ If only one join tag was specified, then the first revision is
+ the greatest common ancestor of the second revision and the
+ working file. */
+ if (jrev1 != NULL)
+ rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, NULL);
+ else
+ {
+ /* Note that we use vn_rcs here, since vn_user may contain a
+ special string such as "-nn". */
+ if (vers->vn_rcs == NULL)
+ rev1 = NULL;
+ else if (rev2 == NULL)
+ {
+ /* This means that the file never existed on the branch.
+ It does not mean that the file was removed on the
+ branch: that case is represented by a dead rev2. If
+ the file never existed on the branch, then we have
+ nothing to merge, so we just return. */
+ return;
+ }
+ else
+ rev1 = gca (vers->vn_rcs, rev2);
+ }
+
+ /* Handle a nonexistent or dead merge target. */
+ if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
+ {
+ char *mrev;
+
+ if (rev2 != NULL)
+ free (rev2);
+
+ /* If the first revision doesn't exist either, then there is
+ no change between the two revisions, so we don't do
+ anything. */
+ if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
+ {
+ if (rev1 != NULL)
+ free (rev1);
+ return;
+ }
+
+ /* If we are merging two revisions, then the file was removed
+ between the first revision and the second one. In this
+ case we want to mark the file for removal.
+
+ If we are merging one revision, then the file has been
+ removed between the greatest common ancestor and the merge
+ revision. From the perspective of the branch on to which
+ we ar emerging, which may be the trunk, either 1) the file
+ does not currently exist on the target, or 2) the file has
+ not been modified on the target branch since the greatest
+ common ancestor, or 3) the file has been modified on the
+ target branch since the greatest common ancestor. In case
+ 1 there is nothing to do. In case 2 we mark the file for
+ removal. In case 3 we have a conflict.
+
+ Note that the handling is slightly different depending upon
+ whether one or two join targets were specified. If two
+ join targets were specified, we don't check whether the
+ file was modified since a given point. My reasoning is
+ that if you ask for an explicit merge between two tags,
+ then you want to merge in whatever was changed between
+ those two tags. If a file was removed between the two
+ tags, then you want it to be removed. However, if you ask
+ for a merge of a branch, then you want to merge in all
+ changes which were made on the branch. If a file was
+ removed on the branch, that is a change to the file. If
+ the file was also changed on the main line, then that is
+ also a change. These two changes--the file removal and the
+ modification--must be merged. This is a conflict. */
+
+ /* If the user file is dead, or does not exist, or has been
+ marked for removal, then there is nothing to do. */
+ if (vers->vn_user == NULL
+ || vers->vn_user[0] == '-'
+ || RCS_isdead (vers->srcfile, vers->vn_user))
+ {
+ if (rev1 != NULL)
+ free (rev1);
+ return;
+ }
+
+ /* If the user file has been marked for addition, or has been
+ locally modified, then we have a conflict which we can not
+ resolve. No_Difference will already have been called in
+ this case, so comparing the timestamps is sufficient to
+ determine whether the file is locally modified. */
+ if (strcmp (vers->vn_user, "0") == 0
+ || (vers->ts_user != NULL
+ && strcmp (vers->ts_user, vers->ts_rcs) != 0))
+ {
+ if (jdate2 != NULL)
+ error (0, 0,
+ "file %s is locally modified, but has been removed in
revision %s as of %s",
+ finfo->fullname, jrev2, jdate2);
+ else
+ error (0, 0,
+ "file %s is locally modified, but has been removed in
revision %s",
+ finfo->fullname, jrev2);
+
+ /* FIXME: Should we arrange to return a non-zero exit
+ status? */
+
+ if (rev1 != NULL)
+ free (rev1);
+
+ return;
+ }
+
+ /* If only one join tag was specified, and the user file has
+ been changed since the greatest common ancestor (rev1),
+ then there is a conflict we can not resolve. See above for
+ the rationale. */
+ if (j2 == NULL
+ && strcmp (rev1, vers->vn_user) != 0)
+ {
+ if (jdate2 != NULL)
+ error (0, 0,
+ "file %s has been modified, but has been removed in
revision %s as of %s",
+ finfo->fullname, jrev2, jdate2);
+ else
+ error (0, 0,
+ "file %s has been modified, but has been removed in
revision %s",
+ finfo->fullname, jrev2);
+
+ /* FIXME: Should we arrange to return a non-zero exit
+ status? */
+
+ if (rev1 != NULL)
+ free (rev1);
+
+ return;
+ }
+
+ if (rev1 != NULL)
+ free (rev1);
+
+ /* The user file exists and has not been modified. Mark it
+ for removal. FIXME: If we are doing a checkout, this has
+ the effect of first checking out the file, and then
+ removing it. It would be better to just register the
+ removal.
+
+ The same goes for a removal then an add. e.g.
+ cvs up -rbr -jbr2 could remove and readd the same file
+ */
+ /* save the rev since server_updated might invalidate it */
+ mrev = Xasprintf ("-%s", vers->vn_user);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ server_scratch (finfo->file);
+ server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
+ NULL, NULL);
+ }
+#endif
+ Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
+ vers->options, vers->tag, vers->date, vers->ts_conflict);
+ free (mrev);
+ /* We need to check existence_error here because if we are
+ running as the server, and the file is up to date in the
+ working directory, the client will not have sent us a copy. */
+ if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
+ error (0, errno, "cannot remove file %s", finfo->fullname);
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ server_checked_in (finfo->file, finfo->update_dir,
+ finfo->repository);
+#endif
+ if (! really_quiet)
+ error (0, 0, "scheduling `%s' for removal", finfo->fullname);
+
+ return;
+ }
+
+ /* If the two merge revisions are the same, then there is nothing
+ * to do. This needs to be checked before the rev2 == up-to-date base
+ * revision check tha comes next. Otherwise, rev1 can == rev2 and get an
+ * "already contains the changes between <rev1> and <rev1>" message.
+ */
+ if (rev1 && strcmp (rev1, rev2) == 0)
+ {
+ free (rev1);
+ free (rev2);
+ return;
+ }
+
+ /* If we know that the user file is up-to-date, then it becomes an
+ * optimization to skip the merge when rev2 is the same as the base
+ * revision. i.e. we know that diff3(file2,file1,file2) will produce
+ * file2.
+ */
+ if (vers->vn_user != NULL && vers->ts_user != NULL
+ && strcmp (vers->ts_user, vers->ts_rcs) == 0
+ && strcmp (rev2, vers->vn_user) == 0)
+ {
+ if (!really_quiet)
+ {
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" already contains the differences between ", 0);
+ cvs_output (rev1 ? rev1 : "creation", 0);
+ cvs_output (" and ", 0);
+ cvs_output (rev2, 0);
+ cvs_output ("\n", 1);
+ }
+
+ if (rev1 != NULL)
+ free (rev1);
+ free (rev2);
+
+ return;
+ }
+
+ /* If rev1 is dead or does not exist, then the file was added
+ between rev1 and rev2. */
+ if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
+ {
+ if (rev1 != NULL)
+ free (rev1);
+ free (rev2);
+
+ /* If the file does not exist in the working directory, then
+ we can just check out the new revision and mark it for
+ addition. */
+ if (vers->vn_user == NULL)
+ {
+ char *saved_options = options;
+ Vers_TS *xvers;
+
+ xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
+
+ /* Reset any keyword expansion option. Otherwise, when a
+ command like `cvs update -kk -jT1 -jT2' creates a new file
+ (because a file had the T2 tag, but not T1), the subsequent
+ commit of that just-added file effectively would set the
+ admin `-kk' option for that file in the repository. */
+ options = NULL;
+
+ /* FIXME: If checkout_file fails, we should arrange to
+ return a non-zero exit status. */
+ status = checkout_file (finfo, xvers, 1, 0, 1);
+ options = saved_options;
+
+ freevers_ts (&xvers);
+
+ return;
+ }
+
+ /* The file currently exists in the working directory, so we
+ have a conflict which we can not resolve. Note that this
+ is true even if the file is marked for addition or removal. */
+
+ if (jdate2 != NULL)
+ error (0, 0,
+ "file %s exists, but has been added in revision %s as of %s",
+ finfo->fullname, jrev2, jdate2);
+ else
+ error (0, 0,
+ "file %s exists, but has been added in revision %s",
+ finfo->fullname, jrev2);
+
+ return;
+ }
+
+ /* If there is no working file, then we can't do the merge. */
+ if (vers->vn_user == NULL || vers->vn_user[0] == '-')
+ {
+ free (rev1);
+ free (rev2);
+
+ if (jdate2 != NULL)
+ error (0, 0,
+ "file %s does not exist, but is present in revision %s as of
%s",
+ finfo->fullname, jrev2, jdate2);
+ else
+ error (0, 0,
+ "file %s does not exist, but is present in revision %s",
+ finfo->fullname, jrev2);
+
+ /* FIXME: Should we arrange to return a non-zero exit status? */
+
+ return;
+ }
+
+#ifdef SERVER_SUPPORT
+ if (server_active && !isreadable (finfo->file))
+ {
+ int retcode;
+ /* The file is up to date. Need to check out the current contents. */
+ /* FIXME - see the FIXME comment above the call to RCS_checkout in the
+ * patch_file function.
+ */
+ retcode = RCS_checkout (vers->srcfile, finfo->file,
+ vers->vn_user, vers->tag,
+ NULL, RUN_TTY, NULL, NULL);
+ if (retcode != 0)
+ error (1, 0,
+ "failed to check out %s file", finfo->fullname);
+ }
+#endif
+
+ /*
+ * The users currently modified file is moved to a backup file name
+ * ".#filename.version", so that it will stay around for a few days
+ * before being automatically removed by some cron daemon. The "version"
+ * is the version of the file that the user was most up-to-date with
+ * before the merge.
+ */
+ backup = Xasprintf ("%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
+
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
+ copy_file (finfo->file, backup);
+ xchmod (finfo->file, 1);
+
+ t_options = vers->options;
+#if 0
+ if (*t_options == '\0')
+ t_options = "-kk"; /* to ignore keyword expansions */
+#endif
+
+ /* If the source of the merge is the same as the working file
+ revision, then we can just RCS_checkout the target (no merging
+ as such). In the text file case, this is probably quite
+ similar to the RCS_merge, but in the binary file case,
+ RCS_merge gives all kinds of trouble. */
+ if (vers->vn_user != NULL
+ && strcmp (rev1, vers->vn_user) == 0
+ /* See comments above about how No_Difference has already been
+ called. */
+ && vers->ts_user != NULL
+ && strcmp (vers->ts_user, vers->ts_rcs) == 0
+
+ /* Avoid this in the text file case. See below for why.
+ */
+ && (strcmp (t_options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file)))
+ {
+ /* FIXME: Verify my comment below:
+ *
+ * RCS_merge does nothing with keywords. It merges the changes between
+ * two revisions without expanding the keywords (it might expand in
+ * -kk mode before computing the diff between rev1 and rev2 - I'm not
+ * sure). In other words, the keyword lines in the current work file
+ * get left alone.
+ *
+ * Therfore, checking out the destination revision (rev2) is probably
+ * incorrect in the text case since we should see the keywords that were
+ * substituted into the original file at the time it was checked out
+ * and not the keywords from rev2.
+ *
+ * Also, it is safe to pass in NULL for nametag since we know no
+ * substitution is happening during the binary mode checkout.
+ */
+ if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, t_options,
+ RUN_TTY, NULL, NULL) != 0)
+ status = 2;
+ else
+ status = 0;
+
+ /* OK, this is really stupid. RCS_checkout carefully removes
+ write permissions, and we carefully put them back. But
+ until someone gets around to fixing it, that seems like the
+ easiest way to get what would seem to be the right mode.
+ I don't check CVSWRITE or _watched; I haven't thought about
+ that in great detail, but it seems like a watched file should
+ be checked out (writable) after a merge. */
+ xchmod (finfo->file, 1);
+
+ /* Traditionally, the text file case prints a whole bunch of
+ scary looking and verbose output which fails to tell the user
+ what is really going on (it gives them rev1 and rev2 but doesn't
+ indicate in any way that rev1 == vn_user). I think just a
+ simple "U foo" is good here; it seems analogous to the case in
+ which the file was added on the branch in terms of what to
+ print. */
+ write_letter (finfo, 'U');
+ }
+ else if (strcmp (t_options, "-kb") == 0
+ || wrap_merge_is_copy (finfo->file)
+ || special_file_mismatch (finfo, rev1, rev2))
+ {
+ /* We are dealing with binary files, or files with a
+ permission/linkage mismatch (this second case only occurs when
+ PRESERVE_PERMISSIONS_SUPPORT is enabled), and real merging would
+ need to take place. This is a conflict. We give the user
+ the two files, and let them resolve it. It is possible
+ that we should require a "touch foo" or similar step before
+ we allow a checkin. */
+ if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL,
+ t_options, RUN_TTY, NULL, NULL) != 0)
+ status = 2;
+ else
+ status = 0;
+
+ /* OK, this is really stupid. RCS_checkout carefully removes
+ write permissions, and we carefully put them back. But
+ until someone gets around to fixing it, that seems like the
+ easiest way to get what would seem to be the right mode.
+ I don't check CVSWRITE or _watched; I haven't thought about
+ that in great detail, but it seems like a watched file should
+ be checked out (writable) after a merge. */
+ xchmod (finfo->file, 1);
+
+ /* Hmm. We don't give them REV1 anywhere. I guess most people
+ probably don't have a 3-way merge tool for the file type in
+ question, and might just get confused if we tried to either
+ provide them with a copy of the file from REV1, or even just
+ told them what REV1 is so they can get it themself, but it
+ might be worth thinking about. */
+ /* See comment in merge_file about the "nonmergeable file"
+ terminology. */
+ error (0, 0, "nonmergeable file needs merge");
+ error (0, 0, "revision %s from repository is now in %s",
+ rev2, finfo->fullname);
+ error (0, 0, "file from working directory is now in %s", backup);
+ write_letter (finfo, 'C');
+ }
+ else
+ status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
+ t_options, rev1, rev2);
+
+ if (status != 0)
+ {
+ if (status != 1)
+ {
+ error (0, status == -1 ? errno : 0,
+ "could not merge revision %s of %s", rev2, finfo->fullname);
+ error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
+ finfo->fullname, backup);
+ rename_file (backup, finfo->file);
+ }
+ }
+ else /* status == 0 */
+ {
+ /* FIXME: the noexec case is broken. RCS_merge could be doing the
+ xcmp on the temporary files without much hassle, I think. */
+ if (!noexec && !xcmp (backup, finfo->file))
+ {
+ if (!really_quiet)
+ {
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" already contains the differences between ", 0);
+ cvs_output (rev1, 0);
+ cvs_output (" and ", 0);
+ cvs_output (rev2, 0);
+ cvs_output ("\n", 1);
+ }
+
+ /* and skip the registering and sending the new file since it
+ * hasn't been updated.
+ */
+ goto out;
+ }
+ }
+
+ /* The file has changed, but if we just checked it out it may
+ still have the same timestamp it did when it was first
+ registered above in checkout_file. We register it again with a
+ dummy timestamp to make sure that later runs of CVS will
+ recognize that it has changed.
+
+ We don't actually need to register again if we called
+ RCS_checkout above, and we aren't running as the server.
+ However, that is not the normal case, and calling Register
+ again won't cost much in that case. */
+ RegisterMerge (finfo, vers, backup, status);
+
+out:
+ free (rev1);
+ free (rev2);
+ free (backup);
+}
+
+
+
+/*
+ * Report whether revisions REV1 and REV2 of FINFO agree on:
+ * . file ownership
+ * . permissions
+ * . major and minor device numbers
+ * . symbolic links
+ * . hard links
+ *
+ * If either REV1 or REV2 is NULL, the working copy is used instead.
+ *
+ * Return 1 if the files differ on these data.
+ */
+
+int
+special_file_mismatch (struct file_info *finfo, char *rev1, char *rev2)
+{
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ struct stat sb;
+ RCSVers *vp;
+ Node *n;
+ uid_t rev1_uid, rev2_uid;
+ gid_t rev1_gid, rev2_gid;
+ mode_t rev1_mode, rev2_mode;
+ unsigned long dev_long;
+ dev_t rev1_dev, rev2_dev;
+ char *rev1_symlink = NULL;
+ char *rev2_symlink = NULL;
+ List *rev1_hardlinks = NULL;
+ List *rev2_hardlinks = NULL;
+ int check_uids, check_gids, check_modes;
+ int result;
+
+ /* If we don't care about special file info, then
+ don't report a mismatch in any case. */
+ if (!preserve_perms)
+ return 0;
+
+ /* When special_file_mismatch is called from No_Difference, the
+ RCS file has been only partially parsed. We must read the
+ delta tree in order to compare special file info recorded in
+ the delta nodes. (I think this is safe. -twp) */
+ if (finfo->rcs->flags & PARTIAL)
+ RCS_reparsercsfile (finfo->rcs, NULL, NULL);
+
+ check_uids = check_gids = check_modes = 1;
+
+ /* Obtain file information for REV1. If this is null, then stat
+ finfo->file and use that info. */
+ /* If a revision does not know anything about its status,
+ then presumably it doesn't matter, and indicates no conflict. */
+
+ if (rev1 == NULL)
+ {
+ ssize_t rsize;
+
+ if ((rsize = islink (finfo->file)) > 0)
+ rev1_symlink = Xreadlink (finfo->file, rsize);
+ else
+ {
+# ifdef HAVE_STRUCT_STAT_ST_RDEV
+ if (lstat (finfo->file, &sb) < 0)
+ error (1, errno, "could not get file information for %s",
+ finfo->file);
+ rev1_uid = sb.st_uid;
+ rev1_gid = sb.st_gid;
+ rev1_mode = sb.st_mode;
+ if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
+ rev1_dev = sb.st_rdev;
+# else
+ error (1, 0, "cannot handle device files on this system (%s)",
+ finfo->file);
+# endif
+ }
+ rev1_hardlinks = list_linked_files_on_disk (finfo->file);
+ }
+ else
+ {
+ n = findnode (finfo->rcs->versions, rev1);
+ vp = n->data;
+
+ n = findnode (vp->other_delta, "symlink");
+ if (n != NULL)
+ rev1_symlink = xstrdup (n->data);
+ else
+ {
+ n = findnode (vp->other_delta, "owner");
+ if (n == NULL)
+ check_uids = 0; /* don't care */
+ else
+ rev1_uid = strtoul (n->data, NULL, 10);
+
+ n = findnode (vp->other_delta, "group");
+ if (n == NULL)
+ check_gids = 0; /* don't care */
+ else
+ rev1_gid = strtoul (n->data, NULL, 10);
+
+ n = findnode (vp->other_delta, "permissions");
+ if (n == NULL)
+ check_modes = 0; /* don't care */
+ else
+ rev1_mode = strtoul (n->data, NULL, 8);
+
+ n = findnode (vp->other_delta, "special");
+ if (n == NULL)
+ rev1_mode |= S_IFREG;
+ else
+ {
+ /* If the size of `ftype' changes, fix the sscanf call also */
+ char ftype[16];
+ if (sscanf (n->data, "%15s %lu", ftype,
+ &dev_long) < 2)
+ error (1, 0, "%s:%s has bad `special' newphrase %s",
+ finfo->file, rev1, (char *)n->data);
+ rev1_dev = dev_long;
+ if (strcmp (ftype, "character") == 0)
+ rev1_mode |= S_IFCHR;
+ else if (strcmp (ftype, "block") == 0)
+ rev1_mode |= S_IFBLK;
+ else
+ error (0, 0, "%s:%s unknown file type `%s'",
+ finfo->file, rev1, ftype);
+ }
+
+ rev1_hardlinks = vp->hardlinks;
+ if (rev1_hardlinks == NULL)
+ rev1_hardlinks = getlist();
+ }
+ }
+
+ /* Obtain file information for REV2. */
+ if (rev2 == NULL)
+ {
+ ssize_t rsize;
+
+ if ((rsize = islink (finfo->file)) > 0)
+ rev2_symlink = Xreadlink (finfo->file, rsize);
+ else
+ {
+# ifdef HAVE_STRUCT_STAT_ST_RDEV
+ if (lstat (finfo->file, &sb) < 0)
+ error (1, errno, "could not get file information for %s",
+ finfo->file);
+ rev2_uid = sb.st_uid;
+ rev2_gid = sb.st_gid;
+ rev2_mode = sb.st_mode;
+ if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
+ rev2_dev = sb.st_rdev;
+# else
+ error (1, 0, "cannot handle device files on this system (%s)",
+ finfo->file);
+# endif
+ }
+ rev2_hardlinks = list_linked_files_on_disk (finfo->file);
+ }
+ else
+ {
+ n = findnode (finfo->rcs->versions, rev2);
+ vp = n->data;
+
+ n = findnode (vp->other_delta, "symlink");
+ if (n != NULL)
+ rev2_symlink = xstrdup (n->data);
+ else
+ {
+ n = findnode (vp->other_delta, "owner");
+ if (n == NULL)
+ check_uids = 0; /* don't care */
+ else
+ rev2_uid = strtoul (n->data, NULL, 10);
+
+ n = findnode (vp->other_delta, "group");
+ if (n == NULL)
+ check_gids = 0; /* don't care */
+ else
+ rev2_gid = strtoul (n->data, NULL, 10);
+
+ n = findnode (vp->other_delta, "permissions");
+ if (n == NULL)
+ check_modes = 0; /* don't care */
+ else
+ rev2_mode = strtoul (n->data, NULL, 8);
+
+ n = findnode (vp->other_delta, "special");
+ if (n == NULL)
+ rev2_mode |= S_IFREG;
+ else
+ {
+ /* If the size of `ftype' changes, fix the sscanf call also */
+ char ftype[16];
+ if (sscanf (n->data, "%15s %lu", ftype,
+ &dev_long) < 2)
+ error (1, 0, "%s:%s has bad `special' newphrase %s",
+ finfo->file, rev2, (char *)n->data);
+ rev2_dev = dev_long;
+ if (strcmp (ftype, "character") == 0)
+ rev2_mode |= S_IFCHR;
+ else if (strcmp (ftype, "block") == 0)
+ rev2_mode |= S_IFBLK;
+ else
+ error (0, 0, "%s:%s unknown file type `%s'",
+ finfo->file, rev2, ftype);
+ }
+
+ rev2_hardlinks = vp->hardlinks;
+ if (rev2_hardlinks == NULL)
+ rev2_hardlinks = getlist();
+ }
+ }
+
+ /* Check the user/group ownerships and file permissions, printing
+ an error for each mismatch found. Return 0 if all characteristics
+ matched, and 1 otherwise. */
+
+ result = 0;
+
+ /* Compare symlinks first, since symlinks are simpler (don't have
+ any other characteristics). */
+ if (rev1_symlink != NULL && rev2_symlink == NULL)
+ {
+ error (0, 0, "%s is a symbolic link",
+ (rev1 == NULL ? "working file" : rev1));
+ result = 1;
+ }
+ else if (rev1_symlink == NULL && rev2_symlink != NULL)
+ {
+ error (0, 0, "%s is a symbolic link",
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+ else if (rev1_symlink != NULL)
+ result = (strcmp (rev1_symlink, rev2_symlink) == 0);
+ else
+ {
+ /* Compare user ownership. */
+ if (check_uids && rev1_uid != rev2_uid)
+ {
+ error (0, 0, "%s: owner mismatch between %s and %s",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+
+ /* Compare group ownership. */
+ if (check_gids && rev1_gid != rev2_gid)
+ {
+ error (0, 0, "%s: group mismatch between %s and %s",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+
+ /* Compare permissions. */
+ if (check_modes &&
+ (rev1_mode & 07777) != (rev2_mode & 07777))
+ {
+ error (0, 0, "%s: permission mismatch between %s and %s",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+
+ /* Compare device file characteristics. */
+ if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
+ {
+ error (0, 0, "%s: %s and %s are different file types",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+ else if (S_ISBLK (rev1_mode))
+ {
+ if (rev1_dev != rev2_dev)
+ {
+ error (0, 0, "%s: device numbers of %s and %s do not match",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+ }
+
+ /* Compare hard links. */
+ if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0)
+ {
+ error (0, 0, "%s: hard linkage of %s and %s do not match",
+ finfo->file,
+ (rev1 == NULL ? "working file" : rev1),
+ (rev2 == NULL ? "working file" : rev2));
+ result = 1;
+ }
+ }
+
+ if (rev1_symlink != NULL)
+ free (rev1_symlink);
+ if (rev2_symlink != NULL)
+ free (rev2_symlink);
+ if (rev1_hardlinks != NULL)
+ dellist (&rev1_hardlinks);
+ if (rev2_hardlinks != NULL)
+ dellist (&rev2_hardlinks);
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+
+int
+joining (void)
+{
+ return join_rev1 || join_date1;
+}
Index: ccvs/src/vers_ts.c
diff -u /dev/null ccvs/src/vers_ts.c:1.65.8.1
--- /dev/null Tue Jan 17 15:41:25 2006
+++ ccvs/src/vers_ts.c Tue Jan 17 15:41:24 2006
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ */
+
+#include "cvs.h"
+#include "lstat.h"
+
+#ifdef SERVER_SUPPORT
+static void time_stamp_server (const char *, Vers_TS *, Entnode *);
+#endif
+
+/* Fill in and return a Vers_TS structure for the file FINFO.
+ *
+ * INPUTS
+ * finfo struct file_info data about the file to be examined.
+ * options Keyword expansion options, I think generally from the
+ * command line. Can be either NULL or "" to indicate
+ * none are specified here.
+ * tag Tag specified by user on the command line (via -r).
+ * date Date specified by user on the command line (via -D).
+ * force_tag_match If set and TAG is specified, will only set RET->vn_rcs
+ * based on TAG. Otherwise, if TAG is specified and does
+ * not exist in the file, RET->vn_rcs will be set to the
+ * head revision.
+ * set_time If set, set the last modification time of the user file
+ * specified by FINFO to the checkin time of RET->vn_rcs.
+ *
+ * RETURNS
+ * Vers_TS structure for FINFO.
+ */
+Vers_TS *
+Version_TS (struct file_info *finfo, char *options, char *tag, char *date,
+ int force_tag_match, int set_time)
+{
+ Node *p;
+ RCSNode *rcsdata;
+ Vers_TS *vers_ts;
+ struct stickydirtag *sdtp;
+ Entnode *entdata;
+ char *rcsexpand = NULL;
+ bool setstickynumtag = false;
+
+ /* get a new Vers_TS struct */
+
+ vers_ts = xmalloc (sizeof (Vers_TS));
+ memset (vers_ts, 0, sizeof (*vers_ts));
+
+ /*
+ * look up the entries file entry and fill in the version and timestamp
+ * if entries is NULL, there is no entries file so don't bother trying to
+ * look it up (used by checkout -P)
+ */
+ if (finfo->entries == NULL)
+ {
+ sdtp = NULL;
+ p = NULL;
+ }
+ else
+ {
+ p = findnode_fn (finfo->entries, finfo->file);
+ sdtp = finfo->entries->list->data; /* list-private */
+ }
+
+ if (p == NULL)
+ {
+ entdata = NULL;
+ }
+ else
+ {
+ entdata = p->data;
+
+ if (entdata->type == ENT_SUBDIR)
+ {
+ /* According to cvs.texinfo, the various fields in the Entries
+ file for a directory (other than the name) do not have a
+ defined meaning. We need to pass them along without getting
+ confused based on what is in them. Therefore we make sure
+ not to set vn_user and the like from Entries, add.c and
+ perhaps other code will expect these fields to be NULL for
+ a directory. */
+ vers_ts->entdata = entdata;
+ }
+ else
+#ifdef SERVER_SUPPORT
+ /* An entries line with "D" in the timestamp indicates that the
+ client sent Is-modified without sending Entry. So we want to
+ use the entries line for the sole purpose of telling
+ time_stamp_server what is up; we don't want the rest of CVS
+ to think there is an entries line. */
+ if (strcmp (entdata->timestamp, "D") != 0)
+#endif
+ {
+ vers_ts->vn_user = xstrdup (entdata->version);
+ vers_ts->ts_rcs = xstrdup (entdata->timestamp);
+ vers_ts->ts_conflict = xstrdup (entdata->conflict);
+ if (!(tag || date) && !(sdtp && sdtp->aflag))
+ {
+ vers_ts->tag = xstrdup (entdata->tag);
+ vers_ts->date = xstrdup (entdata->date);
+ }
+ vers_ts->entdata = entdata;
+ }
+ /* Even if we don't have an "entries line" as such
+ (vers_ts->entdata), we want to pick up options which could
+ have been from a Kopt protocol request. */
+ if (!options || *options == '\0')
+ {
+ if (!(sdtp && sdtp->aflag))
+ vers_ts->options = xstrdup (entdata->options);
+ }
+ }
+
+ /* Always look up the RCS keyword mode when we have an RCS archive. It
+ * will either be needed as a default or to avoid allowing the -k options
+ * specified on the command line from overriding binary mode (-kb).
+ */
+ if (finfo->rcs != NULL)
+ rcsexpand = RCS_getexpand (finfo->rcs);
+
+ /*
+ * -k options specified on the command line override (and overwrite)
+ * options stored in the entries file and default options from the RCS
+ * archive, except for binary mode (-kb).
+ */
+ if (options && *options != '\0')
+ {
+ if (vers_ts->options != NULL)
+ free (vers_ts->options);
+ if (rcsexpand != NULL && strcmp (rcsexpand, "b") == 0)
+ vers_ts->options = xstrdup ("-kb");
+ else
+ vers_ts->options = xstrdup (options);
+ }
+ else if ((!vers_ts->options || *vers_ts->options == '\0')
+ && rcsexpand != NULL)
+ {
+ /* If no keyword expansion was specified on command line,
+ use whatever was in the rcs file (if there is one). This
+ is how we, if we are the server, tell the client whether
+ a file is binary. */
+ if (vers_ts->options != NULL)
+ free (vers_ts->options);
+ vers_ts->options = xmalloc (strlen (rcsexpand) + 3);
+ strcpy (vers_ts->options, "-k");
+ strcat (vers_ts->options, rcsexpand);
+ }
+ if (!vers_ts->options)
+ vers_ts->options = xstrdup ("");
+
+ /*
+ * if tags were specified on the command line, they override what is in
+ * the Entries file
+ */
+ if (tag || date)
+ {
+ if (tag)
+ {
+ /* If a relative tag extension is used,
+ * prepend the local revision number, and set setstickynumtag
+ * so the sticky tag gets set to the numeric revision number
+ */
+ int position = 0;
+ bool head = false;
+ bool base = false;
+ bool root = false;
+ bool origin = false;
+ bool relative = true;
+ bool prepversion = false;
+ bool dot = false;
+
+ if (*tag == '.')
+ dot = true;
+
+ char *tmp = xstrdup (tag);
+ char *token = strtok (tmp, ".");
+ while (token)
+ {
+ if (position == 0)
+ {
+ if (dot)
+ {
+ if (!strcmp (token, TAG_ROOT))
+ root = true;
+ else if (!strcmp (token, TAG_ORIGIN))
+ origin = true;
+ else if (!strcmp (token, TAG_DOTHEAD))
+ head = true;
+ else if (!strcmp (token, TAG_DOTBASE))
+ {
+ base = true;
+ relative = false;
+ }
+ else if (strcmp (token, TAG_TRUNK)
+ && strcmp (token, TAG_COMMITID))
+ {
+ prepversion = true;
+ setstickynumtag = true;
+ break;
+ }
+ else
+ relative = false;
+ }
+ else
+ relative = false;
+ }
+ else if (!isdigit ((unsigned char) *token))
+ {
+ if (!strcmp (token, TAG_DOTHEAD))
+ setstickynumtag = false;
+ else if (!strcmp (token, TAG_PREVIOUS)
+ || !strcmp (token, TAG_NEXT))
+ setstickynumtag = true;
+ }
+ token = strtok (NULL, ".");
+ ++position;
+ }
+ free (tmp);
+
+ char *preptag = NULL;
+ if (prepversion)
+ preptag = vers_ts->vn_user;
+ else if (head || root || origin)
+ {
+ /* make sure that extensions to static tags
+ * force usage of numeric revisions so we
+ * don't send '.base.xxx' or alike to translate_tag()
+ */
+ if (vers_ts->entdata
+ && vers_ts->entdata->tag
+ && !(*vers_ts->entdata->tag == '.'
+ && !strcmp (vers_ts->entdata->tag+1, TAG_DOTBASE))
+ && strcmp (vers_ts->entdata->tag, TAG_HEAD)
+ && strcmp (vers_ts->entdata->tag, TAG_BASE))
+ {
+ preptag = vers_ts->entdata->tag;
+ }
+ else if (sdtp && !sdtp->aflag && sdtp->tag
+ && !isdigit (*sdtp->tag)
+ && !(*sdtp->tag == '.'
+ && !strcmp (sdtp->tag+1, TAG_DOTBASE))
+ && strcmp (sdtp->tag, TAG_HEAD)
+ && strcmp (sdtp->tag, TAG_BASE))
+ {
+ preptag = sdtp->tag;
+ }
+ else
+ preptag = vers_ts->vn_user;
+ }
+
+ if (preptag)
+ {
+ char *p = NULL;
+ if (origin
+ && (p = strrchr (preptag, '.'))
+ && !strcmp (p+1, TAG_ORIGIN))
+ {
+ tmp = xstrdup (preptag);
+ tmp[p-preptag] = '\0';
+ vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+ free (tmp);
+ }
+ else if (head
+ && (p = strrchr (preptag, '.'))
+ && !strcmp (p+1, TAG_DOTHEAD))
+ {
+ tmp = xstrdup (preptag);
+ tmp[p-preptag] = '\0';
+ vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+ free (tmp);
+ }
+ else
+ vers_ts->tag = Xasprintf ("%s%s", preptag, tag);
+ }
+ else if (base && position > 1)
+ {
+ const char *p = strchr (tag+1, '.');
+ vers_ts->tag = Xasprintf ("%s%s", vers_ts->vn_user, p);
+ }
+ else if (relative)
+ vers_ts->tag = Xasprintf (".%s%s", TAG_TRUNK, tag);
+ else
+ vers_ts->tag = xstrdup (tag);
+ }
+ if (date)
+ {
+ vers_ts->date = xstrdup (date);
+ }
+ }
+ else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
+ {
+ if (!vers_ts->tag)
+ {
+ vers_ts->tag = xstrdup (sdtp->tag);
+ vers_ts->nonbranch = sdtp->nonbranch;
+ }
+ if (!vers_ts->date)
+ vers_ts->date = xstrdup (sdtp->date);
+ }
+
+ /* Now look up the info on the source controlled file */
+ if (finfo->rcs != NULL)
+ {
+ rcsdata = finfo->rcs;
+ rcsdata->refcount++;
+ }
+ else if (finfo->repository != NULL)
+ rcsdata = RCS_parse (finfo->file, finfo->repository);
+ else
+ rcsdata = NULL;
+
+ if (rcsdata != NULL)
+ {
+ /* squirrel away the rcsdata pointer for others */
+ vers_ts->srcfile = rcsdata;
+
+ if (vers_ts->tag
+ && (!strcmp (vers_ts->tag, TAG_BASE)
+ || (*(vers_ts->tag) == '.'
+ && !strcmp (vers_ts->tag+1, TAG_DOTBASE)) ) )
+ {
+ vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
+ vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
+ }
+ else
+ {
+ int simple;
+
+ vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
+ vers_ts->date, force_tag_match,
+ &simple);
+ if (vers_ts->vn_rcs == NULL)
+ vers_ts->vn_tag = NULL;
+ else if (simple)
+ vers_ts->vn_tag = xstrdup (vers_ts->tag);
+ else
+ vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
+
+ if (vers_ts->vn_rcs && setstickynumtag)
+ {
+ /* for some relative tags, the sticky tag
+ * needs to be set to its numeric equivalent
+ */
+ free (vers_ts->tag);
+ vers_ts->tag = xstrdup (vers_ts->vn_rcs);
+ }
+ }
+
+ /*
+ * If the source control file exists and has the requested revision,
+ * get the Date the revision was checked in. If "user" exists, set
+ * its mtime.
+ */
+ if (set_time && vers_ts->vn_rcs != NULL)
+ {
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ server_modtime (finfo, vers_ts);
+ else
+#endif
+ {
+ struct utimbuf t;
+
+ memset (&t, 0, sizeof (t));
+ t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, 0, 0);
+ if (t.modtime != (time_t) -1)
+ {
+#ifdef UTIME_EXPECTS_WRITABLE
+ int change_it_back = 0;
+#endif
+
+ (void) time (&t.actime);
+
+#ifdef UTIME_EXPECTS_WRITABLE
+ if (!iswritable (finfo->file))
+ {
+ xchmod (finfo->file, 1);
+ change_it_back = 1;
+ }
+#endif /* UTIME_EXPECTS_WRITABLE */
+
+ /* This used to need to ignore existence_errors
+ (for cases like where update.c now clears
+ set_time if noexec, but didn't used to). I
+ think maybe now it doesn't (server_modtime does
+ not like those kinds of cases). */
+ (void) utime (finfo->file, &t);
+
+#ifdef UTIME_EXPECTS_WRITABLE
+ if (change_it_back)
+ xchmod (finfo->file, 0);
+#endif /* UTIME_EXPECTS_WRITABLE */
+ }
+ }
+ }
+ }
+
+ /* get user file time-stamp in ts_user */
+ if (finfo->entries != NULL)
+ {
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ time_stamp_server (finfo->file, vers_ts, entdata);
+ else
+#endif
+ vers_ts->ts_user = time_stamp (finfo->file);
+ }
+
+ return (vers_ts);
+}
+
+char *Version_resolve_relTag (struct file_info *finfo, const char *tag, bool
force_trunk)
+{
+ Node *p;
+ char *entversion = NULL;
+ char *enttag = NULL;
+ Entnode *entdata;
+ struct stickydirtag *sdtp;
+ char *res_vers = NULL;
+
+ /*
+ * look up the entries file entry and fill in the version and timestamp
+ * if entries is NULL, there is no entries file so don't bother trying to
+ * look it up
+ */
+ if (finfo->entries == NULL)
+ {
+ sdtp = NULL;
+ p = NULL;
+ }
+ else
+ {
+ p = findnode_fn (finfo->entries, finfo->file);
+ sdtp = finfo->entries->list->data; /* list-private */
+ }
+
+ if (p == NULL)
+ {
+ entdata = NULL;
+ }
+ else
+ {
+ entdata = p->data;
+
+ if (entdata->type != ENT_SUBDIR)
+#ifdef SERVER_SUPPORT
+ /* An entries line with "D" in the timestamp indicates that the
+ client sent Is-modified without sending Entry. So we want to
+ use the entries line for the sole purpose of telling
+ time_stamp_server what is up; we don't want the rest of CVS
+ to think there is an entries line. */
+ if (strcmp (entdata->timestamp, "D") != 0)
+#endif
+ {
+ entversion = xstrdup (entdata->version);
+ enttag = xstrdup (entdata->tag);
+ }
+ }
+
+ assert (*tag == '.');
+
+ /* If a relative tag extension is used,
+ * prepend the local revision number
+ */
+ if (entversion)
+ {
+ res_vers = Xasprintf ("%s%s", entversion, tag);
+ }
+ else if (enttag)
+ {
+ res_vers = Xasprintf ("%s%s", enttag, tag);
+ }
+ else if (sdtp && sdtp->tag)
+ {
+ res_vers = Xasprintf ("%s%s", sdtp->tag, tag);
+ }
+ else if (force_trunk)
+ {
+ res_vers = Xasprintf (".%s%s", TAG_TRUNK, tag);
+ }
+
+ /* If the version now starts with TAG_DOTBASE,
+ * replace TAG_DOTBASE with the local revision number
+ */
+ if (res_vers && !strncmp (res_vers+1, TAG_DOTBASE, strlen (TAG_DOTBASE)))
+ {
+ char *tmp = res_vers;
+ char *rem = strchr (res_vers+1, '.');
+ if (rem)
+ ++rem;
+ res_vers = Xasprintf ("%s.%s", entversion, rem);
+ free (tmp);
+ }
+
+ return (res_vers);
+}
+
+#ifdef SERVER_SUPPORT
+
+/* Set VERS_TS->TS_USER to time stamp for FILE. */
+
+/* Separate these out to keep the logic below clearer. */
+#define mark_lost(V) ((V)->ts_user = 0)
+#define mark_unchanged(V) ((V)->ts_user = xstrdup ((V)->ts_rcs))
+
+static void
+time_stamp_server (const char *file, Vers_TS *vers_ts, Entnode *entdata)
+{
+ struct stat sb;
+ char *cp;
+
+ TRACE (TRACE_FUNCTION, "time_stamp_server (%s, %s, %s, %s)",
+ file,
+ entdata && entdata->version ? entdata->version : "(null)",
+ entdata && entdata->timestamp ? entdata->timestamp : "(null)",
+ entdata && entdata->conflict ? entdata->conflict : "(null)");
+
+ if (lstat (file, &sb) < 0)
+ {
+ if (! existence_error (errno))
+ error (1, errno, "cannot stat temp file");
+
+ /* Missing file means lost or unmodified; check entries
+ file to see which.
+
+ XXX FIXME - If there's no entries file line, we
+ wouldn't be getting the file at all, so consider it
+ lost. I don't know that that's right, but it's not
+ clear to me that either choice is. Besides, would we
+ have an RCS string in that case anyways? */
+ if (entdata == NULL)
+ mark_lost (vers_ts);
+ else if (entdata->timestamp
+ && entdata->timestamp[0] == '='
+ && entdata->timestamp[1] == '\0')
+ mark_unchanged (vers_ts);
+ else if (entdata->conflict
+ && entdata->conflict[0] == '=')
+ {
+ /* These just need matching content. Might as well minimize it. */
+ vers_ts->ts_user = xstrdup ("");
+ vers_ts->ts_conflict = xstrdup ("");
+ }
+ else if (entdata->timestamp
+ && (entdata->timestamp[0] == 'M'
+ || entdata->timestamp[0] == 'D')
+ && entdata->timestamp[1] == '\0')
+ vers_ts->ts_user = xstrdup ("Is-modified");
+ else
+ mark_lost (vers_ts);
+ }
+ else if (sb.st_mtime == 0)
+ {
+ /* We shouldn't reach this case any more! */
+ abort ();
+ }
+ else
+ {
+ struct tm *tm_p;
+
+ vers_ts->ts_user = xmalloc (25);
+ /* We want to use the same timestamp format as is stored in the
+ st_mtime. For unix (and NT I think) this *must* be universal
+ time (UT), so that files don't appear to be modified merely
+ because the timezone has changed. For VMS, or hopefully other
+ systems where gmtime returns NULL, the modification time is
+ stored in local time, and therefore it is not possible to cause
+ st_mtime to be out of sync by changing the timezone. */
+ tm_p = gmtime (&sb.st_mtime);
+ cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime);
+ cp[24] = 0;
+ /* Fix non-standard format. */
+ if (cp[8] == '0') cp[8] = ' ';
+ (void) strcpy (vers_ts->ts_user, cp);
+ }
+}
+
+#endif /* SERVER_SUPPORT */
+
+
+
+/* Given a UNIX seconds since the epoch, return a string in the format used by
+ * the Entries file.
+ *
+ *
+ * INPUTS
+ * UNIXTIME The timestamp to be formatted.
+ *
+ * RETURNS
+ * A freshly allocated string the caller is responsible for disposing of.
+ */
+char *
+entries_time (time_t unixtime)
+{
+ struct tm *tm_p;
+ char *cp;
+
+ /* We want to use the same timestamp format as is stored in the
+ st_mtime. For unix (and NT I think) this *must* be universal
+ time (UT), so that files don't appear to be modified merely
+ because the timezone has changed. For VMS, or hopefully other
+ systems where gmtime returns NULL, the modification time is
+ stored in local time, and therefore it is not possible to cause
+ st_mtime to be out of sync by changing the timezone. */
+ tm_p = gmtime (&unixtime);
+ cp = tm_p ? asctime (tm_p) : ctime (&unixtime);
+ /* Get rid of the EOL */
+ cp[24] = '\0';
+ /* Fix non-standard format. */
+ if (cp[8] == '0') cp[8] = ' ';
+
+ return Xasprintf ("%s", cp);
+}
+
+
+
+time_t
+unix_time_stamp (const char *file)
+{
+ struct stat sb;
+ time_t mtime = 0L;
+
+ if (!lstat (file, &sb))
+ {
+ mtime = sb.st_mtime;
+ }
+
+ /* If it's a symlink, return whichever is the newest mtime of
+ the link and its target, for safety.
+ */
+ if (!stat (file, &sb))
+ {
+ if (mtime < sb.st_mtime)
+ mtime = sb.st_mtime;
+ }
+
+ return mtime;
+}
+
+
+
+/*
+ * Gets the time-stamp for the file "file" and returns it in space it
+ * allocates
+ */
+char *
+time_stamp (const char *file)
+{
+ time_t mtime = unix_time_stamp (file);
+ return mtime ? entries_time (mtime) : NULL;
+}
+
+
+
+/*
+ * free up a Vers_TS struct
+ */
+void
+freevers_ts (Vers_TS **versp)
+{
+ if ((*versp)->srcfile)
+ freercsnode (&((*versp)->srcfile));
+ if ((*versp)->vn_user)
+ free ((*versp)->vn_user);
+ if ((*versp)->vn_rcs)
+ free ((*versp)->vn_rcs);
+ if ((*versp)->vn_tag)
+ free ((*versp)->vn_tag);
+ if ((*versp)->ts_user)
+ free ((*versp)->ts_user);
+ if ((*versp)->ts_rcs)
+ free ((*versp)->ts_rcs);
+ if ((*versp)->options)
+ free ((*versp)->options);
+ if ((*versp)->tag)
+ free ((*versp)->tag);
+ if ((*versp)->date)
+ free ((*versp)->date);
+ if ((*versp)->ts_conflict)
+ free ((*versp)->ts_conflict);
+ free ((char *) *versp);
+ *versp = NULL;
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Cvs-cvs] ccvs/src ChangeLog admin.c annotate.c commit.c ... [newtags2],
Derek Robert Price <=