summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/cvs/src
diff options
context:
space:
mode:
authortholo <tholo@openbsd.org>1996-01-30 00:16:59 +0000
committertholo <tholo@openbsd.org>1996-01-30 00:16:59 +0000
commit13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f (patch)
treeaa8cdfd44dacc5312746813b3fccfb366f1ccf0f /gnu/usr.bin/cvs/src
parentAdd kernel PLL for system clock (diff)
downloadwireguard-openbsd-13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f.tar.xz
wireguard-openbsd-13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f.zip
Upgrade to 1.7.1 snapshot
Diffstat (limited to 'gnu/usr.bin/cvs/src')
-rw-r--r--gnu/usr.bin/cvs/src/.cvsignore10
-rw-r--r--gnu/usr.bin/cvs/src/ChangeLog1619
-rw-r--r--gnu/usr.bin/cvs/src/Makefile.in51
-rw-r--r--gnu/usr.bin/cvs/src/add.c23
-rw-r--r--gnu/usr.bin/cvs/src/admin.c58
-rw-r--r--gnu/usr.bin/cvs/src/checkin.c8
-rw-r--r--gnu/usr.bin/cvs/src/checkout.c36
-rw-r--r--gnu/usr.bin/cvs/src/client.c1908
-rw-r--r--gnu/usr.bin/cvs/src/client.h32
-rw-r--r--gnu/usr.bin/cvs/src/cvsbug.sh22
-rw-r--r--gnu/usr.bin/cvs/src/cvsrc.c28
-rw-r--r--gnu/usr.bin/cvs/src/diff.c54
-rw-r--r--gnu/usr.bin/cvs/src/edit.c1032
-rw-r--r--gnu/usr.bin/cvs/src/edit.h40
-rw-r--r--gnu/usr.bin/cvs/src/entries.c243
-rw-r--r--gnu/usr.bin/cvs/src/error.c188
-rw-r--r--gnu/usr.bin/cvs/src/error.h47
-rw-r--r--gnu/usr.bin/cvs/src/expand_path.c134
-rw-r--r--gnu/usr.bin/cvs/src/fileattr.c508
-rw-r--r--gnu/usr.bin/cvs/src/fileattr.h123
-rw-r--r--gnu/usr.bin/cvs/src/filesubr.c106
-rw-r--r--gnu/usr.bin/cvs/src/find_names.c3
-rw-r--r--gnu/usr.bin/cvs/src/hash.c35
-rw-r--r--gnu/usr.bin/cvs/src/hash.h4
-rw-r--r--gnu/usr.bin/cvs/src/history.c7
-rw-r--r--gnu/usr.bin/cvs/src/ignore.c107
-rw-r--r--gnu/usr.bin/cvs/src/import.c90
-rw-r--r--gnu/usr.bin/cvs/src/log.c12
-rw-r--r--gnu/usr.bin/cvs/src/login.c380
-rw-r--r--gnu/usr.bin/cvs/src/logmsg.c57
-rw-r--r--gnu/usr.bin/cvs/src/mkmodules.c5
-rw-r--r--gnu/usr.bin/cvs/src/modules.c42
-rw-r--r--gnu/usr.bin/cvs/src/myndbm.c83
-rw-r--r--gnu/usr.bin/cvs/src/myndbm.h11
-rw-r--r--gnu/usr.bin/cvs/src/options.h.in98
-rw-r--r--gnu/usr.bin/cvs/src/parseinfo.c53
-rw-r--r--gnu/usr.bin/cvs/src/patch.c48
-rw-r--r--gnu/usr.bin/cvs/src/rcs.c68
-rw-r--r--gnu/usr.bin/cvs/src/rcs.h12
-rw-r--r--gnu/usr.bin/cvs/src/rcscmds.c10
-rw-r--r--gnu/usr.bin/cvs/src/recurse.c61
-rw-r--r--gnu/usr.bin/cvs/src/release.c62
-rw-r--r--gnu/usr.bin/cvs/src/remove.c37
-rw-r--r--gnu/usr.bin/cvs/src/root.c13
-rw-r--r--gnu/usr.bin/cvs/src/rtag.c30
-rw-r--r--gnu/usr.bin/cvs/src/run.c15
-rw-r--r--gnu/usr.bin/cvs/src/sanity.sh1479
-rw-r--r--gnu/usr.bin/cvs/src/scramble.c245
-rw-r--r--gnu/usr.bin/cvs/src/server.h6
-rw-r--r--gnu/usr.bin/cvs/src/status.c8
-rw-r--r--gnu/usr.bin/cvs/src/subr.c16
-rw-r--r--gnu/usr.bin/cvs/src/tag.c249
-rw-r--r--gnu/usr.bin/cvs/src/update.c275
-rw-r--r--gnu/usr.bin/cvs/src/update.h26
-rw-r--r--gnu/usr.bin/cvs/src/vers_ts.c26
-rw-r--r--gnu/usr.bin/cvs/src/version.c2
-rw-r--r--gnu/usr.bin/cvs/src/watch.c530
-rw-r--r--gnu/usr.bin/cvs/src/watch.h56
-rw-r--r--gnu/usr.bin/cvs/src/wrapper.c15
59 files changed, 9071 insertions, 1475 deletions
diff --git a/gnu/usr.bin/cvs/src/.cvsignore b/gnu/usr.bin/cvs/src/.cvsignore
new file mode 100644
index 00000000000..be8c63aa45b
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/.cvsignore
@@ -0,0 +1,10 @@
+.pure
+Makefile
+TAGS
+check.log
+check.plog
+cvs
+cvsbug
+mkmodules
+options.h
+options.h-SAVED
diff --git a/gnu/usr.bin/cvs/src/ChangeLog b/gnu/usr.bin/cvs/src/ChangeLog
index 1937aef1d7b..5e731b5ab46 100644
--- a/gnu/usr.bin/cvs/src/ChangeLog
+++ b/gnu/usr.bin/cvs/src/ChangeLog
@@ -1,3 +1,1610 @@
+Sun Jan 28 09:45:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * edit.c, edit.h (mark_up_to_date): New function, to remove file
+ in CVS/Base.
+ * client.c (update_entries): Call it if file is up to date.
+ * checkin.c (Checkin): Call it in non-server (local) case.
+ * sanity.sh: New test 182.5, tests for above-fixed bug.
+
+Sun Jan 28 01:07:22 1996 Jim Kingdon (kingdon@beezley)
+
+ * client.c (change_mode): Separate out CHMOD_BROKEN code to parse
+ mode_string, rather than going through a mode_t. Cleaner than
+ the previous CHMOD_BROKEN code (which also had a typo of && not &).
+
+Sat Jan 27 23:29:46 1996 Jim Kingdon (kingdon@beezley)
+
+ * edit.c (edit_fileproc): Check for EACCESS as well as EEXIST.
+
+Sat Jan 27 16:26:30 1996 Karl Fogel (kfogel@floss.cyclic.com)
+
+ * client.c (notified_a_file): use rename_file() instead of
+ rename() (but temporarily set `noexec' to 0 so it runs
+ unconditionally).
+ (change_mode): deal with CHMOD_BROKEN.
+
+Fri Jan 26 00:14:00 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * server.c: renamed `dirname' to `dir_name', to avoid conflicts
+ with system headers.
+
+ * client.c: renamed `dirname' and `last_dirname' to `dir_name' and
+ last_dir_name' (see above). Not strictly necessary, but
+ consistency is nice -- as long as you do it all the time.
+
+Thu Jan 25 00:41:59 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * options.h.in (AUTH_SERVER_SUPPORT, AUTH_CLIENT_SUPPORT): change
+ comment now that no longer under construction.
+
+Wed Jan 24 15:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.7.1.
+
+ * Version 1.7.
+
+Sat Jan 20 00:05:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.87.
+
+Mon Jan 15 18:14:55 1996 Gary Oberbrunner <garyo@avs.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * tag.c (val_direntproc): New function to ignore
+ nonexistent dirs when recursing to check tag validity.
+ (tag_check_valid): Pass it to start_recursion.
+ * sanity.sh (death): New tests 65a0-65a6 cause test 74 to test for
+ above-fixed bug.
+
+Mon Jan 15 12:55:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * main.c: Revert change to run getopt_long twice. This can go in
+ after 1.7.
+
+Mon Jan 15 13:03:28 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * filesubr.c (deep_remove_dir): added test of EEXIST for nonempty
+ directory (Posix states that both ENOTEMPTY (BSD) and EEXIST
+ (SYSV) are valid)
+
+ * main.c (main): run getopt_long twice to allow command-line
+ suppression of reading the cvsrc file
+
+Fri Jan 12 10:02:43 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.86.
+
+Thu Jan 11 23:28:05 1996 J.T. Conklin <jtc@rtl.cygnus.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * fileattr.h (fileattr_startdir): Add comment about REPOS == NULL.
+ * fileattr.c (fileattr_read, fileattr_write): Assert that
+ fileattr_stored_repos != NULL.
+ (fileattr_free): If fileattr_stored_repos is NULL, don't free it.
+
+Thu Jan 11 18:03:21 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * scramble.c (descramble): deal with DIAGNOSTIC better.
+
+Thu Jan 11 12:04:42 1996 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * main.c: remove CVS_NOADMIN.
+
+ * options.h.in: remove CVS_NOADMIN
+
+Thu Jan 11 10:28:44 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * scramble.c (descramble): make sure the string returned is safe
+ to free().
+
+Wed Jan 10 01:11:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c (serve_notify): Cast return value from malloc.
+
+ * edit.c (notify_do): Use struct assignment, not struct
+ initialization (which SunOS4 /bin/cc doesn't have).
+
+Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.85.
+
+ We use version numbers instead of patchlevels. But there was some
+ confusing patchlevel stuff lying around. Nuke it:
+ * Makefile.in (HEADERS): Remove patchlevel.h
+ * patchlevel.h: Removed.
+ * main.c: Don't include patchlevel.h.
+ (main): Don't print patch level.
+
+ * server.c (check_repository_password): Check for errors from
+ system calls; reindent function.
+
+Tue Jan 9 23:15:30 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * expand_path.c: fix comments (explain expand_path()'s behavior
+ correctly).
+
+Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * edit.c (notify_proc): After copying in string following %s,
+ don't clobber it. Instead set up q to end of string.
+
+ * watch.c (watch_modify_watchers), edit.c (editor_set): Fix sense
+ of test in trying to decide whether attributes are changed.
+
+ * cvs.h (CVSROOTADM_USERS): New macro.
+ * edit.c (notify_do): Look up notifyee in CVSROOTADM_USERS if it
+ exists.
+
+Tue Jan 9 21:39:45 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * expand_path.c: don't redundantly #include things that cvs.h
+ already #includes (i.e., stdio.h, ctype.h, string[s].h).
+
+Tue Jan 9 09:41:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * ignore.c (ign_default): Add *.obj.
+
+ * server.c: Put /* */ around #endif comment.
+
+Mon Jan 8 20:37:17 1996 Karl Fogel <kfogel@floss.red-bean.com>
+
+ * client.c (connect_to_pserver): check return value of recv().
+
+Mon Jan 8 11:37:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (connect_to_pserver): Check for error from connect;
+ reindent function.
+
+ * sanity.sh (4.75): Use dotest, so we get a PASS if test passes.
+
+ * sanity.sh (dotest): New argument OUTPUT2.
+ (188a): Use it instead of \|.
+
+ * sanity.sh (import): Avoid using string $ followed by Id followed
+ by $ in sanity.sh source, in case sanity.sh itself is under CVS.
+ I hate keyword expansion.
+
+ * sanity.sh: If expr cannot handle multiline expressions, fail and
+ tell the user to get one which can.
+
+ * release.c (release_delete): Remove unused variable retcode.
+
+Fri Jan 5 13:30:00 1996 Jim Kingdon <kingdon@peary.cyclic.com>
+
+ * release.c (release_delete): Call unlink_file_dir rather
+ than "rm -rf".
+
+Thu Jan 4 09:58:30 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * commit.c (find_fileproc): Print "nothing known about foo" and
+ return 1 if the file doesn't exist and isn't in CVS/Entries.
+ (commit): If the recursion over find_fileproc returns an error,
+ print "correct above errors first!" just like local CVS.
+ * sanity.sh (basica): Test for above-fixed bug.
+
+ * release.c (release): If we are the client, only unedit if the
+ server supports it.
+
+ * sanity.sh: Remove STARTANCHOR stuff; expr patterns are
+ automatically anchored to the start. ENDANCHOR remains.
+
+ * commit.c (commit): Don't start the server until we have
+ determined that there is something to commit.
+
+Thu Jan 4 09:48:33 1996 Ben Laurie <ben@gonzo.ben.algroup.co.uk>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (start_server): dup the file descriptor before
+ fdopening it.
+
+Wed Jan 3 18:25:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Remove tests 5, 5.5, and 5.75. All that stuff is
+ tested elsewhere.
+
+ * ignore.c (ign_default): Change CVS* to CVS CVS.adm. CVS* is too
+ broad, especially in a case-insensitive filesystem.
+
+ * Makefile.in (cvsbug): version.c is in srcdir.
+
+Wed Jan 3 17:30:45 1996 Phi-Long Tran <ptran@autodesk.com>
+
+ * modules.c (do_module): Honor error_use_protocol in printing trace.
+ * server.c (server_register): Move check for options NULL to above
+ printing of the trace.
+
+Wed Jan 3 01:19:53 1996 Mark Immel <immel@centerline.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * update.c (checkout_file): Do not resurrect file on join if it
+ doesn't contain the revisions we are joining. Probably not a
+ perfect test, but should be an improvement.
+ * sanity.sh (death): New death-file4-* tests, for bug fixed above.
+
+Wed Jan 3 01:19:53 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * add.c, admin.c, checkout.c, client.c, commit.c, diff.c, edit.c,
+ history.c, import.c, log.c, patch.c, release.c, remove.c, rtag.c,
+ status.c, tag.c, update.c, watch.c: In calling send_to_server,
+ pass \012 not \n. On the Mac \n is CR, not LF, and we want to
+ send LF. I didn't try to deal with whether files in CVSADM should
+ contain CR or LF--in fact there is some code in client.c which
+ reads \n from CVSADM files and passes it to send_to_server; it
+ needs to be cleaned up one way or the other.
+
+ * entries.c (Entries_Open): Don't try to close fpin twice.
+
+ * client.c (update_entries): Fix typo ("strlen (filename + 10)"
+ -> "strlen (filename) + 10").
+
+ * commit.c (checkaddfile): Remove arbitrary limit.
+
+Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * commit.c (commit): Only pass files which were modified, added,
+ or removed to send_file_names. This has as a side effect a
+ semantic change--the up-to-date check is now skipped for other
+ files--but probably a good one, or at least not a bad one.
+ * sanity.sh (basica): New test; tests for bug fixed above.
+ * sanity.sh (187a3): Adjust for new 'cvs commit' output. Set up
+ DOTSTAR to match arbitrary text (another GNU expr bug/misfeature,
+ sigh).
+
+ * sanity.sh: Test that the commit in test 43 actually worked.
+ Merge tests basic2 and basic3 and make them independent of basic1.
+ (pass,fail): Don't insert spurious space.
+ (45.5): Fix typo in directory name.
+
+Tue Jan 2 13:00:00 1996 Jim Kingdon <kingdon@peary.cyclic.com>
+
+ Visual C++ lint:
+ * myndbm.c: Prototype write_item.
+
+Tue Jan 2 11:25:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ gcc -Wall lint:
+ * client.c (client_expand_modules): Pass error message not "" to error.
+ * client.c (supported_request), server.c (supported_response):
+ Return a value (gcc -Wall can't know that error doesn't return).
+ * commit.c (copy_ulist): Return a value.
+ * history.c (fill_hrec): Don't make assumptions about whether
+ time_t is "int" or "long" or what.
+ * cvs.h: Declare link_file.
+ * server.c: Include fileattr.h.
+ * server.c (server_notify): Remove unused variable val.
+ * tag.c (val_fileproc): Remove unused variable foundtag.
+
+Mon Jan 1 09:49:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.5.
+
+ * Version 1.6.4.
+
+ * filesubr.c (link_file): Add comment about link vs. copy semantics.
+
+ * cvs.h (struct vers_ts): Fix comments.
+ * commit.c (commit): Before we ask for a log message, figure out
+ what is modified and what is not and pass the information to
+ do_editor.
+ (copy_ulist,find_fileproc): New helper functions for above code.
+
+ * client.c (read_line): When writing to from_server_logfile, write
+ the \n too.
+
+ * client.c (send_files): No longer call send_file_names.
+ * client.h: Update comment.
+ * add.c, admin.c, commit.c, diff.c, edit.c, log.c, remove.c,
+ status.c, tag.c, update.c, watch.c: Call send_file_names before
+ send_files.
+ * client.c: New variables module_argc, module_argv.
+ (client_expand_modules): Set them, to arguments.
+ (client_send_expansions): Use them instead of modules_vector to
+ send arguments.
+ * sanity.sh (modules): Add test of modules -d flag.
+
+Sun Dec 31 17:33:47 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * import.c (add_rev): Revert portion of 31 Aug 95 change which
+ passes -u to ci instead of using a hard link.
+ * sanity.sh (import): Add test for above-fixed bug.
+
+Sun Dec 31 16:40:41 1995 Peter Chubb <peterc@bookworm.sw.oz.au>
+ and Jim Kingdon <kingdon@cyclic.com>
+
+ * admin.c (admin_fileproc): Call freevers_ts before returning.
+
+Mon Dec 25 12:20:06 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * logmsg.c (rcsinfo_proc): initialise line and
+ line_chars_allocated so they dont cause malloc problems within
+ getline(). This was causing rcsinfo templates to not work.
+
+Sun Dec 24 01:38:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): clarify protocol.
+
+ * login.c (login): deprolixify the password prompt.
+
+Sat Dec 23 10:46:41 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * myndbm.h, myndbm.c (dbm_store): New function.
+ * myndbm.h (DBM): Add modified and filename fields.
+ * myndbm.c (dbm_open, dbm_close): Manipulate new fields. dbm_open
+ no longer fails if the file doesn't exist and O_CREAT is set.
+ * cvs.h (CVSROOTADM_VALTAGS): Added.
+ * tag.c, cvs.h (tag_check_valid): New function.
+ * update.c (update), checkout.c (checkout_proc), commit.c (commit),
+ diff.c (diff), patch.c (patch_proc), rtag.c (rtag_proc), tag.c (tag):
+ Call it.
+ * sanity.sh: Test for rejection of invalid tagname.
+
+Fri Dec 22 18:21:39 1995 Karl Fogel <kfogel@csxt.cs.oberlin.edu>
+
+ * client.c (start_server): don't use kerberos if authenticating
+ server was specified.
+
+Fri Dec 22 16:35:57 1995 Karl Fogel <kfogel@csxt.cs.oberlin.edu>
+
+ * login.c (login): deal with new scramble methods.
+ (get_cvs_password): same.
+
+ * server.c (check_repository_password): remove arbitrary limit on
+ line length.
+ (authenticate_connection): use a separate variable for the
+ descrambled password, now that we no longer scramble in place.
+ Set `error_use_protocol' to 1 and just use error() where used to
+ do its job inline.
+
+ * cvs.h (scramble, descramble): adjust prototype.
+
+ * scramble.c (scramble, descramble): return char *.
+
+Fri Dec 22 13:00:00 1995 Jim Kingdon <kingdon@peary.cyclic.com>
+
+ * release.c (release): If SERVER_SUPPORT is not defined, still
+ set up arg_start_idx.
+
+ * release.c (release): When calling unedit, set argv[1] to
+ NULL (since argc is only 1).
+
+ * edit.c: Pass dosrcs 0 to all calls to start_recursion.
+ None of the fileprocs were using it, so it just slowed things
+ down and caused potentially harmful checks for rcs files.
+
+ * edit.c (send_notifications): In client case, do not readlock.
+
+Thu Dec 21 16:00:00 1995 Jim Kingdon <kingdon@peary.cyclic.com>
+
+ Clean up Visual C++ lint:
+ * client.c (read_line): Change input_index and result_size to size_t.
+ (update_entries): Remove unused variables buf2, size_left, size_read.
+ (handle_mode): Prototype.
+ * client.c, client.h (send_to_server, read_from_server): Change
+ len to size_t.
+ * client.c (send_to_server): Change wrtn to size_t.
+ (read_from_server): Change red to size_t.
+ * client.c, myndbm.c, edit.c, fileattr.c: Include getline.h.
+ * checkin.c, commit.c, update.c: Include fileattr.h.
+ * commit.c, update.c: Include edit.h.
+ * edit.c (onoff_filesdoneproc): Prototype.
+ (ncheck_fileproc,edit_fileproc): Change "return" to "return 0".
+ (notify_do): Cast a signed value to unsigned before comparing
+ with unsigned value.
+
+Thu Dec 21 15:24:37 1995 Karl Fogel <kfogel@occs.cs.oberlin.edu>
+
+ * client.c: don't include socket headers twice just because
+ both HAVE_KERBEROS and AUTH_CLIENT_SUPPORT are set.
+ (start_kerberos_server): if fail to connect to kerberos, print out
+ a more specific error message, mainly so pcl-cvs can know what
+ happened and not panic.
+ (start_server): don't assume sprintf() returns len
+ written (only some systems provide this); instead, have
+ send_to_server() calculate the length itself.
+ (send_modified): same.
+ (send_fileproc): same.
+ (send_file_names): same.
+
+Wed Dec 20 14:00:28 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * update.c (ignore_files): Move from here...
+ * ignore.c (ignore_files): ...to here. No longer static. Take
+ new argument PROC.
+ * cvs.h (ignore_files): Declare.
+ * client.c (send_filesdoneproc): Split off from
+ update_filesdone_proc. Pass new function send_ignproc to
+ ignore_files (to ask server about ignored file before printing
+ "?").
+ * server.c: Rename outbuf from but_to_net and take it from
+ do_cvs_command to a global. Move initialization accordingly.
+ (serve_questionable): New function.
+ (requests): Add it.
+ * update.c (update_filesdone_proc): Remove client stuff. Pass new
+ function update_ignproc to ignore_files.
+ * cvs.h (joining, do_update): Move declarations from here...
+ * update.h: ...to here.
+ * cvs.h: Include update.h.
+ * update.c, client.c: Don't include update.h
+ * ignore.c, cvs.h: New variable ign_inhibit_server, set on -I !.
+ * import.c (import): Pass -I ! to server if specified.
+ (import_descend): If server, ignore CVS directories even if -I !.
+ * update.c (update), import.c (import): Only call ign_setup before
+ argument processing; don't call it again afterwards in client case.
+ * sanity.sh (ignore): Test above-fixed bugs and other ignore behaviors.
+ (dotest): New function.
+ Move modules checkin from modules test to start, so that other
+ tests can use mkmodules without a warning message.
+
+Wed Dec 20 13:06:17 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (send_to_server): don't check string's length twice.
+
+Wed Dec 20 02:05:19 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): took out debugging printf's.
+ (login): Removed unused variable `p'.
+
+Wed Dec 20 00:27:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): prefix scrambled password with 'A', so we know
+ which version of scrambling was used. This may be useful in the
+ future.
+ (get_cvs_password): skip past the leading 'A'.
+ Scramble $CVS_PASSWORD before returning it.
+
+ * scramble.c: made this work.
+
+Tue Dec 19 17:45:11 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (cvs_password): new static var, init to NULL.
+ (login): scramble() the password before using it.
+ Verify the password with the server.
+ Check CVSroot more carefully to insure that it is
+ "fully-qualified".
+ (get_cvs_password): if cvs_password is not NULL, just return it.
+ Never prompt -- just tell user why failed, then exit.
+ Try CVS_PASSWORD environment variable first.
+ (construct_cvspass_filename): try CVS_PASSFILE environment
+ variable first.
+
+ * client.h (connect_to_pserver): update prototype.
+
+ * client.c (cvsroot_parsed): new static var.
+ (parse_cvsroot): set `cvsroot_parsed' to 1 when done.
+ (connect_to_pserver): return int.
+ Take `verify_only' arg. If it is non-zero, perform password
+ verification with the server and then shut down the connection and
+ return.
+ Call parse_cvsroot() before doing anything.
+
+ * server.c (authenticate_connection): deal with verification
+ requests as well as authorization requests.
+ descramble() the password before hashing it.
+
+ * cvs.h: prototype scramble() and descramble().
+
+ * Makefile.in: build scramble.o.
+
+ * scramble.c: new file, provides trivial encoding but NOT real
+ encryption.
+
+Mon Dec 18 20:57:58 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): don't insert extra newlines. They were
+ harmless, but confusing.
+
+Mon Dec 18 15:32:32 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * hash.c, hash.h (findnode_fn): New function.
+ * hash.c (hashp): Tweak hash function so that findnode_fn works.
+ * update.c (ignore_files): Call findnode_fn, not findnode.
+
+Mon Dec 18 09:34:56 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * myndbm.c: Remove arbitrary limit.
+
+ * client.c: Fix comment--Windows 95 requires NO_SOCKET_TO_FD, not
+ Windows NT.
+
+Mon Dec 18 01:06:20 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (server_sock): replaces `server_socket'.
+ (start_kerberos_server): added FIXME comment about how
+ NO_SOCKET_TO_FD is not dealt with in the kerberos case.
+ (connect_to_pserver): deal with NO_SOCKET_TO_FD case.
+ (read_line): deal with NO_SOCKET_TO_FD case.
+ (read_from_server): deal with NO_SOCKET_TO_FD case.
+ (send_to_server): deal with NO_SOCKET_TO_FD case.
+ (get_responses_and_close): deal with NO_SOCKET_TO_FD case.
+
+ * client.c (send_to_server): error check logging.
+ (start_server): error check opening of logfiles.
+ (read_from_server): error check logging.
+ (read_line): use fwrite() to log, & error_check it.
+ Don't log if using socket style, because read_from_server()
+ already logged for us.
+
+Mon Dec 18 00:52:26 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (use_socket_style): new static var, init to 0.
+ (server_socket): new static var.
+ (connect_to_pserver): don't deal with logging here.
+ Caller changed.
+ (start_kerberos_server): don't deal with logging here either.
+ Caller changed.
+
+Mon Dec 18 00:40:46 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (send_modified): don't error-check `to_server';
+ send_to_server() does that now.
+
+Mon Dec 18 00:19:16 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (get_cvs_password): Init `linebuf' to NULL.
+ free() `linebuf' and reset it for each new line.
+ (login): same as above.
+
+ * client.c: Removed all the varargs prototyping gunk.
+ (to_server, from_server): make these static.
+ (from_server_logfile, to_server_logfile): new vars.
+ (start_server): init above two new vars to NULL.
+ (send_to_server): return void.
+ Correct bug in which amount to be written would be too high if the
+ loop ever ran more than once.
+ Log to `to_server_logfile' if it's non-NULL.
+ (read_from_server): new func, does raw reading from server.
+ Logs to `from_server_logfile' if it's non-NULL.
+ (update_entries): just use read_from_server() instead of looping
+ to fread() directly from `from_server'.
+ (read_line): Log to `from_server_logfile' if it's non-NULL.
+
+ * client.h: send_to_server() returns void now.
+ (read_from_server): prototype.
+
+Sun Dec 17 19:38:03 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * checkout.c (checkout_proc), client.c, lock.c (readers_exist),
+ login.c, modules.c (cat_module, do_module): Remove arbitrary limits.
+
+ * client.c (send_to_server): Fix typo (NULL -> '\0').
+ (get_responses_and_close): Set server_started to 0 instead of
+ setting to_server and from_server to NULL.
+ * client.c: Make to_server and from_server static.
+
+Sun Dec 17 17:59:04 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.h (to_server, from_server): don't declare these anymore.
+ They are now entirely private to client.c (and in fact will go
+ away soon there too).
+
+Sun Dec 17 15:40:58 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.h: update prototype of send_to_server().
+
+ * client.c, watch.c, update.c, tag.c, status.c, rtag.c, remove.c,
+ release.c, patch.c, log.c, import.c, history.c, edit.c, diff.c,
+ commit.c, client.c, checkout.c, admin.c, add.c:
+ Convert all send_to_server() calls that used formatting to send
+ pre-formatted strings instead. And don't error check
+ send_to_server(), because it does its own error checking now.
+
+ * client.c (send_to_server): don't use vasprintf(), just fwrite a
+ certain number of bytes to the server. And do error checking
+ here, so our callers don't have to.
+ (send_arg): use send_to_server() instead of putc()'ing
+ directly to `to_server'.
+
+Sun Dec 17 14:37:52 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * options.h.in (AUTH_CLIENT_SUPPORT, AUTH_SERVER_SUPPORT):
+ Define to 1 but leave commented out, instead of #undef'ing them.
+ This treats them like everything else in this file.
+
+ * client.c: define server_started, init to 0.
+ (start_server): set server_started to 1.
+
+ * client.h: declare `server_started', extern.
+ AUTH_CLIENT_SUPPORT moved here from cvs.h.
+
+ * cvs.h: moved AUTH_CLIENT_SUPPORT stuff to client.h.
+
+ * edit.c (notify_check): use new var server_started.
+
+Sun Dec 17 00:44:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (get_responses_and_close): Really stop ignoring ECHILD
+ errors. The Nov 30 1995 change claimed to do this, but the code
+ was not actually changed.
+
+ * update.c (ignore_files): Revert H.J. Lu change; it was wrong for
+ directories and sometimes looked at sb.st_mode when it wasn't set.
+ * import.c (import_descend): Revert H.J. Lu change; it was wrong
+ for directories and the extra lstat call was an unnecessary
+ performance hit.
+ * sanity.sh (import): Add test for the second of these two bugs.
+
+Sat Dec 16 17:26:08 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * client.c (send_to_server): Remove arbitrary limit. Also remove
+ !HAVE_VPRINTF code; all relevant systems have vprintf these days.
+
+Sat Dec 16 21:35:31 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * checkout.c (checkout): use send_to_server() now.
+
+Sat Dec 16 21:18:16 1995 H.J. Lu (hjl@gnu.ai.mit.edu)
+ (applied by kfogel@cyclic.com)
+
+ * import.c (import_descend): We ignore an entry if it is
+ 1. not a file, nor a link, nor a directory, or
+ 2. a file and on the ignore list.
+
+ * update.c (ignore_files): We ignore any thing which is
+ 1. not a file, or
+ 2. it is a file on the ignore list.
+
+Sat Dec 16 00:14:19 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (send_to_server): corrected comment.
+
+ * client.h: prototype new func send_to_server().
+
+ * add.c, admin.c, client.c, commit.c, diff.c, edit.c, history.c,
+ import.c, log.c, patch.c, release.c, remove.c, rtag.c, status.c,
+ tag.c, update.c, watch.c:
+ Use send_to_server() instead of writing directly to to_server.
+
+ * client.c: conditionally include the right stuff for variable arg
+ lists.
+ (send_to_server): new func.
+
+Fri Dec 15 23:10:22 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * error.c: expanded comments.
+
+ * client.c (connect_to_pserver): verbosify errors.
+ (connect_to_pserver): use send() and recv(), not write() and
+ read(). Sockets are not file descriptors on all systems.
+
+Fri Dec 15 22:36:05 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (connect_to_pserver): oops, removed old debugging
+ printf.
+
+Fri Dec 15 18:21:16 1995 Karl Fogel (kfogel@floss.cyclic.com)
+
+ * client.c (auth_server_port_number): don't call htons();
+ init_sockaddr() does that for us.
+ (init_sockaddr): zero the sockadder_in struct before doing
+ anything with it. IBM TCP/IP docs recommend this, and it can't
+ hurt.
+
+Fri Dec 15 15:21:53 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (connect_to_pserver): new var `port_number', initialize
+ with new func auth_server_port_number() and pass to
+ init_sockaddr().
+ (auth_server_port_number): new func. Right now it just returns
+ `htons (CVS_AUTH_PORT)'. We'll probably add the ability to
+ specify the port at run time soon, anyway, so having this function
+ will make that easier.
+
+Wed Dec 6 18:08:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h: Add CVSREP.
+ * find_names.c (find_dirs): Skip CVSREP too.
+ * fileattr.h, fileattr.c: New files, to manipulate file attributes.
+ * hash.c (nodetypestring), hash.h (enum ntype): Add FILEATTR.
+ * hash.c, hash.h (list_isempty): New function.
+ * recurse.c (do_recursion): Call fileattr_startdir before
+ processing files in a directory and fileattr_write and
+ fileattr_free (after files, before recursing).
+ * watch.c, watch.h: New files, to handle notification features.
+ * edit.c, edit.h: New file, to handle new read-only checkout features.
+ * client.c, server.c: Add "Mode" request, to change the mode of a file
+ when it is checked in.
+ * main.c (cmds): Add "watch", "edit", "unedit", "watchers", "editors".
+ * main.c: Split command help from usg into new variable cmd_usage,
+ which.
+ (main): Add --help-commands option to print out cmd_usage.
+ * cvs.h: Declare watch, edit, unedit, watchers, editors.
+ * client.c, client.h: Add client_watch, client_edit, client_unedit,
+ client_watchers, client_editors.
+ * client.c, server.c: Add notification stuff.
+ * update.c (checkout_file, patch_file), checkin.c (Checkin): Check
+ _watched attribute when deciding read-only or read-write.
+ * commit.c (checkaddfile): Call fileattr_newfile to set attributes
+ on newly created files.
+ * release.c (release):
+ * cvs.h: Add CVSADM_NOTIFY and CVSADM_NOTIFYBAK.
+ * recurse.c (do_recursion): Call notify_check.
+ * commit.c (commit_fileproc): Call notify_do after committing file.
+ * client.c (get_responses_and_close): Set to_server and from_server
+ to NULL so that it is possible to tell whether we are speaking to
+ the server.
+ * cvs.h: Add CVSROOTADM_NOTIFY.
+ * mkmodules.c (main): Add CVSROOTADM_NOTIFY to filelist.
+ * Makefile.in (SOURCES,OBJECTS,HEADERS): Add new files mentioned above.
+ * lock.c, cvs.h (lock_tree_for_write, lock_tree_cleanup): New
+ functions, taken from old commit.c writelock code. As part of
+ this, fsortcmp and lock_filesdoneproc go from commit.c to lock.c.
+ So does locklist but it gets renamed to lock_tree_list.
+ * commit.c: Use lock_tree_*.
+
+Fri Dec 15 10:37:00 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * tag.c (tag_usage): Added -r and -D flags to usage string.
+ (tag): Detect when user specifies both -r and -D arguments.
+ Pass -r and -D arguments to server.
+
+Thu Dec 14 11:56:13 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (start_rsh_server): use RSH_NEEDS_BINARY_FLAG to
+ conditionalize "-b" option to "rsh".
+
+ * run.c (filter_stream_through_program): document return value and
+ error behavior.
+
+ * client.c (filter_through_gunzip): pass the supposedly
+ superfluous "-d" option to gunzip, to avoid stimulating what seems
+ to be an argument-passing bug in spawn() under OS/2 with IBM
+ C/C++. Yucko.
+
+Wed Dec 13 20:08:37 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * options.h.in (RCSBIN_DFLT): Recommend specifying -b in
+ inetd.conf for pserver. That is a pretty good solution.
+
+Wed Dec 13 18:29:59 1995 Preston L. Bannister <pbannister@ca.mdis.com>
+ and Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (send_modified): make sure that vers and vers->options
+ are non-NULL before strcmp()'ing them with "-kb".
+ Initialize `bin' near where it is used, not at beginning of
+ function.
+ (update_entries): make sure `options' is non-NULL before
+ strcmp()'ing with "-kb".
+ Initialize `bin' near where it is used, not at beginning of
+ function.
+
+Tue Dec 12 18:56:38 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * options.h.in (RCSBIN_DFLT): document the probable need for this
+ to be set in the authenticating server.
+
+Tue Dec 12 11:56:43 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c (expand_proc): If mfile is non-NULL, return it too as
+ part of the expansion.
+ * sanity.sh (modules): Add tests for above-fixed bug.
+
+Mon Dec 11 21:39:07 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * dog.c (flea_bath): Take `suds' arg.
+ All collars changed.
+
+Mon Dec 11 15:58:47 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): if client password file doesn't exist, create
+ it, duh.
+
+ * main.c (main): die if CVSroot has access-method but no
+ username.
+
+ * root.c: added some comments.
+
+ * main.c: removed all code pertaining to the "-a" option. We
+ specify access-method in CVSroot now.
+
+ * client.c (parse_cvsroot): new var, `access_method'. If CVSroot
+ is prepended with an access method (i.e.,
+ ":pserver:user@host:/path"), then handle it.
+
+ * login.c (login): use || when checking if CVSroot is "fully
+ qualified".
+ Prepend ":pserver:" before writing to ~/.cvspass.
+ (get_cvs_password): Take no parameters; we'll just use CVSroot to
+ get the password.
+
+Mon Dec 11 12:43:35 1995 adamg <adamg@microsoft.com>
+
+ * error.c, client.c, remove.c, main.c: Add explicit casts for some
+ function pointers to remove warnings under MS VC.
+ * main.c (main): remove use of NEED_CALL_SOCKINIT in favor of the
+ more generic INITIALIZE_SOCKET_SUBSYSTEM. Note that the code assumes
+ that if INITIALIZE_SOCKET_SUBSYSTEM() returns, socket subsystem
+ initialization has been successful.
+
+Sat Dec 9 22:01:41 1995 Dan O'Connor <doconnor@tii.com>
+
+ * commit.c (check_fileproc): pass RUN_REALLY flag to run_exec,
+ because it's okay to examine the file with noexec set.
+
+Sat Dec 9 20:28:01 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (update_entries): new var, `bin, init to 0.
+ Use it in determining whether to convert the file.
+ (send_modified): same as above.
+
+Fri Dec 8 17:47:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (downcase_string): removed.
+ (check_repository_password): don't deal with case-insensitivity
+ anymore.
+
+ * options.h.in (CVS_PASSWORDS_CASE_SENSITIVE): deleted this. No
+ need for it anymore.
+
+Thu Dec 7 21:08:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_repository_password): when checking for false
+ prefix-matches, look for ':', not '@'. Duh.
+
+Thu Dec 7 18:44:51 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * options.h.in (CVS_PASSWORDS_CASE_SENSITIVE): replaces
+ CVS_PASSWORDS_CASE_INSENSITIVE; passwords are now insensitive by
+ default. Expanded explanatory comment.
+
+ * login.c (get_cvs_password): Use memset(), not bzero(). I
+ botched this change earlier.
+
+ * server.c (check_repository_password): no need to check
+ xmalloc()'s return value.
+ (check_repository_password): check for false prefix-matches (for
+ example, username is "theo" and linebuf contains user
+ "theocracy").
+
+Thu Dec 7 14:49:16 1995 Jim Meyering (meyering@comco.com)
+
+ * filesubr.c (isaccessible): Rename from isaccessable.
+ Update callers.
+ * cvs.h: Update prototype.
+ * main.c (main): Update callers.
+ * server.c (main): Update callers.
+
+Thu Dec 7 12:50:20 1995 Adam Glass <glass@NetBSD.ORG>
+
+ * cvs.h: "isaccessible" is the correct spelling.
+ Also add "const" to second arg to make prototype match
+ declaration.
+
+Thu Dec 7 11:06:51 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c, login.c: memset() instead of bzero().
+
+Thu Dec 7 00:08:53 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): document server's side of
+ the Authentication Protocol too.
+
+ * client.c (connect_to_pserver): when printing out "unrecognized
+ response", also print out the offending response.
+
+ * server.c (check_password): take `repository' arg too now.
+ Call check_repository_password() before checking /etc/passwd.
+ (check_repository_password): new func.
+
+ * options.h.in (CVS_PASSWORDS_CASE_INSENSITIVE): new define, unset
+ by default.
+
+Wed Dec 6 18:51:16 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_password): If user has a null password, then
+ return 1 if arg is also null.
+ Reverse sense of return value. Caller changed.
+
+Wed Dec 6 14:42:57 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (check_password): new func.
+ (authenticate_connection): call above new func.
+
+ * login.c (login): use construct_cvspass_filename().
+ If CVSroot is not "fully-qualified", then insist the user qualify
+ it before going on.
+ (get_cvs_password): fleshed out. Now reads from ~/.cvspass, or
+ prompts if no appropriate password found.
+ (construct_cvspass_filename): new func.
+
+ * server.c (authenticate_connection): send ACK or NACK to client.
+
+ * client.c (connect_to_pserver): check for ACK vs NACK response
+ from server after sending authorization request.
+
+ * login.c (get_cvs_password): new func.
+
+ * client.c (connect_to_pserver): use new func get_cvs_password().
+ Prototype it at top of file. Hmmm.
+
+Wed Dec 6 13:29:22 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c: same as below (AUTH_SERVER_SUPPORT).
+
+ * main.c: same as below (AUTH_SERVER_SUPPORT where appropriate).
+
+ * login.c: same same as below.
+
+ * cvs.h: same as below.
+
+ * client.c: use AUTH_CLIENT_SUPPORT, not CVS_LOGIN.
+
+ * options.h.in (AUTH_CLIENT_SUPPORT, AUTH_SERVER_SUPPORT): these
+ replace CVS_LOGIN.
+
+Wed Dec 6 00:04:58 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): expanded comment.
+
+Tue Dec 5 23:37:39 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (connect_to_pserver): read password from prompt for
+ now.
+
+ * server.c (authenticate_connection): if the password passes
+ muster, then don't abort.
+
+Tue Dec 5 22:46:37 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * subr.c (strip_trailing_newlines): new func.
+
+ * client.c (connect_to_pserver): took out print statements.
+
+ * server.c (authenticate_connection): removed print statments.
+ Use new func strip_trailing_newlines() to purify `repository',
+ `username', and `password'.
+ Run a primitive password check, just for testing.
+
+ * client.c (connect_to_pserver): use CVS_AUTH_PORT.
+ Take tofdp, fromfdp, and log args. Caller changed.
+ (get_responses_and_close): either kerberos and CVS_LOGIN might
+ have one fd for both directions, so adjust #ifdef accordingly.
+
+ * cvs.h (CVS_AUTH_PORT): new define, default to 2401.
+ Prototype strip_trailing_newlines().
+
+Tue Dec 5 16:53:35 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * server.c (authenticate_connection): new func.
+
+ * client.c (init_sockaddr): func moved here from login.c.
+ (connect_to_pserver): same as above. Take no args, now.
+ Include <sys/socket.h>, <netinet/in.h>, <netdb.h>, if CVS_LOGIN.
+
+ * cvs.h: Declare use_authenticating_server, as extern int.
+ Declare connect_to_pserver().
+
+ * main.c (main): call authenticate_connection(). Removed testing
+ code.
+ Add 'a' to the short-option string in the getopt() call.
+
+ * login.c (connect_to_pserver): moved to client.c.
+
+Tue Dec 5 16:01:42 1995 Peter Chubb <peterc@bookworm.sw.oz.au>
+ (patch applied by Karl Fogel <kfogel@cyclic.com>)
+
+ * update.c (join_file): if vers->vn_user is "0", file has been
+ removed on the current branch, so print an error and return.
+
+Mon Dec 4 14:27:42 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.3.
+
+Mon Dec 4 16:28:25 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * release.c (release): add return (0) as last line
+
+ * cvs.h: declare program_path
+
+ * main.c define program_path
+ (main): set program_path
+
+ * release.c (release): use program_path for update_cmd
+
+Mon Dec 4 11:22:42 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Version 1.6.2.
+
+Sun Dec 3 20:02:29 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * rcs.h (struct rcsnode), rcs.c (freercsnode): Add expand field.
+ * rcs.h (RCSEXPAND): New #define.
+ * rcs.c (RCS_reparsercsfile): Record keyword expansion in expand
+ field of struct rcsnode.
+ * update.c (checkout_file): Set keyword expansion in Entries file
+ from rcs file if there is nowhere else to set it from.
+ * client.c (send_modified, update_entries) [LINES_CRLF_TERMINATED]:
+ If -kb is in effect, don't convert.
+
+ * update.c (update_file_proc), commit.c (check_fileproc),
+ rcscmds.c (RCS_merge): Direct stdout to DEVNULL rather than
+ passing -s option to grep. This avoids trouble with respect to
+ finding a grep which support -s and whether we should use the (GNU
+ grep) -q option if it exists.
+ * options.h.in: Change "@ggrep_path@" to "grep".
+
+Fri Dec 1 11:53:19 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * rcs.c (RCS_gettag): new parameter return_both force return both
+ tags: the symbolic and the numeric one.
+ (RCS_getversion): new parameter return_both is forwarded to
+ RCS_gettag.
+
+ * rtag.c, tag.c, commit.c, patch.c, update.c: pass 0 as additional
+ last parameter to RCS_getversion and RCS_gettag
+
+ * rcs.h (RCS_gettag): new parameter return_both.
+ (RCS_getversion): new parameter return_both.
+
+ * cvs.h (struct vers_ts): add vn_tag slot for symbolic tag name
+
+ * vers_ts.c (Version_TS): call RCS_getversion with 1 for
+ return_both and split output into vn_rcs and vn_tag
+ (freevers_ts): free vn_tag
+
+ * update.c (checkout_file): use vn_tag instead of vn_rcs when
+ calling 'rcs co' to allow rcs expansion of :$Name :
+
+Thu Nov 30 20:44:30 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (get_responses_and_close): undo previous change
+ regarding waitpid(). The problem has been solved by modifying
+ os2/waitpid.c instead of its callers.
+
+Thu Nov 30 16:37:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c: All these changes are for OS/2, which will no longer have
+ a separate client.c:
+ (start_kerberos_server): new func, contains code that
+ used to be in start_server().
+ (start_server): moved kerberos code to above function, reorganized
+ the rest. Added authentication clause.
+ (call_in_directory): test errno against EACCESS, if EACCESS is
+ defined (this is for OS/2's oddball mkdir).
+ (change_mode): don't set execute permission on anything if
+ EXECUTE_PERMISSION_LOSES is defined.
+ (get_responses_and_close): if START_RSH_WITH_POPEN_RW, then use
+ pclose() instead of fclose().
+ If waitpid errors with ECHILD, don't die. This is okay.
+ (start_rsh_server): alternate definition if
+ START_RSH_WITH_POPEN_RW.
+
+ * main.c: [all these changes conditional on CVS_LOGIN: ]
+ Don't prototype connect_to_pserver, don't enter it in cmds[]
+ (actually, it was never in there, I don't know why my previous
+ change said it was).
+ (use_authenticating_server): new global var.
+ (main): if "-a", then set above new var to TRUE.
+ (usg): document "-a" option.
+
+Wed Nov 29 12:55:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c: Prototype connect_to_pserver(), and enter it in cmds[].
+ (main): test some extremely primitive authentication.
+
+ * login.c: Include <sys/socket.h>
+ (connect_to_pserver): new func.
+ (init_sockaddr): new func.
+
+Mon Nov 20 14:07:41 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * Makefile.in (TAGFILES): Separate out from DISTFILES, for C code.
+ (TAGS,tags): Use TAGFILES not DISTFILES.
+
+Sun Nov 19 11:22:43 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * recurse.c (do_recursion): Don't call server_pause_check if there
+ are writelocks around. Revise comment to reflect fact we are no
+ longer relying on a writelock'd operations being "unable" to
+ generate enough data to pause.
+
+Sun Nov 19 10:04:50 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * server.c, server.h, options.h.in: Implement hooks for doing
+ simple flow control on the server to prevent VM exhaustion on a
+ slow network with a fast server.
+ * recurse.c: Call the flow control check at a convenient location
+ while no locks are active. This is a convenience tradeoff against
+ accurate flow control - if you have a large directory it will all
+ be queued up, bypassing the flow control check until the next
+ directory is processed.
+
+Sat Nov 18 16:22:06 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c, update.c, vers_ts.c, server.c, rcs.c, lock.c,
+ ignore.c, entries.c, diff.c, commit.c, checkin.c:
+ Use new macro `existence_error', instead of comparing errno to
+ ENOENT directly.
+
+Fri Nov 17 14:56:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (start_server): removed alternate version of this func,
+ since os2/client.c will now be used under OS/2.
+
+Thu Nov 16 22:57:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (start_server): ifdef HAVE_POPEN_RW, use a different
+ version of start_server(). This is maybe not the cleanest cut to
+ make, but it's better than mucking around with yet more #ifdefs in
+ the middle of the old start_server() function. Once things are
+ up, I may reposition this code.
+
+Wed Nov 15 15:33:37 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): ifdef NEED_CALL_SOCKINIT, then call SockInit().
+ Only OS/2 needs this initialization.
+
+Tue Nov 14 18:54:01 1995 Greg A. Woods <woods@most.weird.com>
+
+ * patch.c:
+ - fix orientation of test for result of getline() call
+ - use fputs() not printf() when just copying file out
+
+ * cvsbug.sh:
+ - add space after #!
+ - new rcs id
+ - allow version to be edited by Makefile.
+
+ * Makefile.in:
+ - make Makefile a dependent of all (this might not be perfect, but
+ it at least gives you a chance to catch up on the second
+ go-around).
+ - filter cvsbug.sh in a manner similar to cvsinit.sh to get the
+ version number set from version.c
+
+Tue Nov 14 13:28:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Call old log file check.plog, not check.olog.
+
+ * sanity.sh: Convert remaining tests from old-style ('***' on fail
+ and nothing on pass), to new-style (FAIL on fail and PASS on pass).
+
+ * sanity.sh: Fix ability to run only some of the tests (always run
+ tests 1-4.75 to set up repository, document better how it works).
+
+ * sanity.sh: Change "completed successfully" to "completed" in
+ message--many tests, but not all, exit if they fail.
+
+Tue Nov 14 15:10:00 1995 Greg A. Woods <woods@most.weird.com>
+
+ * sanity.sh: test 63 doesn't work and probably can't
+
+Tue Nov 14 12:22:00 1995 Greg A. Woods <woods@most.weird.com>
+
+ * sanity.sh: many minor tweaks:
+ - make the optional arguments almost work
+ - use a function 'directory_cmp' instead of 'diff -r'
+ - fix up a few more tests that weren't working....
+
+Mon Nov 13 07:33:55 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: ifdef USE_OWN_POPEN, #include "popen.h". Only OS/2 has
+ its own popen()/pclose() right now.
+
+Mon Nov 13 04:06:10 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: conform to 80 column standard (yes, I'm a pedant).
+
+Sat Nov 11 13:45:13 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * client.c (process_prune_candidates): use unlink_file_dir() to
+ remove the directory, instead of invoking "rm" via run_exec().
+
+Fri Nov 10 14:38:56 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): removed "#define KF_GETOPT_LONG 1", since that
+ change is no longer in testing.
+
+Thu Nov 9 20:32:12 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * release.c (release): Use Popen(), not popen().
+
+Wed Nov 8 10:20:20 1995 Jim Meyering (meyering@comco.com)
+
+ * entries.c (ParseTag): Remove dcl of unused local.
+
+ * patch.c: Include getline.h.
+
+Wed Nov 8 11:57:31 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * options.h.in: add configuration option STEXID_SUPPORT (default
+ is off i.e. old semantics)
+
+ * filesubr.c (isaccessable): new function. Checks access-rights
+ for files like access(), but is getxid-safe. Falls back to
+ access() if SETXID_SUPPORT is not enabled.
+ (isfile): replace stat() by isaccessable(file, F_OK)
+ (isreadable): replace access() by isaccessable()
+ (iswritable): ditto
+ (make_directory): rename local variable buf to sb
+
+ * cvs.h: add prototype for new function isaccessable.
+
+ * server.c (serve_root): replace access() by isaccessable()
+
+ * cvsrc.c (read_cvsrc): replace access() by isreadable()
+
+ * main.c (main): replace access() by isaccessable()
+
+Wed Nov 8 10:22:41 1995 Greg A. Woods <woods@most.weird.com>
+
+ * entries.c (fgetentent): change definition to static to match the
+ declaration at the top of the file
+
+Tue Nov 7 16:59:25 1995 J.T. Conklin <jtc@lestat.cygnus.com>
+
+ * rcs.c (RCS_getbranch, RCS_getdate, RCS_getrevtime, RCS_gettag,
+ RCS_getversion, RCS_head): Use assert() instead of attempting to
+ "do the right thing" with a bogus RCSNode argument.
+
+Mon Nov 6 14:24:34 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * vers_ts.c: Remove ctime define. It is just asking for trouble.
+
+Mon Nov 6 11:58:26 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * vers_ts.c: ifdef ctime, undef it before redefining it. It is a
+ macro on some systems.
+
+ * lock.c: don't prototype ctime() here. (See note below about
+ fgetentent() in entries.c.)
+
+Sun Nov 5 16:06:01 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * entries.c (fgetentent): don't prototype ctime here; we include
+ cvs.h, which includes system.h, which includes <time.h>
+ unconditionally (either as <time.h> or <sys/time.h>). Anyway, IBM
+ C/C++ chokes on mid-function, or even mid-file, prototypes. Sigh.
+
+Thu Nov 2 21:51:04 1995 Dan Wilder <dan@gasboy.com>
+
+ * rtag.c (rtag): Fix typo ("-T" -> "-F").
+
+Tue Oct 31 19:09:11 1995 Dan Wilder <dan@gasboy.com>
+
+ * diff.c (diff_dirproc): just return R_SKIP_ALL if dir not exist.
+ (diff_file_nodiff): don't complain if file doesn't exist, just
+ ignore.
+
+Tue Oct 31 09:25:10 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * sanity.sh: Use absolute pathname for mkmodules.
+
+Sat Oct 28 01:01:41 1995 Jim Meyering (meyering@comco.com)
+
+ * entries.c (ParseTag): Use getline instead of fgets.
+
+Fri Oct 27 13:44:20 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * cvs.h: do nothing about alloca ifdef ALLOCA_IN_STDLIB. I am
+ rather suspicious of this solution, and will not be surprised to
+ find out that there's a Right Way to handle this situation ("this
+ situation" being that OS/2 simply declares alloca in <stdlib.h>).
+ Suggestions are welcome; see src/cvs.h and lib/system.h to see why
+ I was getting a conflict in the first place.
+
+Wed Oct 25 16:03:20 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * cvs.h (struct entnode): Add user field.
+ * entries.c (fputentent): New function, write entries line.
+ (write_ent_proc): Call fputentent to write entries line.
+ (Entnode_Create): New function, construct new Entnode.
+ (Entnode_Destroy): New function, destruct old Entnode.
+ (AddEntryNode): Changed to take an Entnode argument instead of
+ separate user, version, timestamp, etc. arguments.
+ (fgetentent): Changed to return Entnode.
+ (struct entent, free_entent): Removed.
+
+Wed Oct 25 12:44:32 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * admin.c (admin): Don't rely on ANSI C string concatenation;
+ SunOS 4.1.3 /bin/cc doesn't support it.
+
+Tue Oct 24 22:34:22 1995 Anthony J. Lill <ajlill@ajlc.waterloo.on.ca>
+
+ * import.c (expand_at_signs): Check errno as well as return value
+ from putc. Some systems bogusly return EOF when successfully
+ writing 0xff.
+
+Tue Oct 24 14:32:45 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * admin.c (admin): use getcaller() instead of getpwuid
+
+ * subr.c (getcaller): prefer getlogin() to $USER and $LOGNAME
+ (especially useful for NT where getuid always returns 0)
+
+Tue Oct 24 06:22:08 1995 Jim Meyering (meyering@comco.com)
+
+ * cvsrc.c (read_cvsrc): Use getline instead of fgets.
+ * patch.c (patch_fileproc): Use getline instead of fgets.
+
+ * entries.c (fgetentent): Use getline instead of fgets.
+ Use xmalloc to allocate space for each returned entry.
+ Since LINE is no longer static, save it in struct entent.
+ (struct entent): New member, line.
+ (free_entent): New function.
+ (Entries_Open): Call it after each call to fgetentent.
+
+Tue Oct 24 11:13:15 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * cvs.h: Declare valloc again, but this time with the right
+ signature (also changed in libs/valloc.c)
+
+Mon Oct 23 12:17:03 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * logmsg.c (do_editor): Check for errors from stdio calls.
+
+Mon Oct 23 12:37:06 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * cvs.h: Don't declare valloc. Some systems (e.g. linux) declare
+ it in stdlib.h in a conflicting way.
+
+Mon Oct 23 08:41:25 1995 Jim Meyering (meyering@comco.com)
+
+ * commit.c (commit_filesdoneproc): Use getline instead of fgets.
+
+ * logmsg.c (do_editor): Use getline instead of fgets.
+ (rcsinfo_proc): Likewise.
+
+ * logmsg.c (do_editor): Lose if fclose of temp file output
+ stream fails.
+
+Mon Oct 23 11:59:41 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * cvs.h: add valloc declaration
+
+ * server.h: add server_cleanup prototype
+
+ * server.c: remove server_cleanup prototype
+
+ * mkmodules.c (server_cleanup): fix parameter type
+
+ * server.c: encapsulate wait_sig in #ifdef sun (it's only used in
+ code which is also encapsulated in #ifdef sun)
+
+ * rcscmds.c (RCS_deltag, RCS_lock): add definition of noerr
+ parameter
+
+ * error.c: include cvs.h instead of config.h, add USE(rcsid)
+
+ * error.c (error): fix parameter type
+
+ * update.c (join_file): encapsulate recent changes from garyo
+ within #ifdef SERVER_SUPPORT
+
+Sun Oct 22 13:47:53 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * client.c (update_entries): Fix memory leak; free mode_string and
+ file_timestamp.
+ (send_fileproc): Fix memory leak; call freevers_ts before exiting.
+
+ * module.c (do_module): Partially fix memory leak; added
+ variable so that the address of memory allocated by line2argv
+ is retained, but comment out the call to free_names. Freeing
+ the vector at that point loses because some of the elements
+ may be used later in the function.
+ (cat_module): fix memory leak.
+
+ * recurse.c (start_recursion): Fix memory leak; free return
+ value of Name_Repository after it has been used.
+
+Sat Oct 21 23:24:26 1995 Jim Meyering (meyering@comco.com)
+
+ * client.c (send_modified) [LINES_CRLF_TERMINATED]: Comment text
+ after #endif.
+
+Fri Oct 20 14:41:49 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Add test 87a, to test for bug fixed by garyo in
+ change below.
+
+Fri Oct 20 10:59:58 1995 Gary Oberbrunner <garyo@darkstar.avs.com>
+
+ * update.c (join_file): send file back to client even if no
+ conflicts were detected, by calling Register().
+
+Fri Oct 20 10:46:45 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * lock.c: Add prototype for Check_Owner
+
+Thu Oct 19 16:38:14 1995 Jim Meyering (meyering@comco.com)
+
+ * lock.c (Check_Owner): Declare function `static int'.
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * expand_path.c (expand_variable): Fix typo ('*'->'(').
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * commit.c (commit_filesdoneproc): Check for errors from fopen,
+ fgets, and fclose.
+
+ * rcscmds.c (RCS_merge): Remove comment about rcsmerge -E.
+ Hacking CVS was never a very good solution; the situation is fixed
+ in RCS 5.7, and is documented in ../INSTALL.
+
+Thu Oct 19 15:06:15 1995 Jim Meyering (meyering@comco.com)
+
+ * filesubr.c (xchmod): Parenthesize arithmetic in operand of |
+ to placate gcc -Wall.
+
+ * expand_path.c (expand_path): Parenthesize assignments used as
+ truth values to placate gcc -Wall.
+
+ * commit.c (checkaddfile): Remove dcls of unused variables.
+ * lock.c (unlock): Remove dcl of unused variable.
+
+Thu Oct 19 14:58:40 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * root.c (Create_Root): If noexec, don't create CVS/Root.
+
+Wed Oct 18 11:19:40 1995 J.T. Conklin <jtc@slave.cygnus.com>
+
+ * lock.c (unlock): Change order of comparison so that Check_Owner
+ is called only if other conditions are true. This performance
+ enhancement was broken when the AFS support was added.
+
+Wed Oct 18 12:51:33 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): check if argv[0] is "pserver" with else-if, not
+ if, since we've already asked if it's "kserver".
+
+Tue Oct 17 18:09:23 1995 Warren Jones <wjones@tc.fluke.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * sanity.sh: Deal with supplying a relative cvs filename, or
+ with a cvs filename which doesn't have basename "cvs".
+
+Mon Oct 16 15:58:31 1995 Vince Demarco <vdemarco@bou.shl.com>
+
+ * parseinfo.c (Parse_Info): if the Keyword isn't ALL the current
+ version doesn't use the expanded variable, It should.
+
+Mon Oct 16 15:58:31 1995 Gary Oberbrunner <garyo@avs.com>
+ and Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * server.c (server_register): Don't pass NULL to printf if tag,
+ date, or conflict is NULL.
+
+Thu Oct 12 12:13:42 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * main.c (main): begin to handle "pserver"; support not complete
+ yet, however.
+
+Thu Oct 12 02:52:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * expand_path.c: Don't #include <pwd.h>, since cvs.h already does,
+ and not all systems' <pwd.h>s are protected from multiple inclusion.
+ * login.c: Likewise.
+
+Wed Oct 11 15:23:24 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * login.c (login): handle everything correctly now.
+
+Wed Oct 11 12:02:48 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * rcs.c (RCS_gettag): support RCS keyword Name
+
+Tue Oct 10 19:11:16 1995 Karl Fogel <kfogel@floss.cyclic.com>
+
+ * options.h.in (CVS_LOGIN): discuss, but leave commented out.
+ The "cvs login" command is still under construction; however, the
+ repository was changing so fast that instead of creating a branch
+ and dealing with the attendant hair, I'm just developing on the
+ trunk, making sure that everything is surrounded by "#ifdef
+ CVS_LOGIN ... #endif" so I don't get in anyone's way.
+
+ * login.c: include cvs.h before checking CVS_LOGIN, so it has a
+ chance to get defined before we ask if it's defined.
+ (login): oops, use semi not comma in `for' loop init.
+
+ * Makefile.in (SOURCES, OBJECTS): include login.c, login.o.
+
+ * main.c: added protoype for login().
+ Added "login" entry to cmds[].
+ (usg): added line about "login".
+
+ * login.c: new file.
+
+Tue Oct 10 18:33:47 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * Makefile.in (COMMON_OBJECTS): added error.o.
+ (OBJECTS): took error.o out; it's in COMMON_OBJECTS now.
+
+Tue Oct 10 12:02:37 1995 Thorsten Lockert <tholo@sigmasoft.com>
+
+ * cvsbug.sh: Cater to lame versions of sh (4.4BSD ash) by using
+ ${foo-bar} instead of `if....`.
+
+Tue Oct 10 12:02:37 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * remove.c (remove_fileproc): If noexec, don't remove file. Check
+ for error when removing file.
+
+Sun Oct 8 12:32:15 1995 Peter Wemm <peter@haywire.DIALix.COM>
+
+ * run.c: detect/use POSIX/BSD style reliable signals for critical
+ section masking etc. Helps prevent stray locks on interruption.
+
+Sat Oct 7 23:26:54 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * admin.c (admin): If group CVS_ADMIN_GROUP exists, allow only
+ users in that group to use "cvs admin".
+ * options.h.in: Default CVS_ADMIN_GROUP to "cvsadmin".
+
+Sat Oct 7 23:05:24 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * add.c, checkout.c, commit.c, cvs.h, filesubr.c, import.c,
+ lock.c, main.c, modules.c, options.h.in: New variable cvsumask
+ which is used to set mode of files in repository (regardless of
+ umask in effect when cvs is run).
+
+Sat Oct 7 22:40:17 1995 Stephen Bailey <sjbailey@sand.npl.washington.edu>
+
+ * lock.c: Include AFSCVS ifdefs to deal with AFS's lack of
+ correspondance between userid's from stat and from geteuid.
+
+Sat Oct 7 22:28:49 1995 Scott Carson <sdc@TracerTech.COM>
+
+ * add.c (add): Pass -ko, not -k -ko, to set keyword expansion options.
+
+ * admin.c (admin): Don't skip first argument when sending to server.
+
+Fri Oct 6 21:45:03 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * version.c: Version 1.6.1.
+
+Fri Oct 6 21:31:28 1995 Jeff Johnson <jbj@brewster.jbj.org>
+
+ * cvs.h, admin.c, client.c, commit.c, log.c, modules.c,
+ parseinfo.c, patch.c, recurse.c, rtag.c, status.c, tag.c:
+ Prototype when dealing in pointers to functions.
+
+Fri Oct 6 21:07:22 1995 Mark H. Wilkinson <mhw@minster.york.ac.uk>
+
+ * cvsrc.c (read_cvsrc): fix look up of command names in cvsrc file
+ to use full name from command table rather than possible nickname
+ in argv. Fixes errors with things like `cvs di' when cvsrc has
+ `diff -u5' in it.
+
+Thu Aug 3 01:03:52 1995 Vince DeMarco <vdemarco@bou.shl.com>
+
+ * parseinfo.c (Parse_Info): Add code to call expand_path function
+ instead of using built in code.
+
+ * wrapper.c (wrap_add): Add code to call expand_path function to
+ expand all built in variables.
+
+ * expand_path.c (New file): expand things that look like
+ environmental variables (only expand local CVS environmental
+ variables) and user names like ~/.
+ * cvs.h: Declare expand_path.
+
+ * Makefile.in (SOURCES, OBJECTS): Added expand_path.c,
+ expand_path.o.
+
+Fri Oct 6 14:03:09 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * ignore.c (ign_setup): Don't try to look for a file in CVSroot if
+ client. (The recent tightening of the error checking detects this).
+
+ * commit.c (checkaddfile): Don't try to pass options if it is "".
+
+Thu Oct 5 18:04:46 1995 Karl Fogel <kfogel@totoro.cyclic.com>
+
+ * sanity.sh: unset CVSREAD, since it causes the script to bomb.
+
+Thu Oct 5 18:29:17 1995 Jim Kingdon <kingdon@harvey.cyclic.com>
+
+ * remove.c, add.c, commit.c, cvs.h: Remove CVSEXT_OPT stuff; it
+ has been broken for ages and the options are already stored in the
+ Entries file.
+
+Thu Oct 5 18:20:13 1995 Norbert Kiesel <nk@col.sw-ley.de>
+
+ * commit.c (checkaddfile): New argument options; pass it to RCS.
+ (commit_fileproc): Pass it.
+
Tue Oct 3 09:26:00 1995 Karl Fogel <kfogel@totoro.cyclic.com>
* version.c: upped to 1.6.
@@ -40,11 +1647,11 @@ Fri Sep 29 13:22:44 1995 Jim Blandy <jimb@totoro.cyclic.com>
* diff.c (diff): Doc fix.
Fri Sep 29 14:32:36 1995 Norbert Kiesel <nk@col.sw-ley.de>
-
+
* repos.c (Short_Repository): chop superfluous "/".
-
+
* tag.c (pretag_proc): correct user-visible string.
-
+
* rtag.c (pretag_proc): correct user-visible string.
Fri Sep 29 13:45:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
@@ -53,9 +1660,9 @@ Fri Sep 29 13:45:36 1995 Karl Fogel <kfogel@floss.cyclic.com>
nothing.
Thu Sep 28 13:37:05 1995 Larry Jones <larry.jones@sdrc.com>
-
+
* server.c: ifdef ISC, include <sys/bsdtypes.h>.
-
+
Fri Sep 29 07:54:22 1995 Mike Sutton <mws115@llcoolj.dayton.saic.com>
* filesubr.c (last_component): Don't use ANSI style declaration.
@@ -134,7 +1741,7 @@ Thu Sep 7 19:18:00 1995 Jim Blandy <jimb@cyclic.com>
Thu Sep 7 14:38:06 1995 Karl Fogel <kfogel@floss.cyclic.com>
* ALL FILES: add semicolon, as indicated below.
-
+
* cvs.h (USE): don't provide semicolon in the expansion of the USE
macro; we'd rather the callers provided it themselves because that
way etags doesn't get fooled.
diff --git a/gnu/usr.bin/cvs/src/Makefile.in b/gnu/usr.bin/cvs/src/Makefile.in
index 73478a8e619..436d1ef6cef 100644
--- a/gnu/usr.bin/cvs/src/Makefile.in
+++ b/gnu/usr.bin/cvs/src/Makefile.in
@@ -44,30 +44,34 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
LIBS = @LIBS@
SOURCES = add.c admin.c checkin.c checkout.c classify.c client.c commit.c \
-create_adm.c cvsrc.c diff.c entries.c find_names.c hash.c history.c ignore.c \
-import.c lock.c log.c logmsg.c main.c modules.c myndbm.c no_diff.c \
+create_adm.c cvsrc.c diff.c edit.c entries.c error.c expand_path.c \
+fileattr.c find_names.c hash.c history.c ignore.c import.c \
+lock.c log.c login.c logmsg.c main.c modules.c myndbm.c no_diff.c \
parseinfo.c patch.c rcs.c rcscmds.c recurse.c release.c remove.c repos.c \
-root.c rtag.c server.c status.c subr.c filesubr.c run.c tag.c update.c \
-wrapper.c vers_ts.c version.c
+root.c rtag.c scramble.c server.c status.c subr.c filesubr.c run.c \
+tag.c update.c watch.c wrapper.c vers_ts.c version.c
MSOURCES = mkmodules.c
-OBJECTS = add.o admin.o checkin.o checkout.o classify.o commit.o \
-create_adm.o cvsrc.o diff.o entries.o find_names.o hash.o history.o \
-ignore.o import.o lock.o log.o logmsg.o main.o modules.o myndbm.o no_diff.o \
+OBJECTS = add.o admin.o checkin.o checkout.o classify.o client.o commit.o \
+create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \
+fileattr.o find_names.o hash.o history.o ignore.o import.o \
+lock.o log.o login.o logmsg.o main.o modules.o myndbm.o no_diff.o \
parseinfo.o patch.o rcs.o rcscmds.o recurse.o release.o remove.o repos.o \
-root.o rtag.o status.o tag.o update.o wrapper.o vers_ts.o server.o client.o
+root.o rtag.o scramble.o server.o status.o tag.o update.o \
+watch.o wrapper.o vers_ts.o
MOBJECTS = hash.o mkmodules.o myndbm.o
-COMMON_OBJECTS = subr.o filesubr.o run.o version.o
+COMMON_OBJECTS = subr.o filesubr.o run.o version.o error.o
-HEADERS = cvs.h rcs.h hash.h myndbm.h patchlevel.h \
- update.h server.h client.h
+HEADERS = cvs.h rcs.h hash.h myndbm.h \
+ update.h server.h client.h error.h fileattr.h edit.h watch.h
+
+TAGFILES = $(HEADERS) options.h.in $(SOURCES) $(MSOURCES)
DISTFILES = .cvsignore Makefile.in ChangeLog ChangeLog.fsf \
NOTES README-rm-add \
- sanity.sh cvsbug.sh \
- $(HEADERS) options.h.in $(SOURCES) $(MSOURCES)
+ sanity.sh cvsbug.sh $(TAGFILES)
PROGS = cvs mkmodules cvsbug
@@ -82,7 +86,7 @@ INCLUDES = -I. -I.. -I$(srcdir) -I$(top_srcdir)/lib
.c.o:
$(CC) $(CPPFLAGS) $(INCLUDES) $(DEFS) $(CFLAGS) -c $<
-all: $(PROGS)
+all: Makefile $(PROGS)
.PHONY: all
saber_cvs:
@@ -121,11 +125,11 @@ remotecheck: all
$(SHELL) $(srcdir)/sanity.sh -r `pwd`/cvs
.PHONY: remotecheck
-tags: $(DISTFILES)
- ctags $(DISTFILES)
+tags: $(TAGFILES)
+ ctags $(TAGFILES)
-TAGS: $(DISTFILES)
- etags `for i in $(DISTFILES); do echo $(srcdir)/$$i; done`
+TAGS: $(TAGFILES)
+ etags `for i in $(TAGFILES); do echo $(srcdir)/$$i; done`
ls:
@echo $(DISTFILES)
@@ -172,7 +176,16 @@ mkmodules: $(MOBJECTS) $(COMMON_OBJECTS)
$(CC) $(LDFLAGS) -o $@ $(MOBJECTS) $(COMMON_OBJECTS) ../lib/libcvs.a $(LIBS) $(LIBIBERTY)
cvsbug: cvsbug.sh
- cp $(srcdir)/cvsbug.sh cvsbug
+ echo > .fname \
+ cvs-`sed < $(srcdir)/version.c \
+ -e '/version_string/!d' \
+ -e 's/[^0-9.]*\([0-9.]*\).*/\1/' \
+ -e q`
+ sed -e 's,xLIBDIRx,$(libdir)/cvs,g' \
+ -e "s,xVERSIONx,`cat .fname`,g" $(srcdir)/$@.sh > $@-t
+ rm -f .fname
+ mv $@-t $@
+ chmod a+x $@
# Compilation rules.
diff --git a/gnu/usr.bin/cvs/src/add.c b/gnu/usr.bin/cvs/src/add.c
index d5224f16e08..fd800763254 100644
--- a/gnu/usr.bin/cvs/src/add.c
+++ b/gnu/usr.bin/cvs/src/add.c
@@ -101,7 +101,7 @@ add (argc, argv)
int i;
start_server ();
ign_setup ();
- option_with_arg ("-k", options);
+ if (options) send_arg(options);
option_with_arg ("-m", message);
for (i = 0; i < argc; ++i)
/* FIXME: Does this erroneously call Create_Admin in error
@@ -128,9 +128,9 @@ add (argc, argv)
free (date);
free (rcsdir);
}
+ send_file_names (argc, argv);
send_files (argc, argv, 0, 0);
- if (fprintf (to_server, "add\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("add\012", 0);
return get_responses_and_close ();
}
#endif
@@ -438,7 +438,7 @@ add_directory (repository, dir)
}
#endif
- omask = umask ((mode_t) 2);
+ omask = umask (cvsumask);
if (CVS_MKDIR (rcsdir, 0777) < 0)
{
error (0, errno, "cannot mkdir %s", rcsdir);
@@ -517,16 +517,7 @@ build_entry (repository, user, options, message, entries, tag)
return (0);
/*
- * The options for the "add" command are store in the file CVS/user,p
- * XXX - no they are not!
- */
- (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
- fp = open_file (fname, "w+");
- if (fclose (fp) == EOF)
- error(1, errno, "cannot close %s", fname);
-
- /*
- * And the requested log is read directly from the user and stored in the
+ * The requested log is read directly from the user and stored in the
* file user,t. If the "message" argument is set, use it as the
* initial creation log (which typically describes the file).
*/
@@ -539,8 +530,8 @@ build_entry (repository, user, options, message, entries, tag)
/*
* Create the entry now, since this allows the user to interrupt us above
- * without needing to clean anything up (well, we could clean up the ,p
- * and ,t files, but who cares).
+ * without needing to clean anything up (well, we could clean up the
+ * ,t file, but who cares).
*/
(void) sprintf (line, "Initial %s", user);
Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
diff --git a/gnu/usr.bin/cvs/src/admin.c b/gnu/usr.bin/cvs/src/admin.c
index b85df3622a9..ebbda9a122c 100644
--- a/gnu/usr.bin/cvs/src/admin.c
+++ b/gnu/usr.bin/cvs/src/admin.c
@@ -12,6 +12,9 @@
*/
#include "cvs.h"
+#ifdef CVS_ADMIN_GROUP
+#include <grp.h>
+#endif
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)admin.c 1.20 94/09/30 $";
@@ -38,10 +41,37 @@ admin (argc, argv)
char **argv;
{
int err;
-
+#ifdef CVS_ADMIN_GROUP
+ struct group *grp;
+#endif
if (argc <= 1)
usage (admin_usage);
+#ifdef CVS_ADMIN_GROUP
+ grp = getgrnam(CVS_ADMIN_GROUP);
+ /* skip usage right check if group CVS_ADMIN_GROUP does not exist */
+ if (grp != NULL)
+ {
+ char *me = getcaller();
+ char **grnam = grp->gr_mem;
+ int denied = 1;
+
+ while (*grnam)
+ {
+ if (strcmp(*grnam, me) == 0)
+ {
+ denied = 0;
+ break;
+ }
+ grnam++;
+ }
+
+ if (denied)
+ error (1, 0, "usage is restricted to members of the group %s",
+ CVS_ADMIN_GROUP);
+ }
+#endif
+
wrap_setup ();
/* skip all optional arguments to see if we have any file names */
@@ -65,26 +95,22 @@ admin (argc, argv)
ign_setup ();
- for (i = 1; i <= ac; ++i)
+ for (i = 0; i <= ac; ++i) /* XXX send -ko too with i = 0 */
send_arg (av[i]);
-#if 0
+ send_file_names (argc, argv);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
- send_file_names (argc, argv);
-#else
send_files (argc, argv, 0, 0);
-#endif
- if (fprintf (to_server, "admin\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("admin\012", 0);
return get_responses_and_close ();
}
#endif /* CLIENT_SUPPORT */
/* start the recursion processor */
- err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc,
- (int (*) ()) NULL, argc, argv, 0,
+ err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
+ (DIRLEAVEPROC) NULL, argc, argv, 0,
W_LOCAL, 0, 1, (char *) NULL, 1, 0);
return (err);
}
@@ -106,17 +132,18 @@ admin_fileproc (file, update_dir, repository, entries, srcfiles)
char **argv;
int argc;
int retcode = 0;
+ int status = 0;
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
file, 0, 0, entries, srcfiles);
version = vers->vn_user;
if (version == NULL)
- return (0);
+ goto exitfunc;
else if (strcmp (version, "0") == 0)
{
error (0, 0, "cannot admin newly added file `%s'", file);
- return (0);
+ goto exitfunc;
}
run_setup ("%s%s", Rcsbin, RCS);
@@ -128,9 +155,12 @@ admin_fileproc (file, update_dir, repository, entries, srcfiles)
if (!quiet)
error (0, retcode == -1 ? errno : 0,
"%s failed for `%s'", RCS, file);
- return (1);
+ status = 1;
+ goto exitfunc;
}
- return (0);
+ exitfunc:
+ freevers_ts (&vers);
+ return status;
}
/*
diff --git a/gnu/usr.bin/cvs/src/checkin.c b/gnu/usr.bin/cvs/src/checkin.c
index 09a81f764ff..6a75b4cd736 100644
--- a/gnu/usr.bin/cvs/src/checkin.c
+++ b/gnu/usr.bin/cvs/src/checkin.c
@@ -16,6 +16,8 @@
*/
#include "cvs.h"
+#include "fileattr.h"
+#include "edit.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)checkin.c 1.48 94/10/07 $";
@@ -66,7 +68,7 @@ Checkin (type, file, update_dir, repository,
{
copy_file (tocvsPath, fname);
if (unlink_file_dir (file) < 0)
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "cannot remove %s", file);
copy_file (tocvsPath, file);
}
@@ -121,7 +123,7 @@ Checkin (type, file, update_dir, repository,
* If we want read-only files, muck the permissions here, before
* getting the file time-stamp.
*/
- if (cvswrite == FALSE)
+ if (cvswrite == FALSE || fileattr_get (file, "_watched"))
xchmod (file, 0);
#ifndef DEATH_SUPPORT
@@ -198,7 +200,9 @@ Checkin (type, file, update_dir, repository,
else
server_checked_in (file, update_dir, repository);
}
+ else
#endif
+ mark_up_to_date (file);
return (0);
}
diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c
index 422cfeeb75f..b78eb97388e 100644
--- a/gnu/usr.bin/cvs/src/checkout.c
+++ b/gnu/usr.bin/cvs/src/checkout.c
@@ -89,6 +89,7 @@ static int pipeout;
static int aflag;
static char *options = NULL;
static char *tag = NULL;
+static int tag_validated = 0;
static char *date = NULL;
static char *join_rev1 = NULL;
static char *join_rev2 = NULL;
@@ -324,15 +325,13 @@ checkout (argc, argv)
client_nonexpanded_setup ();
}
- if (fprintf
- (to_server,
- strcmp (command_name, "export") == 0 ? "export\n" : "co\n")
- < 0)
- error (1, errno, "writing to server");
+ send_to_server (strcmp (command_name, "export") == 0 ?
+ "export\012" : "co\012",
+ 0);
return get_responses_and_close ();
}
-#endif
+#endif /* CLIENT_SUPPORT */
if (cat || status)
{
@@ -360,7 +359,12 @@ checkout (argc, argv)
(void) sprintf (repository, "%s/%s/%s", CVSroot, CVSROOTADM,
CVSNULLREPOS);
if (!isfile (repository))
+ {
+ mode_t omask;
+ omask = umask (cvsumask);
(void) CVS_MKDIR (repository, 0777);
+ (void) umask (omask);
+ }
/* I'm not sure whether this check is redundant. */
if (!isdir (repository))
@@ -719,13 +723,29 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
return (1);
}
which = W_REPOS;
+ if (tag != NULL && !tag_validated)
+ {
+ tag_check_valid (tag, *pargc - 1, argv + 1, 0, aflag, NULL);
+ tag_validated = 1;
+ }
}
else
+ {
which = W_LOCAL | W_REPOS;
+ if (tag != NULL && !tag_validated)
+ {
+ tag_check_valid (tag, *pargc - 1, argv + 1, 0, aflag,
+ repository);
+ tag_validated = 1;
+ }
+ }
if (tag != NULL || date != NULL)
which |= W_ATTIC;
+ /* FIXME: We don't call tag_check_valid on join_rev1 and join_rev2
+ yet (make sure to handle ':' correctly if we do, though). */
+
/*
* if we are going to be recursive (building dirs), go ahead and call the
* update recursion processor. We will be recursive unless either local
@@ -755,7 +775,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
entries = Entries_Open (0);
for (i = 1; i < *pargc; i++)
{
- char line[MAXLINELEN];
+ char *line;
char *user;
Vers_TS *vers;
@@ -764,10 +784,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
force_tag_match, 0, entries, (List *) NULL);
if (vers->ts_user == NULL)
{
+ line = xmalloc (strlen (user) + 15);
(void) sprintf (line, "Initial %s", user);
Register (entries, user, vers->vn_rcs ? vers->vn_rcs : "0",
line, vers->options, vers->tag,
vers->date, (char *) 0);
+ free (line);
}
freevers_ts (&vers);
}
diff --git a/gnu/usr.bin/cvs/src/client.c b/gnu/usr.bin/cvs/src/client.c
index 5f34c5dcd7b..0fa3299e4d9 100644
--- a/gnu/usr.bin/cvs/src/client.c
+++ b/gnu/usr.bin/cvs/src/client.c
@@ -1,25 +1,33 @@
/* CVS client-related stuff. */
#include "cvs.h"
+#include "getline.h"
#ifdef CLIENT_SUPPORT
-#include "update.h" /* Things shared with update.c */
#include "md5.h"
-#if HAVE_KERBEROS
-#define CVS_PORT 1999
-
+#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
+#endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS */
+
+#ifdef AUTH_CLIENT_SUPPORT
+char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot));
+#endif /* AUTH_CLIENT_SUPPORT */
+
+#if HAVE_KERBEROS
+#define CVS_PORT 1999
+
#include <krb.h>
extern char *krb_realmofhost ();
#ifndef HAVE_KRB_GET_ERR_TEXT
#define krb_get_err_text(status) krb_err_txt[status]
-#endif
-#endif
+#endif /* HAVE_KRB_GET_ERR_TEXT */
+#endif /* HAVE_KERBEROS */
+
static void add_prune_candidate PROTO((char *));
@@ -62,6 +70,7 @@ static void handle_set_update_prog PROTO((char *, int));
static void handle_module_expansion PROTO((char *, int));
static void handle_m PROTO((char *, int));
static void handle_e PROTO((char *, int));
+static void handle_notified PROTO((char *, int));
#endif /* CLIENT_SUPPORT */
@@ -76,10 +85,10 @@ static void handle_e PROTO((char *, int));
char *
#ifdef __STDC__
mode_to_string (mode_t mode)
-#else
+#else /* ! __STDC__ */
mode_to_string (mode)
mode_t mode;
-#endif
+#endif /* __STDC__ */
{
char buf[18], u[4], g[4], o[4];
int i;
@@ -115,6 +124,40 @@ change_mode (filename, mode_string)
char *filename;
char *mode_string;
{
+#ifdef CHMOD_BROKEN
+ char *p;
+ int writeable = 0;
+
+ /* We can only distinguish between
+ 1) readable
+ 2) writeable
+ 3) Picasso's "Blue Period"
+ We handle the first two. */
+ p = mode_string;
+ while (*p != '\0')
+ {
+ if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
+ {
+ char *q = p + 2;
+ while (*q != ',' && *q != '\0')
+ {
+ if (*q == 'w')
+ writeable = 1;
+ ++q;
+ }
+ }
+ /* Skip to the next field. */
+ while (*p != ',' && *p != '\0')
+ ++p;
+ if (*p == ',')
+ ++p;
+ }
+
+ xchmod (filename, writeable);
+ return 0;
+
+#else /* ! CHMOD_BROKEN */
+
char *p;
mode_t mode = 0;
@@ -169,9 +212,11 @@ change_mode (filename, mode_string)
if (*p == ',')
++p;
}
+
if (chmod (filename, mode) < 0)
return errno;
return 0;
+#endif /* ! CHMOD_BROKEN */
}
#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
@@ -189,20 +234,68 @@ int client_active;
int client_prune_dirs;
+static int cvsroot_parsed = 0;
+
+static List *ignlist = (List *) NULL;
+
/* Set server_host and server_cvsroot. */
static void
parse_cvsroot ()
{
char *p;
+#ifdef AUTH_CLIENT_SUPPORT
+ static char *access_method;
+#endif /* AUTH_CLIENT_SUPPORT */
+
+ /* Don't go through the trouble twice. */
+ if (cvsroot_parsed)
+ return;
server_host = xstrdup (CVSroot);
+
+#ifdef AUTH_CLIENT_SUPPORT
+ if ((server_host[0] == ':'))
+ {
+ /* Access method specified, as in
+ * "cvs -d :pserver:user@host:/path".
+ * We need to get past that part of CVSroot before parsing the
+ * rest of it.
+ */
+ access_method = p = &(server_host[1]);
+
+ if (! *access_method)
+ error (1, 0, "bad CVSroot: %s", CVSroot);
+
+ if (! *(p = strchr (access_method, ':')))
+ error (1, 0, "bad CVSroot: %s", CVSroot);
+
+ *p = '\0';
+ p++;
+
+ server_host = p;
+
+ if (! *server_host)
+ error (1, 0, "bad CVSroot: %s", CVSroot);
+
+ if (strcmp (access_method, "pserver") == 0)
+ use_authenticating_server = 1;
+ else
+ error (1, 0, "unknown access method: %s", access_method);
+ }
+#endif /* AUTH_CLIENT_SUPPORT */
+
+ /* First get just the pathname. */
server_cvsroot = strchr (server_host, ':');
*server_cvsroot = '\0';
++server_cvsroot;
-
- if ( (p = strchr (server_host, '@')) == NULL) {
+
+ /* Then deal with host and possible user. */
+ if ( (p = strchr (server_host, '@')) == NULL)
+ {
server_user = NULL;
- } else {
+ }
+ else
+ {
server_user = server_host;
server_host = p;
++server_host;
@@ -210,20 +303,36 @@ parse_cvsroot ()
}
client_active = 1;
+ cvsroot_parsed = 1;
}
+#ifdef NO_SOCKET_TO_FD
+/* Under certain circumstances, we must communicate with the server
+ via a socket using send() and recv(). This is because under some
+ operating systems (OS/2 and Windows 95 come to mind), a socket
+ cannot be converted to a file descriptor -- it must be treated as a
+ socket and nothing else. */
+static int use_socket_style = 0;
+static int server_sock;
+#endif /* NO_SOCKET_TO_FD */
+
/* Stream to write to the server. */
-FILE *to_server;
+static FILE *to_server;
/* Stream to read from the server. */
-FILE *from_server;
+static FILE *from_server;
+
+/* We might want to log client/server traffic. */
+static FILE *from_server_logfile;
+static FILE *to_server_logfile;
#if ! RSH_NOT_TRANSPARENT
/* Process ID of rsh subprocess. */
static int rsh_pid = -1;
-#endif
+#endif /* ! RSH_NOT_TRANSPARENT */
+
/*
- * Read a line from the server.
+ * Read a line from the server. Result does not include the terminating \n.
*
* Space for the result is malloc'd and should be freed by the caller.
*
@@ -237,21 +346,46 @@ read_line (resultp, eof_ok)
{
int c;
char *result;
- int input_index = 0;
- int result_size = 80;
+ size_t input_index = 0;
+ size_t result_size = 80;
+
+#ifdef NO_SOCKET_TO_FD
+ if (! use_socket_style)
+#endif /* NO_SOCKET_TO_FD */
+ fflush (to_server);
- fflush (to_server);
result = (char *) xmalloc (result_size);
while (1)
{
- c = getc (from_server);
+
+#ifdef NO_SOCKET_TO_FD
+ if (use_socket_style)
+ {
+ char ch;
+ /* Yes, this sucks performance-wise. Short of implementing
+ our own buffering, I'm not sure how to effect a big
+ improvement. We could at least avoid calling
+ read_from_server() for each character if we were willing
+ to duplicate a lot of its code, but I'm not sure that's
+ worth it. */
+ read_from_server (&ch, 1);
+ c = ch;
+ }
+ else
+#endif /* NO_SOCKET_TO_FD */
+ c = getc (from_server);
if (c == EOF)
{
free (result);
- if (ferror (from_server))
- error (1, errno, "reading from server");
+
+#ifdef NO_SOCKET_TO_FD
+ if (! use_socket_style)
+#endif /* NO_SOCKET_TO_FD */
+ if (ferror (from_server))
+ error (1, errno, "reading from server");
+
/* It's end of file. */
if (eof_ok)
return 0;
@@ -276,12 +410,31 @@ read_line (resultp, eof_ok)
/* Terminate it just for kicks, but we *can* deal with embedded NULs. */
result[input_index] = '\0';
+#ifdef NO_SOCKET_TO_FD
+ if (! use_socket_style)
+#endif /* NO_SOCKET_TO_FD */
+ {
+ /*
+ * If we're using socket style, then everything has already
+ * been logged because read_from_server() was used to get the
+ * individual chars, and read_from_server() logs already.
+ */
+ if (from_server_logfile)
+ {
+ if (fwrite (result, 1, input_index, from_server_logfile)
+ < input_index)
+ error (0, errno, "writing to from-server logfile");
+ putc ('\n', from_server_logfile);
+ }
+ }
+
if (resultp == NULL)
free (result);
return input_index;
}
#endif /* CLIENT_SUPPORT */
+
#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
@@ -306,7 +459,7 @@ int filter_through_gunzip (fd, dir, pidp)
int fd, dir;
pid_t *pidp;
{
- static char *gunzip_argv[2] = { "gunzip" };
+ static char *gunzip_argv[3] = { "gunzip", "-d" };
return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
}
@@ -392,8 +545,9 @@ handle_valid_requests (args, len)
* Server wants to know if we have this, to enable the
* feature.
*/
- if (fprintf(to_server, "%s\n", rq->name) < 0)
- error (1, errno, "writing to server");
+ send_to_server (rq->name, 0);
+ send_to_server ("\012", 0);
+
if (!strcmp("UseUnchanged",rq->name))
use_unchanged = 1;
}
@@ -443,7 +597,7 @@ get_short_pathname (name)
* SHORT_PATHNAME. When we call FUNC, the curent directory points to
* the directory portion of SHORT_PATHNAME. */
-static char *last_dirname;
+static char *last_dir_name;
static void
call_in_directory (pathname, func, data)
@@ -454,7 +608,7 @@ call_in_directory (pathname, func, data)
{
static List *last_entries;
- char *dirname;
+ char *dir_name;
char *filename;
/* Just the part of pathname relative to toplevel_repos. */
char *short_pathname = get_short_pathname (pathname);
@@ -515,17 +669,17 @@ call_in_directory (pathname, func, data)
else
*p = '\0';
- dirname = xstrdup (short_pathname);
- p = strrchr (dirname, '/');
+ dir_name = xstrdup (short_pathname);
+ p = strrchr (dir_name, '/');
if (p == NULL)
{
- dirname = xrealloc (dirname, 2);
- dirname[0] = '.'; dirname[1] = '\0';
+ dir_name = xrealloc (dir_name, 2);
+ dir_name[0] = '.'; dir_name[1] = '\0';
}
else
*p = '\0';
if (client_prune_dirs)
- add_prune_candidate (dirname);
+ add_prune_candidate (dir_name);
filename = strrchr (short_repos, '/');
if (filename == NULL)
@@ -542,12 +696,12 @@ call_in_directory (pathname, func, data)
strcat (short_pathname, filename);
}
- if (last_dirname == NULL
- || strcmp (last_dirname, dirname) != 0)
+ if (last_dir_name == NULL
+ || strcmp (last_dir_name, dir_name) != 0)
{
- if (last_dirname)
- free (last_dirname);
- last_dirname = dirname;
+ if (last_dir_name)
+ free (last_dir_name);
+ last_dir_name = dir_name;
if (toplevel_wd[0] == '\0')
if (getwd (toplevel_wd) == NULL)
@@ -556,17 +710,17 @@ call_in_directory (pathname, func, data)
if (chdir (toplevel_wd) < 0)
error (1, errno, "could not chdir to %s", toplevel_wd);
- if (chdir (dirname) < 0)
+ if (chdir (dir_name) < 0)
{
char *dir;
char *dirp;
- if (errno != ENOENT)
- error (1, errno, "could not chdir to %s", dirname);
+ if (! existence_error (errno))
+ error (1, errno, "could not chdir to %s", dir_name);
/* Directory does not exist, we need to create it. */
- dir = xmalloc (strlen (dirname) + 1);
- dirp = dirname;
+ dir = xmalloc (strlen (dir_name) + 1);
+ dirp = dir_name;
rdirp = reposdirname;
/* This algorithm makes nested directories one at a time
@@ -578,11 +732,11 @@ call_in_directory (pathname, func, data)
2) .. foo/bar .. <root>/foo/bar
3) .. foo/bar/baz .. <root>/foo/bar/baz
- As you can see, we're just stepping along DIRNAME (with
+ As you can see, we're just stepping along DIR_NAME (with
DIRP) and REPOSDIRNAME (with RDIRP) respectively.
We need to be careful when we are checking out a
- module, however, since DIRNAME and REPOSDIRNAME are not
+ module, however, since DIR_NAME and REPOSDIRNAME are not
going to be the same. Since modules will not have any
slashes in their names, we should watch the output of
STRCHR to decide whether or not we should use STRCHR on
@@ -594,8 +748,8 @@ call_in_directory (pathname, func, data)
dirp = strchr (dirp, '/');
if (dirp)
{
- strncpy (dir, dirname, dirp - dirname);
- dir[dirp - dirname] = '\0';
+ strncpy (dir, dir_name, dirp - dir_name);
+ dir[dirp - dir_name] = '\0';
/* Skip the slash. */
++dirp;
if (rdirp == NULL)
@@ -622,15 +776,36 @@ call_in_directory (pathname, func, data)
STRCHR call here). */
rdirp = NULL;
- strcpy (dir, dirname);
+ strcpy (dir, dir_name);
}
if (CVS_MKDIR (dir, 0777) < 0)
{
- if (errno != EEXIST)
- error (1, errno, "cannot make directory %s", dir);
-
- /* It already existed, fine. Just keep going. */
+ /* Now, let me get this straight. In IBM C/C++
+ * under OS/2, the error string for EEXIST is:
+ *
+ * "The file already exists",
+ *
+ * and the error string for EACCESS is:
+ *
+ * "The file or directory specified is read-only".
+ *
+ * Nonetheless, mkdir() will set EACCESS if the
+ * directory *exists*, according both to the
+ * documentation and its actual behavior.
+ *
+ * I'm sure that this made sense, to someone,
+ * somewhere, sometime. Just not me, here, now.
+ */
+#ifdef EACCESS
+ if ((errno != EACCESS) && (errno != EEXIST))
+ error (1, errno, "cannot make directory %s", dir);
+#else /* ! defined(EACCESS) */
+ if ((errno != EEXIST))
+ error (1, errno, "cannot make directory %s", dir);
+#endif /* defined(EACCESS) */
+
+ /* It already existed, fine. Just keep going. */
}
else if (strcmp (command_name, "export") == 0)
/* Don't create CVSADM directories if this is export. */
@@ -680,8 +855,8 @@ call_in_directory (pathname, func, data)
} while (dirp != NULL);
free (dir);
/* Now it better work. */
- if (chdir (dirname) < 0)
- error (1, errno, "could not chdir to %s", dirname);
+ if (chdir (dir_name) < 0)
+ error (1, errno, "could not chdir to %s", dir_name);
}
if (strcmp (command_name, "export") != 0)
@@ -692,7 +867,7 @@ call_in_directory (pathname, func, data)
}
}
else
- free (dirname);
+ free (dir_name);
free (reposdirname);
(*func) (data, last_entries, short_pathname, filename);
if (reposname != NULL)
@@ -718,8 +893,8 @@ copy_a_file (data, ent_list, short_pathname, filename)
static void
handle_copy_file (args, len)
- char *args;
- int len;
+ char *args;
+ int len;
{
call_in_directory (args, copy_a_file, (char *)NULL);
}
@@ -735,8 +910,8 @@ static unsigned char stored_checksum[16];
static void
handle_checksum (args, len)
- char *args;
- int len;
+ char *args;
+ int len;
{
char *s;
char buf[3];
@@ -764,6 +939,24 @@ handle_checksum (args, len)
stored_checksum_valid = 1;
}
+static int stored_mode_valid;
+static char *stored_mode;
+
+static void handle_mode PROTO ((char *, int));
+
+static void
+handle_mode (args, len)
+ char *args;
+ int len;
+{
+ if (stored_mode_valid)
+ error (1, 0, "protocol error: duplicate Mode");
+ if (stored_mode != NULL)
+ free (stored_mode);
+ stored_mode = xstrdup (args);
+ stored_mode_valid = 1;
+}
+
/*
* If we receive a patch, but the patch program fails to apply it, we
* want to request the original file. We keep a list of files whose
@@ -808,19 +1001,68 @@ update_entries (data_arg, ent_list, short_pathname, filename)
char *entries_line;
struct update_entries_data *data = (struct update_entries_data *)data_arg;
+ char *cp;
+ char *user;
+ char *vn;
+ /* Timestamp field. Always empty according to the protocol. */
+ char *ts;
+ char *options;
+ char *tag;
+ char *date;
+ char *tag_or_date;
+ char *scratch_entries;
+ int bin;
+
read_line (&entries_line, 0);
+ /*
+ * Parse the entries line.
+ */
+ if (strcmp (command_name, "export") != 0)
+ {
+ scratch_entries = xstrdup (entries_line);
+
+ if (scratch_entries[0] != '/')
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ user = scratch_entries + 1;
+ if ((cp = strchr (user, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ vn = cp;
+ if ((cp = strchr (vn, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+
+ ts = cp;
+ if ((cp = strchr (ts, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ options = cp;
+ if ((cp = strchr (options, '/')) == NULL)
+ error (1, 0, "bad entries line `%s' from server", entries_line);
+ *cp++ = '\0';
+ tag_or_date = cp;
+
+ /* If a slash ends the tag_or_date, ignore everything after it. */
+ cp = strchr (tag_or_date, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ tag = (char *) NULL;
+ date = (char *) NULL;
+ if (*tag_or_date == 'T')
+ tag = tag_or_date + 1;
+ else if (*tag_or_date == 'D')
+ date = tag_or_date + 1;
+ }
+
if (data->contents == UPDATE_ENTRIES_UPDATE
|| data->contents == UPDATE_ENTRIES_PATCH)
{
char *size_string;
char *mode_string;
int size;
- int size_read;
- int size_left;
int fd;
char *buf;
- char *buf2;
char *temp_filename;
int use_gzip, gzip_status;
pid_t gzip_pid = 0;
@@ -843,11 +1085,29 @@ update_entries (data_arg, ent_list, short_pathname, filename)
temp_filename = xmalloc (strlen (filename) + 80);
#ifdef _POSIX_NO_TRUNC
sprintf (temp_filename, ".new.%.9s", filename);
-#else
+#else /* _POSIX_NO_TRUNC */
sprintf (temp_filename, ".new.%s", filename);
-#endif
+#endif /* _POSIX_NO_TRUNC */
buf = xmalloc (size);
- fd = open (temp_filename, O_WRONLY | O_CREAT | O_TRUNC, 0777);
+
+ /* Some systems, like OS/2 and Windows NT, end lines with CRLF
+ instead of just LF. Format translation is done in the C
+ library I/O funtions. Here we tell them whether or not to
+ convert -- if this file is marked "binary" with the RCS -kb
+ flag, then we don't want to convert, else we do (because
+ CVS assumes text files by default). */
+
+ /* Actually, I don't believe options can be NULL here, but I'm
+ not dead certain of that. */
+ if (options)
+ bin = !(strcmp (options, "-kb"));
+ else
+ bin = 0;
+
+ fd = open (temp_filename,
+ O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0),
+ 0777);
+
if (fd < 0)
error (1, errno, "writing %s", short_pathname);
@@ -856,26 +1116,12 @@ update_entries (data_arg, ent_list, short_pathname, filename)
if (size > 0)
{
- buf2 = buf;
- size_left = size;
- while ((size_read = fread (buf2, 1, size_left, from_server)) != size_left)
- {
- if (feof (from_server))
- /* FIXME: Should delete temp_filename. */
- error (1, 0, "unexpected end of file from server");
- else if (ferror (from_server))
- /* FIXME: Should delete temp_filename. */
- error (1, errno, "reading from server");
- else
- {
- /* short reads are ok if we keep trying */
- buf2 += size_read;
- size_left -= size_read;
- }
- }
- if (write (fd, buf, size) != size)
- error (1, errno, "writing %s", short_pathname);
+ read_from_server (buf, size);
+
+ if (write (fd, buf, size) != size)
+ error (1, errno, "writing %s", short_pathname);
}
+
if (close (fd) < 0)
error (1, errno, "writing %s", short_pathname);
if (gzip_pid > 0)
@@ -897,19 +1143,24 @@ update_entries (data_arg, ent_list, short_pathname, filename)
if (data->contents == UPDATE_ENTRIES_UPDATE)
{
#ifdef LINES_CRLF_TERMINATED
- if (use_gzip)
+
+ /* `bin' is non-zero iff `options' contains "-kb", meaning
+ treat this file as binary. */
+
+ if (use_gzip && (! bin))
{
convert_file (temp_filename, O_RDONLY | OPEN_BINARY,
filename, O_WRONLY | O_CREAT | O_TRUNC);
if (unlink (temp_filename) < 0)
- error (0, errno, "warning: couldn't delete %s", temp_filename);
+ error (0, errno, "warning: couldn't delete %s",
+ temp_filename);
}
else
rename_file (temp_filename, filename);
-#else
+#else /* ! LINES_CRLF_TERMINATED */
rename_file (temp_filename, filename);
-#endif
+#endif /* LINES_CRLF_TERMINATED */
}
else
{
@@ -948,7 +1199,7 @@ update_entries (data_arg, ent_list, short_pathname, filename)
rename_file (backup, filename);
/* Get rid of the patch reject file. */
- path_tmp = xmalloc (strlen (filename + 10));
+ path_tmp = xmalloc (strlen (filename) + 10);
strcpy (path_tmp, filename);
strcat (path_tmp, ".rej");
/* FIXME: should we really be silently ignoring errors? */
@@ -1037,64 +1288,29 @@ update_entries (data_arg, ent_list, short_pathname, filename)
if (status != 0)
error (0, status, "cannot change mode of %s", short_pathname);
}
+
+ free (mode_string);
free (buf);
}
+ if (stored_mode_valid)
+ change_mode (filename, stored_mode);
+ stored_mode_valid = 0;
+
/*
* Process the entries line. Do this after we've written the file,
* since we need the timestamp.
*/
if (strcmp (command_name, "export") != 0)
{
- char *cp;
- char *user;
- char *vn;
- /* Timestamp field. Always empty according to the protocol. */
- char *ts;
- char *options;
- char *tag;
- char *date;
- char *tag_or_date;
char *local_timestamp;
char *file_timestamp;
- char *scratch_entries = xstrdup (entries_line);
-
- if (scratch_entries[0] != '/')
- error (1, 0, "bad entries line `%s' from server", entries_line);
- user = scratch_entries + 1;
- if ((cp = strchr (user, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- vn = cp;
- if ((cp = strchr (vn, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
-
- ts = cp;
- if ((cp = strchr (ts, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- options = cp;
- if ((cp = strchr (options, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- tag_or_date = cp;
-
- /* If a slash ends the tag_or_date, ignore everything after it. */
- cp = strchr (tag_or_date, '/');
- if (cp != NULL)
- *cp = '\0';
- tag = (char *) NULL;
- date = (char *) NULL;
- if (*tag_or_date == 'T')
- tag = tag_or_date + 1;
- else if (*tag_or_date == 'D')
- date = tag_or_date + 1;
-
local_timestamp = data->timestamp;
if (local_timestamp == NULL || ts[0] == '+')
file_timestamp = time_stamp (filename);
+ else
+ file_timestamp = NULL;
/*
* These special version numbers signify that it is not up to
@@ -1104,10 +1320,17 @@ update_entries (data_arg, ent_list, short_pathname, filename)
if (vn[0] == '\0' || vn[0] == '0' || vn[0] == '-')
local_timestamp = "dummy timestamp";
else if (local_timestamp == NULL)
+ {
local_timestamp = file_timestamp;
+ mark_up_to_date (filename);
+ }
Register (ent_list, filename, vn, local_timestamp,
options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
+
+ if (file_timestamp)
+ free (file_timestamp);
+
free (scratch_entries);
}
free (entries_line);
@@ -1269,7 +1492,7 @@ clear_static (data, ent_list, short_pathname, filename)
char *short_pathname;
char *filename;
{
- if (unlink_file (CVSADM_ENTSTAT) < 0 && errno != ENOENT)
+ if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
}
@@ -1352,7 +1575,7 @@ clear_sticky (data, ent_list, short_pathname, filename)
char *short_pathname;
char *filename;
{
- if (unlink_file (CVSADM_TAG) < 0 && errno != ENOENT)
+ if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove %s", CVSADM_TAG);
}
@@ -1484,7 +1707,7 @@ client_isemptydir (dir)
if ((dirp = opendir (dir)) == NULL)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (0, errno, "cannot open directory %s for empty check", dir);
return (0);
}
@@ -1546,9 +1769,7 @@ process_prune_candidates ()
{
if (client_isemptydir (p->dir))
{
- run_setup ("%s -fr", RM);
- run_arg (p->dir);
- (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ unlink_file_dir (p->dir);
}
free (p->dir);
q = p->next;
@@ -1593,19 +1814,17 @@ send_repository (dir, repos, update_dir)
if (use_directory)
{
- if (fprintf (to_server, "Directory ") < 0)
- error (1, errno, "writing to server");
- if (fprintf (to_server, "%s", update_dir) < 0)
- error (1, errno, "writing to server");
-
- if (fprintf (to_server, "\n%s\n", repos)
- < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Directory ", 0);
+ send_to_server (update_dir, 0);
+ send_to_server ("\012", 1);
+ send_to_server (repos, 0);
+ send_to_server ("\012", 1);
}
else
{
- if (fprintf (to_server, "Repository %s\n", repos) < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Repository ", 0);
+ send_to_server (repos, 0);
+ send_to_server ("\012", 1);
}
if (supported_request ("Static-directory"))
{
@@ -1618,8 +1837,7 @@ send_repository (dir, repos, update_dir)
strcat (adm_name, CVSADM_ENTSTAT);
if (isreadable (adm_name))
{
- if (fprintf (to_server, "Static-directory\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Static-directory\012", 0);
}
}
if (supported_request ("Sticky"))
@@ -1633,26 +1851,23 @@ send_repository (dir, repos, update_dir)
f = fopen (adm_name, "r");
if (f == NULL)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl;
- if (fprintf (to_server, "Sticky ") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Sticky ", 0);
while (fgets (line, sizeof (line), f) != NULL)
{
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
+ send_to_server (line, 0);
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
@@ -1668,26 +1883,26 @@ send_repository (dir, repos, update_dir)
f = fopen (adm_name, "r");
if (f == NULL)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl;
- if (fprintf (to_server, "Checkin-prog ") < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("Checkin-prog ", 0);
+
while (fgets (line, sizeof (line), f) != NULL)
{
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
+ send_to_server (line, 0);
+
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
@@ -1703,26 +1918,26 @@ send_repository (dir, repos, update_dir)
f = fopen (adm_name, "r");
if (f == NULL)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl;
- if (fprintf (to_server, "Update-prog ") < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("Update-prog ", 0);
+
while (fgets (line, sizeof (line), f) != NULL)
{
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
+ send_to_server (line, 0);
+
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
@@ -1815,6 +2030,7 @@ send_a_repository (dir, repository, update_dir)
send_repository (dir, repository, update_dir);
}
+/* The "expanded" modules. */
static int modules_count;
static int modules_allocated;
static char **modules_vector;
@@ -1842,6 +2058,10 @@ handle_module_expansion (args, len)
++modules_count;
}
+/* Original, not "expanded" modules. */
+static int module_argc;
+static char **module_argv;
+
void
client_expand_modules (argc, argv, local)
int argc;
@@ -1851,11 +2071,18 @@ client_expand_modules (argc, argv, local)
int errs;
int i;
+ module_argc = argc;
+ module_argv = (char **) xmalloc ((argc + 1) * sizeof (module_argv[0]));
+ for (i = 0; i < argc; ++i)
+ module_argv[i] = xstrdup (argv[i]);
+ module_argv[argc] = NULL;
+
for (i = 0; i < argc; ++i)
send_arg (argv[i]);
send_a_repository ("", server_cvsroot, "");
- if (fprintf (to_server, "expand-modules\n") < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("expand-modules\012", 0);
+
errs = get_server_responses ();
if (last_repos != NULL)
free (last_repos);
@@ -1864,7 +2091,7 @@ client_expand_modules (argc, argv, local)
free (last_update_dir);
last_update_dir = NULL;
if (errs)
- error (errs, 0, "");
+ error (errs, 0, "cannot expand modules");
}
void
@@ -1873,13 +2100,20 @@ client_send_expansions (local)
{
int i;
char *argv[1];
+
+ /* Send the original module names. The "expanded" module name might
+ not be suitable as an argument to a co request (e.g. it might be
+ the result of a -d argument in the modules file). It might be
+ cleaner if we genuinely expanded module names, all the way to a
+ local directory and repository, but that isn't the way it works
+ now. */
+ send_file_names (module_argc, module_argv);
+
for (i = 0; i < modules_count; ++i)
{
argv[0] = modules_vector[i];
if (isfile (argv[0]))
send_files (1, argv, local, 0);
- else
- send_file_names (1, argv);
}
send_a_repository ("", server_cvsroot, "");
}
@@ -1916,9 +2150,9 @@ struct response responses[] =
{
#ifdef CLIENT_SUPPORT
#define RSP_LINE(n, f, t, s) {n, f, t, s}
-#else
+#else /* ! CLIENT_SUPPORT */
#define RSP_LINE(n, f, t, s) {n, s}
-#endif
+#endif /* CLIENT_SUPPORT */
RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
RSP_LINE("error", handle_error, response_type_error, rs_essential),
@@ -1932,6 +2166,7 @@ struct response responses[] =
RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
+ RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional),
RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
rs_optional),
@@ -1949,6 +2184,7 @@ struct response responses[] =
rs_optional),
RSP_LINE("Set-update-prog", handle_set_update_prog, response_type_normal,
rs_optional),
+ RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
rs_optional),
RSP_LINE("M", handle_m, response_type_normal, rs_essential),
@@ -1962,10 +2198,117 @@ struct response responses[] =
#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
#ifdef CLIENT_SUPPORT
+/*
+ * If LEN is 0, then send_to_server() computes string's length itself.
+ *
+ * Therefore, pass the real length when transmitting data that might
+ * contain 0's.
+ */
+void
+send_to_server (str, len)
+ char *str;
+ size_t len;
+{
+ if (len == 0)
+ len = strlen (str);
+
+#ifdef NO_SOCKET_TO_FD
+ if (use_socket_style)
+ {
+ int just_wrtn = 0;
+ int wrtn = 0;
+
+ while (wrtn < len)
+ {
+ just_wrtn = send (server_sock, str + wrtn, len - wrtn, 0);
+
+ if (just_wrtn == -1)
+ error (1, errno, "reading from server socket");
+
+ wrtn += just_wrtn;
+ if (wrtn == len)
+ break;
+ }
+ }
+ else
+#endif /* NO_SOCKET_TO_FD */
+ {
+ size_t wrtn = 0;
+
+ while (wrtn < len)
+ {
+ wrtn += fwrite (str + wrtn, 1, len - wrtn, to_server);
+
+ if (wrtn == len)
+ break;
+
+ if (ferror (to_server))
+ error (1, errno, "writing to server");
+ if (feof (to_server))
+ error (1, 0, "premature end-of-file on server");
+ }
+ }
+
+ if (to_server_logfile)
+ if (fwrite (str, 1, len, to_server_logfile) < len)
+ error (0, errno, "writing to to-server logfile");
+}
+
/*
- * Get some server responses and process them. Returns nonzero for
- * error, 0 for success.
+ * Read LEN bytes from the server or die trying.
*/
+void
+read_from_server (buf, len)
+ char *buf;
+ size_t len;
+{
+#ifdef NO_SOCKET_TO_FD
+ if (use_socket_style)
+ {
+ int just_red = 0;
+ int red = 0;
+
+ while (red < len)
+ {
+ just_red = recv (server_sock, buf + red, len - red, 0);
+
+ if (just_red == -1)
+ error (1, errno, "reading from server");
+
+ red += just_red;
+ if (red == len)
+ break;
+ }
+ }
+ else
+#endif /* NO_SOCKET_TO_FD */
+ {
+ size_t red = 0;
+
+ while (red < len)
+ {
+ red += fread (buf + red, 1, len - red, from_server);
+
+ if (red == len)
+ break;
+
+ if (ferror (from_server))
+ error (1, errno, "reading from server");
+ if (feof (from_server))
+ error (1, 0, "premature end-of-file from server");
+ }
+ }
+
+ /* Log, if that's what we're doing. */
+ if (from_server_logfile)
+ if (fwrite (buf, 1, len, from_server_logfile) < len)
+ error (0, errno, "writing to from-server logfile");
+}
+
+
+/*
+ * Get some server responses and process them. Returns nonzero for
+ * error, 0 for success. */
int
get_server_responses ()
{
@@ -2007,6 +2350,16 @@ get_server_responses ()
/* Get the responses and then close the connection. */
int server_fd = -1;
+/*
+ * Flag var; we'll set it in start_server() and not one of its
+ * callees, such as start_rsh_server(). This means that there might
+ * be a small window between the starting of the server and the
+ * setting of this var, but all the code in that window shouldn't care
+ * because it's busy checking return values to see if the server got
+ * started successfully anyway.
+ */
+int server_started = 0;
+
int
get_responses_and_close ()
{
@@ -2017,51 +2370,72 @@ get_responses_and_close ()
if (client_prune_dirs)
process_prune_candidates ();
-#ifdef HAVE_KERBEROS
- if (server_fd != -1)
- {
- if (shutdown (server_fd, 1) < 0)
- error (1, errno, "shutting down connection to %s", server_host);
- /*
- * In this case, both sides of the net connection will use the
- * same fd.
- */
- if (fileno (from_server) != fileno (to_server))
- {
- if (fclose (to_server) != 0)
- error (1, errno, "closing down connection to %s", server_host);
- }
- }
+#ifdef NO_SOCKET_TO_FD
+ if (use_socket_style)
+ {
+ if (shutdown (server_sock, 2) < 0)
+ error (1, errno, "shutting down server socket");
+ }
else
-#endif
+#endif /* NO_SOCKET_TO_FD */
+ {
+#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT)
+ if (server_fd != -1)
+ {
+ if (shutdown (server_fd, 1) < 0)
+ error (1, errno, "shutting down connection to %s", server_host);
+ /*
+ * This test will always be true because we dup the descriptor
+ */
+ if (fileno (from_server) != fileno (to_server))
+ {
+ if (fclose (to_server) != 0)
+ error (1, errno,
+ "closing down connection to %s",
+ server_host);
+ }
+ }
+ else
+#endif /* HAVE_KERBEROS || AUTH_CLIENT_SUPPORT */
+
#ifdef SHUTDOWN_SERVER
- SHUTDOWN_SERVER (fileno (to_server));
-#else
- {
- if (fclose (to_server) == EOF)
- error (1, errno, "closing connection to %s", server_host);
- }
-
- if (getc (from_server) != EOF)
- error (0, 0, "dying gasps from %s unexpected", server_host);
- else if (ferror (from_server))
- error (0, errno, "reading from %s", server_host);
-
- fclose (from_server);
-#endif
-
-#if !RSH_NOT_TRANSPARENT
+ SHUTDOWN_SERVER (fileno (to_server));
+#else /* ! SHUTDOWN_SERVER */
+ {
+
+#ifdef START_RSH_WITH_POPEN_RW
+ if (pclose (to_server) == EOF)
+#else /* ! START_RSH_WITH_POPEN_RW */
+ if (fclose (to_server) == EOF)
+#endif /* START_RSH_WITH_POPEN_RW */
+ {
+ error (1, errno, "closing connection to %s", server_host);
+ }
+ }
+
+ if (getc (from_server) != EOF)
+ error (0, 0, "dying gasps from %s unexpected", server_host);
+ else if (ferror (from_server))
+ error (0, errno, "reading from %s", server_host);
+
+ fclose (from_server);
+#endif /* SHUTDOWN_SERVER */
+ }
+
+#if ! RSH_NOT_TRANSPARENT
if (rsh_pid != -1
&& waitpid (rsh_pid, (int *) 0, 0) == -1)
- error (1, errno, "waiting for process %d", rsh_pid);
-#endif
+ error (1, errno, "waiting for process %d", rsh_pid);
+#endif /* ! RSH_NOT_TRANSPARENT */
+
+ server_started = 0;
return errs;
}
#ifndef RSH_NOT_TRANSPARENT
static void start_rsh_server PROTO((int *, int *));
-#endif
+#endif /* RSH_NOT_TRANSPARENT */
int
supported_request (name)
@@ -2073,194 +2447,434 @@ supported_request (name)
if (!strcmp (rq->name, name))
return rq->status == rq_supported;
error (1, 0, "internal error: testing support for unknown option?");
+ /* NOTREACHED */
+ return 0;
}
-/* Contact the server. */
-
+
+#ifdef AUTH_CLIENT_SUPPORT
void
-start_server ()
+init_sockaddr (name, hostname, port)
+ struct sockaddr_in *name;
+ const char *hostname;
+ unsigned short int port;
{
- int tofd, fromfd;
- char *log = getenv ("CVS_CLIENT_LOG");
-
-#if HAVE_KERBEROS
+ struct hostent *hostinfo;
+
+ memset (name, 0, sizeof (*name));
+ name->sin_family = AF_INET;
+ name->sin_port = htons (port);
+ hostinfo = gethostbyname (hostname);
+ if (hostinfo == NULL)
{
- struct hostent *hp;
- char *hname;
- const char *realm;
- const char *portenv;
- int port;
- struct sockaddr_in sin;
- int s;
- KTEXT_ST ticket;
- int status;
+ fprintf (stderr, "Unknown host %s.\n", hostname);
+ exit (EXIT_FAILURE);
+ }
+ name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
+}
- /*
- * We look up the host to give a better error message if it
- * does not exist. However, we then pass server_host to
- * krb_sendauth, rather than the canonical name, because
- * krb_sendauth is going to do its own canonicalization anyhow
- * and that lets us not worry about the static storage used by
- * gethostbyname.
- */
- hp = gethostbyname (server_host);
- if (hp == NULL)
- error (1, 0, "%s: unknown host", server_host);
- hname = xmalloc (strlen (hp->h_name) + 1);
- strcpy (hname, hp->h_name);
- realm = krb_realmofhost (hname);
+int
+auth_server_port_number ()
+{
+ return CVS_AUTH_PORT;
+}
+
- portenv = getenv ("CVS_CLIENT_PORT");
- if (portenv != NULL)
+/*
+ * Connect to the authenticating server.
+ *
+ * If VERIFY_ONLY is non-zero, then just verify that the password is
+ * correct and then shutdown the connection. In this case, the return
+ * values is 1 if the password was correct, 0 if not.
+ *
+ * If VERIFY_ONLY is 0, then really connect to the server. In this
+ * case the return value is 1 on succees, but is probably ignored. If
+ * fail to connect, then die with error.
+ */
+int
+connect_to_pserver (tofdp, fromfdp, verify_only)
+ int *tofdp, *fromfdp;
+ int verify_only;
+{
+ int sock;
+ int tofd, fromfd;
+ int port_number;
+ struct hostent *host;
+ struct sockaddr_in client_sai;
+
+ /* Does nothing if already called before now. */
+ parse_cvsroot ();
+
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+ if (sock == -1)
+ {
+ fprintf (stderr, "socket() failed\n");
+ exit (1);
+ }
+ port_number = auth_server_port_number ();
+ init_sockaddr (&client_sai, server_host, port_number);
+ if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai))
+ < 0)
+ error (1, errno, "connect to %s:%d failed", server_host,
+ CVS_AUTH_PORT);
+
+ /* Run the authorization mini-protocol before anything else. */
+ {
+ int i;
+ char ch, read_buf[PATH_MAX];
+ char *begin = NULL;
+ char *repository = server_cvsroot;
+ char *username = server_user;
+ char *password = NULL;
+ char *end = NULL;
+
+ if (verify_only)
{
- port = atoi (portenv);
- if (port <= 0)
- goto try_rsh_no_message;
- port = htons (port);
+ begin = "BEGIN VERIFICATION REQUEST\n";
+ end = "END VERIFICATION REQUEST\n";
}
else
{
- struct servent *sp;
-
- sp = getservbyname ("cvs", "tcp");
- if (sp == NULL)
- port = htons (CVS_PORT);
- else
- port = sp->s_port;
+ begin = "BEGIN AUTH REQUEST\n";
+ end = "END AUTH REQUEST\n";
}
- s = socket (AF_INET, SOCK_STREAM, 0);
- if (s < 0)
- error (1, errno, "socket");
-
- memset (&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = 0;
-
- if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0)
- error (1, errno, "bind");
-
- memcpy (&sin.sin_addr, hp->h_addr, hp->h_length);
- sin.sin_port = port;
-
- tofd = -1;
- if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ /* Get the password, probably from ~/.cvspass. */
+ password = get_cvs_password (server_user, server_host, server_cvsroot);
+
+ /* Announce that we're starting the authorization protocol. */
+ send (sock, begin, strlen (begin), 0);
+
+ /* Send the data the server needs. */
+ send (sock, repository, strlen (repository), 0);
+ send (sock, "\n", 1, 0);
+ send (sock, username, strlen (username), 0);
+ send (sock, "\n", 1, 0);
+ send (sock, password, strlen (password), 0);
+ send (sock, "\n", 1, 0);
+
+ /* Announce that we're ending the authorization protocol. */
+ send (sock, end, strlen (end), 0);
+
+ /* Paranoia. */
+ memset (password, 0, strlen (password));
+
+ /* Get ACK or NACK from the server.
+ *
+ * We could avoid this careful read-char loop by having the ACK
+ * and NACK cookies be of the same length, so we'd simply read
+ * that length and see what we got. But then there'd be Yet
+ * Another Protocol Requirement floating around, and someday
+ * someone would make a change that breaks it and spend a hellish
+ * day tracking it down. Therefore, we use "\n" to mark off the
+ * end of both ACK and NACK, and we loop, reading until "\n".
+ */
+ ch = 0;
+ memset (read_buf, 0, PATH_MAX);
+ for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++)
{
- error (0, errno, "connect");
- close (s);
+ if (recv (sock, &ch, 1, 0) < 0)
+ error (1, errno, "recv() from server %s", server_host);
+
+ read_buf[i] = ch;
}
- else
+
+ if (strcmp (read_buf, "I HATE YOU\n") == 0)
{
- struct sockaddr_in laddr;
- int laddrlen;
- MSG_DAT msg_data;
- CREDENTIALS cred;
- Key_schedule sched;
-
- laddrlen = sizeof (laddr);
- if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
- error (1, errno, "getsockname");
-
- /* We don't care about the checksum, and pass it as zero. */
- status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd",
- hname, realm, (unsigned long) 0, &msg_data,
- &cred, sched, &laddr, &sin, "KCVSV1.0");
- if (status != KSUCCESS)
+ /* Authorization not granted. */
+ if (shutdown (sock, 2) < 0)
{
- error (0, 0, "kerberos: %s", krb_get_err_text(status));
- close (s);
+ error (0, 0,
+ "authorization failed: server %s rejected access",
+ server_host);
+ error (1, errno,
+ "shutdown() failed (server %s)", server_host);
}
+
+ if (verify_only)
+ return 0;
else
+ error (1, 0,
+ "authorization failed: server %s rejected access",
+ server_host);
+ }
+ else if (strcmp (read_buf, "I LOVE YOU\n") != 0)
+ {
+ /* Unrecognized response from server. */
+ if (shutdown (sock, 2) < 0)
{
- server_fd = s;
- close_on_exec (server_fd);
- /*
- * If we do any filtering, TOFD and FROMFD will be
- * closed. So make sure they're copies of SERVER_FD,
- * and not the same fd number.
- */
- if (log)
- {
- tofd = dup (s);
- fromfd = dup (s);
- }
- else
- tofd = fromfd = s;
+ error (0, 0,
+ "unrecognized auth response from %s: %s",
+ server_host, read_buf);
+ error (1, errno, "shutdown() failed, server %s", server_host);
}
+ error (1, 0,
+ "unrecognized auth response from %s: %s",
+ server_host, read_buf);
}
+ }
- if (tofd == -1)
- {
- error (0, 0, "trying to start server using rsh");
- try_rsh_no_message:
- server_fd = -1;
+ if (verify_only)
+ {
+ if (shutdown (sock, 2) < 0)
+ error (0, errno, "shutdown() failed, server %s", server_host);
+ return 1;
+ }
+ else
+ {
+#ifdef NO_SOCKET_TO_FD
+ use_socket_style = 1;
+ server_sock = sock;
+ /* Try to break mistaken callers: */
+ *tofdp = 0;
+ *fromfdp = 0;
+#else /* ! NO_SOCKET_TO_FD */
+ server_fd = sock;
+ close_on_exec (server_fd);
+ tofd = fromfd = sock;
+ /* Hand them back to the caller. */
+ *tofdp = tofd;
+ *fromfdp = fromfd;
+#endif /* NO_SOCKET_TO_FD */
+ }
+
+ return 1;
+}
+#endif /* AUTH_CLIENT_SUPPORT */
+
+
+#if HAVE_KERBEROS
+
+/*
+ * FIXME: this function has not been changed to deal with
+ * NO_SOCKET_TO_FD (i.e., systems on which sockets cannot be converted
+ * to file descriptors. The first person to try building a kerberos
+ * client on such a system (OS/2, Windows 95, and maybe others) will
+ * have to make take care of this.
+ */
+void
+start_kerberos_server (tofdp, fromfdp)
+ int *tofdp, *fromfdp;
+{
+ int tofd, fromfd;
+
+ struct hostent *hp;
+ char *hname;
+ const char *realm;
+ const char *portenv;
+ int port;
+ struct sockaddr_in sin;
+ int s;
+ KTEXT_ST ticket;
+ int status;
+
+ /*
+ * We look up the host to give a better error message if it
+ * does not exist. However, we then pass server_host to
+ * krb_sendauth, rather than the canonical name, because
+ * krb_sendauth is going to do its own canonicalization anyhow
+ * and that lets us not worry about the static storage used by
+ * gethostbyname.
+ */
+ hp = gethostbyname (server_host);
+ if (hp == NULL)
+ error (1, 0, "%s: unknown host", server_host);
+ hname = xmalloc (strlen (hp->h_name) + 1);
+ strcpy (hname, hp->h_name);
+
+ realm = krb_realmofhost (hname);
+
+ portenv = getenv ("CVS_CLIENT_PORT");
+ if (portenv != NULL)
+ {
+ port = atoi (portenv);
+ if (port <= 0)
+ goto try_rsh_no_message;
+ port = htons (port);
+ }
+ else
+ {
+ struct servent *sp;
+
+ sp = getservbyname ("cvs", "tcp");
+ if (sp == NULL)
+ port = htons (CVS_PORT);
+ else
+ port = sp->s_port;
+ }
+
+ s = socket (AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ error (1, errno, "socket");
+
+ memset (&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = 0;
+
+ if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ error (1, errno, "bind");
+
+ memcpy (&sin.sin_addr, hp->h_addr, hp->h_length);
+ sin.sin_port = port;
+
+ tofd = -1;
+ if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ {
+ error (0, errno, "kerberos connect");
+ close (s);
+ }
+ else
+ {
+ struct sockaddr_in laddr;
+ int laddrlen;
+ MSG_DAT msg_data;
+ CREDENTIALS cred;
+ Key_schedule sched;
+
+ laddrlen = sizeof (laddr);
+ if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
+ error (1, errno, "getsockname");
+
+ /* We don't care about the checksum, and pass it as zero. */
+ status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd",
+ hname, realm, (unsigned long) 0, &msg_data,
+ &cred, sched, &laddr, &sin, "KCVSV1.0");
+ if (status != KSUCCESS)
+ {
+ error (0, 0, "kerberos: %s", krb_get_err_text(status));
+ close (s);
+ }
+ else
+ {
+ server_fd = s;
+ close_on_exec (server_fd);
+ tofd = fromfd = s;
+ }
+ }
+
+ if (tofd == -1)
+ {
+ error (0, 0, "trying to start server using rsh");
+ try_rsh_no_message:
+ server_fd = -1;
#if ! RSH_NOT_TRANSPARENT
- start_rsh_server (&tofd, &fromfd);
-#else
+ start_rsh_server (&tofd, &fromfd);
+#else /* RSH_NOT_TRANSPARENT */
#if defined (START_SERVER)
- START_SERVER (&tofd, &fromfd, getcaller (),
- server_user, server_host, server_cvsroot);
-#endif /* START_SERVER */
-#endif /* RSH_NOT_TRANSPARENT */
- }
- free (hname);
+ START_SERVER (&tofd, &fromfd, getcaller (),
+ server_user, server_host, server_cvsroot);
+#endif /* defined (START_SERVER) */
+#endif /* ! RSH_NOT_TRANSPARENT */
}
+ free (hname);
+
+ /* Give caller the values it wants. */
+ *tofdp = tofd;
+ *fromfdp = fromfd;
+}
+
+#endif /* HAVE_KERBEROS */
+
+/* Contact the server. */
+void
+start_server ()
+{
+ int tofd, fromfd;
+ char *log = getenv ("CVS_CLIENT_LOG");
+
+ /* Init these to NULL. They will be set later if logging is on. */
+ from_server_logfile = (FILE *) NULL;
+ to_server_logfile = (FILE *) NULL;
+#ifdef AUTH_CLIENT_SUPPORT
+ if (use_authenticating_server)
+ {
+ /* Toss the return value. It will die with error if anything
+ goes wrong anyway. */
+ connect_to_pserver (&tofd, &fromfd, 0);
+ }
+ else
+#endif /* AUTH_CLIENT_SUPPORT */
+ {
+#if HAVE_KERBEROS
+ start_kerberos_server (&tofd, &fromfd);
#else /* ! HAVE_KERBEROS */
+
#if ! RSH_NOT_TRANSPARENT
- start_rsh_server (&tofd, &fromfd);
-#else
+ start_rsh_server (&tofd, &fromfd);
+#else /* RSH_NOT_TRANSPARENT */
+
#if defined(START_SERVER)
- /* This is all a real mess. We now have three ways of connecting
- to the server, and there's a fourth on the horizon. We should
- clean this all up before adding the fourth. */
- START_SERVER (&tofd, &fromfd, getcaller (),
- server_user, server_host, server_cvsroot);
-#endif /* START_SERVER */
-#endif /* RSH_NOT_TRANSPARENT */
-#endif /* ! HAVE_KERBEROS */
+ START_SERVER (&tofd, &fromfd, getcaller (),
+ server_user, server_host, server_cvsroot);
+#endif /* defined(START_SERVER) */
- close_on_exec (tofd);
- close_on_exec (fromfd);
+#endif /* ! RSH_NOT_TRANSPARENT */
+#endif /* HAVE_KERBEROS */
+ }
+ /* "Hi, I'm Darlene and I'll be your server tonight..." */
+ server_started = 1;
+
+ /* Set up logfiles, if any. */
if (log)
{
int len = strlen (log);
- char *buf = xmalloc (5 + len);
+ char *buf = xmalloc (len + 5);
char *p;
- static char *teeprog[3] = { "tee" };
- teeprog[1] = buf;
strcpy (buf, log);
p = buf + len;
strcpy (p, ".in");
- tofd = filter_stream_through_program (tofd, 0, teeprog, 0);
+ to_server_logfile = open_file (buf, "w");
+ if (to_server_logfile == NULL)
+ error (0, errno, "opening to-server logfile %s", buf);
strcpy (p, ".out");
- fromfd = filter_stream_through_program (fromfd, 1, teeprog, 0);
+ from_server_logfile = open_file (buf, "w");
+ if (from_server_logfile == NULL)
+ error (0, errno, "opening from-server logfile %s", buf);
free (buf);
}
- /* Should be using binary mode on systems which have it. */
- to_server = fdopen (tofd, FOPEN_BINARY_WRITE);
- if (to_server == NULL)
- error (1, errno, "cannot fdopen %d for write", tofd);
- /* Should be using binary mode on systems which have it. */
- from_server = fdopen (fromfd, FOPEN_BINARY_READ);
- if (from_server == NULL)
- error (1, errno, "cannot fdopen %d for read", fromfd);
+#ifdef NO_SOCKET_TO_FD
+ if (! use_socket_style)
+#endif /* NO_SOCKET_TO_FD */
+ {
+ /* todo: some OS's don't need these calls... */
+ close_on_exec (tofd);
+ close_on_exec (fromfd);
+
+ /* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes
+ fdopening the same file descriptor twice, so dup it if it is the
+ same. */
+ if (tofd == fromfd)
+ {
+ fromfd = dup (tofd);
+ if (fromfd < 0)
+ error (1, errno, "cannot dup net connection");
+ }
+
+ /* These will use binary mode on systems which have it. */
+ to_server = fdopen (tofd, FOPEN_BINARY_WRITE);
+ if (to_server == NULL)
+ error (1, errno, "cannot fdopen %d for write", tofd);
+ from_server = fdopen (fromfd, FOPEN_BINARY_READ);
+ if (from_server == NULL)
+ error (1, errno, "cannot fdopen %d for read", fromfd);
+ }
/* Clear static variables. */
if (toplevel_repos != NULL)
free (toplevel_repos);
toplevel_repos = NULL;
- if (last_dirname != NULL)
- free (last_dirname);
- last_dirname = NULL;
+ if (last_dir_name != NULL)
+ free (last_dir_name);
+ last_dir_name = NULL;
if (last_repos != NULL)
free (last_repos);
last_repos = NULL;
@@ -2268,23 +2882,26 @@ start_server ()
free (last_update_dir);
last_update_dir = NULL;
stored_checksum_valid = 0;
+ stored_mode_valid = 0;
+
+ send_to_server ("Root ", 0);
+ send_to_server (server_cvsroot, 0);
+ send_to_server ("\012", 1);
- if (fprintf (to_server, "Root %s\n", server_cvsroot) < 0)
- error (1, errno, "writing to server");
{
struct response *rs;
- if (fprintf (to_server, "Valid-responses") < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("Valid-responses", 0);
+
for (rs = responses; rs->name != NULL; ++rs)
{
- if (fprintf (to_server, " %s", rs->name) < 0)
- error (1, errno, "writing to server");
+ send_to_server (" ", 0);
+ send_to_server (rs->name, 0);
}
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("\012", 1);
}
- if (fprintf (to_server, "valid-requests\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("valid-requests\012", 0);
+
if (get_server_responses ())
exit (1);
@@ -2309,8 +2926,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -n\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -n\012", 0);
}
else
error (1, 0,
@@ -2320,8 +2936,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -q\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -q\012", 0);
}
else
error (1, 0,
@@ -2331,8 +2946,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -Q\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -Q\012", 0);
}
else
error (1, 0,
@@ -2342,8 +2956,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -r\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -r\012", 0);
}
else
error (1, 0,
@@ -2353,8 +2966,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -t\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -t\012", 0);
}
else
error (1, 0,
@@ -2364,8 +2976,7 @@ start_server ()
{
if (have_global)
{
- if (fprintf (to_server, "Global_option -l\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Global_option -l\012", 0);
}
else
error (1, 0,
@@ -2376,8 +2987,12 @@ start_server ()
{
if (supported_request ("gzip-file-contents"))
{
- if (fprintf (to_server, "gzip-file-contents %d\n", gzip_level) < 0)
- error (1, 0, "writing to server");
+ char gzip_level_buf[5];
+ send_to_server ("gzip-file-contents ", 0);
+ sprintf (gzip_level_buf, "%d", gzip_level);
+ send_to_server (gzip_level_buf, 0);
+
+ send_to_server ("\012", 1);
}
else
{
@@ -2390,6 +3005,88 @@ start_server ()
#ifndef RSH_NOT_TRANSPARENT
/* Contact the server by starting it with rsh. */
+/* Right now, we have two different definitions for this function,
+ depending on whether we start the rsh server using popenRW or not.
+ This isn't ideal, and the best thing would probably be to change
+ the OS/2 port to be more like the regular Unix client (i.e., by
+ implementing piped_child)... but I'm doing something else at the
+ moment, and wish to make only one change at a time. -Karl */
+
+#ifdef START_RSH_WITH_POPEN_RW
+
+/* This is actually a crock -- it's OS/2-specific, for no one else
+ uses it. If I get time, I want to make piped_child and all the
+ other stuff in os2/run.c work right. In the meantime, this gets us
+ up and running, and that's most important. */
+
+static void
+start_rsh_server (tofdp, fromfdp)
+ int *tofdp, *fromfdp;
+{
+ int pipes[2];
+
+ /* If you're working through firewalls, you can set the
+ CVS_RSH environment variable to a script which uses rsh to
+ invoke another rsh on a proxy machine. */
+ char *cvs_rsh = getenv ("CVS_RSH");
+ char *cvs_server = getenv ("CVS_SERVER");
+ char command[PATH_MAX];
+ int i = 0;
+ /* This needs to fit "rsh", "-b", "-l", "USER", "host",
+ "cmd (w/ args)", and NULL. We leave some room to grow. */
+ char *rsh_argv[10];
+
+ if (!cvs_rsh)
+ cvs_rsh = "rsh";
+ if (!cvs_server)
+ cvs_server = "cvs";
+
+ /* If you are running a very old (Nov 3, 1994, before 1.5)
+ * version of the server, you need to make sure that your .bashrc
+ * on the server machine does not set CVSROOT to something
+ * containing a colon (or better yet, upgrade the server). */
+
+ /* The command line starts out with rsh. */
+ rsh_argv[i++] = cvs_rsh;
+
+#ifdef RSH_NEEDS_BINARY_FLAG
+ /* "-b" for binary, under OS/2. */
+ rsh_argv[i++] = "-b";
+#endif /* RSH_NEEDS_BINARY_FLAG */
+
+ /* Then we strcat more things on the end one by one. */
+ if (server_user != NULL)
+ {
+ rsh_argv[i++] = "-l";
+ rsh_argv[i++] = server_user;
+ }
+
+ rsh_argv[i++] = server_host;
+ rsh_argv[i++] = cvs_server;
+ rsh_argv[i++] = "server";
+
+ /* Mark the end of the arg list. */
+ rsh_argv[i] = (char *) NULL;
+
+ if (trace)
+ {
+ fprintf (stderr, " -> Starting server: ");
+ fprintf (stderr, "%s", command);
+ putc ('\n', stderr);
+ }
+
+ /* Do the deed. */
+ rsh_pid = popenRW (rsh_argv, pipes);
+ if (rsh_pid < 0)
+ error (1, errno, "cannot start server via rsh");
+
+ /* Give caller the file descriptors. */
+ *tofdp = pipes[0];
+ *fromfdp = pipes[1];
+}
+
+#else /* ! START_RSH_WITH_POPEN_RW */
+
static void
start_rsh_server (tofdp, fromfdp)
int *tofdp;
@@ -2455,7 +3152,10 @@ start_rsh_server (tofdp, fromfdp)
error (1, errno, "cannot start server via rsh");
}
}
-#endif
+
+#endif /* START_RSH_WITH_POPEN_RW */
+#endif /* ! RSH_NOT_TRANSPARENT */
+
/* Send an argument STRING. */
@@ -2463,28 +3163,34 @@ void
send_arg (string)
char *string;
{
+ char buf[1];
char *p = string;
- if (fprintf (to_server, "Argument ") < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("Argument ", 0);
+
while (*p)
{
if (*p == '\n')
{
- if (fprintf (to_server, "\nArgumentx ") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("\012Argumentx ", 0);
}
- else if (putc (*p, to_server) == EOF)
- error (1, errno, "writing to server");
+ else
+ {
+ buf[0] = *p;
+ send_to_server (buf, 1);
+ }
++p;
}
- if (putc ('\n', to_server) == EOF)
- error (1, errno, "writing to server");
+ send_to_server ("\012", 1);
}
-void
-send_modified (file, short_pathname)
+static void send_modified PROTO ((char *, char *, Vers_TS *));
+
+static void
+send_modified (file, short_pathname, vers)
char *file;
char *short_pathname;
+ Vers_TS *vers;
{
/* File was modified, send it. */
struct stat sb;
@@ -2492,6 +3198,7 @@ send_modified (file, short_pathname)
char *buf;
char *mode_string;
int bufsize;
+ int bin;
/* Don't think we can assume fstat exists. */
if (stat (file, &sb) < 0)
@@ -2507,7 +3214,16 @@ send_modified (file, short_pathname)
bufsize = sb.st_size;
buf = xmalloc (bufsize);
- fd = open (file, O_RDONLY);
+ /* Is the file marked as containing binary data by the "-kb" flag?
+ If so, make sure to open it in binary mode: */
+
+ if (vers && vers->options)
+ bin = !(strcmp (vers->options, "-kb"));
+ else
+ bin = 0;
+
+ fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
+
if (fd < 0)
error (1, errno, "reading %s", short_pathname);
@@ -2519,37 +3235,52 @@ send_modified (file, short_pathname)
int readsize = 8192;
#ifdef LINES_CRLF_TERMINATED
char tempfile[L_tmpnam];
-#endif
+ int converting;
+#endif /* LINES_CRLF_TERMINATED */
#ifdef LINES_CRLF_TERMINATED
- /* gzip reads and writes files without munging CRLF sequences, as it
- should, but files should be transmitted in LF form. Convert CRLF
- to LF before gzipping, on systems where this is necessary.
-
- If Windows NT supported fork, we could do this by pushing another
- filter on in front of gzip. But it doesn't. I'd have to write a
- trivial little program to do the conversion and have CVS spawn it
- off. But little executables like that always get lost.
-
- Alternatively, this cruft could go away if we switched to a gzip
- library instead of a subprocess; then we could tell gzip to open
- the file with CRLF translation enabled. */
- if (close (fd) < 0)
- error (0, errno, "warning: can't close %s", short_pathname);
-
- tmpnam (tempfile);
- convert_file (file, O_RDONLY,
- tempfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
+ /* Assume everything in a "cvs import" is text. */
+ if (vers == NULL)
+ converting = 1;
+ else
+ /* Otherwise, we convert things unless they're binary. */
+ converting = (! bin);
- /* This OPEN_BINARY doesn't make any difference, I think, because
- gzip will deal with the inherited handle as it pleases. But I
- do remember something obscure in the manuals about propagating
- the translation mode to created processes via environment
- variables, ick. */
- fd = open (tempfile, O_RDONLY | OPEN_BINARY);
- if (fd < 0)
- error (1, errno, "reading %s", short_pathname);
-#endif
+ if (converting)
+ {
+ /* gzip reads and writes files without munging CRLF
+ sequences, as it should, but files should be
+ transmitted in LF form. Convert CRLF to LF before
+ gzipping, on systems where this is necessary.
+
+ If Windows NT supported fork, we could do this by
+ pushing another filter on in front of gzip. But it
+ doesn't. I'd have to write a trivial little program to
+ do the conversion and have CVS spawn it off. But
+ little executables like that always get lost.
+
+ Alternatively, this cruft could go away if we switched
+ to a gzip library instead of a subprocess; then we
+ could tell gzip to open the file with CRLF translation
+ enabled. */
+ if (close (fd) < 0)
+ error (0, errno, "warning: can't close %s", short_pathname);
+
+ tmpnam (tempfile);
+ convert_file (file, O_RDONLY,
+ tempfile,
+ O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
+
+ /* This OPEN_BINARY doesn't make any difference, I think, because
+ gzip will deal with the inherited handle as it pleases. But I
+ do remember something obscure in the manuals about propagating
+ the translation mode to created processes via environment
+ variables, ick. */
+ fd = open (tempfile, O_RDONLY | OPEN_BINARY);
+ if (fd < 0)
+ error (1, errno, "reading %s", short_pathname);
+ }
+#endif /* LINES_CRLF_TERMINATED */
fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
while (1)
@@ -2584,15 +3315,27 @@ send_modified (file, short_pathname)
error (1, errno, "gzip exited %d", gzip_status);
#if LINES_CRLF_TERMINATED
- if (unlink (tempfile) < 0)
- error (0, errno, "warning: can't remove temp file %s", tempfile);
-#endif LINES_CRLF_TERMINATED
+ if (converting)
+ {
+ if (unlink (tempfile) < 0)
+ error (0, errno,
+ "warning: can't remove temp file %s", tempfile);
+ }
+#endif /* LINES_CRLF_TERMINATED */
- fprintf (to_server, "Modified %s\n%s\nz%lu\n", file, mode_string,
- (unsigned long) newsize);
- fwrite (buf, newsize, 1, to_server);
- if (feof (to_server) || ferror (to_server))
- error (1, errno, "writing to server");
+ {
+ char tmp[80];
+
+ send_to_server ("Modified ", 0);
+ send_to_server (file, 0);
+ send_to_server ("\012", 1);
+ send_to_server (mode_string, 0);
+ send_to_server ("\012z", 2);
+ sprintf (tmp, "%lu\n", (unsigned long) newsize);
+ send_to_server (tmp, 0);
+
+ send_to_server (buf, newsize);
+ }
}
else
{
@@ -2613,22 +3356,31 @@ send_modified (file, short_pathname)
if (close (fd) < 0)
error (0, errno, "warning: can't close %s", short_pathname);
- if (fprintf (to_server, "Modified %s\n%s\n%lu\n", file,
- mode_string, (unsigned long) newsize) < 0)
- error (1, errno, "writing to server");
+ {
+ char tmp[80];
+
+ send_to_server ("Modified ", 0);
+ send_to_server (file, 0);
+ send_to_server ("\012", 1);
+ send_to_server (mode_string, 0);
+ send_to_server ("\012", 1);
+ sprintf (tmp, "%lu\012", (unsigned long) newsize);
+ send_to_server (tmp, 0);
+ }
/*
* Note that this only ends with a newline if the file ended with
* one.
*/
if (newsize > 0)
- if (fwrite (buf, newsize, 1, to_server) != 1)
- error (1, errno, "writing to server");
+ send_to_server (buf, newsize);
}
free (buf);
free (mode_string);
}
+static int send_fileproc PROTO ((char *, char *, char *, List *, List *));
+
/* Deal with one file. */
static int
send_fileproc (file, update_dir, repository, entries, srcfiles)
@@ -2654,27 +3406,35 @@ send_fileproc (file, update_dir, repository, entries, srcfiles)
if (vers->vn_user != NULL)
{
+ char *tmp;
+
+ tmp = xmalloc (strlen (file) + strlen (vers->vn_user)
+ + strlen (vers->options) + 200);
+ sprintf (tmp, "Entry /%s/%s/%s%s/%s/",
+ file, vers->vn_user,
+ vers->ts_conflict == NULL ? "" : "+",
+ (vers->ts_conflict == NULL ? ""
+ : (vers->ts_user != NULL &&
+ strcmp (vers->ts_conflict, vers->ts_user) == 0
+ ? "="
+ : "modified")),
+ vers->options);
+
/* The Entries request. */
/* Not sure about whether this deals with -k and stuff right. */
- if (fprintf (to_server, "Entry /%s/%s/%s%s/%s/", file, vers->vn_user,
- vers->ts_conflict == NULL ? "" : "+",
- (vers->ts_conflict == NULL ? ""
- : (vers->ts_user != NULL &&
- strcmp (vers->ts_conflict, vers->ts_user) == 0
- ? "="
- : "modified")),
- vers->options) < 0)
- error (1, errno, "writing to server");
+ send_to_server (tmp, 0);
+ free (tmp);
if (vers->entdata != NULL && vers->entdata->tag)
{
- if (fprintf (to_server, "T%s", vers->entdata->tag) < 0)
- error (1, errno, "writing to server");
+ send_to_server ("T", 0);
+ send_to_server (vers->entdata->tag, 0);
}
else if (vers->entdata != NULL && vers->entdata->date)
- if (fprintf (to_server, "D%s", vers->entdata->date) < 0)
- error (1, errno, "writing to server");
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
+ {
+ send_to_server ("D", 0);
+ send_to_server (vers->entdata->date, 0);
+ }
+ send_to_server ("\012", 1);
}
if (vers->ts_user == NULL)
@@ -2687,8 +3447,9 @@ send_fileproc (file, update_dir, repository, entries, srcfiles)
if (!use_unchanged)
{
/* if the server is old, use the old request... */
- if (fprintf (to_server, "Lost %s\n", file) < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Lost ", 0);
+ send_to_server (file, 0);
+ send_to_server ("\012", 1);
/*
* Otherwise, don't do anything for missing files,
* they just happen.
@@ -2698,14 +3459,17 @@ send_fileproc (file, update_dir, repository, entries, srcfiles)
else if (vers->ts_rcs == NULL
|| strcmp (vers->ts_user, vers->ts_rcs) != 0)
{
- send_modified (file, short_pathname);
+ send_modified (file, short_pathname, vers);
}
else
{
/* Only use this request if the server supports it... */
if (use_unchanged)
- if (fprintf (to_server, "Unchanged %s\n", file) < 0)
- error (1, errno, "writing to server");
+ {
+ send_to_server ("Unchanged ", 0);
+ send_to_server (file, 0);
+ send_to_server ("\012", 1);
+ }
}
/* if this directory has an ignore list, add this file to it */
@@ -2719,10 +3483,53 @@ send_fileproc (file, update_dir, repository, entries, srcfiles)
(void) addnode (ignlist, p);
}
+ freevers_ts (&vers);
free (short_pathname);
return 0;
}
+static void send_ignproc PROTO ((char *, char *));
+
+static void
+send_ignproc (file, dir)
+ char *file;
+ char *dir;
+{
+ if (ign_inhibit_server || !supported_request ("Questionable"))
+ {
+ if (dir[0] != '\0')
+ (void) printf ("? %s/%s\n", dir, file);
+ else
+ (void) printf ("? %s\n", file);
+ }
+ else
+ {
+ send_to_server ("Questionable ", 0);
+ send_to_server (file, 0);
+ send_to_server ("\012", 1);
+ }
+}
+
+static int send_filesdoneproc PROTO ((int, char *, char *));
+
+static int
+send_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ /* if this directory has an ignore list, process it then free it */
+ if (ignlist)
+ {
+ ignore_files (ignlist, update_dir, send_ignproc);
+ dellist (&ignlist);
+ }
+
+ return (err);
+}
+
+static Dtype send_dirent_proc PROTO ((char *, char *, char *));
+
/*
* send_dirent_proc () is called back by the recursion processor before a
* sub-directory is processed for update.
@@ -2849,8 +3656,12 @@ send_file_names (argc, argv)
{
if (supported_request ("Max-dotdot"))
{
- if (fprintf (to_server, "Max-dotdot %d\n", max_level) < 0)
- error (1, errno, "writing to server");
+ char buf[10];
+ sprintf (buf, "%d", max_level);
+
+ send_to_server ("Max-dotdot ", 0);
+ send_to_server (buf, 0);
+ send_to_server ("\012", 1);
}
else
/*
@@ -2883,16 +3694,14 @@ send_files (argc, argv, local, aflag)
{
int err;
- send_file_names (argc, argv);
-
/*
* aflag controls whether the tag/date is copied into the vers_ts.
* But we don't actually use it, so I don't think it matters what we pass
* for aflag here.
*/
err = start_recursion
- (send_fileproc, update_filesdone_proc,
- send_dirent_proc, (int (*) ())NULL,
+ (send_fileproc, send_filesdoneproc,
+ send_dirent_proc, (DIRLEAVEPROC)NULL,
argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0);
if (err)
exit (1);
@@ -2949,7 +3758,7 @@ client_process_import_file (message, vfile, vtag, targc, targv, repository)
{
send_a_repository ("", repository, short_pathname);
}
- send_modified (vfile, short_pathname);
+ send_modified (vfile, short_pathname, NULL);
return 0;
}
@@ -2970,6 +3779,137 @@ client_import_done ()
send_repository ("", toplevel_repos, ".");
}
+static void
+notified_a_file (data, ent_list, short_pathname, filename)
+ char *data;
+ List *ent_list;
+ char *short_pathname;
+ char *filename;
+{
+ FILE *fp;
+ FILE *newf;
+ size_t line_len = 8192;
+ char *line = xmalloc (line_len);
+ char *cp;
+ int nread;
+ int nwritten;
+ char *p;
+
+ fp = open_file (CVSADM_NOTIFY, "r");
+ if (getline (&line, &line_len, fp) < 0)
+ {
+ error (0, errno, "cannot read %s", CVSADM_NOTIFY);
+ goto error_exit;
+ }
+ cp = strchr (line, '\t');
+ if (cp == NULL)
+ {
+ error (0, 0, "malformed %s file", CVSADM_NOTIFY);
+ goto error_exit;
+ }
+ *cp = '\0';
+ if (strcmp (filename, line + 1) != 0)
+ {
+ error (0, 0, "protocol error: notified %s, expected %s", filename,
+ line + 1);
+ }
+
+ if (getline (&line, &line_len, fp) < 0)
+ {
+ if (feof (fp))
+ {
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
+ if (unlink (CVSADM_NOTIFY) < 0)
+ error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
+ return;
+ }
+ else
+ {
+ error (0, errno, "cannot read %s", CVSADM_NOTIFY);
+ goto error_exit;
+ }
+ }
+ newf = open_file (CVSADM_NOTIFYTMP, "w");
+ if (fputs (line, newf) < 0)
+ {
+ error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
+ goto error2;
+ }
+ while ((nread = fread (line, 1, line_len, fp)) > 0)
+ {
+ p = line;
+ while ((nwritten = fwrite (p, 1, nread, newf)) > 0)
+ {
+ nread -= nwritten;
+ p += nwritten;
+ }
+ if (ferror (newf))
+ {
+ error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
+ goto error2;
+ }
+ }
+ if (ferror (fp))
+ {
+ error (0, errno, "cannot read %s", CVSADM_NOTIFY);
+ goto error2;
+ }
+ if (fclose (newf) < 0)
+ {
+ error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP);
+ goto error_exit;
+ }
+ if (fclose (fp) < 0)
+ {
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
+ return;
+ }
+
+ {
+ /* In this case, we want rename_file() to ignore noexec. */
+ int saved_noexec = noexec;
+ noexec = 0;
+ rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY);
+ noexec = saved_noexec;
+ }
+
+ return;
+ error2:
+ (void) fclose (newf);
+ error_exit:
+ (void) fclose (fp);
+}
+
+static void
+handle_notified (args, len)
+ char *args;
+ int len;
+{
+ call_in_directory (args, notified_a_file, NULL);
+}
+
+void
+client_notify (repository, update_dir, filename, notif_type, val)
+ char *repository;
+ char *update_dir;
+ char *filename;
+ int notif_type;
+ char *val;
+{
+ char buf[2];
+
+ send_a_repository ("", repository, update_dir);
+ send_to_server ("Notify ", 0);
+ send_to_server (filename, 0);
+ send_to_server ("\012", 1);
+ buf[0] = notif_type;
+ buf[1] = '\0';
+ send_to_server (buf, 1);
+ send_to_server ("\t", 1);
+ send_to_server (val, 0);
+}
+
/*
* Send an option with an argument, dealing correctly with newlines in
* the argument. If ARG is NULL, forget the whole thing.
@@ -2981,8 +3921,11 @@ option_with_arg (option, arg)
{
if (arg == NULL)
return;
- if (fprintf (to_server, "Argument %s\n", option) < 0)
- error (1, errno, "writing to server");
+
+ send_to_server ("Argument ", 0);
+ send_to_server (option, 0);
+ send_to_server ("\012", 1);
+
send_arg (arg);
}
@@ -3010,7 +3953,7 @@ client_senddate (date)
#ifndef HAVE_RCS5
/* We need to fix the timezone in this case; see Make_Date. */
abort ();
-#endif
+#endif /* HAVE_RCS5 */
sprintf (buf, "%d/%d/%d %d:%d:%d GMT", month, day, year,
hour, minute, second);
@@ -3187,4 +4130,59 @@ client_release (argc, argv)
return release (argc, argv); /* Call real code */
}
+int
+client_watch (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return watch (argc, argv); /* Call real code */
+}
+
+int
+client_watchers (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return watchers (argc, argv); /* Call real code */
+}
+
+int
+client_editors (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return editors (argc, argv); /* Call real code */
+}
+
+int
+client_edit (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return edit (argc, argv); /* Call real code */
+}
+
+int
+client_unedit (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ parse_cvsroot ();
+
+ return unedit (argc, argv); /* Call real code */
+}
+
#endif /* CLIENT_SUPPORT */
diff --git a/gnu/usr.bin/cvs/src/client.h b/gnu/usr.bin/cvs/src/client.h
index 0602900aaf5..53043857527 100644
--- a/gnu/usr.bin/cvs/src/client.h
+++ b/gnu/usr.bin/cvs/src/client.h
@@ -30,6 +30,11 @@ extern int client_admin PROTO((int argc, char **argv));
extern int client_export PROTO((int argc, char **argv));
extern int client_history PROTO((int argc, char **argv));
extern int client_release PROTO((int argc, char **argv));
+extern int client_watch PROTO((int argc, char **argv));
+extern int client_watchers PROTO((int argc, char **argv));
+extern int client_editors PROTO((int argc, char **argv));
+extern int client_edit PROTO((int argc, char **argv));
+extern int client_unedit PROTO((int argc, char **argv));
/*
* Flag variable for seeing whether common code is running as a client
@@ -37,13 +42,26 @@ extern int client_release PROTO((int argc, char **argv));
*/
extern int client_active;
+/*
+ * Flag variable for seeing whether the server has been started yet.
+ * As of this writing, only edit.c:notify_check() uses it.
+ */
+extern int server_started;
+
/* Is the -P option to checkout or update specified? */
extern int client_prune_dirs;
-/* Stream to write to the server. */
-extern FILE *to_server;
-/* Stream to read from the server. */
-extern FILE *from_server;
+#ifdef AUTH_CLIENT_SUPPORT
+extern int use_authenticating_server;
+int connect_to_pserver PROTO((int *tofdp, int* fromfdp, int verify_only));
+# ifndef CVS_AUTH_PORT
+# define CVS_AUTH_PORT 2401
+# endif /* CVS_AUTH_PORT */
+#endif /* AUTH_CLIENT_SUPPORT */
+
+/* Talking to the server. */
+void send_to_server PROTO((char *str, size_t len));
+void read_from_server PROTO((char *buf, size_t len));
/* Internal functions that handle client communication to server, etc. */
int supported_request PROTO ((char *));
@@ -65,9 +83,7 @@ send_file_names PROTO((int argc, char **argv));
/*
* Send Repository, Modified and Entry. argc and argv contain only
* the files to operate on (or empty for everything), not options.
- * local is nonzero if we should not recurse (-l option). Also sends
- * Argument lines for argc and argv, so should be called after options
- * are sent.
+ * local is nonzero if we should not recurse (-l option).
*/
void
send_files PROTO((int argc, char **argv, int local, int aflag));
@@ -159,5 +175,5 @@ extern int client_process_import_file
PROTO((char *message, char *vfile, char *vtag,
int targc, char *targv[], char *repository));
extern void client_import_done PROTO((void));
-
+extern void client_notify PROTO((char *, char *, char *, int, char *));
#endif /* CLIENT_SUPPORT */
diff --git a/gnu/usr.bin/cvs/src/cvsbug.sh b/gnu/usr.bin/cvs/src/cvsbug.sh
index b47597f93cd..bee910f990d 100644
--- a/gnu/usr.bin/cvs/src/cvsbug.sh
+++ b/gnu/usr.bin/cvs/src/cvsbug.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#! /bin/sh
# Submit a problem report to a GNATS site.
# Copyright (C) 1993 Free Software Foundation, Inc.
# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
@@ -6,8 +6,8 @@
#
# This file is part of GNU GNATS.
# Modified by Berliner for CVS.
-# Modified by Jim Blandy for CVS 1.5.
-# $CVSid: @(#)cvsbug.sh 1.2 94/10/22 $
+#
+#ident "@(#)cvs/src:$Name: $:$Id: cvsbug.sh,v 1.1.1.2 1996/01/30 00:17:47 tholo Exp $"
#
# GNU GNATS is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -43,7 +43,7 @@ GNATS_ADDR=bug-cvs@prep.ai.mit.edu
## [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && DATADIR=${GCC_EXEC_PREFIX}..
# The default release for this host.
-DEFAULT_RELEASE="post-cvs-1.5"
+DEFAULT_RELEASE="xVERSIONx"
# The default organization.
DEFAULT_ORGANIZATION="net"
@@ -337,24 +337,14 @@ X-send-pr-version: $VERSION
>Submitter-Id: $SUBMITTER
>Originator: $ORIGINATOR
>Organization:
-`
- if [ -n "$ORGANIZATION" ]; then
- echo "$ORGANIZATION"
- else
- echo " $ORGANIZATION_C" ;
- fi ;
-`
+${ORGANIZATION-$ORGANIZATION_C}
>Confidential: $CONFIDENTIAL_C
>Synopsis: $SYNOPSIS_C
>Severity: $SEVERITY_C
>Priority: $PRIORITY_C
>Category: $CATEGORY_C
>Class: $CLASS_C
->Release: `if [ -n "$DEFAULT_RELEASE" ]; then
- echo "$DEFAULT_RELEASE"
- else
- echo " $RELEASE_C"
- fi; `
+>Release: ${DEFAULT_RELEASE-$RELEASE_C}
>Environment:
$ENVIRONMENT_C
`[ -n "$SYSTEM" ] && echo System: $SYSTEM`
diff --git a/gnu/usr.bin/cvs/src/cvsrc.c b/gnu/usr.bin/cvs/src/cvsrc.c
index ec594eb5456..5882afceb26 100644
--- a/gnu/usr.bin/cvs/src/cvsrc.c
+++ b/gnu/usr.bin/cvs/src/cvsrc.c
@@ -10,6 +10,7 @@
#include "cvs.h"
+#include "getline.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)cvsrc.c 1.9 94/09/30 $";
@@ -36,10 +37,13 @@ read_cvsrc (argc, argv)
char *homeinit;
FILE *cvsrcfile;
- char linebuf [MAXLINELEN];
-
+ char *line;
+ int line_length;
+ size_t line_chars_allocated;
+
char *optstart;
+ int command_len;
int found = 0;
int i;
@@ -72,7 +76,7 @@ read_cvsrc (argc, argv)
/* if it can't be read, there's no point to continuing */
- if (access (homeinit, R_OK) != 0)
+ if (!isreadable (homeinit))
{
free (homeinit);
return;
@@ -80,15 +84,20 @@ read_cvsrc (argc, argv)
/* now scan the file until we find the line for the command in question */
+ line = NULL;
+ line_chars_allocated = 0;
+ command_len = strlen (command_name);
cvsrcfile = open_file (homeinit, "r");
- while (fgets (linebuf, MAXLINELEN, cvsrcfile))
+ while ((line_length = getline (&line, &line_chars_allocated, cvsrcfile))
+ >= 0)
{
/* skip over comment lines */
- if (linebuf[0] == '#')
+ if (line[0] == '#')
continue;
/* stop if we match the current command */
- if (!strncmp (linebuf, (*argv)[0], strlen ((*argv)[0])))
+ if (!strncmp (line, command_name, command_len)
+ && isspace (*(line + command_len)))
{
found = 1;
break;
@@ -100,8 +109,8 @@ read_cvsrc (argc, argv)
if (found)
{
/* skip over command in the options line */
- optstart = strtok(linebuf+strlen((*argv)[0]), "\t \n");
-
+ optstart = strtok (line + command_len, "\t \n");
+
do
{
new_argv [new_argc] = xstrdup (optstart);
@@ -123,6 +132,9 @@ read_cvsrc (argc, argv)
while ((optstart = strtok (NULL, "\t \n")) != NULL);
}
+ if (line != NULL)
+ free (line);
+
/* now copy the remaining arguments */
for (i=1; i < *argc; i++)
diff --git a/gnu/usr.bin/cvs/src/diff.c b/gnu/usr.bin/cvs/src/diff.c
index b7a41697082..f798f7ae567 100644
--- a/gnu/usr.bin/cvs/src/diff.c
+++ b/gnu/usr.bin/cvs/src/diff.c
@@ -183,36 +183,29 @@ diff (argc, argv)
if (diff_date2)
client_senddate (diff_date2);
+ send_file_names (argc, argv);
#if 0
-/* FIXME: We shouldn't have to send current files to diff two revs, but it
- doesn't work yet and I haven't debugged it. So send the files --
- it's slower but it works. gnu@cygnus.com Apr94 */
-
-/* Idea: often times the changed region of a file is relatively small.
- It would be cool if the client could just divide the file into 4k
- blocks or whatever and send hash values for the blocks. Send hash
- values for blocks aligned with the beginning of the file and the
- end of the file. Then the server can tell how much of the head and
- tail of the file is unchanged. Well, hash collisions will screw
- things up, but MD5 has 128 bits of hash value... */
-
+ /* FIXME: We shouldn't have to send current files to diff two
+ revs, but it doesn't work yet and I haven't debugged it.
+ So send the files -- it's slower but it works.
+ gnu@cygnus.com Apr94 */
/* Send the current files unless diffing two revs from the archive */
if (diff_rev2 == NULL && diff_date2 == NULL)
- send_files (argc, argv, local);
- else
- send_file_names (argc, argv);
-#else
- send_files (argc, argv, local, 0);
#endif
+ send_files (argc, argv, local, 0);
- if (fprintf (to_server, "diff\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("diff\012", 0);
err = get_responses_and_close ();
free (options);
return (err);
}
#endif
+ if (diff_rev1 != NULL)
+ tag_check_valid (diff_rev1, argc, argv, local, 0, "");
+ if (diff_rev2 != NULL)
+ tag_check_valid (diff_rev2, argc, argv, local, 0, "");
+
which = W_LOCAL;
if (diff_rev2 != NULL || diff_date2 != NULL)
which |= W_REPOS | W_ATTIC;
@@ -350,7 +343,7 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles)
/* Backup the current version of the file to CVS/,,filename */
sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, file);
if (unlink_file_dir (fname) < 0)
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "cannot remove %s", file);
rename_file (file, fname);
/* Copy the wrapped file to the current directory then go to work */
@@ -420,7 +413,7 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles)
if (tocvsPath)
{
if (unlink_file_dir (file) < 0)
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "cannot remove %s", file);
rename_file (fname,file);
@@ -450,6 +443,8 @@ diff_mark_errors (err)
/*
* Print a warm fuzzy message when we enter a dir
+ *
+ * Don't try to diff directories that don't exist! -- DW
*/
/* ARGSUSED */
static Dtype
@@ -459,6 +454,11 @@ diff_dirproc (dir, pos_repos, update_dir)
char *update_dir;
{
/* XXX - check for dirs we don't want to process??? */
+
+ /* YES ... for instance dirs that don't exist!!! -- DW */
+ if (!isdir (dir) )
+ return (R_SKIP_ALL);
+
if (!quiet)
error (0, 0, "Diffing %s", update_dir);
return (R_PROCESS);
@@ -522,8 +522,11 @@ diff_file_nodiff (file, repository, entries, srcfiles, vers)
diff_date1, file, 1, 0, entries, srcfiles);
if (xvers->vn_rcs == NULL)
{
- if (diff_rev1)
- error (0, 0, "tag %s is not in file %s", diff_rev1, file);
+ /* Don't gripe if it doesn't exist, just ignore! */
+ if (! isfile (file))
+ /* null statement */ ;
+ else if (diff_rev1)
+ error (0, 0, "tag %s is not in file %s", diff_rev1, file);
else
error (0, 0, "no revision for date %s in file %s",
diff_date1, file);
@@ -544,7 +547,10 @@ diff_file_nodiff (file, repository, entries, srcfiles, vers)
diff_date2, file, 1, 0, entries, srcfiles);
if (xvers->vn_rcs == NULL)
{
- if (diff_rev1)
+ /* Don't gripe if it doesn't exist, just ignore! */
+ if (! isfile (file))
+ /* null statement */ ;
+ else if (diff_rev1)
error (0, 0, "tag %s is not in file %s", diff_rev2, file);
else
error (0, 0, "no revision for date %s in file %s",
diff --git a/gnu/usr.bin/cvs/src/edit.c b/gnu/usr.bin/cvs/src/edit.c
new file mode 100644
index 00000000000..8eeecb51e45
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/edit.c
@@ -0,0 +1,1032 @@
+/* Implementation for "cvs edit", "cvs watch on", and related commands
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "cvs.h"
+#include "getline.h"
+#include "watch.h"
+#include "edit.h"
+#include "fileattr.h"
+
+static int watch_onoff PROTO ((int, char **));
+
+static int setting_default;
+static int turning_on;
+
+static int setting_tedit;
+static int setting_tunedit;
+static int setting_tcommit;
+
+static int onoff_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+onoff_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ fileattr_set (file, "_watched", turning_on ? "" : NULL);
+ return 0;
+}
+
+static int onoff_filesdoneproc PROTO ((int, char *, char *));
+
+static int
+onoff_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ if (setting_default)
+ fileattr_set (NULL, "_watched", turning_on ? "" : NULL);
+ return err;
+}
+
+static int
+watch_onoff (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int local = 0;
+ int err;
+
+ optind = 1;
+ while ((c = getopt (argc, argv, "l")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case '?':
+ default:
+ usage (watch_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ start_server ();
+
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ send_file_names (argc, argv);
+ /* FIXME: We shouldn't have to send current files, but I'm not sure
+ whether it works. So send the files --
+ it's slower but it works. */
+ send_files (argc, argv, local, 0);
+ send_to_server (turning_on ? "watch-on\012" : "watch-off\012", 0);
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ setting_default = (argc <= 0);
+
+ lock_tree_for_write (argc, argv, local, 0);
+
+ err = start_recursion (onoff_fileproc, onoff_filesdoneproc,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 0, 0);
+
+ lock_tree_cleanup ();
+ return err;
+}
+
+int
+watch_on (argc, argv)
+ int argc;
+ char **argv;
+{
+ turning_on = 1;
+ return watch_onoff (argc, argv);
+}
+
+int
+watch_off (argc, argv)
+ int argc;
+ char **argv;
+{
+ turning_on = 0;
+ return watch_onoff (argc, argv);
+}
+
+static int dummy_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+dummy_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ /* This is a pretty hideous hack, but the gist of it is that recurse.c
+ won't call notify_check unless there is a fileproc, so we can't just
+ pass NULL for fileproc. */
+ return 0;
+}
+
+static int ncheck_fileproc PROTO ((char *file, char *update_dir,
+ char *repository,
+ List * entries, List * srcfiles));
+
+/* Check for and process notifications. Local only. I think that doing
+ this as a fileproc is the only way to catch all the
+ cases (e.g. foo/bar.c), even though that means checking over and over
+ for the same CVSADM_NOTIFY file which we removed the first time we
+ processed the directory. */
+
+static int
+ncheck_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ int notif_type;
+ char *filename;
+ char *val;
+ char *cp;
+ char *watches;
+
+ FILE *fp;
+ char *line = NULL;
+ size_t line_len = 0;
+
+ /* We send notifications even if noexec. I'm not sure which behavior
+ is most sensible. */
+
+ fp = fopen (CVSADM_NOTIFY, "r");
+ if (fp == NULL)
+ {
+ if (!existence_error (errno))
+ error (0, errno, "cannot open %s", CVSADM_NOTIFY);
+ return 0;
+ }
+ while (getline (&line, &line_len, fp) > 0)
+ {
+ notif_type = line[0];
+ if (notif_type == '\0')
+ continue;
+ filename = line + 1;
+ cp = strchr (filename, '\t');
+ if (cp == NULL)
+ continue;
+ *cp++ = '\0';
+ val = cp;
+ cp = strchr (val, '\t');
+ if (cp == NULL)
+ continue;
+ *cp++ = '+';
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ continue;
+ *cp++ = '+';
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ continue;
+ *cp++ = '\0';
+ watches = cp;
+ cp = strchr (cp, '\n');
+ if (cp == NULL)
+ continue;
+ *cp = '\0';
+
+ notify_do (notif_type, filename, getcaller (), val, watches,
+ repository);
+ }
+
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", CVSADM_NOTIFY);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
+
+ if (unlink (CVSADM_NOTIFY) < 0)
+ error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
+
+ return 0;
+}
+
+static int send_notifications PROTO ((int, char **, int));
+
+/* Look through the CVSADM_NOTIFY file and process each item there
+ accordingly. */
+static int
+send_notifications (argc, argv, local)
+ int argc;
+ char **argv;
+ int local;
+{
+ int err = 0;
+
+#ifdef CLIENT_SUPPORT
+ /* OK, we've done everything which needs to happen on the client side.
+ Now we can try to contact the server; if we fail, then the
+ notifications stay in CVSADM_NOTIFY to be sent next time. */
+ if (client_active)
+ {
+ if (strcmp (command_name, "release") != 0)
+ {
+ start_server ();
+ ign_setup ();
+ }
+
+ err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 0, 0);
+
+ send_to_server ("noop\012", 0);
+ if (strcmp (command_name, "release") == 0)
+ err += get_server_responses ();
+ else
+ err += get_responses_and_close ();
+ }
+ else
+#endif
+ {
+ /* Local. */
+
+ lock_tree_for_write (argc, argv, local, 0);
+ err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 0, 0);
+ lock_tree_cleanup ();
+ }
+ return err;
+}
+
+static int edit_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+edit_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ FILE *fp;
+ time_t now;
+ char *ascnow;
+ char *basefilename;
+
+ if (noexec)
+ return 0;
+
+ fp = open_file (CVSADM_NOTIFY, "a");
+
+ (void) time (&now);
+ ascnow = asctime (gmtime (&now));
+ ascnow[24] = '\0';
+ fprintf (fp, "E%s\t%s GMT\t%s\t%s\t", file,
+ ascnow, hostname, CurDir);
+ if (setting_tedit)
+ fprintf (fp, "E");
+ if (setting_tunedit)
+ fprintf (fp, "U");
+ if (setting_tcommit)
+ fprintf (fp, "C");
+ fprintf (fp, "\n");
+
+ if (fclose (fp) < 0)
+ {
+ if (update_dir[0] == '\0')
+ error (0, errno, "cannot close %s", file);
+ else
+ error (0, errno, "cannot close %s/%s", update_dir, file);
+ }
+
+ xchmod (file, 1);
+
+ /* Now stash the file away in CVSADM so that unedit can revert even if
+ it can't communicate with the server. We stash away a writable
+ copy so that if the user removes the working file, then restores it
+ with "cvs update" (which clears _editors but does not update
+ CVSADM_BASE), then a future "cvs edit" can still win. */
+ /* Could save a system call by only calling mkdir if trying to create
+ the output file fails. But copy_file isn't set up to facilitate
+ that. */
+ if (CVS_MKDIR (CVSADM_BASE, 0777) < 0)
+ {
+ if (errno != EEXIST
+#ifdef EACCESS
+ /* OS/2; see longer comment in client.c. */
+ && errno != EACCESS
+#endif
+ )
+ error (1, errno, "cannot mkdir %s", CVSADM_BASE);
+ }
+ basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (file));
+ strcpy (basefilename, CVSADM_BASE);
+ strcat (basefilename, "/");
+ strcat (basefilename, file);
+ copy_file (file, basefilename);
+ free (basefilename);
+
+ return 0;
+}
+
+static const char *const edit_usage[] =
+{
+ "Usage: %s %s [-l] [files...]\n",
+ "-l: Local directory only, not recursive\n",
+ "-a: Specify what actions for temporary watch, one of\n",
+ " edit,unedit,commit.all,none\n",
+ NULL
+};
+
+int
+edit (argc, argv)
+ int argc;
+ char **argv;
+{
+ int local = 0;
+ int c;
+ int err;
+ int a_omitted;
+
+ if (argc == -1)
+ usage (edit_usage);
+
+ a_omitted = 1;
+ setting_tedit = 0;
+ setting_tunedit = 0;
+ setting_tcommit = 0;
+ optind = 1;
+ while ((c = getopt (argc, argv, "la:")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'a':
+ a_omitted = 0;
+ if (strcmp (optarg, "edit") == 0)
+ setting_tedit = 1;
+ else if (strcmp (optarg, "unedit") == 0)
+ setting_tunedit = 1;
+ else if (strcmp (optarg, "commit") == 0)
+ setting_tcommit = 1;
+ else if (strcmp (optarg, "all") == 0)
+ {
+ setting_tedit = 1;
+ setting_tunedit = 1;
+ setting_tcommit = 1;
+ }
+ else if (strcmp (optarg, "none") == 0)
+ {
+ setting_tedit = 0;
+ setting_tunedit = 0;
+ setting_tcommit = 0;
+ }
+ else
+ usage (edit_usage);
+ break;
+ case '?':
+ default:
+ usage (edit_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (a_omitted)
+ {
+ setting_tedit = 1;
+ setting_tunedit = 1;
+ setting_tcommit = 1;
+ }
+
+ /* No need to readlock since we aren't doing anything to the
+ repository. */
+ err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 0, 0);
+
+ err += send_notifications (argc, argv, local);
+
+ return err;
+}
+
+static int unedit_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+unedit_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ FILE *fp;
+ time_t now;
+ char *ascnow;
+ char *basefilename;
+
+ if (noexec)
+ return 0;
+
+ basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (file));
+ strcpy (basefilename, CVSADM_BASE);
+ strcat (basefilename, "/");
+ strcat (basefilename, file);
+ if (!isfile (basefilename))
+ {
+ /* This file apparently was never cvs edit'd (e.g. we are uneditting
+ a directory where only some of the files were cvs edit'd. */
+ free (basefilename);
+ return 0;
+ }
+
+ if (xcmp (file, basefilename) != 0)
+ {
+ if (update_dir[0] != '\0')
+ printf ("%s/", update_dir);
+ printf ("%s has been modified; revert changes? ", file);
+ if (!yesno ())
+ {
+ /* "no". */
+ free (basefilename);
+ return 0;
+ }
+ }
+ rename_file (basefilename, file);
+ free (basefilename);
+
+ fp = open_file (CVSADM_NOTIFY, "a");
+
+ (void) time (&now);
+ ascnow = asctime (gmtime (&now));
+ ascnow[24] = '\0';
+ fprintf (fp, "U%s\t%s GMT\t%s\t%s\t\n", file,
+ ascnow, hostname, CurDir);
+
+ if (fclose (fp) < 0)
+ {
+ if (update_dir[0] == '\0')
+ error (0, errno, "cannot close %s", file);
+ else
+ error (0, errno, "cannot close %s/%s", update_dir, file);
+ }
+
+ xchmod (file, 0);
+ return 0;
+}
+
+int
+unedit (argc, argv)
+ int argc;
+ char **argv;
+{
+ int local = 0;
+ int c;
+ int err;
+
+ if (argc == -1)
+ usage (edit_usage);
+
+ optind = 1;
+ while ((c = getopt (argc, argv, "l")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case '?':
+ default:
+ usage (edit_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* No need to readlock since we aren't doing anything to the
+ repository. */
+ err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 0, 0);
+
+ err += send_notifications (argc, argv, local);
+
+ return err;
+}
+
+void
+mark_up_to_date (file)
+ char *file;
+{
+ char *base;
+
+ /* The file is up to date, so we better get rid of an out of
+ date file in CVSADM_BASE. */
+ base = xmalloc (strlen (file) + 80);
+ strcpy (base, CVSADM_BASE);
+ strcat (base, "/");
+ strcat (base, file);
+ if (unlink_file (base) < 0 && ! existence_error (errno))
+ error (0, errno, "cannot remove %s", file);
+ free (base);
+}
+
+
+void
+editor_set (filename, editor, val)
+ char *filename;
+ char *editor;
+ char *val;
+{
+ char *edlist;
+ char *newlist;
+
+ edlist = fileattr_get0 (filename, "_editors");
+ newlist = fileattr_modify (edlist, editor, val, '>', ',');
+ if (edlist != NULL)
+ free (edlist);
+ /* If the attributes is unchanged, don't rewrite the attribute file. */
+ if (!((edlist == NULL && newlist == NULL)
+ || (edlist != NULL
+ && newlist != NULL
+ && strcmp (edlist, newlist) == 0)))
+ fileattr_set (filename, "_editors", newlist);
+ if (newlist != NULL)
+ free (newlist);
+}
+
+struct notify_proc_args {
+ /* What kind of notification, "edit", "tedit", etc. */
+ char *type;
+ /* User who is running the command which causes notification. */
+ char *who;
+ /* User to be notified. */
+ char *notifyee;
+ /* File. */
+ char *file;
+};
+
+/* Pass as a static until we get around to fixing Parse_Info to pass along
+ a void * where we can stash it. */
+static struct notify_proc_args *notify_args;
+
+static int notify_proc PROTO ((char *repository, char *filter));
+
+static int
+notify_proc (repository, filter)
+ char *repository;
+ char *filter;
+{
+ FILE *pipefp;
+ char *prog;
+ char *p;
+ char *q;
+ char *srepos;
+ struct notify_proc_args *args = notify_args;
+
+ srepos = Short_Repository (repository);
+ prog = xmalloc (strlen (filter) + strlen (args->notifyee) + 1);
+ /* Copy FILTER to PROG, replacing the first occurrence of %s with
+ the notifyee. We only allocated enough memory for one %s, and I doubt
+ there is a need for more. */
+ for (p = filter, q = prog; *p != '\0'; ++p)
+ {
+ if (p[0] == '%')
+ {
+ if (p[1] == 's')
+ {
+ strcpy (q, args->notifyee);
+ q += strlen (q);
+ strcpy (q, p + 2);
+ q += strlen (q);
+ break;
+ }
+ else
+ continue;
+ }
+ *q++ = *p;
+ }
+ *q = '\0';
+
+ pipefp = Popen (prog, "w");
+ if (pipefp == NULL)
+ {
+ error (0, errno, "cannot write entry to notify filter: %s", prog);
+ free (prog);
+ return 1;
+ }
+
+ fprintf (pipefp, "%s %s\n---\n", srepos, args->file);
+ fprintf (pipefp, "Triggered %s watch on %s\n", args->type, repository);
+ fprintf (pipefp, "By %s\n", args->who);
+
+ /* Lots more potentially useful information we could add here; see
+ logfile_write for inspiration. */
+
+ free (prog);
+ return (pclose (pipefp));
+}
+
+void
+notify_do (type, filename, who, val, watches, repository)
+ int type;
+ char *filename;
+ char *who;
+ char *val;
+ char *watches;
+ char *repository;
+{
+ static struct addremove_args blank;
+ struct addremove_args args;
+ char *watchers;
+ char *p;
+ char *endp;
+ char *nextp;
+
+ /* Initialize fields to 0, NULL, or 0.0. */
+ args = blank;
+ switch (type)
+ {
+ case 'E':
+ editor_set (filename, who, val);
+ break;
+ case 'U':
+ case 'C':
+ editor_set (filename, who, NULL);
+ break;
+ default:
+ return;
+ }
+
+ watchers = fileattr_get0 (filename, "_watchers");
+ p = watchers;
+ while (p != NULL)
+ {
+ char *q;
+ char *endq;
+ char *nextq;
+ char *notif;
+
+ endp = strchr (p, '>');
+ if (endp == NULL)
+ break;
+ nextp = strchr (p, ',');
+
+ if ((size_t)(endp - p) == strlen (who) && strncmp (who, p, endp - p) == 0)
+ {
+ /* Don't notify user of their own changes. Would perhaps
+ be better to check whether it is the same working
+ directory, not the same user, but that is hairy. */
+ p = nextp == NULL ? nextp : nextp + 1;
+ continue;
+ }
+
+ /* Now we point q at a string which looks like
+ "edit+unedit+commit,"... and walk down it. */
+ q = endp + 1;
+ notif = NULL;
+ while (q != NULL)
+ {
+ endq = strchr (q, '+');
+ if (endq == NULL || (nextp != NULL && endq > nextp))
+ {
+ if (nextp == NULL)
+ endq = q + strlen (q);
+ else
+ endq = nextp;
+ nextq = NULL;
+ }
+ else
+ nextq = endq + 1;
+
+ /* If there is a temporary and a regular watch, send a single
+ notification, for the regular watch. */
+ if (type == 'E' && endq - q == 4 && strncmp ("edit", q, 4) == 0)
+ {
+ notif = "edit";
+ }
+ else if (type == 'U'
+ && endq - q == 6 && strncmp ("unedit", q, 6) == 0)
+ {
+ notif = "unedit";
+ }
+ else if (type == 'C'
+ && endq - q == 6 && strncmp ("commit", q, 6) == 0)
+ {
+ notif = "commit";
+ }
+ else if (type == 'E'
+ && endq - q == 5 && strncmp ("tedit", q, 5) == 0)
+ {
+ if (notif == NULL)
+ notif = "temporary edit";
+ }
+ else if (type == 'U'
+ && endq - q == 7 && strncmp ("tunedit", q, 7) == 0)
+ {
+ if (notif == NULL)
+ notif = "temporary unedit";
+ }
+ else if (type == 'C'
+ && endq - q == 7 && strncmp ("tcommit", q, 7) == 0)
+ {
+ if (notif == NULL)
+ notif = "temporary commit";
+ }
+ q = nextq;
+ }
+ if (nextp != NULL)
+ ++nextp;
+
+ if (notif != NULL)
+ {
+ struct notify_proc_args args;
+ size_t len = endp - p;
+ FILE *fp;
+ char *usersname;
+ char *line = NULL;
+ size_t line_len = 0;
+
+ args.notifyee = NULL;
+ usersname = xmalloc (strlen (CVSroot)
+ + sizeof CVSROOTADM
+ + sizeof CVSROOTADM_USERS
+ + 20);
+ strcpy (usersname, CVSroot);
+ strcat (usersname, "/");
+ strcat (usersname, CVSROOTADM);
+ strcat (usersname, "/");
+ strcat (usersname, CVSROOTADM_USERS);
+ fp = fopen (usersname, "r");
+ if (fp == NULL && !existence_error (errno))
+ error (0, errno, "cannot read %s", usersname);
+ if (fp != NULL)
+ {
+ while (getline (&line, &line_len, fp) >= 0)
+ {
+ if (strncmp (line, p, len) == 0
+ && line[len] == ':')
+ {
+ char *cp;
+ args.notifyee = xstrdup (line + len + 1);
+ cp = strchr (args.notifyee, ':');
+ if (cp != NULL)
+ *cp = '\0';
+ break;
+ }
+ }
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", usersname);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", usersname);
+ }
+ free (usersname);
+ free (line);
+
+ if (args.notifyee == NULL)
+ {
+ args.notifyee = xmalloc (endp - p + 1);
+ strncpy (args.notifyee, p, endp - p);
+ args.notifyee[endp - p] = '\0';
+ }
+
+ notify_args = &args;
+ args.type = notif;
+ args.who = who;
+ args.file = filename;
+
+ (void) Parse_Info (CVSROOTADM_NOTIFY, repository, notify_proc, 1);
+ free (args.notifyee);
+ }
+
+ p = nextp;
+ }
+ if (watchers != NULL)
+ free (watchers);
+
+ switch (type)
+ {
+ case 'E':
+ if (*watches == 'E')
+ {
+ args.add_tedit = 1;
+ ++watches;
+ }
+ if (*watches == 'U')
+ {
+ args.add_tunedit = 1;
+ ++watches;
+ }
+ if (*watches == 'C')
+ {
+ args.add_tcommit = 1;
+ }
+ watch_modify_watchers (filename, &args);
+ break;
+ case 'U':
+ case 'C':
+ args.remove_temp = 1;
+ watch_modify_watchers (filename, &args);
+ break;
+ }
+}
+
+/* Check and send notifications. This is only for the client. */
+void
+notify_check (repository, update_dir)
+ char *repository;
+ char *update_dir;
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t line_len = 0;
+
+ if (! server_started)
+ /* We are in the midst of a command which is not to talk to
+ the server (e.g. the first phase of a cvs edit). Just chill
+ out, we'll catch the notifications on the flip side. */
+ return;
+
+ /* We send notifications even if noexec. I'm not sure which behavior
+ is most sensible. */
+
+ fp = fopen (CVSADM_NOTIFY, "r");
+ if (fp == NULL)
+ {
+ if (!existence_error (errno))
+ error (0, errno, "cannot open %s", CVSADM_NOTIFY);
+ return;
+ }
+ while (getline (&line, &line_len, fp) > 0)
+ {
+ int notif_type;
+ char *filename;
+ char *val;
+ char *cp;
+
+ notif_type = line[0];
+ if (notif_type == '\0')
+ continue;
+ filename = line + 1;
+ cp = strchr (filename, '\t');
+ if (cp == NULL)
+ continue;
+ *cp++ = '\0';
+ val = cp;
+
+ client_notify (repository, update_dir, filename, notif_type, val);
+ }
+
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", CVSADM_NOTIFY);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", CVSADM_NOTIFY);
+
+ /* Leave the CVSADM_NOTIFY file there, until the server tells us it
+ has dealt with it. */
+}
+
+static const char *const editors_usage[] =
+{
+ "Usage: %s %s [files...]\n",
+ NULL
+};
+
+static int editors_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+editors_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ char *them;
+ char *p;
+
+ them = fileattr_get0 (file, "_editors");
+ if (them == NULL)
+ return 0;
+
+ if (update_dir[0] == '\0')
+ printf ("%s", file);
+ else
+ printf ("%s/%s", update_dir, file);
+
+ p = them;
+ while (1)
+ {
+ putc ('\t', stdout);
+ while (*p != '>' && *p != '\0')
+ putc (*p++, stdout);
+ if (*p == '\0')
+ {
+ /* Only happens if attribute is misformed. */
+ putc ('\n', stdout);
+ break;
+ }
+ ++p;
+ putc ('\t', stdout);
+ while (1)
+ {
+ while (*p != '+' && *p != ',' && *p != '\0')
+ putc (*p++, stdout);
+ if (*p == '\0')
+ {
+ putc ('\n', stdout);
+ goto out;
+ }
+ if (*p == ',')
+ {
+ ++p;
+ break;
+ }
+ ++p;
+ putc ('\t', stdout);
+ }
+ putc ('\n', stdout);
+ }
+ out:;
+ return 0;
+}
+
+int
+editors (argc, argv)
+ int argc;
+ char **argv;
+{
+ int local = 0;
+ int c;
+
+ if (argc == -1)
+ usage (editors_usage);
+
+ optind = 1;
+ while ((c = getopt (argc, argv, "l")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case '?':
+ default:
+ usage (editors_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ start_server ();
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ send_file_names (argc, argv);
+ /* FIXME: We shouldn't have to send current files, but I'm not sure
+ whether it works. So send the files --
+ it's slower but it works. */
+ send_files (argc, argv, local, 0);
+ send_to_server ("editors\012", 0);
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ return start_recursion (editors_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
+ 0, 0);
+}
diff --git a/gnu/usr.bin/cvs/src/edit.h b/gnu/usr.bin/cvs/src/edit.h
new file mode 100644
index 00000000000..416ba79c24c
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/edit.h
@@ -0,0 +1,40 @@
+/* Interface to "cvs edit", "cvs watch on", and related features
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern int watch_on PROTO ((int argc, char **argv));
+extern int watch_off PROTO ((int argc, char **argv));
+
+/* Check to see if any notifications are sitting around in need of being
+ sent. These are the notifications stored in CVSADM_NOTIFY (edit,unedit);
+ commit calls notify_do directly. */
+extern void notify_check PROTO ((char *repository, char *update_dir));
+
+/* Issue a notification for file FILENAME. TYPE is 'E' for edit, 'U'
+ for unedit, and 'C' for commit. WHO is the user currently running.
+ For TYPE 'E', VAL is the time+host+directory data which goes in
+ _editors, and WATCHES is zero or more of E,U,C, in that order, to specify
+ what kinds of temporary watches to set. */
+extern void notify_do PROTO ((int type, char *filename, char *who,
+ char *val, char *watches, char *repository));
+
+/* Set attributes to reflect the fact that EDITOR is editing FILENAME.
+ VAL is time+host+directory, or NULL if we are to say that EDITOR is
+ *not* editing FILENAME. */
+extern void editor_set PROTO ((char *filename, char *editor, char *val));
+
+/* Take note of the fact that FILE is up to date (this munges CVS/Base;
+ processing of CVS/Entries is done separately). */
+extern void mark_up_to_date PROTO ((char *file));
diff --git a/gnu/usr.bin/cvs/src/entries.c b/gnu/usr.bin/cvs/src/entries.c
index f5b40121632..19890f2f098 100644
--- a/gnu/usr.bin/cvs/src/entries.c
+++ b/gnu/usr.bin/cvs/src/entries.c
@@ -12,20 +12,70 @@
*/
#include "cvs.h"
+#include "getline.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)entries.c 1.44 94/10/07 $";
USE(rcsid);
#endif
-static Node *AddEntryNode PROTO((List * list, char *name, char *version,
- char *timestamp, char *options, char *tag,
- char *date, char *conflict));
+static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
+
+static Entnode *fgetentent PROTO((FILE *));
+static int fputentent PROTO((FILE *, Entnode *));
static FILE *entfile;
static char *entfilename; /* for error messages */
/*
+ * Construct an Entnode
+ */
+Entnode *
+Entnode_Create(user, vn, ts, options, tag, date, ts_conflict)
+ const char *user;
+ const char *vn;
+ const char *ts;
+ const char *options;
+ const char *tag;
+ const char *date;
+ const char *ts_conflict;
+{
+ Entnode *ent;
+
+ /* Note that timestamp and options must be non-NULL */
+ ent = (Entnode *) xmalloc (sizeof (Entnode));
+ ent->user = xstrdup (user);
+ ent->version = xstrdup (vn);
+ ent->timestamp = xstrdup (ts ? ts : "");
+ ent->options = xstrdup (options ? options : "");
+ ent->tag = xstrdup (tag);
+ ent->date = xstrdup (date);
+ ent->conflict = xstrdup (ts_conflict);
+
+ return ent;
+}
+
+/*
+ * Destruct an Entnode
+ */
+void
+Entnode_Destroy (ent)
+ Entnode *ent;
+{
+ free (ent->user);
+ free (ent->version);
+ free (ent->timestamp);
+ free (ent->options);
+ if (ent->tag)
+ free (ent->tag);
+ if (ent->date)
+ free (ent->date);
+ if (ent->conflict)
+ free (ent->conflict);
+ free (ent);
+}
+
+/*
* Write out the line associated with a node of an entries file
*/
static int write_ent_proc PROTO ((Node *, void *));
@@ -34,32 +84,9 @@ write_ent_proc (node, closure)
Node *node;
void *closure;
{
- Entnode *p;
-
- p = (Entnode *) node->data;
- if (fprintf (entfile, "/%s/%s/%s", node->key, p->version,
- p->timestamp) == EOF)
- error (1, errno, "cannot write %s", entfilename);
- if (p->conflict)
- {
- if (fprintf (entfile, "+%s", p->conflict) < 0)
- error (1, errno, "cannot write %s", entfilename);
- }
- if (fprintf (entfile, "/%s/", p->options) < 0)
+ if (fputentent(entfile, (Entnode *) node->data))
error (1, errno, "cannot write %s", entfilename);
- if (p->tag)
- {
- if (fprintf (entfile, "T%s\n", p->tag) < 0)
- error (1, errno, "cannot write %s", entfilename);
- }
- else if (p->date)
- {
- if (fprintf (entfile, "D%s\n", p->date) < 0)
- error (1, errno, "cannot write %s", entfilename);
- }
- else if (fprintf (entfile, "\n") < 0)
- error (1, errno, "cannot write %s", entfilename);
return (0);
}
@@ -131,6 +158,7 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict)
char *date;
char *ts_conflict;
{
+ Entnode *entnode;
Node *node;
#ifdef SERVER_SUPPORT
@@ -156,7 +184,8 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict)
#endif
}
- node = AddEntryNode (list, fname, vn, ts, options, tag, date, ts_conflict);
+ entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict);
+ node = AddEntryNode (list, entnode);
if (!noexec)
{
@@ -188,27 +217,22 @@ freesdt (p)
free ((char *) sdtp);
}
-struct entent {
- char *user;
- char *vn;
- char *ts;
- char *options;
- char *tag;
- char *date;
- char *ts_conflict;
-};
-
-struct entent *
+static Entnode *
fgetentent(fpin)
FILE *fpin;
{
- static struct entent ent;
- static char line[MAXLINELEN];
+ Entnode *ent;
+ char *line;
+ size_t line_chars_allocated;
register char *cp;
char *user, *vn, *ts, *options;
char *tag_or_date, *tag, *date, *ts_conflict;
- while (fgets (line, sizeof (line), fpin) != NULL)
+ line = NULL;
+ line_chars_allocated = 0;
+
+ ent = NULL;
+ while (getline (&line, &line_chars_allocated, fpin) > 0)
{
if (line[0] != '/')
continue;
@@ -257,7 +281,6 @@ fgetentent(fpin)
struct stat sb;
if (strlen (ts) > 30 && stat (user, &sb) == 0)
{
- extern char *ctime ();
char *c = ctime (&sb.st_mtime);
if (!strncmp (ts + 25, c, 24))
@@ -270,18 +293,46 @@ fgetentent(fpin)
}
}
- ent.user = user;
- ent.vn = vn;
- ent.ts = ts;
- ent.options = options;
- ent.tag = tag;
- ent.date = date;
- ent.ts_conflict = ts_conflict;
+ ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict);
+ break;
+ }
+
+ free (line);
+ return ent;
+}
+
+static int
+fputentent(fp, p)
+ FILE *fp;
+ Entnode *p;
+{
+ if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
+ return 1;
+ if (p->conflict)
+ {
+ if (fprintf (fp, "+%s", p->conflict) < 0)
+ return 1;
+ }
+ if (fprintf (fp, "/%s/", p->options) < 0)
+ return 1;
- return &ent;
+ if (p->tag)
+ {
+ if (fprintf (fp, "T%s\n", p->tag) < 0)
+ return 1;
+ }
+ else if (p->date)
+ {
+ if (fprintf (fp, "D%s\n", p->date) < 0)
+ return 1;
+ }
+ else
+ {
+ if (fprintf (fp, "\n") < 0)
+ return 1;
}
- return NULL;
+ return 0;
}
@@ -293,7 +344,7 @@ Entries_Open (aflag)
int aflag;
{
List *entries;
- struct entent *ent;
+ Entnode *ent;
char *dirtag, *dirdate;
int do_rewrite = 0;
FILE *fpin;
@@ -328,31 +379,18 @@ Entries_Open (aflag)
{
while ((ent = fgetentent (fpin)) != NULL)
{
- (void) AddEntryNode (entries,
- ent->user,
- ent->vn,
- ent->ts,
- ent->options,
- ent->tag,
- ent->date,
- ent->ts_conflict);
+ (void) AddEntryNode (entries, ent);
}
fclose (fpin);
}
fpin = fopen (CVSADM_ENTLOG, "r");
- if (fpin != NULL) {
+ if (fpin != NULL)
+ {
while ((ent = fgetentent (fpin)) != NULL)
{
- (void) AddEntryNode (entries,
- ent->user,
- ent->vn,
- ent->ts,
- ent->options,
- ent->tag,
- ent->date,
- ent->ts_conflict);
+ (void) AddEntryNode (entries, ent);
}
do_rewrite = 1;
fclose (fpin);
@@ -362,8 +400,6 @@ Entries_Open (aflag)
write_entries (entries);
/* clean up and return */
- if (fpin)
- (void) fclose (fpin);
if (dirtag)
free (dirtag);
if (dirdate)
@@ -398,16 +434,7 @@ Entries_delproc (node)
Entnode *p;
p = (Entnode *) node->data;
- free (p->version);
- free (p->timestamp);
- free (p->options);
- if (p->tag)
- free (p->tag);
- if (p->date)
- free (p->date);
- if (p->conflict)
- free (p->conflict);
- free ((char *) p);
+ Entnode_Destroy(p);
}
/*
@@ -415,21 +442,14 @@ Entries_delproc (node)
* list
*/
static Node *
-AddEntryNode (list, name, version, timestamp, options, tag, date, conflict)
+AddEntryNode (list, entdata)
List *list;
- char *name;
- char *version;
- char *timestamp;
- char *options;
- char *tag;
- char *date;
- char *conflict;
+ Entnode *entdata;
{
Node *p;
- Entnode *entdata;
/* was it already there? */
- if ((p = findnode (list, name)) != NULL)
+ if ((p = findnode (list, entdata->user)) != NULL)
{
/* take it out */
delnode (p);
@@ -441,21 +461,11 @@ AddEntryNode (list, name, version, timestamp, options, tag, date, conflict)
p->delproc = Entries_delproc;
/* this one gets a key of the name for hashing */
- p->key = xstrdup (name);
-
- /* malloc the data parts and fill them in */
- p->data = xmalloc (sizeof (Entnode));
- entdata = (Entnode *) p->data;
- entdata->version = xstrdup (version);
- entdata->timestamp = xstrdup (timestamp);
- if (entdata->timestamp == NULL)
- entdata->timestamp = xstrdup ("");/* must be non-NULL */
- entdata->options = xstrdup (options);
- if (entdata->options == NULL)
- entdata->options = xstrdup ("");/* must be non-NULL */
- entdata->conflict = xstrdup (conflict);
- entdata->tag = xstrdup (tag);
- entdata->date = xstrdup (date);
+ /* FIXME This results in duplicated data --- the hash package shouldn't
+ assume that the key is dynamically allocated. The user's free proc
+ should be responsible for freeing the key. */
+ p->key = xstrdup (entdata->user);
+ p->data = (char *) entdata;
/* put the node into the list */
addnode (list, p);
@@ -499,7 +509,7 @@ WriteTag (dir, tag, date)
error (1, errno, "cannot close %s", tmp);
}
else
- if (unlink_file (tmp) < 0 && errno != ENOENT)
+ if (unlink_file (tmp) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove %s", tmp);
}
@@ -512,8 +522,6 @@ ParseTag (tagp, datep)
char **datep;
{
FILE *fp;
- char line[MAXLINELEN];
- char *cp;
if (tagp)
*tagp = (char *) NULL;
@@ -522,15 +530,24 @@ ParseTag (tagp, datep)
fp = fopen (CVSADM_TAG, "r");
if (fp)
{
- if (fgets (line, sizeof (line), fp) != NULL)
+ char *line;
+ int line_length;
+ size_t line_chars_allocated;
+
+ line = NULL;
+ line_chars_allocated = 0;
+
+ if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
{
- if ((cp = strrchr (line, '\n')) != NULL)
- *cp = '\0';
+ /* Remove any trailing newline. */
+ if (line[line_length - 1] == '\n')
+ line[--line_length] = '\0';
if (*line == 'T' && tagp)
*tagp = xstrdup (line + 1);
else if (*line == 'D' && datep)
*datep = xstrdup (line + 1);
}
(void) fclose (fp);
+ free (line);
}
}
diff --git a/gnu/usr.bin/cvs/src/error.c b/gnu/usr.bin/cvs/src/error.c
new file mode 100644
index 00000000000..beee0633090
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/error.c
@@ -0,0 +1,188 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990-1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie */
+/* Brian Berliner added support for CVS */
+
+#include "cvs.h"
+
+#ifndef lint
+static const char rcsid[] = "$CVSid: @(#)error.c 1.13 94/09/30 $";
+USE(rcsid);
+#endif /* not lint */
+
+#include <stdio.h>
+
+/* If non-zero, error will use the CVS protocol to stdout to report error
+ messages. This will only be set in the CVS server parent process;
+ most other code is run via do_cvs_command, which forks off a child
+ process and packages up its stderr in the protocol. */
+int error_use_protocol;
+
+#ifdef HAVE_VPRINTF
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else /* ! __STDC__ */
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif /* __STDC__ */
+
+#else /* ! HAVE_VPRINTF */
+
+#ifdef HAVE_DOPRNT
+#define va_alist args
+#define va_dcl int args;
+#else /* ! HAVE_DOPRNT */
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif /* HAVE_DOPRNT */
+
+#endif /* HAVE_VPRINTF */
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else /* ! STDC_HEADERS */
+#if __STDC__
+void exit(int status);
+#else /* ! __STDC__ */
+void exit ();
+#endif /* __STDC__ */
+#endif /* STDC_HEADERS */
+
+extern char *strerror ();
+
+typedef void (*fn_returning_void) PROTO((void));
+
+/* Function to call before exiting. */
+static fn_returning_void cleanup_fn;
+
+fn_returning_void
+error_set_cleanup (arg)
+ fn_returning_void arg;
+{
+ fn_returning_void retval = cleanup_fn;
+ cleanup_fn = arg;
+ return retval;
+}
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+error (int status, int errnum, const char *message, ...)
+#else
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ const char *message;
+ va_dcl
+#endif
+{
+ FILE *out = stderr;
+ extern char *program_name;
+ extern char *command_name;
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif
+
+ if (error_use_protocol)
+ {
+ out = stdout;
+ printf ("E ");
+ }
+
+ if (command_name && *command_name)
+ if (status)
+ fprintf (out, "%s [%s aborted]: ", program_name, command_name);
+ else
+ fprintf (out, "%s %s: ", program_name, command_name);
+ else
+ fprintf (out, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+ VA_START (args, message);
+ vfprintf (out, message, args);
+ va_end (args);
+#else
+#ifdef HAVE_DOPRNT
+ _doprnt (message, &args, out);
+#else
+ fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+#endif
+ if (errnum)
+ fprintf (out, ": %s", strerror (errnum));
+ putc ('\n', out);
+ fflush (out);
+ if (status)
+ {
+ if (cleanup_fn)
+ (*cleanup_fn) ();
+ exit (status);
+ }
+}
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args to the file specified by FP.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+fperror (FILE *fp, int status, int errnum, char *message, ...)
+#else
+fperror (fp, status, errnum, message, va_alist)
+ FILE *fp;
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif
+{
+ extern char *program_name;
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif
+
+ fprintf (fp, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+ VA_START (args, message);
+ vfprintf (fp, message, args);
+ va_end (args);
+#else
+#ifdef HAVE_DOPRNT
+ _doprnt (message, &args, fp);
+#else
+ fprintf (fp, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+#endif
+ if (errnum)
+ fprintf (fp, ": %s", strerror (errnum));
+ putc ('\n', fp);
+ fflush (fp);
+ if (status)
+ {
+ if (cleanup_fn)
+ (*cleanup_fn) ();
+ exit (status);
+ }
+}
diff --git a/gnu/usr.bin/cvs/src/error.h b/gnu/usr.bin/cvs/src/error.h
new file mode 100644
index 00000000000..7d4f5353797
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/error.h
@@ -0,0 +1,47 @@
+/* error.h -- declaration for error-reporting function
+ Copyright (C) 1995 Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _error_h_
+#define _error_h_
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# endif
+/* The __-protected variants of `format' and `printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __format__ format
+# define __printf__ printf
+# endif
+#endif
+
+#if __STDC__
+void error (int, int, const char *, ...) \
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+#else
+void error ();
+#endif
+
+/* If non-zero, error will use the CVS protocol to report error
+ messages. This will only be set in the CVS server parent process;
+ most other code is run via do_cvs_command, which forks off a child
+ process and packages up its stderr in the protocol. */
+extern int error_use_protocol;
+
+#endif /* _error_h_ */
diff --git a/gnu/usr.bin/cvs/src/expand_path.c b/gnu/usr.bin/cvs/src/expand_path.c
new file mode 100644
index 00000000000..f63ddff0035
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/expand_path.c
@@ -0,0 +1,134 @@
+/* expand_path.c -- expand environmental variables in passed in string
+ *
+ * The main routine is expand_path(), it is the routine that handles
+ * the '~' character in four forms:
+ * ~name
+ * ~name/
+ * ~/
+ * ~
+ * and handles environment variables contained within the pathname
+ * which are defined by:
+ * ${var_name} (var_name is the name of the environ variable)
+ * $var_name (var_name ends w/ non-alphanumeric char other than '_')
+ */
+
+#include "cvs.h"
+#include <sys/types.h>
+
+static char *expand_variable PROTO((char *env));
+extern char *xmalloc ();
+extern void free ();
+
+/* char *expand_pathname(char *name)
+ *
+ * This routine will expand the pathname to account for ~
+ * and $ characters as described above. If an error occurs, NULL
+ * is returned.
+ * Will only expand Built in CVS variables all others are ignored.
+ */
+char *
+expand_path (name)
+ char *name;
+{
+ char *s;
+ char *d;
+ char mybuf[PATH_MAX];
+ char buf[PATH_MAX];
+ char *result;
+ s = name;
+ d = mybuf;
+ while ((*d++ = *s))
+ if (*s++ == '$')
+ {
+ char *p = d;
+ char *e;
+ int flag = (*s == '{');
+
+ for (; (*d++ = *s); s++)
+ if (flag ? *s =='}' :
+ isalnum (*s) == 0 && *s!='_' )
+ break;
+ *--d = 0;
+ e = expand_variable (&p[flag]);
+
+ if (e)
+ {
+ for (d = &p[-1]; (*d++ = *e++);)
+ ;
+ --d;
+ if (flag && *s)
+ s++;
+ }
+ else
+ return NULL; /* no env variable */
+ }
+ *d = 0;
+ s = mybuf;
+ d = buf;
+ /* If you don't want ~username ~/ to be expanded simply remove
+ * This entire if statement including the else portion
+ */
+ if (*s++ == '~')
+ {
+ char *t;
+ char *p=s;
+ if (*s=='/' || *s==0)
+ t = getenv ("HOME");
+ else
+ {
+ struct passwd *ps;
+ for (; *p!='/' && *p; p++)
+ ;
+ *p = 0;
+ ps = getpwnam (s);
+ if (ps == 0)
+ return NULL; /* no such user */
+ t = ps->pw_dir;
+ }
+ while ((*d++ = *t++))
+ ;
+ --d;
+ if (*p == 0)
+ *p = '/'; /* always add / */
+ s=p;
+ }
+ else
+ --s;
+ /* Kill up to here */
+ while ((*d++ = *s++))
+ ;
+ *d=0;
+ result = xmalloc (sizeof(char) * strlen(buf)+1);
+ strcpy (result, buf);
+ return result;
+}
+static char *
+expand_variable (name)
+ char *name;
+{
+ /* There is nothing expanding this function to allow it
+ * to read a file in the $CVSROOT/CVSROOT directory that
+ * says which environmental variables could be expanded
+ * or just say everything is fair game to be expanded
+ */
+ if ( strcmp (name, CVSROOT_ENV) == 0 )
+ return CVSroot;
+ else
+ if ( strcmp (name, RCSBIN_ENV) == 0 )
+ return Rcsbin;
+ else
+ if ( strcmp (name, EDITOR1_ENV) == 0 )
+ return Editor;
+ else
+ if ( strcmp (name, EDITOR2_ENV) == 0 )
+ return Editor;
+ else
+ if ( strcmp (name, EDITOR3_ENV) == 0 )
+ return Editor;
+ else
+ return NULL;
+ /* The code here could also just
+ * return whatever getenv would
+ * return.
+ */
+}
diff --git a/gnu/usr.bin/cvs/src/fileattr.c b/gnu/usr.bin/cvs/src/fileattr.c
new file mode 100644
index 00000000000..44987006bc8
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/fileattr.c
@@ -0,0 +1,508 @@
+/* Implementation for file attribute munging features.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "cvs.h"
+#include "getline.h"
+#include "fileattr.h"
+#include <assert.h>
+
+static void fileattr_read PROTO ((void));
+static int writeattr_proc PROTO ((Node *, void *));
+
+/* Where to look for CVSREP_FILEATTR. */
+static char *fileattr_stored_repos;
+
+/* The in-memory attributes. */
+static List *attrlist;
+static char *fileattr_default_attrs;
+/* We have already tried to read attributes and failed in this directory
+ (for example, there is no CVSREP_FILEATTR file). */
+static int attr_read_attempted;
+
+/* Have the in-memory attributes been modified since we read them? */
+static int attrs_modified;
+
+/* Note that if noone calls fileattr_get, this is very cheap. No stat(),
+ no open(), no nothing. */
+void
+fileattr_startdir (repos)
+ char *repos;
+{
+ assert (fileattr_stored_repos == NULL);
+ fileattr_stored_repos = xstrdup (repos);
+ assert (attrlist == NULL);
+ attr_read_attempted = 0;
+}
+
+static void
+fileattr_delproc (node)
+ Node *node;
+{
+ free (node->data);
+}
+
+/* Read all the attributes for the current directory into memory. */
+static void
+fileattr_read ()
+{
+ char *fname;
+ FILE *fp;
+ char *line = NULL;
+ size_t line_len = 0;
+
+ /* If there are no attributes, don't waste time repeatedly looking
+ for the CVSREP_FILEATTR file. */
+ if (attr_read_attempted)
+ return;
+
+ /* If NULL was passed to fileattr_startdir, then it isn't kosher to look
+ at attributes. */
+ assert (fileattr_stored_repos != NULL);
+
+ fname = xmalloc (strlen (fileattr_stored_repos)
+ + 1
+ + sizeof (CVSREP_FILEATTR)
+ + 1);
+
+ strcpy (fname, fileattr_stored_repos);
+ strcat (fname, "/");
+ strcat (fname, CVSREP_FILEATTR);
+
+ attr_read_attempted = 1;
+ fp = fopen (fname, "r");
+ if (fp == NULL)
+ {
+ if (!existence_error (errno))
+ error (0, errno, "cannot read %s", fname);
+ free (fname);
+ return;
+ }
+ attrlist = getlist ();
+ while (1) {
+ int nread;
+ nread = getline (&line, &line_len, fp);
+ if (nread < 0)
+ break;
+ /* Remove trailing newline. */
+ line[nread - 1] = '\0';
+ if (line[0] == 'F')
+ {
+ char *p;
+ Node *newnode;
+
+ p = strchr (line, '\t');
+ *p++ = '\0';
+ newnode = getnode ();
+ newnode->type = FILEATTR;
+ newnode->delproc = fileattr_delproc;
+ newnode->key = xstrdup (line + 1);
+ newnode->data = xstrdup (p);
+ addnode (attrlist, newnode);
+ }
+ else if (line[0] == 'D')
+ {
+ char *p;
+ /* Currently nothing to skip here, but for future expansion,
+ ignore anything located here. */
+ p = strchr (line, '\t');
+ ++p;
+ fileattr_default_attrs = xstrdup (p);
+ }
+ /* else just ignore the line, for future expansion. */
+ }
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", fname);
+ if (line != NULL)
+ free (line);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", fname);
+ attrs_modified = 0;
+ free (fname);
+}
+
+char *
+fileattr_get (filename, attrname)
+ char *filename;
+ char *attrname;
+{
+ Node *node;
+ size_t attrname_len = strlen (attrname);
+ char *p;
+
+ if (attrlist == NULL)
+ fileattr_read ();
+ if (attrlist == NULL)
+ /* Either nothing has any attributes, or fileattr_read already printed
+ an error message. */
+ return NULL;
+
+ node = findnode (attrlist, filename);
+ if (node == NULL)
+ /* A file not mentioned has no attributes. */
+ return NULL;
+ p = node->data;
+ while (1) {
+ if (strncmp (attrname, p, attrname_len) == 0
+ && p[attrname_len] == '=')
+ {
+ /* Found it. */
+ return p + attrname_len + 1;
+ }
+ p = strchr (p, ';');
+ if (p == NULL)
+ break;
+ ++p;
+ }
+ /* The file doesn't have this attribute. */
+ return NULL;
+}
+
+char *
+fileattr_get0 (filename, attrname)
+ char *filename;
+ char *attrname;
+{
+ char *cp;
+ char *cpend;
+ char *retval;
+
+ cp = fileattr_get (filename, attrname);
+ if (cp == NULL)
+ return NULL;
+ cpend = strchr (cp, ';');
+ if (cpend == NULL)
+ cpend = cp + strlen (cp);
+ retval = xmalloc (cpend - cp + 1);
+ strncpy (retval, cp, cpend - cp);
+ retval[cpend - cp] = '\0';
+ return retval;
+}
+
+char *
+fileattr_modify (list, attrname, attrval, namevalsep, entsep)
+ char *list;
+ char *attrname;
+ char *attrval;
+ int namevalsep;
+ int entsep;
+{
+ char *retval;
+ char *rp;
+ size_t attrname_len = strlen (attrname);
+
+ /* Portion of list before the attribute to be replaced. */
+ char *pre;
+ char *preend;
+ /* Portion of list after the attribute to be replaced. */
+ char *post;
+
+ char *p;
+ char *p2;
+
+ p = list;
+ pre = list;
+ preend = NULL;
+ /* post is NULL unless set otherwise. */
+ post = NULL;
+ p2 = NULL;
+ if (list != NULL)
+ {
+ while (1) {
+ p2 = strchr (p, entsep);
+ if (p2 == NULL)
+ {
+ p2 = p + strlen (p);
+ if (preend == NULL)
+ preend = p2;
+ }
+ else
+ ++p2;
+ if (strncmp (attrname, p, attrname_len) == 0
+ && p[attrname_len] == namevalsep)
+ {
+ /* Found it. */
+ preend = p;
+ if (preend > list)
+ /* Don't include the preceding entsep. */
+ --preend;
+
+ post = p2;
+ }
+ if (p2[0] == '\0')
+ break;
+ p = p2;
+ }
+ }
+ if (post == NULL)
+ post = p2;
+
+ if (preend == pre && attrval == NULL && post == p2)
+ return NULL;
+
+ retval = xmalloc ((preend - pre)
+ + 1
+ + (attrval == NULL ? 0 : (attrname_len + 1
+ + strlen (attrval)))
+ + 1
+ + (p2 - post)
+ + 1);
+ if (preend != pre)
+ {
+ strncpy (retval, pre, preend - pre);
+ rp = retval + (preend - pre);
+ if (attrval != NULL)
+ *rp++ = entsep;
+ *rp = '\0';
+ }
+ else
+ retval[0] = '\0';
+ if (attrval != NULL)
+ {
+ strcat (retval, attrname);
+ rp = retval + strlen (retval);
+ *rp++ = namevalsep;
+ strcpy (rp, attrval);
+ }
+ if (post != p2)
+ {
+ rp = retval + strlen (retval);
+ if (preend != pre || attrval != NULL)
+ *rp++ = entsep;
+ strncpy (rp, post, p2 - post);
+ rp += p2 - post;
+ *rp = '\0';
+ }
+ return retval;
+}
+
+void
+fileattr_set (filename, attrname, attrval)
+ char *filename;
+ char *attrname;
+ char *attrval;
+{
+ Node *node;
+ char *p;
+
+ attrs_modified = 1;
+
+ if (filename == NULL)
+ {
+ p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
+ '=', ';');
+ if (fileattr_default_attrs != NULL)
+ free (fileattr_default_attrs);
+ fileattr_default_attrs = p;
+ return;
+ }
+ if (attrlist == NULL)
+ fileattr_read ();
+ if (attrlist == NULL)
+ {
+ /* Not sure this is a graceful way to handle things
+ in the case where fileattr_read was unable to read the file. */
+ /* No attributes existed previously. */
+ attrlist = getlist ();
+ }
+
+ node = findnode (attrlist, filename);
+ if (node == NULL)
+ {
+ if (attrval == NULL)
+ /* Attempt to remove an attribute which wasn't there. */
+ return;
+
+ /* First attribute for this file. */
+ node = getnode ();
+ node->type = FILEATTR;
+ node->delproc = fileattr_delproc;
+ node->key = xstrdup (filename);
+ node->data = xmalloc (strlen (attrname) + 1 + strlen (attrval) + 1);
+ strcpy (node->data, attrname);
+ strcat (node->data, "=");
+ strcat (node->data, attrval);
+ addnode (attrlist, node);
+ }
+
+ p = fileattr_modify (node->data, attrname, attrval, '=', ';');
+ free (node->data);
+ if (p == NULL)
+ delnode (node);
+ else
+ node->data = p;
+}
+
+void
+fileattr_newfile (filename)
+ char *filename;
+{
+ Node *node;
+
+ if (attrlist == NULL)
+ fileattr_read ();
+
+ if (fileattr_default_attrs == NULL)
+ return;
+
+ if (attrlist == NULL)
+ {
+ /* Not sure this is a graceful way to handle things
+ in the case where fileattr_read was unable to read the file. */
+ /* No attributes existed previously. */
+ attrlist = getlist ();
+ }
+
+ node = getnode ();
+ node->type = FILEATTR;
+ node->delproc = fileattr_delproc;
+ node->key = xstrdup (filename);
+ node->data = xstrdup (fileattr_default_attrs);
+ addnode (attrlist, node);
+ attrs_modified = 1;
+}
+
+static int
+writeattr_proc (node, data)
+ Node *node;
+ void *data;
+{
+ FILE *fp = (FILE *)data;
+ fputs ("F", fp);
+ fputs (node->key, fp);
+ fputs ("\t", fp);
+ fputs (node->data, fp);
+ fputs ("\n", fp);
+ return 0;
+}
+
+void
+fileattr_write ()
+{
+ FILE *fp;
+ char *fname;
+ mode_t omask;
+
+ if (!attrs_modified)
+ return;
+
+ if (noexec)
+ return;
+
+ /* If NULL was passed to fileattr_startdir, then it isn't kosher to set
+ attributes. */
+ assert (fileattr_stored_repos != NULL);
+
+ fname = xmalloc (strlen (fileattr_stored_repos)
+ + 1
+ + sizeof (CVSREP_FILEATTR)
+ + 1);
+
+ strcpy (fname, fileattr_stored_repos);
+ strcat (fname, "/");
+ strcat (fname, CVSREP_FILEATTR);
+
+ if (list_isempty (attrlist) && fileattr_default_attrs == NULL)
+ {
+ /* There are no attributes. */
+ if (unlink_file (fname) < 0)
+ {
+ if (!existence_error (errno))
+ {
+ error (0, errno, "cannot remove %s", fname);
+ }
+ }
+
+ /* Now remove CVSREP directory, if empty. The main reason we bother
+ is that CVS 1.6 and earlier will choke if a CVSREP directory
+ exists, so provide the user a graceful way to remove it. */
+ strcpy (fname, fileattr_stored_repos);
+ strcat (fname, "/");
+ strcat (fname, CVSREP);
+ if (rmdir (fname) < 0)
+ {
+ if (errno != ENOTEMPTY
+
+ /* Don't know why we would be here if there is no CVSREP
+ directory, but it seemed to be happening anyway, so
+ check for it. */
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", fname);
+ }
+
+ free (fname);
+ return;
+ }
+
+ omask = umask (cvsumask);
+ fp = fopen (fname, "w");
+ if (fp == NULL)
+ {
+ if (existence_error (errno))
+ {
+ /* Maybe the CVSREP directory doesn't exist. Try creating it. */
+ char *repname;
+
+ repname = xmalloc (strlen (fileattr_stored_repos)
+ + 1
+ + sizeof (CVSREP)
+ + 1);
+ strcpy (repname, fileattr_stored_repos);
+ strcat (repname, "/");
+ strcat (repname, CVSREP);
+
+ if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
+ {
+ error (0, errno, "cannot make directory %s", repname);
+ (void) umask (omask);
+ free (repname);
+ return;
+ }
+ free (repname);
+
+ fp = fopen (fname, "w");
+ }
+ if (fp == NULL)
+ {
+ error (0, errno, "cannot write %s", fname);
+ (void) umask (omask);
+ return;
+ }
+ }
+ (void) umask (omask);
+ walklist (attrlist, writeattr_proc, fp);
+ if (fileattr_default_attrs != NULL)
+ {
+ fputs ("D\t", fp);
+ fputs (fileattr_default_attrs, fp);
+ fputs ("\n", fp);
+ }
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", fname);
+ attrs_modified = 0;
+ free (fname);
+}
+
+void
+fileattr_free ()
+{
+ dellist (&attrlist);
+ if (fileattr_stored_repos != NULL)
+ free (fileattr_stored_repos);
+ fileattr_stored_repos = NULL;
+ if (fileattr_default_attrs != NULL)
+ free (fileattr_default_attrs);
+ fileattr_default_attrs = NULL;
+}
diff --git a/gnu/usr.bin/cvs/src/fileattr.h b/gnu/usr.bin/cvs/src/fileattr.h
new file mode 100644
index 00000000000..1a83ab43de5
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/fileattr.h
@@ -0,0 +1,123 @@
+/* Declarations for file attribute munging features.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef FILEATTR_H
+
+/* File containing per-file attributes. Format is a series of entries:
+
+ ENT-TYPE FILENAME <tab> ATTRNAME = ATTRVAL
+ {; ATTRNAME = ATTRVAL} <linefeed>
+
+ ENT-TYPE is 'F' for a file, in which case the entry specifies the
+ attributes for that file.
+
+ ENT-TYPE is 'D', and FILENAME empty, to specify default attributes
+ to be used for newly added files.
+
+ There is currently no way of quoting tabs or linefeeds in the
+ filename, '=' in ATTRNAME, ';' in ATTRVAL, etc. I'm not sure
+ whether I think we need one. Note: the current implementation also
+ doesn't handle '\0' in any of the fields.
+
+ By convention, ATTRNAME starting with '_' is for an attribute given
+ special meaning by CVS; other ATTRNAMEs are for user-defined attributes
+ (or will be, once we add commands to manipulate user-defined attributes).
+
+ Builtin attributes:
+
+ _watched: Present means the file is watched and should be checked out
+ read-only.
+
+ _watchers: Users with watches for this file. Value is
+ WATCHER > TYPE { , WATCHER > TYPE }
+ where WATCHER is a username, and TYPE is edit,unedit,commit separated by
+ + (or nothing if none; there is no "none" or "all" keyword).
+
+ _editors: Users editing this file. Value is
+ EDITOR > VAL { , EDITOR > VAL }
+ where EDITOR is a username, and VAL is TIME+HOSTNAME+PATHNAME, where
+ TIME is when the "cvs edit" command happened,
+ and HOSTNAME and PATHNAME are for the working directory. */
+
+#define CVSREP_FILEATTR "CVS/fileattr"
+
+/* Prepare for a new directory with repository REPOS. If REPOS is NULL,
+ then prepare for a "non-directory"; the caller can call fileattr_write
+ and fileattr_free, but must not call fileattr_get or fileattr_set. */
+extern void fileattr_startdir PROTO ((char *repos));
+
+/* Get the attribute ATTRNAME for file FILENAME. The return value
+ points into memory managed by the fileattr_* routines, should not
+ be altered by the caller, and is only good until the next call to
+ fileattr_clear or fileattr_set. It points to the value, terminated
+ by '\0' or ';'. Return NULL if said file lacks said attribute. */
+extern char *fileattr_get PROTO ((char *filename, char *attrname));
+
+/* Like fileattr_get, but return a pointer to a newly malloc'd string
+ terminated by '\0' (or NULL if said file lacks said attribute). */
+extern char *fileattr_get0 PROTO ((char *filename, char *attrname));
+
+/* This is just a string manipulation function; it does not manipulate
+ file attributes as such.
+
+ LIST is in the format
+
+ ATTRNAME NAMEVALSEP ATTRVAL {ENTSEP ATTRNAME NAMEVALSEP ATTRVAL}
+
+ And we want to put in an attribute with name NAME and value VAL,
+ replacing the already-present attribute with name NAME if there is
+ one. Or if VAL is NULL remove attribute NAME. Return a new
+ malloc'd list; don't muck with the one passed in. If we are removing
+ the last attribute return NULL. LIST can be NULL to mean that we
+ started out without any attributes.
+
+ Examples:
+
+ fileattr_modify ("abc=def", "xxx", "val", '=', ';')) => "abc=def;xxx=val"
+ fileattr_modify ("abc=def", "abc", "val", '=', ';')) => "abc=val"
+ fileattr_modify ("abc=v1;def=v2", "abc", "val", '=', ';'))
+ => "abc=val;def=v2"
+ fileattr_modify ("abc=v1;def=v2", "def", "val", '=', ';'))
+ => "abc=v1;def=val"
+ fileattr_modify ("abc=v1;def=v2", "xxx", "val"))
+ => "abc=v1;def=v2;xxx=val"
+ fileattr_modify ("abc=v1;def=v2;ghi=v3", "def", "val", '=', ';'))
+ => "abc=v1;def=val;ghi=v3"
+*/
+
+extern char *fileattr_modify PROTO ((char *list, char *attrname,
+ char *attrval, int namevalsep,
+ int entsep));
+
+/* Set attribute ATTRNAME for file FILENAME to ATTRVAL. If ATTRVAL is NULL,
+ the attribute is removed. Changes are not written to disk until the
+ next call to fileattr_write. If FILENAME is NULL, set attributes for
+ files created in the future. If ATTRVAL is NULL, remove that attribute. */
+extern void fileattr_set PROTO ((char *filename, char *attrname,
+ char *attrval));
+
+/* Set the attributes for file FILENAME in whatever manner is appropriate
+ for a newly created file. */
+extern void fileattr_newfile PROTO ((char *filename));
+
+/* Write out all modified attributes. */
+extern void fileattr_write PROTO ((void));
+
+/* Free all memory allocated by fileattr_*. */
+extern void fileattr_free PROTO ((void));
+
+#define FILEATTR_H 1
+#endif /* fileattr.h */
diff --git a/gnu/usr.bin/cvs/src/filesubr.c b/gnu/usr.bin/cvs/src/filesubr.c
index c2fb5a4304d..41bbfc89846 100644
--- a/gnu/usr.bin/cvs/src/filesubr.c
+++ b/gnu/usr.bin/cvs/src/filesubr.c
@@ -151,33 +151,88 @@ int
isfile (file)
const char *file;
{
- struct stat sb;
-
- if (stat (file, &sb) < 0)
- return (0);
- return (1);
+ return isaccessible(file, F_OK);
}
/*
* Returns non-zero if the argument file is readable.
- * XXX - must be careful if "cvs" is ever made setuid!
*/
int
isreadable (file)
const char *file;
{
- return (access (file, R_OK) != -1);
+ return isaccessible(file, R_OK);
}
/*
- * Returns non-zero if the argument file is writable
- * XXX - muct be careful if "cvs" is ever made setuid!
+ * Returns non-zero if the argument file is writable.
*/
int
iswritable (file)
const char *file;
{
- return (access (file, W_OK) != -1);
+ return isaccessible(file, W_OK);
+}
+
+/*
+ * Returns non-zero if the argument file is accessable according to
+ * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
+ * bits set.
+ */
+int
+isaccessible (file, mode)
+ const char *file;
+ const int mode;
+{
+#ifdef SETXID_SUPPORT
+ struct stat sb;
+ int umask = 0;
+ int gmask = 0;
+ int omask = 0;
+ int uid;
+
+ if (stat(file, &sb) == -1)
+ return 0;
+ if (mode == F_OK)
+ return 1;
+
+ uid = geteuid();
+ if (uid == 0) /* superuser */
+ {
+ if (mode & X_OK)
+ return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
+ else
+ return 1;
+ }
+
+ if (mode & R_OK)
+ {
+ umask |= S_IRUSR;
+ gmask |= S_IRGRP;
+ omask |= S_IROTH;
+ }
+ if (mode & W_OK)
+ {
+ umask |= S_IWUSR;
+ gmask |= S_IWGRP;
+ omask |= S_IWOTH;
+ }
+ if (mode & X_OK)
+ {
+ umask |= S_IXUSR;
+ gmask |= S_IXGRP;
+ omask |= S_IXOTH;
+ }
+
+ if (sb.st_uid == uid)
+ return (sb.st_mode & umask) == umask;
+ else if (sb.st_gid == getegid())
+ return (sb.st_mode & gmask) == gmask;
+ else
+ return (sb.st_mode & omask) == omask;
+#else
+ return access(file, mode) == 0;
+#endif
}
/*
@@ -202,9 +257,9 @@ void
make_directory (name)
const char *name;
{
- struct stat buf;
+ struct stat sb;
- if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
+ if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
error (0, 0, "%s already exists but is not a directory", name);
if (!noexec && mkdir (name, 0777) < 0)
error (1, errno, "cannot make directory %s", name);
@@ -225,7 +280,7 @@ make_directories (name)
if (mkdir (name, 0777) == 0 || errno == EEXIST)
return;
- if (errno != ENOENT)
+ if (! existence_error (errno))
{
error (0, errno, "cannot make path to %s", name);
return;
@@ -242,8 +297,7 @@ make_directories (name)
/*
* Change the mode of a file, either adding write permissions, or removing
- * all write permissions. Adding write permissions honors the current umask
- * setting.
+ * all write permissions. Either change honors the current umask setting.
*/
void
xchmod (fname, writable)
@@ -259,17 +313,18 @@ xchmod (fname, writable)
error (0, errno, "cannot stat %s", fname);
return;
}
+ oumask = umask (0);
+ (void) umask (oumask);
if (writable)
{
- oumask = umask (0);
- (void) umask (oumask);
- mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) |
- ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) |
- ((sb.st_mode & S_IROTH) ? S_IWOTH : 0));
+ mode = sb.st_mode | (~oumask
+ & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
+ | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
+ | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
}
else
{
- mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
}
if (trace)
@@ -309,7 +364,9 @@ rename_file (from, to)
}
/*
- * link a file, if possible.
+ * link a file, if possible. Warning: the Windows NT version of this
+ * function just copies the file, so only use this function in ways
+ * that can deal with either a link or a copy.
*/
int
link_file (from, to)
@@ -401,7 +458,7 @@ deep_remove_dir (path)
struct dirent *dp;
char buf[PATH_MAX];
- if ( rmdir (path) != 0 && errno == ENOTEMPTY )
+ if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST))
{
if ((dirp = opendir (path)) == NULL)
/* If unable to open the directory return
@@ -439,7 +496,8 @@ deep_remove_dir (path)
}
closedir (dirp);
return rmdir (path);
- }
+ }
+
/* Was able to remove the directory return 0 */
return 0;
}
diff --git a/gnu/usr.bin/cvs/src/find_names.c b/gnu/usr.bin/cvs/src/find_names.c
index 82959b5754b..b7bf42bc070 100644
--- a/gnu/usr.bin/cvs/src/find_names.c
+++ b/gnu/usr.bin/cvs/src/find_names.c
@@ -219,7 +219,8 @@ find_dirs (dir, list, checkadm)
if (strcmp (dp->d_name, ".") == 0 ||
strcmp (dp->d_name, "..") == 0 ||
strcmp (dp->d_name, CVSATTIC) == 0 ||
- strcmp (dp->d_name, CVSLCK) == 0)
+ strcmp (dp->d_name, CVSLCK) == 0 ||
+ strcmp (dp->d_name, CVSREP) == 0)
continue;
#ifdef DT_DIR
diff --git a/gnu/usr.bin/cvs/src/hash.c b/gnu/usr.bin/cvs/src/hash.c
index 8ac93237c38..084fdf2c0e4 100644
--- a/gnu/usr.bin/cvs/src/hash.c
+++ b/gnu/usr.bin/cvs/src/hash.c
@@ -30,7 +30,9 @@ hashp (key)
while (*key != 0)
{
- h = (h << 4) + *key++;
+ unsigned int c = *key++;
+ /* The FOLD_FN_CHAR is so that findnode_fn works. */
+ h = (h << 4) + FOLD_FN_CHAR (c);
if ((g = h & 0xf0000000) != 0)
h = (h ^ (g >> 24)) ^ g;
}
@@ -272,6 +274,29 @@ findnode (list, key)
}
/*
+ * Like findnode, but for a filename.
+ */
+Node *
+findnode_fn (list, key)
+ List *list;
+ const char *key;
+{
+ Node *head, *p;
+
+ if (list == (List *) NULL)
+ return ((Node *) NULL);
+
+ head = list->hasharray[hashp (key)];
+ if (head == (Node *) NULL)
+ return ((Node *) NULL);
+
+ for (p = head->hashnext; p != head; p = p->hashnext)
+ if (fncmp (p->key, key) == 0)
+ return (p);
+ return ((Node *) NULL);
+}
+
+/*
* walk a list with a specific proc
*/
int
@@ -292,6 +317,13 @@ walklist (list, proc, closure)
return (err);
}
+int
+list_isempty (list)
+ List *list;
+{
+ return list == NULL || list->list->next == list->list;
+}
+
/*
* sort the elements of a list (in place)
*/
@@ -358,6 +390,7 @@ nodetypestring (type)
case UPDATE: return("UPDATE");
case LOCK: return("LOCK");
case NDBMNODE: return("NDBMNODE");
+ case FILEATTR: return("FILEATTR");
}
return("<trash>");
diff --git a/gnu/usr.bin/cvs/src/hash.h b/gnu/usr.bin/cvs/src/hash.h
index e30511a2701..5dcc4f6d6f1 100644
--- a/gnu/usr.bin/cvs/src/hash.h
+++ b/gnu/usr.bin/cvs/src/hash.h
@@ -19,7 +19,7 @@
enum ntype
{
UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE,
- RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE
+ RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE, FILEATTR
};
typedef enum ntype Ntype;
@@ -46,9 +46,11 @@ typedef struct list List;
List *getlist PROTO((void));
Node *findnode PROTO((List * list, const char *key));
+Node *findnode_fn PROTO((List * list, const char *key));
Node *getnode PROTO((void));
int addnode PROTO((List * list, Node * p));
int walklist PROTO((List * list, int (*)(Node *n, void *closure), void *closure));
+int list_isempty PROTO ((List *list));
void dellist PROTO((List ** listp));
void delnode PROTO((Node * p));
void freenode PROTO((Node * p));
diff --git a/gnu/usr.bin/cvs/src/history.c b/gnu/usr.bin/cvs/src/history.c
index 7a40b7bd8bd..47310a91654 100644
--- a/gnu/usr.bin/cvs/src/history.c
+++ b/gnu/usr.bin/cvs/src/history.c
@@ -579,8 +579,7 @@ history (argc, argv)
option_with_arg ("-x", rec_types);
option_with_arg ("-z", tz_name);
- if (fprintf (to_server, "history\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("history\012", 0);
return get_responses_and_close ();
}
#endif
@@ -944,6 +943,7 @@ fill_hrec (line, hr)
int c;
int off;
static int idx = 0;
+ unsigned long date;
memset ((char *) hr, 0, sizeof (*hr));
while (isspace (*line))
@@ -953,7 +953,8 @@ fill_hrec (line, hr)
*rtn++ = '\0';
hr->type = line++;
- (void) sscanf (line, "%x", &hr->date);
+ (void) sscanf (line, "%lx", &date);
+ hr->date = date;
while (*line && strchr ("0123456789abcdefABCDEF", *line))
line++;
if (*line == '\0')
diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c
index c4bf510ecd1..fc2a425a3fe 100644
--- a/gnu/usr.bin/cvs/src/ignore.c
+++ b/gnu/usr.bin/cvs/src/ignore.c
@@ -27,11 +27,15 @@ static int ign_size; /* This many slots available (plus
static int ign_hold; /* Index where first "temporary" item
* is held */
-const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS* .del-* *.a *.o *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej";
+const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.o *.obj *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej";
#define IGN_GROW 16 /* grow the list by 16 elements at a
* time */
+/* Nonzero if we have encountered an -I ! directive, which means one should
+ no longer ask the server about what is in CVSROOTADM_IGNORE. */
+int ign_inhibit_server;
+
/*
* To the "ignore list", add the hard-coded default ignored wildcards above,
* the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
@@ -45,14 +49,26 @@ ign_setup ()
char file[PATH_MAX];
char *tmp;
+ ign_inhibit_server = 0;
+
/* Start with default list and special case */
tmp = xstrdup (ign_default);
ign_add (tmp, 0);
free (tmp);
- /* Then add entries found in repository, if it exists */
- (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_IGNORE);
- ign_add_file (file, 0);
+#ifdef CLIENT_SUPPORT
+ /* The client handles another way, by (after it does its own ignore file
+ processing, and only if !ign_inhibit_server), letting the server
+ know about the files and letting it decide whether to ignore
+ them based on CVSROOOTADM_IGNORE. */
+ if (!client_active)
+#endif
+ {
+ /* Then add entries found in repository, if it exists */
+ (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM,
+ CVSROOTADM_IGNORE);
+ ign_add_file (file, 0);
+ }
/* Then add entries found in home dir, (if user has one) and file exists */
if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir)
@@ -118,7 +134,7 @@ ign_add_file (file, hold)
fp = fopen (file, "r");
if (fp == NULL)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (0, errno, "cannot open %s", file);
return;
}
@@ -165,7 +181,10 @@ ign_add (ign, hold)
/* if we are doing a '!', continue; otherwise add the '*' */
if (*ign == '!')
+ {
+ ign_inhibit_server = 1;
continue;
+ }
}
else if (*ign == '!')
{
@@ -273,3 +292,81 @@ int ignore_directory (name)
return 0;
}
+
+/*
+ * Process the current directory, looking for files not in ILIST and not on
+ * the global ignore list for this directory. If we find one, call PROC
+ * passing it the name of the file and the update dir.
+ */
+void
+ignore_files (ilist, update_dir, proc)
+ List *ilist;
+ char *update_dir;
+ Ignore_proc proc;
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct stat sb;
+ char *file;
+ char *xdir;
+
+ /* we get called with update_dir set to "." sometimes... strip it */
+ if (strcmp (update_dir, ".") == 0)
+ xdir = "";
+ else
+ xdir = update_dir;
+
+ dirp = opendir (".");
+ if (dirp == NULL)
+ return;
+
+ ign_add_file (CVSDOTIGNORE, 1);
+ wrap_add_file (CVSDOTWRAPPER, 1);
+
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ file = dp->d_name;
+ if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
+ continue;
+ if (findnode_fn (ilist, file) != NULL)
+ continue;
+
+ if (
+#ifdef DT_DIR
+ dp->d_type != DT_UNKNOWN ||
+#endif
+ lstat(file, &sb) != -1)
+ {
+
+ if (
+#ifdef DT_DIR
+ dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN &&
+#endif
+ S_ISDIR(sb.st_mode))
+ {
+ char temp[PATH_MAX];
+
+ (void) sprintf (temp, "%s/%s", file, CVSADM);
+ if (isdir (temp))
+ continue;
+ }
+#ifdef S_ISLNK
+ else if (
+#ifdef DT_DIR
+ dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN &&
+#endif
+ S_ISLNK(sb.st_mode))
+ {
+ continue;
+ }
+#endif
+ }
+
+ /* We could be ignoring FIFOs and other files which are neither
+ regular files nor directories here. */
+ if (ign_name (file))
+ continue;
+ (*proc) (file, xdir);
+ }
+ (void) closedir (dirp);
+}
diff --git a/gnu/usr.bin/cvs/src/import.c b/gnu/usr.bin/cvs/src/import.c
index 95d3c4338d0..9fdf5a11be3 100644
--- a/gnu/usr.bin/cvs/src/import.c
+++ b/gnu/usr.bin/cvs/src/import.c
@@ -207,8 +207,6 @@ import (argc, argv)
{
int err;
- ign_setup ();
-
if (use_file_modtime)
send_arg("-d");
@@ -218,6 +216,14 @@ import (argc, argv)
option_with_arg ("-m", message);
if (keyword_opt != NULL)
option_with_arg ("-k", keyword_opt);
+ /* The only ignore processing which takes place on the server side
+ is the CVSROOT/cvsignore file. But if the user specified -I !,
+ the documented behavior is to not process said file. */
+ if (ign_inhibit_server)
+ {
+ send_arg ("-I");
+ send_arg ("!");
+ }
{
int i;
@@ -229,8 +235,7 @@ import (argc, argv)
client_import_setup (repository);
err = import_descend (message, argv[1], argc - 2, argv + 2);
client_import_done ();
- if (fprintf (to_server, "import\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("import\012", 0);
err += get_responses_and_close ();
return err;
}
@@ -240,7 +245,7 @@ import (argc, argv)
* Make all newly created directories writable. Should really use a more
* sophisticated security mechanism here.
*/
- (void) umask (2);
+ (void) umask (cvsumask);
make_directories (repository);
/* Create the logfile that will be logged upon completion */
@@ -333,16 +338,15 @@ import_descend (message, vtag, targc, targv)
{
if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
continue;
- if (ign_name (dp->d_name))
- {
#ifdef SERVER_SUPPORT
- /* CVS directories are created by server.c because it doesn't
- special-case import. So don't print a message about them.
- Do print a message about other ignored files (although
- most of these will get ignored on the client side). */
- if (server_active && strcmp (dp->d_name, CVSADM) == 0)
- continue;
+ /* 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)
+ continue;
#endif
+ if (ign_name (dp->d_name))
+ {
add_log ('I', dp->d_name);
continue;
}
@@ -572,7 +576,6 @@ add_rev (message, rcs, vfile, vers)
{
int locked, status, ierrno;
char *tocvsPath;
- struct stat vfile_stat;
if (noexec)
return (0);
@@ -591,20 +594,29 @@ add_rev (message, rcs, vfile, vers)
locked = 1;
}
tocvsPath = wrap_tocvs_process_file (vfile);
+ if (tocvsPath == NULL)
+ {
+ /* We play with hard links rather than passing -u to ci to avoid
+ expanding RCS keywords (see test 106.5 in sanity.sh). */
+ if (link_file (vfile, FILE_HOLDER) < 0)
+ {
+ if (errno == EEXIST)
+ {
+ (void) unlink_file (FILE_HOLDER);
+ (void) link_file (vfile, FILE_HOLDER);
+ }
+ else
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno,
+ "ERROR: cannot create link to %s", vfile);
+ error (0, ierrno, "ERROR: cannot create link to %s", vfile);
+ return (1);
+ }
+ }
+ }
- /* We used to deposit the revision with -r; RCS would delete the
- working file, but we'd keep a hard link to it, and rename it
- back after running RCS (ooh, atomicity). However, that
- strategy doesn't work on operating systems without hard links
- (like Windows NT). Instead, let's deposit it using -u, and
- restore its permission bits afterwards. This also means the
- file always exists under its own name. */
- if (! tocvsPath)
- stat (vfile, &vfile_stat);
-
- run_setup ("%s%s -q -f %s%s", Rcsbin, RCS_CI,
- (tocvsPath ? "-r" : "-u"),
- vbranch);
+ run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch);
run_args ("-m%s", make_message_rcslegal (message));
if (use_file_modtime)
run_arg ("-d");
@@ -613,9 +625,11 @@ add_rev (message, rcs, vfile, vers)
status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
ierrno = errno;
- /* Restore the permissions on vfile. */
- if (! tocvsPath)
- chmod (vfile, vfile_stat.st_mode);
+ if (tocvsPath == NULL)
+ rename_file (FILE_HOLDER, vfile);
+ else
+ if (unlink_file_dir (tocvsPath) < 0)
+ error (0, errno, "cannot remove %s", tocvsPath);
if (status)
{
@@ -989,11 +1003,16 @@ add_rcs_file (message, rcs, user, vtag, targc, targv)
(void) fclose (fpuser);
/*
- * Fix the modes on the RCS files. They must maintain the same modes as
- * the original user file, except that all write permissions must be
+ * 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 & ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ 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;
@@ -1041,14 +1060,15 @@ expand_at_signs (buf, size, fp)
{
char *cp, *end;
+ errno = 0;
for (cp = buf, end = buf + size; cp < end; cp++)
{
if (*cp == '@')
{
- if (putc ('@', fp) == EOF)
+ if (putc ('@', fp) == EOF && errno != 0)
return EOF;
}
- if (putc (*cp, fp) == EOF)
+ if (putc (*cp, fp) == EOF && errno != 0)
return (EOF);
}
return (1);
diff --git a/gnu/usr.bin/cvs/src/log.c b/gnu/usr.bin/cvs/src/log.c
index d11757166b6..cbe24fc4801 100644
--- a/gnu/usr.bin/cvs/src/log.c
+++ b/gnu/usr.bin/cvs/src/log.c
@@ -67,17 +67,13 @@ cvslog (argc, argv)
for (i = 1; i < argc && argv[i][0] == '-'; i++)
send_arg (argv[i]);
-#if 0
+ send_file_names (argc - i, argv + i);
/* FIXME: We shouldn't have to send current files to get log entries, but it
doesn't work yet and I haven't debugged it. So send the files --
it's slower but it works. gnu@cygnus.com Apr94 */
- send_file_names (argc - i, argv + i);
-#else
send_files (argc - i, argv + i, local, 0);
-#endif
- if (fprintf (to_server, "log\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("log\012", 0);
err = get_responses_and_close ();
return err;
}
@@ -86,8 +82,8 @@ cvslog (argc, argv)
av = argv;
#endif
- err = start_recursion (log_fileproc, (int (*) ()) NULL, log_dirproc,
- (int (*) ()) NULL, argc - i, argv + i, local,
+ err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc,
+ (DIRLEAVEPROC) NULL, argc - i, argv + i, local,
W_LOCAL | W_REPOS | W_ATTIC, 0, 1,
(char *) NULL, 1, 0);
return (err);
diff --git a/gnu/usr.bin/cvs/src/login.c b/gnu/usr.bin/cvs/src/login.c
new file mode 100644
index 00000000000..c6001514ff7
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/login.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with CVS.
+ *
+ * Allow user to log in for an authenticating server.
+ */
+
+#include "cvs.h"
+
+#ifdef AUTH_CLIENT_SUPPORT /* This covers the rest of the file. */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#ifndef lint
+static const char rcsid[] = "$CVSid: @(#)login.c 1.1 95/10/01 $";
+USE(rcsid);
+#endif
+
+#ifndef CVS_PASSWORD_FILE
+#define CVS_PASSWORD_FILE ".cvspass"
+#endif
+
+/* If non-NULL, get_cvs_password() will just return this. */
+static char *cvs_password = NULL;
+
+/* The return value will need to be freed. */
+char *
+construct_cvspass_filename ()
+{
+ char *homedir;
+ char *passfile;
+
+ /* Environment should override file. */
+ if ((passfile = getenv ("CVS_PASSFILE")) != NULL)
+ return xstrdup (passfile);
+
+ /* Construct absolute pathname to user's password file. */
+ /* todo: does this work under Win-NT and OS/2 ? */
+ homedir = getenv ("HOME");
+ if (! homedir)
+ {
+ error (1, errno, "could not find out home directory");
+ return (char *) NULL;
+ }
+
+ passfile =
+ (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3);
+ strcpy (passfile, homedir);
+ strcat (passfile, "/");
+ strcat (passfile, CVS_PASSWORD_FILE);
+
+ /* Safety first and last, Scouts. */
+ if (isfile (passfile))
+ /* xchmod() is too polite. */
+ chmod (passfile, 0600);
+
+ return passfile;
+}
+
+
+/* Prompt for a password, and store it in the file "CVS/.cvspass".
+ *
+ * Because the user might be accessing multiple repositories, with
+ * different passwords for each one, the format of ~/.cvspass is:
+ *
+ * user@host:/path Acleartext_password
+ * user@host:/path Acleartext_password
+ * ...
+ *
+ * Of course, the "user@" might be left off -- it's just based on the
+ * value of CVSroot.
+ *
+ * The "A" before "cleartext_password" is a literal capital A. It's a
+ * version number indicating which form of scrambling we're doing on
+ * the password -- someday we might provide something more secure than
+ * the trivial encoding we do now, and when that day comes, it would
+ * be nice to remain backward-compatible.
+ *
+ * Like .netrc, the file's permissions are the only thing preventing
+ * it from being read by others. Unlike .netrc, we will not be
+ * fascist about it, at most issuing a warning, and never refusing to
+ * work.
+ */
+int
+login (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *username;
+ int i;
+ char *passfile;
+ FILE *fp;
+ char *typed_password, *found_password;
+ char *linebuf = (char *) NULL;
+ size_t linebuf_len;
+ int root_len, already_entered = 0;
+
+ /* Make this a "fully-qualified" CVSroot if necessary. */
+ if (! strchr (CVSroot, '@'))
+ {
+ /* We need to prepend "user@host:". */
+ char *tmp;
+
+ printf ("Repository \"%s\" not fully-qualified.\n", CVSroot);
+ printf ("Please enter \"user@host:/path\": ");
+ fflush (stdout);
+ getline (&linebuf, &linebuf_len, stdin);
+
+ tmp = xmalloc (strlen (linebuf) + 1);
+
+ /* Give it some permanent storage. */
+ strcpy (tmp, linebuf);
+ tmp[strlen (linebuf) - 1] = '\0';
+ CVSroot = tmp;
+
+ /* Reset. */
+ free (linebuf);
+ linebuf = (char *) NULL;
+ }
+
+ if (CVSroot[0] != ':')
+ {
+ /* Then we need to prepend ":pserver:". */
+ char *tmp;
+
+ tmp = xmalloc (strlen (":pserver:") + strlen (CVSroot) + 1);
+ strcpy (tmp, ":pserver:");
+ strcat (tmp, CVSroot);
+ CVSroot = tmp;
+ }
+
+ /* Check to make sure it's fully-qualified before going on.
+ * Fully qualified in this context means it has both a user and a
+ * host:repos portion.
+ */
+ {
+ char *r;
+
+ /* After confirming that CVSroot is non-NULL, we skip past the
+ initial ":pserver:" to test the rest of it. */
+
+ if (! CVSroot)
+ error (1, 0, "CVSroot is NULL");
+ else if (! strchr ((r = (CVSroot + strlen (":pserver:"))), '@'))
+ goto not_fqrn;
+ else if (! strchr (r, ':'))
+ goto not_fqrn;
+
+ if (0) /* Lovely. */
+ {
+ not_fqrn:
+ error (0, 0, "CVSroot not fully-qualified: %s", CVSroot);
+ error (1, 0, "should be format user@host:/path/to/repository");
+ }
+ }
+
+ passfile = construct_cvspass_filename ();
+ typed_password = getpass ("CVS password: ");
+ typed_password = scramble (typed_password);
+
+ /* Force get_cvs_password() to use this one (when the client
+ * confirms the new password with the server), instead of consulting
+ * the file. We make a new copy because cvs_password will get
+ * zeroed by connect_to_server().
+ */
+ cvs_password = xstrdup (typed_password);
+
+ if (connect_to_pserver (NULL, NULL, 1) == 0)
+ {
+ /* The password is wrong, according to the server. */
+ error (1, 0, "incorrect password");
+ }
+
+ /* IF we have a password for this "[user@]host:/path" already
+ * THEN
+ * IF it's the same as the password we read from the prompt
+ * THEN
+ * do nothing
+ * ELSE
+ * replace the old password with the new one
+ * ELSE
+ * append new entry to the end of the file.
+ */
+
+ root_len = strlen (CVSroot);
+
+ /* Yes, the method below reads the user's password file twice. It's
+ inefficient, but we're not talking about a gig of data here. */
+
+ fp = fopen (passfile, "r");
+ if (fp != NULL)
+ {
+ /* Check each line to see if we have this entry already. */
+ while (getline (&linebuf, &linebuf_len, fp) >= 0)
+ {
+ if (strncmp (CVSroot, linebuf, root_len) == 0)
+ {
+ already_entered = 1;
+ break;
+ }
+ else
+ {
+ free (linebuf);
+ linebuf = (char *) NULL;
+ }
+ }
+ }
+ fclose (fp);
+
+
+ if (already_entered)
+ {
+ /* This user/host has a password in the file already. */
+
+ strtok (linebuf, " ");
+ found_password = strtok (NULL, "\n");
+ if (strcmp (found_password, typed_password))
+ {
+ /* typed_password and found_password don't match, so we'll
+ * have to update passfile. We replace the old password
+ * with the new one by writing a tmp file whose contents are
+ * exactly the same as passfile except that this one entry
+ * gets typed_password instead of found_password. Then we
+ * rename the tmp file on top of passfile.
+ */
+ char *tmp_name;
+ FILE *tmp_fp;
+
+ tmp_name = tmpnam (NULL);
+ if ((tmp_fp = fopen (tmp_name, "w")) == NULL)
+ {
+ error (1, errno, "unable to open temp file %s", tmp_name);
+ return 1;
+ }
+ chmod (tmp_name, 0600);
+
+ fp = fopen (passfile, "r");
+ if (fp == NULL)
+ {
+ error (1, errno, "unable to open %s", passfile);
+ return 1;
+ }
+ /* I'm not paranoid, they really ARE out to get me: */
+ chmod (passfile, 0600);
+
+ free (linebuf);
+ linebuf = (char *) NULL;
+ while (getline (&linebuf, &linebuf_len, fp) >= 0)
+ {
+ if (strncmp (CVSroot, linebuf, root_len))
+ fprintf (tmp_fp, "%s", linebuf);
+ else
+ fprintf (tmp_fp, "%s %s\n", CVSroot, typed_password);
+
+ free (linebuf);
+ linebuf = (char *) NULL;
+ }
+ fclose (tmp_fp);
+ fclose (fp);
+ rename_file (tmp_name, passfile);
+ chmod (passfile, 0600);
+ }
+ }
+ else
+ {
+ if ((fp = fopen (passfile, "a")) == NULL)
+ {
+ error (1, errno, "could not open %s", passfile);
+ free (passfile);
+ return 1;
+ }
+
+ fprintf (fp, "%s %s\n", CVSroot, typed_password);
+ fclose (fp);
+ }
+
+ /* Utter, total, raving paranoia, I know. */
+ chmod (passfile, 0600);
+ memset (typed_password, 0, strlen (typed_password));
+ free (typed_password);
+
+ free (passfile);
+ free (cvs_password);
+ cvs_password = NULL;
+ return 0;
+}
+
+/* todo: "cvs logout" could erase an entry from the file.
+ * But to what purpose?
+ */
+
+/* Returns the _scrambled_ password. The server must descramble
+ before hashing and comparing. */
+char *
+get_cvs_password ()
+{
+ int found_it = 0;
+ int root_len;
+ char *password;
+ char *linebuf = (char *) NULL;
+ size_t linebuf_len;
+ FILE *fp;
+ char *passfile;
+
+ /* If someone (i.e., login()) is calling connect_to_pserver() out of
+ context, then assume they have supplied the correct, scrambled
+ password. */
+ if (cvs_password)
+ return cvs_password;
+
+ /* Environment should override file. */
+ if ((password = getenv ("CVS_PASSWORD")) != NULL)
+ {
+ char *p;
+ p = xstrdup (password);
+ /* If we got it from the environment, then it wasn't properly
+ scrambled. Since unscrambling is done on the server side, we
+ need to transmit it scrambled. */
+ p = scramble (p);
+ return p;
+ }
+
+ /* Else get it from the file. */
+ passfile = construct_cvspass_filename ();
+ fp = fopen (passfile, "r");
+ if (fp == NULL)
+ {
+ error (0, errno, "could not open %s", passfile);
+ free (passfile);
+ error (1, 0, "use \"cvs login\" to log in first");
+ }
+
+ root_len = strlen (CVSroot);
+
+ /* Check each line to see if we have this entry already. */
+ while (getline (&linebuf, &linebuf_len, fp) >= 0)
+ {
+ if (strncmp (CVSroot, linebuf, root_len) == 0)
+ {
+ /* This is it! So break out and deal with linebuf. */
+ found_it = 1;
+ break;
+ }
+ else
+ {
+ free (linebuf);
+ linebuf = (char *) NULL;
+ }
+ }
+
+ if (found_it)
+ {
+ /* linebuf now contains the line with the password. */
+ char *tmp;
+
+ strtok (linebuf, " ");
+ password = strtok (NULL, "\n");
+
+ /* Give it permanent storage. */
+ tmp = xmalloc (strlen (password) + 1);
+ strcpy (tmp, password);
+ tmp[strlen (password)] = '\0';
+ memset (password, 0, strlen (password));
+ return tmp;
+ }
+ else
+ {
+ error (0, 0, "cannot find password");
+ error (0, 0, "use \"cvs login\" to log in first");
+ error (1, 0, "or set the CVS_PASSWORD environment variable");
+ }
+ free (linebuf);
+}
+
+#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
+
diff --git a/gnu/usr.bin/cvs/src/logmsg.c b/gnu/usr.bin/cvs/src/logmsg.c
index f39dd31e888..7686a3680a6 100644
--- a/gnu/usr.bin/cvs/src/logmsg.c
+++ b/gnu/usr.bin/cvs/src/logmsg.c
@@ -7,6 +7,7 @@
*/
#include "cvs.h"
+#include "getline.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)logmsg.c 1.48 94/09/29 $";
@@ -129,9 +130,13 @@ do_editor (dir, messagep, repository, changes)
List *changes;
{
static int reuse_log_message = 0;
- char line[MAXLINELEN], fname[L_tmpnam+1];
+ char *line;
+ int line_length;
+ size_t line_chars_allocated;
+ char fname[L_tmpnam+1];
struct stat pre_stbuf, post_stbuf;
int retcode = 0;
+ char *p;
if (noexec || reuse_log_message)
return;
@@ -172,7 +177,8 @@ do_editor (dir, messagep, repository, changes)
CVSEDITPREFIX);
/* finish off the temp file */
- (void) fclose (fp);
+ if (fclose (fp) == EOF)
+ error (1, errno, "%s", fname);
if (stat (fname, &pre_stbuf) == -1)
pre_stbuf.st_mtime = 0;
@@ -209,19 +215,30 @@ do_editor (dir, messagep, repository, changes)
*messagep[0] = '\0';
}
-/* !!! XXX FIXME: fgets is broken. This should not have any line
- length limits. */
+ line = NULL;
+ line_chars_allocated = 0;
if (*messagep)
{
- while (fgets (line, sizeof (line), fp) != NULL)
+ p = *messagep;
+ while (1)
{
+ line_length = getline (&line, &line_chars_allocated, fp);
+ if (line_length == -1)
+ {
+ if (ferror (fp))
+ error (0, errno, "warning: cannot read %s", fname);
+ break;
+ }
if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0)
continue;
- (void) strcat (*messagep, line);
+ (void) strcpy (p, line);
+ p += line_length;
}
}
- (void) fclose (fp);
+ if (fclose (fp) < 0)
+ error (0, errno, "warning: cannot close %s", fname);
+
if (pre_stbuf.st_mtime == post_stbuf.st_mtime ||
*messagep == NULL ||
strcmp (*messagep, "\n") == 0)
@@ -232,9 +249,9 @@ do_editor (dir, messagep, repository, changes)
(void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
(void) printf ("Action: (continue) ");
(void) fflush (stdout);
- *line = '\0';
- (void) fgets (line, sizeof (line), stdin);
- if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C')
+ line_length = getline (&line, &line_chars_allocated, stdin);
+ if (line_length <= 0
+ || *line == '\n' || *line == 'c' || *line == 'C')
break;
if (*line == 'a' || *line == 'A')
error (1, 0, "aborted by user");
@@ -248,7 +265,10 @@ do_editor (dir, messagep, repository, changes)
(void) printf ("Unknown input\n");
}
}
- (void) unlink_file (fname);
+ if (line)
+ free (line);
+ if (unlink_file (fname) < 0)
+ error (0, errno, "warning: cannot remove temp file %s", fname);
}
/*
@@ -264,7 +284,6 @@ rcsinfo_proc (repository, template)
{
static char *last_template;
FILE *tfp;
- char line[MAXLINELEN];
/* nothing to do if the last one included is the same as this one */
if (last_template && strcmp (last_template, template) == 0)
@@ -275,14 +294,22 @@ rcsinfo_proc (repository, template)
if ((tfp = fopen (template, "r")) != NULL)
{
- while (fgets (line, sizeof (line), tfp) != NULL)
+ char *line = NULL;
+ size_t line_chars_allocated = 0;
+
+ while (getline (&line, &line_chars_allocated, tfp) >= 0)
(void) fputs (line, fp);
- (void) fclose (tfp);
+ if (ferror (tfp))
+ error (0, errno, "warning: cannot read %s", template);
+ if (fclose (tfp) < 0)
+ error (0, errno, "warning: cannot close %s", template);
+ if (line)
+ free (line);
return (0);
}
else
{
- error (0, 0, "Couldn't open rcsinfo template file %s", template);
+ error (0, errno, "Couldn't open rcsinfo template file %s", template);
return (1);
}
}
diff --git a/gnu/usr.bin/cvs/src/mkmodules.c b/gnu/usr.bin/cvs/src/mkmodules.c
index c4453588921..999833e95d4 100644
--- a/gnu/usr.bin/cvs/src/mkmodules.c
+++ b/gnu/usr.bin/cvs/src/mkmodules.c
@@ -72,6 +72,8 @@ main (argc, argv)
"a %s file can specify extra CVSROOT files to auto-checkout"},
{CVSROOTADM_WRAPPER,
"a %s file can be used to specify files to treat as wrappers"},
+ {CVSROOTADM_NOTIFY,
+ "a %s file can be used to specify where notifications go"},
{NULL, NULL}};
/*
@@ -422,7 +424,8 @@ Lock_Cleanup ()
int server_active = 0;
void
-server_cleanup ()
+server_cleanup (sig)
+ int sig;
{
}
diff --git a/gnu/usr.bin/cvs/src/modules.c b/gnu/usr.bin/cvs/src/modules.c
index 9dcce131473..2f1a6155deb 100644
--- a/gnu/usr.bin/cvs/src/modules.c
+++ b/gnu/usr.bin/cvs/src/modules.c
@@ -84,7 +84,7 @@ do_module (db, mname, m_type, msg, callback_proc, where,
char *mname;
enum mtype m_type;
char *msg;
- int (*callback_proc) ();
+ CALLBACKPROC callback_proc;
char *where;
int shorten;
int local_specified;
@@ -97,16 +97,18 @@ do_module (db, mname, m_type, msg, callback_proc, where,
char *tag_prog = NULL;
char *update_prog = NULL;
struct saved_cwd cwd;
- char line[MAXLINELEN];
- char *xmodargv[MAXFILEPERDIR];
+ char *line;
+ int modargc;
+ int xmodargc;
char **modargv;
+ char *xmodargv[MAXFILEPERDIR];
char *value;
char *zvalue;
char *mwhere = NULL;
char *mfile = NULL;
char *spec_opt = NULL;
char xvalue[PATH_MAX];
- int modargc, alias = 0;
+ int alias = 0;
datum key, val;
char *cp;
int c, err = 0;
@@ -114,10 +116,11 @@ do_module (db, mname, m_type, msg, callback_proc, where,
#ifdef SERVER_SUPPORT
if (trace)
{
- fprintf (stderr, "%c-> do_module (%s, %s, %s, %s)\n",
+ fprintf (stderr, "%s%c-> do_module (%s, %s, %s, %s)\n",
+ error_use_protocol ? "E " : "",
(server_active) ? 'S' : ' ',
- mname, msg, where ? where : "",
- extra_arg ? extra_arg : "");
+ mname, msg, where ? where : "",
+ extra_arg ? extra_arg : "");
}
#endif
@@ -348,7 +351,12 @@ do_module (db, mname, m_type, msg, callback_proc, where,
(void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
CVSROOTADM, CVSNULLREPOS);
if (!isfile (nullrepos))
+ {
+ mode_t omask;
+ omask = umask (cvsumask);
(void) CVS_MKDIR (nullrepos, 0777);
+ (void) umask (omask);
+ }
if (!isdir (nullrepos))
error (1, 0, "there is no repository %s", nullrepos);
@@ -386,10 +394,13 @@ do_module (db, mname, m_type, msg, callback_proc, where,
*/
/* Put the value on a line with XXX prepended for getopt to eat */
+ line = xmalloc (strlen (value) + 10);
(void) sprintf (line, "%s %s", "XXX", value);
/* turn the line into an argv[] array */
- line2argv (&modargc, xmodargv, line);
+ line2argv (&xmodargc, xmodargv, line);
+ free (line);
+ modargc = xmodargc;
modargv = xmodargv;
/* parse the args */
@@ -471,8 +482,12 @@ do_module (db, mname, m_type, msg, callback_proc, where,
err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
local_specified, mname, msg);
- /* clean up */
- free_names (&modargc, modargv);
+#if 0
+ /* FIXME: I've fixed this so that the correct arguments are called,
+ but now this fails because there is code below this point that
+ uses optarg values extracted from the arg vector. */
+ free_names (&xmodargc, xmodargv);
+#endif
/* if there were special include args, process them now */
@@ -749,7 +764,8 @@ cat_module (status)
int moduleargc;
struct sortrec *s_h;
char *cp, *cp2, **argv;
- char line[MAXLINELEN], *moduleargv[MAXFILEPERDIR];
+ char *line;
+ char *moduleargv[MAXFILEPERDIR];
#ifdef sun
#ifdef TIOCGSIZE
@@ -801,8 +817,10 @@ cat_module (status)
}
/* Parse module file entry as command line and print options */
+ line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 10);
(void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
line2argv (&moduleargc, moduleargv, line);
+ free (line);
argc = moduleargc;
argv = moduleargv;
@@ -866,5 +884,7 @@ cat_module (status)
*cp++ = '\0';
(void) printf ("%s\n", cp2);
}
+
+ free_names(&moduleargc, moduleargv);
}
}
diff --git a/gnu/usr.bin/cvs/src/myndbm.c b/gnu/usr.bin/cvs/src/myndbm.c
index fef326576bd..f5d8f4f4dea 100644
--- a/gnu/usr.bin/cvs/src/myndbm.c
+++ b/gnu/usr.bin/cvs/src/myndbm.c
@@ -13,7 +13,9 @@
* size, and this code works fine.
*/
+#include <assert.h>
#include "cvs.h"
+#include "getline.h"
#ifdef MY_NDBM
@@ -34,21 +36,54 @@ mydbm_open (file, flags, mode)
FILE *fp;
DBM *db;
- if ((fp = fopen (file, "r")) == NULL)
+ fp = fopen (file, "r");
+ if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT)))
return ((DBM *) 0);
db = (DBM *) xmalloc (sizeof (*db));
db->dbm_list = getlist ();
+ db->modified = 0;
+ db->name = xstrdup (file);
- mydbm_load_file (fp, db->dbm_list);
- (void) fclose (fp);
+ if (fp != NULL)
+ {
+ mydbm_load_file (fp, db->dbm_list);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", file);
+ }
return (db);
}
+static int write_item PROTO ((Node *, void *));
+
+static int
+write_item (node, data)
+ Node *node;
+ void *data;
+{
+ FILE *fp = (FILE *)data;
+ fputs (node->key, fp);
+ fputs (" ", fp);
+ fputs (node->data, fp);
+ fputs ("\n", fp);
+ return 0;
+}
+
void
mydbm_close (db)
DBM *db;
{
+ if (db->modified)
+ {
+ FILE *fp;
+ fp = fopen (db->name, "w");
+ if (fp == NULL)
+ error (1, errno, "cannot write %s", db->name);
+ walklist (db->dbm_list, write_item, (void *)fp);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", db->name);
+ }
+ free (db->name);
dellist (&db->dbm_list);
free ((char *) db);
}
@@ -128,16 +163,53 @@ mydbm_nextkey (db)
return (key);
}
+/* Note: only updates the in-memory copy, which is written out at
+ mydbm_close time. Note: Also differs from DBM in that on duplication,
+ it gives a warning, rather than either DBM_INSERT or DBM_REPLACE
+ behavior. */
+int
+mydbm_store (db, key, value, flags)
+ DBM *db;
+ datum key;
+ datum value;
+ int flags;
+{
+ Node *node;
+
+ node = getnode ();
+ node->type = NDBMNODE;
+
+ node->key = xmalloc (key.dsize + 1);
+ strncpy (node->key, key.dptr, key.dsize);
+ node->key[key.dsize] = '\0';
+
+ node->data = xmalloc (value.dsize + 1);
+ strncpy (node->data, value.dptr, value.dsize);
+ node->data[value.dsize] = '\0';
+
+ db->modified = 1;
+ if (addnode (db->dbm_list, node) == -1)
+ {
+ error (0, 0, "attempt to insert duplicate key `%s'", node->key);
+ freenode (node);
+ return 0;
+ }
+ return 0;
+}
+
static void
mydbm_load_file (fp, list)
FILE *fp;
List *list;
{
- char line[MAXLINELEN], value[MAXLINELEN];
+ char *line = NULL;
+ size_t line_len;
+ /* FIXME: arbitrary limit. */
+ char value[MAXLINELEN];
char *cp, *vp;
int len, cont;
- for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
+ for (cont = 0; getline (&line, &line_len, fp) >= 0;)
{
if ((cp = strrchr (line, '\n')) != NULL)
*cp = '\0'; /* strip the newline */
@@ -208,6 +280,7 @@ mydbm_load_file (fp, list)
}
}
}
+ free (line);
}
#endif /* MY_NDBM */
diff --git a/gnu/usr.bin/cvs/src/myndbm.h b/gnu/usr.bin/cvs/src/myndbm.h
index 3af31305506..0431e15d4d6 100644
--- a/gnu/usr.bin/cvs/src/myndbm.h
+++ b/gnu/usr.bin/cvs/src/myndbm.h
@@ -8,6 +8,13 @@ typedef struct
{
List *dbm_list; /* cached database */
Node *dbm_next; /* next key to return for nextkey() */
+
+ /* Name of the file to write to if modified is set. malloc'd. */
+ char *name;
+
+ /* Nonzero if the database has been modified and dbm_close needs to
+ write it out to disk. */
+ int modified;
} DBM;
typedef struct
@@ -26,11 +33,15 @@ typedef struct
#define dbm_fetch mydbm_fetch
#define dbm_firstkey mydbm_firstkey
#define dbm_nextkey mydbm_nextkey
+#define dbm_store mydbm_store
+#define DBM_INSERT 0
+#define DBM_REPLACE 1
DBM *mydbm_open PROTO((char *file, int flags, int mode));
void mydbm_close PROTO((DBM * db));
datum mydbm_fetch PROTO((DBM * db, datum key));
datum mydbm_firstkey PROTO((DBM * db));
datum mydbm_nextkey PROTO((DBM * db));
+extern int mydbm_store PROTO ((DBM *, datum, datum, int));
#endif /* MY_NDBM */
diff --git a/gnu/usr.bin/cvs/src/options.h.in b/gnu/usr.bin/cvs/src/options.h.in
index de55040ea72..d7166a9affd 100644
--- a/gnu/usr.bin/cvs/src/options.h.in
+++ b/gnu/usr.bin/cvs/src/options.h.in
@@ -54,9 +54,9 @@
/*
* The "diff" program to execute when creating patch output. This "diff"
* must support the "-c" option for context diffing. Specify a full
- * pathname if your site wants to use a particular diff. If you are
- * using the GNU version of diff (version 1.15 or later), this should
- * be "diff -a".
+ * pathname if your site wants to use a particular diff. Note that unlike
+ * the diff used with RCS, you *must not* supply -a here (doing so will cause
+ * the server to generate patches which patch cannot handle in some cases).
*
* NOTE: this program is only used for the ``patch'' sub-command (and
* for ``update'' if you are using the server). The other commands
@@ -65,18 +65,18 @@
*/
#ifndef DIFF
-#define DIFF "@gdiff_path@"
+#define DIFF "diff"
#endif
/*
* The "grep" program to execute when checking to see if a merged file had
- * any conflicts. This "grep" must support the "-s" option and a standard
+ * any conflicts. This "grep" must support a standard basic
* regular expression as an argument. Specify a full pathname if your site
* wants to use a particular grep.
*/
#ifndef GREP
-#define GREP "@ggrep_path@"
+#define GREP "grep"
#endif
/*
@@ -114,9 +114,20 @@
* unless the user overrides the default with the RCSBIN environment variable
* or the "-b" option to CVS.
*
+ * If you're compiling the authenticating server (see
+ * AUTH_SERVER_SUPPORT farther down), then you probably want to set
+ * RCSBIN_DFLT. The authenticating server starts out running as root,
+ * and then switches to run as the appropriate user once
+ * authentication is complete. No shell is ever started by that user,
+ * so the PATH environment variable may not contain the directory with
+ * the RCS binaries, even though if that user logged in normally, PATH
+ * would include the directory. An alternative to setting RCSBIN_DFLT
+ * is to make sure that root has the right directory in its path
+ * already. Another, probably better alternative is to specify -b in
+ * /etc/inetd.conf.
+ *
* This define should be either the empty string ("") or a full pathname to the
- * directory containing all the installed programs from the RCS distribution.
- */
+ * directory containing all the installed programs from the RCS distribution. */
#ifndef RCSBIN_DFLT
#define RCSBIN_DFLT ""
#endif
@@ -133,6 +144,30 @@
#endif
/*
+ * The default umask to use when creating or otherwise setting file or
+ * directory permissions in the repository. Must be a value in the
+ * range of 0 through 0777. For example, a value of 002 allows group
+ * rwx access and world rx access; a value of 007 allows group rwx
+ * access but no world access. This value is overridden by the value
+ * of the CVSUMASK environment variable, which is interpreted as an
+ * octal number.
+ */
+#ifndef UMASK_DFLT
+#define UMASK_DFLT 002
+#endif
+
+/*
+ * The cvs admin command is restricted to the members of the group
+ * CVS_ADMIN_GROUP. If this group does not exist, all users are
+ * allowed to run cvs admin. To disable the cvs admin for all users,
+ * create an empty group CVS_ADMIN_GROUP. To disable access control for
+ * cvs admin, comment out the define below.
+ */
+#ifndef CVS_ADMIN_GROUP
+#define CVS_ADMIN_GROUP "cvsadmin"
+#endif
+
+/*
* The Repository file holds the path to the directory within the source
* repository that contains the RCS ,v files for each CVS working directory.
* This path is either a full-path or a path relative to CVSROOT.
@@ -188,19 +223,6 @@
#endif
/*
- * The "cvs admin" command allows people to get around most of the logging
- * and info procedures within CVS. For exmaple, "cvs tag tagname filename"
- * will perform some validity checks on the tag, while "cvs admin -Ntagname"
- * will not perform those checks. For this reason, some sites may wish to
- * disable the admin function completely.
- *
- * To disable the admin function, uncomment the lines below.
- */
-#ifndef CVS_NOADMIN
-/* #define CVS_NOADMIN */
-#endif
-
-/*
* The "cvs diff" command accepts all the single-character options that GNU
* diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put
* cpp-style #define's around the output differences. CVS, by default, uses
@@ -212,6 +234,40 @@
#define CVS_DIFFDATE
#endif
+/*
+ * define this to enable the SETXID support (see FAQ 4D.13)
+ */
+#ifndef SETXID_SUPPORT
+/* #define SETXID_SUPPORT */
+#endif
+
+/*
+ * The client and server will not perform password-authentication
+ * unless you explicitly ask for it. You can build a binary that only
+ * serves or only clients (sure it's a verb), or one that does both.
+ */
+/* #define AUTH_CLIENT_SUPPORT 1 */
+/* #define AUTH_SERVER_SUPPORT 1 */
+
+/*
+ * If you are working with a large remote repository and a 'cvs checkout' is
+ * swamping your network and memory, define these to enable flow control.
+ * You will end up with even less guarantees of a consistant checkout,
+ * but that may be better than no checkout at all. The master server process
+ * will monitor how far it is getting behind, if it reaches the high water
+ * mark, it will signal the child process to stop generating data when
+ * convenient (ie: no locks are held, currently at the beginning of a
+ * new directory). Once the buffer has drained sufficiently to reach the
+ * low water mark, it will be signalled to start again.
+ * -- EXPERIMENTAL! -- A better solution may be in the works.
+ * You may override the default hi/low watermarks here too.
+ */
+#ifndef SERVER_FLOWCONTROL
+/* #define SERVER_FLOWCONTROL */
+/* #define SERVER_HI_WATER (2 * 1024 * 1024) */
+/* #define SERVER_LO_WATER (1 * 1024 * 1024) */
+#endif
+
/* End of CVS configuration section */
/*
diff --git a/gnu/usr.bin/cvs/src/parseinfo.c b/gnu/usr.bin/cvs/src/parseinfo.c
index d19e774f294..6d59884db9a 100644
--- a/gnu/usr.bin/cvs/src/parseinfo.c
+++ b/gnu/usr.bin/cvs/src/parseinfo.c
@@ -24,7 +24,7 @@ int
Parse_Info (infofile, repository, callproc, all)
char *infofile;
char *repository;
- int (*callproc) ();
+ CALLPROC callproc;
int all;
{
int err = 0;
@@ -32,6 +32,7 @@ Parse_Info (infofile, repository, callproc, all)
char infopath[PATH_MAX];
char line[MAXLINELEN];
char *default_value = NULL;
+ char *expanded_value= NULL;
int callback_done, line_number;
char *cp, *exp, *value, *srepos;
const char *regex_err;
@@ -97,42 +98,14 @@ Parse_Info (infofile, repository, callproc, all)
if ((cp = strrchr (value, '\n')) != NULL)
*cp = '\0';
- /* FIXME: probably should allow multiple occurrences of CVSROOT. */
- /* FIXME-maybe: perhaps should allow CVSREAD and other cvs
- settings (if there is a need for them, which isn't clear). */
- /* FIXME-maybe: Should there be a way to substitute arbitrary
- environment variables? Probably not, because then what gets
- substituted would depend on who runs cvs. A better feature might
- be to allow a file in CVSROOT to specify variables to be
- substituted. */
+ expanded_value = expand_path (value);
+ if (!expanded_value)
{
- char *p, envname[128];
-
- strcpy(envname, "$");
- /* FIXME: I'm not at all sure this should be CVSROOT_ENV as opposed
- to literal CVSROOT. The value we subsitute is the cvs root
- in use which is not the same thing as the environment variable
- CVSROOT_ENV. */
- strcat(envname, CVSROOT_ENV);
-
- cp = xstrdup(value);
- if ((p = strstr(cp, envname))) {
- if (strlen(line) + strlen(CVSroot) + 1 > MAXLINELEN) {
- /* FIXME: there is no reason for this arbitrary limit. */
- error(0, 0,
- "line %d in %s too long to expand $CVSROOT, ignored",
- line_number, infofile);
- continue;
- }
- if (p > cp) {
- strncpy(value, cp, p - cp);
- value[p - cp] = '\0';
- strcat(value, CVSroot);
- } else
- strcpy(value, CVSroot);
- strcat(value, p + strlen(envname));
- }
- free(cp);
+ error (0, 0,
+ "Invalid environmental variable at line %d in file %s",
+ line_number, infofile);
+ continue;
+
}
/*
@@ -145,7 +118,7 @@ Parse_Info (infofile, repository, callproc, all)
/* save the default value so we have it later if we need it */
if (strcmp (exp, "DEFAULT") == 0)
{
- default_value = xstrdup (value);
+ default_value = xstrdup (expanded_value);
continue;
}
@@ -157,7 +130,7 @@ Parse_Info (infofile, repository, callproc, all)
if (strcmp (exp, "ALL") == 0)
{
if (all)
- err += callproc (repository, value);
+ err += callproc (repository, expanded_value);
else
error(0, 0, "Keyword `ALL' is ignored at line %d in %s file",
line_number, infofile);
@@ -179,7 +152,7 @@ Parse_Info (infofile, repository, callproc, all)
continue; /* no match */
/* it did, so do the callback and note that we did one */
- err += callproc (repository, value);
+ err += callproc (repository, expanded_value);
callback_done = 1;
}
(void) fclose (fp_info);
@@ -191,6 +164,8 @@ Parse_Info (infofile, repository, callproc, all)
/* free up space if necessary */
if (default_value != NULL)
free (default_value);
+ if (expanded_value != NULL)
+ free (expanded_value);
return (err);
}
diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c
index f9e9f3a9f1b..2170d98636b 100644
--- a/gnu/usr.bin/cvs/src/patch.c
+++ b/gnu/usr.bin/cvs/src/patch.c
@@ -13,6 +13,7 @@
*/
#include "cvs.h"
+#include "getline.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)patch.c 1.57 94/09/30 $";
@@ -33,7 +34,9 @@ static int toptwo_diffs = 0;
static int local = 0;
static char *options = NULL;
static char *rev1 = NULL;
+static int rev1_validated = 1;
static char *rev2 = NULL;
+static int rev2_validated = 1;
static char *date1 = NULL;
static char *date2 = NULL;
static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
@@ -202,8 +205,7 @@ patch (argc, argv)
send_arg (argv[i]);
}
- if (fprintf (to_server, "rdiff\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("rdiff\012", 0);
return get_responses_and_close ();
}
#endif
@@ -310,9 +312,20 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
else
which = W_REPOS;
+ if (rev1 != NULL && !rev1_validated)
+ {
+ tag_check_valid (rev1, *pargc - 1, argv + 1, local, 0, NULL);
+ rev1_validated = 1;
+ }
+ if (rev2 != NULL && !rev2_validated)
+ {
+ tag_check_valid (rev2, *pargc - 1, argv + 1, local, 0, NULL);
+ rev2_validated = 1;
+ }
+
/* start the recursion processor */
- err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
- (int (*) ()) NULL, *pargc - 1, argv + 1, local,
+ err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
+ (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local,
which, 0, 1, where, 1, 1);
return (err);
@@ -342,7 +355,9 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
int isattic = 0;
int retcode = 0;
char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
- char line1[MAXLINELEN], line2[MAXLINELEN];
+ char *line1, *line2;
+ size_t line1_chars_allocated;
+ size_t line2_chars_allocated;
char *cp1, *cp2, *commap;
FILE *fp;
@@ -360,7 +375,7 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
if (isattic && rev2 == NULL && date2 == NULL)
vers_head = NULL;
else
- vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match);
+ vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 0);
if (toptwo_diffs)
{
@@ -378,7 +393,7 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
return (1);
}
}
- vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
+ vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, 0);
if (vers_tag == NULL && (vers_head == NULL || isattic))
return (0); /* nothing known about specified revs */
@@ -465,6 +480,12 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
run_arg (tmpfile1);
run_arg (tmpfile2);
+
+ line1 = NULL;
+ line1_chars_allocated = 0;
+ line2 = NULL;
+ line2_chars_allocated = 0;
+
switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
{
case -1: /* fork/wait failure */
@@ -488,8 +509,8 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
(void) fflush (stdout);
fp = open_file (tmpfile3, "r");
- if (fgets (line1, sizeof (line1), fp) == NULL ||
- fgets (line2, sizeof (line2), fp) == NULL)
+ if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
+ getline (&line2, &line2_chars_allocated, fp) < 0)
{
error (0, errno, "failed to read diff file header %s for %s",
tmpfile3, rcs);
@@ -557,14 +578,19 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles)
if (update_dir[0] != '\0')
(void) printf ("%s/", update_dir);
(void) printf ("%s%s", rcs, cp2);
- while (fgets (line1, sizeof (line1), fp) != NULL)
- (void) printf ("%s", line1);
+ /* spew the rest of the diff out */
+ while (getline (&line1, &line1_chars_allocated, fp) >= 0)
+ (void) fputs (line1, stdout);
(void) fclose (fp);
break;
default:
error (0, 0, "diff failed for %s", rcs);
}
out:
+ if (line1)
+ free (line1);
+ if (line2)
+ free (line2);
(void) unlink_file (tmpfile1);
(void) unlink_file (tmpfile2);
(void) unlink_file (tmpfile3);
diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c
index 9722b338bfb..43282a668ff 100644
--- a/gnu/usr.bin/cvs/src/rcs.c
+++ b/gnu/usr.bin/cvs/src/rcs.c
@@ -152,7 +152,7 @@ RCS_parse (file, repos)
fclose (fp);
return (rcs);
}
- else if (errno != ENOENT)
+ else if (! existence_error (errno))
{
error (0, errno, "cannot open %s", rcsfile);
return NULL;
@@ -177,7 +177,7 @@ RCS_parse (file, repos)
fclose (fp);
return (rcs);
}
- else if (errno != ENOENT)
+ else if (! existence_error (errno))
{
error (0, errno, "cannot open %s", rcsfile);
return NULL;
@@ -347,6 +347,12 @@ RCS_reparsercsfile (rdata)
}
}
+ if (strcmp (RCSEXPAND, key) == 0)
+ {
+ rdata->expand = xstrdup (value);
+ continue;
+ }
+
/*
* check key for '.''s and digits (probably a rev) if it is a
* revision, we are done with the headers and are down to the
@@ -494,6 +500,8 @@ freercsnode (rnodep)
dellist (&(*rnodep)->symbols);
if ((*rnodep)->symbols_data != (char *) NULL)
free ((*rnodep)->symbols_data);
+ if ((*rnodep)->expand != NULL)
+ free ((*rnodep)->expand);
if ((*rnodep)->head != (char *) NULL)
free ((*rnodep)->head);
if ((*rnodep)->branch != (char *) NULL)
@@ -827,15 +835,15 @@ do_branches (list, val)
* The result is returned; null-string if error.
*/
char *
-RCS_getversion (rcs, tag, date, force_tag_match)
+RCS_getversion (rcs, tag, date, force_tag_match, return_both)
RCSNode *rcs;
char *tag;
char *date;
int force_tag_match;
+ int return_both;
{
/* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
+ assert (rcs != NULL);
if (tag && date)
{
@@ -845,7 +853,7 @@ RCS_getversion (rcs, tag, date, force_tag_match)
* first lookup the tag; if that works, turn the revision into
* a branch and lookup the date.
*/
- tagrev = RCS_gettag (rcs, tag, force_tag_match);
+ tagrev = RCS_gettag (rcs, tag, force_tag_match, 0);
if (tagrev == NULL)
return ((char *) NULL);
@@ -856,7 +864,7 @@ RCS_getversion (rcs, tag, date, force_tag_match)
return (rev);
}
else if (tag)
- return (RCS_gettag (rcs, tag, force_tag_match));
+ return (RCS_gettag (rcs, tag, force_tag_match, return_both));
else if (date)
return (RCS_getdate (rcs, date, force_tag_match));
else
@@ -873,16 +881,17 @@ RCS_getversion (rcs, tag, date, force_tag_match)
* If the matched tag is a branch tag, find the head of the branch.
*/
char *
-RCS_gettag (rcs, tag, force_tag_match)
+RCS_gettag (rcs, symtag, force_tag_match, return_both)
RCSNode *rcs;
- char *tag;
+ char *symtag;
int force_tag_match;
+ int return_both;
{
Node *p;
+ char *tag = symtag;
/* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
+ assert (rcs != NULL);
/* XXX this is probably not necessary, --jtc */
if (rcs->flags & PARTIAL)
@@ -982,7 +991,25 @@ RCS_gettag (rcs, tag, force_tag_match)
else
p = findnode (rcs->versions, tag);
if (p != NULL)
- return (xstrdup (tag));
+ {
+ /*
+ * we have found a numeric revision for the revision tag.
+ * To support expanding the RCS keyword Name, return both
+ * the numeric tag and the supplied tag (which might be
+ * symbolic). They are separated with a ':' which is not
+ * a valid tag char. The variable return_both is only set
+ * if this function is called through Version_TS ->
+ * RCS_getversion.
+ */
+ if (return_both)
+ {
+ char *both = xmalloc(strlen(tag) + 2 + strlen(symtag));
+ sprintf(both, "%s:%s", tag, symtag);
+ return both;
+ }
+ else
+ return (xstrdup (tag));
+ }
else
{
/* The revision wasn't there, so return the head or NULL */
@@ -1211,8 +1238,7 @@ RCS_getbranch (rcs, tag, force_tag_match)
char *cp;
/* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
+ assert (rcs != NULL);
if (rcs->flags & PARTIAL)
RCS_reparsercsfile (rcs);
@@ -1323,16 +1349,14 @@ RCS_head (rcs)
RCSNode *rcs;
{
/* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- if (rcs->branch)
- return (RCS_getbranch (rcs, rcs->branch, 1));
+ assert (rcs != NULL);
/*
* NOTE: we call getbranch with force_tag_match set to avoid any
* possibility of recursion
*/
+ if (rcs->branch)
+ return (RCS_getbranch (rcs, rcs->branch, 1));
else
return (xstrdup (rcs->head));
}
@@ -1353,8 +1377,7 @@ RCS_getdate (rcs, date, force_tag_match)
RCSVers *vers = NULL;
/* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
+ assert (rcs != NULL);
if (rcs->flags & PARTIAL)
RCS_reparsercsfile (rcs);
@@ -1526,8 +1549,7 @@ RCS_getrevtime (rcs, rev, date, fudge)
RCSVers *vers;
/* make sure we have something to look at... */
- if (rcs == NULL)
- return (revdate);
+ assert (rcs != NULL);
if (rcs->flags & PARTIAL)
RCS_reparsercsfile (rcs);
diff --git a/gnu/usr.bin/cvs/src/rcs.h b/gnu/usr.bin/cvs/src/rcs.h
index d5ac5989c29..f64501d71e6 100644
--- a/gnu/usr.bin/cvs/src/rcs.h
+++ b/gnu/usr.bin/cvs/src/rcs.h
@@ -25,7 +25,13 @@
#define RCSSYMBOLS "symbols"
#define RCSDATE "date"
#define RCSDESC "desc"
+#define RCSEXPAND "expand"
+
+/* Used by the version of death support which results if you define
+ DEATH_SUPPORT and not DEATH_STATE. Requires a hacked up RCS. Considered
+ obsolete. */
#define RCSDEAD "dead"
+
#define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d"
#define SDATEFORM "%d.%d.%d.%d.%d.%d"
@@ -44,6 +50,7 @@ struct rcsnode
char *head;
char *branch;
char *symbols_data;
+ char *expand;
List *symbols;
List *versions;
List *dates;
@@ -78,9 +85,10 @@ RCSNode *RCS_parse PROTO((const char *file, const char *repos));
RCSNode *RCS_parsercsfile PROTO((char *rcsfile));
char *RCS_check_kflag PROTO((const char *arg));
char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match));
-char *RCS_gettag PROTO((RCSNode * rcs, char *tag, int force_tag_match));
+char *RCS_gettag PROTO((RCSNode * rcs, char *symtag, int force_tag_match,
+ int return_both));
char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date,
- int force_tag_match));
+ int force_tag_match, int return_both));
char *RCS_magicrev PROTO((RCSNode *rcs, char *rev));
int RCS_isbranch PROTO((char *file, char *rev, List *srcfiles));
int RCS_nodeisbranch PROTO((char *rev, RCSNode *rcs));
diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c
index 593cf5c0bba..af32cea1d6d 100644
--- a/gnu/usr.bin/cvs/src/rcscmds.c
+++ b/gnu/usr.bin/cvs/src/rcscmds.c
@@ -28,6 +28,7 @@ int
RCS_deltag(path, tag, noerr)
const char *path;
const char *tag;
+ int noerr;
{
run_setup ("%s%s -q -N%s", Rcsbin, RCS, tag);
run_arg (path);
@@ -64,6 +65,7 @@ int
RCS_unlock(path, rev, noerr)
const char *path;
const char *rev;
+ int noerr;
{
run_setup ("%s%s -q -u%s", Rcsbin, RCS, rev ? rev : "");
run_arg (path);
@@ -80,10 +82,6 @@ RCS_merge(path, options, rev1, rev2)
{
int status;
- /* We pass -E to rcsmerge so that it will not indicate a conflict if
- both things we are merging are modified the same way.
-
- Well, okay, but my rcsmerge doesn't take a -E option. --JimB */
/* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
run_setup ("%s%s %s -r%s -r%s %s", Rcsbin, RCS_RCSMERGE,
@@ -93,10 +91,10 @@ RCS_merge(path, options, rev1, rev2)
if (status == 0)
{
/* Run GREP to see if there appear to be conflicts in the file */
- run_setup ("%s -s", GREP);
+ run_setup ("%s", GREP);
run_arg (RCS_MERGE_PAT);
run_arg (path);
- status = (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) == 0);
+ status = (run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_NORMAL) == 0);
}
#endif
diff --git a/gnu/usr.bin/cvs/src/recurse.c b/gnu/usr.bin/cvs/src/recurse.c
index fdf972f5a9f..d11bdf4a499 100644
--- a/gnu/usr.bin/cvs/src/recurse.c
+++ b/gnu/usr.bin/cvs/src/recurse.c
@@ -10,6 +10,8 @@
#include "cvs.h"
#include "save-cwd.h"
+#include "fileattr.h"
+#include "edit.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)recurse.c 1.31 94/09/30 $";
@@ -26,10 +28,10 @@ static void addfile PROTO((List **listp, char *dir, char *file));
/*
* Local static versions eliminates the need for globals
*/
-static int (*fileproc) ();
-static int (*filesdoneproc) ();
-static Dtype (*direntproc) ();
-static int (*dirleaveproc) ();
+static FILEPROC fileproc;
+static FILESDONEPROC filesdoneproc;
+static DIRENTPROC direntproc;
+static DIRLEAVEPROC dirleaveproc;
static int which;
static Dtype flags;
static int aflag;
@@ -44,10 +46,10 @@ static List *filelist = NULL; /* holds list of files on which to operate */
static List *dirlist = NULL; /* holds list of directories on which to operate */
struct recursion_frame {
- int (*fileproc)();
- int (*filesdoneproc) ();
- Dtype (*direntproc) ();
- int (*dirleaveproc) ();
+ FILEPROC fileproc;
+ FILESDONEPROC filesdoneproc;
+ DIRENTPROC direntproc;
+ DIRLEAVEPROC dirleaveproc;
Dtype flags;
int which;
int aflag;
@@ -68,10 +70,10 @@ int
start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
argc, argv, local, which, aflag, readlock,
update_preload, dosrcs, wd_is_repos)
- int (*fileproc) ();
- int (*filesdoneproc) ();
- Dtype (*direntproc) ();
- int (*dirleaveproc) ();
+ FILEPROC fileproc;
+ FILESDONEPROC filesdoneproc;
+ DIRENTPROC direntproc;
+ DIRLEAVEPROC dirleaveproc;
int argc;
char **argv;
int local;
@@ -213,6 +215,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
/* look for it in the repository. */
repos = Name_Repository (dir, update_dir);
(void) sprintf (tmp, "%s/%s", repos, comp);
+ free (repos);
if (!wrap_name_has (comp, WRAP_TOCVS) && isdir(tmp))
addlist (&dirlist, argv[i]);
@@ -266,10 +269,10 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
int
do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
xflags, xwhich, xaflag, xreadlock, xdosrcs)
- int (*xfileproc) ();
- int (*xfilesdoneproc) ();
- Dtype (*xdirentproc) ();
- int (*xdirleaveproc) ();
+ FILEPROC xfileproc;
+ FILESDONEPROC xfilesdoneproc;
+ DIRENTPROC xdirentproc;
+ DIRLEAVEPROC xdirleaveproc;
Dtype xflags;
int xwhich;
int xaflag;
@@ -295,6 +298,18 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
readlock = noexec ? 0 : xreadlock;
dosrcs = xdosrcs;
+#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL)
+ /*
+ * Now would be a good time to check to see if we need to stop
+ * generating data, to give the buffers a chance to drain to the
+ * remote client. We should not have locks active at this point.
+ */
+ if (server_active
+ /* If there are writelocks around, we cannot pause here. */
+ && (readlock || noexec))
+ server_pause_check();
+#endif
+
/*
* Fill in repository with the current repository
*/
@@ -312,6 +327,8 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
}
srepository = repository; /* remember what to free */
+ fileattr_startdir (repository);
+
/*
* The filesdoneproc needs to be called for each directory where files
* processed, or each directory that is processed by a call where no
@@ -365,6 +382,15 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
if (readlock && repository && Reader_Lock (repository) != 0)
error (1, 0, "read lock failed - giving up");
+#ifdef CLIENT_SUPPORT
+ /* For the server, we handle notifications in a completely different
+ place (server_notify). For local, we can't do them here--we don't
+ have writelocks in place, and there is no way to get writelocks
+ here. */
+ if (client_active)
+ notify_check (repository, update_dir);
+#endif
+
/* pre-parse the source files */
if (dosrcs && repository)
srcfiles = RCS_parsefiles (filelist, repository);
@@ -389,6 +415,9 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
if (dodoneproc && filesdoneproc != NULL)
err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");
+ fileattr_write ();
+ fileattr_free ();
+
/* process the directories (if necessary) */
if (dirlist != NULL)
err += walklist (dirlist, do_dir_proc, NULL);
diff --git a/gnu/usr.bin/cvs/src/release.c b/gnu/usr.bin/cvs/src/release.c
index 987edd0d301..c7cd55c9410 100644
--- a/gnu/usr.bin/cvs/src/release.c
+++ b/gnu/usr.bin/cvs/src/release.c
@@ -27,6 +27,21 @@ static const char *const release_usage[] =
static short delete;
+/* FIXME: This implementation is cheezy in quite a few ways:
+
+ 1. The whole "cvs update" junk could be checked locally with a
+ fairly simple start_recursion/classify_file loop--a win for
+ portability, performance, and cleanliness.
+
+ 2. Should be like edit/unedit in terms of working well if disconnected
+ from the network, and then sending a delayed notification.
+
+ 3. Way too many network turnarounds. More than one for each argument.
+ Puh-leeze.
+
+ 4. Oh, and as a purely stylistic nit, break this out into separate
+ functions for client/local and for server. Those #ifdefs are a mess. */
+
int
release (argc, argv)
int argc;
@@ -38,6 +53,7 @@ release (argc, argv)
char line[PATH_MAX], update_cmd[PATH_MAX];
char *thisarg;
int arg_start_idx;
+ int err = 0;
#ifdef SERVER_SUPPORT
if (!server_active)
@@ -82,7 +98,7 @@ release (argc, argv)
*/
/* Construct the update command. */
sprintf (update_cmd, "%s -n -q -d %s update",
- program_name, CVSroot);
+ program_path, CVSroot);
#ifdef CLIENT_SUPPORT
/* Start the server; we'll close it after looping. */
@@ -100,8 +116,8 @@ release (argc, argv)
if (server_active)
arg_start_idx = 1;
else
- arg_start_idx = 0;
#endif /* SERVER_SUPPORT */
+ arg_start_idx = 0;
for (i = arg_start_idx; i < argc; i++)
{
@@ -157,11 +173,8 @@ release (argc, argv)
* the user, telling her how many files have been
* modified, and asking if she still wants to do the
* release.
- *
- * This is "popen()" instead of "Popen()" since we
- * wouldn't want the `noexec' flag to stop it.
*/
- fp = popen (update_cmd, "r");
+ fp = Popen (update_cmd, "r");
c = 0;
while (fgets (line, sizeof (line), fp))
@@ -196,13 +209,33 @@ release (argc, argv)
}
}
+ if (1
+#ifdef SERVER_SUPPORT
+ && !server_active
+#endif
+#ifdef CLIENT_SUPPORT
+ && !(client_active
+ && (!supported_request ("noop")
+ || !supported_request ("Notify")))
+#endif
+ )
+ {
+ /* We are chdir'ed into the directory in question.
+ So don't pass args to unedit. */
+ int argc = 1;
+ char *argv[3];
+ argv[0] = "dummy";
+ argv[1] = NULL;
+ err += unedit (argc, argv);
+ }
+
#ifdef CLIENT_SUPPORT
if (client_active)
{
- if (fprintf (to_server, "Argument %s\n", thisarg) < 0)
- error (1, errno, "writing to server");
- if (fprintf (to_server, "release\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("Argument ", 0);
+ send_to_server (thisarg, 0);
+ send_to_server ("\012", 1);
+ send_to_server ("release\012", 0);
}
else
{
@@ -226,6 +259,7 @@ release (argc, argv)
} /* else server not active */
#endif /* SERVER_SUPPORT */
} /* `for' loop */
+ return err;
}
@@ -237,7 +271,6 @@ release_delete (dir)
{
struct stat st;
ino_t ino;
- int retcode = 0;
(void) stat (".", &st);
ino = st.st_ino;
@@ -253,9 +286,6 @@ release_delete (dir)
* XXX - shouldn't this just delete the CVS-controlled files and, perhaps,
* the files that would normally be ignored and leave everything else?
*/
- run_setup ("%s -fr", RM);
- run_arg (dir);
- if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
- error (0, retcode == -1 ? errno : 0,
- "deletion of module %s failed.", dir);
+ if (unlink_file_dir (dir) < 0)
+ error (0, errno, "deletion of directory %s failed", dir);
}
diff --git a/gnu/usr.bin/cvs/src/remove.c b/gnu/usr.bin/cvs/src/remove.c
index 95140d66ea0..ee3d81f5fda 100644
--- a/gnu/usr.bin/cvs/src/remove.c
+++ b/gnu/usr.bin/cvs/src/remove.c
@@ -82,17 +82,17 @@ cvsremove (argc, argv)
ign_setup ();
if (local)
send_arg("-l");
+ send_file_names (argc, argv);
send_files (argc, argv, local, 0);
- if (fprintf (to_server, "remove\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("remove\012", 0);
return get_responses_and_close ();
}
#endif
/* start the recursion processor */
- err = start_recursion (remove_fileproc, (int (*) ()) NULL, remove_dirproc,
- (int (*) ()) NULL, argc, argv, local,
- W_LOCAL, 0, 1, (char *) NULL, 1, 0);
+ err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL,
+ remove_dirproc, (DIRLEAVEPROC) NULL, argc, argv,
+ local, W_LOCAL, 0, 1, (char *) NULL, 1, 0);
if (removed_files)
error (0, 0, "use '%s commit' to remove %s permanently", program_name,
@@ -123,12 +123,22 @@ remove_fileproc (file, update_dir, repository, entries, srcfiles)
char fname[PATH_MAX];
Vers_TS *vers;
- /*
- * If unlinking the file works, good. If not, the "unremoved"
- * error will indicate problems.
- */
if (force)
- (void) unlink (file);
+ {
+ if (!noexec)
+ {
+ if (unlink (file) < 0 && ! existence_error (errno))
+ {
+ if (update_dir[0] == '\0')
+ error (0, errno, "unable to remove %s", file);
+ else
+ error (0, errno, "unable to remove %s/%s", update_dir,
+ file);
+ }
+ }
+ /* else FIXME should probably act as if the file doesn't exist
+ in doing the following checks. */
+ }
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
file, 0, 0, entries, srcfiles);
@@ -148,12 +158,9 @@ remove_fileproc (file, update_dir, repository, entries, srcfiles)
{
/*
* It's a file that has been added, but not commited yet. So,
- * remove the ,p and ,t file for it and scratch it from the
- * entries file.
- */
+ * remove the ,t file for it and scratch it from the
+ * entries file. */
Scratch_Entry (entries, file);
- (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
- (void) unlink_file (fname);
(void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
(void) unlink_file (fname);
if (!quiet)
diff --git a/gnu/usr.bin/cvs/src/root.c b/gnu/usr.bin/cvs/src/root.c
index 8b7c83807cb..3129c3b6610 100644
--- a/gnu/usr.bin/cvs/src/root.c
+++ b/gnu/usr.bin/cvs/src/root.c
@@ -50,7 +50,7 @@ Name_Root(dir, update_dir)
* Do not bother looking for a readable file if there is no cvsadm
* directory present.
*
- * It is possiible that not all repositories will have a CVS/Root
+ * It is possible that not all repositories will have a CVS/Root
* file. This is ok, but the user will need to specify -d
* /path/name or have the environment variable CVSROOT set in
* order to continue.
@@ -92,9 +92,9 @@ Name_Root(dir, update_dir)
/* It must specify a server via remote CVS or be an absolute pathname. */
if ((strchr (root, ':') == NULL)
&& ! isabsolute (root))
-#else
+#else /* ! CLIENT_SUPPORT */
if (root[0] != '/')
-#endif
+#endif /* CLIENT_SUPPORT */
{
error (0, 0, "in directory %s:", xupdate_dir);
error (0, 0,
@@ -105,9 +105,9 @@ Name_Root(dir, update_dir)
#ifdef CLIENT_SUPPORT
if ((strchr (root, ':') == NULL) && !isdir (root))
-#else
+#else /* ! CLIENT_SUPPORT */
if (!isdir (root))
-#endif
+#endif /* CLIENT_SUPPORT */
{
error (0, 0, "in directory %s:", xupdate_dir);
error (0, 0,
@@ -162,6 +162,9 @@ Create_Root (dir, rootdir)
FILE *fout;
char tmp[PATH_MAX];
+ if (noexec)
+ return;
+
/* record the current cvs root */
if (rootdir != NULL)
diff --git a/gnu/usr.bin/cvs/src/rtag.c b/gnu/usr.bin/cvs/src/rtag.c
index 238ff7db595..bf476297c51 100644
--- a/gnu/usr.bin/cvs/src/rtag.c
+++ b/gnu/usr.bin/cvs/src/rtag.c
@@ -55,6 +55,7 @@ static List *tlist;
static char *symtag;
static char *numtag;
+static int numtag_validated = 0;
static int delete; /* adding a tag by default */
static int attic_too; /* remove tag from Attic files */
static int branch_mode; /* make an automagic "branch" tag */
@@ -175,7 +176,7 @@ rtag (argc, argv)
if (branch_mode)
send_arg("-b");
if (force_tag_move)
- send_arg("-T");
+ send_arg("-F");
if (run_module_prog)
send_arg("-n");
if (attic_too)
@@ -194,8 +195,7 @@ rtag (argc, argv)
send_arg (argv[i]);
}
- if (fprintf (to_server, "rtag\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("rtag\012", 0);
return get_responses_and_close ();
}
#endif
@@ -289,12 +289,18 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
else
which = W_REPOS;
+ if (numtag != NULL && !numtag_validated)
+ {
+ tag_check_valid (numtag, *pargc - 1, argv + 1, local, 0, NULL);
+ numtag_validated = 1;
+ }
+
/* 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,
- (Dtype (*) ()) NULL, (int (*) ()) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
*pargc - 1, argv + 1, local, which, 0, 1,
where, 1, 1);
@@ -304,8 +310,8 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
}
/* start the recursion processor */
- err = start_recursion (rtag_fileproc, (int (*) ()) NULL, rtag_dirproc,
- (int (*) ()) NULL, *pargc - 1, argv + 1, local,
+ err = start_recursion (rtag_fileproc, (FILESDONEPROC) NULL, rtag_dirproc,
+ (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local,
which, 0, 1, where, 1, 1);
dellist(&mtlist);
@@ -358,13 +364,13 @@ check_fileproc(file, update_dir, repository, entries, srcfiles)
p->delproc = tag_delproc;
vers = Version_TS (repository, (char *) NULL, (char *) NULL,
(char *) NULL, file, 0, 0, entries, srcfiles);
- p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match);
+ p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
if (p->data != NULL)
{
int addit = 1;
char *oversion;
- oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
if (oversion == NULL)
{
if (delete)
@@ -541,7 +547,7 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles)
return (rtag_delete (rcsfile));
}
- version = RCS_getversion (rcsfile, numtag, date, force_tag_match);
+ version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 0);
if (version == NULL)
{
/* If -a specified, clean up any old tags */
@@ -584,7 +590,7 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles)
* typical tagging operation.
*/
rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
- oversion = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
+ oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
if (oversion != NULL)
{
int isbranch = RCS_isbranch (file, symtag, srcfiles);
@@ -651,13 +657,13 @@ rtag_delete (rcsfile)
if (numtag)
{
- version = RCS_getversion (rcsfile, numtag, (char *) 0, 1);
+ version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0);
if (version == NULL)
return (0);
free (version);
}
- version = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
+ version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
if (version == NULL)
return (0);
free (version);
diff --git a/gnu/usr.bin/cvs/src/run.c b/gnu/usr.bin/cvs/src/run.c
index ebc26ae42d8..0909878267a 100644
--- a/gnu/usr.bin/cvs/src/run.c
+++ b/gnu/usr.bin/cvs/src/run.c
@@ -18,10 +18,6 @@
#include "cvs.h"
-#ifdef _MINIX
-#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */
-#endif
-
#ifdef HAVE_VPRINTF
#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
#include <stdarg.h>
@@ -179,7 +175,7 @@ run_exec (stin, stout, sterr, flags)
int rerrno = 0;
int pid, w;
-#ifdef POSIX
+#ifdef POSIX_SIGNALS
sigset_t sigset_mask, sigset_omask;
struct sigaction act, iact, qact;
@@ -286,7 +282,7 @@ run_exec (stin, stout, sterr, flags)
}
/* the parent. Ignore some signals for now */
-#ifdef POSIX
+#ifdef POSIX_SIGNALS
if (flags & RUN_SIGIGNORE)
{
act.sa_handler = SIG_IGN;
@@ -320,7 +316,7 @@ run_exec (stin, stout, sterr, flags)
#endif
/* wait for our process to die and munge return status */
-#ifdef POSIX
+#ifdef POSIX_SIGNALS
while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
;
#else
@@ -347,7 +343,7 @@ run_exec (stin, stout, sterr, flags)
rc = 1;
/* restore the signals */
-#ifdef POSIX
+#ifdef POSIX_SIGNALS
if (flags & RUN_SIGIGNORE)
{
(void) sigaction (SIGINT, &iact, (struct sigaction *) NULL);
@@ -477,6 +473,9 @@ close_on_exec (fd)
/*
* dir = 0 : main proc writes to new proc, which writes to oldfd
* dir = 1 : main proc reads from new proc, which reads from oldfd
+ *
+ * Returns: a file descriptor. On failure (i.e., the exec fails),
+ * then filter_stream_through_program() complains and dies.
*/
int
diff --git a/gnu/usr.bin/cvs/src/sanity.sh b/gnu/usr.bin/cvs/src/sanity.sh
index 1d6ea42ed3d..a47632de71a 100644
--- a/gnu/usr.bin/cvs/src/sanity.sh
+++ b/gnu/usr.bin/cvs/src/sanity.sh
@@ -1,76 +1,271 @@
-#!/bin/sh
-# a quick sanity test for cvs.
+#! /bin/sh
+:
+# sanity.sh -- a growing sanity test for cvs.
+#
+#ident "$CVSid$"
#
# Copyright (C) 1992, 1993 Cygnus Support
#
# Original Author: K. Richard Pixley
-# usage: sanity.sh [-r] @var{cvs-to-test}
+# usage: sanity.sh [-r] @var{cvs-to-test} @var{tests-to-run}
# -r means to test remote instead of local cvs.
+# @var{tests-to-run} are the names of the tests to run; if omitted run all
+# tests.
# See TODO list at end of file.
+# required to make this script work properly.
+unset CVSREAD
+
TESTDIR=/tmp/cvs-sanity
# "debugger"
#set -x
-echo This test should produce no other output than this line, and "Ok."
-
-# clean any old remnants
-rm -rf ${TESTDIR}
+echo 'This test should produce no other output than this line, and a final "OK".'
if test x"$1" = x"-r"; then
- shift
- remote=yes
+ shift
+ remote=yes
else
- remote=no
+ remote=no
fi
-testcvs=$1; shift
+# Use full path for CVS executable, so that CVS_SERVER gets set properly
+# for remote.
+case $1 in
+/*)
+ testcvs=$1
+ ;;
+*)
+ testcvs=`pwd`/$1
+ ;;
+esac
-# Remaining arguments are the names of tests to run.
-if test x"$*" = x; then
- tests="basic0 basic1 basic2 basic3 rtags death import new conflicts modules mflag errmsg1"
-else
- tests="$*"
-fi
+shift
+
+# Use full path for mkmodules, so that the right one will be invoked
+#
+testmkmodules=`pwd`/mkmodules
-# fixme: try things (what things? checkins?) without -m.
+# 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}.
-CVS="${testcvs} -Q"
+# and then they've obviously got to do something with the output....
+#
+CVS="${testcvs} -Q -f"
LOGFILE=`pwd`/check.log
-if test -f check.log; then mv check.log check.plog; fi
+# 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.
+if test -f check.log; then
+ mv check.log check.plog
+fi
+
+# That we should have to do this is total bogosity, but GNU expr
+# version 1.9.4 uses the emacs definition of "$" instead of the unix
+# (e.g. SunOS 4.1.3 expr) one. IMHO, this is a GNU expr bug, but I
+# don't have a copy of POSIX.2 handy to check.
+ENDANCHOR="$"
+if expr 'abc
+def' : 'abc$' >/dev/null; then
+ ENDANCHOR='\'\'
+fi
+
+# Work around another GNU expr (version 1.10) 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.
+DOTSTAR='.*'
+if expr 'abc
+def' : "a${DOTSTAR}f" >/dev/null; then
+ : good, it works
+else
+ DOTSTAR='\(.\|
+\)*'
+fi
+
+# Cause NextStep 3.3 users to lose in a more graceful fashion.
+if expr 'abc
+def' : 'abc
+def' >/dev/null; then
+ : good, it works
+else
+ 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.'
+ exit 1
+fi
+
+pass ()
+{
+ echo "PASS: $1" >>${LOGFILE}
+}
+
+fail ()
+{
+ echo "FAIL: $1" | tee -a ${LOGFILE}
+ # This way the tester can go and see what remnants were left
+ exit 1
+}
+
+# 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 ()
+{
+ if $2 >${TESTDIR}/dotest.tmp 2>&1; then
+ : so far so good
+ else
+ status=$?
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ echo "exit status was $status" >>${LOGFILE}
+ fail "$1"
+ fi
+ # expr can't distinguish between "zero characters matched" and "no match",
+ # so special-case it.
+ if test -z "$3"; then
+ if test -s ${TESTDIR}/dotest.tmp; then
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ else
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ fi
+ else
+ if expr "`cat ${TESTDIR}/dotest.tmp`" : \
+ "$3"${ENDANCHOR} >/dev/null; then
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ else
+ if test x"$4" != x; then
+ if expr "`cat ${TESTDIR}/dotest.tmp`" : \
+ "$4"${ENDANCHOR} >/dev/null; then
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ else
+ 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
+}
+
+# Like dotest except exitstatus should be nonzero. Probably their
+# implementations could be unified (if I were a good enough sh script
+# writer to get the quoting right).
+dotest_fail ()
+{
+ if $2 >${TESTDIR}/dotest.tmp 2>&1; then
+ status=$?
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ echo "exit status was $status" >>${LOGFILE}
+ fail "$1"
+ else
+ : so far so good
+ fi
+ # expr can't distinguish between "zero characters matched" and "no match",
+ # so special-case it.
+ if test -z "$3"; then
+ if test -s ${TESTDIR}/dotest.tmp; then
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ else
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ fi
+ else
+ if expr "`cat ${TESTDIR}/dotest.tmp`" : \
+ ${STARTANCHOR}"$3"${ENDANCHOR} >/dev/null; then
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ pass "$1"
+ else
+ echo "** expected: " >>${LOGFILE}
+ echo "$3" >>${LOGFILE}
+ echo "** got: " >>${LOGFILE}
+ cat ${TESTDIR}/dotest.tmp >>${LOGFILE}
+ fail "$1"
+ fi
+ fi
+}
+
+# clean any old remnants
+rm -rf ${TESTDIR}
mkdir ${TESTDIR}
cd ${TESTDIR}
-# so far so good. Let's try something harder.
+# Remaining arguments are the names of tests to run.
+#
+# FIXME: not all combinations are possible; rtags depends on files set
+# up by basic2, for example. This should be changed. The goal is
+# that tests can be run in manageably-sized chunks, 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
+ tests="basica basic0 basic1 basic2 rtags death import new conflicts modules mflag errmsg1 devcom ignore binfiles"
+else
+ tests="$*"
+fi
# this should die
if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then
- echo "FAIL: test 1" | tee -a ${LOGFILE}; exit 1
+ echo "FAIL: test 1" | tee -a ${LOGFILE}
+ exit 1
else
- echo "PASS: test 1" >>${LOGFILE}
+ echo "PASS: test 1" >>${LOGFILE}
fi
# this should still die
mkdir cvsroot
if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then
- echo "FAIL: test 2" | tee -a ${LOGFILE}; exit 1
+ echo "FAIL: test 2" | tee -a ${LOGFILE}
+ exit 1
else
- echo "PASS: test 2" >>${LOGFILE}
+ echo "PASS: test 2" >>${LOGFILE}
fi
# this should still die
mkdir cvsroot/CVSROOT
if ${CVS} -d `pwd`/cvsroot co cvs-sanity 2>> ${LOGFILE} ; then
- echo "FAIL: test 3" | tee -a ${LOGFILE}; exit 1
+ echo "FAIL: test 3" | tee -a ${LOGFILE}
+ exit 1
else
- echo "PASS: test 3" >>${LOGFILE}
+ echo "PASS: test 3" >>${LOGFILE}
fi
# This one should work, although it should spit a warning.
@@ -78,44 +273,139 @@ mkdir tmp ; cd tmp
${CVS} -d `pwd`/../cvsroot co CVSROOT 2>> ${LOGFILE}
cd .. ; rm -rf tmp
+# set up a minimal modules file...
+echo "CVSROOT -i ${testmkmodules} CVSROOT" > cvsroot/CVSROOT/modules
+# The following line stolen from cvsinit.sh. FIXME: create our
+# repository via cvsinit.sh; that way we test it too.
+(cd cvsroot/CVSROOT; ci -q -u -t/dev/null \
+ -m'initial checkin of modules' modules)
+
# This one should succeed. No warnings.
-echo 'CVSROOT -i mkmodules CVSROOT' > cvsroot/CVSROOT/modules
mkdir tmp ; cd tmp
if ${CVS} -d `pwd`/../cvsroot co CVSROOT ; then
- echo "PASS: test 4" >>${LOGFILE}
+ echo "PASS: test 4" >>${LOGFILE}
else
- echo "FAIL: test 4" | tee -a ${LOGFILE}; exit 1
+ echo "FAIL: test 4" | tee -a ${LOGFILE}
+ exit 1
fi
-cd .. ; rm -rf tmp
+if echo "yes" | ${CVS} -d `pwd`/../cvsroot release -d CVSROOT ; then
+ echo "PASS: test 4.5" >>${LOGFILE}
+else
+ echo "FAIL: test 4.5" | tee -a ${LOGFILE}
+ exit 1
+fi
+# this had better be empty
+cd ..; rmdir tmp
+dotest_fail 4.75 "test -d tmp" ''
+
+# a simple function to compare directory contents
+#
+# BTW, I don't care any more -- if you don't have a /bin/sh that handles
+# shell functions, well get one.
+#
+# Returns: ISDIFF := true|false
+#
+directory_cmp ()
+{
+ OLDPWD=`pwd`
+ DIR_1=$1
+ DIR_2=$2
+ ISDIFF=false
+
+ cd $DIR_1
+ find . -print | fgrep -v /CVS | sort > /tmp/dc$$d1
+
+ # go back where we were to avoid symlink hell...
+ cd $OLDPWD
+ cd $DIR_2
+ find . -print | fgrep -v /CVS | sort > /tmp/dc$$d2
+
+ if diff /tmp/dc$$d1 /tmp/dc$$d2 >/dev/null 2>&1
+ then
+ :
+ else
+ ISDIFF=true
+ return
+ fi
+ cd $OLDPWD
+ while read a
+ do
+ if [ -f $DIR_1/"$a" ] ; then
+ cmp -s $DIR_1/"$a" $DIR_2/"$a"
+ if [ $? -ne 0 ] ; then
+ ISDIFF=true
+ fi
+ fi
+ done < /tmp/dc$$d1
+### FIXME:
+### rm -f /tmp/dc$$*
+}
+
+# so much for the setup. Let's try something harder.
# Try setting CVSROOT so we don't have to worry about it anymore. (now that
# we've tested -d cvsroot.)
-CVSROOT_FILENAME=`pwd`/cvsroot
-CVSROOT=${CVSROOT_FILENAME} ; export CVSROOT
+CVSROOT_DIRNAME=${TESTDIR}/cvsroot
+CVSROOT=${CVSROOT_DIRNAME} ; export CVSROOT
if test "x$remote" = xyes; then
- CVSROOT=`hostname`:${CVSROOT_FILENAME} ; export CVSROOT
- # Use rsh so we can test it without having to muck with inetd or anything
- # like that. Also needed to get CVS_SERVER to work.
- CVS_CLIENT_PORT=-1; export CVS_CLIENT_PORT
- CVS_SERVER=${testcvs}; export CVS_SERVER
-fi
-
-mkdir tmp ; cd tmp
-if ${CVS} -d `pwd`/../cvsroot co CVSROOT ; then
- echo "PASS: test 5" >>${LOGFILE}
-else
- echo "FAIL: test 5" | tee -a ${LOGFILE}; exit 1
+ CVSROOT=`hostname`:${CVSROOT_DIRNAME} ; export CVSROOT
+ # Use rsh so we can test it without having to muck with inetd or anything
+ # like that. Also needed to get CVS_SERVER to work.
+ CVS_CLIENT_PORT=-1; export CVS_CLIENT_PORT
+ CVS_SERVER=${testcvs}; export CVS_SERVER
fi
-cd .. ; rm -rf tmp
-
# start keeping history
-touch ${CVSROOT_FILENAME}/CVSROOT/history
+touch ${CVSROOT_DIRNAME}/CVSROOT/history
### The big loop
for what in $tests; do
case $what in
+ basica)
+ # Similar in spirit to some of the basic0, basic1, and basic2
+ # tests, but hopefully a lot faster. Also tests operating on
+ # files two directories down *without* operating on the parent dirs.
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ dotest basica-1 "${testcvs} -q co first-dir" ''
+ cd first-dir
+ mkdir sdir
+ dotest basica-2 "${testcvs} add sdir" \
+'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir added to the repository'
+ cd sdir
+ mkdir ssdir
+ dotest basica-3 "${testcvs} add ssdir" \
+'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir added to the repository'
+ cd ssdir
+ echo ssfile >ssfile
+ dotest basica-4 "${testcvs} add ssfile" \
+'cvs [a-z]*: scheduling file `ssfile'\'' for addition
+cvs [a-z]*: use '\''cvs commit'\'' to add this file permanently'
+ cd ../..
+ dotest basica-5 "${testcvs} -q ci -m add-it" \
+'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v
+done
+Checking in sdir/ssdir/ssfile;
+/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+initial revision: 1.1
+done'
+ dotest basica-6 "${testcvs} -q update" ''
+ echo "ssfile line 2" >>sdir/ssdir/ssfile
+ dotest basica-7 "${testcvs} -q ci -m modify-it" \
+'Checking in sdir/ssdir/ssfile;
+/tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile
+new revision: 1.2; previous revision: 1.1
+done'
+ dotest_fail basica-nonexist "${testcvs} -q ci nonexist" \
+'cvs [a-z]*: nothing known about `nonexist'\''
+cvs \[[a-z]* aborted\]: correct above errors first!'
+ dotest basica-8 "${testcvs} -q update" ''
+ cd ..
+
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
+ rm -r first-dir
+ ;;
+
basic0) # Now, let's build something.
# mkdir first-dir
# this doesn't yet work, though I think maybe it should. xoxorich.
@@ -123,13 +413,17 @@ for what in $tests; do
# true
# else
# echo cvs does not yet add top level directories cleanly.
- mkdir ${CVSROOT_FILENAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
# fi
# rm -rf first-dir
# check out an empty directory
if ${CVS} co first-dir ; then
- echo "PASS: test 6" >>${LOGFILE}
+ if [ -r first-dir/CVS/Entries ] ; then
+ echo "PASS: test 6" >>${LOGFILE}
+ else
+ echo "FAIL: test 6" | tee -a ${LOGFILE}; exit 1
+ fi
else
echo "FAIL: test 6" | tee -a ${LOGFILE}; exit 1
fi
@@ -185,9 +479,9 @@ for what in $tests; do
;;
basic1) # first dive - add a files, first singly, then in a group.
- rm -rf ${CVSROOT_FILENAME}/first-dir
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
rm -rf first-dir
- mkdir ${CVSROOT_FILENAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
# check out an empty directory
if ${CVS} co first-dir ; then
echo "PASS: test 13a" >>${LOGFILE}
@@ -232,7 +526,7 @@ for what in $tests; do
echo "FAIL: test 17-${do}-$j" | tee -a ${LOGFILE}; exit 1
fi
- # fixme: this one doesn't work yet for added files.
+ # FIXME: this one doesn't work yet for added files.
# log all.
if ${CVS} log >> ${LOGFILE}; then
echo "PASS: test 18-${do}-$j" >>${LOGFILE}
@@ -267,7 +561,7 @@ for what in $tests; do
fi
# log all.
- # fixme: doesn't work right for added files.
+ # FIXME: doesn't work right for added files.
if ${CVS} log first-dir >> ${LOGFILE}; then
echo "PASS: test 22-${do}-$j" >>${LOGFILE}
else
@@ -283,34 +577,36 @@ for what in $tests; do
# update all.
if ${CVS} update first-dir ; then
- true
+ echo "PASS: test 24-${do}-$j" >>${LOGFILE}
else
- echo '***' failed test 24-${do}-$j. ; exit 1
+ echo "FAIL: test 24-${do}-$j" | tee -a ${LOGFILE} ; exit 1
fi
if test "x${do}-$j" = "xadd-add" || test "x${do}-$j" = "xrm-rm" ; then
- true
+ echo "PASS: test 25-${do}-$j" >>${LOGFILE}
else
# diff all
if ${CVS} diff -u >> ${LOGFILE} || [ $? = 1 ] ; then
- true
+ echo "PASS: test 25-${do}-$j" >>${LOGFILE}
else
- echo '***' failed test 25-${do}-$j. # FIXME; exit 1
+ echo "FAIL: test 25-${do}-$j" | tee -a ${LOGFILE}
+ # FIXME; exit 1
fi
# diff all
if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then
- true
+ echo "PASS: test 26-${do}-$j" >>${LOGFILE}
else
- echo '***' failed test 26-${do}-$j. # FIXME; exit 1
+ echo "FAIL: test 26-${do}-$j" | tee -a ${LOGFILE}
+ # FIXME; exit 1
fi
fi
# update all.
if ${CVS} co first-dir ; then
- true
+ echo "PASS: test 27-${do}-$j" >>${LOGFILE}
else
- echo '***' failed test 27-${do}-$j. ; exit 1
+ echo "FAIL: test 27-${do}-$j" | tee -a ${LOGFILE} ; exit 1
fi
cd first-dir
@@ -321,21 +617,27 @@ for what in $tests; do
files="file2 file3 file4 file5"
done
if ${CVS} tag first-dive ; then
- true
+ echo "PASS: test 28" >>${LOGFILE}
else
- echo '***' failed test 28. ; exit 1
+ echo "FAIL: test 28" | tee -a ${LOGFILE} ; exit 1
fi
cd ..
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
+ rm -rf first-dir
;;
- basic2) # second dive - add bunch o' files in bunch o' added directories
+ basic2)
+ # second dive - add bunch o' files in bunch o' added
+ # directories
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ dotest basic2-1 "${testcvs} -q co first-dir" ''
for i in first-dir dir1 dir2 dir3 dir4 ; do
if [ ! -d $i ] ; then
mkdir $i
if ${CVS} add $i >> ${LOGFILE}; then
- true
+ echo "PASS: test 29-$i" >>${LOGFILE}
else
- echo '***' failed test 29-$i. ; exit 1
+ echo "FAIL: test 29-$i" | tee -a ${LOGFILE} ; exit 1
fi
fi
@@ -346,51 +648,52 @@ for what in $tests; do
done
if ${CVS} add file6 file7 file8 file9 file10 file11 file12 file13 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 30-$i-$j" >>${LOGFILE}
else
- echo '***' failed test 30-$i-$j. ; exit 1
+ echo "FAIL: test 30-$i-$j" | tee -a ${LOGFILE} ; exit 1
fi
done
cd ../../../../..
if ${CVS} update first-dir ; then
- true
+ echo "PASS: test 31" >>${LOGFILE}
else
- echo '***' failed test 31. ; exit 1
+ echo "FAIL: test 31" | tee -a ${LOGFILE} ; exit 1
fi
# fixme: doesn't work right for added files.
if ${CVS} log first-dir >> ${LOGFILE}; then
- true
+ echo "PASS: test 32" >>${LOGFILE}
else
- echo '***' failed test 32. # ; exit 1
+ echo "FAIL: test 32" | tee -a ${LOGFILE} # ; exit 1
fi
if ${CVS} status first-dir >> ${LOGFILE}; then
- true
+ echo "PASS: test 33" >>${LOGFILE}
else
- echo '***' failed test 33. ; exit 1
+ echo "FAIL: test 33" | tee -a ${LOGFILE} ; exit 1
fi
# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then
-# true
+# echo "PASS: test 34" >>${LOGFILE}
# else
-# echo '***' failed test 34. # ; exit 1
+# echo "FAIL: test 34" | tee -a ${LOGFILE} # ; exit 1
# fi
if ${CVS} ci -m "second dive" first-dir >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 35" >>${LOGFILE}
else
- echo '***' failed test 35. ; exit 1
+ echo "FAIL: test 35" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} tag second-dive first-dir ; then
- true
+ echo "PASS: test 36" >>${LOGFILE}
else
- echo '***' failed test 36. ; exit 1
+ echo "FAIL: test 36" | tee -a ${LOGFILE} ; exit 1
fi
- ;;
- basic3) # third dive - in bunch o' directories, add bunch o' files, delete some, change some.
+ # third dive - in bunch o' directories, add bunch o' files,
+ # delete some, change some.
+
for i in first-dir dir1 dir2 dir3 dir4 ; do
cd $i
@@ -403,9 +706,9 @@ for what in $tests; do
rm file7 file9 file11 file13
if ${CVS} rm file7 file9 file11 file13 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 37-$i" >>${LOGFILE}
else
- echo '***' failed test 37-$i. ; exit 1
+ echo "FAIL: test 37-$i" | tee -a ${LOGFILE} ; exit 1
fi
# and add some new ones
@@ -414,336 +717,403 @@ for what in $tests; do
done
if ${CVS} add file14 file15 file16 file17 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 38-$i" >>${LOGFILE}
else
- echo '***' failed test 38-$i. ; exit 1
+ echo "FAIL: test 38-$i" | tee -a ${LOGFILE} ; exit 1
fi
done
cd ../../../../..
if ${CVS} update first-dir ; then
- true
+ echo "PASS: test 39" >>${LOGFILE}
else
- echo '***' failed test 39. ; exit 1
+ echo "FAIL: test 39" | tee -a ${LOGFILE} ; exit 1
fi
# fixme: doesn't work right for added files
if ${CVS} log first-dir >> ${LOGFILE}; then
- true
+ echo "PASS: test 40" >>${LOGFILE}
else
- echo '***' failed test 40. # ; exit 1
+ echo "FAIL: test 40" | tee -a ${LOGFILE} # ; exit 1
fi
if ${CVS} status first-dir >> ${LOGFILE}; then
- true
+ echo "PASS: test 41" >>${LOGFILE}
else
- echo '***' failed test 41. ; exit 1
+ echo "FAIL: test 41" | tee -a ${LOGFILE} ; exit 1
fi
# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then
-# true
+# echo "PASS: test 42" >>${LOGFILE}
# else
-# echo '***' failed test 42. # ; exit 1
+# echo "FAIL: test 42" | tee -a ${LOGFILE} # ; exit 1
# fi
if ${CVS} ci -m "third dive" first-dir >>${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 43" >>${LOGFILE}
else
- echo '***' failed test 43. ; exit 1
+ echo "FAIL: test 43" | tee -a ${LOGFILE} ; exit 1
fi
+ dotest 43.5 "${testcvs} -q update first-dir" ''
if ${CVS} tag third-dive first-dir ; then
- true
+ echo "PASS: test 44" >>${LOGFILE}
else
- echo '***' failed test 44. ; exit 1
+ echo "FAIL: test 44" | tee -a ${LOGFILE} ; exit 1
fi
- # Hmm... fixme.
-# if ${CVS} release first-dir ; then
-# true
-# else
-# echo '***' failed test 45. # ; exit 1
-# fi
+ if echo "yes" | ${CVS} release -d first-dir ; then
+ echo "PASS: test 45" >>${LOGFILE}
+ else
+ echo "FAIL: test 45" | tee -a ${LOGFILE} ; exit 1
+ fi
# end of third dive
- rm -rf first-dir
+ if [ -d first-dir ] ; then
+ echo "FAIL: test 45.5" | tee -a ${LOGFILE} ; exit 1
+ else
+ echo "PASS: test 45.5" >>${LOGFILE}
+ fi
+
;;
rtags) # now try some rtags
# rtag HEADS
if ${CVS} rtag rtagged-by-head first-dir ; then
- true
+ echo "PASS: test 46" >>${LOGFILE}
else
- echo '***' failed test 46. ; exit 1
+ echo "FAIL: test 46" | tee -a ${LOGFILE} ; exit 1
fi
# tag by tag
if ${CVS} rtag -r rtagged-by-head rtagged-by-tag first-dir ; then
- true
+ echo "PASS: test 47" >>${LOGFILE}
else
- echo '***' failed test 47. ; exit 1
+ echo "FAIL: test 47" | tee -a ${LOGFILE} ; exit 1
fi
# tag by revision
if ${CVS} rtag -r1.1 rtagged-by-revision first-dir ; then
- true
+ echo "PASS: test 48" >>${LOGFILE}
else
- echo '***' failed test 48. ; exit 1
+ echo "FAIL: test 48" | tee -a ${LOGFILE} ; exit 1
fi
# rdiff by revision
if ${CVS} rdiff -r1.1 -rrtagged-by-head first-dir >> ${LOGFILE} || [ $? = 1 ] ; then
- true
+ echo "PASS: test 49" >>${LOGFILE}
else
- echo '***' failed test 49. ; exit 1
+ echo "FAIL: test 49" | tee -a ${LOGFILE} ; exit 1
fi
# now export by rtagged-by-head and rtagged-by-tag and compare.
rm -rf first-dir
if ${CVS} export -r rtagged-by-head first-dir ; then
- true
+ echo "PASS: test 50" >>${LOGFILE}
else
- echo '***' failed test 50. ; exit 1
+ echo "FAIL: test 50" | tee -a ${LOGFILE} ; exit 1
fi
mv first-dir 1dir
if ${CVS} export -r rtagged-by-tag first-dir ; then
- true
+ echo "PASS: test 51" >>${LOGFILE}
else
- echo '***' failed test 51. ; exit 1
+ echo "FAIL: test 51" | tee -a ${LOGFILE} ; exit 1
fi
- if diff -c -r 1dir first-dir ; then
- true
+ directory_cmp 1dir first-dir
+
+ if $ISDIFF ; then
+ echo "FAIL: test 52" | tee -a ${LOGFILE} ; exit 1
else
- echo '***' failed test 52. ; exit 1
+ echo "PASS: test 52" >>${LOGFILE}
fi
rm -rf 1dir first-dir
- # For some reason, this command has stopped working and hence much of this sequence is currently off.
- # export by revision vs checkout by rtagged-by-revision and compare.
-# if ${CVS} export -r1.1 first-dir ; then
-# true
-# else
-# echo '***' failed test 53. # ; exit 1
-# fi
- # note sidestep below
- #mv first-dir 1dir
+ # checkout by revision vs export by rtagged-by-revision and compare.
+ if ${CVS} export -rrtagged-by-revision -d export-dir first-dir ; then
+ echo "PASS: test 53" >>${LOGFILE}
+ else
+ echo "FAIL: test 53" | tee -a ${LOGFILE} ; exit 1
+ fi
- if ${CVS} co -rrtagged-by-revision first-dir ; then
- true
+ if ${CVS} co -r1.1 first-dir ; then
+ echo "PASS: test 54" >>${LOGFILE}
else
- echo '***' failed test 54. ; exit 1
+ echo "FAIL: test 54" | tee -a ${LOGFILE} ; exit 1
fi
- # fixme: this is here temporarily to sidestep test 53.
- ln -s first-dir 1dir
# 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 -))
- if diff --exclude=CVS -c -r 1dir first-dir ; then
- true
+ directory_cmp first-dir export-dir
+
+ if $ISDIFF ; then
+ echo "FAIL: test 55" | tee -a ${LOGFILE} ; exit 1
else
- echo '***' failed test 55. ; exit 1
+ echo "PASS: test 55" >>${LOGFILE}
fi
# interrupt, while we've got a clean 1.1 here, let's import it into another tree.
- cd 1dir
+ cd export-dir
if ${CVS} import -m "first-import" second-dir first-immigration immigration1 immigration1_0 ; then
- true
+ echo "PASS: test 56" >>${LOGFILE}
else
- echo '***' failed test 56. ; exit 1
+ echo "FAIL: test 56" | tee -a ${LOGFILE} ; exit 1
fi
cd ..
if ${CVS} export -r HEAD second-dir ; then
- true
+ echo "PASS: test 57" >>${LOGFILE}
else
- echo '***' failed test 57. ; exit 1
+ echo "FAIL: test 57" | tee -a ${LOGFILE} ; exit 1
fi
- if diff --exclude=CVS -c -r first-dir second-dir ; then
- true
+ directory_cmp first-dir second-dir
+
+ if $ISDIFF ; then
+ echo "FAIL: test 58" | tee -a ${LOGFILE} ; exit 1
else
- echo '***' failed test 58. ; exit 1
+ echo "PASS: test 58" >>${LOGFILE}
fi
- rm -rf 1dir first-dir
+ rm -rf second-dir
+ rm -rf 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
if ${CVS} update -A -l *file* 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 59" >>${LOGFILE}
else
- echo '***' failed test 59. ; exit 1
+ echo "FAIL: test 59" | tee -a ${LOGFILE} ; exit 1
fi
# If we don't delete the tag first, cvs won't retag it.
# This would appear to be a feature.
if ${CVS} tag -l -d rtagged-by-revision ; then
- true
+ echo "PASS: test 60a" >>${LOGFILE}
else
- echo '***' failed test 60a. ; exit 1
+ echo "FAIL: test 60a" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} tag -l rtagged-by-revision ; then
- true
+ echo "PASS: test 60b" >>${LOGFILE}
else
- echo '***' failed test 60b. ; exit 1
+ echo "FAIL: test 60b" | tee -a ${LOGFILE} ; exit 1
fi
- cd .. ; mv first-dir 1dir
- mv first-dir.cpy first-dir ; cd first-dir
+ cd ..
+ mv first-dir 1dir
+ mv first-dir.cpy first-dir
+ cd first-dir
+
if ${CVS} diff -u >> ${LOGFILE} || [ $? = 1 ] ; then
- true
+ echo "PASS: test 61" >>${LOGFILE}
else
- echo '***' failed test 61. ; exit 1
+ echo "FAIL: test 61" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} update ; then
- true
+ echo "PASS: test 62" >>${LOGFILE}
else
- echo '***' failed test 62. ; exit 1
+ echo "FAIL: test 62" | tee -a ${LOGFILE} ; exit 1
fi
cd ..
-# Haven't investigated why this is failing.
-# if diff --exclude=CVS -c -r 1dir first-dir ; then
-# true
+ #### FIXME: is this expected to work??? Need to investigate
+ #### and fix or remove the test.
+# directory_cmp 1dir first-dir
+#
+# if $ISDIFF ; then
+# echo "FAIL: test 63" | tee -a ${LOGFILE} # ; exit 1
# else
-# echo '***' failed test 63. # ; exit 1
+# echo "PASS: test 63" >>${LOGFILE}
# fi
rm -rf 1dir first-dir
if ${CVS} his -e -a >> ${LOGFILE}; then
- true
+ echo "PASS: test 64" >>${LOGFILE}
else
- echo '***' failed test 64. ; exit 1
+ echo "FAIL: test 64" | tee -a ${LOGFILE} ; exit 1
fi
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
+ rm -rf ${CVSROOT_DIRNAME}/second-dir
;;
death) # next dive. test death support.
- rm -rf ${CVSROOT_FILENAME}/first-dir
- mkdir ${CVSROOT_FILENAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
if ${CVS} co first-dir ; then
- true
+ echo "PASS: test 65" >>${LOGFILE}
else
- echo '***' failed test 65 ; exit 1
+ echo "FAIL: test 65" | tee -a ${LOGFILE} ; exit 1
fi
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 /tmp/cvs-sanity/cvsroot/first-dir/subdir added to the repository'
+ cd subdir
+ echo file in subdir >sfile
+ dotest 65a1 "${testcvs} add sfile" \
+'cvs [a-z]*: scheduling file `sfile'\'' for addition
+cvs [a-z]*: use '\''cvs commit'\'' to add this file permanently'
+ dotest 65a2 "${testcvs} -q ci -m add-it" \
+'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v
+done
+Checking in sfile;
+/tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v <-- sfile
+initial revision: 1.1
+done'
+ rm sfile
+ dotest 65a3 "${testcvs} rm sfile" \
+'cvs [a-z]*: scheduling `sfile'\'' for removal
+cvs [a-z]*: use '\''cvs commit'\'' to remove this file permanently'
+ dotest 65a4 "${testcvs} -q ci -m remove-it" \
+'Removing sfile;
+/tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v <-- sfile
+new revision: delete; previous revision: 1.1
+done'
+ 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
- true
+ echo "PASS: test 66" >>${LOGFILE}
else
- echo '***' failed test 66 ; exit 1
+ echo "FAIL: test 66" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 67" >>${LOGFILE}
else
- echo '***' failed test 67 ; exit 1
+ echo "FAIL: test 67" | tee -a ${LOGFILE} ; exit 1
fi
# remove
rm file1
if ${CVS} rm file1 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 68" >>${LOGFILE}
else
- echo '***' failed test 68 ; exit 1
+ echo "FAIL: test 68" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >>${LOGFILE} ; then
- true
+ echo "PASS: test 69" >>${LOGFILE}
else
- echo '***' failed test 69 ; exit 1
+ echo "FAIL: test 69" | tee -a ${LOGFILE} ; exit 1
fi
# add again and create second file
touch file1 file2
if ${CVS} add file1 file2 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 70" >>${LOGFILE}
else
- echo '***' failed test 70 ; exit 1
+ echo "FAIL: test 70" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 71" >>${LOGFILE}
else
- echo '***' failed test 71 ; exit 1
+ echo "FAIL: test 71" | tee -a ${LOGFILE} ; exit 1
fi
# log
if ${CVS} log file1 >> ${LOGFILE}; then
- true
+ echo "PASS: test 72" >>${LOGFILE}
else
- echo '***' failed test 72 ; exit 1
+ echo "FAIL: test 72" | tee -a ${LOGFILE} ; exit 1
fi
+ # file4 will be dead at the time of branching and stay dead.
+ echo file4 > file4
+ dotest death-file4-add "${testcvs} add file4" \
+'cvs [a-z]*: scheduling file `file4'\'' for addition
+cvs [a-z]*: use '\''cvs commit'\'' to add this file permanently'
+ dotest death-file4-ciadd "${testcvs} -q ci -m add file4" \
+'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file4,v
+done
+Checking in file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+initial revision: 1.1
+done'
+ rm file4
+ dotest death-file4-rm "${testcvs} remove file4" \
+'cvs [a-z]*: scheduling `file4'\'' for removal
+cvs [a-z]*: use '\''cvs commit'\'' to remove this file permanently'
+ dotest death-file4-cirm "${testcvs} -q ci -m remove file4" \
+'Removing file4;
+/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4
+new revision: delete; previous revision: 1.1
+done'
# branch1
if ${CVS} tag -b branch1 ; then
- true
+ echo "PASS: test 73" >>${LOGFILE}
else
- echo '***' failed test 73 ; exit 1
+ echo "FAIL: test 73" | tee -a ${LOGFILE} ; exit 1
fi
# and move to the branch.
if ${CVS} update -r branch1 ; then
- true
+ echo "PASS: test 74" >>${LOGFILE}
else
- echo '***' failed test 74 ; exit 1
+ echo "FAIL: test 74" | tee -a ${LOGFILE} ; exit 1
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
- true
+ echo "PASS: test 75" >>${LOGFILE}
else
- echo '***' failed test 75 ; exit 1
+ echo "FAIL: test 75" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 76" >>${LOGFILE}
else
- echo '***' failed test 76 ; exit 1
+ echo "FAIL: test 76" | tee -a ${LOGFILE} ; exit 1
fi
# remove
rm file3
if ${CVS} rm file3 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 77" >>${LOGFILE}
else
- echo '***' failed test 77 ; exit 1
+ echo "FAIL: test 77" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >>${LOGFILE} ; then
- true
+ echo "PASS: test 78" >>${LOGFILE}
else
- echo '***' failed test 78 ; exit 1
+ echo "FAIL: test 78" | tee -a ${LOGFILE} ; exit 1
fi
# add again
echo line1 from branch1 >> file3
if ${CVS} add file3 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 79" >>${LOGFILE}
else
- echo '***' failed test 79 ; exit 1
+ echo "FAIL: test 79" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 80" >>${LOGFILE}
else
- echo '***' failed test 80 ; exit 1
+ echo "FAIL: test 80" | tee -a ${LOGFILE} ; exit 1
fi
# change the first file
@@ -751,108 +1121,139 @@ for what in $tests; do
# commit
if ${CVS} ci -m test >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 81" >>${LOGFILE}
else
- echo '***' failed test 81 ; exit 1
+ echo "FAIL: test 81" | tee -a ${LOGFILE} ; exit 1
fi
# remove the second
rm file2
if ${CVS} rm file2 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 82" >>${LOGFILE}
else
- echo '***' failed test 82 ; exit 1
+ echo "FAIL: test 82" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >>${LOGFILE}; then
- true
+ echo "PASS: test 83" >>${LOGFILE}
else
- echo '***' failed test 83 ; exit 1
+ echo "FAIL: test 83" | tee -a ${LOGFILE} ; exit 1
fi
# back to the trunk.
if ${CVS} update -A 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 84" >>${LOGFILE}
else
- echo '***' failed test 84 ; exit 1
+ echo "FAIL: test 84" | tee -a ${LOGFILE} ; exit 1
fi
+ dotest_fail death-file4-4 "test -f file4" ''
+
if [ -f file3 ] ; then
- echo '***' failed test 85 ; exit 1
+ echo "FAIL: test 85" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 85" >>${LOGFILE}
fi
# join
if ${CVS} update -j branch1 >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 86" >>${LOGFILE}
else
- echo '***' failed test 86 ; exit 1
+ echo "FAIL: test 86" | tee -a ${LOGFILE} ; exit 1
fi
+ dotest_fail death-file4-5 "test -f file4" ''
+
if [ -f file3 ] ; then
- true
+ echo "PASS: test 87" >>${LOGFILE}
else
- echo '***' failed test 87 ; exit 1
+ echo "FAIL: test 87" | tee -a ${LOGFILE} ; exit 1
+ fi
+
+ # Make sure that we joined the correct change to file1
+ if echo line2 from branch1 | cmp - file1 >/dev/null; then
+ echo 'PASS: test 87a' >>${LOGFILE}
+ else
+ echo 'FAIL: test 87a' | tee -a ${LOGFILE}
+ exit 1
fi
# update
if ${CVS} update ; then
- true
+ echo "PASS: test 88" >>${LOGFILE}
else
- echo '***' failed test 88 ; exit 1
+ echo "FAIL: test 88" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >>${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 89" >>${LOGFILE}
else
- echo '***' failed test 89 ; exit 1
+ echo "FAIL: test 89" | tee -a ${LOGFILE} ; exit 1
fi
# remove first file.
rm file1
if ${CVS} rm file1 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 90" >>${LOGFILE}
else
- echo '***' failed test 90 ; exit 1
+ echo "FAIL: test 90" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m test >>${LOGFILE}; then
- true
+ echo "PASS: test 91" >>${LOGFILE}
else
- echo '***' failed test 91 ; exit 1
+ echo "FAIL: test 91" | tee -a ${LOGFILE} ; exit 1
fi
if [ -f file1 ] ; then
- echo '***' failed test 92 ; exit 1
+ echo "FAIL: test 92" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 92" >>${LOGFILE}
+ fi
+
+ # typo; try to get to the branch and fail
+ dotest_fail 92.1a "${testcvs} update -r brnach1" \
+ 'cvs \[[a-z]* aborted\]: no such tag brnach1'
+ # Make sure we are still on the trunk
+ if test -f file1 ; then
+ echo "FAIL: 92.1b" | tee -a ${LOGFILE} ; exit 1
+ else
+ echo "PASS: 92.1b" >>${LOGFILE}
+ fi
+ if test -f file2 ; then
+ echo "PASS: 92.1c" >>${LOGFILE}
+ else
+ echo "FAIL: 92.1c" | tee -a ${LOGFILE} ; exit 1
fi
# back to branch1
if ${CVS} update -r branch1 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 93" >>${LOGFILE}
else
- echo '***' failed test 93 ; exit 1
+ echo "FAIL: test 93" | tee -a ${LOGFILE} ; exit 1
fi
+ dotest_fail death-file4-6 "test -f file4" ''
+
if [ -f file1 ] ; then
- true
+ echo "PASS: test 94" >>${LOGFILE}
else
- echo '***' failed test 94 ; exit 1
+ echo "FAIL: test 94" | tee -a ${LOGFILE} ; exit 1
fi
# and join
if ${CVS} update -j HEAD >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 95" >>${LOGFILE}
else
- echo '***' failed test 95 ; exit 1
+ echo "FAIL: test 95" | tee -a ${LOGFILE} ; exit 1
fi
- cd .. ; rm -rf first-dir ${CVSROOT_FILENAME}/first-dir
+ dotest_fail death-file4-7 "test -f file4" ''
+
+ cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir
;;
import) # test death after import
@@ -863,35 +1264,54 @@ for what in $tests; do
echo imported file"$i" > imported-file"$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-file2
+ cp imported-file2 ../imported-file2-orig.tmp
+
if ${CVS} import -m first-import first-dir vendor-branch junk-1_0 ; then
- true
+ echo "PASS: test 96" >>${LOGFILE}
else
- echo '***' failed test 96 ; exit 1
+ echo "FAIL: test 96" | tee -a ${LOGFILE} ; exit 1
+ fi
+
+ if cmp ../imported-file2-orig.tmp imported-file2; then
+ pass 96.5
+ else
+ fail 96.5
fi
cd ..
# co
if ${CVS} co first-dir ; then
- true
+ echo "PASS: test 97" >>${LOGFILE}
else
- echo '***' failed test 97 ; exit 1
+ echo "FAIL: test 97" | tee -a ${LOGFILE} ; exit 1
fi
cd first-dir
for i in 1 2 3 4 ; do
if [ -f imported-file"$i" ] ; then
- true
+ echo "PASS: test 98-$i" >>${LOGFILE}
else
- echo '***' failed test 98-$i ; exit 1
+ echo "FAIL: test 98-$i" | tee -a ${LOGFILE} ; exit 1
fi
done
+ if test -d RCS; then
+ echo "FAIL: test 98.5" | tee -a ${LOGFILE} ; exit 1
+ else
+ echo "PASS: test 98.5" >>${LOGFILE}
+ fi
# remove
rm imported-file1
if ${CVS} rm imported-file1 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 99" >>${LOGFILE}
else
- echo '***' failed test 99 ; exit 1
+ echo "FAIL: test 99" | tee -a ${LOGFILE} ; exit 1
fi
# change
@@ -902,46 +1322,46 @@ for what in $tests; do
# commit
if ${CVS} ci -m local-changes >> ${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 100" >>${LOGFILE}
else
- echo '***' failed test 100 ; exit 1
+ echo "FAIL: test 100" | tee -a ${LOGFILE} ; exit 1
fi
# log
if ${CVS} log imported-file1 | grep '1.1.1.2 (dead)' ; then
- echo '***' failed test 101 ; exit 1
+ echo "FAIL: test 101" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 101" >>${LOGFILE}
fi
# update into the vendor branch.
if ${CVS} update -rvendor-branch ; then
- true
+ echo "PASS: test 102" >>${LOGFILE}
else
- echo '***' failed test 102 ; exit 1
+ echo "FAIL: test 102" | tee -a ${LOGFILE} ; exit 1
fi
# remove file4 on the vendor branch
rm imported-file4
if ${CVS} rm imported-file4 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 103" >>${LOGFILE}
else
- echo '***' failed test 103 ; exit 1
+ echo "FAIL: test 103" | tee -a ${LOGFILE} ; exit 1
fi
# commit
if ${CVS} ci -m vendor-removed imported-file4 >>${LOGFILE}; then
- true
+ echo "PASS: test 104" >>${LOGFILE}
else
- echo '***' failed test 104 ; exit 1
+ echo "FAIL: test 104" | tee -a ${LOGFILE} ; exit 1
fi
# update to main line
if ${CVS} update -A 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 105" >>${LOGFILE}
else
- echo '***' failed test 105 ; exit 1
+ echo "FAIL: test 105" | tee -a ${LOGFILE} ; exit 1
fi
# second import - file4 deliberately unchanged
@@ -949,146 +1369,152 @@ for what in $tests; do
for i in 1 2 3 ; do
echo rev 2 of file $i >> imported-file"$i"
done
+ cp imported-file2 ../imported-file2-orig.tmp
if ${CVS} import -m second-import first-dir vendor-branch junk-2_0 ; then
- true
+ echo "PASS: test 106" >>${LOGFILE}
+ else
+ echo "FAIL: test 106" | tee -a ${LOGFILE} ; exit 1
+ fi
+ if cmp ../imported-file2-orig.tmp imported-file2; then
+ pass 106.5
else
- echo '***' failed test 106 ; exit 1
+ fail 106.5
fi
cd ..
# co
if ${CVS} co first-dir ; then
- true
+ echo "PASS: test 107" >>${LOGFILE}
else
- echo '***' failed test 107 ; exit 1
+ echo "FAIL: test 107" | tee -a ${LOGFILE} ; exit 1
fi
cd first-dir
if [ -f imported-file1 ] ; then
- echo '***' failed test 108 ; exit 1
+ echo "FAIL: test 108" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 108" >>${LOGFILE}
fi
for i in 2 3 ; do
if [ -f imported-file"$i" ] ; then
- true
+ echo "PASS: test 109-$i" >>${LOGFILE}
else
- echo '***' failed test 109-$i ; exit 1
+ echo "FAIL: test 109-$i" | tee -a ${LOGFILE} ; exit 1
fi
done
# check vendor branch for file4
if ${CVS} update -rvendor-branch ; then
- true
+ echo "PASS: test 110" >>${LOGFILE}
else
- echo '***' failed test 110 ; exit 1
+ echo "FAIL: test 110" | tee -a ${LOGFILE} ; exit 1
fi
if [ -f imported-file4 ] ; then
- true
+ echo "PASS: test 111" >>${LOGFILE}
else
- echo '***' failed test 111 ; exit 1
+ echo "FAIL: test 111" | tee -a ${LOGFILE} ; exit 1
fi
# update to main line
if ${CVS} update -A 2>> ${LOGFILE}; then
- true
+ echo "PASS: test 112" >>${LOGFILE}
else
- echo '***' failed test 112 ; exit 1
+ echo "FAIL: test 112" | tee -a ${LOGFILE} ; exit 1
fi
cd ..
if ${CVS} co -jjunk-1_0 -jjunk-2_0 first-dir >>${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 113" >>${LOGFILE}
else
- echo '***' failed test 113 ; exit 1
+ echo "FAIL: test 113" | tee -a ${LOGFILE} ; exit 1
fi
cd first-dir
if [ -f imported-file1 ] ; then
- echo '***' failed test 114 ; exit 1
+ echo "FAIL: test 114" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 114" >>${LOGFILE}
fi
for i in 2 3 ; do
if [ -f imported-file"$i" ] ; then
- true
+ echo "PASS: test 115-$i" >>${LOGFILE}
else
- echo '***' failed test 115-$i ; exit 1
+ echo "FAIL: test 115-$i" | tee -a ${LOGFILE} ; exit 1
fi
done
if cat imported-file2 | grep '====' >> ${LOGFILE}; then
- true
+ echo "PASS: test 116" >>${LOGFILE}
else
- echo '***' failed test 116 ; exit 1
+ echo "FAIL: test 116" | tee -a ${LOGFILE} ; exit 1
fi
- cd .. ; rm -rf first-dir ${CVSROOT_FILENAME}/first-dir
+ cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir
+ rm -rf import-dir
;;
new) # look for stray "no longer pertinent" messages.
- rm -rf first-dir ${CVSROOT_FILENAME}/first-dir
- mkdir ${CVSROOT_FILENAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
if ${CVS} co first-dir ; then
- true
+ echo "PASS: test 117" >>${LOGFILE}
else
- echo '***' failed test 117 ; exit 1
+ echo "FAIL: test 117" | tee -a ${LOGFILE} ; exit 1
fi
cd first-dir
touch a
if ${CVS} add a 2>>${LOGFILE}; then
- true
+ echo "PASS: test 118" >>${LOGFILE}
else
- echo '***' failed test 118 ; exit 1
+ echo "FAIL: test 118" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} ci -m added >>${LOGFILE} 2>&1; then
- true
+ echo "PASS: test 119" >>${LOGFILE}
else
- echo '***' failed test 119 ; exit 1
+ echo "FAIL: test 119" | tee -a ${LOGFILE} ; exit 1
fi
rm a
if ${CVS} rm a 2>>${LOGFILE}; then
- true
+ echo "PASS: test 120" >>${LOGFILE}
else
- echo '***' failed test 120 ; exit 1
+ echo "FAIL: test 120" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} ci -m removed >>${LOGFILE} ; then
- true
+ echo "PASS: test 121" >>${LOGFILE}
else
- echo '***' failed test 121 ; exit 1
+ echo "FAIL: test 121" | tee -a ${LOGFILE} ; exit 1
fi
if ${CVS} update -A 2>&1 | grep longer ; then
- echo '***' failed test 122 ; exit 1
+ echo "FAIL: test 122" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 122" >>${LOGFILE}
fi
if ${CVS} update -rHEAD 2>&1 | grep longer ; then
- echo '***' failed test 123 ; exit 1
+ echo "FAIL: test 123" | tee -a ${LOGFILE} ; exit 1
else
- true
+ echo "PASS: test 123" >>${LOGFILE}
fi
- cd .. ; rm -rf first-dir ; rm -rf ${CVSROOT_FILENAME}/first-dir
+ cd .. ; rm -rf first-dir ; rm -rf ${CVSROOT_DIRNAME}/first-dir
;;
conflicts)
- rm -rf first-dir ${CVSROOT_FILENAME}/first-dir
- mkdir ${CVSROOT_FILENAME}/first-dir
+ rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
mkdir 1
cd 1
@@ -1249,16 +1675,11 @@ for what in $tests; do
fi
cd ../..
- rm -rf 1 2 3 ; rm -rf ${CVSROOT_FILENAME}/first-dir
+ rm -rf 1 2 3 ; rm -rf ${CVSROOT_DIRNAME}/first-dir
;;
modules)
- # The following line stolen from cvsinit.sh. FIXME: create our
- # repository via cvsinit.sh; that way we test it too.
- (cd ${CVSROOT_FILENAME}/CVSROOT; ci -q -u -t/dev/null \
- -m'initial checkin of modules' modules)
-
- rm -rf first-dir ${CVSROOT_FILENAME}/first-dir
- mkdir ${CVSROOT_FILENAME}/first-dir
+ rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir
+ mkdir ${CVSROOT_DIRNAME}/first-dir
mkdir 1
cd 1
@@ -1267,6 +1688,7 @@ for what in $tests; do
echo 'PASS: test 143' >>${LOGFILE}
else
echo 'FAIL: test 143' | tee -a ${LOGFILE}
+ exit 1
fi
cd first-dir
@@ -1274,18 +1696,20 @@ for what in $tests; do
${testcvs} add subdir >>${LOGFILE}
cd subdir
- touch a
+ touch a b
- if ${testcvs} add a 2>>${LOGFILE} ; then
+ if ${testcvs} add a b 2>>${LOGFILE} ; then
echo 'PASS: test 144' >>${LOGFILE}
else
echo 'FAIL: test 144' | tee -a ${LOGFILE}
+ exit 1
fi
if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then
echo 'PASS: test 145' >>${LOGFILE}
else
echo 'FAIL: test 145' | tee -a ${LOGFILE}
+ exit 1
fi
cd ..
@@ -1293,6 +1717,7 @@ for what in $tests; do
echo 'PASS: test 146' >>${LOGFILE}
else
echo 'FAIL: test 146' | tee -a ${LOGFILE}
+ exit 1
fi
# Here we test that CVS can deal with CVSROOT (whose repository
@@ -1303,34 +1728,131 @@ for what in $tests; do
echo 'PASS: test 147' >>${LOGFILE}
else
echo 'FAIL: test 147' | tee -a ${LOGFILE}
+ exit 1
fi
echo realmodule first-dir/subdir a >>CVSROOT/modules
+ echo dirmodule first-dir/subdir >>CVSROOT/modules
+ echo namedmodule -d nameddir first-dir/subdir >>CVSROOT/modules
echo aliasmodule -a first-dir/subdir/a >>CVSROOT/modules
if ${testcvs} ci -m 'add modules' CVSROOT/modules \
>>${LOGFILE} 2>&1; then
echo 'PASS: test 148' >>${LOGFILE}
else
echo 'FAIL: test 148' | tee -a ${LOGFILE}
+ exit 1
fi
cd ..
+
+ # Test that real modules check out to realmodule/a, not subdir/a.
if ${testcvs} co realmodule >>${LOGFILE}; then
- echo 'PASS: test 149' >>${LOGFILE}
+ echo 'PASS: test 149a1' >>${LOGFILE}
else
- echo 'FAIL: test 149' | tee -a ${LOGFILE}
+ echo 'FAIL: test 149a1' | tee -a ${LOGFILE}
+ exit 1
fi
if test -d realmodule && test -f realmodule/a; then
- echo 'PASS: test 150' >>${LOGFILE}
+ echo 'PASS: test 149a2' >>${LOGFILE}
+ else
+ echo 'FAIL: test 149a2' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if test -f realmodule/b; then
+ echo 'FAIL: test 149a3' | tee -a ${LOGFILE}
+ exit 1
else
- echo 'FAIL: test 150' | tee -a ${LOGFILE}
+ echo 'PASS: test 149a3' >>${LOGFILE}
fi
+ if ${testcvs} -q co realmodule; then
+ echo 'PASS: test 149a4' >>${LOGFILE}
+ else
+ echo 'FAIL: test 149a4' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if echo "yes" | ${testcvs} release -d realmodule >>${LOGFILE} ; then
+ echo 'PASS: test 149a5' >>${LOGFILE}
+ else
+ echo 'FAIL: test 149a5' | tee -a ${LOGFILE}
+ exit 1
+ fi
+
+ # Now test the ability to check out a single file from a directory
+ if ${testcvs} co dirmodule/a >>${LOGFILE}; then
+ echo 'PASS: test 150c' >>${LOGFILE}
+ else
+ echo 'FAIL: test 150c' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if test -d dirmodule && test -f dirmodule/a; then
+ echo 'PASS: test 150d' >>${LOGFILE}
+ else
+ echo 'FAIL: test 150d' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if test -f dirmodule/b; then
+ echo 'FAIL: test 150e' | tee -a ${LOGFILE}
+ exit 1
+ else
+ echo 'PASS: test 150e' >>${LOGFILE}
+ fi
+ if echo "yes" | ${testcvs} release -d dirmodule >>${LOGFILE} ; then
+ echo 'PASS: test 150f' >>${LOGFILE}
+ else
+ echo 'FAIL: test 150f' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ # Now test the ability to correctly reject a non-existent filename.
+ # For maximum studliness we would check that an error message is
+ # being output.
+ if ${testcvs} co dirmodule/nonexist >>${LOGFILE} 2>&1; then
+ # We accept a zero exit status because it is what CVS does
+ # (Dec 95). Probably the exit status should be nonzero,
+ # however.
+ echo 'PASS: test 150g1' >>${LOGFILE}
+ else
+ echo 'PASS: test 150g1' >>${LOGFILE}
+ 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.
+ if test -f dirmodule/a || test -f dirmodule/b; then
+ echo 'FAIL: test 150g2' | tee -a ${LOGFILE}
+ exit 1
+ else
+ echo 'PASS: test 150g2' >>${LOGFILE}
+ fi
+ rm -rf dirmodule
+
+ # Now test that a module using -d checks out to the specified
+ # directory.
+ dotest 150h1 "${testcvs} -q co namedmodule" 'U nameddir/a
+U nameddir/b'
+ if test -f nameddir/a && test -f nameddir/b; then
+ pass 150h2
+ else
+ fail 150h2
+ fi
+ echo add line >>nameddir/a
+ dotest 150h3 "${testcvs} -q co namedmodule" 'M nameddir/a'
+ rm nameddir/a
+ dotest 150h4 "${testcvs} -q co namedmodule" 'U nameddir/a'
+ if echo "yes" | ${testcvs} release -d nameddir >>${LOGFILE} ; then
+ pass 150h99
+ else
+ fail 150h99
+ fi
+
+ # Now test that alias modules check out to subdir/a, not
+ # aliasmodule/a.
if ${testcvs} co aliasmodule >>${LOGFILE}; then
echo 'PASS: test 151' >>${LOGFILE}
else
echo 'FAIL: test 151' | tee -a ${LOGFILE}
+ exit 1
fi
if test -d aliasmodule; then
echo 'FAIL: test 152' | tee -a ${LOGFILE}
+ exit 1
else
echo 'PASS: test 152' >>${LOGFILE}
fi
@@ -1340,20 +1862,17 @@ for what in $tests; do
echo 'PASS: test 153' >>${LOGFILE}
else
echo 'FAIL: test 153' | tee -a ${LOGFILE}
+ exit 1
fi
echo 'M first-dir/subdir/a' >ans153.tmp
if cmp test153.tmp ans153.tmp; then
echo 'PASS: test 154' >>${LOGFILE}
else
echo 'FAIL: test 154' | tee -a ${LOGFILE}
- fi
- if ${testcvs} -q co realmodule; then
- echo 'PASS: test 155' >>${LOGFILE}
- else
- echo 'FAIL: test 155' | tee -a ${LOGFILE}
+ exit 1
fi
cd ..
- rm -rf 1 ; rm -rf ${CVSROOT_FILENAME}/first-dir
+ rm -rf 1 ; rm -rf ${CVSROOT_DIRNAME}/first-dir
;;
mflag)
for message in '' ' ' '
@@ -1366,6 +1885,7 @@ for what in $tests; do
echo 'PASS: test 156' >>${LOGFILE}
else
echo 'FAIL: test 156' | tee -a ${LOGFILE}
+ exit 1
fi
# Must import twice since the first time uses inline code that
# avoids RCS call.
@@ -1374,6 +1894,7 @@ for what in $tests; do
echo 'PASS: test 157' >>${LOGFILE}
else
echo 'FAIL: test 157' | tee -a ${LOGFILE}
+ exit 1
fi
# Test handling of -m during ci
cd ..; rm -rf a-dir;
@@ -1381,6 +1902,7 @@ for what in $tests; do
echo 'PASS: test 158' >>${LOGFILE}
else
echo 'FAIL: test 158' | tee -a ${LOGFILE}
+ exit 1
fi
cd a-dir
echo testc >>test
@@ -1388,6 +1910,7 @@ for what in $tests; do
echo 'PASS: test 159' >>${LOGFILE}
else
echo 'FAIL: test 159' | tee -a ${LOGFILE}
+ exit 1
fi
# Test handling of -m during rm/ci
rm test;
@@ -1395,24 +1918,27 @@ for what in $tests; do
echo 'PASS: test 160' >>${LOGFILE}
else
echo 'FAIL: test 160' | tee -a ${LOGFILE}
+ exit 1
fi
if ${testcvs} ci -m "$message" >>${LOGFILE} 2>&1; then
echo 'PASS: test 161' >>${LOGFILE}
else
echo 'FAIL: test 161' | tee -a ${LOGFILE}
+ exit 1
fi
# Clean up
- cd ..; rm -rf a-dir ${CVSROOT_FILENAME}/a-dir
+ cd ..; rm -rf a-dir ${CVSROOT_DIRNAME}/a-dir
done
;;
errmsg1)
- mkdir ${CVSROOT_FILENAME}/1dir
+ mkdir ${CVSROOT_DIRNAME}/1dir
mkdir 1
cd 1
if ${testcvs} -q co 1dir; then
echo 'PASS: test 162' >>${LOGFILE}
else
echo 'FAIL: test 162' | tee -a ${LOGFILE}
+ exit 1
fi
cd 1dir
touch foo
@@ -1420,11 +1946,13 @@ for what in $tests; do
echo 'PASS: test 163' >>${LOGFILE}
else
echo 'FAIL: test 163' | tee -a ${LOGFILE}
+ exit 1
fi
if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then
echo 'PASS: test 164' >>${LOGFILE}
else
echo 'FAIL: test 164' | tee -a ${LOGFILE}
+ exit 1
fi
cd ../..
mkdir 2
@@ -1433,6 +1961,7 @@ for what in $tests; do
echo 'PASS: test 165' >>${LOGFILE}
else
echo 'FAIL: test 165' | tee -a ${LOGFILE}
+ exit 1
fi
chmod a-w 1dir
cd ../1/1dir
@@ -1441,37 +1970,339 @@ for what in $tests; do
echo 'PASS: test 166' >>${LOGFILE}
else
echo 'FAIL: test 166' | tee -a ${LOGFILE}
+ exit 1
fi
if ${testcvs} ci -m removed >>${LOGFILE} 2>&1; then
echo 'PASS: test 167' >>${LOGFILE}
else
echo 'FAIL: test 167' | tee -a ${LOGFILE}
+ exit 1
fi
cd ../../2/1dir
${testcvs} -q update 2>../tst167.err
+ CVSBASE=`basename $testcvs` # Get basename of CVS executable.
cat <<EOF >../tst167.ans
-cvs server: warning: foo is not (any longer) pertinent
-cvs update: unable to remove ./foo: Permission denied
+$CVSBASE server: warning: foo is not (any longer) pertinent
+$CVSBASE update: unable to remove ./foo: Permission denied
EOF
if cmp ../tst167.ans ../tst167.err >/dev/null ||
- ( echo 'cvs [update aborted]: cannot rename file foo to CVS/,,foo: Permission denied' | cmp - ../tst167.err >/dev/null )
+ ( echo "$CVSBASE [update aborted]: cannot rename file foo to CVS/,,foo: Permission denied" | cmp - ../tst167.err >/dev/null )
then
echo 'PASS: test 168' >>${LOGFILE}
else
echo 'FAIL: test 168' | tee -a ${LOGFILE}
+ exit 1
fi
cd ..
chmod u+w 1dir
cd ..
- rm -rf 1 2 ${CVSROOT_FILENAME}/1dir
+ rm -rf 1 2 ${CVSROOT_DIRNAME}/1dir
;;
- *) echo $what is not the name of a test -- ignored ;;
+ devcom)
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ mkdir 1
+ cd 1
+ if ${testcvs} -q co first-dir >>${LOGFILE} ; then
+ echo 'PASS: test 169' >>${LOGFILE}
+ else
+ echo 'FAIL: test 169' | tee -a ${LOGFILE}
+ exit 1
+ fi
+
+ cd first-dir
+ echo abb >abb
+ if ${testcvs} add abb 2>>${LOGFILE}; then
+ echo 'PASS: test 170' >>${LOGFILE}
+ else
+ echo 'FAIL: test 170' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then
+ echo 'PASS: test 171' >>${LOGFILE}
+ else
+ echo 'FAIL: test 171' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ if ${testcvs} watch on; then
+ echo 'PASS: test 172' >>${LOGFILE}
+ else
+ echo 'FAIL: test 172' | tee -a ${LOGFILE}
+ fi
+ echo abc >abc
+ if ${testcvs} add abc 2>>${LOGFILE}; then
+ echo 'PASS: test 173' >>${LOGFILE}
+ else
+ echo 'FAIL: test 173' | tee -a ${LOGFILE}
+ fi
+ if ${testcvs} ci -m added >>${LOGFILE} 2>&1; then
+ echo 'PASS: test 174' >>${LOGFILE}
+ else
+ echo 'FAIL: test 174' | tee -a ${LOGFILE}
+ fi
+
+ cd ../..
+ mkdir 2
+ cd 2
+
+ if ${testcvs} -q co first-dir >>${LOGFILE}; then
+ echo 'PASS: test 175' >>${LOGFILE}
+ else
+ echo 'FAIL: test 175' | tee -a ${LOGFILE}
+ fi
+ cd first-dir
+ if test -w abb; then
+ echo 'FAIL: test 176' | tee -a ${LOGFILE}
+ else
+ echo 'PASS: test 176' >>${LOGFILE}
+ fi
+ if test -w abc; then
+ echo 'FAIL: test 177' | tee -a ${LOGFILE}
+ else
+ echo 'PASS: test 177' >>${LOGFILE}
+ fi
+
+ if ${testcvs} editors >../ans178.tmp; then
+ echo 'PASS: test 178' >>${LOGFILE}
+ else
+ echo 'FAIL: test 178' | tee -a ${LOGFILE}
+ fi
+ cat ../ans178.tmp >>${LOGFILE}
+ if test -s ../ans178.tmp; then
+ echo 'FAIL: test 178a' | tee -a ${LOGFILE}
+ else
+ echo 'PASS: test 178a' >>${LOGFILE}
+ fi
+
+ if ${testcvs} edit abb; then
+ echo 'PASS: test 179' >>${LOGFILE}
+ else
+ echo 'FAIL: test 179' | tee -a ${LOGFILE}
+ exit 1
+ fi
+
+ if ${testcvs} editors >../ans180.tmp; then
+ echo 'PASS: test 180' >>${LOGFILE}
+ else
+ echo 'FAIL: test 180' | tee -a ${LOGFILE}
+ exit 1
+ fi
+ cat ../ans180.tmp >>${LOGFILE}
+ if test -s ../ans180.tmp; then
+ echo 'PASS: test 181' >>${LOGFILE}
+ else
+ echo 'FAIL: test 181' | tee -a ${LOGFILE}
+ fi
+
+ echo aaaa >>abb
+ if ${testcvs} ci -m modify abb >>${LOGFILE} 2>&1; then
+ echo 'PASS: test 182' >>${LOGFILE}
+ else
+ echo 'FAIL: test 182' | tee -a ${LOGFILE}
+ fi
+ # Unedit of a file not being edited should be a noop.
+ dotest 182.5 "${testcvs} unedit abb" ''
+
+ if ${testcvs} editors >../ans183.tmp; then
+ echo 'PASS: test 183' >>${LOGFILE}
+ else
+ echo 'FAIL: test 183' | tee -a ${LOGFILE}
+ fi
+ cat ../ans183.tmp >>${LOGFILE}
+ if test -s ../ans183.tmp; then
+ echo 'FAIL: test 184' | tee -a ${LOGFILE}
+ else
+ echo 'PASS: test 184' >>${LOGFILE}
+ fi
+
+ if test -w abb; then
+ echo 'FAIL: test 185' | tee -a ${LOGFILE}
+ else
+ echo 'PASS: test 185' >>${LOGFILE}
+ fi
+
+ if ${testcvs} edit abc; then
+ echo 'PASS: test 186a1' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a1' | tee -a ${LOGFILE}
+ fi
+ # Unedit of an unmodified file.
+ if ${testcvs} unedit abc; then
+ echo 'PASS: test 186a2' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a2' | tee -a ${LOGFILE}
+ fi
+ if ${testcvs} edit abc; then
+ echo 'PASS: test 186a3' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a3' | tee -a ${LOGFILE}
+ fi
+ echo changedabc >abc
+ # Try to unedit a modified file; cvs should ask for confirmation
+ if (echo no | ${testcvs} unedit abc) >>${LOGFILE}; then
+ echo 'PASS: test 186a4' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a4' | tee -a ${LOGFILE}
+ fi
+ if echo changedabc | cmp - abc; then
+ echo 'PASS: test 186a5' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a5' | tee -a ${LOGFILE}
+ fi
+ # OK, now confirm the unedit
+ if (echo yes | ${testcvs} unedit abc) >>${LOGFILE}; then
+ echo 'PASS: test 186a6' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a6' | tee -a ${LOGFILE}
+ fi
+ if echo abc | cmp - abc; then
+ echo 'PASS: test 186a7' >>${LOGFILE}
+ else
+ echo 'FAIL: test 186a7' | tee -a ${LOGFILE}
+ fi
+
+ cd ../..
+ rm -rf 1 2 ${CVSROOT_DIRNAME}/first-dir
+ ;;
+
+ ignore)
+ mkdir home
+ HOME=${TESTDIR}/home; export HOME
+ dotest 187a1 "${testcvs} -q co CVSROOT" 'U CVSROOT/modules'
+ cd CVSROOT
+ echo rootig.c >cvsignore
+ dotest 187a2 "${testcvs} add cvsignore" 'cvs [a-z]*: scheduling file `cvsignore'"'"' for addition
+cvs [a-z]*: use '"'"'cvs commit'"'"' to add this file permanently'
+
+ # As of Jan 96, local CVS prints "Examining ." and remote doesn't.
+ # Accept either.
+ dotest 187a3 " ${testcvs} ci -m added" \
+"${DOTSTAR}"'CS file: /tmp/cvs-sanity/cvsroot/CVSROOT/cvsignore,v
+done
+Checking in cvsignore;
+/tmp/cvs-sanity/cvsroot/CVSROOT/cvsignore,v <-- cvsignore
+initial revision: 1.1
+done
+cvs [a-z]*: Executing '"'"''"'"'.*mkmodules'"'"' '"'"'/tmp/cvs-sanity/cvsroot/CVSROOT'"'"''"'"''
+
+ cd ..
+ if echo "yes" | ${testcvs} release -d CVSROOT >>${LOGFILE} ; then
+ echo 'PASS: test 187a4' >>${LOGFILE}
+ else
+ echo 'FAIL: test 187a4' | tee -a ${LOGFILE}
+ exit 1
+ 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 really should allow the files to be listed in any order.
+ # But we (kludgily) just list the orders which have been observed.
+ dotest 188a "${testcvs} import -m m -I optig.c first-dir tag1 tag2" \
+ 'N first-dir/foobar.c
+N first-dir/bar.c
+I first-dir/rootig.c
+I first-dir/defig.o
+I first-dir/envig.c
+I first-dir/optig.c
+
+No conflicts created by this import' 'I first-dir/defig.o
+I first-dir/envig.c
+I first-dir/optig.c
+N first-dir/foobar.c
+N first-dir/bar.c
+I first-dir/rootig.c
+
+No conflicts created by this import'
+ dotest 188b "${testcvs} import -m m -I ! second-dir tag3 tag4" \
+ 'N second-dir/foobar.c
+N second-dir/bar.c
+N second-dir/rootig.c
+N second-dir/defig.o
+N second-dir/envig.c
+N second-dir/optig.c
+
+No conflicts created by this import'
+ cd ..
+ rm -rf dir-to-import
+
+ dotest 189a "${testcvs} -q co second-dir" \
+'U second-dir/bar.c
+U second-dir/defig.o
+U second-dir/envig.c
+U second-dir/foobar.c
+U second-dir/optig.c
+U second-dir/rootig.c'
+ rm -rf second-dir
+ dotest 189b "${testcvs} -q co first-dir" 'U first-dir/bar.c
+U first-dir/foobar.c'
+ cd first-dir
+ touch rootig.c defig.o envig.c optig.c notig.c
+ dotest 189c "${testcvs} -q update -I optig.c" '\? notig.c'
+ # The fact that CVS requires us to specify -I CVS here strikes me
+ # as a bug.
+ dotest 189d "${testcvs} -q update -I ! -I CVS" '\? rootig.c
+\? defig.o
+\? envig.c
+\? optig.c
+\? notig.c'
+ cd ..
+ rm -rf first-dir
+
+ rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir
+ ;;
+
+ binfiles)
+ # Test cvs's ability to handle binary files.
+ mkdir ${CVSROOT_DIRNAME}/first-dir
+ mkdir 1; cd 1
+ dotest binfiles-1 "${testcvs} -q co first-dir" ''
+ awk 'BEGIN { printf "%c%c%c%c%c%c", 2, 10, 137, 0, 13, 10 }' \
+ </dev/null >binfile.dat
+ cat binfile.dat binfile.dat >binfile2.dat
+ cd first-dir
+ cp ../binfile.dat binfile
+ dotest binfiles-2 "${testcvs} add -kb binfile" \
+'cvs [a-z]*: scheduling file `binfile'\'' for addition
+cvs [a-z]*: use '\''cvs commit'\'' to add this file permanently'
+ dotest binfiles-3 "${testcvs} -q ci -m add-it" \
+'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/binfile,v
+done
+Checking in binfile;
+/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile
+initial revision: 1.1
+done'
+ cd ../..
+ mkdir 2; cd 2
+ dotest binfiles-4 "${testcvs} -q co first-dir" 'U first-dir/binfile'
+ cd first-dir
+ dotest binfiles-5 "cmp ../../1/binfile.dat binfile" ''
+ cp ../../1/binfile2.dat binfile
+ dotest binfiles-6 "${testcvs} -q ci -m modify-it" \
+'Checking in binfile;
+/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile
+new revision: 1.2; previous revision: 1.1
+done'
+ cd ../../1/first-dir
+ dotest binfiles-7 "${testcvs} -q update" '[UP] binfile'
+ dotest binfiles-8 "cmp ../binfile2.dat binfile" ''
+
+ cd ../..
+ rm -rf ${CVSROOT_DIRNAME}/first-dir
+ rm -r 1 2
+ ;;
+ *)
+ echo $what is not the name of a test -- ignored
+ ;;
esac
done
-echo Ok.
+echo "OK, all tests completed."
# TODO:
# * Test `cvs admin'.
@@ -1479,7 +2310,6 @@ echo Ok.
# * Test `cvs update foo bar' (where foo and bar are both from the same
# repository). Suppose one is a branch--make sure that both directories
# get updated with the respective correct thing.
-# * Zero length files (check in, check out).
# * `cvs update ../foo'. Also ../../foo ./../foo foo/../../bar /foo/bar
# foo/.././../bar foo/../bar etc.
# * Test all flags in modules file.
@@ -1497,6 +2327,11 @@ echo Ok.
# gives an appropriate error (e.g.
# Cannot access /tmp/cvs-sanity/non-existent/CVSROOT
# No such file or directory).
+# * Test "cvs watch add", "cvs watch remove", "cvs watchers", that
+# notify script gets called where appropriate.
+# * Test "cvs unedit" and that it really reverts a change.
+# * Test that remote edit and/or unedit works when disconnected from
+# server (e.g. set CVS_SERVER to "foobar").
# End of TODO list.
# Remove the test directory, but first change out of it.
diff --git a/gnu/usr.bin/cvs/src/scramble.c b/gnu/usr.bin/cvs/src/scramble.c
new file mode 100644
index 00000000000..4bf92447366
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/scramble.c
@@ -0,0 +1,245 @@
+/*
+ * Trivially encode strings to protect them from innocent eyes (i.e.,
+ * inadvertent password compromises, like a network administrator
+ * who's watching packets for legitimate reasons and accidentally sees
+ * the password protocol go by).
+ *
+ * This is NOT secure encryption.
+ *
+ * It would be tempting to encode the password according to username
+ * and repository, so that the same password would encode to a
+ * different string when used with different usernames and/or
+ * repositories. However, then users would not be able to cut and
+ * paste passwords around. They're not supposed to anyway, but we all
+ * know they will, and there's no reason to make it harder for them if
+ * we're not trying to provide real security anyway.
+ */
+
+/* Set this to test as a standalone program. */
+/* #define DIAGNOSTIC */
+
+#ifndef DIAGNOSTIC
+#include "cvs.h"
+#else /* ! DIAGNOSTIC */
+/* cvs.h won't define this for us */
+#define AUTH_CLIENT_SUPPORT
+#define xmalloc malloc
+/* Use "gcc -fwritable-strings". */
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
+#endif /* ! DIAGNOSTIC */
+
+#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
+
+/* Map characters to each other randomly and symmetrically, A <--> B.
+ *
+ * We divide the ASCII character set into 3 domains: control chars (0
+ * thru 31), printing chars (32 through 126), and "meta"-chars (127
+ * through 255). The control chars map _to_ themselves, the printing
+ * chars map _among_ themselves, and the meta chars map _among_
+ * themselves. Why is this thus?
+ *
+ * No character in any of these domains maps to a character in another
+ * domain, because I'm not sure what characters are legal in
+ * passwords, or what tools people are likely to use to cut and paste
+ * them. It seems prudent not to introduce control or meta chars,
+ * unless the user introduced them first. And having the control
+ * chars all map to themselves insures that newline and
+ * carriage-return are safely handled.
+ */
+
+static char
+shifts[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 114, 120,
+53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87, 111, 52,
+75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105, 41, 57,
+83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35, 125, 55,
+54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56, 36, 121,
+117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, 58, 113,
+32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223, 225, 216,
+187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
+199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203,
+226, 193, 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238,
+161, 179, 160, 212, 207, 221, 254, 173, 202, 146, 224, 151, 140, 196,
+205, 130, 135, 133, 143, 246, 192, 159, 244, 239, 185, 168, 215, 144,
+139, 165, 180, 157, 147, 186, 214, 176, 227, 231, 219, 169, 175, 156,
+206, 198, 129, 164, 150, 210, 154, 177, 134, 127, 182, 128, 158, 208,
+162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195, 243, 233,
+253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152 };
+
+
+/* SCRAMBLE and DESCRAMBLE work like this:
+ *
+ * scramble(STR) returns SCRM, a scrambled copy of STR. SCRM[0] is a
+ * single letter indicating the scrambling method. As of this
+ * writing, the only legal method is 'A', but check the code for more
+ * up-to-date information. The copy will have been allocated with
+ * malloc().
+ *
+ * descramble(SCRM) returns STR, again in its own malloc'd space.
+ * descramble() uses SCRM[0] to determine which method of unscrambling
+ * to use. If it does not recognize the method, it dies with error.
+ */
+
+/* Return a malloc'd, scrambled version of STR. */
+char *
+scramble (str)
+ char *str;
+{
+ int i;
+ char *s;
+
+ /* +2 to hold the 'A' prefix that indicates which version of
+ * scrambling this is (the first, obviously, since we only do one
+ * kind of scrambling so far), and then the '\0' of course.
+ */
+ s = xmalloc (strlen (str) + 2);
+
+ s[0] = 'A'; /* Scramble (TM) version prefix. */
+ strcpy (s + 1, str);
+
+ for (i = 1; s[i]; i++)
+ s[i] = shifts[(s[i])];
+
+ return s;
+}
+
+/* Decode the string in place. */
+char *
+descramble (str)
+ char *str;
+{
+ char *s, *ret;
+
+ /* For now we can only handle one kind of scrambling. In the future
+ * there may be other kinds, and this `if' will become a `switch'.
+ */
+ if (str[0] != 'A')
+#ifndef DIAGNOSTIC
+ error (1, 0, "descramble: unknown scrambling method");
+#else /* DIAGNOSTIC */
+ {
+ fprintf (stderr, "descramble: unknown scrambling method\n", str);
+ fflush (stderr);
+ exit (1);
+ }
+#endif /* DIAGNOSTIC */
+
+ /* Method `A' is symmetrical, so scramble again to decrypt. */
+ s = scramble (str + 1);
+
+ /* Make sure the string we return can be free()'d! */
+ ret = xmalloc (strlen (s));
+ strcpy (ret, s + 1); /* scoot past the 'A' */
+ free (s);
+
+ return ret;
+}
+
+#endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
+
+#ifdef DIAGNOSTIC
+int
+main ()
+{
+ int i;
+ char *e, *m, biggie[256];
+
+ char *cleartexts[5];
+ cleartexts[0] = "first";
+ cleartexts[1] = "the second";
+ cleartexts[2] = "this is the third";
+ cleartexts[3] = "$#% !!\\3";
+ cleartexts[4] = biggie;
+
+ /* Set up the most important test string: */
+ /* Can't have a real ASCII zero in the string, because we want to
+ use printf, so we substitute the character zero. */
+ biggie[0] = '0';
+ /* The rest of the string gets straight ascending ASCII. */
+ for (i = 1; i < 256; i++)
+ biggie[i] = i;
+
+ /* Test all the strings. */
+ for (i = 0; i < 5; i++)
+ {
+ printf ("clear%d: %s\n", i, cleartexts[i]);
+ e = scramble (cleartexts[i]);
+ printf ("scram%d: %s\n", i, e);
+ m = descramble (e);
+ free (e);
+ printf ("clear%d: %s\n\n", i, m);
+ free (m);
+ }
+
+ fflush (stdout);
+ return 0;
+}
+#endif /* DIAGNOSTIC */
+
+/*
+ * ;;; The Emacs Lisp that did the dirty work ;;;
+ * (progn
+ *
+ * ;; Helper func.
+ * (defun random-elt (lst)
+ * (let* ((len (length lst))
+ * (rnd (random len)))
+ * (nth rnd lst)))
+ *
+ * ;; A list of all characters under 127, each appearing once.
+ * (setq non-meta-chars
+ * (let ((i 0)
+ * (l nil))
+ * (while (< i 127)
+ * (setq l (cons i l)
+ * i (1+ i)))
+ * l))
+ *
+ * ;; A list of all characters 127 and above, each appearing once.
+ * (setq meta-chars
+ * (let ((i 127)
+ * (l nil))
+ * (while (< i 256)
+ * (setq l (cons i l)
+ * i (1+ i)))
+ * l))
+ *
+ * ;; A vector that will hold the chars in a random order.
+ * (setq scrambled-chars (make-vector 256 0))
+ *
+ * ;; These characters should map to themselves.
+ * (let ((i 0))
+ * (while (< i 32)
+ * (aset scrambled-chars i i)
+ * (setq non-meta-chars (delete i non-meta-chars)
+ * i (1+ i))))
+ *
+ * ;; Assign random (but unique) values, within the non-meta chars.
+ * (let ((i 32))
+ * (while (< i 127)
+ * (let ((ch (random-elt non-meta-chars)))
+ * (if (= 0 (aref scrambled-chars i))
+ * (progn
+ * (aset scrambled-chars i ch)
+ * (aset scrambled-chars ch i)
+ * (setq non-meta-chars (delete ch non-meta-chars)
+ * non-meta-chars (delete i non-meta-chars))))
+ * (setq i (1+ i)))))
+ *
+ * ;; Assign random (but unique) values, within the non-meta chars.
+ * (let ((i 127))
+ * (while (< i 256)
+ * (let ((ch (random-elt meta-chars)))
+ * (if (= 0 (aref scrambled-chars i))
+ * (progn
+ * (aset scrambled-chars i ch)
+ * (aset scrambled-chars ch i)
+ * (setq meta-chars (delete ch meta-chars)
+ * meta-chars (delete i meta-chars))))
+ * (setq i (1+ i)))))
+ *
+ * ;; Now use the `scrambled-chars' vector to get your C array.
+ * )
+ */
diff --git a/gnu/usr.bin/cvs/src/server.h b/gnu/usr.bin/cvs/src/server.h
index d0560be7661..cb49267e991 100644
--- a/gnu/usr.bin/cvs/src/server.h
+++ b/gnu/usr.bin/cvs/src/server.h
@@ -79,6 +79,12 @@ extern void server_update_entries
enum progs {PROG_CHECKIN, PROG_UPDATE};
extern void server_prog PROTO((char *, char *, enum progs));
+extern void server_cleanup PROTO((int sig));
+
+#ifdef SERVER_FLOWCONTROL
+/* Pause if it's convenient to avoid memory blowout */
+extern void server_check_pause PROTO((void));
+#endif /* SERVER_FLOWCONTROL */
#endif /* SERVER_SUPPORT */
diff --git a/gnu/usr.bin/cvs/src/status.c b/gnu/usr.bin/cvs/src/status.c
index d987a91ac8c..eb12fe9f7a4 100644
--- a/gnu/usr.bin/cvs/src/status.c
+++ b/gnu/usr.bin/cvs/src/status.c
@@ -82,12 +82,12 @@ status (argc, argv)
if (local)
send_arg("-l");
+ send_file_names (argc, argv);
/* XXX This should only need to send file info; the file
contents themselves will not be examined. */
send_files (argc, argv, local, 0);
- if (fprintf (to_server, "status\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("status\012", 0);
err = get_responses_and_close ();
return err;
@@ -95,8 +95,8 @@ status (argc, argv)
#endif
/* start the recursion processor */
- err = start_recursion (status_fileproc, (int (*) ()) NULL, status_dirproc,
- (int (*) ()) NULL, argc, argv, local,
+ err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, status_dirproc,
+ (DIRLEAVEPROC) NULL, argc, argv, local,
W_LOCAL, 0, 1, (char *) NULL, 1, 0);
return (err);
diff --git a/gnu/usr.bin/cvs/src/subr.c b/gnu/usr.bin/cvs/src/subr.c
index b94fc8abfa5..228581c5b1e 100644
--- a/gnu/usr.bin/cvs/src/subr.c
+++ b/gnu/usr.bin/cvs/src/subr.c
@@ -76,6 +76,18 @@ xstrdup (str)
return (s);
}
+/* Remove trailing newlines from STRING, destructively. */
+void
+strip_trailing_newlines (str)
+ char *str;
+{
+ int len;
+ len = strlen (str) - 1;
+
+ while (str[len] == '\n')
+ str[len--] = '\0';
+}
+
/*
* Recover the space allocated by Find_Names() and line2argv()
*/
@@ -148,8 +160,8 @@ getcaller ()
if (uid == (uid_t) 0)
{
/* super-user; try getlogin() to distinguish */
- if (((name = getenv("LOGNAME")) || (name = getenv("USER")) ||
- (name = getlogin ())) && *name)
+ if (((name = getlogin ()) || (name = getenv("LOGNAME")) ||
+ (name = getenv("USER"))) && *name)
return (name);
}
if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
diff --git a/gnu/usr.bin/cvs/src/tag.c b/gnu/usr.bin/cvs/src/tag.c
index a2544afd8d5..21da5f4d733 100644
--- a/gnu/usr.bin/cvs/src/tag.c
+++ b/gnu/usr.bin/cvs/src/tag.c
@@ -12,6 +12,7 @@
*/
#include "cvs.h"
+#include "save-cwd.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)tag.c 1.60 94/09/30 $";
@@ -59,7 +60,7 @@ static List *tlist;
static const char *const tag_usage[] =
{
- "Usage: %s %s [-lRF] [-b] [-d] tag [files...]\n",
+ "Usage: %s %s [-lRF] [-b] [-d] [-r tag|-D date] tag [files...]\n",
"\t-l\tLocal directory only, not recursive.\n",
"\t-R\tProcess directories recursively.\n",
"\t-d\tDelete the given Tag.\n",
@@ -138,6 +139,8 @@ tag (argc, argv)
argc--;
argv++;
+ if (date && numtag)
+ error (1, 0, "-r and -D options are mutually exclusive");
if (delete && branch_mode)
error (0, 0, "warning: -b ignored with -d options");
RCS_check_tag (symtag);
@@ -159,28 +162,32 @@ tag (argc, argv)
if (force_tag_move)
send_arg("-F");
+ if (numtag)
+ option_with_arg ("-r", numtag);
+ if (date)
+ client_senddate (date);
+
send_arg (symtag);
-#if 0
+ send_file_names (argc, argv);
/* FIXME: We shouldn't have to send current files, but I'm not sure
whether it works. So send the files --
it's slower but it works. */
- send_file_names (argc, argv);
-#else
send_files (argc, argv, local, 0);
-#endif
- if (fprintf (to_server, "tag\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("tag\012", 0);
return get_responses_and_close ();
}
#endif
+ if (numtag != NULL)
+ tag_check_valid (numtag, argc, argv, local, 0, "");
+
/* 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,
- (Dtype (*) ()) NULL, (int (*) ()) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
argc, argv, local, W_LOCAL, 0, 1,
(char *) NULL, 1, 0);
@@ -190,8 +197,8 @@ tag (argc, argv)
}
/* start the recursion processor */
- err = start_recursion (tag_fileproc, (int (*) ()) NULL, tag_dirproc,
- (int (*) ()) NULL, argc, argv, local,
+ err = start_recursion (tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc,
+ (DIRLEAVEPROC) NULL, argc, argv, local,
W_LOCAL, 0, 1, (char *) NULL, 1, 0);
dellist(&mtlist);
return (err);
@@ -242,13 +249,13 @@ check_fileproc(file, update_dir, repository, entries, srcfiles)
p->delproc = tag_delproc;
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
file, 0, 0, entries, srcfiles);
- p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match);
+ p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
if (p->data != NULL)
{
int addit = 1;
char *oversion;
- oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
if (oversion == NULL)
{
if (delete)
@@ -409,7 +416,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles)
nversion = RCS_getversion(vers->srcfile,
numtag,
date,
- force_tag_match);
+ force_tag_match, 0);
if (nversion == NULL)
{
freevers_ts (&vers);
@@ -428,7 +435,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles)
* "rcs" to remove the tag... trust me.
*/
- version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
if (version == NULL || vers->srcfile == NULL)
{
freevers_ts (&vers);
@@ -507,7 +514,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles)
* module -- which I have found to be a typical tagging operation.
*/
rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
- oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
if (oversion != NULL)
{
int isbranch = RCS_isbranch (file, symtag, srcfiles);
@@ -580,3 +587,215 @@ tag_dirproc (dir, repos, update_dir)
error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir);
return (R_PROCESS);
}
+
+/* Code relating to the val-tags file. Note that this file has no way
+ of knowing when a tag has been deleted. The problem is that there
+ is no way of knowing whether a tag still exists somewhere, when we
+ delete it some places. Using per-directory val-tags files (in
+ CVSREP) might be better, but that might slow down the process of
+ verifying that a tag is correct (maybe not, for the likely cases,
+ if carefully done), and/or be harder to implement correctly. */
+
+struct val_args {
+ char *name;
+ int found;
+};
+
+/* Pass as a static until we get around to fixing start_recursion to pass along
+ a void * where we can stash it. */
+static struct val_args *val_args_static;
+
+static int val_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+val_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ RCSNode *rcsdata;
+ Node *node;
+ struct val_args *args = val_args_static;
+
+ node = findnode (srcfiles, file);
+ if (node == NULL)
+ /* Not sure this can happen, after all we passed only
+ W_REPOS | W_ATTIC. */
+ return 0;
+ rcsdata = (RCSNode *) node->data;
+ if (RCS_gettag (rcsdata, args->name, 1, 0) != NULL)
+ {
+ /* FIXME: should find out a way to stop the search at this point. */
+ args->found = 1;
+ }
+ return 0;
+}
+
+static Dtype val_direntproc PROTO ((char *, char *, char *));
+
+static Dtype
+val_direntproc (dir, repository, update_dir)
+ char *dir;
+ char *repository;
+ char *update_dir;
+{
+ /* 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 0;
+ return R_SKIP_ALL;
+}
+
+/* Check to see whether NAME is a valid tag. If so, return. If not
+ print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify
+ which files we will be operating on.
+
+ REPOSITORY is the repository if we need to cd into it, or NULL if
+ we are already there, or "" if we should do a W_LOCAL recursion.
+ Sorry for three cases, but the "" case is needed in case the
+ working directories come from diverse parts of the repository, the
+ NULL case avoids an unneccesary chdir, and the non-NULL, non-""
+ case is needed for checkout, where we don't want to chdir if the
+ tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
+ local directory. */
+void
+tag_check_valid (name, argc, argv, local, aflag, repository)
+ char *name;
+ int argc;
+ char **argv;
+ int local;
+ int aflag;
+ char *repository;
+{
+ DBM *db;
+ char *valtags_filename;
+ int err;
+ datum mytag;
+ struct val_args the_val_args;
+ struct saved_cwd cwd;
+ int which;
+
+ /* Numeric tags require only a syntactic check. */
+ if (isdigit (name[0]))
+ {
+ char *p;
+ for (p = name; *p != '\0'; ++p)
+ {
+ if (!(isdigit (*p) || *p == '.'))
+ error (1, 0, "\
+Numeric tag %s contains characters other than digits and '.'", name);
+ }
+ return;
+ }
+
+ mytag.dptr = name;
+ mytag.dsize = strlen (name);
+
+ valtags_filename = xmalloc (strlen (CVSroot) + sizeof CVSROOTADM
+ + sizeof CVSROOTADM_HISTORY + 20);
+ strcpy (valtags_filename, CVSroot);
+ strcat (valtags_filename, "/");
+ strcat (valtags_filename, CVSROOTADM);
+ strcat (valtags_filename, "/");
+ strcat (valtags_filename, CVSROOTADM_VALTAGS);
+ db = dbm_open (valtags_filename, O_RDWR, 0666);
+ if (db == NULL)
+ {
+ if (!existence_error (errno))
+ error (1, errno, "cannot read %s", valtags_filename);
+
+ /* If the file merely fails to exist, we just keep going and create
+ it later if need be. */
+ }
+ else
+ {
+ datum val;
+
+ val = dbm_fetch (db, mytag);
+ if (val.dptr != NULL)
+ {
+ /* Found. The tag is valid. */
+ dbm_close (db);
+ free (valtags_filename);
+ return;
+ }
+ /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
+ }
+
+ /* We didn't find the tag in val-tags, so look through all the RCS files
+ to see whether it exists there. Yes, this is expensive, but there
+ is no other way to cope with a tag which might have been created
+ by an old version of CVS, from before val-tags was invented. */
+
+ the_val_args.name = name;
+ the_val_args.found = 0;
+ val_args_static = &the_val_args;
+
+ which = W_REPOS | W_ATTIC;
+
+ if (repository != NULL)
+ {
+ if (repository[0] == '\0')
+ which |= W_LOCAL;
+ else
+ {
+ if (save_cwd (&cwd))
+ exit (1);
+ if (chdir (repository) < 0)
+ error (1, errno, "cannot change to %s directory", repository);
+ }
+ }
+
+ err = start_recursion (val_fileproc, (FILESDONEPROC) NULL,
+ val_direntproc, (DIRLEAVEPROC) NULL,
+ argc, argv, local, which, aflag,
+ 1, NULL, 1, 0);
+ if (repository != NULL && repository[0] != '\0')
+ {
+ if (restore_cwd (&cwd, NULL))
+ exit (1);
+ free_cwd (&cwd);
+ }
+
+ if (!the_val_args.found)
+ error (1, 0, "no such tag %s", name);
+ else
+ {
+ /* The tags is valid but not mentioned in val-tags. Add it. */
+ datum value;
+
+ if (noexec)
+ {
+ if (db != NULL)
+ dbm_close (db);
+ free (valtags_filename);
+ return;
+ }
+
+ if (db == NULL)
+ {
+ mode_t omask;
+ omask = umask (cvsumask);
+ db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ (void) umask (omask);
+
+ if (db == NULL)
+ {
+ error (0, errno, "cannot create %s", valtags_filename);
+ free (valtags_filename);
+ return;
+ }
+ }
+ value.dptr = "y";
+ value.dsize = 1;
+ if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
+ error (0, errno, "cannot store %s into %s", name,
+ valtags_filename);
+ dbm_close (db);
+ }
+ free (valtags_filename);
+}
diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c
index 0e01faeda74..cae9cb32690 100644
--- a/gnu/usr.bin/cvs/src/update.c
+++ b/gnu/usr.bin/cvs/src/update.c
@@ -34,12 +34,12 @@
*/
#include "cvs.h"
-#ifdef CLIENT_SUPPORT
-#include "update.h"
-#endif
#ifdef SERVER_SUPPORT
#include "md5.h"
#endif
+#include "watch.h"
+#include "fileattr.h"
+#include "edit.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)update.c 1.95 94/10/22 $";
@@ -63,11 +63,9 @@ static Dtype update_dirent_proc PROTO((char *dir, char *repository, char *update
static int update_dirleave_proc PROTO((char *dir, int err, char *update_dir));
static int update_file_proc PROTO((char *file, char *update_dir, char *repository,
List * entries, List * srcfiles));
-#ifndef CLIENT_SUPPORT
-static int update_filesdone_proc PROTO((int err, char *repository, char *update_dir));
-#endif
+static int update_filesdone_proc PROTO((int err, char *repository,
+ char *update_dir));
static int write_letter PROTO((char *file, int letter, char *update_dir));
-static void ignore_files PROTO((List * ilist, char *update_dir));
#ifdef SERVER_SUPPORT
static void join_file PROTO((char *file, List *srcfiles, Vers_TS *vers_ts,
char *update_dir, List *entries, char *repository));
@@ -89,11 +87,7 @@ static int pipeout = 0;
#ifdef SERVER_SUPPORT
static int patches = 0;
#endif
-#ifdef CLIENT_SUPPORT
-List *ignlist = (List *) NULL;
-#else
static List *ignlist = (List *) NULL;
-#endif
static time_t last_register_time;
static const char *const update_usage[] =
{
@@ -226,8 +220,6 @@ update (argc, argv)
start_server ();
- ign_setup ();
-
if (local)
send_arg("-l");
if (update_build_dirs)
@@ -270,7 +262,10 @@ update (argc, argv)
}
if (failed_patches == NULL)
+ {
+ send_file_names (argc, argv);
send_files (argc, argv, local, aflag);
+ }
else
{
int i;
@@ -286,6 +281,7 @@ update (argc, argv)
for (i = 0; i < failed_patches_count; i++)
(void) unlink_file (failed_patches[i]);
+ send_file_names (failed_patches_count, failed_patches);
send_files (failed_patches_count, failed_patches, local,
aflag);
}
@@ -293,8 +289,7 @@ update (argc, argv)
failed_patches = NULL;
failed_patches_count = 0;
- if (fprintf (to_server, "update\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("update\012", 0);
status = get_responses_and_close ();
if (status != 0)
@@ -306,6 +301,11 @@ update (argc, argv)
}
#endif
+ if (tag != NULL)
+ tag_check_valid (tag, argc, argv, local, aflag, "");
+ /* FIXME: We don't call tag_check_valid on join_rev1 and join_rev2
+ yet (make sure to handle ':' correctly if we do, though). */
+
/*
* 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
@@ -315,7 +315,7 @@ update (argc, argv)
{
if (update_build_dirs)
{
- if (unlink_file (CVSADM_ENTSTAT) < 0 && errno != ENOENT)
+ if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
#ifdef SERVER_SUPPORT
if (server_active)
@@ -552,10 +552,10 @@ update_file_proc (file, update_dir, repository, entries, srcfiles)
* If the timestamps differ, look for Conflict
* indicators to see if 'C' anyway.
*/
- run_setup ("%s -s", GREP);
+ run_setup ("%s", GREP);
run_arg (RCS_MERGE_PAT);
run_arg (file);
- retcode = run_exec (RUN_TTY, RUN_TTY,
+ retcode = run_exec (RUN_TTY, DEVNULL,
RUN_TTY,RUN_NORMAL);
if (retcode == -1)
{
@@ -668,16 +668,18 @@ update_file_proc (file, update_dir, repository, entries, srcfiles)
return (retval);
}
-/*
- * update_filesdone_proc () is used
- */
+static void update_ignproc PROTO ((char *, char *));
+
+static void
+update_ignproc (file, dir)
+ char *file;
+ char *dir;
+{
+ (void) write_letter (file, '?', dir);
+}
+
/* ARGSUSED */
-#ifdef CLIENT_SUPPORT
-/* Also used by client.c */
-int
-#else
static int
-#endif
update_filesdone_proc (err, repository, update_dir)
int err;
char *repository;
@@ -686,19 +688,12 @@ update_filesdone_proc (err, repository, update_dir)
/* if this directory has an ignore list, process it then free it */
if (ignlist)
{
- ignore_files (ignlist, update_dir);
+ ignore_files (ignlist, update_dir, update_ignproc);
dellist (&ignlist);
}
/* Clean up CVS admin dirs if we are export */
-#ifdef CLIENT_SUPPORT
- /* In the client, we need to clean these up after we create them. Doing
- it here might would clean up the user's previous contents even on
- SIGINT which probably is bad. */
- if (!client_active && strcmp (command_name, "export") == 0)
-#else
if (strcmp (command_name, "export") == 0)
-#endif
{
run_setup ("%s -fr", RM);
run_arg (CVSADM);
@@ -712,14 +707,7 @@ update_filesdone_proc (err, repository, update_dir)
#endif /* SERVER_SUPPORT */
{
/* If there is no CVS/Root file, add one */
-#ifdef CLIENT_SUPPORT
- if (!isfile (CVSADM_ROOT)
- /* but only if we want it */
- && ! (getenv ("CVS_IGNORE_REMOTE_ROOT") && strchr (CVSroot, ':'))
- )
-#else /* No CLIENT_SUPPORT */
if (!isfile (CVSADM_ROOT))
-#endif /* No CLIENT_SUPPORT */
Create_Root( (char *) NULL, CVSroot );
}
#endif /* CVSADM_ROOT */
@@ -780,7 +768,7 @@ update_dirent_proc (dir, repository, update_dir)
char tmp[PATH_MAX];
(void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
- if (unlink_file (tmp) < 0 && errno != ENOENT)
+ if (unlink_file (tmp) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", tmp);
#ifdef SERVER_SUPPORT
if (server_active)
@@ -846,6 +834,7 @@ update_dirleave_proc (dir, err, update_dir)
free (repository);
}
+ /* FIXME: chdir ("..") loses with symlinks. */
/* Prune empty dirs on the way out - if necessary */
(void) chdir ("..");
if (update_prune_dirs && isemptydir (dir))
@@ -938,7 +927,7 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
if (!file_is_dead) {
#endif
- run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs,
+ run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_tag,
vers_ts->options);
/*
@@ -985,31 +974,65 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
if (file_is_dead && joining())
{
- /* when joining, we need to get dead files checked
- out. Try harder. */
- run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs,
- vers_ts->options);
-
- run_arg ("-f");
- run_arg (vers_ts->srcfile->path);
- run_arg (file);
- if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ if (RCS_getversion (vers_ts->srcfile, join_rev1,
+ date_rev1, 1, 0)
+ || (join_rev2 != NULL &&
+ RCS_getversion (vers_ts->srcfile, join_rev2,
+ date_rev2, 1, 0)))
{
- error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
- "could not check out %s", file);
- (void) unlink_file (backup);
- return (retcode);
+ /* when joining, we need to get dead files checked
+ out. Try harder. */
+ run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO,
+ vers_ts->vn_rcs,
+ vers_ts->options);
+
+ run_arg ("-f");
+ run_arg (vers_ts->srcfile->path);
+ run_arg (file);
+ retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ if (retcode != 0)
+ {
+ error (retcode == -1 ? 1 : 0,
+ retcode == -1 ? errno : 0,
+ "could not check out %s", file);
+ (void) unlink_file (backup);
+ return (retcode);
+ }
+ file_is_dead = 0;
+ resurrecting = 1;
+ }
+ else
+ {
+ /* If the file is dead and does not contain either of
+ the join revisions, then we don't want to check it
+ out. */
+ return 0;
}
- file_is_dead = 0;
- resurrecting = 1;
}
+#endif /* DEATH_SUPPORT */
- if (cvswrite == TRUE && !file_is_dead)
- xchmod (file, 1);
-#else /* No DEATH_SUPPORT */
- if (cvswrite == TRUE)
+ if (cvswrite == TRUE
+#ifdef DEATH_SUPPORT
+ && !file_is_dead
+#endif
+ && !fileattr_get (file, "_watched"))
xchmod (file, 1);
-#endif /* No DEATH_SUPPORT */
+
+ {
+ /* 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 (file, getcaller (), NULL);
+
+ memset (&args, 0, sizeof args);
+ args.remove_temp = 1;
+ watch_modify_watchers (file, &args);
+ }
/* set the time from the RCS file iff it was unknown before */
if (vers_ts->vn_user == NULL ||
@@ -1026,6 +1049,20 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
force_tag_match, set_time, entries, srcfiles);
if (strcmp (xvers_ts->options, "-V4") == 0)
xvers_ts->options[0] = '\0';
+ /* If no keyword expansion was specified on command line,
+ use whatever was in the file. This is how we tell the client
+ whether a file is binary. */
+ if (xvers_ts->options[0] == '\0')
+ {
+ if (vers_ts->srcfile->expand != NULL)
+ {
+ free (xvers_ts->options);
+ xvers_ts->options =
+ xmalloc (strlen (vers_ts->srcfile->expand) + 3);
+ strcpy (xvers_ts->options, "-k");
+ strcat (xvers_ts->options, vers_ts->srcfile->expand);
+ }
+ }
(void) time (&last_register_time);
@@ -1044,7 +1081,7 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
update_dir, file);
}
Scratch_Entry (entries, file);
- if (unlink_file (file) < 0 && errno != ENOENT)
+ if (unlink_file (file) < 0 && ! existence_error (errno))
{
if (update_dir[0] == '\0')
error (0, errno, "cannot remove %s", file);
@@ -1203,7 +1240,8 @@ patch_file (file, repository, entries, srcfiles, vers_ts, update_dir,
else
{
rename_file (file, file2);
- if (cvswrite == TRUE)
+ if (cvswrite == TRUE
+ && !fileattr_get (file, "_watched"))
xchmod (file2, 1);
e = fopen (file2, "r");
if (e == NULL)
@@ -1544,9 +1582,20 @@ join_file (file, srcfiles, vers, update_dir, entries)
return;
}
+ /* Fix for bug CVS/193:
+ * Used to dump core if the file had been removed on the current branch.
+ */
+ if (strcmp(vers->vn_user, "0") == 0)
+ {
+ error(0, 0,
+ "file %s has been deleted",
+ file);
+ return;
+ }
+
/* convert the second rev spec, walking branches and dates. */
- rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1);
+ rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, 0);
if (rev2 == NULL)
{
if (!quiet)
@@ -1598,7 +1647,7 @@ join_file (file, srcfiles, vers, update_dir, entries)
abort();
}
- tst = RCS_gettag (vers->srcfile, rev2, 1);
+ tst = RCS_gettag (vers->srcfile, rev2, 1, 0);
if (tst == NULL)
{
/* this should not be possible. */
@@ -1621,7 +1670,7 @@ join_file (file, srcfiles, vers, update_dir, entries)
/* otherwise, convert the first rev spec, walking branches and
dates. */
- rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1);
+ rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, 0);
if (rev1 == NULL)
{
if (!quiet) {
@@ -1645,7 +1694,7 @@ join_file (file, srcfiles, vers, update_dir, entries)
/* special handling when two revisions are specified */
if (join_rev1 && join_rev2)
{
- rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1);
+ rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1, 0);
if (rev == NULL)
{
if (!quiet && date_rev2 == NULL)
@@ -1654,7 +1703,7 @@ join_file (file, srcfiles, vers, update_dir, entries)
return;
}
- baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
+ baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
if (baserev == NULL)
{
if (!quiet && date_rev1 == NULL)
@@ -1682,7 +1731,7 @@ join_file (file, srcfiles, vers, update_dir, entries)
}
else
{
- rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
+ rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0);
if (rev == NULL)
return;
if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */
@@ -1769,7 +1818,17 @@ join_file (file, srcfiles, vers, update_dir, entries)
free (rev1);
free (rev2);
+#ifdef SERVER_SUPPORT
+ /*
+ * If we're in server mode, then we need to re-register the file
+ * even if there were no conflicts (status == 0).
+ * This tells server_updated() to send the modified file back to
+ * the client.
+ */
+ if (status == 1 || (status == 0 && server_active))
+#else
if (status == 1)
+#endif
{
char *cp = 0;
@@ -1791,80 +1850,6 @@ join_file (file, srcfiles, vers, update_dir, entries)
#endif
}
-/*
- * Process the current directory, looking for files not in ILIST and not on
- * the global ignore list for this directory.
- */
-static void
-ignore_files (ilist, update_dir)
- List *ilist;
- char *update_dir;
-{
- DIR *dirp;
- struct dirent *dp;
- struct stat sb;
- char *file;
- char *xdir;
-
- /* we get called with update_dir set to "." sometimes... strip it */
- if (strcmp (update_dir, ".") == 0)
- xdir = "";
- else
- xdir = update_dir;
-
- dirp = opendir (".");
- if (dirp == NULL)
- return;
-
- ign_add_file (CVSDOTIGNORE, 1);
- wrap_add_file (CVSDOTWRAPPER, 1);
-
- while ((dp = readdir (dirp)) != NULL)
- {
- file = dp->d_name;
- if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
- continue;
- if (findnode (ilist, file) != NULL)
- continue;
-
- if (
-#ifdef DT_DIR
- dp->d_type != DT_UNKNOWN ||
-#endif
- lstat(file, &sb) != -1)
- {
-
- if (
-#ifdef DT_DIR
- dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN &&
-#endif
- S_ISDIR(sb.st_mode))
- {
- char temp[PATH_MAX];
-
- (void) sprintf (temp, "%s/%s", file, CVSADM);
- if (isdir (temp))
- continue;
- }
-#ifdef S_ISLNK
- else if (
-#ifdef DT_DIR
- dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN &&
-#endif
- S_ISLNK(sb.st_mode))
- {
- continue;
- }
-#endif
- }
-
- if (ign_name (file))
- continue;
- (void) write_letter (file, '?', xdir);
- }
- (void) closedir (dirp);
-}
-
int
joining ()
{
diff --git a/gnu/usr.bin/cvs/src/update.h b/gnu/usr.bin/cvs/src/update.h
index 68c91d55ab9..bad6562fb02 100644
--- a/gnu/usr.bin/cvs/src/update.h
+++ b/gnu/usr.bin/cvs/src/update.h
@@ -1,9 +1,21 @@
-/* Definitions of routines shared between local and client/server
- "update" code. */
+/* Declarations for update.c.
-/* List of files that we have either processed or are willing to
- ignore. Any file not on this list gets a question mark printed. */
-extern List *ignlist;
+ 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.
-extern int
-update_filesdone_proc PROTO((int err, char *repository, char *update_dir));
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+int do_update PROTO((int argc, char *argv[], char *xoptions, char *xtag,
+ char *xdate, int xforce, int local, int xbuild,
+ int xaflag, int xprune, int xpipeout, int which,
+ char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir));
+int joining PROTO((void));
diff --git a/gnu/usr.bin/cvs/src/vers_ts.c b/gnu/usr.bin/cvs/src/vers_ts.c
index 2fc912521df..ebb7ca8d305 100644
--- a/gnu/usr.bin/cvs/src/vers_ts.c
+++ b/gnu/usr.bin/cvs/src/vers_ts.c
@@ -13,8 +13,6 @@ static const char rcsid[] = "$CVSid: @(#)vers_ts.c 1.45 94/10/07 $";
USE(rcsid);
#endif
-#define ctime(X) do not use ctime, please
-
#ifdef SERVER_SUPPORT
static void time_stamp_server PROTO((char *, Vers_TS *));
#endif
@@ -148,10 +146,28 @@ Version_TS (repository, options, tag, date, user, force_tag_match,
{
#endif
if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
+ {
vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
+ vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
+ }
else
+ {
vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
- vers_ts->date, force_tag_match);
+ vers_ts->date, force_tag_match, 1);
+ if (vers_ts->vn_rcs == NULL)
+ vers_ts->vn_tag = NULL;
+ else
+ {
+ char *colon = strchr (vers_ts->vn_rcs, ':');
+ if (colon)
+ {
+ vers_ts->vn_tag = xstrdup (colon+1);
+ *colon = '\0';
+ }
+ else
+ vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
+ }
+ }
#ifndef DEATH_SUPPORT
}
#endif
@@ -205,7 +221,7 @@ time_stamp_server (file, vers_ts)
if (stat (file, &sb) < 0)
{
- if (errno != ENOENT)
+ if (! existence_error (errno))
error (1, errno, "cannot stat temp file");
if (use_unchanged)
{
@@ -291,6 +307,8 @@ freevers_ts (versp)
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)
diff --git a/gnu/usr.bin/cvs/src/version.c b/gnu/usr.bin/cvs/src/version.c
index e01fb7aef50..e2ddd0fa331 100644
--- a/gnu/usr.bin/cvs/src/version.c
+++ b/gnu/usr.bin/cvs/src/version.c
@@ -17,7 +17,7 @@ static const char rcsid[] = "$CVSid: @(#)version.c 1.15 94/10/03 $";
USE(rcsid);
#endif
-char *version_string = "\nConcurrent Versions System (CVS) 1.6";
+char *version_string = "\nConcurrent Versions System (CVS) 1.7.1";
#ifdef CLIENT_SUPPORT
#ifdef SERVER_SUPPORT
diff --git a/gnu/usr.bin/cvs/src/watch.c b/gnu/usr.bin/cvs/src/watch.c
new file mode 100644
index 00000000000..b84926fb429
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/watch.c
@@ -0,0 +1,530 @@
+/* Implementation for "cvs watch add", "cvs watchers", and related commands
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "cvs.h"
+#include "edit.h"
+#include "fileattr.h"
+#include "watch.h"
+
+const char *const watch_usage[] =
+{
+ "Usage: %s %s [on|off|add|remove] [-l] [-a action] [files...]\n",
+ "on/off: turn on/off read-only checkouts of files\n",
+ "add/remove: add or remove notification on actions\n",
+ "-l (on/off/add/remove): Local directory only, not recursive\n",
+ "-a (add/remove): Specify what actions, one of\n",
+ " edit,unedit,commit,all,none\n",
+ NULL
+};
+
+static struct addremove_args the_args;
+
+void
+watch_modify_watchers (file, what)
+ char *file;
+ struct addremove_args *what;
+{
+ char *curattr = fileattr_get0 (file, "_watchers");
+ char *p;
+ char *pend;
+ char *nextp;
+ char *who;
+ int who_len;
+ char *mycurattr;
+ char *mynewattr;
+ size_t mynewattr_size;
+
+ int add_edit_pending;
+ int add_unedit_pending;
+ int add_commit_pending;
+ int remove_edit_pending;
+ int remove_unedit_pending;
+ int remove_commit_pending;
+ int add_tedit_pending;
+ int add_tunedit_pending;
+ int add_tcommit_pending;
+
+ who = getcaller ();
+ who_len = strlen (who);
+
+ /* Look for current watcher types for this user. */
+ mycurattr = NULL;
+ if (curattr != NULL)
+ {
+ p = curattr;
+ while (1) {
+ if (strncmp (who, p, who_len) == 0
+ && p[who_len] == '>')
+ {
+ /* Found this user. */
+ mycurattr = p + who_len + 1;
+ }
+ p = strchr (p, ',');
+ if (p == NULL)
+ break;
+ ++p;
+ }
+ }
+ if (mycurattr != NULL)
+ {
+ mycurattr = xstrdup (mycurattr);
+ p = strchr (mycurattr, ',');
+ if (p != NULL)
+ *p = '\0';
+ }
+
+ /* Now copy mycurattr to mynewattr, making the requisite modifications.
+ Note that we add a dummy '+' to the start of mynewattr, to reduce
+ special cases (but then we strip it off when we are done). */
+
+ mynewattr_size = sizeof "+edit+unedit+commit+tedit+tunedit+tcommit";
+ if (mycurattr != NULL)
+ mynewattr_size += strlen (mycurattr);
+ mynewattr = xmalloc (mynewattr_size);
+ mynewattr[0] = '\0';
+
+ add_edit_pending = what->adding && what->edit;
+ add_unedit_pending = what->adding && what->unedit;
+ add_commit_pending = what->adding && what->commit;
+ remove_edit_pending = !what->adding && what->edit;
+ remove_unedit_pending = !what->adding && what->unedit;
+ remove_commit_pending = !what->adding && what->commit;
+ add_tedit_pending = what->add_tedit;
+ add_tunedit_pending = what->add_tunedit;
+ add_tcommit_pending = what->add_tcommit;
+
+ /* Copy over existing watch types, except those to be removed. */
+ p = mycurattr;
+ while (p != NULL)
+ {
+ pend = strchr (p, '+');
+ if (pend == NULL)
+ {
+ pend = p + strlen (p);
+ nextp = NULL;
+ }
+ else
+ nextp = pend + 1;
+
+ /* Process this item. */
+ if (pend - p == 4 && strncmp ("edit", p, 4) == 0)
+ {
+ if (!remove_edit_pending)
+ strcat (mynewattr, "+edit");
+ add_edit_pending = 0;
+ }
+ else if (pend - p == 6 && strncmp ("unedit", p, 6) == 0)
+ {
+ if (!remove_unedit_pending)
+ strcat (mynewattr, "+unedit");
+ add_unedit_pending = 0;
+ }
+ else if (pend - p == 6 && strncmp ("commit", p, 6) == 0)
+ {
+ if (!remove_commit_pending)
+ strcat (mynewattr, "+commit");
+ add_commit_pending = 0;
+ }
+ else if (pend - p == 5 && strncmp ("tedit", p, 5) == 0)
+ {
+ if (!what->remove_temp)
+ strcat (mynewattr, "+tedit");
+ add_tedit_pending = 0;
+ }
+ else if (pend - p == 7 && strncmp ("tunedit", p, 7) == 0)
+ {
+ if (!what->remove_temp)
+ strcat (mynewattr, "+tunedit");
+ add_tunedit_pending = 0;
+ }
+ else if (pend - p == 7 && strncmp ("tcommit", p, 7) == 0)
+ {
+ if (!what->remove_temp)
+ strcat (mynewattr, "+tcommit");
+ add_tcommit_pending = 0;
+ }
+ else
+ {
+ char *mp;
+
+ /* Copy over any unrecognized watch types, for future
+ expansion. */
+ mp = mynewattr + strlen (mynewattr);
+ *mp++ = '+';
+ strncpy (mp, p, pend - p);
+ *(mp + (pend - p)) = '\0';
+ }
+
+ /* Set up for next item. */
+ p = nextp;
+ }
+
+ /* Add in new watch types. */
+ if (add_edit_pending)
+ strcat (mynewattr, "+edit");
+ if (add_unedit_pending)
+ strcat (mynewattr, "+unedit");
+ if (add_commit_pending)
+ strcat (mynewattr, "+commit");
+ if (add_tedit_pending)
+ strcat (mynewattr, "+tedit");
+ if (add_tunedit_pending)
+ strcat (mynewattr, "+tunedit");
+ if (add_tcommit_pending)
+ strcat (mynewattr, "+tcommit");
+
+ {
+ char *curattr_new;
+
+ curattr_new =
+ fileattr_modify (curattr,
+ who,
+ mynewattr[0] == '\0' ? NULL : mynewattr + 1,
+ '>',
+ ',');
+ /* If the attribute is unchanged, don't rewrite the attribute file. */
+ if (!((curattr_new == NULL && curattr == NULL)
+ || (curattr_new != NULL
+ && curattr != NULL
+ && strcmp (curattr_new, curattr) == 0)))
+ fileattr_set (file,
+ "_watchers",
+ curattr_new);
+ if (curattr_new != NULL)
+ free (curattr_new);
+ }
+
+ if (curattr != NULL)
+ free (curattr);
+ if (mycurattr != NULL)
+ free (mycurattr);
+}
+
+static int addremove_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+addremove_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ watch_modify_watchers (file, &the_args);
+ return 0;
+}
+
+static int addremove_filesdoneproc PROTO ((int, char *, char *));
+
+static int
+addremove_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ if (the_args.setting_default)
+ watch_modify_watchers (NULL, &the_args);
+ return err;
+}
+
+static int watch_addremove PROTO ((int argc, char **argv));
+
+static int
+watch_addremove (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int local = 0;
+ int err;
+ int a_omitted;
+
+ a_omitted = 1;
+ the_args.commit = 0;
+ the_args.edit = 0;
+ the_args.unedit = 0;
+ optind = 1;
+ while ((c = getopt (argc, argv, "la:")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'a':
+ a_omitted = 0;
+ if (strcmp (optarg, "edit") == 0)
+ the_args.edit = 1;
+ else if (strcmp (optarg, "unedit") == 0)
+ the_args.unedit = 1;
+ else if (strcmp (optarg, "commit") == 0)
+ the_args.commit = 1;
+ else if (strcmp (optarg, "all") == 0)
+ {
+ the_args.edit = 1;
+ the_args.unedit = 1;
+ the_args.commit = 1;
+ }
+ else if (strcmp (optarg, "none") == 0)
+ {
+ the_args.edit = 0;
+ the_args.unedit = 0;
+ the_args.commit = 0;
+ }
+ else
+ usage (watch_usage);
+ break;
+ case '?':
+ default:
+ usage (watch_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (a_omitted)
+ {
+ the_args.edit = 1;
+ the_args.unedit = 1;
+ the_args.commit = 1;
+ }
+
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ start_server ();
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ /* FIXME: copes poorly with "all" if server is extended to have
+ new watch types and client is still running an old version. */
+ if (the_args.edit)
+ {
+ send_arg ("-a");
+ send_arg ("edit");
+ }
+ if (the_args.unedit)
+ {
+ send_arg ("-a");
+ send_arg ("unedit");
+ }
+ if (the_args.commit)
+ {
+ send_arg ("-a");
+ send_arg ("commit");
+ }
+ if (!the_args.edit && !the_args.unedit && !the_args.commit)
+ {
+ send_arg ("-a");
+ send_arg ("none");
+ }
+ send_file_names (argc, argv);
+ /* FIXME: We shouldn't have to send current files, but I'm not sure
+ whether it works. So send the files --
+ it's slower but it works. */
+ send_files (argc, argv, local, 0);
+ send_to_server (the_args.adding ?
+ "watch-add\012" : "watch-remove\012",
+ 0);
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ the_args.setting_default = (argc <= 0);
+
+ lock_tree_for_write (argc, argv, local, 0);
+
+ err = start_recursion (addremove_fileproc, addremove_filesdoneproc,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
+ 1, 0);
+
+ lock_tree_cleanup ();
+ return err;
+}
+
+int
+watch_add (argc, argv)
+ int argc;
+ char **argv;
+{
+ the_args.adding = 1;
+ return watch_addremove (argc, argv);
+}
+
+int
+watch_remove (argc, argv)
+ int argc;
+ char **argv;
+{
+ the_args.adding = 0;
+ return watch_addremove (argc, argv);
+}
+
+int
+watch (argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc == -1)
+ usage (watch_usage);
+ if (strcmp (argv[1], "on") == 0)
+ {
+ --argc;
+ ++argv;
+ return watch_on (argc, argv);
+ }
+ else if (strcmp (argv[1], "off") == 0)
+ {
+ --argc;
+ ++argv;
+ return watch_off (argc, argv);
+ }
+ else if (strcmp (argv[1], "add") == 0)
+ {
+ --argc;
+ ++argv;
+ return watch_add (argc, argv);
+ }
+ else if (strcmp (argv[1], "remove") == 0)
+ {
+ --argc;
+ ++argv;
+ return watch_remove (argc, argv);
+ }
+ else
+ usage (watch_usage);
+ return 0;
+}
+
+static const char *const watchers_usage[] =
+{
+ "Usage: %s %s [files...]\n",
+ NULL
+};
+
+static int watchers_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+static int
+watchers_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ char *them;
+ char *p;
+
+ them = fileattr_get0 (file, "_watchers");
+ if (them == NULL)
+ return 0;
+
+ if (update_dir[0] == '\0')
+ printf ("%s", file);
+ else
+ printf ("%s/%s", update_dir, file);
+
+ p = them;
+ while (1)
+ {
+ putc ('\t', stdout);
+ while (*p != '>' && *p != '\0')
+ putc (*p++, stdout);
+ if (*p == '\0')
+ {
+ /* Only happens if attribute is misformed. */
+ putc ('\n', stdout);
+ break;
+ }
+ ++p;
+ putc ('\t', stdout);
+ while (1)
+ {
+ while (*p != '+' && *p != ',' && *p != '\0')
+ putc (*p++, stdout);
+ if (*p == '\0')
+ {
+ putc ('\n', stdout);
+ goto out;
+ }
+ if (*p == ',')
+ {
+ ++p;
+ break;
+ }
+ ++p;
+ putc ('\t', stdout);
+ }
+ putc ('\n', stdout);
+ }
+ out:;
+ return 0;
+}
+
+int
+watchers (argc, argv)
+ int argc;
+ char **argv;
+{
+ int local = 0;
+ int c;
+
+ if (argc == -1)
+ usage (watchers_usage);
+
+ optind = 1;
+ while ((c = getopt (argc, argv, "l")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case '?':
+ default:
+ usage (watchers_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ start_server ();
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ send_file_names (argc, argv);
+ /* FIXME: We shouldn't have to send current files, but I'm not sure
+ whether it works. So send the files --
+ it's slower but it works. */
+ send_files (argc, argv, local, 0);
+ send_to_server ("watchers\012", 0);
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
+ 1, 0);
+}
diff --git a/gnu/usr.bin/cvs/src/watch.h b/gnu/usr.bin/cvs/src/watch.h
new file mode 100644
index 00000000000..d279c7126b5
--- /dev/null
+++ b/gnu/usr.bin/cvs/src/watch.h
@@ -0,0 +1,56 @@
+/* Interface to "cvs watch add", "cvs watchers", and related features
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern const char *const watch_usage[];
+
+/* Flags to pass between the various functions making up the
+ add/remove code. All in a single structure in case there is some
+ need to make the code reentrant some day. */
+
+struct addremove_args {
+ /* A flag for each watcher type. */
+ int edit;
+ int unedit;
+ int commit;
+
+ /* Are we adding or removing (non-temporary) edit,unedit,and/or commit
+ watches? */
+ int adding;
+
+ /* Should we add a temporary edit watch? */
+ int add_tedit;
+ /* Should we add a temporary unedit watch? */
+ int add_tunedit;
+ /* Should we add a temporary commit watch? */
+ int add_tcommit;
+
+ /* Should we remove all temporary watches? */
+ int remove_temp;
+
+ /* Should we set the default? This is here for passing among various
+ routines in watch.c (a good place for it if there is ever any reason
+ to make the stuff reentrant), not for watch_modify_watchers. */
+ int setting_default;
+};
+
+/* Modify the watchers for FILE. *WHAT tells what to do to them.
+ If FILE is NULL, modify default args (WHAT->SETTING_DEFAULT is
+ not used). */
+extern void watch_modify_watchers PROTO ((char *file,
+ struct addremove_args *what));
+
+extern int watch_add PROTO ((int argc, char **argv));
+extern int watch_remove PROTO ((int argc, char **argv));
diff --git a/gnu/usr.bin/cvs/src/wrapper.c b/gnu/usr.bin/cvs/src/wrapper.c
index 2aafb2f69ca..ec5f43e8dcf 100644
--- a/gnu/usr.bin/cvs/src/wrapper.c
+++ b/gnu/usr.bin/cvs/src/wrapper.c
@@ -217,17 +217,26 @@ wrap_add (line, isTemp)
case 'f':
if(e.fromcvsFilter)
free(e.fromcvsFilter);
- e.fromcvsFilter=xstrdup(temp);
+ e.fromcvsFilter=expand_path (temp);
+ if (!e.fromcvsFilter)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
break;
case 't':
if(e.tocvsFilter)
free(e.tocvsFilter);
- e.tocvsFilter=xstrdup(temp);
+ e.tocvsFilter=expand_path (temp);
+ if (!e.tocvsFilter)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
break;
case 'c':
if(e.conflictHook)
free(e.conflictHook);
- e.conflictHook=xstrdup(temp);
+ e.conflictHook=expand_path (temp);
+ if (!e.conflictHook)
+ error (1, 0,
+ "Invalid environmental variable string '%s'",temp);
break;
case 'm':
if(*temp=='C' || *temp=='c')