diff options
author | 1996-10-18 03:34:32 +0000 | |
---|---|---|
committer | 1996-10-18 03:34:32 +0000 | |
commit | 50bf276cd1c7e20f1eda64a5e63e0fae39e12a95 (patch) | |
tree | b3261b3bb6bd3ee497b476dbac783f5ffa519157 /gnu/usr.bin/cvs/src | |
parent | Do not run IP defragmentation routines unneccecarily; NetBSD PR# 2772 (diff) | |
download | wireguard-openbsd-50bf276cd1c7e20f1eda64a5e63e0fae39e12a95.tar.xz wireguard-openbsd-50bf276cd1c7e20f1eda64a5e63e0fae39e12a95.zip |
New release from Cyclic Software
Diffstat (limited to 'gnu/usr.bin/cvs/src')
52 files changed, 13256 insertions, 2852 deletions
diff --git a/gnu/usr.bin/cvs/src/ChangeLog b/gnu/usr.bin/cvs/src/ChangeLog index 99161b09557..fea7afaa183 100644 --- a/gnu/usr.bin/cvs/src/ChangeLog +++ b/gnu/usr.bin/cvs/src/ChangeLog @@ -1,3 +1,2217 @@ +Fri Oct 4 15:11:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_cleanup): Temporarily clear noexec when calling + unlink_file_dir. This is so we clean up the temp directory even + when the -n global option is specified. + +Wed Oct 2 10:47:33 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * client.c (send_repository): initialize some variables before + first usage + +Tue Oct 1 13:01:24 1996 Jim Blandy <jimb@floss.cyclic.com> + + Revert some of Greg's changes; they're welcome later, but we're + trying to keep CVS stable for pre-release testing at the moment. + * checkin.c, commit.c, cvs.h, diff.c, import.c, main.c, no_diff.c, + options.h.in, patch.c, rcs.c, rcs.h, rcscmds.c, sanity.sh, update.c: + Revert changes of Sep 29 and 30. + +Tue Oct 1 13:17:31 1996 Ian Lance Taylor <ian@cygnus.com> + + Make sure the server temporary directory is removed even if + Max-dotdot is used. + * server.c (orig_server_temp_dir): New static variable. + (serve_max_dotdot): Don't free server_temp_dir if it is the same + as orig_server_temp_dir. + (do_cvs_command): Use orig_server_temp_dir in error message. + (server_cleanup): Remove orig_server_temp_dir. + (server): Set orig_server_temp_dir. Remove incorrect indentation + of error message. + + * import.c (update_rcs_file): Restore new argument to + RCS_checkout, removed in last patch. + +Tue Oct 1 00:32:55 1996 Jim Blandy <jimb@floss.cyclic.com> + + * import.c: Revert Greg Woods' changes of Sep 30. We may want + them later, but not before 1.9. + +Mon Sep 30 23:31:01 1996 Jim Blandy <jimb@floss.cyclic.com> + + * log.c (log_fileproc): Now that we might actually find a "desc" + node in rcsfile->other, thanks to Ian's change below, we had + better deal correctly if we find a null pointer in it. + +Mon Sep 30 13:55:03 1996 Greg A. Woods <woods@most.weird.com> + + * main.c (main): don't set need_to_create_root for "cvs init" + either, just in case it's run from within a valid working + directory. + + * sanity.sh (testcvs): oops, forgot to comment out test version I + was using... + + * diff.c (diff_fileproc): use Diffbin instead of DIFF (3). + * patch.c (patch_fileproc): use Diffbin instead of DIFF. + * commit.c (check_fileproc): use Grepbin instead of GREP. + * rcscmds.c (RCS_merge): use Grepbin instead of GREP. + * update.c (patch_file): use Diffbin instead of DIFF. + (update_fileproc): use Grepbin instead of GREP. + * cvs.h (Diffbin): new declaration. + (Grepbin): new declaration. + (DIFFBIN_ENV): new manifest to name DIFFBIN environ var. + (GREPBIN_ENV): new manifest to name GREPBIN environ var. + * option.h.in (DIFFBIN_DFLT): renamed from DIFF. + (GREPBIN_DFLT): renamed from GREP. + * main.c (main): new variables diffbin_update_env and + grepbin_update_inv, ala rcsbin_update_env. + (main): new options -D diffbin and -g grepbin + (usg): describe new options -D diffbin and -g grepbin. + (Diffbin): new global variable for DIFF path. + (Grepfbin): new global variable for GREP path. + + * options.h.in (RCSBIN_DFLT): mention this needs to be set if + your PATH isn't set properly by rshd. + + * sanity.sh (rdiff): re-do Jim's change, but with the original + keywords I had intended (should be a bit more like real life), and + Jim's better RCS date and user matching form. + [I guess that's what I get for checking things in at 3am! ;-)] + +Mon Sep 30 17:00:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_reparsercsfile): Store desc field value in main RCS + node data, not in version specific data. + * sanity.sh: Enable log2 test (for local CVS only). + +Mon Sep 30 13:01:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (log2): New test, tests cvs add -m. Not yet enabled + in "tests" because CVS currently flunks this test. + + * sanity.sh (rdiff, basic2): Allow "cvs server" as well as "cvs + checkout" and friends in messages. In testing output of cvs + status, don't require a tab which isn't there for remote. Skip + test rdiff-9 for remote. In test basic2-64, add missing slash in + the pattern which gets used for remote. + + * sanity.sh (rdiff): Fix strings we were matching against which + got keyword-expanded in checking in sanity.sh. + +Mon Sep 30 03:21:37 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh: change all regexpr literal '.' to '\.' + (basic2): why are tests 34 & 42 commented out (because + of 'diff -u'?)? + add tests 56[abc], 57a, and 58a to test import to the main + branch (i.e. branch '1'). + (rdiff): new test section for rdiff -K, etc. + (dotest): remove dotest.ex? before running a new test. + (dotest_fail): remove dotest.ex? before running a new test. + (dotest_internal): write expected output to dotest.exp, or if $4 + also used, to dotest.ex1 and dotest.ex2. + (patch): renamed this test to 'serverpatch'. + (dotest_lit): rename dotest.res to dotest.exp ala dotest(). + remove dotest.ex? before running a new test. + (DOTSTAR): mention the bug exists up to 1.12 + (ENDANCHOR): mention the bug exists up to 1.12 + (dotest_all_in_one): new function for debugging. + (dotest_line_by_line): new function for debugging. + (dotest_internal_debug): new function for debugging. + (dotest_internal): stop emulating the ancient tests and don't spew + the dotest.tmp contents onto $LOGFILE -- it's just too much + meaningless noise. Only do this if the test fails. Many tests + don't use dotest() yet, so this isn't quite so helpful as it might + otherwise be. + (TODO): mention CVS/* files, especially CVS/Root. + + * main.c (main): add a commented out piece of code to suggest that + there should be a function lookup_command_attribute() that could + tell us various things about internal commands, such as whether + they use CVS/Root, or if they're repository-only, or if they need + a working directory, etc.... + (main): don't set need_to_create_root if command doesn't use a + local working directory. + + * patch.c (patch): CLIENT_SUPPORT: send '-f' if NOT force_tag_match + + * error.c (fperror): protect declaration for un-defined __STDC__ + + * import.c (import): permit imports to a branch with zero dots, + i.e. the trunk. + (update_rcs_file): don't detect conflicts if importing to the + trunk. + (import): add hint that we should allow a module name, instead of + just a pathname relative to $CVSROOT. + (add_rcs_file): if importing to trunk, do it with ci(1). + + * import.c: XXX the following are all #if 0'ed out until a full + implementation can be designed.... + (cbranch): new variable to support conflict detection on another + branch set by -c. + (import): truncate -b and -c optarg if to fit in static storage. + (import_usage): describe -c + + * rcscmds.c (RCS_checkout): add new argument 'rcsver'. If rcsver + is set, turn on 'keywords' to force call to RCS_exec_checkout. + * rcs.c (RCS_exec_checkout): add new argument 'rcsver'. Pass + 'rcsver' to "co" with run_arg(). + * cvs.h: (RCS_checkout): add new argument 'rcsver' to prototype. + (RCS_exec_checkout): add new argument 'rcsver' to prototype. + * commit.c (remove_file): supply new argument to RCS_checkout. + * checkin.c (Checkin): supply new argument to RCS_checkout. + * diff.c (diff_fileproc): supply new argument to RCS_checkout. + (diff_file_nodiff): supply new argument to RCS_checkout. + * no_diff.c (No_Difference): supply new argument to RCS_checkout. + * update.c (checkout_file): supply new argument to RCS_checkout. + (patch_file): supply new argument to RCS_checkout. + (join_file): supply new argument to RCS_checkout. + + * patch.c: (o_options): new variable for -K + (rcsver): new variable for -V. + (patch): add -K flag which sets o_options, change -V to set + rcsver, send o_options and rcsver if in client mode. + (patch_fileproc): use RCS_checkout instead of RCS_fast_checkout in + order to ensure $Name is expanded, use o_options if set, or + options if set, or by default "-ko" when getting "old" file. + +Sun Sep 29 16:43:28 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcscmds.c: Replace comment at top of file concerning RCS library + with a reworded version based on discussion between me, Ian, Paul + Eggert, and JimB. + +Sun Sep 29 13:09:45 1996 Noel Cragg <noel@kiva.rain.org> + + * main.c (main): don't create/update CVS/Root when doing the "cvs + login" command. Consider: if the user executes "cvs login" with + the working directory inside an already checked out module, we'd + incorrectly change the CVS/Root file to reflect the CVSROOT of the + "cvs login" command. + + * login.c (login): if we're re-logging into a server for which a + .cvspass entry already exists, copy the temporary file to its home + location rather than renaming. Renaming doesn't work between + filesystems. After copying, unlink the temporary file. + +Fri Sep 27 05:24:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * diff.c: Add comment about --brief option. + + * README-rm-add: Removed; the information which was here is now in + cvs.texinfo. + * Makefile.in (DISTFILES): Remove README-rm-add. + +Wed Sep 25 10:00:00 1996 Larry Jones <larry.jones@sdrc.com> + + * Makefile.in (cvsbug): Add dependency on version.c. + +Wed Sep 25 09:01:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * filesubr.c (get_homedir), update.c (update): Reindent. + +Wed Sep 25 04:44:54 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * version.c (version_string): Bump to 1.8.86. + +Wed Sep 25 05:17:50 1996 Jim Blandy <jimb@floss.cyclic.com> + + * update.c (update): Don't neglect to pass the -kmumble options + to the server. + * sanity.sh (binfiles-sticky): New tests for above. + + * cvsrc.c (read_cvsrc): Deal correctly with lines that specify a + command, but no options; don't corrupt argv. + + * sanity.sh: When testing rsh, use the program specified by + the CVS_RSH environment variable, if it's set. Move test to top + of file, so it runs before all other tests (it's really a + meta-test). + + * filesubr.c (get_homedir): Use getpwuid to find the home + directory, if the HOME environment variable isn't set. + * ignore.c (ign_add_file): Call get_homedir to find the user's + home directory; this is more portable than calling getpwuid. + +Tue Sep 24 09:08:17 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * log.c (log_tree): When walking through branches, follow the + ->prev field rather than following ->next which insures that the + loop only executes once and we only see the last branch. + * sanity.sh (multibranch): Test "cvs log" too; tests for above fix. + +Mon Sep 23 09:55:22 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * options.h.in: Fixed some typos in the comments and reindented + them. + +Sat Sep 21 02:33:26 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * sanity.sh: If we're testing remote CVS, make sure rsh itself is + working before running any tests. It's confusing when basica-1 + fails just because you don't have the local host in your .rhosts + file. + + * version.c (version_string): Bump to 1.8.85. + +Thu Sep 19 09:15:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * options.h.in: Define SERVER_FLOWCONTROL, SERVER_HI_WATER, + SERVER_LO_WATER. Several large sites (FreeBSD, Cygnus) have been + pounding on this code without problems, and it doesn't seem to + have any significant downsides. + +Tue Sep 17 01:13:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * status.c (status_fileproc): Instead of a default case, set sstat + before the switch. This way gcc -Wall can detect a missed case. + Add explicit T_TITLE case. + +Tue Sep 17 00:09:44 1996 Assar Westerlund <assar@pdc.kth.se> + + * login.c (login): Print usage if argc < 0. + +Tue Sep 17 00:09:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c: In comment, mention one more function of readlocks + (fileattr not updated atomically). Note similarity between + solutions #2 and #5. + + * checkout.c (safe_location): Do not reject a location merely + because it textually starts with hardpath; insist that it be + hardpath or a subdirectory thereof. + +Mon Sep 16 11:46:36 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_cleanup): Add comment about ignoring errors + from unlink_file_dir. + +Mon Sep 16 10:31:48 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: Add support for -T <tmpdir> command line option. This + is needed for servers started via inetd. + (usg): Added line for -T. Improved -z documentation. + (main): Read default for tmpdir from the environment. Test for 'T' + in getopt loop. Use '/tmp' as ultimative fallback. Update + environment if possible. + + * cvs.h (TMPDIR_ENV): Added for -T <tmpdir> command line option. + + * options.h.in: Add TMPDIR_DFLT + + * import.c (update_rcs_file): Use global variable Tmpdir instead + of reading the environment. + + * server.c (server_cleanup): Use global variable Tmpdir instead of + reading the environment. Also, replace system("rm -rf") with + unlink_file_dir. + (server): Use global variable Tmpdir instead of reading the + environment. + +Thu Sep 12 1996 Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): If ARGV0_NOT_PROGRAM_NAME, then just set + program_name to "cvs" rather than argv[0]. + +Thu Sep 12 12:06:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (update_entries): If we can't write the file, don't + make it a fatal error. + +Wed Sep 11 12:46:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): Move START_SERVER_RETURNS_SOCKET code + so that it is only run for server_method. It is wrong for + pserver_method (in which connect_to_pserver sets server_sock). + + * login.c (construct_cvspass_filename): If NO_SLASH_AFTER_HOME, + don't put a '/' between $HOME and .cvspass. Reindent function. + * build_src.com: Add zlib.c, login.c, and scramble.c. + + * rcs.c (RCS_deltas): When looking for our branch in ->branches, + check the branch number. + * sanity.sh (multibranch): New tests test for above fix. + + * commit.c (precommit_list_proc): Fix typo in last change + (->status to ->type). + +Tue Sep 10 23:05:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * Makefile.in (DISTFILES): Add build_src.com. + * build_src.com: Add buffer.c, buffer.obj, and zlib.olb. + +Tue Sep 10 20:35:23 1996 Juergen Renz <renz@conware.de> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (precommit_list_proc): Update to reflect Jul 22 change + in which p->data was changed from a Ctype to a struct + logfile_info *. This means that commitinfo scripts again get + passed the file list like they should. + +Tue Sep 10 20:35:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (auth_server_port_number): Change name of service from + "cvs" to "cvspserver". The latter is what the manual has always + recommended, and it is also officially registered with IANA. + +Tue Sep 10 11:12:42 1996 Mark A. Solinski <markso@mcs.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (socket_buffer_output): Change ifdef VMS to ifdef + SEND_NEVER_PARTIAL. + (start_server): Change ifdef VMS to ifdef START_SERVER_RETURNS_SOCKET. + +Tue Sep 10 17:15:21 1996 Jim Blandy <jimb@totoro.cyclic.com> + + * client.c (auth_server_port_number): Look up "cvs" in the + services database, and use the value it returns; fall back to + CVS_AUTH_PORT if no entry is present. + (connect_to_pserver): Use the correct port number in any error + messages. + +Tue Sep 10 11:12:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (newb): New test newb-123j0 tests for another "cvs + status" case. + +Sun Sep 8 15:20:37 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Clarify handling of options parameter. + + * rcs.c (RCS_checkout): Free buffer allocated by RCS_deltas. + +Sat Sep 7 21:28:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (struct cmd): Add comment concerning recognizing unique + abbreviations. + +Fri Sep 6 22:31:52 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (RCS_checkout): Fix indentation. + +Fri Sep 6 11:48:08 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Replace tag parameter with rev and nametag + parameters. Change all callers. + * rcs.h (RCS_checkout): Update declaration. + + * rcs.c (RCS_getversion): Replace return_both parameter with + simple_tag. Change all callers. + (RCS_gettag): Likewise. + * rcs.h (RCS_getversion, RCS_gettag): Update declarations. + * vers_ts.c (Version_TS): Simplify vn_tag initialization using new + simple_tag rather than old return_both. + * cvs.h (struct vers_ts): Clarify vn_tag comment a bit. + + * main.c (usg): Only mention -x if ENCRYPTION is defined. + (main): Mention ENCRYPTION define in comment for -x. + * client.h (krb_encrypt_buffer_initialize): Only declare if + ENCRYPTION is defined. + * client.c (start_server): Only encrypt if ENCRYPTION is defined. + * server.c (serve_kerberos_encrypt): Only define if ENCRYPTION is + defined. + (requests): Only include Kerberos-encrypt is ENCRYPTION is + defined. + (krb_encrypt_*): Only define if ENCRYPTION is defined. + +Thu Sep 5 17:32:39 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh: When testing remote, use :ext: instead of :server: to + match change made earlier today. + +Thu Sep 5 13:57:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_tcp_server): Don't allow :kserver: to mean + "direct tcp" (root.c already takes care of this, but I want to + make it clear what is intended, and not intended, here). + (start_server): Handle ext_method (external rsh program) and + server_method (internal rsh client) separately. + * client.c: Take rsh_pid and start_rsh_server out of + RSH_NOT_TRANSPARENT ifdefs. It is useful for things like SSH on NT. + * cvs.h (CVSmethod), root.c (method_names): Add ext_method. + * root.c (parse_cvsroot): Recognize "ext" access method. + If access method is not specified and CVSROOT contains a colon, + use either ext_method or server_method depending on + RSH_NOT_TRANSPARENT. + +Thu Sep 5 00:09:49 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_checkout): Remove flags parameter, which was not + serving any useful purpose. Change all callers. + * rcscmds.c (RCS_exec_checkout): Likewise. + + * rcscmds.c (RCS_exec_checkout): Rename from RCS_checkout. Change + all callers. + * rcs.c (RCS_checkout): Rename from RCS_fast_checkout. Change all + callers. + +Wed Sep 4 14:42:28 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): If tracing, output a message. If + noexec, and workfile is not NULL, just return immediately. Assert + that sout is RUN_TTY or workfile is NULL, rather than using it as + a conditional. Replace found variable with two variables--gothead + and keywords--reflecting what it actually means. + + * rcs.c (RCS_fast_checkout): Don't handle the case of workfile set + to "". + * rcscmds.c (RCS_checkout): Likewise. + * checkin.c (Checkin): Pass explicit file name, not "", to + RCS_fast_checkout. + * update.c (join_file): Likewise. + * commit.c (remove_file): Pass explicit file name to + RCS_fast_checkout and RCS_checkin. + + * rcs.c (RCS_reparsercsfile): Always continue after seeing + RCSSYMBOLS, even if the value is NULL. Clear the NODELTA flag + after setting delta_pos. + (free_rcsnode_contents): New static function. + (freercsnode): Call free_rcsnode_contents. + (RCS_fast_checkout): If NODELTA is set, reparse the RCS file. + (RCS_settag): New function. Change all callers to old function. + (RCS_deltag, RCS_setbranch): Likewise. + (RCS_lock, RCS_unlock): Likewise. + (RCS_deltas): If NODELTA is set, reparse the RCS file. + * rcs.h (NODELTA): Define. + (RCS_settag, RCS_deltag, RCS_setbranch): Declare. + (RCS_lock, RCS_unlock): Declare. + * rcscmds.c (RCS_exec_settag): Rename from RCS_settag. Don't + check tag against BASE or HEAD (now done in new RCS_settag). + (RCS_exec_deltag): Rename from RCS_deltag. + (RCS_exec_setbranch): Rename from RCS_setbranch. + (RCS_exec_lock): Rename from RCS_lock. + (RCS_exec_unlock): Rename from RCS_unlock. + * cvs.h: Update declarations of renamed functions. + * checkin.c (Checkin): Remove rcscopy variable (no longer needed + because of change in RCS_unlock call). + * commit.c: Include <assert.h>. + (remove_file): Update RCSNode path if the file is renamed. + (unblockrcs): Change rcs parameter to RCSNode. Change all + callers. + (fixbranch): Likewise. + (lock_RCS): Likewise. Don't call RCS_parsercsfile. + (checkaddfile): Update RCSNode path if the file is renamed. After + creating a new file, call RCS_parse. When stubbing a branch, use + the passed in RCSNode if there is one, rather than calling + RCS_Parse. Don't call RCS_Parse again after calling RCS_settag. + Free head and magicrev even if RCS_settag fails. + * import.c (add_rev): Change rcs parameter to RCSNode. Change all + callers. + (add_tag): Likewise. + + * rcs.c (RCS_fast_checkout): Amend last patch: if workfile is + NULL, but sout is not NULL, use sout in error message. + +Wed Sep 4 13:35:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.8. + + * Version 1.8.7. + +Wed Sep 4 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (send_file_names): Look for the name to send in + Entries even if the file doesn't exist; we should send the + name as it appears in Entries in the "rm foo; cvs update FOO" + case. + +Tue Sep 3 20:50:11 1996 William A. Hoffman <hoffman@albirio.crd.ge.com> + + * rcs.c (RCS_fast_checkout): If workfile is NULL, don't try to + include it in error message. + +Mon Aug 26 12:27:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * mkmodules.c (mkdir_if_needed): Move from here ... + * filesubr.c, cvs.h (mkdir_if_needed): ... to here. Have it + return a value saying whether the directory was created. + * client.c (call_in_directory), edit.c (edit_fileproc): Call it. + +Fri Aug 23 19:19:44 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkin.c (Checkin): Copy rcs parameter in case it is freed when + finfo->rcs is freed. + +Fri Aug 23 14:55:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * remove.c (remove_fileproc): Revert change of 23 Aug to print + getwd and finfo->file in message. The latter is redundant with + fullname and the former is redundant with fullname and the working + directory when CVS was invoked. The implementation was also + lacking as the getwd call could overflow the buffer. + +Fri Aug 23 18:40:35 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * remove.c (cvsremove): fix remove -f for client/server + +Fri Aug 23 11:28:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * wrapper.c, cvs.h: Remove conflictHook field of WrapperEntry, + WRAP_CONFLICT in WrapMergeHas, and 'c' option in wrap_add; they + are never used. + +Fri Aug 23 11:41:46 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (switch_to_user): use #ifdef SETXID_SUPPORT instead of + #if SETXID_SUPPORT + +Thu Aug 22 14:18:43 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkin.c (Checkin): Remove local variable xfinfo. Reparse the + RCS file after the checkin. Call RCS_fast_checkout rather than + RCS_checkout. + + * cvs.h (RCS_FLAGS_LOCK): Don't define. + (RCS_FLAGS_*): Adjust values to fill in hole left by removal of + RCS_FLAGS_LOCK. + * rcs.c (RCS_fast_checkout): Don't check for RCS_FLAGS_LOCK. + * rcscmds.c (RCS_checkout): Likewise. + * commit.c (commit_fileproc): Remove rcs local variable. If + status is T_MODIFIED, require that finfo->rcs be set, call + Lock_RCS directly, and don't call locate_rcs. If adding to a tag, + require that finfo->rcs be set, and don't call locate_rcs. + (remove_file): Remove rcs local variable. Require that finfo->rcs + be set. Don't call locate_rcs. Don't pass RCS_FLAGS_LOCK to + RCS_checkout; use RCS_lock instead. Call RCS_fast_checkout rather + than RCS_checkout. + (unlockrcs): Use a single rcs parameter rather than two parameters + for file and repository. Change all callers. Don't call + locate_rcs. + (fixbranch): Likewise. + (lockrcsfile): Remove; no more callers. + +Tue Aug 20 10:13:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * buffer.c, rcs.c: Don't use inline. It wasn't being used in a + loop or any such place where it would matter for performance, and + it was a (minor) portability hassle. + + * server.c (server): Change "Dummy argument 0" to "cvs server" and + add comment explaining why. + + * rcs.c (linevector_add): Add comment regarding changing \n to \0. + +Tue Aug 20 09:19:19 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * checkout.c (checkout_proc): Call RCS_parse to get the default + options from the RCS file. + + * sanity.sh (binfiles): Add tests 5.5b0 and 5.5b1 for the above fix + +Mon Aug 19 18:13:32 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (linevector_init): Make inline. Set lines_alloced to 0, + not 10. Set vector to NULL. + (linevector_add): Remove assertion that lines_alloced is greater + than zero. Initialize lines_alloced if necessary. + (linevector_copy): Initialize lines_alloced if necessary. + (linevector_free): Only free vector if it is not NULL. + (RCS_deltas): Always call linevector_init and linevector_free on + curlines, headlines, and trunklines. + (RCS_fast_checkout): Remove #if 0 around code that calls + RCS_deltas. + +Fri Aug 16 17:52:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (linevector_add): Handle zero length correctly. + (RCS_deltas): In RCS_FETCH case, the data is in headlines, not + curlines. + (RCS_fast_checkout): Update comment about RCS_deltas: the + testsuite now passes. + + * rcs.c (RCS_fully_parse): Use the length of the value, rather + than assuming that there are no embedded zero bytes. + (struct line): Add len field. + (linevector_add): Add len parameter. Change all callers. Use + len, rather than assuming that there are no embedded zero bytes. + Set the len field in new lines. + (RCS_deltas): Use the length of the value, rather than assuming + that there are no embedded zero bytes. Use the line length when + outputting it and when copying it. + (RCS_fast_checkout): Update comment about RCS_deltas to remove + note about supporting zero bytes correctly. + +Thu Aug 15 23:38:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c, import.c: Revise comments regarding the fact that we + call start_server before do_editor. + +Thu Aug 15 11:30:55 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c: Include <sys/socket.h> if AUTH_SERVER_SUPPORT. + (pserver_authenticate_connection): Set SO_KEEPALIVE on + STDIN_FILENO. + (kserver_authenticate_connection): Likewise. + +Thu Aug 15 10:26:41 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (switch_to_user): Fix previous patch to compile it for + both HAVE_KERBEROS and AUTH_SERVER_SUPPORT + +Wed Aug 14 14:02:00 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * server.c (check_password): if available use getspnam instead of + getpwnam when reading system passwords. This allows cvs pserver + to run on systems with shadow passwords. + (switch_to_user): new static function. Contains the extracted + common tail of kserver_authenticate_connection and + pserver_authenticate_connection. If compiled with SETXID_SUPPORT, + honor the setgid bit if it is set. + (check_repository_password): turn into a static function + (check_password): ditto + (pserver_authenticate_connection): little code cleanup + +Wed Aug 14 01:07:10 1996 Greg A. Woods <woods@most.weird.com> + + * history.c (history): apply fix posted by Steven Meyer + <steve@blacksmith.com> to info-cvs to correct handling of '-D' + argument. Message-Id: <9608122335.AA01385@nijel.blacksmith.com> + +Tue Aug 13 13:42:36 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (cvslog): Remove comment about calling rlog. + * rcs.c (translate_symtag): Correct typo in comment (l ist -> + list). + * server.c (server_write_entries): Add omitted word (lists) in + comment. + +Tue Aug 13 14:01:49 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * wrapper.c (wrap_rcsoption): fix memory access error + + * rcs.c (RCS_fast_checkout): fix memory access error (triggered + by an empty option string) + +Mon Aug 12 17:45:15 1996 Jim Kingdon (unknown@beezley) + + * buffer.c, zlib.c: If EIO is not defined, try to define it. + +Mon Aug 12 10:33:27 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * import.c (comtable): Add comment concerning applicability with + RCS 5.7. + + * server.c (server): If TMPDIR is not an absolute pathname, give + an error. + +Mon Aug 12 10:34:43 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: add synonym "ann" for "annotate" again + +Sun Aug 11 17:54:11 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.h (RCS_RLOG): Removed; no longer used. + +Fri Aug 9 20:16:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (dirswitch): Open the Entries file with mode "a" rather + than "w+". + (server_write_entries): Open the Entries file with mode "a" rather + than "w". + * sanity.sh (modules): Add topfiles module and 155cN tests for + above patch. + +Fri Aug 9 12:11:25 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmd): Add comment regarding synonyms. + +Thu Aug 8 14:40:10 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c: Remove synonyms for "cvs annotate". Synonyms create + user confusion. + +Thu Aug 8 10:24:04 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c: Revert (undocumented) change to rename the cvs history + alias "his" to "hist" + +Wed Aug 7 18:26:25 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (cvs_output): Change str parameter to const char *. + Correct loop to print from p, not str. + (cvs_outerr): Likewise. + * cvs.h (cvs_output, cvs_outerr): Update declarations. + + * server.c (receive_partial_file): Read and discard remaining file + data on a write error. + (serve_modified): Discard data while size > 0, not >=. + +Wed Aug 7 15:11:40 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * main.c (cmds): Add some aliases for "annotate". + (usg): Improve usage message text + (cmd_synonyms): New function to print the command synonym list + (main): Add new option --help-synonyms + +Wed Aug 7 00:07:31 1996 Ian Lance Taylor <ian@cygnus.com> + + Keep track of subdirectories in the Entries file. + * cvs.h (enum ent_type): Define. + (struct entnode): Add type field. + (struct stickydirtag): Add subdirs field. + (Subdirs_Known, Subdir_Register, Subdir_Deregister): Declare. + (ignore_files): Update declaration for new parameter. + (FILESDONEPROC): Add entries parameter. + (DIRENTPROC, DIRLEAVEPROC): Likewise. + * entries.c (Entnode_Create): Add type parameter. Change all + callers. + (write_ent_proc): If closure is not NULL, treat it as a pointer to + an int, and set it to 1 if a node is seen which is not ENT_FILE. + (write_entries): If subdirectory information is known, but no + subdirectories were written, write an unadorned D to the file. + (Scratch_Entry): Write an R command to Entries.Log. Don't rewrite + the Entries file. + (Register): Set entfilename. Write an A command rather than an + unadorned entries line. + (fgetentent): Add cmd and sawdir parameters. Change all callers. + If CMD is not NULL, expect and return a single character command. + Handle an initial D by setting the type to ENT_SUBDIR. + (fputentent): Output an initial D for an ENT_SUBDIR entry. + (Entries_Open): Handle removal commands in Entries.Log. Record + whether subdirectory information is known in the list private + data. + (Subdirs_Known): New function. + (subdir_record): New static function. + (Subdir_Register, Subdir_Deregister): New functions. + * find_names.c (add_entries_proc): Skip entries that are not + ENT_FILE. + (add_subdir_proc): New static function. + (register_subdir_proc): New static function. + (Find_Directories): If the Entries file has subdirectory + information, get the directories out of it. Otherwise, call + find_dirs, and add the information to the Entries file. + * recurse.c (struct frame_and_entries): Define. + (do_recursion): Don't call Entries_Close until after processing + dirlist. Pass entries to filesdoneproc. Pass a frame_and_entries + structure to do_dir_proc via walklist. + (do_dir_proc): Expect a frame_and_entries structure in closure, + not a recursion_frame. Pass entries to direntproc and + dirleaveproc. + * ignore.c (ignore_files): Add entries parameter. Change all + callers. If we have subdirectory information, check for + directories in entries. + * add.c (add): If client_active, call Subdir_Register on each new + directory. + (add_directory): Add entries parameter. Change caller. Call + Subdir_Register. + * checkout.c (build_dirs_and_chdir): Call Subdir_Register. + * client.c (call_in_directory): Call Subdir_Register for newly + created directories. Call Subdirs_Known or Find_Directories after + calling Entries_Open. + (process_prune_candidates): Call Subdir_Deregister. + * commit.c (findmaxrev): Skip entries that are not ENT_FILE. + * server.c (dirswitch): Call Subdir_Register. + * update.c (update_dirent_proc): Call Subdir_Register. + (update_dirleave_proc): Call Subdir_Deregister. + * Several files: Change direntproc, dirleaveproc, and + filesdoneproc routines to expect an entries argument. + + * rcs.c (translate_symtag): New static function. + (RCS_gettag): Use translate_symtag rather than RCS_symbols. + (RCS_nodeisbranch, RCS_whatbranch): Likewise. + +Tue Aug 6 15:36:09 1996 Ian Lance Taylor <ian@cygnus.com> + + Finish the conversion of cvs log so that it never invokes rlog. + * log.c (struct log_data): Remove dorlog field. Add nameonly, + header, long_header, statelist, and authorlist fields. + (log_usage): Remove rlog-options. Add -R, -h, -t, -b, -s, -w. + (cvslog): Don't clear opterr. Handle -h, -R, -s, -t, -w. If an + unrecognized option is seen, call usage. + (log_parse_list): New static function. + (log_fileproc): Remove code that called rlog. Check nameonly, + header, and long_header fields in log_data. + (log_version_requested): Check statelist and authorlist. + + * log.c (struct datelist): Define. + (struct log_data): Add datelist and singledatelist fields. + (log_usage): Add -d. + (cvslog): Handle -d. + (log_parse_date): New static function. + (log_fileproc): Do special single date handling. + (log_version_requested): Check datelist and singledatelist. + (log_fix_singledate): New static function. + +Mon Aug 5 23:48:16 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (struct option_revlist): Define. + (struct revlist): Define. + (struct log_data): Add default_branch and revlist fields. + (struct log_data_and_rcs): Define. + (log_usage): Add -N and -r. + (cvslog): Handle -N and -r. + (log_parse_revlist): New static function. + (log_fileproc): Call log_expand_revlist and log_free_revlist. + Pass log_data_and_rcs structure to log_count_print via walklist. + (log_expand_revlist, log_free_revlist): New static functions. + (log_version_requested): New static function. + (log_count_print): New static function. + (log_tree): Add log_data and revlist parameter. Change all + callers. + (log_abranch): Likewise. + (log_version): Likewise. Call log_version_requested. + (version_compare): New static function. + * sanity.sh (log): New tests for -r, -b, and -N options to log. + +Sun Aug 4 11:19:30 1996 Ian Lance Taylor <ian@cygnus.com> + + Handle simple cases of cvs log without invoking rlog. + * log.c (struct log_data): Define. + (cvslog): Use getopt to parse options. Set up a log_data + structure, and pass it to start_recursion. + (log_fileproc): Get arguments form callerdat rather than static + variables. In simple cases, print the log information directly, + rather than invoking rlog. + (log_symbol, log_count, log_tree): New static functions. + (log_abranch, log_version, log_branch): New static functions. + * rcs.h (struct rcsnode): Add other field. + (struct rcsversnode): Add other field. + (RCS_fully_parse): Declare. + * rcs.c (getrcsrev): Move declaration to start of file. + (RCS_reparsercsfile): Add all parameter. Change all callers. + (RCS_fully_parse): New function. + (freercsnode): Free other list. + (rcsvers_delproc): Free other list. + * hash.h (enum ntype): Add RCSFIELD. + * hash.c (nodetypestring): Handle RCSFIELD. + +Sat Aug 3 19:39:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * log.c (cvslog): Correct position of CLIENT_SUPPORT #endif. + +Thu Jul 25 12:06:45 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (join_file): If merging a branch, and the branch + revision does not exist, just return without doing anything. + * sanity.sh (join): Add cases file7 and file8 to test above + patch. + + * server.c (cvsencrypt): Rename from encrypt, to avoid conflict + with NetBSD unistd.h. Rename all uses. + + * server.c (krb_encrypt_buffer_output): Fix typo in comment (reply + -> replay). + +Thu Jul 25 10:37:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (krb_encrypt_buffer_output): Fix typo in comment + (krb_recv_auth -> krb_recvauth). + +Wed Jul 24 09:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c (set_lock): Adjust comment regarding why we call stat. + +Wed Jul 24 15:06:08 1996 Ian Lance Taylor <ian@cygnus.com> + + Add encryption support over a Kerberos connection. + * main.c (usg): Mention -x if CLIENT_SUPPORT. + (main): Handle -x. + * client.h (encrypt): Declare. + (krb_encrypt_buffer_initialize): Declare. + * client.c (kblock, sched): New static variables if + HAVE_KERBEROS. + (start_tcp_server): Remove sched local variable. Copy + cred.session into kblock. + (start_server): Turn on encryption if requested. + * server.c (kblock, sched): New static variables if + HAVE_KERBEROS. + (serve_kerberos_encrypt): New static function. + (requests): Add "Kerberos-encrypt" if HAVE_KERBEROS. + (kserver_authenticate_connection): Remove sched local variable. + Copy auth.session into kblock. + (encrypt): New global variable. + (struct krb_encrypt_buffer): Define. + (krb_encrypt_buffer_initialize): New function. + (krb_encrypt_buffer_input): New static function. + (krb_encrypt_buffer_output): New static function. + (krb_encrypt_buffer_flush): New static function. + (krb_encrypt_buffer_block): New static function. + (krb_encrypt_buffer_shutdown): New static function. + +Wed Jul 24 09:28:33 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * recurse.c (do_recursion): Add comment about calling + Name_Repository in !(which & W_LOCAL) case. + + * expand_path.c (expand_variable): Fix typo (varaible -> variable). + +Tue Jul 23 15:05:01 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_fileproc): In T_REMOVE_ENTRY case, only call + server_scratch_entry_only if ts_user is NULL. + * sanity.sh (death2): Add death2-20 test for above patch. + + * diff.c (diff_fileproc): If a file is not in the working + directory, check that the tag is present before warning that no + comparison is possible. + * sanity.sh (death2): Add death2-diff-9 and death2-diff-10 tests + for above patch. + +Tue Jul 23 12:05:42 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * tag.c (tag_check_valid): Fix indentation. + + * client.c (handle_e): Flush stdout before writing to stderr. + (handle_m): Flush stderr before writing to stdout. + +Fri Jul 19 16:02:11 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c: Added NO_CLIENT_GZIP_PROCESS to deal with the MacOS + client where Gzip-stream is supported, but "gzip-file-contents" is + not. + +Fri Jul 19 16:02:11 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * repos.c: Fixed recent patch which added plain fopen rather than + CVS_FOPEN + +Mon Jul 22 22:25:53 1996 Ian Lance Taylor <ian@cygnus.com> + + * logmsg.c (tag): New static variable. + (setup_tmpfile): Don't print the prefix before calling fmt_proc. + Free tag if it is set. + (find_type): Get type from logfile_info struct. + (fmt_proc): Likewise. Print tag information. Handle all prefix + printing. + (revision): Remove static variable. + (Update_Logfile): Remove xrevision parameter. Change all + callers. + (title_proc): Get type from logfile_info struct. + (logfile_write): Remove revision parameter. Change all callers. + * cvs.h (struct logfile_info): Define. + (Update_Logfile): Update prototype. + * commit.c (find_fileproc): Set logfile_info information. + (check_fileproc): Likewise. + (commit_filesdoneproc): Don't call ParseTag. + (update_delproc): Free logfile_info information. + * add.c (add_directory): Set logfile_info information. + * import.c (import): Likewise. + + * tag.c (tag_check_valid): The special BASE and HEAD tags are + always valid. + * sanity.sh (basica): Add basica-6.3 test for above patch. + +Mon Jul 22 14:41:20 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (merge_file): Pass 0 not NULL to checkout_file (20 Jul + 96 change changed other calls to checkout_file but missed this one). + +Sat Jul 20 00:21:54 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (join_file): Check whether the target of the merge is + the same as the working file revision before checking whether the + file was added during the merge. + + * update.c (scratch_file): Remove existing parameters, and add a + single parameter of type struct file_info. Change all callers. + Warn if unlink_file fails. + (checkout_file): Remove resurrecting_out parameter. Add adding + parameter. Change all callers. Remove joining code. + (join_file): Remove resurrecting parameter. Rewrite to handle + joining dead or added revisions. + * classify.c (Classify_File): If there is no user file, and the + RCS file is dead, return T_UPTODATE rather than T_CHECKOUT. + * checkout.c (checkout_proc): Set W_ATTIC if there is a join tag. + * sanity.sh (join): New set of tests for above patches. + (death): Adjust tests 86, 89, 89a, 92.1c, 95 for above patches. + (import): Adjust test 113 for above patches. + +Thu Jul 18 19:24:08 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * lock.c: Add comment explaining what locks are for. Also discuss + various changes to locking which get proposed from time to time. + + * sanity.sh (death2): Change a number of test names from death-* + to death2-*. + + * wrapper.c (wrap_setup): Don't look in repository if client_active. + * wrapper.c, cvs.h (wrap_send): New function. + * update.c (update), import.c (import): Call it. + * sanity.sh (binwrap): Do binwrap tests for remote as well as + local; tests for above fixes. + + * wrapper.c: Add a few FIXME comments. + +Thu Jul 18 18:43:50 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (patch): Fix names of a couple of tests to say patch + rather than death2. + +Thu Jul 18 16:19:21 1996 Bill Bumgarner <bbum@friday.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add), import.c (add_rcs_file): Check for options from + wrappers and use them if specified. + * cvs.h (WrapMergeHas): Add WRAP_RCSOPTION. + * wrapper.c (WrapperEntry): Add rcsOption field. + (wrap_add): Allow a single character argument to an option. + (wrap_add): Handle -k option. + (wrap_add_entry): Handle rcsOption field. + (wrap_name_has): Handle WRAP_RCSOPTION. + * wrapper.c, cvs.h (wrap_rcsoption): New function. + * add.c, import.c, wrapper.c: Minor beautification (mostly + removing trailing spaces). + * sanity.sh (binwrap): New tests test for this feature. + +Wed Jul 17 10:14:20 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkout.c (checkout): Remove extraneous else accidentally + inserted in last checkin. + +Tue Jul 16 11:37:41 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (import): Use quoting to avoid expansion of RCS ID + strings. + + * sanity.sh (import): Use dotest to examine the output of test + 113, and the actual contents of the file in test 116. + + * update.c (join_file): Always skip rcsmerge if the two revisions + are the same (the old code always did the rcsmerge when two -j + options were specified). + + * checkout.c (history_name): New static variable. + (checkout): Permit both tag and date to be specified. Set + history_name. + (checkout_proc): Use history_name when calling history_write. + * rcs.c (RCS_getversion): If both tag and date are set, use + RCS_whatbranch to get the branch revision number of a symbolic + tag. + (RCS_getdatebranch): If the branch revision itself is early + enough, then use it if the first branch is not early enough. Add + comment for invalid RCS file. Don't bother to check for NULL + before calling xstrdup, since xstrdup checks anyhow. + + * client.h (file_gzip_level): Declare. + * client.c (file_gzip_level): Define. + (start_server): Don't set gzip_level to zero after sending + Gzip-stream command. Set file_gzip_level after sending + gzip-file-contents command. + (send_modified): Use file_gzip_level rather than gzip_level. + * server.c (server_updated): Likewise. + (serve_gzip_contents): Likewise. + + * sanity.sh (patch): New tests. Test remote CVS handling of + unpatchable files. + + * sanity.sh (death2): Accept a '.' in the temporary file name + printed by diff. + + * rcscmds.c (RCS_checkin): Remove noerr parameter. Change all + callers. + * cvs.h (RCS_checkin): Update declaration. + * commit.c (remove_file): Pass RCS_FLAGS_QUIET to RCS_checkin. + + * history.c (history): Cast sizeof to int to use correct type in + error printf string. + (report_hrecs): Cast strlen result to int to use correct type in + printf string. + + * server.c (cvs_flusherr): Correct typo in comment. + + * rcs.c (getrcskey): Hoist three constant strcmp calls out of the + value reading loop. + + * fileattr.c (fileattr_get): Change parameter types from char * to + const char *. + (fileattr_get0, fileattr_modify, fileattr_set): Likewise. + (fileattr_newfile): Likewise. + * fileattr.h (fileattr_get): Update declaration. + (fileattr_get0, fileattr_modify, fileattr_set): Likewise. + (fileattr_newfile): Likewise. + +Thu May 16 11:12:18 1996 Mark P. Immel <immel@radix.net> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.h, client.c, checkout.c (client_send_expansions): + Pass an additional parameter indicating where the checkout is + to occur, to avoid passing the wrong information to send_files(). + * sanity.sh (basicb): New test basicb-cod-1 tests for above fix. + +Mon Jul 15 18:26:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * recurse.c (do_recursion): Require a repository before calling + Find_Names. + * repos.c (Name_Repository): Remove sanity checks which spend time + examining the filesystem. + +Mon Jul 15 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (send_file_names): Send file names as they appear + in CVS/Entries, rather than as specified (in cases where they + might differ in case). + (send_fileproc): Use file name from CVS/Entries (vers->entdata->user) + rather than file name as specified (finfo->file) when available. + +Sun Jul 14 15:39:44 1996 Mark Eichin <eichin@cygnus.com> + and Ian Lance Taylor <ian@cygnus.com> + + Improve diff -N handling of nonexistent tags and removed files. + * diff.c (enum diff_file): New definition for whole file, moving + unnamed enum out of diff_fileproc, renaming DIFF_NEITHER to + DIFF_DIFFERENT, and adding DIFF_SAME. + (diff): Look through the repository even if only one revision is + given. + (diff_fileproc): Change empty_file to be enum diff_file. If there + is no user revision, but there is a repository file, treat it as a + removed file. Pass empty_file to diff_file_nodiff, and set it + from the return value. + (diff_file_nodiff): Change return type to enum diff_file. Replace + just_set_rev parameter with enum diff_file empty_file parameter. + Change handling of a missing tag to return an enum diff_file value + if empty_files is set, rather than reporting an error. Free tmp + if xcmp returns 0. + * sanity.sh (death2): Add tests for above patches. + +Sat Jul 13 19:11:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (annotate): In sending options to server, reverse sense of + test so that we send -f iff -f was specified, rather than iff -f was + not specified. + +Fri Jul 12 20:23:54 1996 Greg A. Woods <woods@most.weird.com> + + * zlib.c (compress_buffer_input): add a couple of casts for + uses of z_stream's next_in and next_out + +Fri Jul 12 18:55:26 1996 Ian Lance Taylor <ian@cygnus.com> + + * zlib.c: New file. + * client.c (log_buffer_block): Call set_block and set_nonblock, + rather than lb->buf->block. + (log_buffer_shutdown): New static function. + (get_responses_and_close): Call buf_shutdown on to_server and + from_server. + (start_server): If "Gzip-stream" is supported, use it rather than + "gzip-file-contents". + * server.c (print_error): Call buf_flush rather than + buf_send_output. + (print_pending_error, serve_valid_responses): Likewise. + (serve_expand_modules, serve_valid_requests): Likewise. + (do_cvs_command): Call buf_flush rather than buf_send_output + before the fork, and in the parent after the child has completed. + In the child, set buf_to_net and buf_from_net to NULL. + (serve_gzip_stream): New static function. + (requests): Add "Gzip-stream". + (server_cleanup): Don't do anything with buf_to_net if it is + NULL. Call buf_flush rather than buf_send_output. Call + buf_shutdown on buf_to_net and buf_from_net. Call error for an + malloc failure rather than buf_output to buf_to_net. + * buffer.h (struct buffer): Add shutdown field. + (buf_initialize): Update declaration for new shutdown parameter. + (compress_buffer_initialize): Declare. + (buf_shutdown): Declare. + * buffer.c (buf_initialize): Add shutdown parameter. Change all + callers. + (buf_shutdown): New function. + * Makefile.in (SOURCES): Add zlib.c + (OBJECTS): Add zlib.o. + ($(PROGS)): Depend upon ../zlib/libz.a. + (cvs): Link against ../zlib/libz.a. + (zlib.o): New target. + +Fri Jul 12 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (log_buffer_input, log_buffer_output): Use size_t + to avoid Visual C++ signed/unsigned warnings. + +Thu Jul 11 22:01:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (handle_f): Reindent. + + * client.c (mode_to_string, handle_m, handle_e, + auth_server_port_number, get_responses_and_close), server.c + (pserver_authenticate_connection, serve_modified, + serve_enable_unchanged, wait_sig, server_cleanup): Reindent. + * server.c: Remove #if 0'd block of code above + check_repository_password; it was yanked out of some unknown + context and didn't seem to be very useful. + +Thu Jul 11 20:10:21 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (do_cvs_command): Pass new special parameter to + buf_copy_counted. If it gets set to -1, send an 'F' response if + the client supports it, and call cvs_flusherr. + (cvs_flusherr): New function. + * cvs.h (cvs_flusherr): Declare. + * client.c (handle_f): New static function. + (responses): Add "F". + * buffer.c (buf_send_special_count): New function. + (buf_copy_counted): Add special parameter. Handle negative counts + specially. + * buffer.h (buf_send_sepcial_count): Declare. + (buf_copy_counted): Update declaration. + * lock.c (lock_wait, lock_obtained): Call cvs_flusherr. + + Change the client to use the buffer data structure. + * client.c: Include "buffer.h". + (to_server): Change to be struct buffer *. + (to_server_fp): New static variable. + (from_server): Change to be struct buffer *. + (from_server_fp): New static variable. + (from_server_logfile, to_server_logfile): Remove. + (buf_memory_error): New static function. + (struct log_buffer): Define. + (log_buffer_initialize, log_buffer_input): New static functions. + (log_buffer_output, log_buffer_flush): New static functions. + (log_buffer_block): New static function. + (struct socket_buffer): Define if NO_SOCKET_TO_FD. + (socket_buffer_initialize): New static function if + NO_SOCKET_TO_FD. + (socket_buffer_input, socket_buffer_output): Likewise. + (socket_buffer_flush): Likewise. + (read_line): Rewrite to use buf_read_line. Remove eof_ok + parameter (it was always passed as 0); change all callers. + (send_to_server): Rewrite to use buf_output. + (try_read_from_server): Rewrite to use buf_read_data. + (get_responses_and_close): Use from_server_fp and to_server_fp for + the streams. Check buf_empty_p when checking for dying gasps. + (start_server): Don't set from_server_logfile and + to_server_logfile; instead, call log_buffer_initialize. If + NO_SOCKET_TO_FD and use_socket_style, call + socket_buffer_initialize; otherwise, call + stdio_buffer_initialize. + * buffer.c: Compile if CLIENT_SUPPORT is defined. + (buf_flush): Fix comment to describe return value. + (buf_read_line): Add lenp parameter. Change all callers. Look + for a line terminated by \012 rather than \n. + * buffer.h: Compile if CLIENT_SUPPORT is defined. + (buf_read_line): Update declaration. + + * server.c (server): Initialize buf_to_net, buf_from_net, + saved_output, and saved_outerr before setting error_use_protocol. + (pserver_authenticate_connection): Don't set error_use_protocol. + Errors before the authentication is complete aren't handled + cleanly anyhow. Change error call after authentication to use + printf. + +Thu Jul 11 1996 Jim Kingdon <kingdon@cyclic.com> + + * client.c (start_server): Open logfiles in binary, not text, mode. + +Wed Jul 10 19:24:22 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (print_pending_error, print_error): Remove comments + about deadlocks; they don't apply here. Add comments saying + that these functions must only be called when it is OK to + send output (which is why the deadlock concern doesn't apply). The + comments remain for server_cleanup and serve_valid_responses, + where they are an example of the "print a message and exit" + behavior which is noted in cvsclient.texi and which also exists + places like kserver_authenticate_connection. + +Wed Jul 10 18:24:46 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (print_error): Add comment warning about potential + deadlock. + (print_pending_error, serve_valid_responses): Likewise. + (server_cleanup): Likewise. + (serve_directory): Don't call buf_send_output. + (serve_modified, serve_notify, server, cvs_outerr): Likewise. + (serve_expand_modules): Call buf_send_output. + (serve_valid_requests): Likewise. + +Wed Jul 10 15:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Print a warning for rlog command. + +Wed Jul 10 15:00:55 1996 Ian Lance Taylor <ian@cygnus.com> + + Abstract the buffer data structure away from the underlying + communication medium. + * buffer.h (struct buffer): Remove fd and output fields. Add + input, output, flush, block, and closure fields. + (buf_initialize, buf_nonio_initialize): Declare. + (stdio_buffer_initialize, buf_flush): Declare. + (buf_read_line, buf_read_data): Declare. + * buffer.c: Include <assert.h>. Don't include <fcntl.h>. + (O_NONBLOCK, blocking_error): Don't define. + (buf_initialize, buf_nonio_initialize): New functions. + (buf_send_output): Use output function, rather than write. + (buf_flush): New function. + (set_nonblock, set_block): Use block function, rather than calling + fcntl. + (buf_send_counted): Don't check output. + (buf_input_data): Call input function, rather than read. + (buf_read_line, buf_read_data): New functions. + (buf_copy_lines, buf_copy_counted): Don't check output. + (stdio_buffer_initialize): New function. + (stdio_buffer_input, stdio_buffer_output): New static functions. + (stdio_bufer_flush): New static function. + * server.c: Include "getline.h". + (buf_to_net): Change to be a pointer. Change all uses. + (protocol, saved_output, saved_outerr): Likewise. + (buf_from_net): New static variable. + (no_mem_error, NO_MEM_ERROR, read_line): Remove. + (struct fd_buffer): Define. + (fd_buffer_initialize, fd_buffer_input): New static functions. + (fd_buffer_output, fd_buffer_flush): New static functions. + (fd_buffer_block): New static function. + (serve_directory): Call buf_read_line rather than read_line. + (serve_notify, server): Likewise. + (receive_partial_file): Call buf_read_data rather than fread. + (serve_modified): Call buf_read_line rather than read_line. Call + buf_read_data rather than fread. + (do_cvs_command): Initialize buffers with fd_buffer_initialize. + Change stdoutbuf, stderrbuf, and protocol_inbuf to be pointers. + (server): Initialize buffers using fd_buffer_initialize, + stdio_buffer_initialize, and buf_nonio_initialize. + (check_repository_password): Call getline rather than read_line. + +Wed Jul 10 15:51:29 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): Add comments describing a few cases + that we aren't handling. + +Tue Jul 9 04:33:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcs.c (RCS_deltas): New function, created from guts of old + annotate_fileproc. + (annotate_fileproc): Call RCS_deltas. + (RCS_fast_checkout): Call it (commented out for now; see comment + for reasons). + + * cvs.h, recurse.c (start_recursion): Add callerdat argument. + * cvs.h: Add callerdat argument to recursion processor callbacks. + * recurse.c: add it to struct recursion_frame and pass it to all + the callbacks. + * admin.c, client.c, commit.c, diff.c, edit.c, lock.c, log.c, + patch.c, rcs.c, remove.c, rtag.c, status.c, tag.c, update.c, + watch.c: Update all the functions used as callbacks. Update calls + to start_recursion. + * commit.c (find_filesdoneproc, find_fileproc, find_dirent_proc, + commit), tag.c (val_fileproc, tag_check_valid): Use callerdat + instead of a static variable. + + * recurse.c (do_recursion): Make static and move declaration to here... + * cvs.h: ...from here. + * recurse.c (do_recursion): Replace plethora of arguments with + single struct recursion_frame *. Change callers. + * recurse.c: New structure frame_and_file. Use it and existing + struct recursion_frame structures to pass info to do_file_proc and + do_dir_proc. Remove globals fileproc, filesdoneproc, direntproc, + dirleaveproc, which, flags, aflag, readlock, and dosrcs. + +Tue Jul 9 11:13:29 1996 Ian Lance Taylor <ian@cygnus.com> + + * modules.c (do_module): Call cvs_outerr rather than fprintf. + +Mon Jul 8 1996 Jim Kingdon <kingdon@cyclic.com> + + * rcs.c (RCS_fast_checkout): If -kb is not in use, open the + working file in text, not binary, mode. + +Sun Jul 7 10:36:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rcscmds.c (RCS_settag): Add comment regarding moving check for + reserved tag names to RCS_check_tag. + + * rcscmds.c: Add comment regarding librarifying RCS and related + issues. This is a lightly edited version of a message I sent to + the CVS developers and didn't get flamed for, so it would appear + to be relatively uncontroversial. + + * rcs.c (annotate): Remove comment suggesting -r option and + related functionality; it is done. + +Fri Jul 5 17:19:57 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (last_entries): Make file static, rather than function + static within call_in_directory. + (get_responses_and_close): If last_entries is not NULL, pass it to + Entries_Close. + + * server.c (server_pause_check): Check for errors when reading + from flowcontrol_pipe. + + * client.c (call_in_directory): If dir_name is ".", call + Create_Admin if there is no CVS directory. + (send_dirent_proc): If there is no CVS subdirectory, pretend that + the directory does not exist (i.e., don't try to send any files in + the directory). + * server.c (dirswitch): If dir is "." in the top level repository, + add "/." after the Repository entry. + * sanity.sh (modules): Add test 155b for above patches. + +Thu Jul 4 15:57:34 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (buf_to_net): Move definition near top of file. + (read_line): Call buf_send_output rather than fflush. + (print_error): Output information to buf_to_net buffer rather than + stdout. + (print_pending_error, serve_valid_responses): Likewise. + (server_notify, do_cvs_command, server_co): Likewise. + (expand_proc, serve_expand_modules, server_prog): Likewise. + (serve_valid_requests, server_cleanup, server): Likewise. + (server_notify): Don't call fflush on stdout. + (do_cvs_command): Flush saved_output and saved_outerr to + buf_to_net before fork. Flush buf_to_net before fork. In child, + just initialize memory_error field of saved_output and + saved_outerr. + (server_cleanup): Flush buf_to_net. + (server): Initialize saved_output and saved_outerr. + (cvs_output): Add support for error_use_protocol case. + (cvs_outerr): Likewise. + * error.c (error): In HAVE_VPRINTF case, just call cvs_outerr. + + * buffer.c: New file; buffer support functions taken from + server.c. + * buffer.h: New file; declarations for buffer.c. + * server.c: Move buffer support functions into buffer.c and + buffer.h. Include "buffer.h". + * Makefile.in (SOURCES): Add buffer.c. + (OBJECTS): Add buffer.o. + (HEADERS): Add buffer.h. + +Thu Jul 4 00:12:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.6. + +Wed Jul 3 22:31:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Version 1.8.5. + +Wed Jul 3 21:51:23 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (blocking_error): Define macro. + (buf_send_output, buf_input_data): Use blocking_error rather than + #ifdef EWOULDBLOCK. + +Tue Jul 2 20:38:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add): Change message which said "version 1.2 of foo.c + will be resurrected"; the message was confusing because it made + people think that the old contents of the file would come back + instead of the contents in the working directory. + +Mon Jul 1 01:38:57 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * find_names.c (find_dirs): Add comment explaining why we bother + with the entries stuff. + +Sat Jun 29 20:23:50 1996 Ian Lance Taylor <ian@cygnus.com> + + * find_names.c (Find_Directories): Add entries parameter, and pass + it to find_dirs. + (find_dirs): Add entries parameter, and skip all files it names. + * cvs.h (Find_Directories): Update declaration. + * recurse.c (start_recursion): Pass NULL to Find_Directories. + (do_recursion): Pass entries to Find_Directories. + + * client.c (send_modified): Add trace output. + + * diff.c (diff_fileproc): Always call diff_file_nodiff. Handle + dead versions correctly. Handle diffs between a specified + revision to a dead file correctly. + (diff_file_nodiff): Add just_set_rev parameter. Change caller. + * patch.c (patch_fileproc): Check for dead versions. + * sanity.sh (death2): Add tests for above patches. + +Fri Jun 28 20:30:48 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + For reference, this takes CVS's text segment from 271136 bytes to + 270352 bytes, a saving of 784. Not as good as I had hoped (oh well, + the source *seems* simpler at least). + * checkin.c (Checkin), commit.c (finaladd, remove_file), update.c + (join_file, checkout_file, patch_file), no_diff.c + (No_Differences), server.c (server_updated), classify.c + (Classify_File), vers_ts.c (Version_TS), diff.c (diff_file_nodiff): + Use a single struct file_info * argument instead of a bunch of + separate arguments for each of its fields. Remove local fullname + emulations. Use fullname in error messages where file had + erroneously been used. + * cvs.h: Update declarations of above functions and move them to + after the struct file_info declaration. + * server.h: Update declarations. + * add.c, admin.c, checkin.c, checkout.c, classify.c, client.c, + commit.c, diff.c, history.c, import.c, update.c, status.c, + remove.c, rtag.c, tag.c: Change callers. + + * diff.c (diff): Remove -q and -Q command options. This somehow + slipped through the cracks of the general removal of -q and -Q + command options on Jul 21 1995. Note that there is no need to + accept and ignore these options in server mode, like there is for + some of the commands, because the client has never sent -q and -Q + command options for "cvs diff". + +Fri Jun 28 16:50:18 1996 Ian Lance Taylor <ian@cygnus.com> + + * add.c (add): Pass force_tag_match as 1 when calling Version_TS. + * sanity.sh (death2): Add test for above patch. Also add + commented out test for adding a file on a nonbranch tag, which CVS + currently, mistakenly, permits. + +Thu Jun 27 23:20:49 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * diff.c (longopts): New static array. + (diff): Handle long options and new short options in diff 2.7. + Fix arbitrary limit associated with the tmp variable. + * client.c (send_option_string): Parse options as space separated, + rather than requiring all options to be single characters. + * diff.c, options.h.in: Remove CVS_DIFFDATE; the need for it is gone + now that we have --ifdef (the new behavior is the behavior which + was the default, which is that -D specifies a date). + +Wed Jun 26 22:36:29 1996 Ian Lance Taylor <ian@cygnus.com> + + * commit.c (check_fileproc): If there is a tag, permit adding a + file even if the RCS file already exists. + (checkaddfile): If there is a tag, use the file in the regular + repository, rather than the Attic, if it exists. + * sanity.sh (death2): New set of tests for above patch. + +Tue Jun 25 23:34:13 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (checkout_file): Add comments about two cases which + seem fishy. + + * sanity.sh (basic2, death): Add comments encouraging people to + stop making these sections bigger and more complex. I'm not (yet + at least) trying to figure out the ideal size for a section (my + current best estimate is 10-20 tests), but surely these + two sections are pushing the limit, whatever it is. + +Tue Jun 25 19:52:02 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (checkout_file): Rewrite handling of dead files when + joining. Avoid space leaks. Avoid unnecessary file + resurrections. + (join_file): Add checks to skip merging a dead revision onto a + dead revision, and to skip merging a common ancestor onto a dead + revision. Move check for non-existent working file after new + checks. + * sanity.sh (death): Use dotest for tests 86 and 95, and add test + death-file2-1, to test above changes. + +Mon Jun 24 11:27:37 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (merge_file): Replace file, repository, entries, and + update_dir arguments with finfo argument. Use fullname field + instead of locally emulating it. + (update_fileproc): Update caller. + (merge_file): If -kb is in effect, call it a conflict, leave + the two versions in the file and the backup file, and tell the + user to deal with it. The previous behavior was that the merge + would fail and then there was no way to do a checkin even once you + resolved the conflict (short of kludges like moving the file + aside, updating, and then moving it back). + * sanity.sh (binfiles): New tests binfiles-con* test for above + behavior. Adjust remaining tests to reflect changes in revision + numbers. + +Mon Jun 17 15:11:09 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh (import): Remove sleep. Requiring it was a bug, and + it is fixed in the current sources. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (TMPPWD): Set to real name of /tmp directory. + (basic2-64, conflicts-126.5): Use ${TMPPWD}. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcscmds.c (RCS_checkout): Remove noerr parameter. Change all + callers. + * rcs.c (RCS_fast_checkout): Likewise. + +Mon Jun 17 1996 Ian Lance Taylor <ian@cygnus.com> + + Cleaner implementation of tag locking code added Jun 13 1996: + * cvs.h (tag_lockdir, tag_unlockdir): Declare. + * rtag.c (locked_dir, locked_list): Remove. + (rtag_fileproc): Don't lock here; just call tag_lockdir. + (rtag_filesdoneproc): Don't unlock here; just call tag_unlockdir. + * tag.c (locked_dir, locked_list): Move farther down in file. + (tag_fileproc): Don't lock here; just call tag_lockdir. + (tag_filesdoneproc): Don't unlock here; just call tag_unlockdir. + (tag_lockdir, tag_unlockdir): New functions. + +Wed Jun 15 07:52:22 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (send_modified, update_entries): Fixed bug which didn't + handle binary file transfers in BROKEN_READWRITE_CONVERSION. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * update.c (checkout_file): Call server_scratch_entry_only when a + non-pertinent file is found that does not exist. + * sanity.sh (newb): Add test case for above patch. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_fileproc): Call server_scratch_entry_only when + handling T_REMOVE_ENTRY on the server. + * sanity.sh (conflicts2): Remove special case for remote server + bug fixed by above patch. + +Thu Jun 13 21:16:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (basica-9): Update to reflect change to "sufficient + access" message. + +Thu Jun 13 20:13:55 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * recurse.c, cvs.h (start_recursion): Remove wd_is_repos argument; + add comment about meaning of which argument. Use !(which & + W_LOCAL) instead of wd_is_repos. + * admin.c, client.c, commit.c, diff.c, edit.c, lock.c, log.c, + patch.c, rcs.c, remove.c, rtag.c, status.c, tag.c, update.c, + watch.c: Change callers. This is a semantic change in only two + cases: (1) tag_check_valid, where repository was not "", and (2) + the pipeout case in checkout_proc. In both of those cases the + previous setting of wd_is_repos did not reflect whether we + actually were cd'd into the repository. + * recurse.c (start_recursion): Only check for the CVS subdirectory + if which & W_LOCAL. + * sanity.sh (devcom): Add test case fixed by above patch. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * ignore.c (ignore_files): Skip based on the file name before + calling lstat. + + * client.c (last_register_time): New static variable. + (update_entries): Set last_register_time when calling Register. + (get_responses_and_close): If the current time is the same as + last_register_time, sleep for a section to avoid timestamp races. + +Thu Jun 13 17:24:38 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (supported_request): Reindent. + +Thu Jun 13 1996 Mark H. Wilkinson <mhw@minster.york.ac.uk> + + * options.h.in, mkmodules.c: Corrections to allow compilation of + non-client-server version. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * tag.c (tag_check_valid_join): New function. + * cvs.h (tag_check_valid_join): Declare. + * checkout.c (join_tags_validated): New static variable. + (checkout_proc): Check validity of join tags. + * update.c (update): Likewise. + + * tag.c (tag_check_valid): Correct sizeof CVSROOTADM_HISTORY to + use CVSROOTADM_VALTAGS. + + * lock.c (Writer_Lock): If we called lock_wait to wait for a lock, + then call lock_obtained when we get it. + (set_lock): Likewise. + (lock_obtained): New static function. + +Thu Jun 13 13:55:38 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): If we can't read cvs root, don't say "you don't + have sufficient access"; just print the message from errno. It + might be "No such file or directory" or something else for which + "you don't have sufficient access" doesn't make any sense. + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + * commit.c (remove_file): Pass noerr as 0 to RCS_checkout. + +Thu Jun 13 12:55:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * patch.c: Initialize rev1_validated and rev2_validated to 0, not 1. + +Thu Jun 13 12:55:56 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * rtag.c (locked_dir): Revise comments regarding locking; the rtag + and tag situations are different (changing from readlocking one + directory at a time to writelocking one directory at a time does + not do everything we might want, but it does fix simultaneous tags + and it doesn't make anything worse). + +Thu Jun 13 1996 Ian Lance Taylor <ian@cygnus.com> + + Prevent simultaneous tag operations from interfering with each + other. + * rtag.c (rtag_proc): Pass rtag_filesdoneproc to start_recursion, + and pass readlock as 0. + (locked_dir, locked_list): New static variables. + (rtag_fileproc): Write lock the repository if it is not already + locked. + (rtag_filesdoneproc): New static function to unlock the + repository. + * tag.c (tag): Pass tag_filesdoneproc to start_recursion, and pass + readlock as 0. + (locked_dir, locked_list): New static variables. + (tag_fileproc): Write lock the repository if it is not already + locked. + (tag_filesdoneproc): New static function. + +Thu Jun 13 11:42:25 1996 Mike Sutton <mws115@llcoolj.dayton.saic.com> + + * sanity.sh: Allow digits in usernames. + +Wed Jun 12 16:23:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (send_modified, update_entries): Reindent and add + comments to BROKEN_READWRITE_CONVERSION code. + +Wed Jun 12 16:23:03 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (send_modified, update_entries): Add + BROKEN_READWRITE_CONVERSION code. + +Mon Jun 10 20:03:16 1996 J.T. Conklin <jtc@cygnus.com> + + * rcs.c (RCS_gettag): No longer set p to NULL if rcs is also NULL. + rcs will never be null, thanks to the assertion at top of function. + +Mon Jun 10 16:28:14 1996 Ian Lance Taylor <ian@cygnus.com> + and Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Ignore CVS/Root file when doing an import. + +Fri Jun 7 18:20:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * status.c (status_fileproc, tag_list_proc): Use cvs_output rather + than writing to stdout directly. + +Wed Jun 5 13:54:57 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (force_tag_match, tag, date): New static variables. + (annotate_fileproc): Redo the loop to look for the version + specified by tag/date/force_tag_match, and handle branches + correctly. + (annotate_usage): Mention -f, -r, and -D. + (annotate): Handle -f, -r, and -D. + +Tue Jun 4 13:38:17 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (annotate_fileproc): Skip unrelated branch deltas. + +Fri Jun 7 13:04:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Change INITIALIZE_SOCKET_SUBSYSTEM to + SYSTEM_INITIALIZE and pass it pointers to argc and argv. Rename + CLEANUP_SOCKET_SUBSYSTEM to SYSTEM_CLEANUP. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * import.c (add_rcs_file): make buf char[] not unsigned char[] + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): Add CLEANUP_SOCKET_SUBSYSTEM hook at end. Revise + comments regarding INITIALIZE_SOCKET_SUBSYSTEM. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * main.c (main): Don't mess with signals if DONT_USE_SIGNALS is + defined. + +Thu Jun 6 15:32:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * modules.c (cat_module): Always format for 80 columns rather than + trying to determine how wide the screen is. The code we had for + the latter didn't cover all cases, was a portability headache, and + didn't work client/server. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * error.c: Don't declare strerror if it is #defined. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * cvs.h: If ENUMS_CAN_BE_TROUBLE, typedef Dtype to int not an enum. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * update.c (update): If DONT_USE_PATCH, don't request patches. + Also call supported_request rather than reimplementing it. + +Wed Jun 05 10:07:29 1996 Mike Ladwig <mike@twinpeaks.prc.com> + + * client.c (read_line): Changed an occurence of '\n' to '\012'. + +Wed Jun 5 17:18:46 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * add.c (add_directory): Don't create the directory if noexec. + * sanity.sh (basica): New tests basica-1a10, basica-1a11 test for + above fix. + * sanity.sh (basicb): New tests basicb-2a10, basicb-2a11, + basicb-3a1 test for analogous situation with files rather than + directories. + +Tue Jun 4 13:38:17 1996 Ian Lance Taylor <ian@cygnus.com> + + * sanity.sh: When doing a remote check, use :server: in CVSROOT. + +Wed Jun 5 13:32:40 1996 Larry Jones <larry.jones@sdrc.com> + and Jim Kingdon <kingdon@cyclic.com> + + * ignore.c: Set ign_hold to -1 when not holding instead of 0 so + that holding an empty list works correctly. + * sanity.sh (ignore): New tests 190 & 191 for above fix. + +Wed Jun 5 1996 Jim Kingdon <kingdon@cyclic.com> + + Visual C++ lint: + * client.c (update_entries): Copy the size to an unsigned variable + before comparing it with unsigned variables. + (handle_created, handle_update_existing): Prototype. + +Tue Jun 4 10:02:44 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (responses): Add Created and Update-existing responses. + * server.c (server_updated): If they are supported, use them + instead of Updated. + * client.c (struct update_entries_data): Add existp field. + (handle_checked_in, handle_updated, handle_new_entry, + handle_merged, handle_patched): Set it. + (handle_update_existing, handle_created): New functions, + for new responses. + (update_entries): Based on existp, check for + existence/nonexistence of file. + (try_read_from_server): Expand comment. + * server.c, server.h (server_updated): New argument vers. + * checkin.c (Checkin), commit.c (commit_fileproc), update.c + (update_fileproc, merge_file, join_file): Pass it. + * cvs.h: Move include of server.h after Vers_TS declaration. + * sanity.sh (conflicts2): New tests conflicts2-142d* test for + above fix. + + * sanity.sh (ignore): Fix typo in comment. + + * tag.c (tag_check_valid): Add comment clarifying when val-tags + entries are created. + +Mon Jun 3 07:26:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.4. + +Mon Jun 3 02:20:30 1996 Noel Cragg <noel@gargle.rain.org> + + * version.c: version 1.8.3. + +Thu May 30 10:07:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmds): Fix typo ("bdif" -> "dif") which was accidentally + introduced 24 May 96. + + * main.c (main_cleanup): Add comment stating default case will + never be reached. + +Wed May 29 21:43:43 1996 noel <noel@BOAT_ANCHOR> + + * main.c (main_cleanup): check to see if SIGHUP, SIGINT, SIGQUIT, + SIGPIPE, and SIGTERM are defined before using them. Also add a + default case to print out those errors numerically which are not + found. + +Wed May 29 18:43:45 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * expand_path.c (expand_path): Document LINE == 0 and allocation + of return value. + * modules.c (do_module): Pass 0, not -1, to indicate line number + not known. Free value returned from expand_path. Deal with NULL + return from expand_path. + +Wed May 29 15:56:47 1996 Greg A. Woods <woods@most.weird.com> + + * modules.c (do_module): call expand_path() on the program name + specfied by one of '-o', '-t', or '-e' in the modules file before + passing it to run_setup(). This makes it possible to use $CVSROOT + (or indeed ~user or any other user-specified variable) to specify + pathnames for programs not installed in the normal execution path. + +Sun May 26 21:57:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_server): Don't include %s in error message; + there is no argument to go with it. Do include "internal error" + in error message since that might not be clear to the user otherwise. + +Sun May 26 11:58:13 1996 Greg A. Woods <woods@most.weird.com> + + * root.c (set_local_cvsroot): enforce a wee bit of portability + (parse_cvsroot): same.... + (DEBUG main): same, plus style guidelines + (DEBUG error): deleted -- not necessary here (use fprintf instead) + + * mkmodules.c (modules_contents): updated notes about what must be + done if you change any of the options for a module. + (loginfo_contents): fixed grammar, re-pargraphed, and added 'echo + %s;' to the example. + (editinfo_contents): minor grammar fix. + +Sun May 26 17:51:18 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * vers_ts.c (Version_TS): Remove case where we get options from + sdtp->options. Whatever case that was intended to handle is + probably lost in the mists of time, but sdtp->options isn't set + anywhere, and I think that has been true for a long time. + * cvs.h (struct stickydirtag): remove options field. + * entries.c (freesdt): Don't free ->options. + * sanity.sh (binfiles): New tests binfiles-13a* test for above fix. + + * tag.c (check_fileproc): Use fullname not file in error message. + Say "locally modified" not "up-to-date"; the file need not match + the head revision it only need match some revision. + +Sun May 26 16:57:02 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * tag.c: added support for new option -c to make sure all tagged + files are up-to-date + (tag): check for option and set check_uptodate + (check_fileproc): check status of file if check_uptodate is set + +Sat May 25 15:22:26 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): Revert change to look for a -H command option; + command option parsing should be up to each subcommand and the -H + global option works fine. + +Mon May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (process_prune_candidates): Set prune_candidates to + NULL at the end of the function. + +Mon May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * checkout.c (checkout): In code to handle multiple arguments, + pass preload_update_dir, not where, to Create_Admin. + (checkout_proc): Pass preload_update_dir, not where, to + Create_Admin. + +Thu May 23 19:14:35 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * server.c (server_set_sticky): Assert that update_dir != NULL. + * sanity.sh (basicb): New test; tests for Ian's fix to checkout.c + above. + +Thu May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * patch.c (patch_fileproc): Don't ignore a file just because it is + in the Attic directory. + +Thu May 23 10:40:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (death): New tests death-{72a,76a0,76a1} test for bug + fixed by Ian's patch_fileproc change above. + + * sanity.sh (death): Remove "temporary hack" in test 89. + + * rcs.c (RCS_fast_checkout): If error closing file, and workfile + is NULL, use sout in error message instead of workfile. + +Thu May 23 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): Do a fast checkout in the case where + workfile is NULL and sout is a file name. + +Wed May 22 19:06:23 1996 Mark Immel <immel@centerline.com> + + * update.c (checkout_file): New arg resurrecting_out, to provide + resurrecting flag to caller. + (join_file): New arg resurrecting. Register with "0" if we are + the server and are resurrecting. + (update_fileproc): Pass the flag from checkout_file to join_file. + +Wed May 22 19:06:23 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (death): Test for above fix, in test 89 and new test 89a. + +Tue May 21 09:49:04 1996 Greg A. Woods <woods@most.weird.com> + + * update.c (update_usage): oops -- fix my spelling typo. + +Mon May 20 10:53:14 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * commit.c (find_fileproc): Call freevers_ts. + + * commit.c (find_*): Keep an ignlist, like update.c and client.c do. + * commit.c (commit): Process the files from the ignlists, once we + are connected to the server. + * sanity.sh (ignore): New tests 189e and 189f test for new + commit.c behavior (and client.c behavior, which is unchanged). + * sanity.sh (conflicts): Remove dir1 and sdir in parts of the test + where we aren't prepared for "? dir1" and similar output. + +Mon May 20 13:23:36 1996 Greg A. Woods <woods@most.weird.com> + + * main.c (cmd_usage): minor corrections to descriptions of status, + rtag, tag, and rdiff. Sort alphabetically by command name. + +Mon May 20 10:36:07 1996 Ian Lance Taylor <ian@cygnus.com> + + * client.c (call_in_directory): Move the call to Entries_Close + before the call to chdir, since Entries_Close examines files in + the current directory. + +Fri May 17 12:13:09 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * client.c (start_tcp_server, start_server, start_rsh_server, + read_line, filter_through_gzip, filter_through_gunzip, + call_in_directory): Reindent as needed. + + * main.c (main): Add missing #endif. Use indentation to indicate + nesting. + +Thu May 16 17:15:01 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (cmd_usage): Add "init" command. + +Thu May 16 16:45:51 1996 Noel Cragg <noel@gargle.rain.org> + + * client.c (start_tcp_server): Error message modified to tell the + user to use ":server:" instead of setting CVS_CLIENT_PORT to a + negative number. + + * main.c (main): Add #ifdefs for turning off buffering of + stdio/stderr, so we don't get it by default. + +Thu May 16 01:29:47 1996 noel <noel@BOAT_ANCHOR> + + * commit.c (commit_filesdoneproc): Print the repository and root + directories as part of the error message. + + * main.c (main): Don't buffer stdout or stderr. It's inefficient, + but it then produces the right output for sanity.sh. + +Thu May 16 09:44:47 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * fileattr.c (fileattr_set): In the case where we are about to + call delproc, don't free ->data; delproc does that. + * sanity.sh (devcom): New tests devcom-b* test for this fix. + + * sanity.sh (conflicts): Remove redundant clean up from previous + tests at the beginning of the test. Use dotest a few more places. + (conflicts2): New test, tests for Ian's fix to Classify_File. + + * client.c (remove_entry_and_file): Add comment about + existence_error's. + +Sat May 16 1996 Ian Lance Taylor <ian@cygnus.com> + + * update.c (update_dirleave_proc): Don't try to chdir .. and check + for an empty directory if there is a slash in the directory name. + +Thu May 16 09:02:59 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (deep): New tests deep-4a* test for Ian's fix to + update_dirleave_proc. + +Sat May 16 1996 Ian Lance Taylor <ian@cygnus.com> + + * main.c (main_cleanup): Report signal name before dying. + +Wed May 15 23:47:59 1996 Noel Cragg <noel@gargle.rain.org> + + * main.c (usg): revert usage strings for `-H' flag change. + +Sat May 15 1996 Ian Lance Taylor <ian@cygnus.com> + + * server.c (serve_static_directory): Return immediately if there + is a pending error. + (serve_sticky): Likewise. + (serve_modified): Read the file data even if there is a pending + error. + +Wed May 15 14:26:32 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): If -d and CVS/Root both specified, after writing + the value from -d into CVS/Root, use the value from -d, not the + old value from CVS/Root. Don't write CVS/Root with value from -d + until we have verified that it works. + * sanity.sh: Reenable test basica-9 and adjust for new behavior. + +Tue May 14 1996 Jim Kingdon <kingdon@cyclic.com> + + * logmsg.c (do_editor): If user aborts the commit, still remove the + temporary file. + +Tue May 14 11:45:41 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * filesubr.c, cvs.h (cvs_temp_name): New function. Move L_tmpnam + define from cvs.h to filesubr.c. + * client.c, diff.c, import.c, login.c, logmsg.c, no_diff.c, + patch.c, wrapper.c: Call cvs_temp_name not tmpnam. + * login.c (login): Reindent function. + +Tue May 14 10:56:56 1996 Ian Lance Taylor <ian@cygnus.com> + + * rcs.c (RCS_fast_checkout): If workfile is NULL, don't call chmod. + +Mon May 13 10:52:10 1996 Greg A. Woods <woods@most.weird.com> + + * checkout.c (export_usage): note which options cause a sticky + version to be set, and which option avoids this. + * update.c (update_usage): likewise + +Sat May 11 18:57:07 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Comment out test basica-9 until I get around to + actually fixing it (the -d vs. CVS/Root change broke it). + +Fri May 10 09:39:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c (main): -d now overrides CVS/Root. + +Thu May 9 19:45:24 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * main.c: Remove comment listing commands at beginning. It was + out of date and redundant with the help. + +Thu May 9 09:33:55 1996 Greg A. Woods <woods@most.weird.com> + + * main.c: add 'init' to opening comment listing commands + + * mkmodules.c (init): fix to recognize argc==-1 as hint to call + usage() [should make "cvs init -H" work as expected] + +Wed May 8 15:02:49 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh: Set EXPR in the case that the expr in the path is the + one that we want. + +Wed May 8 14:06:24 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh (test): - convert all '[' to test ala GCD + +Wed May 8 13:46:56 1996 Greg A. Woods <woods@most.weird.com> + + * sanity.sh (expr): - make a valiant attempt to find GNU expr + - Patch from Larry Jones: + sanity test deep-4 failed with "expr: arg list too long" + sanity test 56 failed because the stderr and stdout output was not + interleaved as expected. + sanity test modules-155a4 failed with "ls: illegal option -- 1" + + * main.c (main): - Patch from Larry Jones for SysV setvbuf + +Tue May 7 16:41:16 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * version.c: Increment version number to 1.8.2 to work around fact + that CVS 1.8 (confusingly) calls itself 1.8.1 not 1.8. + +Tue May 7 10:44:20 MET DST 1996 Norbert Kiesel <nk@col.sw-ley.de> + + * rcs.c (rcsvers_delproc): fix memory leak by freeing author + field. + +Mon May 6 10:40:05 1996 Jim Kingdon <kingdon@harvey.cyclic.com> + + * sanity.sh (conflicts): New test conflicts-126.5 tests for bug + which Ian fixed May 5 in update.c + +Mon May 6 06:00:10 1996 Benjamin J. Lee <benjamin@cyclic.com> + + * Version 1.8.1 + Sun May 5 21:39:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * vers_ts.c (Version_TS): If sdtp is NULL, go ahead and check @@ -18,6 +2232,31 @@ Sun May 5 17:38:21 1996 Benjamin J. Lee <benjamin@cyclic.com> repository which will be checked out to the same name as the directory. +Sat May 4 12:33:02 1996 Ian Lance Taylor <ian@cygnus.com> + + Extract the head revision directly from the RCS file when + possible, rather than execing co. + * rcs.c (RCS_reparsercsfile): Set delta_pos field. + (getrcskey): Add lenp parameter. Change all callers. + (RCS_fast_checkout): New function. + (annotate_fileproc): If PARTIAL is not set, just fseek to + delta_pos. + * rcs.h (struct rcsnode): Add delta_pos field. + (RCS_fast_checkout): Declare. + * diff.c (diff_file_nodiff): Call RCS_fast_checkout rather than + RCS_checkout. + * import.c (update_rcs_file): Likewise. + * no_diff.c (No_Difference): Likewise. + * patch.c (patch_fileproc): Likewise. + * update.c (checkout_file): Likewise. + (patch_file): Likewise. + (join_file): Likewise. + +Sat May 4 12:33:02 1996 Ian Lance Taylor <ian@cygnus.com> + + * classify.c (Classify_File): Don't report a conflict for a + pending remove if somebody else has already removed the file. + Thu May 2 13:34:37 1996 Benjamin J. Lee <benjamin@cyclic.com> * Version 1.7.88 @@ -28,6 +2267,18 @@ Thu May 2 01:40:55 1996 Benjamin J. Lee <benjamin@cyclic.com> located by configure, in the event a system has crypt(), but no initgroups() +Wed May 01 21:08:21 1996 noel <noel@BOAT_ANCHOR> + + * client.c (filter_through_gunzip): use "gzip -d" instead of + "gunzip," since there's no good reason (on NT at least) to have an + extra copy of gzip.exe copied to gunzip.exe (Arrrrgh! No symbolic + links!). + + * mkmodules.c (init): check to see that we have the correct number + of arguments or print out the usage message (used to be argc > 1, + should be argc != 1, because help forces argc == -1 as a special + case). + Wed May 1 18:05:02 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * sanity.sh (basica): When testing rejection of reserved tag name, @@ -57,11 +2308,198 @@ Tue Apr 30 15:46:03 1996 Jim Kingdon <kingdon@harvey.cyclic.com> * server.c (check_password): Don't use ANSI string concatenation. Reindent function. +Mon Apr 29 10:48:38 1996 Noel Cragg <noel@gargle> + + * root.c (parse_cvsroot): removed "rsh" as an alias to "server" in + the method section. + + * main.c (main): new variable help so we can support the `cvs -H + cmd' convention. Reverts change of 26 Apr 96 which removed this + feature. + +Sun Apr 28 14:57:38 1996 Noel Cragg <noel@gargle> + + * main.c (main): update error message if parse_cvsroot fails. + * server.c (serve_root): same. + (serve_init): same. + + * client.c (start_tcp_server): get rid of the "fall through" + stuff, now that we have access methods. + (start_server): switch off the access method to choose routine + that starts the server. + (start_tcp_server): tofd wasn't getting set to -1 early enough, + because a call to error for bind or gethostbyname might fail and + the subsequent error check to see if the connection had been made + would fail. + + * root.c: new variable method_names for error reporting purposes. + +Sun Apr 28 17:22:15 1996 Noel Cragg <noel@occs.cs.oberlin.edu> + + * server.c: moved kerberos #includes from main.c for the + kserver_authenticate_connection routine. + +Fri Apr 26 07:59:44 1996 Noel Cragg <noel@gargle> + + * server.c (serve_init): use the new return value from + parse_cvsroot. + (serve_root): same. + * main.c (main): same. + + * root.c (parse_cvsroot): fix indentation, add a return value + which tells whether the command succeeded or failed. + + * main.c (main): move the setting of the UMASK environment + variable inside the stuff that gets done if the user is NOT asking + for help, so we don't signal any errors prematurely (don't want to + give an error because we can't parse an environment variable + correctly if the user asks for help). Similar mods for the code + that tries to get the working directory. + + Also make CVSADM_Root a local variable instead of a global, since + its scope is only about 20 lines here! + + * server.c (kserver_authenticate_connection): moved code from + main.c to clean up MAIN. Makes sense, since we already have a + pserver_authenticate_connection. + (pserver_authenticate_connection): rename from + authenticate_connection. + + * main.c (main): reorganized the routine to eliminate variables + help, help_commands, and version_flag. Now the routine is much + clearer, since we don't have to be checking to see if these + variables are set. One behavior that was a bug/feature which is + now gone is an invocation like "cvs -H rtag" -- previously this + would give usage for rtag, but now gives usage for cvs itself. + The first behavior didn't make sense, especially since we say in + the docs that command-line flags are position-specific. *Reverted + Above* + +Thu Apr 25 20:05:10 1996 Noel Cragg <noel@gargle> + + * main.c (main): make sure we have a valid command name before we + do anything else (moved the thing that looks for a command in CMDS + to right after the GETOPT loop). Added `kserver' and `pserver' to + the table so they will be recognized; set their functions to + SERVER so that help will be given when asked for. + + * expand_path.c (expand_variable): return CVSroot_original rather + than CVSroot_directory. + + * main.c (main): save CVSroot in the env rather than + CVSroot_original, since we might not have called PARSE_CVSROOT + (this can happen if we use the -H option to a command). + + * root.c (parse_cvsroot): the parsing method was bogus for + guessing when we had hostnames vs. directories specified. Any + ambiguity should be removed by having the user specify the access + method. If the access method isn't specified, choose + server_method if the string contains a colon or local_method + otherwise. + + * Changed CVSroot_remote back to client_active since the code + reads better. + Wed Apr 24 17:27:53 1996 Norbert Kiesel <nk@col.sw-ley.de> * vers_ts.c (Version_TS): xmalloc enough space (1 more byte). Thanks to purify! +Mon Apr 22 00:38:08 1996 Noel Cragg <noel@gargle> + + * create_adm.c (Create_Admin): pass CVSroot_original instead of + CVSroot_directory (oops!). + * update.c (update_filesdone_proc): same. + + * server.c (serve_root): modify to use parse_cvsroot rather than + goofing around with other variables. Will need to fix + parse_cvsroot to have a return value so we can return an error and + quit gracefully if in server mode. + (serve_init): same. + + * main.c: modify command table to remove client_* routines, since + they no longer exist. + (main): don't try to switch off non-existent field in command + table! ;-) + + * client.h (client_*): removed prototypes for now non-existent + functions. + + * client.c: remove proto for get_cvs_password, since it is now in + cvs.h. Modify routines to use new globals that describe CVSROOT + rather than client_active, server_host, server_user, and + server_cvsroot. + (parse_cvsroot): removed function, since a more generic version + now lives in root.c. + (connect_to_pserver): remove call to parse_cvsroot, since main.c + has already done it for us. + (client_*): removed all of these routines, since they only call + parse_cvsroot and then their respective operation functions. + Since main.c has already called parse_cvsroot, we shouldn't bother + with the extra function call, since client-server diffs are + already handled in the core routines themselves. + + * main.c: remove CVSroot as a global variable. Remove + use_authenticating_server variable since we have a new + `CVSroot_method' variable instead. + (main): add `CVSroot' as a local variable. Call parse_cvsroot + after we're sure we have the right setting for `CVSroot.' + + * login.c (login): update to use new global variables. Instead of + old behavior which let the user type in user@host when prompted, + it makes them do it in CVSROOT proper. The routine still lets the + user type the password, however. + (get_cvs_password): make sure that CVSROOT is fully qualified + before trying to find the entry in the .cvspass file. + * cvs.h: add prototype for get_cvs_password. + + * add.c: use new globals that describe CVSROOT. + * admin.c: same. + * checkout.c: same. + * commit.c: same. + * create_adm.c: same. + * diff.c: same. + * edit.c: same. + * expand_path.c: same. + * history.c: same. + * ignore.c: same. + * import.c: same. + * log.c: same. + * mkmodules.c: same. + * modules.c: same. + * parseinfo.c: same. + * patch.c: same. + * rcs.c: same. + * recurse.c: same. + * release.c: same. + * remove.c: same. + * repos.c: same. + * rtag.c: same. + * status.c: same. + * tag.c: same. + * update.c: same. + * watch.c: same. + * wrapper.c: same. + + * root.c (Name_Root): remove error message that reports missing + CVSROOT, since new code in main.c will catch it and also print out + an error. + (parse_cvsroot): new function -- takes a CVSROOT string and breaks + it up into its component parts -- method, hostname, username, and + repository directory. Sets new global variables that describe the + repository location more precisely: CVSroot_original, + CVSroot_remote, CVSroot_method, CVSroot_username, + CVSroot_hostname, CVSroot_directory for use by all other + functions. Checks for obvious errors in format of string. + (main): a short routine to test parse_cvsroot from the command + line. + * cvs.h: add prototype for parse_cvsroot and extern definitions + for new globals. + + * cvs.h: removed CVSroot variable, since we don't want other + routines using the raw CVSROOT (also helped to find all of the + refs to the variable!). + Fri Apr 19 11:22:35 1996 Benjamin J. Lee <benjamin@cyclic.com> * Version 1.7.86 diff --git a/gnu/usr.bin/cvs/src/Makefile.in b/gnu/usr.bin/cvs/src/Makefile.in index acb3343f6ab..d0dea5de9d2 100644 --- a/gnu/usr.bin/cvs/src/Makefile.in +++ b/gnu/usr.bin/cvs/src/Makefile.in @@ -43,31 +43,31 @@ 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 edit.c entries.c error.c expand_path.c \ +SOURCES = add.c admin.c buffer.c checkin.c checkout.c classify.c client.c \ +commit.c create_adm.c cvsrc.c diff.c edit.c entries.c error.c expand_path.c \ fileattr.c find_names.c hash.c history.c ignore.c import.c \ lock.c log.c login.c logmsg.c main.c mkmodules.c modules.c myndbm.c no_diff.c \ parseinfo.c patch.c rcs.c rcscmds.c recurse.c release.c remove.c repos.c \ root.c rtag.c scramble.c server.c status.c subr.c filesubr.c run.c \ -tag.c update.c watch.c wrapper.c vers_ts.c version.c +tag.c update.c watch.c wrapper.c vers_ts.c version.c zlib.c -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 \ +OBJECTS = add.o admin.o buffer.o checkin.o checkout.o classify.o client.o \ +commit.o create_adm.o cvsrc.o diff.o edit.o entries.o expand_path.o \ fileattr.o find_names.o hash.o history.o ignore.o import.o \ lock.o log.o login.o logmsg.o main.o mkmodules.o modules.o myndbm.o no_diff.o \ parseinfo.o patch.o rcs.o rcscmds.o recurse.o release.o remove.o repos.o \ root.o rtag.o scramble.o server.o status.o tag.o update.o \ watch.o wrapper.o vers_ts.o \ -subr.o filesubr.o run.o version.o error.o +subr.o filesubr.o run.o version.o error.o zlib.o -HEADERS = cvs.h rcs.h hash.h myndbm.h \ +HEADERS = buffer.h 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) DISTFILES = .cvsignore Makefile.in ChangeLog ChangeLog-9395 ChangeLog-9194 \ - NOTES README-rm-add \ - sanity.sh cvsbug.sh $(TAGFILES) + NOTES \ + sanity.sh cvsbug.sh $(TAGFILES) build_src.com PROGS = cvs cvsbug @@ -151,10 +151,10 @@ dist-dir: # Linking rules. -$(PROGS): ../lib/libcvs.a +$(PROGS): ../lib/libcvs.a ../zlib/libz.a cvs: $(OBJECTS) - $(CC) $(OBJECTS) ../lib/libcvs.a $(LIBS) $(LDFLAGS) -o $@ + $(CC) $(OBJECTS) ../lib/libcvs.a ../zlib/libz.a $(LIBS) $(LDFLAGS) -o $@ xlint: $(SOURCES) files= ; \ @@ -168,7 +168,7 @@ saber: $(SOURCES) # load $(CFLAGS) $(SOURCES) # load ../lib/libcvs.a $(LIBS) -cvsbug: cvsbug.sh +cvsbug: cvsbug.sh $(srcdir)/version.c echo > .fname \ cvs-`sed < $(srcdir)/version.c \ -e '/version_string/!d' \ @@ -184,6 +184,9 @@ cvsbug: cvsbug.sh $(OBJECTS): $(HEADERS) options.h +zlib.o: zlib.c + $(CC) $(CPPFLAGS) $(INCLUDES) -I$(top_srcdir)/zlib $(DEFS) $(CFLAGS) -c $(srcdir)/zlib.c + subdir = src Makefile: ../config.status Makefile.in cd .. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status diff --git a/gnu/usr.bin/cvs/src/add.c b/gnu/usr.bin/cvs/src/add.c index 82efefe71ca..08f6acc58af 100644 --- a/gnu/usr.bin/cvs/src/add.c +++ b/gnu/usr.bin/cvs/src/add.c @@ -27,7 +27,7 @@ #include "cvs.h" #include "savecwd.h" -static int add_directory PROTO((char *repository, char *dir)); +static int add_directory PROTO((char *repository, List *, char *dir)); static int build_entry PROTO((char *repository, char *user, char *options, char *message, List * entries, char *tag)); @@ -115,6 +115,8 @@ add (argc, argv) sprintf (rcsdir, "%s/%s", repository, argv[i]); + strip_trailing_slashes (argv[i]); + Create_Admin (argv[i], argv[i], rcsdir, tag, date); if (tag) @@ -122,6 +124,19 @@ add (argc, argv) if (date) free (date); free (rcsdir); + + if (strchr (argv[i], '/') == NULL) + Subdir_Register ((List *) NULL, (char *) NULL, argv[i]); + else + { + char *cp, *b; + + cp = xstrdup (argv[i]); + b = strrchr (cp, '/'); + *b++ = '\0'; + Subdir_Register ((List *) NULL, cp, b); + free (cp); + } } send_file_names (argc, argv, SEND_EXPAND_WILD); send_files (argc, argv, 0, 0); @@ -137,6 +152,7 @@ add (argc, argv) { int begin_err = err; int begin_added_files = added_files; + struct file_info finfo; user = argv[i]; strip_trailing_slashes (user); @@ -148,8 +164,19 @@ add (argc, argv) continue; } - vers = Version_TS (repository, options, (char *) NULL, (char *) NULL, - user, 0, 0, entries, (RCSNode *) NULL); + memset (&finfo, 0, sizeof finfo); + finfo.file = user; + finfo.update_dir = ""; + finfo.fullname = user; + finfo.repository = repository; + finfo.entries = entries; + finfo.rcs = NULL; + + /* We pass force_tag_match as 1. If the directory has a + sticky branch tag, and there is already an RCS file which + does not have that tag, then the head revision is + meaningless to us. */ + vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); if (vers->vn_user == NULL) { /* No entry available, ts_rcs is invalid */ @@ -180,11 +207,25 @@ add (argc, argv) error (1, 0, "illegal filename overlap"); } + if (vers->options == NULL || *vers->options == '\0') + { + /* No options specified on command line (or in + rcs file if it existed, e.g. the file exists + on another branch). Check for a value from + the wrapper stuff. */ + if (wrap_name_has (user, WRAP_RCSOPTION)) + { + if (vers->options) + free (vers->options); + vers->options = wrap_rcsoption (user, 1); + } + } + /* There is a user file, so build the entry for it */ if (build_entry (repository, user, vers->options, message, entries, vers->tag) != 0) err++; - else + else { added_files++; if (!quiet) @@ -219,8 +260,12 @@ scheduling %s `%s' for addition on branch `%s'", error (0, 0, "file `%s' will be added on branch `%s' from version %s", user, vers->tag, vers->vn_rcs); else - error (0, 0, "version %s of `%s' will be resurrected", - vers->vn_rcs, user); + /* I'm not sure that mentioning vers->vn_rcs makes + any sense here; I can't think of a way to word the + message which is not confusing. */ + error (0, 0, "\ +re-adding file %s (in place of dead revision %s)", + user, vers->vn_rcs); Register (entries, user, "0", vers->ts_user, NULL, vers->tag, NULL, NULL); ++added_files; @@ -316,7 +361,7 @@ scheduling %s `%s' for addition on branch `%s'", && isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) { - err += add_directory (repository, user); + err += add_directory (repository, entries, user); continue; } #ifdef SERVER_SUPPORT @@ -344,8 +389,9 @@ scheduling %s `%s' for addition on branch `%s'", * Returns 1 on failure, 0 on success. */ static int -add_directory (repository, dir) +add_directory (repository, entries, dir) char *repository; + List *entries; char *dir; { char rcsdir[PATH_MAX]; @@ -371,7 +417,7 @@ add_directory (repository, dir) /* now, remember where we were, so we can get back */ if (save_cwd (&cwd)) return (1); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); return (1); @@ -413,6 +459,7 @@ add_directory (repository, dir) mode_t omask; Node *p; List *ulist; + struct logfile_info *li; #if 0 char line[MAXLINELEN]; @@ -429,14 +476,17 @@ add_directory (repository, dir) } #endif - omask = umask (cvsumask); - if (CVS_MKDIR (rcsdir, 0777) < 0) + if (!noexec) { - error (0, errno, "cannot mkdir %s", rcsdir); + omask = umask (cvsumask); + if (CVS_MKDIR (rcsdir, 0777) < 0) + { + error (0, errno, "cannot mkdir %s", rcsdir); + (void) umask (omask); + goto out; + } (void) umask (omask); - goto out; } - (void) umask (omask); /* * Set up an update list with a single title node for Update_Logfile @@ -446,9 +496,12 @@ add_directory (repository, dir) p->type = UPDATE; p->delproc = update_delproc; p->key = xstrdup ("- New directory"); - p->data = (char *) T_TITLE; + li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + li->type = T_TITLE; + li->tag = xstrdup (tag); + p->data = (char *) li; (void) addnode (ulist, p); - Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist); + Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); dellist (&ulist); } @@ -463,7 +516,16 @@ add_directory (repository, dir) if (date) free (date); + if (restore_cwd (&cwd, NULL)) + exit (EXIT_FAILURE); + free_cwd (&cwd); + + Subdir_Register (entries, (char *) NULL, dir); + (void) printf ("%s", message); + + return (0); + out: if (restore_cwd (&cwd, NULL)) exit (EXIT_FAILURE); diff --git a/gnu/usr.bin/cvs/src/admin.c b/gnu/usr.bin/cvs/src/admin.c index 214318af9c1..489f5c57898 100644 --- a/gnu/usr.bin/cvs/src/admin.c +++ b/gnu/usr.bin/cvs/src/admin.c @@ -16,8 +16,10 @@ #include <grp.h> #endif -static Dtype admin_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int admin_fileproc PROTO((struct file_info *finfo)); +static Dtype admin_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static const char *const admin_usage[] = { @@ -103,8 +105,8 @@ admin (argc, argv) /* start the recursion processor */ err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, 0, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + (DIRLEAVEPROC) NULL, NULL, argc, argv, 0, + W_LOCAL, 0, 1, (char *) NULL, 1); return (err); } @@ -113,7 +115,8 @@ admin (argc, argv) */ /* ARGSUSED */ static int -admin_fileproc (finfo) +admin_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Vers_TS *vers; @@ -123,8 +126,7 @@ admin_fileproc (finfo) int retcode = 0; int status = 0; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); version = vers->vn_user; if (version == NULL) @@ -157,10 +159,12 @@ admin_fileproc (finfo) */ /* ARGSUSED */ static Dtype -admin_dirproc (dir, repos, update_dir) +admin_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Administrating %s", update_dir); diff --git a/gnu/usr.bin/cvs/src/buffer.c b/gnu/usr.bin/cvs/src/buffer.c new file mode 100644 index 00000000000..8baa021fbb9 --- /dev/null +++ b/gnu/usr.bin/cvs/src/buffer.c @@ -0,0 +1,1294 @@ +/* Code for the buffer data structure. */ + +#include <assert.h> +#include "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +/* OS/2 doesn't have EIO. FIXME: this whole notion of turning + a different error into EIO strikes me as pretty dubious. */ +#if !defined (EIO) +#define EIO EBADPOS +#endif + +/* Linked list of available buffer_data structures. */ +static struct buffer_data *free_buffer_data; + +/* Local functions. */ +static void allocate_buffer_datas PROTO((void)); +static struct buffer_data *get_buffer_data PROTO((void)); + +/* Initialize a buffer structure. */ + +struct buffer * +buf_initialize (input, output, flush, block, shutdown, memory, closure) + int (*input) PROTO((void *, char *, int, int, int *)); + int (*output) PROTO((void *, const char *, int, int *)); + int (*flush) PROTO((void *)); + int (*block) PROTO((void *, int)); + int (*shutdown) PROTO((void *)); + void (*memory) PROTO((struct buffer *)); + void *closure; +{ + struct buffer *buf; + + buf = (struct buffer *) xmalloc (sizeof (struct buffer)); + buf->data = NULL; + buf->last = NULL; + buf->nonblocking = 0; + buf->input = input; + buf->output = output; + buf->flush = flush; + buf->block = block; + buf->shutdown = shutdown; + buf->memory_error = memory; + buf->closure = closure; + return buf; +} + +/* Initialize a buffer structure which is not to be used for I/O. */ + +struct buffer * +buf_nonio_initialize (memory) + void (*memory) PROTO((struct buffer *)); +{ + return (buf_initialize + ((int (*) PROTO((void *, char *, int, int, int *))) NULL, + (int (*) PROTO((void *, const char *, int, int *))) NULL, + (int (*) PROTO((void *))) NULL, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + (void *) NULL)); +} + +/* Allocate more buffer_data structures. */ + +static void +allocate_buffer_datas () +{ + struct buffer_data *alc; + char *space; + int i; + + /* Allocate buffer_data structures in blocks of 16. */ +#define ALLOC_COUNT (16) + + alc = ((struct buffer_data *) + malloc (ALLOC_COUNT * sizeof (struct buffer_data))); + space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE); + if (alc == NULL || space == NULL) + return; + for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE) + { + alc->next = free_buffer_data; + free_buffer_data = alc; + alc->text = space; + } +} + +/* Get a new buffer_data structure. */ + +static struct buffer_data * +get_buffer_data () +{ + struct buffer_data *ret; + + if (free_buffer_data == NULL) + { + allocate_buffer_datas (); + if (free_buffer_data == NULL) + return NULL; + } + + ret = free_buffer_data; + free_buffer_data = ret->next; + return ret; +} + +/* See whether a buffer is empty. */ + +int +buf_empty_p (buf) + struct buffer *buf; +{ + struct buffer_data *data; + + for (data = buf->data; data != NULL; data = data->next) + if (data->size > 0) + return 0; + return 1; +} + +#ifdef SERVER_FLOWCONTROL +/* + * Count how much data is stored in the buffer.. + * Note that each buffer is a malloc'ed chunk BUFFER_DATA_SIZE. + */ + +int +buf_count_mem (buf) + struct buffer *buf; +{ + struct buffer_data *data; + int mem = 0; + + for (data = buf->data; data != NULL; data = data->next) + mem += BUFFER_DATA_SIZE; + + return mem; +} +#endif /* SERVER_FLOWCONTROL */ + +/* Add data DATA of length LEN to BUF. */ + +void +buf_output (buf, data, len) + struct buffer *buf; + const char *data; + int len; +{ + if (buf->data != NULL + && (((buf->last->text + BUFFER_DATA_SIZE) + - (buf->last->bufp + buf->last->size)) + >= len)) + { + memcpy (buf->last->bufp + buf->last->size, data, len); + buf->last->size += len; + return; + } + + while (1) + { + struct buffer_data *newdata; + + newdata = get_buffer_data (); + if (newdata == NULL) + { + (*buf->memory_error) (buf); + return; + } + + if (buf->data == NULL) + buf->data = newdata; + else + buf->last->next = newdata; + newdata->next = NULL; + buf->last = newdata; + + newdata->bufp = newdata->text; + + if (len <= BUFFER_DATA_SIZE) + { + newdata->size = len; + memcpy (newdata->text, data, len); + return; + } + + newdata->size = BUFFER_DATA_SIZE; + memcpy (newdata->text, data, BUFFER_DATA_SIZE); + + data += BUFFER_DATA_SIZE; + len -= BUFFER_DATA_SIZE; + } + + /*NOTREACHED*/ +} + +/* Add a '\0' terminated string to BUF. */ + +void +buf_output0 (buf, string) + struct buffer *buf; + const char *string; +{ + buf_output (buf, string, strlen (string)); +} + +/* Add a single character to BUF. */ + +void +buf_append_char (buf, ch) + struct buffer *buf; + int ch; +{ + if (buf->data != NULL + && (buf->last->text + BUFFER_DATA_SIZE + != buf->last->bufp + buf->last->size)) + { + *(buf->last->bufp + buf->last->size) = ch; + ++buf->last->size; + } + else + { + char b; + + b = ch; + buf_output (buf, &b, 1); + } +} + +/* + * Send all the output we've been saving up. Returns 0 for success or + * errno code. If the buffer has been set to be nonblocking, this + * will just write until the write would block. + */ + +int +buf_send_output (buf) + struct buffer *buf; +{ + if (buf->output == NULL) + abort (); + + while (buf->data != NULL) + { + struct buffer_data *data; + + data = buf->data; + + if (data->size > 0) + { + int status, nbytes; + + status = (*buf->output) (buf->closure, data->bufp, data->size, + &nbytes); + if (status != 0) + { + /* Some sort of error. Discard the data, and return. */ + + buf->last->next = free_buffer_data; + free_buffer_data = buf->data; + buf->data = NULL; + buf->last = NULL; + + return status; + } + + if (nbytes != data->size) + { + /* Not all the data was written out. This is only + permitted in nonblocking mode. Adjust the buffer, + and return. */ + + assert (buf->nonblocking); + + data->size -= nbytes; + data->bufp += nbytes; + + return 0; + } + } + + buf->data = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + } + + buf->last = NULL; + + return 0; +} + +/* + * Flush any data queued up in the buffer. If BLOCK is nonzero, then + * if the buffer is in nonblocking mode, put it into blocking mode for + * the duration of the flush. This returns 0 on success, or an error + * code. + */ + +int +buf_flush (buf, block) + struct buffer *buf; + int block; +{ + int nonblocking; + int status; + + if (buf->flush == NULL) + abort (); + + nonblocking = buf->nonblocking; + if (nonblocking && block) + { + status = set_block (buf); + if (status != 0) + return status; + } + + status = buf_send_output (buf); + if (status == 0) + status = (*buf->flush) (buf->closure); + + if (nonblocking && block) + { + int blockstat; + + blockstat = set_nonblock (buf); + if (status == 0) + status = blockstat; + } + + return status; +} + +/* + * Set buffer BUF to nonblocking I/O. Returns 0 for success or errno + * code. + */ + +int +set_nonblock (buf) + struct buffer *buf; +{ + int status; + + if (buf->nonblocking) + return 0; + if (buf->block == NULL) + abort (); + status = (*buf->block) (buf->closure, 0); + if (status != 0) + return status; + buf->nonblocking = 1; + return 0; +} + +/* + * Set buffer BUF to blocking I/O. Returns 0 for success or errno + * code. + */ + +int +set_block (buf) + struct buffer *buf; +{ + int status; + + if (! buf->nonblocking) + return 0; + if (buf->block == NULL) + abort (); + status = (*buf->block) (buf->closure, 1); + if (status != 0) + return status; + buf->nonblocking = 0; + return 0; +} + +/* + * Send a character count and some output. Returns errno code or 0 for + * success. + * + * Sending the count in binary is OK since this is only used on a pipe + * within the same system. + */ + +int +buf_send_counted (buf) + struct buffer *buf; +{ + int size; + struct buffer_data *data; + + size = 0; + for (data = buf->data; data != NULL; data = data->next) + size += data->size; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return ENOMEM; + } + + data->next = buf->data; + buf->data = data; + if (buf->last == NULL) + buf->last = data; + + data->bufp = data->text; + data->size = sizeof (int); + + *((int *) data->text) = size; + + return buf_send_output (buf); +} + +/* + * Send a special count. COUNT should be negative. It will be + * handled speciallyi by buf_copy_counted. This function returns 0 or + * an errno code. + * + * Sending the count in binary is OK since this is only used on a pipe + * within the same system. + */ + +int +buf_send_special_count (buf, count) + struct buffer *buf; + int count; +{ + struct buffer_data *data; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return ENOMEM; + } + + data->next = buf->data; + buf->data = data; + if (buf->last == NULL) + buf->last = data; + + data->bufp = data->text; + data->size = sizeof (int); + + *((int *) data->text) = count; + + return buf_send_output (buf); +} + +/* Append a list of buffer_data structures to an buffer. */ + +void +buf_append_data (buf, data, last) + struct buffer *buf; + struct buffer_data *data; + struct buffer_data *last; +{ + if (data != NULL) + { + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + buf->last = last; + } +} + +/* + * Copy the contents of file F into buffer_data structures. We can't + * copy directly into an buffer, because we want to handle failure and + * succeess differently. Returns 0 on success, or -2 if out of + * memory, or a status code on error. Since the caller happens to + * know the size of the file, it is passed in as SIZE. On success, + * this function sets *RETP and *LASTP, which may be passed to + * buf_append_data. + */ + +int +buf_read_file (f, size, retp, lastp) + FILE *f; + long size; + struct buffer_data **retp; + struct buffer_data **lastp; +{ + int status; + + *retp = NULL; + *lastp = NULL; + + while (size > 0) + { + struct buffer_data *data; + int get; + + data = get_buffer_data (); + if (data == NULL) + { + status = -2; + goto error_return; + } + + if (*retp == NULL) + *retp = data; + else + (*lastp)->next = data; + data->next = NULL; + *lastp = data; + + data->bufp = data->text; + data->size = 0; + + if (size > BUFFER_DATA_SIZE) + get = BUFFER_DATA_SIZE; + else + get = size; + + errno = EIO; + if (fread (data->text, get, 1, f) != 1) + { + status = errno; + goto error_return; + } + + data->size += get; + size -= get; + } + + return 0; + + error_return: + if (*retp != NULL) + { + (*lastp)->next = free_buffer_data; + free_buffer_data = *retp; + } + return status; +} + +/* + * Copy the contents of file F into buffer_data structures. We can't + * copy directly into an buffer, because we want to handle failure and + * succeess differently. Returns 0 on success, or -2 if out of + * memory, or a status code on error. On success, this function sets + * *RETP and *LASTP, which may be passed to buf_append_data. + */ + +int +buf_read_file_to_eof (f, retp, lastp) + FILE *f; + struct buffer_data **retp; + struct buffer_data **lastp; +{ + int status; + + *retp = NULL; + *lastp = NULL; + + while (!feof (f)) + { + struct buffer_data *data; + int get, nread; + + data = get_buffer_data (); + if (data == NULL) + { + status = -2; + goto error_return; + } + + if (*retp == NULL) + *retp = data; + else + (*lastp)->next = data; + data->next = NULL; + *lastp = data; + + data->bufp = data->text; + data->size = 0; + + get = BUFFER_DATA_SIZE; + + errno = EIO; + nread = fread (data->text, 1, get, f); + if (nread == 0 && !feof (f)) + { + status = errno; + goto error_return; + } + + data->size = nread; + } + + return 0; + + error_return: + if (*retp != NULL) + { + (*lastp)->next = free_buffer_data; + free_buffer_data = *retp; + } + return status; +} + +/* Return the number of bytes in a chain of buffer_data structures. */ + +int +buf_chain_length (buf) + struct buffer_data *buf; +{ + int size = 0; + while (buf) + { + size += buf->size; + buf = buf->next; + } + return size; +} + +/* + * Read an arbitrary amount of data into an input buffer. The buffer + * will be in nonblocking mode, and we just grab what we can. Return + * 0 on success, or -1 on end of file, or -2 if out of memory, or an + * error code. If COUNTP is not NULL, *COUNTP is set to the number of + * bytes read. + */ + +int +buf_input_data (buf, countp) + struct buffer *buf; + int *countp; +{ + if (buf->input == NULL) + abort (); + + if (countp != NULL) + *countp = 0; + + while (1) + { + int get; + int status, nbytes; + + if (buf->data == NULL + || (buf->last->bufp + buf->last->size + == buf->last->text + BUFFER_DATA_SIZE)) + { + struct buffer_data *data; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + data->next = NULL; + buf->last = data; + + data->bufp = data->text; + data->size = 0; + } + + get = ((buf->last->text + BUFFER_DATA_SIZE) + - (buf->last->bufp + buf->last->size)); + + status = (*buf->input) (buf->closure, + buf->last->bufp + buf->last->size, + 0, get, &nbytes); + if (status != 0) + return status; + + buf->last->size += nbytes; + if (countp != NULL) + *countp += nbytes; + + if (nbytes < get) + { + /* If we did not fill the buffer, then presumably we read + all the available data. */ + return 0; + } + } + + /*NOTREACHED*/ +} + +/* + * Read a line (characters up to a \012) from an input buffer. (We + * use \012 rather than \n for the benefit of non Unix clients for + * which \n means something else). This returns 0 on success, or -1 + * on end of file, or -2 if out of memory, or an error code. If it + * succeeds, it sets *LINE to an allocated buffer holding the contents + * of the line. The trailing \012 is not included in the buffer. If + * LENP is not NULL, then *LENP is set to the number of bytes read; + * strlen may not work, because there may be embedded null bytes. + */ + +int +buf_read_line (buf, line, lenp) + struct buffer *buf; + char **line; + int *lenp; +{ + if (buf->input == NULL) + abort (); + + *line = NULL; + + while (1) + { + int len, finallen; + struct buffer_data *data; + char *nl; + + /* See if there is a newline in BUF. */ + len = 0; + for (data = buf->data; data != NULL; data = data->next) + { + nl = memchr (data->bufp, '\012', data->size); + if (nl != NULL) + { + finallen = nl - data->bufp; + len += finallen; + break; + } + len += data->size; + } + + /* If we found a newline, copy the line into a memory buffer, + and remove it from BUF. */ + if (data != NULL) + { + char *p; + struct buffer_data *nldata; + + p = malloc (len + 1); + if (p == NULL) + return -2; + *line = p; + + nldata = data; + data = buf->data; + while (data != nldata) + { + struct buffer_data *next; + + memcpy (p, data->bufp, data->size); + p += data->size; + next = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + data = next; + } + + memcpy (p, data->bufp, finallen); + p[finallen] = '\0'; + + data->size -= finallen + 1; + data->bufp = nl + 1; + buf->data = data; + + if (lenp != NULL) + *lenp = len; + + return 0; + } + + /* Read more data until we get a newline. */ + while (1) + { + int size, status, nbytes; + char *mem; + + if (buf->data == NULL + || (buf->last->bufp + buf->last->size + == buf->last->text + BUFFER_DATA_SIZE)) + { + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + if (buf->data == NULL) + buf->data = data; + else + buf->last->next = data; + data->next = NULL; + buf->last = data; + + data->bufp = data->text; + data->size = 0; + } + + mem = buf->last->bufp + buf->last->size; + size = (buf->last->text + BUFFER_DATA_SIZE) - mem; + + /* We need to read at least 1 byte. We can handle up to + SIZE bytes. This will only be efficient if the + underlying communication stream does its own buffering, + or is clever about getting more than 1 byte at a time. */ + status = (*buf->input) (buf->closure, mem, 1, size, &nbytes); + if (status != 0) + return status; + + buf->last->size += nbytes; + + /* Optimize slightly to avoid an unnecessary call to + memchr. */ + if (nbytes == 1) + { + if (*mem == '\012') + break; + } + else + { + if (memchr (mem, '\012', nbytes) != NULL) + break; + } + } + } +} + +/* + * Extract data from the input buffer BUF. This will read up to WANT + * bytes from the buffer. It will set *RETDATA to point at the bytes, + * and set *GOT to the number of bytes to be found there. Any buffer + * call which uses BUF may change the contents of the buffer at *DATA, + * so the data should be fully processed before any further calls are + * made. This returns 0 on success, or -1 on end of file, or -2 if + * out of memory, or an error code. + */ + +int +buf_read_data (buf, want, retdata, got) + struct buffer *buf; + int want; + char **retdata; + int *got; +{ + if (buf->input == NULL) + abort (); + + while (buf->data != NULL && buf->data->size == 0) + { + struct buffer_data *next; + + next = buf->data->next; + buf->data->next = free_buffer_data; + free_buffer_data = buf->data; + buf->data = next; + if (next == NULL) + buf->last = NULL; + } + + if (buf->data == NULL) + { + struct buffer_data *data; + int get, status, nbytes; + + data = get_buffer_data (); + if (data == NULL) + { + (*buf->memory_error) (buf); + return -2; + } + + buf->data = data; + buf->last = data; + data->next = NULL; + data->bufp = data->text; + data->size = 0; + + if (want < BUFFER_DATA_SIZE) + get = want; + else + get = BUFFER_DATA_SIZE; + status = (*buf->input) (buf->closure, data->bufp, get, + BUFFER_DATA_SIZE, &nbytes); + if (status != 0) + return status; + + data->size = nbytes; + } + + *retdata = buf->data->bufp; + if (want < buf->data->size) + { + *got = want; + buf->data->size -= want; + buf->data->bufp += want; + } + else + { + *got = buf->data->size; + buf->data->size = 0; + } + + return 0; +} + +/* + * Copy lines from an input buffer to an output buffer. This copies + * all complete lines (characters up to a newline) from INBUF to + * OUTBUF. Each line in OUTBUF is preceded by the character COMMAND + * and a space. + */ + +void +buf_copy_lines (outbuf, inbuf, command) + struct buffer *outbuf; + struct buffer *inbuf; + int command; +{ + while (1) + { + struct buffer_data *data; + struct buffer_data *nldata; + char *nl; + int len; + + /* See if there is a newline in INBUF. */ + nldata = NULL; + nl = NULL; + for (data = inbuf->data; data != NULL; data = data->next) + { + nl = memchr (data->bufp, '\n', data->size); + if (nl != NULL) + { + nldata = data; + break; + } + } + + if (nldata == NULL) + { + /* There are no more lines in INBUF. */ + return; + } + + /* Put in the command. */ + buf_append_char (outbuf, command); + buf_append_char (outbuf, ' '); + + if (inbuf->data != nldata) + { + /* + * Simply move over all the buffers up to the one containing + * the newline. + */ + for (data = inbuf->data; data->next != nldata; data = data->next) + ; + data->next = NULL; + buf_append_data (outbuf, inbuf->data, data); + inbuf->data = nldata; + } + + /* + * If the newline is at the very end of the buffer, just move + * the buffer onto OUTBUF. Otherwise we must copy the data. + */ + len = nl + 1 - nldata->bufp; + if (len == nldata->size) + { + inbuf->data = nldata->next; + if (inbuf->data == NULL) + inbuf->last = NULL; + + nldata->next = NULL; + buf_append_data (outbuf, nldata, nldata); + } + else + { + buf_output (outbuf, nldata->bufp, len); + nldata->bufp += len; + nldata->size -= len; + } + } +} + +/* + * Copy counted data from one buffer to another. The count is an + * integer, host size, host byte order (it is only used across a + * pipe). If there is enough data, it should be moved over. If there + * is not enough data, it should remain on the original buffer. A + * negative count is a special case. if one is seen, *SPECIAL is set + * to the (negative) count value and no additional data is gathered + * from the buffer; normally *SPECIAL is set to 0. This function + * returns the number of bytes it needs to see in order to actually + * copy something over. + */ + +int +buf_copy_counted (outbuf, inbuf, special) + struct buffer *outbuf; + struct buffer *inbuf; + int *special; +{ + *special = 0; + + while (1) + { + struct buffer_data *data; + int need; + union + { + char intbuf[sizeof (int)]; + int i; + } u; + char *intp; + int count; + struct buffer_data *start; + int startoff; + struct buffer_data *stop; + int stopwant; + + /* See if we have enough bytes to figure out the count. */ + need = sizeof (int); + intp = u.intbuf; + for (data = inbuf->data; data != NULL; data = data->next) + { + if (data->size >= need) + { + memcpy (intp, data->bufp, need); + break; + } + memcpy (intp, data->bufp, data->size); + intp += data->size; + need -= data->size; + } + if (data == NULL) + { + /* We don't have enough bytes to form an integer. */ + return need; + } + + count = u.i; + start = data; + startoff = need; + + if (count < 0) + { + /* A negative COUNT is a special case meaning that we + don't need any further information. */ + stop = start; + stopwant = 0; + } + else + { + /* + * We have an integer in COUNT. We have gotten all the + * data from INBUF in all buffers before START, and we + * have gotten STARTOFF bytes from START. See if we have + * enough bytes remaining in INBUF. + */ + need = count - (start->size - startoff); + if (need <= 0) + { + stop = start; + stopwant = count; + } + else + { + for (data = start->next; data != NULL; data = data->next) + { + if (need <= data->size) + break; + need -= data->size; + } + if (data == NULL) + { + /* We don't have enough bytes. */ + return need; + } + stop = data; + stopwant = need; + } + } + + /* + * We have enough bytes. Free any buffers in INBUF before + * START, and remove STARTOFF bytes from START, so that we can + * forget about STARTOFF. + */ + start->bufp += startoff; + start->size -= startoff; + + if (start->size == 0) + start = start->next; + + if (stop->size == stopwant) + { + stop = stop->next; + stopwant = 0; + } + + while (inbuf->data != start) + { + data = inbuf->data; + inbuf->data = data->next; + data->next = free_buffer_data; + free_buffer_data = data; + } + + /* If COUNT is negative, set *SPECIAL and get out now. */ + if (count < 0) + { + *special = count; + return 0; + } + + /* + * We want to copy over the bytes from START through STOP. We + * only want STOPWANT bytes from STOP. + */ + + if (start != stop) + { + /* Attach the buffers from START through STOP to OUTBUF. */ + for (data = start; data->next != stop; data = data->next) + ; + inbuf->data = stop; + data->next = NULL; + buf_append_data (outbuf, start, data); + } + + if (stopwant > 0) + { + buf_output (outbuf, stop->bufp, stopwant); + stop->bufp += stopwant; + stop->size -= stopwant; + } + } + + /*NOTREACHED*/ +} + +/* Shut down a buffer. This returns 0 on success, or an errno code. */ + +int +buf_shutdown (buf) + struct buffer *buf; +{ + if (buf->shutdown) + return (*buf->shutdown) (buf->closure); + return 0; +} + +/* The simplest type of buffer is one built on top of a stdio FILE. + For simplicity, and because it is all that is required, we do not + implement setting this type of buffer into nonblocking mode. The + closure field is just a FILE *. */ + +static int stdio_buffer_input PROTO((void *, char *, int, int, int *)); +static int stdio_buffer_output PROTO((void *, const char *, int, int *)); +static int stdio_buffer_flush PROTO((void *)); + +/* Initialize a buffer built on a stdio FILE. */ + +struct buffer +*stdio_buffer_initialize (fp, input, memory) + FILE *fp; + int input; + void (*memory) PROTO((struct buffer *)); +{ + return buf_initialize (input ? stdio_buffer_input : NULL, + input ? NULL : stdio_buffer_output, + input ? NULL : stdio_buffer_flush, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + (void *) fp); +} + +/* The buffer input function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + FILE *fp = (FILE *) closure; + int nbytes; + + /* Since stdio does its own buffering, we don't worry about + getting more bytes than we need. */ + + if (need == 0 || need == 1) + { + int ch; + + ch = getc (fp); + + if (ch == EOF) + { + if (feof (fp)) + return -1; + else if (errno == 0) + return EIO; + else + return errno; + } + + *data = ch; + *got = 1; + return 0; + } + + nbytes = fread (data, 1, need, fp); + + if (nbytes == 0) + { + *got = 0; + if (feof (fp)) + return -1; + else if (errno == 0) + return EIO; + else + return errno; + } + + *got = nbytes; + + return 0; +} + +/* The buffer output function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + FILE *fp = (FILE *) closure; + + *wrote = 0; + + while (have > 0) + { + int nbytes; + + nbytes = fwrite (data, 1, have, fp); + + if (nbytes != have) + { + if (errno == 0) + return EIO; + else + return errno; + } + + *wrote += nbytes; + have -= nbytes; + data += nbytes; + } + + return 0; +} + +/* The buffer flush function for a buffer built on a stdio FILE. */ + +static int +stdio_buffer_flush (closure) + void *closure; +{ + FILE *fp = (FILE *) closure; + + if (fflush (fp) != 0) + { + if (errno == 0) + return EIO; + else + return errno; + } + + return 0; +} + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/gnu/usr.bin/cvs/src/buffer.h b/gnu/usr.bin/cvs/src/buffer.h new file mode 100644 index 00000000000..d3d52bdcc47 --- /dev/null +++ b/gnu/usr.bin/cvs/src/buffer.h @@ -0,0 +1,136 @@ +/* Declarations concerning the buffer data structure. */ + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +/* + * We must read data from a child process and send it across the + * network. We do not want to block on writing to the network, so we + * store the data from the child process in memory. A BUFFER + * structure holds the status of one communication, and uses a linked + * list of buffer_data structures to hold data. + */ + +struct buffer +{ + /* Data. */ + struct buffer_data *data; + + /* Last buffer on data chain. */ + struct buffer_data *last; + + /* Nonzero if the buffer is in nonblocking mode. */ + int nonblocking; + + /* Functions must be provided to transfer data in and out of the + buffer. Either the input or output field must be set, but not + both. */ + + /* Read data into the buffer DATA. There is room for up to SIZE + bytes. At least NEED bytes must be read (NEED may be 0). This + should return 0 on success, or -1 on end of file, or an errno + code. It should set the number of bytes read in *GOT. */ + int (*input) PROTO((void *closure, char *data, int need, int size, + int *got)); + + /* Write data. This should write up to HAVE bytes from DATA. + This should return 0 on success, or an errno code. It should + set the number of bytes written in *WROTE. */ + int (*output) PROTO((void *closure, const char *data, int have, + int *wrote)); + + /* Flush any data which may be buffered up after previous calls to + OUTPUT. This should return 0 on success, or an errno code. */ + int (*flush) PROTO((void *closure)); + + /* Change the blocking mode of the underlying communication + stream. If BLOCK is non-zero, it should be placed into + blocking mode. Otherwise, it should be placed into + non-blocking mode. This should return 0 on success, or an + errno code. */ + int (*block) PROTO ((void *closure, int block)); + + /* Shut down the communication stream. This does not mean that it + should be closed. It merely means that no more data will be + read or written, and that any final processing that is + appropriate should be done at this point. This may be NULL. + It should return 0 on success, or an errno code. This entry + point exists for the compression code. */ + int (*shutdown) PROTO((void *closure)); + + /* This field is passed to the INPUT, OUTPUT, and BLOCK functions. */ + void *closure; + + /* Function to call if we can't allocate memory. */ + void (*memory_error) PROTO((struct buffer *)); +}; + +/* Data is stored in lists of these structures. */ + +struct buffer_data +{ + /* Next buffer in linked list. */ + struct buffer_data *next; + + /* + * A pointer into the data area pointed to by the text field. This + * is where to find data that has not yet been written out. + */ + char *bufp; + + /* The number of data bytes found at BUFP. */ + int size; + + /* + * Actual buffer. This never changes after the structure is + * allocated. The buffer is BUFFER_DATA_SIZE bytes. + */ + char *text; +}; + +/* The size we allocate for each buffer_data structure. */ +#define BUFFER_DATA_SIZE (4096) + +extern struct buffer *buf_initialize PROTO((int (*) (void *, char *, int, + int, int *), + int (*) (void *, const char *, + int, int *), + int (*) (void *), + int (*) (void *, int), + int (*) (void *), + void (*) (struct buffer *), + void *)); +extern struct buffer *buf_nonio_initialize PROTO((void (*) (struct buffer *))); +extern struct buffer *stdio_buffer_initialize + PROTO((FILE *, int, void (*) (struct buffer *))); +extern struct buffer *compress_buffer_initialize + PROTO((struct buffer *, int, int, void (*) (struct buffer *))); +extern int buf_empty_p PROTO((struct buffer *)); +extern void buf_output PROTO((struct buffer *, const char *, int)); +extern void buf_output0 PROTO((struct buffer *, const char *)); +extern void buf_append_char PROTO((struct buffer *, int)); +extern int buf_send_output PROTO((struct buffer *)); +extern int buf_flush PROTO((struct buffer *, int)); +extern int set_nonblock PROTO((struct buffer *)); +extern int set_block PROTO((struct buffer *)); +extern int buf_send_counted PROTO((struct buffer *)); +extern int buf_send_special_count PROTO((struct buffer *, int)); +extern void buf_append_data PROTO((struct buffer *, + struct buffer_data *, + struct buffer_data *)); +extern int buf_read_file PROTO((FILE *, long, struct buffer_data **, + struct buffer_data **)); +extern int buf_read_file_to_eof PROTO((FILE *, struct buffer_data **, + struct buffer_data **)); +extern int buf_input_data PROTO((struct buffer *, int *)); +extern int buf_read_line PROTO((struct buffer *, char **, int *)); +extern int buf_read_data PROTO((struct buffer *, int, char **, int *)); +extern void buf_copy_lines PROTO((struct buffer *, struct buffer *, int)); +extern int buf_copy_counted PROTO((struct buffer *, struct buffer *, int *)); +extern int buf_chain_length PROTO((struct buffer_data *)); +extern int buf_shutdown PROTO((struct buffer *)); + +#ifdef SERVER_FLOWCONTROL +extern int buf_count_mem PROTO((struct buffer *)); +#endif /* SERVER_FLOWCONTROL */ + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ diff --git a/gnu/usr.bin/cvs/src/build_src.com b/gnu/usr.bin/cvs/src/build_src.com new file mode 100644 index 00000000000..784adb5e415 --- /dev/null +++ b/gnu/usr.bin/cvs/src/build_src.com @@ -0,0 +1,68 @@ +$ CC :== CC/NOOPT/DEB/STANDARD=VAXC/DEFINE=HAVE_CONFIG_H- +/INCLUDE_DIR=([-],[-.VMS],[-.LIB])/PREFIX=ALL +$ CC add.c +$ CC admin.c +$ CC buffer.c +$ CC checkin.c +$ CC checkout.c +$ CC classify.c +$ CC client.c +$ CC commit.c +$ CC create_adm.c +$ CC cvsrc.c +$ CC diff.c +$ CC edit.c +$ CC entries.c +$ CC error.c +$ CC expand_path.c +$ CC fileattr.c +$ CC find_names.c +$ CC hash.c +$ CC history.c +$ CC ignore.c +$ CC import.c +$ CC lock.c +$ CC log.c +$ CC login.c +$ CC logmsg.c +$ CC main.c +$ CC mkmodules.c +$ CC modules.c +$ CC myndbm.c +$ CC no_diff.c +$ CC parseinfo.c +$ CC patch.c +$ CC rcs.c +$ CC rcscmds.c +$ CC recurse.c +$ CC release.c +$ CC remove.c +$ CC repos.c +$ CC root.c +$ CC rtag.c +$ CC run.c +$ CC scramble.c +$ CC server.c +$ CC status.c +$ CC subr.c +$ CC tag.c +$ CC update.c +$ CC version.c +$ CC vers_ts.c +$ CC watch.c +$ CC wrapper.c +$ CC/INCLUDE_DIR=([-],[-.VMS],[-.LIB],[-.zlib]) zlib.c +$ LIBRARY/CREATE cvslib.olb add.obj,admin.obj,buffer.obj,checkin.obj,- +checkout.obj,- +classify.obj,client.obj,commit.obj,create_adm.obj,cvsrc.obj,- +diff.obj,edit.obj,entries.obj,error.obj,expand_path.obj,fileattr.obj,- +find_names.obj,hash.obj,history.obj,ignore.obj,import.obj,- +lock.obj,log.obj,login.obj,logmsg.obj,mkmodules.obj,modules.obj,myndbm.obj,- +no_diff.obj,- +parseinfo.obj,patch.obj,rcs.obj,rcscmds.obj,recurse.obj,release.obj,- +remove.obj,repos.obj,root.obj,rtag.obj,run.obj,scramble.obj,server.obj,- +status.obj,- +subr.obj,tag.obj,update.obj,version.obj,vers_ts.obj,watch.obj,wrapper.obj,- +zlib.obj +$ link/nodeb/exe=cvs.exe main.obj,cvslib.olb/lib,[-.lib]gnulib.olb/lib,- +[-.vms]openvmslib.olb/lib,[-.zlib]zlib.olb/lib diff --git a/gnu/usr.bin/cvs/src/checkin.c b/gnu/usr.bin/cvs/src/checkin.c index e1ce9cbcbde..50e309e273a 100644 --- a/gnu/usr.bin/cvs/src/checkin.c +++ b/gnu/usr.bin/cvs/src/checkin.c @@ -20,34 +20,22 @@ #include "edit.h" int -Checkin (type, file, update_dir, repository, - rcs, rev, tag, options, message, entries) +Checkin (type, finfo, rcs, rev, tag, options, message) int type; - char *file; - char *update_dir; - char *repository; + struct file_info *finfo; char *rcs; char *rev; char *tag; char *options; char *message; - List *entries; { char fname[PATH_MAX]; Vers_TS *vers; int set_time; - char *fullname; - char *tocvsPath = NULL; - fullname = xmalloc (strlen (update_dir) + strlen (file) + 10); - if (update_dir[0] == '\0') - strcpy (fullname, file); - else - sprintf (fullname, "%s/%s", update_dir, file); - - (void) printf ("Checking in %s;\n", fullname); - (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); + (void) printf ("Checking in %s;\n", finfo->fullname); + (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); /* * Move the user file to a backup file, so as to preserve its @@ -55,25 +43,25 @@ Checkin (type, file, update_dir, repository, * for the checkin and checkout. */ - tocvsPath = wrap_tocvs_process_file (fullname); + tocvsPath = wrap_tocvs_process_file (finfo->fullname); if (!noexec) { if (tocvsPath) { copy_file (tocvsPath, fname); - if (unlink_file_dir (file) < 0) + if (unlink_file_dir (finfo->file) < 0) if (! existence_error (errno)) - error (1, errno, "cannot remove %s", file); - copy_file (tocvsPath, file); + error (1, errno, "cannot remove %s", finfo->fullname); + copy_file (tocvsPath, finfo->file); } else { - copy_file (file, fname); + copy_file (finfo->file, fname); } } - switch (RCS_checkin (rcs, NULL, message, rev, 0, 0)) + switch (RCS_checkin (rcs, NULL, message, rev, 0)) { case 0: /* everything normal */ @@ -89,13 +77,20 @@ Checkin (type, file, update_dir, repository, if (strcmp (options, "-V4") == 0) /* upgrade to V5 now */ options[0] = '\0'; + /* Reparse the RCS file, so that we can safely call + RCS_checkout. FIXME: We could probably calculate + all the changes. */ + freercsnode (&finfo->rcs); + finfo->rcs = RCS_parse (finfo->file, finfo->repository); + /* FIXME: should be checking for errors. */ - (void) RCS_checkout (rcs, "", rev, options, RUN_TTY, 0, 0); + (void) RCS_checkout (finfo->rcs, finfo->file, rev, + (char *) NULL, options, RUN_TTY); - xchmod (file, 1); - if (xcmp (file, fname) == 0) + xchmod (finfo->file, 1); + if (xcmp (finfo->file, fname) == 0) { - rename_file (fname, file); + rename_file (fname, finfo->file); /* the time was correct, so leave it alone */ set_time = 0; } @@ -107,24 +102,23 @@ Checkin (type, file, update_dir, repository, set_time = 1; } - wrap_fromcvs_process_file (file); + wrap_fromcvs_process_file (finfo->file); /* * If we want read-only files, muck the permissions here, before * getting the file time-stamp. */ - if (cvswrite == FALSE || fileattr_get (file, "_watched")) - xchmod (file, 0); + if (cvswrite == FALSE || fileattr_get (finfo->file, "_watched")) + xchmod (finfo->file, 0); - /* re-register with the new data */ - vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL, - file, 1, set_time, entries, (RCSNode *) NULL); + /* Re-register with the new data. */ + vers = Version_TS (finfo, NULL, tag, NULL, 1, set_time); if (strcmp (vers->options, "-V4") == 0) vers->options[0] = '\0'; - Register (entries, file, vers->vn_rcs, vers->ts_user, + Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_user, vers->options, vers->tag, vers->date, (char *) 0); - history_write (type, (char *) 0, vers->vn_rcs, file, repository); - freevers_ts (&vers); + history_write (type, NULL, vers->vn_rcs, + finfo->file, finfo->repository); if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) @@ -139,7 +133,7 @@ Checkin (type, file, update_dir, repository, if (!noexec) error (1, errno, "could not check in %s -- fork failed", - fullname); + finfo->fullname); return (1); default: /* ci failed */ @@ -154,8 +148,8 @@ Checkin (type, file, update_dir, repository, if (!noexec) { - rename_file (fname, file); - error (0, 0, "could not check in %s", fullname); + rename_file (fname, finfo->file); + error (0, 0, "could not check in %s", finfo->fullname); } return (1); } @@ -167,7 +161,7 @@ Checkin (type, file, update_dir, repository, */ if (rev) { - (void) RCS_unlock (rcs, NULL, 1); + (void) RCS_unlock (finfo->rcs, NULL, 1); } #ifdef SERVER_SUPPORT @@ -175,14 +169,15 @@ Checkin (type, file, update_dir, repository, { if (set_time) /* Need to update the checked out file on the client side. */ - server_updated (file, update_dir, repository, SERVER_UPDATED, + server_updated (finfo, vers, SERVER_UPDATED, NULL, NULL); else - server_checked_in (file, update_dir, repository); + server_checked_in (finfo->file, finfo->update_dir, finfo->repository); } else #endif - mark_up_to_date (file); + mark_up_to_date (finfo->file); + freevers_ts (&vers); return (0); } diff --git a/gnu/usr.bin/cvs/src/classify.c b/gnu/usr.bin/cvs/src/classify.c index 924314b2099..c591b1e7867 100644 --- a/gnu/usr.bin/cvs/src/classify.c +++ b/gnu/usr.bin/cvs/src/classify.c @@ -21,34 +21,23 @@ static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers, List * entri * Classify the state of a file */ Ctype -Classify_File (file, tag, date, options, force_tag_match, aflag, repository, - entries, rcsnode, versp, update_dir, pipeout) - char *file; +Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp, + pipeout) + struct file_info *finfo; char *tag; char *date; char *options; int force_tag_match; int aflag; - char *repository; - List *entries; - RCSNode *rcsnode; Vers_TS **versp; - char *update_dir; int pipeout; { Vers_TS *vers; Ctype ret; - char *fullname; - - fullname = xmalloc (strlen (update_dir) + strlen (file) + 10); - if (update_dir[0] == '\0') - strcpy (fullname, file); - else - sprintf (fullname, "%s/%s", update_dir, file); /* get all kinds of good data about the file */ - vers = Version_TS (repository, options, tag, date, file, - force_tag_match, 0, entries, rcsnode); + vers = Version_TS (finfo, options, tag, date, + force_tag_match, 0); if (vers->vn_user == NULL) { @@ -61,7 +50,7 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, /* there is no user file */ if (!force_tag_match || !(vers->tag || vers->date)) if (!really_quiet) - error (0, 0, "nothing known about %s", fullname); + error (0, 0, "nothing known about %s", finfo->fullname); ret = T_UNKNOWN; } else @@ -70,24 +59,18 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, if (!force_tag_match || !(vers->tag || vers->date)) if (!really_quiet) error (0, 0, "use `cvs add' to create an entry for %s", - fullname); + finfo->fullname); ret = T_UNKNOWN; } } else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) { if (vers->ts_user == NULL) - /* - * Logically seems to me this should be T_UPTODATE. - * But the joining code in update.c seems to expect - * T_CHECKOUT, and that is what has traditionally been - * returned for this case. - */ - ret = T_CHECKOUT; + ret = T_UPTODATE; else { error (0, 0, "use `cvs add' to create an entry for %s", - fullname); + finfo->fullname); ret = T_UNKNOWN; } } @@ -115,13 +98,12 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, * conflict list, only if it is indeed different from what we * plan to extract */ - else if (No_Difference (file, vers, entries, - repository, update_dir)) + else if (No_Difference (finfo, vers)) { /* the files were different so it is a conflict */ if (!really_quiet) error (0, 0, "move away %s; it is in the way", - fullname); + finfo->fullname); ret = T_CONFLICT; } else @@ -141,7 +123,7 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, * entry */ if (!really_quiet) - error (0, 0, "warning: new-born %s has disappeared", fullname); + error (0, 0, "warning: new-born %s has disappeared", finfo->fullname); ret = T_REMOVE_ENTRY; } else @@ -166,7 +148,7 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, error (0, 0, "\ conflict: %s has been added, but already exists", - fullname); + finfo->fullname); } else { @@ -178,7 +160,7 @@ conflict: %s has been added, but already exists", error (0, 0, "\ conflict: %s created independently by second party", - fullname); + finfo->fullname); } ret = T_CONFLICT; } @@ -196,7 +178,8 @@ conflict: %s created independently by second party", (void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : ""); - if (vers->vn_rcs == NULL) + if (vers->vn_rcs == NULL + || RCS_isdead (vers->srcfile, vers->vn_rcs)) { /* @@ -222,7 +205,7 @@ conflict: %s created independently by second party", if (!really_quiet) error (0, 0, "conflict: removed %s was modified by second party", - fullname); + finfo->fullname); ret = T_CONFLICT; } } @@ -231,7 +214,7 @@ conflict: %s created independently by second party", /* The user file shouldn't be there */ if (!really_quiet) error (0, 0, "%s should be removed and is still there", - fullname); + finfo->fullname); ret = T_REMOVED; } } @@ -247,7 +230,7 @@ conflict: %s created independently by second party", /* There is no user file, so just remove the entry */ if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -259,7 +242,7 @@ conflict: %s created independently by second party", */ if (!really_quiet) error (0, 0, "%s is no longer in the repository", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } else @@ -268,14 +251,13 @@ conflict: %s created independently by second party", * The user file has been modified and since it is no longer * in the repository, a conflict is raised */ - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) { /* they are different -> conflict */ if (!really_quiet) error (0, 0, "conflict: %s is modified but no longer in the repository", - fullname); + finfo->fullname); ret = T_CONFLICT; } else @@ -284,7 +266,7 @@ conflict: %s created independently by second party", if (!really_quiet) error (0, 0, "warning: %s is not (any longer) pertinent", - fullname); + finfo->fullname); ret = T_REMOVE_ENTRY; } } @@ -302,7 +284,7 @@ conflict: %s created independently by second party", */ if (strcmp (command_name, "update") == 0) if (!really_quiet) - error (0, 0, "warning: %s was lost", fullname); + error (0, 0, "warning: %s was lost", finfo->fullname); ret = T_CHECKOUT; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -320,10 +302,10 @@ conflict: %s created independently by second party", else { #ifdef SERVER_SUPPORT - sticky_ck (file, aflag, vers, entries, - repository, update_dir); + sticky_ck (finfo->file, aflag, vers, finfo->entries, + finfo->repository, finfo->update_dir); #else - sticky_ck (file, aflag, vers, entries); + sticky_ck (finfo->file, aflag, vers, finfo->entries); #endif ret = T_UPTODATE; } @@ -335,8 +317,7 @@ conflict: %s created independently by second party", * The user file appears to have been modified, but we call * No_Difference to verify that it really has been modified */ - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) { /* @@ -352,10 +333,10 @@ conflict: %s created independently by second party", #else ret = T_MODIFIED; #ifdef SERVER_SUPPORT - sticky_ck (file, aflag, vers, entries, - repository, update_dir); + sticky_ck (finfo->file, aflag, vers, finfo->entries, + finfo->repository, finfo->update_dir); #else - sticky_ck (file, aflag, vers, entries); + sticky_ck (finfo->file, aflag, vers, finfo->entries); #endif /* SERVER_SUPPORT */ #endif } @@ -390,7 +371,7 @@ conflict: %s created independently by second party", if (strcmp (command_name, "update") == 0) if (!really_quiet) - error (0, 0, "warning: %s was lost", fullname); + error (0, 0, "warning: %s was lost", finfo->fullname); ret = T_CHECKOUT; } else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) @@ -413,8 +394,7 @@ conflict: %s created independently by second party", } else { - if (No_Difference (file, vers, entries, - repository, update_dir)) + if (No_Difference (finfo, vers)) /* really modified, needs to merge */ ret = T_NEEDS_MERGE; #ifdef SERVER_SUPPORT @@ -442,8 +422,6 @@ conflict: %s created independently by second party", else freevers_ts (&vers); - free (fullname); - /* return the status of the file */ return (ret); } diff --git a/gnu/usr.bin/cvs/src/client.c b/gnu/usr.bin/cvs/src/client.c index c0557ce6fee..ea6340bdfd1 100644 --- a/gnu/usr.bin/cvs/src/client.c +++ b/gnu/usr.bin/cvs/src/client.c @@ -7,6 +7,7 @@ #include "cvs.h" #include "getline.h" #include "edit.h" +#include "buffer.h" #ifdef CLIENT_SUPPORT @@ -22,10 +23,6 @@ # endif /* No winsock.h */ #endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP */ -#ifdef AUTH_CLIENT_SUPPORT -char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot)); -#endif /* AUTH_CLIENT_SUPPORT */ - #if HAVE_KERBEROS || USE_DIRECT_TCP #define CVS_PORT 1999 @@ -36,6 +33,11 @@ extern char *krb_realmofhost (); #ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] #endif /* HAVE_KRB_GET_ERR_TEXT */ + +/* Information we need if we are going to use Kerberos encryption. */ +static C_Block kblock; +static Key_schedule sched; + #endif /* HAVE_KERBEROS */ #endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ @@ -81,8 +83,10 @@ 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_f PROTO((char *, int)); static void handle_notified PROTO((char *, int)); +static void buf_memory_error PROTO((struct buffer *)); static size_t try_read_from_server PROTO ((char *, size_t)); #endif /* CLIENT_SUPPORT */ @@ -102,29 +106,29 @@ mode_to_string (mode) mode_t mode; #endif /* __STDC__ */ { - char buf[18], u[4], g[4], o[4]; - int i; + char buf[18], u[4], g[4], o[4]; + int i; - i = 0; - if (mode & S_IRUSR) u[i++] = 'r'; - if (mode & S_IWUSR) u[i++] = 'w'; - if (mode & S_IXUSR) u[i++] = 'x'; - u[i] = '\0'; - - i = 0; - if (mode & S_IRGRP) g[i++] = 'r'; - if (mode & S_IWGRP) g[i++] = 'w'; - if (mode & S_IXGRP) g[i++] = 'x'; - g[i] = '\0'; - - i = 0; - if (mode & S_IROTH) o[i++] = 'r'; - if (mode & S_IWOTH) o[i++] = 'w'; - if (mode & S_IXOTH) o[i++] = 'x'; - o[i] = '\0'; - - sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); - return xstrdup(buf); + i = 0; + if (mode & S_IRUSR) u[i++] = 'r'; + if (mode & S_IWUSR) u[i++] = 'w'; + if (mode & S_IXUSR) u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if (mode & S_IRGRP) g[i++] = 'r'; + if (mode & S_IWGRP) g[i++] = 'w'; + if (mode & S_IXGRP) g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if (mode & S_IROTH) o[i++] = 'r'; + if (mode & S_IWOTH) o[i++] = 'w'; + if (mode & S_IXOTH) o[i++] = 'x'; + o[i] = '\0'; + + sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); + return xstrdup(buf); } /* @@ -235,214 +239,356 @@ change_mode (filename, mode_string) #ifdef CLIENT_SUPPORT -/* The host part of CVSROOT. */ -static char *server_host; -/* The user part of CVSROOT */ -static char *server_user; -/* The repository part of CVSROOT. */ -static char *server_cvsroot; +int client_prune_dirs; + +static List *ignlist = (List *) NULL; -int client_active; +/* Buffer to write to the server. */ +static struct buffer *to_server; +/* The stream underlying to_server, if we are using a stream. */ +static FILE *to_server_fp; -int client_prune_dirs; +/* Buffer used to read from the server. */ +static struct buffer *from_server; +/* The stream underlying from_server, if we are using a stream. */ +static FILE *from_server_fp; -static int cvsroot_parsed = 0; +/* Process ID of rsh subprocess. */ +static int rsh_pid = -1; -static List *ignlist = (List *) NULL; + +/* This routine is called when one of the buffer routines runs out of + memory. */ -/* Set server_host and server_cvsroot. */ static void -parse_cvsroot () +buf_memory_error (buf) + struct buffer *buf; { - char *p; -#ifdef AUTH_CLIENT_SUPPORT - static char *access_method; -#endif /* AUTH_CLIENT_SUPPORT */ + error (1, 0, "out of memory"); +} + +/* We want to be able to log data sent between us and the server. We + do it using log buffers. Each log buffer has another buffer which + handles the actual I/O, and a file to log information to. - /* Don't go through the trouble twice. */ - if (cvsroot_parsed) - return; + This structure is the closure field of a log buffer. */ - server_host = xstrdup (CVSroot); +struct log_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The file to log information to. */ + FILE *log; +}; -#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]); +static struct buffer *log_buffer_initialize + PROTO((struct buffer *, FILE *, int, void (*) (struct buffer *))); +static int log_buffer_input PROTO((void *, char *, int, int, int *)); +static int log_buffer_output PROTO((void *, const char *, int, int *)); +static int log_buffer_flush PROTO((void *)); +static int log_buffer_block PROTO((void *, int)); +static int log_buffer_shutdown PROTO((void *)); + +/* Create a log buffer. */ + +static struct buffer * +log_buffer_initialize (buf, fp, input, memory) + struct buffer *buf; + FILE *fp; + int input; + void (*memory) PROTO((struct buffer *)); +{ + struct log_buffer *n; + + n = (struct log_buffer *) xmalloc (sizeof *n); + n->buf = buf; + n->log = fp; + return buf_initialize (input ? log_buffer_input : NULL, + input ? NULL : log_buffer_output, + input ? NULL : log_buffer_flush, + log_buffer_block, + log_buffer_shutdown, + memory, + n); +} - if (! *access_method) - error (1, 0, "bad CVSroot: %s", CVSroot); +/* The input function for a log buffer. */ - if (! *(p = strchr (access_method, ':'))) - error (1, 0, "bad CVSroot: %s", CVSroot); - - *p = '\0'; - p++; +static int +log_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + int status; + size_t n_to_write; - server_host = p; - - if (! *server_host) - error (1, 0, "bad CVSroot: %s", CVSroot); + if (lb->buf->input == NULL) + abort (); - 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; - - /* Then deal with host and possible user. */ - if ( (p = strchr (server_host, '@')) == NULL) + status = (*lb->buf->input) (lb->buf->closure, data, need, size, got); + if (status != 0) + return status; + + if (*got > 0) { - server_user = NULL; + n_to_write = *got; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); } - else + + return 0; +} + +/* The output function for a log buffer. */ + +static int +log_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + int status; + size_t n_to_write; + + if (lb->buf->output == NULL) + abort (); + + status = (*lb->buf->output) (lb->buf->closure, data, have, wrote); + if (status != 0) + return status; + + if (*wrote > 0) { - server_user = server_host; - server_host = p; - ++server_host; - *p = '\0'; + n_to_write = *wrote; + if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) + error (0, errno, "writing to log file"); } - - client_active = 1; - cvsroot_parsed = 1; + + return 0; +} + +/* The flush function for a log buffer. */ + +static int +log_buffer_flush (closure) + void *closure; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + if (lb->buf->flush == NULL) + abort (); + + /* We don't really have to flush the log file here, but doing it + will let tail -f on the log file show what is sent to the + network as it is sent. */ + if (fflush (lb->log) != 0) + error (0, errno, "flushing log file"); + + return (*lb->buf->flush) (lb->buf->closure); +} + +/* The block function for a log buffer. */ + +static int +log_buffer_block (closure, block) + void *closure; + int block; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + if (block) + return set_block (lb->buf); + else + return set_nonblock (lb->buf); +} + +/* The shutdown function for a log buffer. */ + +static int +log_buffer_shutdown (closure) + void *closure; +{ + struct log_buffer *lb = (struct log_buffer *) closure; + + return buf_shutdown (lb->buf); } #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. */ + 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. */ -static FILE *to_server; -/* Stream to read from the server. */ -static FILE *from_server; +/* These routines implement a buffer structure which uses send and + recv. We don't need the block routine, so we don't implement it. */ -/* We might want to log client/server traffic. */ -static FILE *from_server_logfile; -static FILE *to_server_logfile; +/* We use an instance of this structure as the closure field. */ -#if ! RSH_NOT_TRANSPARENT -/* Process ID of rsh subprocess. */ -static int rsh_pid = -1; -#endif /* ! RSH_NOT_TRANSPARENT */ +struct socket_buffer +{ + /* The socket number. */ + int socket; +}; + +static struct buffer *socket_buffer_initialize + PROTO ((int, int, void (*) (struct buffer *))); +static int socket_buffer_input PROTO((void *, char *, int, int, int *)); +static int socket_buffer_output PROTO((void *, const char *, int, int *)); +static int socket_buffer_flush PROTO((void *)); + +/* Create a buffer based on a socket. */ + +static struct buffer * +socket_buffer_initialize (socket, input, memory) + int socket; + int input; + void (*memory) PROTO((struct buffer *)); +{ + struct socket_buffer *n; + + n = (struct socket_buffer *) xmalloc (sizeof *n); + n->socket = socket; + return buf_initialize (input ? socket_buffer_input : NULL, + input ? NULL : socket_buffer_output, + input ? NULL : socket_buffer_flush, + (int (*) PROTO((void *, int))) NULL, + (int (*) PROTO((void *))) NULL, + memory, + n); +} + +/* The buffer input function for a buffer built on a socket. */ - -/* - * 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. - * - * Returns number of bytes read. If EOF_OK, then return 0 on end of file, - * else end of file is an error. - */ static int -read_line (resultp, eof_ok) - char **resultp; - int eof_ok; +socket_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; { - int c; - char *result; - size_t input_index = 0; - size_t result_size = 80; + struct socket_buffer *sb = (struct socket_buffer *) closure; + int nbytes; -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) -#endif /* NO_SOCKET_TO_FD */ - fflush (to_server); + /* I believe that the recv function gives us exactly the semantics + we want. If there is a message, it returns immediately with + whatever it could get. If there is no message, it waits until + one comes in. In other words, it is not like read, which in + blocking mode normally waits until all the requested data is + available. */ - result = (char *) xmalloc (result_size); + *got = 0; - while (1) + while (need > 0) { + nbytes = recv (sb->socket, data, size, 0); + if (nbytes < 0) + error (1, errno, "reading from server"); + need -= nbytes; + size -= nbytes; + data += nbytes; + *got += nbytes; + } -#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); + return 0; +} - if (c == EOF) - { - free (result); +/* The buffer output function for a buffer built on a socket. */ -#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; - else - error (1, 0, "end of file from server (consult above messages if any)"); - } +static int +socket_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct socket_buffer *sb = (struct socket_buffer *) closure; - if (c == '\n') - break; - - result[input_index++] = c; - while (input_index + 1 >= result_size) - { - result_size *= 2; - result = (char *) xrealloc (result, result_size); - } + *wrote = have; + +#ifdef SEND_NEVER_PARTIAL + /* If send() never will produce a partial write, then just do it. This + is needed for systems where its return value is something other than + the number of bytes written. */ + if (send (sb->socket, data, have, 0) < 0) + error (1, errno, "writing to server socket"); +#else + while (have > 0) + { + int nbytes; + + nbytes = send (sb->socket, data, have, 0); + if (nbytes < 0) + error (1, errno, "writing to server socket"); + + have -= nbytes; + data += nbytes; } +#endif - if (resultp) - *resultp = result; + return 0; +} - /* Terminate it just for kicks, but we *can* deal with embedded NULs. */ - result[input_index] = '\0'; +/* The buffer flush function for a buffer built on a socket. */ + +/*ARGSUSED*/ +static int +socket_buffer_flush (closure) + void *closure; +{ + /* Nothing to do. Sockets are always flushed. */ + return 0; +} -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) #endif /* NO_SOCKET_TO_FD */ + +/* + * 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. + * + * Returns number of bytes read. + */ +static int +read_line (resultp) + char **resultp; +{ + int status; + char *result; + int len; + + status = buf_flush (to_server, 1); + if (status != 0) + error (1, status, "writing to server"); + + status = buf_read_line (from_server, &result, &len); + if (status != 0) { - /* - * 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 (status == -1) + error (1, 0, "end of file from server (consult above messages if any)"); + else if (status == -2) + error (1, 0, "out of memory"); + else + error (1, status, "reading from server"); } - - if (resultp == NULL) + + if (resultp != NULL) + *resultp = result; + else free (result); - return input_index; + + return len; } #endif /* CLIENT_SUPPORT */ @@ -456,23 +602,28 @@ read_line (resultp, eof_ok) */ int gzip_level; +/* + * Level of compression to use when running gzip on a single file. + */ +int file_gzip_level; + int filter_through_gzip (fd, dir, level, pidp) - int fd, dir, level; - pid_t *pidp; + int fd, dir, level; + pid_t *pidp; { - static char buf[5] = "-"; - static char *gzip_argv[3] = { "gzip", buf }; + static char buf[5] = "-"; + static char *gzip_argv[3] = { "gzip", buf }; - sprintf (buf+1, "%d", level); - return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp); + sprintf (buf+1, "%d", level); + return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp); } int filter_through_gunzip (fd, dir, pidp) - int fd, dir; - pid_t *pidp; + int fd, dir; + pid_t *pidp; { - static char *gunzip_argv[3] = { "gunzip", "-d" }; - return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp); + static char *gunzip_argv[3] = { "gzip", "-d" }; + return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp); } #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ @@ -598,6 +749,11 @@ get_short_pathname (name) return (char *) retval; } +/* This variable holds the result of Entries_Open, so that we can + close Entries_Close on it when we move on to a new directory, or + when we finish. */ +static List *last_entries; + /* * Do all the processing for PATHNAME, where pathname consists of the * repository and the filename. The parameters we pass to FUNC are: @@ -618,8 +774,6 @@ call_in_directory (pathname, func, data) char *filename)); char *data; { - static List *last_entries; - char *dir_name; char *filename; /* Just the part of pathname relative to toplevel_repos. */ @@ -647,7 +801,7 @@ call_in_directory (pathname, func, data) reposname = NULL; if (use_directory) - read_line (&reposname, 0); + read_line (&reposname); reposdirname_absolute = 0; if (reposname != NULL) @@ -711,6 +865,12 @@ call_in_directory (pathname, func, data) if (last_dir_name == NULL || strcmp (last_dir_name, dir_name) != 0) { + int newdir; + + if (strcmp (command_name, "export") != 0) + if (last_entries) + Entries_Close (last_entries); + if (last_dir_name) free (last_dir_name); last_dir_name = dir_name; @@ -720,9 +880,10 @@ call_in_directory (pathname, func, data) error (1, 0, "could not get working directory: %s", toplevel_wd); - if (chdir (toplevel_wd) < 0) + if ( CVS_CHDIR (toplevel_wd) < 0) error (1, errno, "could not chdir to %s", toplevel_wd); - if (chdir (dir_name) < 0) + newdir = 0; + if ( CVS_CHDIR (dir_name) < 0) { char *dir; char *dirp; @@ -731,6 +892,7 @@ call_in_directory (pathname, func, data) error (1, errno, "could not chdir to %s", dir_name); /* Directory does not exist, we need to create it. */ + newdir = 1; dir = xmalloc (strlen (dir_name) + 1); dirp = dir_name; rdirp = reposdirname; @@ -759,19 +921,19 @@ call_in_directory (pathname, func, data) { dirp = strchr (dirp, '/'); if (dirp) - { + { strncpy (dir, dir_name, dirp - dir_name); dir[dirp - dir_name] = '\0'; /* Skip the slash. */ ++dirp; if (rdirp == NULL) - error (0, 0, - "internal error: repository string too short."); + error (0, 0, + "internal error: repository string too short."); else - rdirp = strchr (rdirp, '/'); - } + rdirp = strchr (rdirp, '/'); + } else - { + { /* If there are no more slashes in the dir name, we're down to the most nested directory -OR- to the name of a module. In the first case, we @@ -789,35 +951,11 @@ call_in_directory (pathname, func, data) rdirp = NULL; strcpy (dir, dir_name); - } + } - if (CVS_MKDIR (dir, 0777) < 0) + if (mkdir_if_needed (dir)) { - /* 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. */ + /* It already existed, fine. Just keep going. */ } else if (strcmp (command_name, "export") == 0) /* Don't create CVSADM directories if this is export. */ @@ -831,7 +969,7 @@ call_in_directory (pathname, func, data) * relative to cvsroot. */ char *repo; - char *r; + char *r, *b; repo = xmalloc (strlen (reposdirname) + strlen (toplevel_repos) @@ -856,6 +994,16 @@ call_in_directory (pathname, func, data) Create_Admin (dir, dir, repo, (char *)NULL, (char *)NULL); free (repo); + + b = strrchr (dir, '/'); + if (b == NULL) + Subdir_Register ((List *) NULL, (char *) NULL, dir); + else + { + *b = '\0'; + Subdir_Register ((List *) NULL, dir, b + 1); + *b = '/'; + } } if (rdirp != NULL) @@ -867,15 +1015,69 @@ call_in_directory (pathname, func, data) } while (dirp != NULL); free (dir); /* Now it better work. */ - if (chdir (dir_name) < 0) + if ( CVS_CHDIR (dir_name) < 0) error (1, errno, "could not chdir to %s", dir_name); } + /* If the modules file has an entry for the entire tree (e.g., + ``world -a .''), we may need to create the CVS directory + specially in this working directory. */ + if (strcmp (dir_name, ".") == 0 + && ! isdir (CVSADM)) + { + char *repo; + char *r; + + newdir = 1; + + repo = xmalloc (strlen (reposdirname) + + strlen (toplevel_repos) + + 10); + if (reposdirname_absolute) + r = repo; + else + { + strcpy (repo, toplevel_repos); + r = repo + strlen (repo); + *r++ = '/'; + } + + strcpy (r, reposdirname); + + r += strlen (r); + if (r[-1] != '.' || r[-2] != '/') + strcpy (r, "/."); + + Create_Admin (dir_name, dir_name, repo, (char *) NULL, + (char *) NULL); + + free (repo); + } + if (strcmp (command_name, "export") != 0) { - if (last_entries) - Entries_Close (last_entries); last_entries = Entries_Open (0); + + /* If this is a newly created directory, we will record + all subdirectory information, so call Subdirs_Known in + case there are no subdirectories. If this is not a + newly created directory, it may be an old working + directory from before we recorded subdirectory + information in the Entries file. We force a search for + all subdirectories now, to make sure our subdirectory + information is up to date. If the Entries file does + record subdirectory information, then this call only + does list manipulation. */ + if (newdir) + Subdirs_Known (last_entries); + else + { + List *dirlist; + + dirlist = Find_Directories ((char *) NULL, W_LOCAL, + last_entries); + dellist (&dirlist); + } } } else @@ -901,7 +1103,7 @@ copy_a_file (data, ent_list, short_pathname, filename) char *p; #endif - read_line (&newname, 0); + read_line (&newname); #ifdef USE_VMS_FILENAMES /* Mogrify the filename so VMS is happy with it. */ @@ -949,7 +1151,7 @@ read_counted_file (filename, fullname) FILE *fp; - read_line (&size_string, 0); + read_line (&size_string); if (size_string[0] == 'z') error (1, 0, "\ protocol error: compressed files not supported for that operation"); @@ -968,7 +1170,7 @@ protocol error: compressed files not supported for that operation"); is binary or not. I haven't carefully looked into whether CVS/Template files should use local text file conventions or not. */ - fp = fopen (filename, "wb"); + fp = CVS_FOPEN (filename, "wb"); if (fp == NULL) error (1, errno, "cannot write %s", fullname); nread = size; @@ -1002,6 +1204,11 @@ protocol error: compressed files not supported for that operation"); } /* + * The time stamp of the last file we registered. + */ +static time_t last_register_time; + +/* * The Checksum response gives the checksum for the file transferred * over by the next Updated, Merged or Patch response. We just store * it here, and then check it in update_entries. @@ -1085,6 +1292,15 @@ struct update_entries_data UPDATE_ENTRIES_PATCH } contents; + enum { + /* We are replacing an existing file. */ + UPDATE_ENTRIES_EXISTING, + /* We are creating a new file. */ + UPDATE_ENTRIES_NEW, + /* We don't know whether it is existing or new. */ + UPDATE_ENTRIES_EXISTING_OR_NEW + } existp; + /* * String to put in the timestamp field or NULL to use the timestamp * of the file. @@ -1115,7 +1331,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) char *scratch_entries; int bin; - read_line (&entries_line, 0); + read_line (&entries_line); /* * Parse the entries line. @@ -1178,9 +1394,9 @@ update_entries (data_arg, ent_list, short_pathname, filename) int use_gzip, gzip_status; pid_t gzip_pid = 0; - read_line (&mode_string, 0); + read_line (&mode_string); - read_line (&size_string, 0); + read_line (&size_string); if (size_string[0] == 'z') { use_gzip = 1; @@ -1193,6 +1409,56 @@ update_entries (data_arg, ent_list, short_pathname, filename) } free (size_string); + /* Note that checking this separately from writing the file is + a race condition: if the existing or lack thereof of the + file changes between now and the actually calls which + operate on it, we lose. However (a) there are so many + cases, I'm reluctant to try to fix them all, (b) in some + cases the system might not even have a system call which + does the right thing, and (c) it isn't clear this needs to + work. */ + if (data->existp == UPDATE_ENTRIES_EXISTING + && !isfile (filename)) + /* Emit a warning and update the file anyway. */ + error (0, 0, "warning: %s unexpectedly disappeared", + short_pathname); + + if (data->existp == UPDATE_ENTRIES_NEW + && isfile (filename)) + { + /* Emit a warning and refuse to update the file; we don't want + to clobber a user's file. */ + size_t nread; + size_t toread; + + /* size should be unsigned, but until we get around to fixing + that, work around it. */ + size_t usize; + + char buf[8192]; + + error (0, 0, "move away %s; it is in the way", short_pathname); + + discard_file_and_return: + /* Now read and discard the file contents. */ + usize = size; + nread = 0; + while (nread < usize) + { + toread = usize - nread; + if (toread > sizeof buf) + toread = sizeof buf; + + nread += try_read_from_server (buf, toread); + if (nread == usize) + break; + } + + free (mode_string); + free (entries_line); + return; + } + temp_filename = xmalloc (strlen (filename) + 80); #ifdef USE_VMS_FILENAMES /* A VMS rename of "blah.dat" to "foo" to implies a @@ -1219,22 +1485,31 @@ update_entries (data_arg, ent_list, short_pathname, filename) else bin = 0; - fd = open (temp_filename, + fd = CVS_OPEN (temp_filename, O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0), 0777); if (fd < 0) - error (1, errno, "writing %s", short_pathname); + { + /* I can see a case for making this a fatal error; for a condition + like disk full or network unreachable (for a file server), + carrying on and giving an error on each file seems unnecessary. + But if it is a permission problem, or some such, then it is + entirely possible that future files will not have the same + problem. */ + error (0, errno, "cannot write %s", short_pathname); + goto discard_file_and_return; + } if (use_gzip) fd = filter_through_gunzip (fd, 0, &gzip_pid); if (size > 0) { - read_from_server (buf, size); - - 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) @@ -1267,12 +1542,31 @@ update_entries (data_arg, ent_list, short_pathname, filename) { convert_file (temp_filename, O_RDONLY | OPEN_BINARY, filename, O_WRONLY | O_CREAT | O_TRUNC); - if (unlink (temp_filename) < 0) + if ( CVS_UNLINK (temp_filename) < 0) error (0, errno, "warning: couldn't delete %s", temp_filename); } else +#ifdef BROKEN_READWRITE_CONVERSION + { + /* If only stdio, not open/write/etc., do text/binary + conversion, use convert_file which can compensate + (FIXME: we could just use stdio instead which would + avoid the whole problem). */ + if (!bin) + { + convert_file (temp_filename, O_RDONLY | OPEN_BINARY, + filename, O_WRONLY | O_CREAT | O_TRUNC); + if (CVS_UNLINK (temp_filename) < 0) + error (0, errno, "warning: couldn't delete %s", + temp_filename); + } + else + rename_file (temp_filename, filename); + } +#else rename_file (temp_filename, filename); +#endif #else /* ! LINES_CRLF_TERMINATED */ rename_file (temp_filename, filename); @@ -1289,7 +1583,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) if (!isfile (filename)) error (1, 0, "patch original file %s does not exist", short_pathname); - if (stat (temp_filename, &s) < 0) + if ( CVS_STAT (temp_filename, &s) < 0) error (1, 1, "can't stat patch file %s", temp_filename); if (s.st_size == 0) retcode = 0; @@ -1360,7 +1654,7 @@ update_entries (data_arg, ent_list, short_pathname, filename) * here using text mode, so its lines will be terminated the same * way they were transmitted. */ - e = fopen (filename, "r"); + e = CVS_FOPEN (filename, "r"); if (e == NULL) error (1, errno, "could not open %s", short_pathname); @@ -1422,6 +1716,8 @@ update_entries (data_arg, ent_list, short_pathname, filename) char *local_timestamp; char *file_timestamp; + (void) time (&last_register_time); + local_timestamp = data->timestamp; if (local_timestamp == NULL || ts[0] == '+') file_timestamp = time_stamp (filename); @@ -1459,6 +1755,7 @@ handle_checked_in (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_CHECKIN; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1470,6 +1767,7 @@ handle_new_entry (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_CHECKIN; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = "dummy timestamp from new-entry"; call_in_directory (args, update_entries, (char *)&dat); } @@ -1481,6 +1779,35 @@ handle_updated (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; + dat.timestamp = NULL; + call_in_directory (args, update_entries, (char *)&dat); +} + +static void handle_created PROTO((char *, int)); + +static void +handle_created (args, len) + char *args; + int len; +{ + struct update_entries_data dat; + dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_NEW; + dat.timestamp = NULL; + call_in_directory (args, update_entries, (char *)&dat); +} + +static void handle_update_existing PROTO((char *, int)); + +static void +handle_update_existing (args, len) + char *args; + int len; +{ + struct update_entries_data dat; + dat.contents = UPDATE_ENTRIES_UPDATE; + dat.existp = UPDATE_ENTRIES_EXISTING; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1492,6 +1819,8 @@ handle_merged (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_UPDATE; + /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = "Result of merge"; call_in_directory (args, update_entries, (char *)&dat); } @@ -1503,6 +1832,8 @@ handle_patched (args, len) { struct update_entries_data dat; dat.contents = UPDATE_ENTRIES_PATCH; + /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ + dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; dat.timestamp = NULL; call_in_directory (args, update_entries, (char *)&dat); } @@ -1533,6 +1864,11 @@ remove_entry_and_file (data, ent_list, short_pathname, filename) char *filename; { Scratch_Entry (ent_list, filename); + /* Note that we don't ignore existence_error's here. The server + should be sending Remove-entry rather than Removed in cases + where the file does not exist. And if the user removes the + file halfway through a cvs command, we should be printing an + error. */ if (unlink_file (filename) < 0) error (0, errno, "unable to remove %s", short_pathname); } @@ -1552,20 +1888,20 @@ is_cvsroot_level (pathname) { char *short_pathname; - if (strcmp (toplevel_repos, server_cvsroot) != 0) + if (strcmp (toplevel_repos, CVSroot_directory) != 0) return 0; if (!use_directory) { - if (strncmp (pathname, server_cvsroot, strlen (server_cvsroot)) != 0) + if (strncmp (pathname, CVSroot_directory, strlen (CVSroot_directory)) != 0) error (1, 0, "server bug: pathname `%s' doesn't specify file in `%s'", - pathname, server_cvsroot); - short_pathname = pathname + strlen (server_cvsroot) + 1; + pathname, CVSroot_directory); + short_pathname = pathname + strlen (CVSroot_directory) + 1; if (short_pathname[-1] != '/') error (1, 0, "server bug: pathname `%s' doesn't specify file in `%s'", - pathname, server_cvsroot); + pathname, CVSroot_directory); return strchr (short_pathname, '/') == NULL; } else @@ -1595,7 +1931,7 @@ handle_set_static_directory (args, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } call_in_directory (args, set_static, (char *)NULL); @@ -1620,7 +1956,7 @@ handle_clear_static_directory (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } @@ -1645,7 +1981,7 @@ set_sticky (data, ent_list, short_pathname, filename) char *tagspec; FILE *f; - read_line (&tagspec, 0); + read_line (&tagspec); f = open_file (CVSADM_TAG, "w+"); if (fprintf (f, "%s\n", tagspec) < 0) error (1, errno, "writing %s", CVSADM_TAG); @@ -1662,9 +1998,9 @@ handle_set_sticky (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); /* Swallow the tag line. */ - (void) read_line (NULL, 0); + read_line (NULL); return; } if (is_cvsroot_level (pathname)) @@ -1675,9 +2011,9 @@ handle_set_sticky (pathname, len) */ /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); /* Swallow the tag line. */ - (void) read_line (NULL, 0); + read_line (NULL); return; } @@ -1703,7 +2039,7 @@ handle_clear_sticky (pathname, len) if (strcmp (command_name, "export") == 0) { /* Swallow the repository. */ - read_line (NULL, 0); + read_line (NULL); return; } @@ -1766,7 +2102,7 @@ handle_set_checkin_prog (args, len) { char *prog; struct save_prog *p; - read_line (&prog, 0); + read_line (&prog); p = (struct save_prog *) xmalloc (sizeof (struct save_prog)); p->next = checkin_progs; p->dir = xstrdup (args); @@ -1781,7 +2117,7 @@ handle_set_update_prog (args, len) { char *prog; struct save_prog *p; - read_line (&prog, 0); + read_line (&prog); p = (struct save_prog *) xmalloc (sizeof (struct save_prog)); p->next = update_progs; p->dir = xstrdup (args); @@ -1801,7 +2137,7 @@ do_deferred_progs () FILE *f; if (toplevel_wd[0] != '\0') { - if (chdir (toplevel_wd) < 0) + if ( CVS_CHDIR (toplevel_wd) < 0) error (1, errno, "could not chdir to %s", toplevel_wd); } for (p = checkin_progs; p != NULL; ) @@ -1847,7 +2183,7 @@ client_isemptydir (dir) DIR *dirp; struct dirent *dp; - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) { if (! existence_error (errno)) error (0, errno, "cannot open directory %s for empty check", dir); @@ -1903,21 +2239,32 @@ process_prune_candidates () struct save_dir *q; if (toplevel_wd[0] != '\0') - { - if (chdir (toplevel_wd) < 0) + { + if ( CVS_CHDIR (toplevel_wd) < 0) error (1, errno, "could not chdir to %s", toplevel_wd); - } + } for (p = prune_candidates; p != NULL; ) { if (client_isemptydir (p->dir)) { - unlink_file_dir (p->dir); + char *b; + + unlink_file_dir (p->dir); + b = strrchr (p->dir, '/'); + if (b == NULL) + Subdir_Deregister ((List *) NULL, (char *) NULL, p->dir); + else + { + *b = '\0'; + Subdir_Deregister ((List *) NULL, p->dir, b + 1); + } } free (p->dir); q = p->next; free (p); p = q; } + prune_candidates = NULL; } /* Send a Repository line. */ @@ -2011,7 +2358,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_TAG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2020,7 +2367,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Sticky ", 0); while (fgets (line, sizeof (line), f) != NULL) { @@ -2043,7 +2390,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2052,7 +2399,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Checkin-prog ", 0); @@ -2078,7 +2425,7 @@ send_repository (dir, repos, update_dir) else sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG); - f = fopen (adm_name, "r"); + f = CVS_FOPEN (adm_name, "r"); if (f == NULL) { if (! existence_error (errno)) @@ -2087,7 +2434,7 @@ send_repository (dir, repos, update_dir) else { char line[80]; - char *nl; + char *nl = NULL; send_to_server ("Update-prog ", 0); @@ -2243,7 +2590,7 @@ client_expand_modules (argc, argv, local) for (i = 0; i < argc; ++i) send_arg (argv[i]); - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); send_to_server ("expand-modules\012", 0); @@ -2259,8 +2606,9 @@ client_expand_modules (argc, argv, local) } void -client_send_expansions (local) - int local; +client_send_expansions (local, where) + int local; + char *where; { int i; char *argv[1]; @@ -2275,17 +2623,17 @@ client_send_expansions (local) for (i = 0; i < modules_count; ++i) { - argv[0] = modules_vector[i]; + argv[0] = where ? where : modules_vector[i]; if (isfile (argv[0])) send_files (1, argv, local, 0); } - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); } void client_nonexpanded_setup () { - send_a_repository ("", server_cvsroot, ""); + send_a_repository ("", CVSroot_directory, ""); } static void @@ -2293,8 +2641,13 @@ handle_m (args, len) char *args; int len; { - fwrite (args, len, sizeof (*args), stdout); - putc ('\n', stdout); + /* In the case where stdout and stderr point to the same place, + fflushing stderr will make output happen in the correct order. + Often stderr will be line-buffered and this won't be needed, + but not always. */ + fflush (stderr); + fwrite (args, len, sizeof (*args), stdout); + putc ('\n', stdout); } static void @@ -2302,8 +2655,20 @@ handle_e (args, len) char *args; int len; { - fwrite (args, len, sizeof (*args), stderr); - putc ('\n', stderr); + /* In the case where stdout and stderr point to the same place, + fflushing stdout will make output happen in the correct order. */ + fflush (stdout); + fwrite (args, len, sizeof (*args), stderr); + putc ('\n', stderr); +} + +/*ARGSUSED*/ +static void +handle_f (args, len) + char *args; + int len; +{ + fflush (stderr); } #endif /* CLIENT_SUPPORT */ @@ -2328,6 +2693,9 @@ struct response responses[] = RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional), RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional), RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential), + RSP_LINE("Created", handle_created, response_type_normal, rs_optional), + RSP_LINE("Update-existing", handle_update_existing, response_type_normal, + rs_optional), 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), @@ -2355,6 +2723,7 @@ struct response responses[] = rs_optional), RSP_LINE("M", handle_m, response_type_normal, rs_essential), RSP_LINE("E", handle_e, response_type_normal, rs_essential), + RSP_LINE("F", handle_f, response_type_normal, rs_optional), /* Possibly should be response_type_error. */ RSP_LINE(NULL, NULL, response_type_normal, rs_essential) @@ -2375,91 +2744,52 @@ 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; - size_t wrtn = 0; - -#ifdef VMS - /* send() blocks under VMS */ - if (send (server_sock, str + wrtn, len - wrtn, 0) < 0) - error (1, errno, "writing to server socket"); -#else /* VMS */ - while (wrtn < len) - { - just_wrtn = send (server_sock, str + wrtn, len - wrtn, 0); + static int nbytes; - if (just_wrtn == -1) - error (1, errno, "writing to server socket"); - - wrtn += just_wrtn; - if (wrtn == len) - break; - } -#endif /* VMS */ - } - else -#endif /* NO_SOCKET_TO_FD */ - { - size_t wrtn = 0; + if (len == 0) + len = strlen (str); + + buf_output (to_server, str, len); - 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"); - } + /* There is no reason not to send data to the server, so do it + whenever we've accumulated enough information in the buffer to + make it worth sending. */ + nbytes += len; + if (nbytes >= 2 * BUFFER_DATA_SIZE) + { + int status; + + status = buf_send_output (to_server); + if (status != 0) + error (1, status, "error writing to server"); + nbytes = 0; } - - if (to_server_logfile) - if (fwrite (str, 1, len, to_server_logfile) < len) - error (0, errno, "writing to to-server logfile"); } -/* Read up to LEN bytes from the server. Returns actual number of bytes - read. Gives a fatal error on EOF or error. */ +/* Read up to LEN bytes from the server. Returns actual number of + bytes read, which will always be at least one; blocks if there is + no data available at all. Gives a fatal error on EOF or error. */ static size_t try_read_from_server (buf, len) char *buf; size_t len; { - int nread; + int status, nread; + char *data; -#ifdef NO_SOCKET_TO_FD - if (use_socket_style) + status = buf_read_data (from_server, len, &data, &nread); + if (status != 0) { - nread = recv (server_sock, buf, len, 0); - if (nread == -1) - error (1, errno, "reading from server"); - } - else -#endif - { - nread = fread (buf, 1, len, from_server); - if (ferror (from_server)) - error (1, errno, "reading from server"); - if (feof (from_server)) + if (status == -1) error (1, 0, "end of file from server (consult above messages if any)"); + else if (status == -2) + error (1, 0, "out of memory"); + else + error (1, status, "reading from server"); } - /* Log, if that's what we're doing. */ - if (from_server_logfile != NULL && nread > 0) - { - size_t towrite = nread; - if (fwrite (buf, 1, towrite, from_server_logfile) < towrite) - error (0, errno, "writing to from-server logfile"); - } + memcpy (buf, data, nread); return nread; } @@ -2493,7 +2823,7 @@ get_server_responses () char *cmd; int len; - len = read_line (&cmd, 0); + len = read_line (&cmd); for (rs = responses; rs->name != NULL; ++rs) if (strncmp (cmd, rs->name, strlen (rs->name)) == 0) { @@ -2539,91 +2869,120 @@ int get_responses_and_close () { int errs = get_server_responses (); + int status; + + if (last_entries != NULL) + { + Entries_Close (last_entries); + last_entries = NULL; + } do_deferred_progs (); if (client_prune_dirs) process_prune_candidates (); + /* The calls to buf_shutdown are currently only meaningful when we + are using compression. First we shut down TO_SERVER. That + tells the server that its input is finished. It then shuts + down the buffer it is sending to us, at which point our shut + down of FROM_SERVER will complete. */ + + status = buf_shutdown (to_server); + if (status != 0) + error (0, status, "shutting down buffer to server"); + status = buf_shutdown (from_server); + if (status != 0) + error (0, status, "shutting down buffer from server"); + #ifdef NO_SOCKET_TO_FD if (use_socket_style) - { - if (shutdown (server_sock, 2) < 0) - error (1, errno, "shutting down server socket"); - } + { + if (shutdown (server_sock, 2) < 0) + error (1, errno, "shutting down server socket"); + } else #endif /* NO_SOCKET_TO_FD */ - { + { #if defined(HAVE_KERBEROS) || defined(USE_DIRECT_TCP) || defined(AUTH_CLIENT_SUPPORT) - if (server_fd != -1) - { - if (shutdown (server_fd, 1) < 0) - error (1, errno, "shutting down connection to %s", server_host); + if (server_fd != -1) + { + if (shutdown (server_fd, 1) < 0) + error (1, errno, "shutting down connection to %s", + CVSroot_hostname); /* * 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); - } - } + if (fileno (from_server_fp) != fileno (to_server_fp)) + { + if (fclose (to_server_fp) != 0) + error (1, errno, + "closing down connection to %s", + CVSroot_hostname); + } + } else #endif /* HAVE_KERBEROS || USE_DIRECT_TCP || AUTH_CLIENT_SUPPORT */ #ifdef SHUTDOWN_SERVER - SHUTDOWN_SERVER (fileno (to_server)); + SHUTDOWN_SERVER (fileno (to_server_fp)); #else /* ! SHUTDOWN_SERVER */ - { - + { + #ifdef START_RSH_WITH_POPEN_RW - if (pclose (to_server) == EOF) + if (pclose (to_server_fp) == EOF) #else /* ! START_RSH_WITH_POPEN_RW */ - if (fclose (to_server) == EOF) + if (fclose (to_server_fp) == EOF) #endif /* START_RSH_WITH_POPEN_RW */ - { - error (1, errno, "closing connection to %s", server_host); - } + { + error (1, errno, "closing connection to %s", + CVSroot_hostname); + } } - 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); + if (! buf_empty_p (from_server) + || getc (from_server_fp) != EOF) + error (0, 0, "dying gasps from %s unexpected", CVSroot_hostname); + else if (ferror (from_server_fp)) + error (0, errno, "reading from %s", CVSroot_hostname); + + fclose (from_server_fp); #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 /* ! RSH_NOT_TRANSPARENT */ server_started = 0; + /* see if we need to sleep before returning */ + if (last_register_time) + { + time_t now; + + (void) time (&now); + if (now == last_register_time) + sleep (1); /* to avoid time-stamp races */ + } + return errs; } -#ifndef RSH_NOT_TRANSPARENT static void start_rsh_server PROTO((int *, int *)); -#endif /* RSH_NOT_TRANSPARENT */ int supported_request (name) - char *name; + char *name; { - struct request *rq; - - for (rq = requests; rq->name; rq++) - if (!strcmp (rq->name, name)) - return rq->status == rq_supported; - error (1, 0, "internal error: testing support for unknown option?"); - /* NOTREACHED */ - return 0; + struct request *rq; + + for (rq = requests; rq->name; rq++) + if (!strcmp (rq->name, name)) + return rq->status == rq_supported; + error (1, 0, "internal error: testing support for unknown option?"); + /* NOTREACHED */ + return 0; } @@ -2652,7 +3011,12 @@ init_sockaddr (name, hostname, port) int auth_server_port_number () { - return CVS_AUTH_PORT; + struct servent *s = getservbyname ("cvspserver", "tcp"); + + if (s) + return ntohs (s->s_port); + else + return CVS_AUTH_PORT; } @@ -2679,9 +3043,6 @@ connect_to_pserver (tofdp, fromfdp, verify_only) int port_number; struct sockaddr_in client_sai; - /* Does nothing if already called before now. */ - parse_cvsroot (); - sock = socket (AF_INET, SOCK_STREAM, 0); if (sock == -1) { @@ -2689,19 +3050,19 @@ connect_to_pserver (tofdp, fromfdp, verify_only) exit (EXIT_FAILURE); } port_number = auth_server_port_number (); - init_sockaddr (&client_sai, server_host, port_number); + init_sockaddr (&client_sai, CVSroot_hostname, 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); + error (1, errno, "connect to %s:%d failed", CVSroot_hostname, + port_number); /* 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 *repository = CVSroot_directory; + char *username = CVSroot_username; char *password = NULL; char *end = NULL; @@ -2717,7 +3078,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only) } /* Get the password, probably from ~/.cvspass. */ - password = get_cvs_password (server_user, server_host, server_cvsroot); + password = get_cvs_password (); /* Announce that we're starting the authorization protocol. */ send (sock, begin, strlen (begin), 0); @@ -2751,7 +3112,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only) for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++) { if (recv (sock, &ch, 1, 0) < 0) - error (1, errno, "recv() from server %s", server_host); + error (1, errno, "recv() from server %s", CVSroot_hostname); read_buf[i] = ch; } @@ -2763,9 +3124,9 @@ connect_to_pserver (tofdp, fromfdp, verify_only) { error (0, 0, "authorization failed: server %s rejected access", - server_host); + CVSroot_hostname); error (1, errno, - "shutdown() failed (server %s)", server_host); + "shutdown() failed (server %s)", CVSroot_hostname); } if (verify_only) @@ -2773,7 +3134,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only) else error (1, 0, "authorization failed: server %s rejected access", - server_host); + CVSroot_hostname); } else if (strcmp (read_buf, "I LOVE YOU\n") != 0) { @@ -2782,19 +3143,19 @@ connect_to_pserver (tofdp, fromfdp, verify_only) { error (0, 0, "unrecognized auth response from %s: %s", - server_host, read_buf); - error (1, errno, "shutdown() failed, server %s", server_host); + CVSroot_hostname, read_buf); + error (1, errno, "shutdown() failed, server %s", CVSroot_hostname); } error (1, 0, "unrecognized auth response from %s: %s", - server_host, read_buf); + CVSroot_hostname, read_buf); } } if (verify_only) { if (shutdown (sock, 2) < 0) - error (0, errno, "shutdown() failed, server %s", server_host); + error (0, errno, "shutdown() failed, server %s", CVSroot_hostname); return 1; } else @@ -2831,144 +3192,148 @@ connect_to_pserver (tofdp, fromfdp, verify_only) */ void start_tcp_server (tofdp, fromfdp) - int *tofdp, *fromfdp; + int *tofdp, *fromfdp; { - int tofd, fromfd; - - struct hostent *hp; - char *hname; - const char *portenv; - int port; - struct sockaddr_in sin; - int s; + int tofd = -1, fromfd; + struct hostent *hp; + char *hname; + const char *portenv; + int port; + struct sockaddr_in sin; + int s; #if HAVE_KERBEROS - KTEXT_ST ticket; - const char *realm; + KTEXT_ST ticket; + const char *realm; #endif /* HAVE_KERBEROS */ - 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); + int status; + +#ifndef HAVE_KERBEROS + /* It is a crock to have :kserver: sometimes mean kerberos, + and sometimes mean "direct tcp", based on USE_DIRECT_TCP. + If we need the "direct tcp" stuff, we need a new access method, + like :direct: or something. */ + error (1, 0, "this CVS executable does not support kerberos"); +#endif + + /* + * We look up the host to give a better error message if it + * does not exist. However, we then pass CVSroot_hostname 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 (CVSroot_hostname); + if (hp == NULL) + error (1, 0, "%s: unknown host", CVSroot_hostname); + hname = xmalloc (strlen (hp->h_name) + 1); + strcpy (hname, hp->h_name); #if HAVE_KERBEROS - realm = krb_realmofhost (hname); + realm = krb_realmofhost (hname); #endif /* HAVE_KERBEROS */ - - /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ - portenv = getenv ("CVS_CLIENT_PORT"); - if (portenv != NULL) + + /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ + portenv = getenv ("CVS_CLIENT_PORT"); + if (portenv != NULL) { - port = atoi (portenv); - if (port <= 0) - goto try_rsh_no_message; - if (trace) - fprintf(stderr, "Using TCP port %d to contact server.\n", port); - port = htons (port); + port = atoi (portenv); + if (port <= 0) + { + error (0, 0, "CVS_CLIENT_PORT must be a positive number! If you"); + error (0, 0, "are trying to force a connection via rsh, please"); + error (0, 0, "put \":server:\" at the beginning of your CVSROOT"); + error (1, 0, "variable."); + } + if (trace) + fprintf(stderr, "Using TCP port %d to contact server.\n", port); + port = htons (port); } - else + else { - struct servent *sp; - - sp = getservbyname ("cvs", "tcp"); - if (sp == NULL) - port = htons (CVS_PORT); - else - port = sp->s_port; + 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) + + 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; + + if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) { - error (0, errno, "connect"); - close (s); + error (0, errno, "connect"); + close (s); } - else + else { #ifdef HAVE_KERBEROS - 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) + struct sockaddr_in laddr; + int laddrlen; + MSG_DAT msg_data; + CREDENTIALS cred; + + 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); + error (0, 0, "kerberos: %s", krb_get_err_text(status)); + close (s); } - else + else { + memcpy (kblock, cred.session, sizeof (C_Block)); + #endif /* HAVE_KERBEROS */ - server_fd = s; - close_on_exec (server_fd); - tofd = fromfd = s; + server_fd = s; + close_on_exec (server_fd); + tofd = fromfd = s; #ifdef HAVE_KERBEROS } #endif /* HAVE_KERBEROS */ } - if (tofd == -1) + if (tofd == -1) { - /* FIXME: Falling back like this is slow and we should probably - just make it a fatal error (so that people use the right - environment variables or, when we get around to implementing - the right ones, access methods). */ - 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 /* RSH_NOT_TRANSPARENT */ -#if defined (START_SERVER) - START_SERVER (&tofd, &fromfd, getcaller (), - server_user, server_host, server_cvsroot); -#endif /* defined (START_SERVER) */ -#endif /* ! RSH_NOT_TRANSPARENT */ +#ifdef HAVE_KERBEROS + error (0, 0, "Kerberos connect failed"); +#else + error (0, 0, "Direct TCP connect failed"); +#endif + error (1, 0, "couldn't connect to remote host %s", CVSroot_hostname); } - free (hname); - /* Give caller the values it wants. */ - *tofdp = tofd; - *fromfdp = fromfd; + free (hname); + + /* Give caller the values it wants. */ + *tofdp = tofd; + *fromfdp = fromfd; } #endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ @@ -2992,80 +3357,77 @@ send_variable_proc (node, closure) void start_server () { - int tofd, fromfd; - char *log = getenv ("CVS_CLIENT_LOG"); + int tofd, fromfd; + char *log = getenv ("CVS_CLIENT_LOG"); - /* Note that generally speaking we do *not* fall back to a different - way of connecting if the first one does not work. This is slow - (*really* slow on a 14.4kbps link); the clean way to have a CVS - which supports several ways of connecting is with access methods. */ + /* Note that generally speaking we do *not* fall back to a different + way of connecting if the first one does not work. This is slow + (*really* slow on a 14.4kbps link); the clean way to have a CVS + which supports several ways of connecting is with access methods. */ - /* Init these to NULL. They will be set later if logging is on. */ - from_server_logfile = (FILE *) NULL; - to_server_logfile = (FILE *) NULL; + switch (CVSroot_method) + { #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 */ - { + case pserver_method: + /* Toss the return value. It will die with error if anything + goes wrong anyway. */ + connect_to_pserver (&tofd, &fromfd, 0); + break; +#endif + #if HAVE_KERBEROS || USE_DIRECT_TCP - start_tcp_server (&tofd, &fromfd); -#else + case kserver_method: + start_tcp_server (&tofd, &fromfd); + break; +#endif -# if ! RSH_NOT_TRANSPARENT - start_rsh_server (&tofd, &fromfd); -# else + case ext_method: + start_rsh_server (&tofd, &fromfd); + break; -# if defined(START_SERVER) - START_SERVER (&tofd, &fromfd, getcaller (), - server_user, server_host, server_cvsroot); -# endif + case server_method: +#if defined(START_SERVER) + START_SERVER (&tofd, &fromfd, getcaller (), + CVSroot_username, CVSroot_hostname, + CVSroot_directory); +# if defined (START_SERVER_RETURNS_SOCKET) && defined (NO_SOCKET_TO_FD) + /* This is a system on which we can only write to a socket + using send/recv. Therefore its START_SERVER needs to + return a socket. */ + use_socket_style = 1; + server_sock = tofd; # endif + +#else + /* FIXME: It should be possible to implement this portably, + like pserver, which would get rid of the duplicated code + in {vms,windows-NT,...}/startserver.c. */ + error (1, 0, "\ +the :server: access method is not supported by this port of CVS"); #endif - } + break; -#if defined(VMS) && defined(NO_SOCKET_TO_FD) - /* Avoid mixing sockets with stdio */ - use_socket_style = 1; - server_sock = tofd; -#endif /* VMS && NO_SOCKET_TO_FD */ + default: + error (1, 0, "\ +(start_server internal error): unknown access method"); + break; + } /* "Hi, I'm Darlene and I'll be your server tonight..." */ server_started = 1; - /* Set up logfiles, if any. */ - if (log) +#ifdef NO_SOCKET_TO_FD + if (use_socket_style) { - int len = strlen (log); - char *buf = xmalloc (len + 5); - char *p; - - strcpy (buf, log); - p = buf + len; - - strcpy (p, ".in"); - to_server_logfile = open_file (buf, "w"); - if (to_server_logfile == NULL) - error (0, errno, "opening to-server logfile %s", buf); - - strcpy (p, ".out"); - from_server_logfile = open_file (buf, "w"); - if (from_server_logfile == NULL) - error (0, errno, "opening from-server logfile %s", buf); - - free (buf); + to_server = socket_buffer_initialize (server_sock, 0, + buf_memory_error); + from_server = socket_buffer_initialize (server_sock, 1, + buf_memory_error); } - -#ifdef NO_SOCKET_TO_FD - if (! use_socket_style) + else #endif /* NO_SOCKET_TO_FD */ - { + { /* todo: some OS's don't need these calls... */ close_on_exec (tofd); close_on_exec (fromfd); @@ -3081,26 +3443,65 @@ start_server () } /* 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); - } + to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE); + if (to_server_fp == NULL) + error (1, errno, "cannot fdopen %d for write", tofd); + to_server = stdio_buffer_initialize (to_server_fp, 0, + buf_memory_error); + + from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ); + if (from_server_fp == NULL) + error (1, errno, "cannot fdopen %d for read", fromfd); + from_server = stdio_buffer_initialize (from_server_fp, 1, + buf_memory_error); + } + + /* Set up logfiles, if any. */ + if (log) + { + int len = strlen (log); + char *buf = xmalloc (len + 5); + char *p; + FILE *fp; + + strcpy (buf, log); + p = buf + len; + + /* Open logfiles in binary mode so that they reflect + exactly what was transmitted and received (that is + more important than that they be maximally + convenient to view). */ + strcpy (p, ".in"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening to-server logfile %s", buf); + else + to_server = log_buffer_initialize (to_server, fp, 0, + buf_memory_error); + + strcpy (p, ".out"); + fp = open_file (buf, "wb"); + if (fp == NULL) + error (0, errno, "opening from-server logfile %s", buf); + else + from_server = log_buffer_initialize (from_server, fp, 1, + buf_memory_error); + + free (buf); + } /* Clear static variables. */ if (toplevel_repos != NULL) - free (toplevel_repos); + free (toplevel_repos); toplevel_repos = NULL; if (last_dir_name != NULL) - free (last_dir_name); + free (last_dir_name); last_dir_name = NULL; if (last_repos != NULL) - free (last_repos); + free (last_repos); last_repos = NULL; if (last_update_dir != NULL) - free (last_update_dir); + free (last_update_dir); last_update_dir = NULL; stored_checksum_valid = 0; stored_mode_valid = 0; @@ -3108,7 +3509,7 @@ start_server () if (strcmp (command_name, "init") != 0) { send_to_server ("Root ", 0); - send_to_server (server_cvsroot, 0); + send_to_server (CVSroot_directory, 0); send_to_server ("\012", 1); } @@ -3207,9 +3608,55 @@ start_server () "This server does not support the global -l option."); } } + if (cvsencrypt) + { +#ifdef ENCRYPTION + /* Turn on encryption before turning on compression. We do + not want to try to compress the encrypted stream. Instead, + we want to encrypt the compressed stream. If we can't turn + on encryption, bomb out; don't let the user think the data + is being encrypted when it is not. */ +#ifdef HAVE_KERBEROS + if (CVSroot_method == kserver_method) + { + if (! supported_request ("Kerberos-encrypt")) + error (1, 0, "This server does not support encryption"); + send_to_server ("Kerberos-encrypt\012", 0); + to_server = krb_encrypt_buffer_initialize (to_server, 0, sched, + kblock, + buf_memory_error); + from_server = krb_encrypt_buffer_initialize (from_server, 1, + sched, kblock, + buf_memory_error); + } + else +#endif /* HAVE_KERBEROS */ + error (1, 0, "Encryption is only supported when using Kerberos"); +#else /* ! ENCRYPTION */ + error (1, 0, "This client does not support encryption"); +#endif /* ! ENCRYPTION */ + } if (gzip_level) { - if (supported_request ("gzip-file-contents")) + if (supported_request ("Gzip-stream")) + { + char gzip_level_buf[5]; + send_to_server ("Gzip-stream ", 0); + sprintf (gzip_level_buf, "%d", gzip_level); + send_to_server (gzip_level_buf, 0); + send_to_server ("\012", 1); + + /* All further communication with the server will be + compressed. */ + + to_server = compress_buffer_initialize (to_server, 0, gzip_level, + buf_memory_error); + from_server = compress_buffer_initialize (from_server, 1, + gzip_level, + buf_memory_error); + } +#ifndef NO_CLIENT_GZIP_PROCESS + else if (supported_request ("gzip-file-contents")) { char gzip_level_buf[5]; send_to_server ("gzip-file-contents ", 0); @@ -3217,10 +3664,16 @@ start_server () send_to_server (gzip_level_buf, 0); send_to_server ("\012", 1); + + file_gzip_level = gzip_level; } +#endif else { fprintf (stderr, "server doesn't support gzip-file-contents\n"); + /* Setting gzip_level to 0 prevents us from giving the + error twice if update has to contact the server again + to fetch unpatchable files. */ gzip_level = 0; } } @@ -3239,7 +3692,6 @@ start_server () walklist (variable_list, send_variable_proc, NULL); } -#ifndef RSH_NOT_TRANSPARENT /* Contact the server by starting it with rsh. */ /* Right now, we have two different definitions for this function, @@ -3258,68 +3710,68 @@ start_server () static void start_rsh_server (tofdp, fromfdp) - int *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; - + 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"; + /* "-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) + /* Then we strcat more things on the end one by one. */ + if (CVSroot_username != NULL) { - rsh_argv[i++] = "-l"; - rsh_argv[i++] = server_user; + rsh_argv[i++] = "-l"; + rsh_argv[i++] = CVSroot_username; } - - 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; + rsh_argv[i++] = CVSroot_hostname; + rsh_argv[i++] = cvs_server; + rsh_argv[i++] = "server"; + + /* Mark the end of the arg list. */ + rsh_argv[i] = (char *) NULL; - if (trace) + if (trace) { - fprintf (stderr, " -> Starting server: "); - fprintf (stderr, "%s", command); - putc ('\n', stderr); + 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]; + + /* 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 */ @@ -3346,7 +3798,7 @@ start_rsh_server (tofdp, fromfdp) versions of rsh that grab switches out of the middle of the command (they're calling the GNU getopt routines incorrectly). */ command = xmalloc (strlen (cvs_server) - + strlen (server_cvsroot) + + strlen (CVSroot_directory) + 50); /* If you are running a very old (Nov 3, 1994, before 1.5) @@ -3360,15 +3812,15 @@ start_rsh_server (tofdp, fromfdp) char **p = argv; *p++ = cvs_rsh; - *p++ = server_host; + *p++ = CVSroot_hostname; /* If the login names differ between client and server * pass it on to rsh. */ - if (server_user != NULL) + if (CVSroot_username != NULL) { *p++ = "-l"; - *p++ = server_user; + *p++ = CVSroot_username; } *p++ = command; @@ -3391,7 +3843,6 @@ start_rsh_server (tofdp, fromfdp) } #endif /* START_RSH_WITH_POPEN_RW */ -#endif /* ! RSH_NOT_TRANSPARENT */ @@ -3437,8 +3888,11 @@ send_modified (file, short_pathname, vers) int bufsize; int bin; + if (trace) + (void) fprintf (stderr, " -> Sending file `%s' to server\n", file); + /* Don't think we can assume fstat exists. */ - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) error (1, errno, "reading %s", short_pathname); mode_string = mode_to_string (sb.st_mode); @@ -3459,19 +3913,37 @@ send_modified (file, short_pathname, vers) else bin = 0; - fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0)); +#ifdef BROKEN_READWRITE_CONVERSION + if (!bin) + { + /* If only stdio, not open/write/etc., do text/binary + conversion, use convert_file which can compensate + (FIXME: we could just use stdio instead which would + avoid the whole problem). */ + char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); + convert_file (file, O_RDONLY, + tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY); + fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY); + if (fd < 0) + error (1, errno, "reading %s", short_pathname); + } + else + fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY); +#else + fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0)); +#endif if (fd < 0) error (1, errno, "reading %s", short_pathname); - if (gzip_level && sb.st_size > 100) + if (file_gzip_level && sb.st_size > 100) { int nread, newsize = 0, gzip_status; pid_t gzip_pid; char *bufp = buf; int readsize = 8192; #ifdef LINES_CRLF_TERMINATED - char tempfile[L_tmpnam]; + char *tempfile; int converting; #endif /* LINES_CRLF_TERMINATED */ @@ -3503,7 +3975,7 @@ send_modified (file, short_pathname, vers) if (close (fd) < 0) error (0, errno, "warning: can't close %s", short_pathname); - tmpnam (tempfile); + tempfile = cvs_temp_name (); convert_file (file, O_RDONLY, tempfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY); @@ -3513,13 +3985,13 @@ send_modified (file, short_pathname, vers) 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); + fd = CVS_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); + fd = filter_through_gzip (fd, 1, file_gzip_level, &gzip_pid); /* FIXME: is there any reason to go through all this realloc'ing when we could just be writing the data to the network as we read @@ -3558,9 +4030,11 @@ send_modified (file, short_pathname, vers) #if LINES_CRLF_TERMINATED if (converting) { - if (unlink (tempfile) < 0) + if ( CVS_UNLINK (tempfile) < 0) error (0, errno, "warning: can't remove temp file %s", tempfile); + free (tempfile); + tempfile = NULL; } #endif /* LINES_CRLF_TERMINATED */ @@ -3613,6 +4087,14 @@ send_modified (file, short_pathname, vers) sprintf (tmp, "%lu\012", (unsigned long) newsize); send_to_server (tmp, 0); } +#ifdef BROKEN_READWRITE_CONVERSION + if (!bin) + { + char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); + if (CVS_UNLINK (tfile) < 0) + error (0, errno, "warning: can't remove temp file %s", tfile); + } +#endif /* * Note that this only ends with a newline if the file ended with @@ -3625,29 +4107,40 @@ send_modified (file, short_pathname, vers) free (mode_string); } -static int send_fileproc PROTO ((struct file_info *finfo)); +static int send_fileproc PROTO ((void *callerdat, struct file_info *finfo)); /* Deal with one file. */ static int -send_fileproc (finfo) +send_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Vers_TS *vers; + struct file_info xfinfo; + /* File name to actually use. Might differ in case from + finfo->file. */ + char *filename; send_a_repository ("", finfo->repository, finfo->update_dir); - vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL, - (char *)NULL, - finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL); + xfinfo = *finfo; + xfinfo.repository = NULL; + xfinfo.rcs = NULL; + vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); + + if (vers->entdata != NULL) + filename = vers->entdata->user; + else + filename = finfo->file; if (vers->vn_user != NULL) { char *tmp; - tmp = xmalloc (strlen (finfo->file) + strlen (vers->vn_user) + tmp = xmalloc (strlen (filename) + strlen (vers->vn_user) + strlen (vers->options) + 200); sprintf (tmp, "Entry /%s/%s/%s%s/%s/", - finfo->file, vers->vn_user, + filename, vers->vn_user, vers->ts_conflict == NULL ? "" : "+", (vers->ts_conflict == NULL ? "" : (vers->ts_user != NULL && @@ -3684,7 +4177,7 @@ send_fileproc (finfo) { /* if the server is old, use the old request... */ send_to_server ("Lost ", 0); - send_to_server (finfo->file, 0); + send_to_server (filename, 0); send_to_server ("\012", 1); /* * Otherwise, don't do anything for missing files, @@ -3695,7 +4188,7 @@ send_fileproc (finfo) else if (vers->ts_rcs == NULL || strcmp (vers->ts_user, vers->ts_rcs) != 0) { - send_modified (finfo->file, finfo->fullname, vers); + send_modified (filename, finfo->fullname, vers); } else { @@ -3703,7 +4196,7 @@ send_fileproc (finfo) if (use_unchanged) { send_to_server ("Unchanged ", 0); - send_to_server (finfo->file, 0); + send_to_server (filename, 0); send_to_server ("\012", 1); } } @@ -3745,25 +4238,27 @@ send_ignproc (file, dir) } } -static int send_filesdoneproc PROTO ((int, char *, char *)); +static int send_filesdoneproc PROTO ((void *, int, char *, char *, List *)); static int -send_filesdoneproc (err, repository, update_dir) +send_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { /* if this directory has an ignore list, process it then free it */ if (ignlist) { - ignore_files (ignlist, update_dir, send_ignproc); + ignore_files (ignlist, entries, update_dir, send_ignproc); dellist (&ignlist); } return (err); } -static Dtype send_dirent_proc PROTO ((char *, char *, char *)); +static Dtype send_dirent_proc PROTO ((void *, char *, char *, char *, List *)); /* * send_dirent_proc () is called back by the recursion processor before a @@ -3774,20 +4269,17 @@ static Dtype send_dirent_proc PROTO ((char *, char *, char *)); * */ static Dtype -send_dirent_proc (dir, repository, update_dir) +send_dirent_proc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { int dir_exists; + char *cvsadm_name; char *cvsadm_repos_name; - /* - * If the directory does not exist yet (e.g. "cvs update -d - * foo"), no need to send any files from it. - */ - dir_exists = isdir (dir); - if (ignore_directory (update_dir)) { /* print the warm fuzzy message */ @@ -3796,6 +4288,19 @@ send_dirent_proc (dir, repository, update_dir) return (R_SKIP_ALL); } + /* + * If the directory does not exist yet (e.g. "cvs update -d foo"), + * no need to send any files from it. If the directory does not + * have a CVS directory, then we pretend that it does not exist. + * Otherwise, we will fail when trying to open the Entries file. + * This case will happen when checking out a module defined as + * ``-a .''. + */ + cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); + sprintf (cvsadm_name, "%s/%s", dir, CVSADM); + dir_exists = isdir (cvsadm_name); + free (cvsadm_name); + /* initialize the ignore list for this directory */ ignlist = getlist (); @@ -3826,27 +4331,35 @@ send_dirent_proc (dir, repository, update_dir) /* * Send each option in a string to the server, one by one. - * This assumes that the options are single characters. For - * more complex parsing, do it yourself. + * This assumes that the options are separated by spaces, for example + * STRING might be "--foo -C5 -y". */ void send_option_string (string) char *string; { + char *copy; char *p; - char it[3]; - - for (p = string; p[0]; p++) { - if (p[0] == ' ') - continue; - if (p[0] == '-') - continue; - it[0] = '-'; - it[1] = p[0]; - it[2] = '\0'; - send_arg (it); + + copy = xstrdup (string); + p = copy; + while (1) + { + char *s; + char l; + + for (s = p; *s != ' ' && *s != '\0'; s++) + ; + l = *s; + *s = '\0'; + if (s != p) + send_arg (p); + if (l == '\0') + break; + p = s + 1; } + free (copy); } @@ -3863,6 +4376,11 @@ send_file_names (argc, argv, flags) char *q; int level; int max_level; + char *line; + size_t line_allocated; + + line = NULL; + line_allocated = 0; /* The fact that we do this here as well as start_recursion is a bit of a performance hit. Perhaps worth cleaning up someday. */ @@ -3919,6 +4437,52 @@ send_file_names (argc, argv, flags) char buf[1]; char *p = argv[i]; +#ifdef FILENAMES_CASE_INSENSITIVE + /* We want to send the file name as it appears + in CVS/Entries. We put this inside an ifdef + to avoid doing all these system calls in + cases where fncmp is just strcmp anyway. */ + /* For now just do this for files in the local + directory. Would be nice to handle the + non-local case too, though. */ + if (p == last_component (p)) + { + FILE *ent; + + ent = CVS_FOPEN (CVSADM_ENT, "r"); + if (ent == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot read %s", CVSADM_ENT); + } + else + { + while (getline (&line, &line_allocated, ent) > 0) + { + char *cp; + + if (line[0] != '/') + continue; + cp = strchr (line + 1, '/'); + if (cp == NULL) + continue; + *cp = '\0'; + if (fncmp (p, line + 1) == 0) + { + p = line + 1; + break; + } + } + if (ferror (ent)) + error (0, errno, "cannot read %s", CVSADM_ENT); + if (fclose (ent) < 0) + error (0, errno, "cannot close %s", CVSADM_ENT); + /* We don't attempt to look at CVS/Entries.Log. In a few cases that might + lead to strange behaviors, but they should be fairly obscure. */ + } + } +#endif /* FILENAMES_CASE_INSENSITIVE */ + send_to_server ("Argument ", 0); while (*p) @@ -3942,6 +4506,9 @@ send_file_names (argc, argv, flags) send_to_server ("\012", 1); } + if (line != NULL) + free (line); + if (flags & SEND_EXPAND_WILD) { int i; @@ -3975,8 +4542,8 @@ send_files (argc, argv, local, aflag) */ err = start_recursion (send_fileproc, send_filesdoneproc, - send_dirent_proc, (DIRLEAVEPROC)NULL, - argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0); + send_dirent_proc, (DIRLEAVEPROC)NULL, NULL, + argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0); if (err) exit (EXIT_FAILURE); if (toplevel_repos == NULL) @@ -3987,7 +4554,7 @@ send_files (argc, argv, local, aflag) * latter case; I don't think toplevel_repos matters for the * former. */ - toplevel_repos = xstrdup (server_cvsroot); + toplevel_repos = xstrdup (CVSroot_directory); send_repository ("", toplevel_repos, "."); } @@ -4049,7 +4616,7 @@ client_import_done () */ /* FIXME: "can't happen" now that we call client_import_setup at the beginning. */ - toplevel_repos = xstrdup (server_cvsroot); + toplevel_repos = xstrdup (CVSroot_directory); send_repository ("", toplevel_repos, "."); } @@ -4094,7 +4661,7 @@ notified_a_file (data, ent_list, short_pathname, filename) { if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSADM_NOTIFY); - if (unlink (CVSADM_NOTIFY) < 0) + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) error (0, errno, "cannot remove %s", CVSADM_NOTIFY); return; } @@ -4234,257 +4801,13 @@ client_senddate (date) option_with_arg ("-D", buf); } -int -client_commit (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return commit (argc, argv); -} - -int -client_update (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return update (argc, argv); -} - -int -client_checkout (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return checkout (argc, argv); -} - -int -client_diff (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return diff (argc, argv); /* Call real code */ -} - -int -client_status (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - return status (argc, argv); -} - -int -client_log (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return cvslog (argc, argv); /* Call real code */ -} - -int -client_add (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return add (argc, argv); /* Call real code */ -} - -int -client_remove (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return cvsremove (argc, argv); /* Call real code */ -} - -int -client_rdiff (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return patch (argc, argv); /* Call real code */ -} - -int -client_tag (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return tag (argc, argv); /* Call real code */ -} - -int -client_rtag (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return rtag (argc, argv); /* Call real code */ -} - -int -client_import (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return import (argc, argv); /* Call real code */ -} - -int -client_admin (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return admin (argc, argv); /* Call real code */ -} - -int -client_export (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return checkout (argc, argv); /* Call real code */ -} - -int -client_history (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - return history (argc, argv); /* Call real code */ -} - -int -client_release (argc, argv) - int argc; - char **argv; -{ - - parse_cvsroot (); - - 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 */ -} - void send_init_command () { - /* This is here because we need the server_cvsroot variable. */ + /* This is here because we need the CVSroot_directory variable. */ send_to_server ("init ", 0); - send_to_server (server_cvsroot, 0); + send_to_server (CVSroot_directory, 0); send_to_server ("\012", 0); } -int -client_init (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return init (argc, argv); /* Call real code */ -} - -int -client_annotate (argc, argv) - int argc; - char **argv; -{ - parse_cvsroot (); - - return annotate (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 b238c322b09..d68412628f7 100644 --- a/gnu/usr.bin/cvs/src/client.h +++ b/gnu/usr.bin/cvs/src/client.h @@ -5,45 +5,27 @@ extern char *mode_to_string PROTO((mode_t)); extern int change_mode PROTO((char *, char *)); extern int gzip_level; +extern int file_gzip_level; extern int filter_through_gzip PROTO((int, int, int, pid_t *)); extern int filter_through_gunzip PROTO((int, int, pid_t *)); -#ifdef CLIENT_SUPPORT -/* - * Functions to perform CVS commands via the protocol. argc and argv - * are the arguments and the return value is the exit status (zero success - * nonzero failure). - */ -extern int client_commit PROTO((int argc, char **argv)); -extern int client_update PROTO((int argc, char **argv)); -extern int client_checkout PROTO((int argc, char **argv)); -extern int client_diff PROTO((int argc, char **argv)); -extern int client_log PROTO((int argc, char **argv)); -extern int client_add PROTO((int argc, char **argv)); -extern int client_remove PROTO((int argc, char **argv)); -extern int client_status PROTO((int argc, char **argv)); -extern int client_rdiff PROTO((int argc, char **argv)); -extern int client_tag PROTO((int argc, char **argv)); -extern int client_rtag PROTO((int argc, char **argv)); -extern int client_import PROTO((int argc, char **argv)); -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)); -extern int client_init PROTO ((int argc, char **argv)); -extern int client_annotate PROTO ((int argc, char **argv)); +#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) -/* - * Flag variable for seeing whether common code is running as a client - * or to do a local operation. - */ -extern int client_active; +extern int cvsencrypt; + +#ifdef ENCRYPTION +#ifdef HAVE_KERBEROS +/* We can't declare the arguments without including krb.h, and I don't + want to do that in every file. */ +extern struct buffer *krb_encrypt_buffer_initialize (); + +#endif /* HAVE_KERBEROS */ +#endif /* ENCRYPTION */ + +#endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */ + +#ifdef CLIENT_SUPPORT /* * Flag variable for seeing whether the server has been started yet. * As of this writing, only edit.c:notify_check() uses it. @@ -62,7 +44,11 @@ int connect_to_pserver PROTO((int *tofdp, int* fromfdp, int verify_only)); #endif /* AUTH_CLIENT_SUPPORT */ #ifdef AUTH_SERVER_SUPPORT -extern void authenticate_connection PROTO ((void)); +extern void pserver_authenticate_connection PROTO ((void)); +#endif + +#if defined (SERVER_SUPPORT) && defined (HAVE_KERBEROS) +extern void kserver_authenticate_connection PROTO ((void)); #endif /* Talking to the server. */ @@ -174,7 +160,7 @@ extern struct response responses[]; extern void client_senddate PROTO((const char *date)); extern void client_expand_modules PROTO((int argc, char **argv, int local)); -extern void client_send_expansions PROTO((int local)); +extern void client_send_expansions PROTO((int local, char *where)); extern void client_nonexpanded_setup PROTO((void)); extern void send_init_command PROTO ((void)); diff --git a/gnu/usr.bin/cvs/src/create_adm.c b/gnu/usr.bin/cvs/src/create_adm.c index fd7fd4d7c0b..01d38f90bf8 100644 --- a/gnu/usr.bin/cvs/src/create_adm.c +++ b/gnu/usr.bin/cvs/src/create_adm.c @@ -53,12 +53,12 @@ Create_Admin (dir, update_dir, repository, tag, date) /* record the current cvs root for later use */ - Create_Root (dir, CVSroot); + Create_Root (dir, CVSroot_original); if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); else (void) strcpy (tmp, CVSADM_REP); - fout = fopen (tmp, "w+"); + fout = CVS_FOPEN (tmp, "w+"); if (fout == NULL) { if (update_dir[0] == '\0') @@ -74,11 +74,11 @@ Create_Admin (dir, update_dir, repository, tag, date) * If the Repository file is to hold a relative path, try to strip off * the leading CVSroot argument. */ - if (CVSroot != NULL) + if (CVSroot_directory != NULL) { char path[PATH_MAX]; - (void) sprintf (path, "%s/", CVSroot); + (void) sprintf (path, "%s/", CVSroot_directory); if (strncmp (repository, path, strlen (path)) == 0) cp = repository + strlen (path); } @@ -104,7 +104,7 @@ Create_Admin (dir, update_dir, repository, tag, date) (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); else (void) strcpy (tmp, CVSADM_ENT); - fout = fopen (tmp, "w+"); + fout = CVS_FOPEN (tmp, "w+"); if (fout == NULL) { if (update_dir[0] == '\0') diff --git a/gnu/usr.bin/cvs/src/cvsrc.c b/gnu/usr.bin/cvs/src/cvsrc.c index 140ce1c1f11..ebbc42c4525 100644 --- a/gnu/usr.bin/cvs/src/cvsrc.c +++ b/gnu/usr.bin/cvs/src/cvsrc.c @@ -110,7 +110,7 @@ read_cvsrc (argc, argv, cmdname) /* skip over command in the options line */ optstart = strtok (line + command_len, "\t \n"); - do + while (optstart) { new_argv [new_argc] = xstrdup (optstart); new_argv [new_argc+1] = NULL; @@ -126,9 +126,8 @@ read_cvsrc (argc, argv, cmdname) free(new_argv); new_argv = tmp_argv; } - + optstart = strtok (NULL, "\t \n"); } - while ((optstart = strtok (NULL, "\t \n")) != NULL); } if (line != NULL) diff --git a/gnu/usr.bin/cvs/src/diff.c b/gnu/usr.bin/cvs/src/diff.c index 7520cec4a92..ca113323b4a 100644 --- a/gnu/usr.bin/cvs/src/diff.c +++ b/gnu/usr.bin/cvs/src/diff.c @@ -16,12 +16,28 @@ #include "cvs.h" -static Dtype diff_dirproc PROTO((char *dir, char *pos_repos, char *update_dir)); -static int diff_filesdoneproc PROTO((int err, char *repos, char *update_dir)); -static int diff_dirleaveproc PROTO((char *dir, int err, char *update_dir)); -static int diff_file_nodiff PROTO((char *file, char *repository, List *entries, - RCSNode *rcs, Vers_TS *vers)); -static int diff_fileproc PROTO((struct file_info *finfo)); +enum diff_file +{ + DIFF_ERROR, + DIFF_ADDED, + DIFF_REMOVED, + DIFF_DIFFERENT, + DIFF_SAME +}; + +static Dtype diff_dirproc PROTO ((void *callerdat, char *dir, + char *pos_repos, char *update_dir, + List *entries)); +static int diff_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); +static int diff_dirleaveproc PROTO ((void *callerdat, char *dir, + int err, char *update_dir, + List *entries)); +static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo, + Vers_TS *vers, + enum diff_file)); +static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static void diff_mark_errors PROTO((int err)); static char *diff_rev1, *diff_rev2; @@ -35,18 +51,19 @@ static char *user_file_rev; #endif static char *options; +/* FIXME: arbitrary limit (security hole, if the client passes us + data which overflows it). */ static char opts[PATH_MAX]; static int diff_errors; static int empty_files = 0; +/* FIXME: should be documenting all the options here. They don't + perfectly match rcsdiff options (for example, we always support + --ifdef and --context, but rcsdiff only does if diff does). */ static const char *const diff_usage[] = { "Usage: %s %s [-lN] [rcsdiff-options]\n", -#ifdef CVS_DIFFDATE " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n", -#else - " [-r rev1 [-r rev2]] [files...] \n", -#endif "\t-l\tLocal directory only, not recursive\n", "\t-D d1\tDiff revision for date against working file.\n", "\t-D d2\tDiff rev1/date1 against date2.\n", @@ -56,6 +73,79 @@ static const char *const diff_usage[] = NULL }; +/* I copied this array directly out of diff.c in diffutils 2.7, after + removing the following entries, none of which seem relevant to use + with CVS: + --help + --version + --recursive + --unidirectional-new-file + --starting-file + --exclude + --exclude-from + --sdiff-merge-assist + + I changed the options which take optional arguments (--context and + --unified) to return a number rather than a letter, so that the + optional argument could be handled more easily. I changed the + --paginate and --brief options to return a number, since -l and -q + mean something else to cvs diff. + + The numbers 129- that appear in the fourth element of some entries + tell the big switch in `diff' how to process those options. -- Ian + + The following options, which diff lists as "An alias, no longer + recommended" have been removed: --file-label --entire-new-file + --ascii --print. */ + +static struct option const longopts[] = +{ + {"ignore-blank-lines", 0, 0, 'B'}, + {"context", 2, 0, 143}, + {"ifdef", 1, 0, 147}, + {"show-function-line", 1, 0, 'F'}, + {"speed-large-files", 0, 0, 'H'}, + {"ignore-matching-lines", 1, 0, 'I'}, + {"label", 1, 0, 'L'}, + {"new-file", 0, 0, 'N'}, + {"initial-tab", 0, 0, 'T'}, + {"width", 1, 0, 'W'}, + {"text", 0, 0, 'a'}, + {"ignore-space-change", 0, 0, 'b'}, + {"minimal", 0, 0, 'd'}, + {"ed", 0, 0, 'e'}, + {"forward-ed", 0, 0, 'f'}, + {"ignore-case", 0, 0, 'i'}, + {"paginate", 0, 0, 144}, + {"rcs", 0, 0, 'n'}, + {"show-c-function", 0, 0, 'p'}, + + /* This is a potentially very useful option, except the output is so + silly. It would be much better for it to look like "cvs rdiff -s" + which displays all the same info, minus quite a few lines of + extraneous garbage. */ + {"brief", 0, 0, 145}, + + {"report-identical-files", 0, 0, 's'}, + {"expand-tabs", 0, 0, 't'}, + {"ignore-all-space", 0, 0, 'w'}, + {"side-by-side", 0, 0, 'y'}, + {"unified", 2, 0, 146}, + {"left-column", 0, 0, 129}, + {"suppress-common-lines", 0, 0, 130}, + {"old-line-format", 1, 0, 132}, + {"new-line-format", 1, 0, 133}, + {"unchanged-line-format", 1, 0, 134}, + {"line-format", 1, 0, 135}, + {"old-group-format", 1, 0, 136}, + {"new-group-format", 1, 0, 137}, + {"unchanged-group-format", 1, 0, 138}, + {"changed-group-format", 1, 0, 139}, + {"horizon-lines", 1, 0, 140}, + {"binary", 0, 0, 142}, + {0, 0, 0, 0} +}; + int diff (argc, argv) int argc; @@ -65,6 +155,7 @@ diff (argc, argv) int c, err = 0; int local = 0; int which; + int option_index; if (argc == -1) usage (diff_usage); @@ -80,41 +171,50 @@ diff (argc, argv) opts[0] = '\0'; #endif optind = 1; - while ((c = getopt (argc, argv, - "abcdefhilnpqtuw0123456789BHNQRTC:D:F:I:L:V:k:r:")) != -1) + while ((c = getopt_long (argc, argv, + "abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", + longopts, &option_index)) != -1) { switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'h': case 'i': case 'n': case 'p': case 't': case 'u': - case 'w': case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': case 'B': - case 'H': case 'T': case 'Q': + case 'h': case 'i': case 'n': case 'p': case 's': case 't': + case 'u': case 'w': case 'y': case '0': case '1': case '2': + case '3': case '4': case '5': case '6': case '7': case '8': + case '9': case 'B': case 'H': case 'T': (void) sprintf (tmp, " -%c", (char) c); (void) strcat (opts, tmp); - if (c == 'Q') + break; + case 'C': case 'F': case 'I': case 'L': case 'U': case 'V': + case 'W': + (void) sprintf (tmp, " -%c", (char) c); + strcat (opts, tmp); + strcat (opts, optarg); + break; + case 147: + /* --ifdef. */ + strcat (opts, " -D"); + strcat (opts, optarg); + break; + case 129: case 130: case 131: case 132: case 133: case 134: + case 135: case 136: case 137: case 138: case 139: case 140: + case 141: case 142: case 143: case 144: case 145: case 146: + strcat (opts, " --"); + strcat (opts, longopts[option_index].name); + if (longopts[option_index].has_arg == 1 + || (longopts[option_index].has_arg == 2 + && optarg != NULL)) { - quiet = 1; - really_quiet = 1; - c = 'q'; + strcat (opts, "="); + strcat (opts, optarg); } break; - case 'C': case 'F': case 'I': case 'L': case 'V': -#ifndef CVS_DIFFDATE - case 'D': -#endif - (void) sprintf (tmp, " -%c%s", (char) c, optarg); - (void) strcat (opts, tmp); - break; case 'R': local = 0; break; case 'l': local = 1; break; - case 'q': - quiet = 1; - break; case 'k': if (options) free (options); @@ -129,7 +229,6 @@ diff (argc, argv) else diff_rev1 = optarg; break; -#ifdef CVS_DIFFDATE case 'D': if (diff_rev2 != NULL || diff_date2 != NULL) error (1, 0, @@ -139,7 +238,6 @@ diff (argc, argv) else diff_date1 = Make_Date (optarg); break; -#endif case 'N': empty_files = 1; break; @@ -201,15 +299,15 @@ diff (argc, argv) tag_check_valid (diff_rev2, argc, argv, local, 0, ""); which = W_LOCAL; - if (diff_rev2 != NULL || diff_date2 != NULL) + if (diff_rev1 != NULL || diff_date1 != NULL) which |= W_REPOS | W_ATTIC; wrap_setup (); /* start the recursion processor */ err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc, - diff_dirleaveproc, argc, argv, local, - which, 0, 1, (char *) NULL, 1, 0); + diff_dirleaveproc, NULL, argc, argv, local, + which, 0, 1, (char *) NULL, 1); /* clean up */ free (options); @@ -221,26 +319,21 @@ diff (argc, argv) */ /* ARGSUSED */ static int -diff_fileproc (finfo) +diff_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { int status, err = 2; /* 2 == trouble, like rcsdiff */ Vers_TS *vers; - enum { - DIFF_ERROR, - DIFF_ADDED, - DIFF_REMOVED, - DIFF_NEITHER - } empty_file = DIFF_NEITHER; - char tmp[L_tmpnam+1]; + enum diff_file empty_file = DIFF_DIFFERENT; + char *tmp; char *tocvsPath; char fname[PATH_MAX]; #ifdef SERVER_SUPPORT user_file_rev = 0; #endif - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 1, 0, finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0); if (diff_rev2 != NULL || diff_date2 != NULL) { @@ -249,10 +342,46 @@ diff_fileproc (finfo) } else if (vers->vn_user == NULL) { - error (0, 0, "I know nothing about %s", finfo->fullname); - freevers_ts (&vers); - diff_mark_errors (err); - return (err); + /* The file does not exist in the working directory. */ + if ((diff_rev1 != NULL || diff_date1 != NULL) + && vers->srcfile != NULL) + { + /* The file does exist in the repository. */ + if (empty_files) + empty_file = DIFF_REMOVED; + else + { + int exists; + + exists = 0; + /* special handling for TAG_HEAD */ + if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) + exists = vers->vn_rcs != NULL; + else + { + Vers_TS *xvers; + + xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, + 1, 0); + exists = xvers->vn_rcs != NULL; + freevers_ts (&xvers); + } + if (exists) + error (0, 0, + "%s no longer exists, no comparison available", + finfo->fullname); + freevers_ts (&vers); + diff_mark_errors (err); + return (err); + } + } + else + { + error (0, 0, "I know nothing about %s", finfo->fullname); + freevers_ts (&vers); + diff_mark_errors (err); + return (err); + } } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { @@ -311,14 +440,64 @@ diff_fileproc (finfo) } } - if (empty_file == DIFF_NEITHER && diff_file_nodiff (finfo->file, finfo->repository, finfo->entries, finfo->rcs, vers)) + empty_file = diff_file_nodiff (finfo, vers, empty_file); + if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR) { freevers_ts (&vers); - return (0); + if (empty_file == DIFF_SAME) + return (0); + else + { + diff_mark_errors (err); + return (err); + } } - /* FIXME: Check whether use_rev1 and use_rev2 are dead and deal - accordingly. */ + if (empty_file == DIFF_DIFFERENT) + { + int dead1, dead2; + + if (use_rev1 == NULL) + dead1 = 0; + else + dead1 = RCS_isdead (vers->srcfile, use_rev1); + if (use_rev2 == NULL) + dead2 = 0; + else + dead2 = RCS_isdead (vers->srcfile, use_rev2); + + if (dead1 && dead2) + { + freevers_ts (&vers); + return (0); + } + else if (dead1) + { + if (empty_files) + empty_file = DIFF_ADDED; + else + { + error (0, 0, "%s is a new entry, no comparison available", + finfo->fullname); + freevers_ts (&vers); + diff_mark_errors (err); + return (err); + } + } + else if (dead2) + { + if (empty_files) + empty_file = DIFF_REMOVED; + else + { + error (0, 0, "%s was removed, no comparison available", + finfo->fullname); + freevers_ts (&vers); + diff_mark_errors (err); + return (err); + } + } + } /* Output an "Index:" line for patch to use */ (void) fflush (stdout); @@ -348,23 +527,42 @@ diff_fileproc (finfo) if (empty_file == DIFF_ADDED) { - run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, finfo->file); + if (use_rev2 == NULL) + run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, finfo->file); + else + { + int retcode; + + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, + use_rev2, (char *) NULL, + (*options + ? options + : vers->options), + tmp); + if (retcode == -1) + { + (void) CVS_UNLINK (tmp); + error (1, errno, "fork failed during checkout of %s", + vers->srcfile->path); + } + /* FIXME: what if retcode > 0? */ + + run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, tmp); + } } else { int retcode; - /* - * FIXME: Should be setting use_rev1 using the logic in - * diff_file_nodiff, and using that revision. This code - * is broken for "cvs diff -N -r foo". - */ - retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_rcs, - *options ? options : vers->options, tmpnam (tmp), - 0, 0); + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, + use_rev1, (char *) NULL, + *options ? options : vers->options, + tmp); if (retcode == -1) { - (void) unlink (tmp); + (void) CVS_UNLINK (tmp); error (1, errno, "fork failed during checkout of %s", vers->srcfile->path); } @@ -414,8 +612,12 @@ diff_fileproc (finfo) error (1, errno, "cannot remove %s", finfo->file); } - if (empty_file == DIFF_REMOVED) - (void) unlink (tmp); + if (empty_file == DIFF_REMOVED + || (empty_file == DIFF_ADDED && use_rev2 != NULL)) + { + (void) CVS_UNLINK (tmp); + free (tmp); + } (void) fflush (stdout); freevers_ts (&vers); @@ -441,10 +643,12 @@ diff_mark_errors (err) */ /* ARGSUSED */ static Dtype -diff_dirproc (dir, pos_repos, update_dir) +diff_dirproc (callerdat, dir, pos_repos, update_dir, entries) + void *callerdat; char *dir; char *pos_repos; char *update_dir; + List *entries; { /* XXX - check for dirs we don't want to process??? */ @@ -462,10 +666,12 @@ diff_dirproc (dir, pos_repos, update_dir) */ /* ARGSUSED */ static int -diff_filesdoneproc (err, repos, update_dir) +diff_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { return (diff_errors); } @@ -475,27 +681,27 @@ diff_filesdoneproc (err, repos, update_dir) */ /* ARGSUSED */ static int -diff_dirleaveproc (dir, err, update_dir) +diff_dirleaveproc (callerdat, dir, err, update_dir, entries) + void *callerdat; char *dir; int err; char *update_dir; + List *entries; { return (diff_errors); } /* - * verify that a file is different 0=same 1=different + * verify that a file is different */ -static int -diff_file_nodiff (file, repository, entries, rcs, vers) - char *file; - char *repository; - List *entries; - RCSNode *rcs; +static enum diff_file +diff_file_nodiff (finfo, vers, empty_file) + struct file_info *finfo; Vers_TS *vers; + enum diff_file empty_file; { Vers_TS *xvers; - char tmp[L_tmpnam+1]; + char *tmp; int retcode; /* free up any old use_rev* variables and reset 'em */ @@ -512,23 +718,9 @@ diff_file_nodiff (file, repository, entries, rcs, vers) use_rev1 = xstrdup (vers->vn_rcs); else { - xvers = Version_TS (repository, (char *) NULL, diff_rev1, - diff_date1, file, 1, 0, entries, rcs); - if (xvers->vn_rcs == NULL) - { - /* 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); - - freevers_ts (&xvers); - return (1); - } - use_rev1 = xstrdup (xvers->vn_rcs); + xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); + if (xvers->vn_rcs != NULL) + use_rev1 = xstrdup (xvers->vn_rcs); freevers_ts (&xvers); } } @@ -539,36 +731,84 @@ diff_file_nodiff (file, repository, entries, rcs, vers) use_rev2 = xstrdup (vers->vn_rcs); else { - xvers = Version_TS (repository, (char *) NULL, diff_rev2, - diff_date2, file, 1, 0, entries, rcs); - if (xvers->vn_rcs == NULL) - { - /* 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_date2, file); - - freevers_ts (&xvers); - return (1); - } - use_rev2 = xstrdup (xvers->vn_rcs); + xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0); + if (xvers->vn_rcs != NULL) + use_rev2 = xstrdup (xvers->vn_rcs); freevers_ts (&xvers); } + if (use_rev1 == NULL) + { + /* The first revision does not exist. If EMPTY_FILES is + true, treat this as an added file. Otherwise, warn + about the missing tag. */ + if (use_rev2 == NULL) + return DIFF_SAME; + else if (empty_files) + return DIFF_ADDED; + else if (diff_rev1) + error (0, 0, "tag %s is not in file %s", diff_rev1, + finfo->fullname); + else + error (0, 0, "no revision for date %s in file %s", + diff_date1, finfo->fullname); + return DIFF_ERROR; + } + + if (use_rev2 == NULL) + { + /* The second revision does not exist. If EMPTY_FILES is + true, treat this as a removed file. Otherwise warn + about the missing tag. */ + if (empty_files) + return DIFF_REMOVED; + else if (diff_rev2) + error (0, 0, "tag %s is not in file %s", diff_rev2, + finfo->fullname); + else + error (0, 0, "no revision for date %s in file %s", + diff_date2, finfo->fullname); + return DIFF_ERROR; + } + /* now, see if we really need to do the diff */ - if (use_rev1 && use_rev2) { - return (strcmp (use_rev1, use_rev2) == 0); - } else { - error(0, 0, "No HEAD revision for file %s", file); - return (1); + if (strcmp (use_rev1, use_rev2) == 0) + return DIFF_SAME; + else + return DIFF_DIFFERENT; + } + + if ((diff_rev1 || diff_date1) && use_rev1 == NULL) + { + /* The first revision does not exist, and no second revision + was given. */ + if (empty_files) + { + if (empty_file == DIFF_REMOVED) + return DIFF_SAME; + else + { +#ifdef SERVER_SUPPORT + if (user_file_rev && use_rev2 == NULL) + use_rev2 = xstrdup (user_file_rev); +#endif + return DIFF_ADDED; + } + } + else + { + if (diff_rev1) + error (0, 0, "tag %s is not in file %s", diff_rev1, + finfo->fullname); + else + error (0, 0, "no revision for date %s in file %s", + diff_date1, finfo->fullname); + return DIFF_ERROR; } } + #ifdef SERVER_SUPPORT - if (user_file_rev) + if (user_file_rev) { /* drop user_file_rev into first unused use_rev */ if (!use_rev1) @@ -582,42 +822,56 @@ diff_file_nodiff (file, repository, entries, rcs, vers) /* now, see if we really need to do the diff */ if (use_rev1 && use_rev2) { - return (strcmp (use_rev1, use_rev2) == 0); + if (strcmp (use_rev1, use_rev2) == 0) + return DIFF_SAME; + else + return DIFF_DIFFERENT; } #endif /* SERVER_SUPPORT */ - if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0) + if (use_rev1 == NULL + || (vers->vn_user != NULL && strcmp (use_rev1, vers->vn_user) == 0)) { if (strcmp (vers->ts_rcs, vers->ts_user) == 0 && (!(*options) || strcmp (options, vers->options) == 0)) { - return (1); + return DIFF_SAME; } if (use_rev1 == NULL) use_rev1 = xstrdup (vers->vn_user); } + /* If we already know that the file is being added or removed, + then we don't want to do an actual file comparison here. */ + if (empty_file != DIFF_DIFFERENT) + return empty_file; + /* * with 0 or 1 -r option specified, run a quick diff to see if we * should bother with it at all. */ - retcode = RCS_checkout (vers->srcfile->path, NULL, use_rev1, - *options ? options : vers->options, tmpnam (tmp), 0, 0); + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, use_rev1, + (char *) NULL, + *options ? options : vers->options, + tmp); switch (retcode) { case 0: /* everything ok */ - if (xcmp (file, tmp) == 0) + if (xcmp (finfo->file, tmp) == 0) { - (void) unlink (tmp); - return (1); + (void) CVS_UNLINK (tmp); + free (tmp); + return DIFF_SAME; } break; case -1: /* fork failed */ - (void) unlink (tmp); + (void) CVS_UNLINK (tmp); error (1, errno, "fork failed during checkout of %s", vers->srcfile->path); default: break; } - (void) unlink (tmp); - return (0); + (void) CVS_UNLINK (tmp); + free (tmp); + return DIFF_DIFFERENT; } diff --git a/gnu/usr.bin/cvs/src/edit.c b/gnu/usr.bin/cvs/src/edit.c index 0473a03cf7d..0e4687c50a0 100644 --- a/gnu/usr.bin/cvs/src/edit.c +++ b/gnu/usr.bin/cvs/src/edit.c @@ -29,23 +29,26 @@ static int setting_tedit; static int setting_tunedit; static int setting_tcommit; -static int onoff_fileproc PROTO ((struct file_info *finfo)); +static int onoff_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -onoff_fileproc (finfo) +onoff_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { fileattr_set (finfo->file, "_watched", turning_on ? "" : NULL); return 0; } -static int onoff_filesdoneproc PROTO ((int, char *, char *)); +static int onoff_filesdoneproc PROTO ((void *, int, char *, char *, List *)); static int -onoff_filesdoneproc (err, repository, update_dir) +onoff_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { if (setting_default) fileattr_set (NULL, "_watched", turning_on ? "" : NULL); @@ -102,9 +105,9 @@ watch_onoff (argc, argv) lock_tree_for_write (argc, argv, local, 0); err = start_recursion (onoff_fileproc, onoff_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); lock_tree_cleanup (); return err; @@ -128,10 +131,11 @@ watch_off (argc, argv) return watch_onoff (argc, argv); } -static int dummy_fileproc PROTO ((struct file_info *finfo)); +static int dummy_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -dummy_fileproc (finfo) +dummy_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { /* This is a pretty hideous hack, but the gist of it is that recurse.c @@ -140,7 +144,7 @@ dummy_fileproc (finfo) return 0; } -static int ncheck_fileproc PROTO ((struct file_info *finfo)); +static int ncheck_fileproc PROTO ((void *callerdat, struct file_info *finfo)); /* Check for and process notifications. Local only. I think that doing this as a fileproc is the only way to catch all the @@ -149,7 +153,8 @@ static int ncheck_fileproc PROTO ((struct file_info *finfo)); processed the directory. */ static int -ncheck_fileproc (finfo) +ncheck_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { int notif_type; @@ -165,7 +170,7 @@ ncheck_fileproc (finfo) /* We send notifications even if noexec. I'm not sure which behavior is most sensible. */ - fp = fopen (CVSADM_NOTIFY, "r"); + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); if (fp == NULL) { if (!existence_error (errno)) @@ -212,7 +217,7 @@ ncheck_fileproc (finfo) if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSADM_NOTIFY); - if (unlink (CVSADM_NOTIFY) < 0) + if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) error (0, errno, "cannot remove %s", CVSADM_NOTIFY); return 0; @@ -243,9 +248,9 @@ send_notifications (argc, argv, local) } err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); send_to_server ("noop\012", 0); if (strcmp (command_name, "release") == 0) @@ -260,18 +265,19 @@ send_notifications (argc, argv, local) lock_tree_for_write (argc, argv, local, 0); err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); lock_tree_cleanup (); } return err; } -static int edit_fileproc PROTO ((struct file_info *finfo)); +static int edit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -edit_fileproc (finfo) +edit_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { FILE *fp; @@ -313,19 +319,10 @@ edit_fileproc (finfo) 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); - } + /* Could save a system call by only calling mkdir_if_needed if + trying to create the output file fails. But copy_file isn't + set up to facilitate that. */ + mkdir_if_needed (CVSADM_BASE); basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file)); strcpy (basefilename, CVSADM_BASE); strcat (basefilename, "/"); @@ -412,19 +409,20 @@ edit (argc, argv) /* No need to readlock since we aren't doing anything to the repository. */ err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); err += send_notifications (argc, argv, local); return err; } -static int unedit_fileproc PROTO ((struct file_info *finfo)); +static int unedit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -unedit_fileproc (finfo) +unedit_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { FILE *fp; @@ -513,9 +511,9 @@ unedit (argc, argv) /* No need to readlock since we aren't doing anything to the repository. */ err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 0, 0); + 0); err += send_notifications (argc, argv, local); @@ -768,16 +766,16 @@ notify_do (type, filename, who, val, watches, repository) size_t line_len = 0; args.notifyee = NULL; - usersname = xmalloc (strlen (CVSroot) + usersname = xmalloc (strlen (CVSroot_directory) + sizeof CVSROOTADM + sizeof CVSROOTADM_USERS + 20); - strcpy (usersname, CVSroot); + strcpy (usersname, CVSroot_directory); strcat (usersname, "/"); strcat (usersname, CVSROOTADM); strcat (usersname, "/"); strcat (usersname, CVSROOTADM_USERS); - fp = fopen (usersname, "r"); + fp = CVS_FOPEN (usersname, "r"); if (fp == NULL && !existence_error (errno)) error (0, errno, "cannot read %s", usersname); if (fp != NULL) @@ -871,7 +869,7 @@ notify_check (repository, update_dir) /* We send notifications even if noexec. I'm not sure which behavior is most sensible. */ - fp = fopen (CVSADM_NOTIFY, "r"); + fp = CVS_FOPEN (CVSADM_NOTIFY, "r"); if (fp == NULL) { if (!existence_error (errno)) @@ -915,10 +913,11 @@ static const char *const editors_usage[] = NULL }; -static int editors_fileproc PROTO ((struct file_info *finfo)); +static int editors_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -editors_fileproc (finfo) +editors_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *them; @@ -1014,7 +1013,7 @@ editors (argc, argv) #endif /* CLIENT_SUPPORT */ return start_recursion (editors_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 0, 0); + 0); } diff --git a/gnu/usr.bin/cvs/src/entries.c b/gnu/usr.bin/cvs/src/entries.c index 350f7f8fcf8..48e55979be4 100644 --- a/gnu/usr.bin/cvs/src/entries.c +++ b/gnu/usr.bin/cvs/src/entries.c @@ -16,9 +16,11 @@ static Node *AddEntryNode PROTO((List * list, Entnode *entnode)); -static Entnode *fgetentent PROTO((FILE *)); +static Entnode *fgetentent PROTO((FILE *, char *, int *)); static int fputentent PROTO((FILE *, Entnode *)); +static Entnode *subdir_record PROTO((int, const char *, const char *)); + static FILE *entfile; static char *entfilename; /* for error messages */ @@ -26,7 +28,8 @@ static char *entfilename; /* for error messages */ * Construct an Entnode */ Entnode * -Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) +Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict) + enum ent_type type; const char *user; const char *vn; const char *ts; @@ -39,6 +42,7 @@ Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) /* Note that timestamp and options must be non-NULL */ ent = (Entnode *) xmalloc (sizeof (Entnode)); + ent->type = type; ent->user = xstrdup (user); ent->version = xstrdup (vn); ent->timestamp = xstrdup (ts ? ts : ""); @@ -79,7 +83,14 @@ write_ent_proc (node, closure) Node *node; void *closure; { - if (fputentent(entfile, (Entnode *) node->data)) + Entnode *entnode; + + entnode = (Entnode *) node->data; + + if (closure != NULL && entnode->type != ENT_FILE) + *(int *) closure = 1; + + if (fputentent(entfile, entnode)) error (1, errno, "cannot write %s", entfilename); return (0); @@ -93,10 +104,26 @@ static void write_entries (list) List *list; { + int sawdir; + + sawdir = 0; + /* open the new one and walk the list writing entries */ entfilename = CVSADM_ENTBAK; entfile = open_file (entfilename, "w+"); - (void) walklist (list, write_ent_proc, NULL); + (void) walklist (list, write_ent_proc, (void *) &sawdir); + if (! sawdir) + { + struct stickydirtag *sdtp; + + /* We didn't write out any directories. Check the list + private data to see whether subdirectory information is + known. If it is, we need to write out an empty D line. */ + sdtp = (struct stickydirtag *) list->list->data; + if (sdtp == NULL || sdtp->subdirs) + if (fprintf (entfile, "D\n") < 0) + error (1, errno, "cannot write %s", entfilename); + } if (fclose (entfile) == EOF) error (1, errno, "error closing %s", entfilename); @@ -128,13 +155,26 @@ Scratch_Entry (list, fname) /* hashlookup to see if it is there */ if ((node = findnode_fn (list, fname)) != NULL) { + if (!noexec) + { + entfilename = CVSADM_ENTLOG; + entfile = open_file (entfilename, "a"); + + if (fprintf (entfile, "R ") < 0) + error (1, errno, "cannot write %s", entfilename); + + write_ent_proc (node, NULL); + + if (fclose (entfile) == EOF) + error (1, errno, "error closing %s", entfilename); + } + delnode (node); /* delete the node */ + #ifdef SERVER_SUPPORT if (server_active) server_scratch (fname); #endif - if (!noexec) - write_entries (list); /* re-write the file */ } } @@ -179,17 +219,22 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict) #endif } - entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict); + entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date, + ts_conflict); node = AddEntryNode (list, entnode); if (!noexec) { - entfile = open_file (CVSADM_ENTLOG, "a"); - + entfilename = CVSADM_ENTLOG; + entfile = open_file (entfilename, "a"); + + if (fprintf (entfile, "A ") < 0) + error (1, errno, "cannot write %s", entfilename); + write_ent_proc (node, NULL); if (fclose (entfile) == EOF) - error (1, errno, "error closing %s", CVSADM_ENTLOG); + error (1, errno, "error closing %s", entfilename); } } @@ -207,20 +252,21 @@ freesdt (p) free (sdtp->tag); if (sdtp->date) free (sdtp->date); - if (sdtp->options) - free (sdtp->options); free ((char *) sdtp); } static Entnode * -fgetentent(fpin) +fgetentent(fpin, cmd, sawdir) FILE *fpin; + char *cmd; + int *sawdir; { Entnode *ent; char *line; size_t line_chars_allocated; register char *cp; - char *user, *vn, *ts, *options; + enum ent_type type; + char *l, *user, *vn, *ts, *options; char *tag_or_date, *tag, *date, *ts_conflict; line = NULL; @@ -229,10 +275,39 @@ fgetentent(fpin) ent = NULL; while (getline (&line, &line_chars_allocated, fpin) > 0) { - if (line[0] != '/') + l = line; + + /* If CMD is not NULL, we are reading an Entries.Log file. + Each line in the Entries.Log file starts with a single + character command followed by a space. For backward + compatibility, the absence of a space indicates an add + command. */ + if (cmd != NULL) + { + if (l[1] != ' ') + *cmd = 'A'; + else + { + *cmd = l[0]; + l += 2; + } + } + + type = ENT_FILE; + + if (l[0] == 'D') + { + type = ENT_SUBDIR; + *sawdir = 1; + ++l; + /* An empty D line is permitted; it is a signal that this + Entries file lists all known subdirectories. */ + } + + if (l[0] != '/') continue; - user = line + 1; + user = l + 1; if ((cp = strchr (user, '/')) == NULL) continue; *cp++ = '\0'; @@ -258,7 +333,7 @@ fgetentent(fpin) tag = tag_or_date + 1; else if (*tag_or_date == 'D') date = tag_or_date + 1; - + if ((ts_conflict = strchr (ts, '+'))) *ts_conflict++ = '\0'; @@ -274,7 +349,7 @@ fgetentent(fpin) */ { struct stat sb; - if (strlen (ts) > 30 && stat (user, &sb) == 0) + if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0) { char *c = ctime (&sb.st_mtime); @@ -288,7 +363,8 @@ fgetentent(fpin) } } - ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict); + ent = Entnode_Create (type, user, vn, ts, options, tag, date, + ts_conflict); break; } @@ -301,6 +377,16 @@ fputentent(fp, p) FILE *fp; Entnode *p; { + switch (p->type) + { + case ENT_FILE: + break; + case ENT_SUBDIR: + if (fprintf (fp, "D") < 0) + return 1; + break; + } + if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0) return 1; if (p->conflict) @@ -339,10 +425,12 @@ Entries_Open (aflag) int aflag; { List *entries; + struct stickydirtag *sdtp = NULL; Entnode *ent; char *dirtag, *dirdate; int do_rewrite = 0; FILE *fpin; + int sawdir; /* get a fresh list... */ entries = getlist (); @@ -354,8 +442,6 @@ Entries_Open (aflag) ParseTag (&dirtag, &dirdate); if (aflag || dirtag || dirdate) { - struct stickydirtag *sdtp; - sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); memset ((char *) sdtp, 0, sizeof (*sdtp)); sdtp->aflag = aflag; @@ -367,12 +453,14 @@ Entries_Open (aflag) entries->list->delproc = freesdt; } - fpin = fopen (CVSADM_ENT, "r"); + sawdir = 0; + + fpin = CVS_FOPEN (CVSADM_ENT, "r"); if (fpin == NULL) error (0, errno, "cannot open %s for reading", CVSADM_ENT); else { - while ((ent = fgetentent (fpin)) != NULL) + while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) { (void) AddEntryNode (entries, ent); } @@ -380,17 +468,48 @@ Entries_Open (aflag) fclose (fpin); } - fpin = fopen (CVSADM_ENTLOG, "r"); + fpin = CVS_FOPEN (CVSADM_ENTLOG, "r"); if (fpin != NULL) { - while ((ent = fgetentent (fpin)) != NULL) + char cmd; + Node *node; + + while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL) { - (void) AddEntryNode (entries, ent); + switch (cmd) + { + case 'A': + (void) AddEntryNode (entries, ent); + break; + case 'R': + node = findnode_fn (entries, ent->user); + if (node != NULL) + delnode (node); + Entnode_Destroy (ent); + break; + default: + /* Ignore unrecognized commands. */ + break; + } } do_rewrite = 1; fclose (fpin); } + /* Update the list private data to indicate whether subdirectory + information is known. Nonexistent list private data is taken + to mean that it is known. */ + if (sdtp != NULL) + sdtp->subdirs = sawdir; + else if (! sawdir) + { + sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); + memset ((char *) sdtp, 0, sizeof (*sdtp)); + sdtp->subdirs = 0; + entries->list->data = (char *) sdtp; + entries->list->delproc = freesdt; + } + if (do_rewrite && !noexec) write_entries (entries); @@ -522,7 +641,7 @@ ParseTag (tagp, datep) *tagp = (char *) NULL; if (datep) *datep = (char *) NULL; - fp = fopen (CVSADM_TAG, "r"); + fp = CVS_FOPEN (CVSADM_TAG, "r"); if (fp) { char *line; @@ -546,3 +665,159 @@ ParseTag (tagp, datep) free (line); } } + +/* + * This is called if all subdirectory information is known, but there + * aren't any subdirectories. It records that fact in the list + * private data. + */ + +void +Subdirs_Known (entries) + List *entries; +{ + struct stickydirtag *sdtp; + + /* If there is no list private data, that means that the + subdirectory information is known. */ + sdtp = (struct stickydirtag *) entries->list->data; + if (sdtp != NULL && ! sdtp->subdirs) + { + FILE *fp; + + sdtp->subdirs = 1; + /* Create Entries.Log so that Entries_Close will do something. */ + fp = open_file (CVSADM_ENTLOG, "a"); + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTLOG); + } +} + +/* Record subdirectory information. */ + +static Entnode * +subdir_record (cmd, parent, dir) + int cmd; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + /* None of the information associated with a directory is + currently meaningful. */ + entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "", + (char *) NULL, (char *) NULL, + (char *) NULL); + + if (!noexec) + { + if (parent == NULL) + entfilename = CVSADM_ENTLOG; + else + { + entfilename = xmalloc (strlen (parent) + + sizeof CVSADM_ENTLOG + + 10); + sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG); + } + + entfile = CVS_FOPEN (entfilename, "a"); + if (entfile == NULL) + { + int save_errno = errno; + + /* It is not an error if there is no CVS administration + directory. Permitting this case simplifies some + calling code. */ + + if (parent == NULL) + { + if (! isdir (CVSADM)) + return entnode; + } + else + { + sprintf (entfilename, "%s/%s", parent, CVSADM); + if (! isdir (entfilename)) + { + free (entfilename); + entfilename = NULL; + return entnode; + } + } + + error (1, save_errno, "cannot open %s", entfilename); + } + + if (fprintf (entfile, "%c ", cmd) < 0) + error (1, errno, "cannot write %s", entfilename); + + if (fputentent (entfile, entnode) != 0) + error (1, errno, "cannot write %s", entfilename); + + if (fclose (entfile) == EOF) + error (1, errno, "error closing %s", entfilename); + + if (parent != NULL) + { + free (entfilename); + entfilename = NULL; + } + } + + return entnode; +} + +/* + * Record the addition of a new subdirectory DIR in PARENT. PARENT + * may be NULL, which means the current directory. ENTRIES is the + * current entries list; it may be NULL, which means that it need not + * be updated. + */ + +void +Subdir_Register (entries, parent, dir) + List *entries; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + /* Ignore attempts to register ".". These can happen in the + server code. */ + if (dir[0] == '.' && dir[1] == '\0') + return; + + entnode = subdir_record ('A', parent, dir); + + if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) + (void) AddEntryNode (entries, entnode); + else + Entnode_Destroy (entnode); +} + +/* + * Record the removal of a subdirectory. The arguments are the same + * as for Subdir_Register. + */ + +void +Subdir_Deregister (entries, parent, dir) + List *entries; + const char *parent; + const char *dir; +{ + Entnode *entnode; + + entnode = subdir_record ('R', parent, dir); + Entnode_Destroy (entnode); + + if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) + { + Node *p; + + p = findnode_fn (entries, dir); + if (p != NULL) + delnode (p); + } +} diff --git a/gnu/usr.bin/cvs/src/error.c b/gnu/usr.bin/cvs/src/error.c index 8a10cc7c0db..48452d71fdd 100644 --- a/gnu/usr.bin/cvs/src/error.c +++ b/gnu/usr.bin/cvs/src/error.c @@ -61,7 +61,9 @@ void exit (); #endif /* __STDC__ */ #endif /* STDC_HEADERS */ +#ifndef strerror extern char *strerror (); +#endif extern int vasprintf (); @@ -95,19 +97,9 @@ error (status, errnum, message, va_alist) va_dcl #endif { - FILE *out = stderr; -#ifdef HAVE_VPRINTF - va_list args; -#endif - - if (error_use_protocol) - { - out = stdout; - printf ("E "); - } - #ifdef HAVE_VPRINTF { + va_list args; char *mess = NULL; char *entire; size_t len; @@ -157,10 +149,7 @@ error (status, errnum, message, va_alist) free (mess); } } - if (error_use_protocol) - fputs (entire ? entire : "out of memory", out); - else - cvs_outerr (entire ? entire : "out of memory", 0); + cvs_outerr (entire ? entire : "out of memory", 0); if (entire != NULL) free (entire); } @@ -169,38 +158,49 @@ error (status, errnum, message, va_alist) /* I think that all relevant systems have vprintf these days. But just in case, I'm leaving this code here. */ - if (command_name && *command_name) { - if (status) - fprintf (out, "%s [%s aborted]: ", program_name, command_name); + FILE *out = stderr; + + 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 %s: ", program_name, command_name); - } - else - fprintf (out, "%s: ", program_name); + fprintf (out, "%s: ", program_name); #ifdef HAVE_VPRINTF - VA_START (args, message); - vfprintf (out, message, args); - va_end (args); + VA_START (args, message); + vfprintf (out, message, args); + va_end (args); #else #ifdef HAVE_DOPRNT - _doprnt (message, &args, out); + _doprnt (message, &args, out); #else - fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8); + fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif #endif - if (errnum) - fprintf (out, ": %s", strerror (errnum)); - putc ('\n', out); + if (errnum) + fprintf (out, ": %s", strerror (errnum)); + putc ('\n', out); -#endif /* No HAVE_VPRINTF */ + /* In the error_use_protocol case, this probably does + something useful. In most other cases, I suspect it is a + noop (either stderr is line buffered or we haven't written + anything to stderr) or unnecessary (if stderr is not line + buffered, maybe there is a reason....). */ + fflush (out); + } - /* In the error_use_protocol case, this probably does something useful. - In most other cases, I suspect it is a noop (either stderr is line - buffered or we haven't written anything to stderr) or unnecessary - (if stderr is not line buffered, maybe there is a reason....). */ - fflush (out); +#endif /* No HAVE_VPRINTF */ if (status) { @@ -216,7 +216,7 @@ error (status, errnum, message, va_alist) Exit with status EXIT_FAILURE if STATUS is nonzero. */ /* VARARGS */ void -#if defined (HAVE_VPRINTF) && __STDC__ +#if defined (HAVE_VPRINTF) && ((__STDC__ - 0) > 0) fperror (FILE *fp, int status, int errnum, char *message, ...) #else fperror (fp, status, errnum, message, va_alist) diff --git a/gnu/usr.bin/cvs/src/expand_path.c b/gnu/usr.bin/cvs/src/expand_path.c index 98980510e44..ab093512955 100644 --- a/gnu/usr.bin/cvs/src/expand_path.c +++ b/gnu/usr.bin/cvs/src/expand_path.c @@ -81,10 +81,12 @@ variable_set (nameval) } /* This routine will expand the pathname to account for ~ and $ - characters as described above. If an error occurs, an error - message is printed via error() and NULL is returned. FILE and - LINE are the filename and linenumber to include in the error - message. */ + characters as described above. Returns a pointer to a newly + malloc'd string. If an error occurs, an error message is printed + via error() and NULL is returned. FILE and LINE are the filename + and linenumber to include in the error message. FILE must point + to something; LINE can be zero to indicate the line number is not + known. */ char * expand_path (name, file, line) char *name; @@ -181,7 +183,7 @@ expand_variable (name, file, line) int line; { if (strcmp (name, CVSROOT_ENV) == 0) - return CVSroot; + return CVSroot_original; else if (strcmp (name, RCSBIN_ENV) == 0) return Rcsbin; else if (strcmp (name, EDITOR1_ENV) == 0) @@ -231,10 +233,10 @@ expand_variable (name, file, line) that various crazy syntaxes might be invented for inserting information about revisions, branches, etc. */ if (line != 0) - error (0, 0, "%s:%d: unrecognized varaible syntax %s", + error (0, 0, "%s:%d: unrecognized variable syntax %s", file, line, name); else - error (0, 0, "%s: unrecognized varaible syntax %s", + error (0, 0, "%s: unrecognized variable syntax %s", file, name); return NULL; } diff --git a/gnu/usr.bin/cvs/src/fileattr.c b/gnu/usr.bin/cvs/src/fileattr.c index 827c69ca990..dd105c9441b 100644 --- a/gnu/usr.bin/cvs/src/fileattr.c +++ b/gnu/usr.bin/cvs/src/fileattr.c @@ -84,7 +84,7 @@ fileattr_read () strcat (fname, CVSREP_FILEATTR); attr_read_attempted = 1; - fp = fopen (fname, FOPEN_BINARY_READ); + fp = CVS_FOPEN (fname, FOPEN_BINARY_READ); if (fp == NULL) { if (!existence_error (errno)) @@ -137,8 +137,8 @@ fileattr_read () char * fileattr_get (filename, attrname) - char *filename; - char *attrname; + const char *filename; + const char *attrname; { Node *node; size_t attrname_len = strlen (attrname); @@ -180,8 +180,8 @@ fileattr_get (filename, attrname) char * fileattr_get0 (filename, attrname) - char *filename; - char *attrname; + const char *filename; + const char *attrname; { char *cp; char *cpend; @@ -202,8 +202,8 @@ fileattr_get0 (filename, attrname) char * fileattr_modify (list, attrname, attrval, namevalsep, entsep) char *list; - char *attrname; - char *attrval; + const char *attrname; + const char *attrval; int namevalsep; int entsep; { @@ -298,9 +298,9 @@ fileattr_modify (list, attrname, attrval, namevalsep, entsep) void fileattr_set (filename, attrname, attrval) - char *filename; - char *attrname; - char *attrval; + const char *filename; + const char *attrname; + const char *attrval; { Node *node; char *p; @@ -346,17 +346,18 @@ fileattr_set (filename, attrname, attrval) } p = fileattr_modify (node->data, attrname, attrval, '=', ';'); - free (node->data); - node->data = NULL; if (p == NULL) delnode (node); else + { + free (node->data); node->data = p; + } } void fileattr_newfile (filename) - char *filename; + const char *filename; { Node *node; @@ -440,7 +441,7 @@ fileattr_write () strcpy (fname, fileattr_stored_repos); strcat (fname, "/"); strcat (fname, CVSREP); - if (rmdir (fname) < 0) + if (CVS_RMDIR (fname) < 0) { if (errno != ENOTEMPTY @@ -456,7 +457,7 @@ fileattr_write () } omask = umask (cvsumask); - fp = fopen (fname, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); if (fp == NULL) { if (existence_error (errno)) @@ -481,7 +482,7 @@ fileattr_write () } free (repname); - fp = fopen (fname, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE); } if (fp == NULL) { diff --git a/gnu/usr.bin/cvs/src/fileattr.h b/gnu/usr.bin/cvs/src/fileattr.h index c24c0359088..27cc30df285 100644 --- a/gnu/usr.bin/cvs/src/fileattr.h +++ b/gnu/usr.bin/cvs/src/fileattr.h @@ -66,11 +66,12 @@ extern void fileattr_startdir PROTO ((char *repos)); by '\0' or ';'. Return NULL if said file lacks said attribute. If FILENAME is NULL, return default attributes (attributes for files created in the future). */ -extern char *fileattr_get PROTO ((char *filename, char *attrname)); +extern char *fileattr_get PROTO ((const char *filename, const 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)); +extern char *fileattr_get0 PROTO ((const char *filename, + const char *attrname)); /* This is just a string manipulation function; it does not manipulate file attributes as such. @@ -100,20 +101,20 @@ extern char *fileattr_get0 PROTO ((char *filename, char *attrname)); => "abc=v1;def=val;ghi=v3" */ -extern char *fileattr_modify PROTO ((char *list, char *attrname, - char *attrval, int namevalsep, +extern char *fileattr_modify PROTO ((char *list, const char *attrname, + const 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)); +extern void fileattr_set PROTO ((const char *filename, const char *attrname, + const 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)); +extern void fileattr_newfile PROTO ((const char *filename)); /* Write out all modified attributes. */ extern void fileattr_write PROTO ((void)); diff --git a/gnu/usr.bin/cvs/src/filesubr.c b/gnu/usr.bin/cvs/src/filesubr.c index 086da8391ae..3c7bf3b584f 100644 --- a/gnu/usr.bin/cvs/src/filesubr.c +++ b/gnu/usr.bin/cvs/src/filesubr.c @@ -290,6 +290,22 @@ make_directories (name) (void) mkdir (name, 0777); } +/* Create directory NAME if it does not already exist; fatal error for + other errors. Returns 0 if directory was created; 1 if it already + existed. */ +int +mkdir_if_needed (name) + char *name; +{ + if (mkdir (name, 0777) < 0) + { + if (errno != EEXIST) + error (1, errno, "cannot make directory %s", name); + return 1; + } + return 0; +} + /* * Change the mode of a file, either adding write permissions, or removing * all write permissions. Either change honors the current umask setting. @@ -596,24 +612,42 @@ xcmp (file1, file2) (void) close (fd2); return (ret); } + +/* Just in case this implementation does not define this. */ +#ifndef L_tmpnam +#define L_tmpnam 50 +#endif #ifdef LOSING_TMPNAM_FUNCTION -char *tmpnam(char *s) +char * +cvs_temp_name () { - static char value[L_tmpnam+1]; - - if (s){ - strcpy(s,"/tmp/cvsXXXXXX"); - mktemp(s); - return s; - }else{ - strcpy(value,"/tmp/cvsXXXXXX"); - mktemp(s); - return value; - } + char value[L_tmpnam + 1]; + + /* FIXME: Should be using TMPDIR. */ + strcpy (value, "/tmp/cvsXXXXXX"); + mktemp (value); + return xstrdup (value); +} +#else +/* Generate a unique temporary filename. Returns a pointer to a newly + malloc'd string containing the name. Returns successfully or not at + all. */ +char * +cvs_temp_name () +{ + char value[L_tmpnam + 1]; + char *retval; + + /* FIXME: should be using TMPDIR, perhaps by using tempnam on systems + which have it. */ + retval = tmpnam (value); + if (retval == NULL) + error (1, errno, "cannot generate temporary filename"); + return xstrdup (retval); } #endif - + /* Return non-zero iff FILENAME is absolute. Trivial under Unix, but more complicated under other systems. */ int @@ -642,7 +676,19 @@ last_component (path) char * get_homedir () { - return getenv ("HOME"); + static char home[PATH_MAX]; + char *env = getenv ("HOME"); + struct passwd *pw; + + if (env) + strcpy (home, env); + else if ((pw = (struct passwd *) getpwuid (getuid ())) + && pw->pw_dir) + strcpy (home, pw->pw_dir); + else + return 0; + + return home; } /* See cvs.h for description. On unix this does nothing, because the diff --git a/gnu/usr.bin/cvs/src/find_names.c b/gnu/usr.bin/cvs/src/find_names.c index 48854377a75..9143a6cd08d 100644 --- a/gnu/usr.bin/cvs/src/find_names.c +++ b/gnu/usr.bin/cvs/src/find_names.c @@ -18,8 +18,11 @@ #include "cvs.h" -static int find_dirs PROTO((char *dir, List * list, int checkadm)); +static int find_dirs PROTO((char *dir, List * list, int checkadm, + List *entries)); static int find_rcs PROTO((char *dir, List * list)); +static int add_subdir_proc PROTO((Node *, void *)); +static int register_subdir_proc PROTO((Node *, void *)); static List *filelist; @@ -32,8 +35,13 @@ add_entries_proc (node, closure) Node *node; void *closure; { + Entnode *entnode; Node *fnode; + entnode = (Entnode *) node->data; + if (entnode->type != ENT_FILE) + return (0); + fnode = getnode (); fnode->type = FILES; fnode->key = xstrdup (node->key); @@ -106,12 +114,55 @@ Find_Names (repository, which, aflag, optentries) } /* + * Add an entry from the subdirs list to the directories list. This + * is called via walklist. + */ + +static int +add_subdir_proc (p, closure) + Node *p; + void *closure; +{ + List *dirlist = (List *) closure; + Entnode *entnode; + Node *dnode; + + entnode = (Entnode *) p->data; + if (entnode->type != ENT_SUBDIR) + return 0; + + dnode = getnode (); + dnode->type = DIRS; + dnode->key = xstrdup (entnode->user); + if (addnode (dirlist, dnode) != 0) + freenode (dnode); + return 0; +} + +/* + * Register a subdirectory. This is called via walklist. + */ + +/*ARGSUSED*/ +static int +register_subdir_proc (p, closure) + Node *p; + void *closure; +{ + List *entries = (List *) closure; + + Subdir_Register (entries, (char *) NULL, p->key); + return 0; +} + +/* * create a list of directories to traverse from the current directory */ List * -Find_Directories (repository, which) +Find_Directories (repository, which, entries) char *repository; int which; + List *entries; { List *dirlist; @@ -121,16 +172,53 @@ Find_Directories (repository, which) /* find the local ones */ if (which & W_LOCAL) { - /* look only for CVS controlled sub-directories */ - if (find_dirs (".", dirlist, 1) != 0) - error (1, errno, "cannot open current directory"); + List *tmpentries; + struct stickydirtag *sdtp; + + /* Look through the Entries file. */ + + if (entries != NULL) + tmpentries = entries; + else if (isfile (CVSADM_ENT)) + tmpentries = Entries_Open (0); + else + tmpentries = NULL; + + if (tmpentries != NULL) + sdtp = (struct stickydirtag *) tmpentries->list->data; + + /* If we do have an entries list, then if sdtp is NULL, or if + sdtp->subdirs is nonzero, all subdirectory information is + recorded in the entries list. */ + if (tmpentries != NULL && (sdtp == NULL || sdtp->subdirs)) + walklist (tmpentries, add_subdir_proc, (void *) dirlist); + else + { + /* This is an old working directory, in which subdirectory + information is not recorded in the Entries file. Find + the subdirectories the hard way, and, if possible, add + it to the Entries file for next time. */ + if (find_dirs (".", dirlist, 1, tmpentries) != 0) + error (1, errno, "cannot open current directory"); + if (tmpentries != NULL) + { + if (! list_isempty (dirlist)) + walklist (dirlist, register_subdir_proc, + (void *) tmpentries); + else + Subdirs_Known (tmpentries); + } + } + + if (entries == NULL && tmpentries != NULL) + Entries_Close (tmpentries); } /* look for sub-dirs in the repository */ if ((which & W_REPOS) && repository) { /* search the repository */ - if (find_dirs (repository, dirlist, 0) != 0) + if (find_dirs (repository, dirlist, 0, entries) != 0) error (1, errno, "cannot open directory %s", repository); #ifdef ATTIC_DIR_SUPPORT /* XXX - FIXME */ @@ -140,7 +228,7 @@ Find_Directories (repository, which) char dir[PATH_MAX]; (void) sprintf (dir, "%s/%s", repository, CVSATTIC); - (void) find_dirs (dir, dirlist, 0); + (void) find_dirs (dir, dirlist, 0, entries); } #endif } @@ -165,7 +253,7 @@ find_rcs (dir, list) DIR *dirp; /* set up to read the dir */ - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) return (1); /* read the dir, grabbing the ,v files */ @@ -189,15 +277,18 @@ find_rcs (dir, list) } /* - * Finds all the subdirectories of the argument dir and adds them to the - * specified list. Sub-directories without a CVS administration directory - * are optionally ignored Returns 0 for success or 1 on error. + * Finds all the subdirectories of the argument dir and adds them to + * the specified list. Sub-directories without a CVS administration + * directory are optionally ignored. If ENTRIES is not NULL, all + * files on the list are ignored. Returns 0 for success or 1 on + * error. */ static int -find_dirs (dir, list, checkadm) +find_dirs (dir, list, checkadm, entries) char *dir; List *list; int checkadm; + List *entries; { Node *p; char tmp[PATH_MAX]; @@ -205,7 +296,7 @@ find_dirs (dir, list, checkadm) DIR *dirp; /* set up to read the dir */ - if ((dirp = opendir (dir)) == NULL) + if ((dirp = CVS_OPENDIR (dir)) == NULL) return (1); /* read the dir, grabbing sub-dirs */ @@ -218,6 +309,12 @@ find_dirs (dir, list, checkadm) strcmp (dp->d_name, CVSREP) == 0) continue; + /* findnode() is going to be significantly faster than stat() + because it involves no system calls. That is why we bother + with the entries argument, and why we check this first. */ + if (entries != NULL && findnode (entries, dp->d_name) != NULL) + continue; + #ifdef DT_DIR if (dp->d_type != DT_DIR) { diff --git a/gnu/usr.bin/cvs/src/hash.c b/gnu/usr.bin/cvs/src/hash.c index 2197db07f8a..229b588bf3d 100644 --- a/gnu/usr.bin/cvs/src/hash.c +++ b/gnu/usr.bin/cvs/src/hash.c @@ -400,6 +400,7 @@ nodetypestring (type) case NDBMNODE: return("NDBMNODE"); case FILEATTR: return("FILEATTR"); case VARIABLE: return("VARIABLE"); + case RCSFIELD: return("RCSFIELD"); } return("<trash>"); diff --git a/gnu/usr.bin/cvs/src/hash.h b/gnu/usr.bin/cvs/src/hash.h index dd83665c4c8..92b25493862 100644 --- a/gnu/usr.bin/cvs/src/hash.h +++ b/gnu/usr.bin/cvs/src/hash.h @@ -20,7 +20,7 @@ enum ntype { UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE, RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE, FILEATTR, - VARIABLE + VARIABLE, RCSFIELD }; typedef enum ntype Ntype; diff --git a/gnu/usr.bin/cvs/src/history.c b/gnu/usr.bin/cvs/src/history.c index 81c71ffbea5..ef153fc49dc 100644 --- a/gnu/usr.bin/cvs/src/history.c +++ b/gnu/usr.bin/cvs/src/history.c @@ -424,7 +424,7 @@ history (argc, argv) if (strlen (optarg) >= sizeof (backto)) { error (0, 0, "backto truncated to %d bytes", - sizeof (backto) - 1); + (int) sizeof (backto) - 1); optarg[sizeof (backto) - 1] = '\0'; } (void) strcpy (backto, optarg); @@ -564,9 +564,9 @@ history (argc, argv) send_arg("-m"); for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) option_with_arg ("-n", *mod); - if (since_rev != NULL) + if (*since_rev) option_with_arg ("-r", since_rev); - if (since_tag != NULL) + if (*since_tag) option_with_arg ("-t", since_tag); for (mod = user_list; mod < &user_list[user_count]; ++mod) option_with_arg ("-u", *mod); @@ -649,7 +649,7 @@ history (argc, argv) if (histfile) (void) strcpy (fname, histfile); else - (void) sprintf (fname, "%s/%s/%s", CVSroot, + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSROOTADM_HISTORY); read_hrecs (fname); @@ -678,7 +678,8 @@ history_write (type, update_dir, revs, name, repository) if (logoff) /* History is turned off by cmd line switch */ return; - (void) sprintf (fname, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_HISTORY); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_HISTORY); /* turn off history logging if the history file does not exist */ if (!isfile (fname)) @@ -696,7 +697,7 @@ history_write (type, update_dir, revs, name, repository) #endif if (noexec) return; - fd = open (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); + fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); if (fd < 0) error (1, errno, "cannot open history file: %s", fname); @@ -722,11 +723,11 @@ history_write (type, update_dir, revs, name, repository) /* Try harder to find a "homedir" */ if (!getwd (workdir)) error (1, errno, "can't getwd in history"); - if (chdir (pwdir) < 0) + if ( CVS_CHDIR (pwdir) < 0) error (1, errno, "can't chdir(%s)", pwdir); if (!getwd (homedir)) error (1, errno, "can't getwd in %s", pwdir); - (void) chdir (workdir); + (void) CVS_CHDIR (workdir); i = strlen (homedir); if (!strncmp (CurDir, homedir, i)) @@ -994,7 +995,7 @@ read_hrecs (fname) struct hrec *hr; struct stat st_buf; - if ((fd = open (fname, O_RDONLY | OPEN_BINARY)) < 0) + if ((fd = CVS_OPEN (fname, O_RDONLY | OPEN_BINARY)) < 0) error (1, errno, "cannot open history file: %s", fname); if (fstat (fd, &st_buf) < 0) @@ -1134,9 +1135,19 @@ select_hrec (hr) { Vers_TS *vers; time_t t; - - vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL, - hr->file, 1, 0, (List *) NULL, (RCSNode *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = hr->file; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = hr->repos; + finfo.entries = NULL; + finfo.rcs = NULL; + + vers = Version_TS (&finfo, (char *) NULL, since_rev, (char *) NULL, + 1, 0); if (vers->vn_rcs) { if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0)) @@ -1399,7 +1410,8 @@ report_hrecs () if (lr->rev && *(lr->rev)) (void) printf (" [%s]", lr->rev); (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod, - mod_len + 1 - strlen (lr->mod), "=", workdir); + mod_len + 1 - (int) strlen (lr->mod), + "=", workdir); break; case 'W': case 'U': diff --git a/gnu/usr.bin/cvs/src/import.c b/gnu/usr.bin/cvs/src/import.c index 98c163555a6..6c9d0ae6434 100644 --- a/gnu/usr.bin/cvs/src/import.c +++ b/gnu/usr.bin/cvs/src/import.c @@ -25,8 +25,9 @@ static char *get_comment PROTO((char *user)); static int add_rcs_file PROTO((char *message, char *rcs, char *user, char *vtag, int targc, char *targv[])); static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp)); -static int add_rev PROTO((char *message, char *rcs, char *vfile, char *vers)); -static int add_tags PROTO((char *rcs, char *vfile, char *vtag, int targc, +static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile, + char *vers)); +static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc, char *targv[])); static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[])); static int import_descend_dir PROTO((char *message, char *dir, char *vtag, @@ -65,11 +66,12 @@ import (argc, argv) char **argv; { char *message = NULL; - char tmpfile[L_tmpnam+1]; + char *tmpfile; char *cp; int i, c, msglen, err; List *ulist; Node *p; + struct logfile_info *li; if (argc == -1) usage (import_usage); @@ -115,7 +117,7 @@ import (argc, argv) /* RCS_check_kflag returns strings of the form -kxx. We only use it for validation, so we can free the value as soon as it is returned. */ - free (RCS_check_kflag(optarg)); + free (RCS_check_kflag (optarg)); keyword_opt = optarg; break; case 'W': @@ -138,14 +140,14 @@ import (argc, argv) /* XXX - this should be a module, not just a pathname */ if (! isabsolute (argv[0])) { - if (CVSroot == NULL) + if (CVSroot_directory == NULL) { error (0, 0, "missing CVSROOT environment variable\n"); error (1, 0, "Set it or specify the '-d' option to %s.", program_name); } - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); - repos_len = strlen (CVSroot); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + repos_len = strlen (CVSroot_directory); } else { @@ -171,9 +173,8 @@ import (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) { - /* Do this now; don't ask for a log message if we can't talk to the - server. But if there is a syntax error in the options, give - an error message without connecting. */ + /* For rationale behind calling start_server before do_editor, see + commit.c */ start_server (); } #endif @@ -181,7 +182,7 @@ import (argc, argv) if (use_editor) { do_editor ((char *) NULL, &message, repository, - (List *) NULL); + (List *) NULL); } msglen = message == NULL ? 0 : strlen (message); @@ -219,6 +220,7 @@ import (argc, argv) send_arg ("-I"); send_arg ("!"); } + wrap_send (); { int i; @@ -244,9 +246,10 @@ import (argc, argv) make_directories (repository); /* Create the logfile that will be logged upon completion */ - if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL) + tmpfile = cvs_temp_name (); + if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); - (void) unlink (tmpfile); /* to be sure it goes away */ + (void) CVS_UNLINK (tmpfile); /* to be sure it goes away */ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); (void) fprintf (logfp, "Release Tags:\t"); for (i = 2; i < argc; i++) @@ -298,15 +301,19 @@ import (argc, argv) p->type = UPDATE; p->delproc = update_delproc; p->key = xstrdup ("- Imported sources"); - p->data = (char *) T_TITLE; + li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + li->type = T_TITLE; + li->tag = xstrdup (vbranch); + p->data = (char *) li; (void) addnode (ulist, p); - Update_Logfile (repository, message, vbranch, logfp, ulist); + Update_Logfile (repository, message, logfp, ulist); dellist (&ulist); (void) fclose (logfp); /* Make sure the temporary file goes away, even on systems that don't let you delete a file that's in use. */ - unlink (tmpfile); + CVS_UNLINK (tmpfile); + free (tmpfile); if (message) free (message); @@ -333,7 +340,7 @@ import_descend (message, vtag, targc, targv) ign_add_file (CVSDOTIGNORE, 1); wrap_add_file (CVSDOTWRAPPER, 1); - if ((dirp = opendir (".")) == NULL) + if ((dirp = CVS_OPENDIR (".")) == NULL) { err++; } @@ -365,7 +372,7 @@ import_descend (message, vtag, targc, targv) #endif && !wrap_name_has (dp->d_name, WRAP_TOCVS) ) - { + { Node *n; if (dirlist == NULL) @@ -377,7 +384,7 @@ import_descend (message, vtag, targc, targv) } else if ( #ifdef DT_DIR - dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && + dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && #endif islink (dp->d_name)) { @@ -400,7 +407,7 @@ import_descend (message, vtag, targc, targv) (void) closedir (dirp); } - if (dirlist != NULL) + if (dirlist != NULL) { Node *head, *p; @@ -471,11 +478,19 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) Vers_TS *vers; int letter; int ierrno; - char *tmpdir; char *tocvsPath; - - vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile, - 1, 0, (List *) NULL, (RCSNode *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL, + 1, 0); if (vers->vn_rcs != NULL && !RCS_isdead(vers->srcfile, vers->vn_rcs)) { @@ -483,11 +498,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) int different; int retcode = 0; - tmpdir = getenv ("TMPDIR"); - if (tmpdir == NULL || tmpdir[0] == '\0') - tmpdir = "/tmp"; - - (void) sprintf (xtmpfile, "%s/cvs-imp%ld", tmpdir, (long) getpid()); + (void) sprintf (xtmpfile, "%s/cvs-imp%ld", Tmpdir, (long) getpid()); /* * The rcs file does have a revision on the vendor branch. Compare @@ -498,15 +509,14 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * This is to try to cut down the number of "C" conflict messages for * locally modified import source files. */ - /* Why is RCS_FLAGS_FORCE here? I wouldn't think that it would have any - effect in conjunction with passing NULL for workfile (i.e. to stdout). */ - retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_rcs, + retcode = RCS_checkout (vers->srcfile, (char *) NULL, vers->vn_rcs, + (char *) NULL, #ifdef HAVE_RCS5 - "-ko", + "-ko", #else - NULL, + NULL, #endif - xtmpfile, RCS_FLAGS_FORCE, 0); + xtmpfile); if (retcode != 0) { ierrno = errno; @@ -536,7 +546,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * "U", signifying that the file has changed, but needs no * attention, and we're done. */ - if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv)) + if (add_tags (vers->srcfile, vfile, vtag, targc, targv)) retval = 1; add_log ('U', vfile); freevers_ts (&vers); @@ -546,8 +556,8 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) /* We may have failed to parse the RCS file; check just in case */ if (vers->srcfile == NULL || - add_rev (message, vers->srcfile->path, vfile, vers->vn_rcs) || - add_tags (vers->srcfile->path, vfile, vtag, targc, targv)) + add_rev (message, vers->srcfile, vfile, vers->vn_rcs) || + add_tags (vers->srcfile, vfile, vtag, targc, targv)) { freevers_ts (&vers); return (1); @@ -573,7 +583,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) static int add_rev (message, rcs, vfile, vers) char *message; - char *rcs; + RCSNode *rcs; char *vfile; char *vers; { @@ -619,11 +629,10 @@ add_rev (message, rcs, vfile, vers) } } - status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath, + status = RCS_checkin (rcs->path, tocvsPath == NULL ? vfile : tocvsPath, message, vbranch, (RCS_FLAGS_QUIET - | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)), - 0); + | (use_file_modtime ? RCS_FLAGS_MODTIME : 0))); ierrno = errno; if (tocvsPath == NULL) @@ -636,8 +645,10 @@ add_rev (message, rcs, vfile, vers) { if (!noexec) { - fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs); - error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs); + fperror (logfp, 0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); + error (0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); } if (locked) { @@ -656,7 +667,7 @@ add_rev (message, rcs, vfile, vers) */ static int add_tags (rcs, vfile, vtag, targc, targv) - char *rcs; + RCSNode *rcs; char *vfile; char *vtag; int targc; @@ -665,6 +676,7 @@ add_tags (rcs, vfile, vtag, targc, targv) int i, ierrno; Vers_TS *vers; int retcode = 0; + struct file_info finfo; if (noexec) return (0); @@ -672,23 +684,33 @@ add_tags (rcs, vfile, vtag, targc, targv) if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0) { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); error (0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); return (1); } - vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile, - 1, 0, (List *) NULL, (RCSNode *) NULL); + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0); for (i = 0; i < targc; i++) { if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0) { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); error (0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); } } freevers_ts (&vers); @@ -712,6 +734,10 @@ static const struct compair comtable[] = * table is used to guess the proper comment leader from the working file's * suffix during initial ci (see InitAdmin()). Comment leaders are needed for * languages without multiline comments; for others they are optional. + * + * I believe that the comment leader is unused if you are using RCS 5.7, which + * decides what leader to use based on the text surrounding the $Log keyword + * rather than a specified comment leader. */ {"a", "-- "}, /* Ada */ {"ada", "-- "}, @@ -857,17 +883,23 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) mode_t mode; char *tocvsPath; char *userfile; + char *local_opt = keyword_opt; + char *free_opt = NULL; if (noexec) return (0); - /* FIXME? We always import files as text files (note that means - that files get stored with straight linefeeds). There isn't an - obvious, clean, way to let people specify which files are binary. - Maybe based on the file name.... */ + if (local_opt == NULL) + { + if (wrap_name_has (user, WRAP_RCSOPTION)) + { + local_opt = free_opt = wrap_rcsoption (user, 0); + } + } + tocvsPath = wrap_tocvs_process_file (user); userfile = (tocvsPath == NULL ? user : tocvsPath); - fpuser = fopen (userfile, "r"); + fpuser = CVS_FOPEN (userfile, "r"); if (fpuser == NULL) { /* not fatal, continue import */ @@ -875,7 +907,7 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) error (0, errno, "ERROR: cannot read file %s", userfile); goto read_error; } - fprcs = fopen (rcs, "w+b"); + fprcs = CVS_FOPEN (rcs, "w+b"); if (fprcs == NULL) { ierrno = errno; @@ -905,11 +937,13 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) goto write_error; } - if (keyword_opt != NULL) - if (fprintf (fprcs, "expand @%s@;\012", keyword_opt) < 0) + if (local_opt != NULL) + { + if (fprintf (fprcs, "expand @%s@;\012", local_opt) < 0) { - goto write_error; + goto write_error; } + } if (fprintf (fprcs, "\012") < 0) goto write_error; @@ -976,7 +1010,7 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) /* Now copy over the contents of the file, expanding at signs. */ { - unsigned char buf[8192]; + char buf[8192]; unsigned int len; while (1) @@ -1030,6 +1064,8 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); return (err); write_error: @@ -1041,7 +1077,7 @@ write_error_noclose: error (0, ierrno, "ERROR: cannot write file %s", rcs); if (ierrno == ENOSPC) { - (void) unlink (rcs); + (void) CVS_UNLINK (rcs); fperror (logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } @@ -1050,6 +1086,9 @@ read_error: if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); + return (err + 1); } @@ -1157,7 +1196,7 @@ import_descend_dir (message, dir, vtag, targc, targv) #endif error (0, 0, "Importing %s", repository); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { ierrno = errno; fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository); diff --git a/gnu/usr.bin/cvs/src/log.c b/gnu/usr.bin/cvs/src/log.c index f167d92d987..2e2533249cf 100644 --- a/gnu/usr.bin/cvs/src/log.c +++ b/gnu/usr.bin/cvs/src/log.c @@ -17,42 +17,213 @@ #include "cvs.h" -static Dtype log_dirproc PROTO((char *dir, char *repository, char *update_dir)); -static int log_fileproc PROTO((struct file_info *finfo)); +/* This structure holds information parsed from the -r option. */ + +struct option_revlist +{ + /* The next -r option. */ + struct option_revlist *next; + /* The first revision to print. This is NULL if the range is + :rev, or if no revision is given. */ + char *first; + /* The last revision to print. This is NULL if the range is rev:, + or if no revision is given. If there is no colon, first and + last are the same. */ + char *last; + /* Nonzero if there was a trailing `.', which means to print only + the head revision of a branch. */ + int branchhead; +}; + +/* This structure holds information derived from option_revlist given + a particular RCS file. */ + +struct revlist +{ + /* The next pair. */ + struct revlist *next; + /* The first numeric revision to print. */ + char *first; + /* The last numeric revision to print. */ + char *last; + /* The number of fields in these revisions (one more than + numdots). */ + int fields; +}; + +/* This structure holds information parsed from the -d option. */ + +struct datelist +{ + /* The next date. */ + struct datelist *next; + /* The starting date. */ + char *start; + /* The ending date. */ + char *end; + /* Nonzero if the range is inclusive rather than exclusive. */ + int inclusive; +}; + +/* This structure is used to pass information through start_recursion. */ +struct log_data +{ + /* Nonzero if the -R option was given, meaning that only the name + of the RCS file should be printed. */ + int nameonly; + /* Nonzero if the -h option was given, meaning that only header + information should be printed. */ + int header; + /* Nonzero if the -t option was given, meaning that only the + header and the descriptive text should be printed. */ + int long_header; + /* Nonzero if the -N option was seen, meaning that tag information + should not be printed. */ + int notags; + /* Nonzero if the -b option was seen, meaning that only revisions + on the default branch should be printed. */ + int default_branch; + /* If not NULL, the value given for the -r option, which lists + sets of revisions to be printed. */ + struct option_revlist *revlist; + /* If not NULL, the date pairs given for the -d option, which + select date ranges to print. */ + struct datelist *datelist; + /* If not NULL, the single dates given for the -d option, which + select specific revisions to print based on a date. */ + struct datelist *singledatelist; + /* If not NULL, the list of states given for the -s option, which + only prints revisions of given states. */ + List *statelist; + /* If not NULL, the list of login names given for the -w option, + which only prints revisions checked in by given users. */ + List *authorlist; +}; + +/* This structure is used to pass information through walklist. */ +struct log_data_and_rcs +{ + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; +}; + +static Dtype log_dirproc PROTO ((void *callerdat, char *dir, + char *repository, char *update_dir, + List *entries)); +static int log_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static struct option_revlist *log_parse_revlist PROTO ((const char *)); +static void log_parse_date PROTO ((struct log_data *, const char *)); +static void log_parse_list PROTO ((List **, const char *)); +static struct revlist *log_expand_revlist PROTO ((RCSNode *, + struct option_revlist *, + int)); +static void log_free_revlist PROTO ((struct revlist *)); +static int log_version_requested PROTO ((struct log_data *, struct revlist *, + RCSNode *, RCSVers *)); +static int log_symbol PROTO ((Node *, void *)); +static int log_count PROTO ((Node *, void *)); +static int log_fix_singledate PROTO ((Node *, void *)); +static int log_count_print PROTO ((Node *, void *)); +static void log_tree PROTO ((struct log_data *, struct revlist *, + RCSNode *, const char *)); +static void log_abranch PROTO ((struct log_data *, struct revlist *, + RCSNode *, const char *)); +static void log_version PROTO ((struct log_data *, struct revlist *, + RCSNode *, RCSVers *, int)); +static int log_branch PROTO ((Node *, void *)); +static int version_compare PROTO ((const char *, const char *, int)); static const char *const log_usage[] = { - "Usage: %s %s [-l] [rlog-options] [files...]\n", + "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n", + " [-w[logins]] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", + "\t-R\tOnly print name of RCS file.\n", + "\t-h\tOnly print header.\n", + "\t-t\tOnly print header and descriptive text.\n", + "\t-N\tDo not list tags.\n", + "\t-b\tOnly list revisions on the default branch.\n", + "\t-r[revisions]\tSpecify revision(s)s to list.\n", + "\t-d dates\tSpecify dates (D1<D2 for range, D for latest before).\n", + "\t-s states\tOnly list revisions with specified states.\n", + "\t-w[logins]\tOnly list revisions checked in by specified logins.\n", NULL }; -static int ac; -static char **av; - int cvslog (argc, argv) int argc; char **argv; { - int i; + int c; int err = 0; int local = 0; + struct log_data log_data; + struct option_revlist *rl, **prl; if (argc == -1) usage (log_usage); - /* - * All 'log' command options except -l are passed directly on to 'rlog' - */ - for (i = 1; i < argc && argv[i][0] == '-'; i++) - if (argv[i][1] == 'l') - local = 1; + memset (&log_data, 0, sizeof log_data); + + optind = 1; + while ((c = getopt (argc, argv, "bd:hlNRr::s:tw::")) != -1) + { + switch (c) + { + case 'b': + log_data.default_branch = 1; + break; + case 'd': + log_parse_date (&log_data, optarg); + break; + case 'h': + log_data.header = 1; + break; + case 'l': + local = 1; + break; + case 'N': + log_data.notags = 1; + break; + case 'R': + log_data.nameonly = 1; + break; + case 'r': + rl = log_parse_revlist (optarg); + for (prl = &log_data.revlist; + *prl != NULL; + prl = &(*prl)->next) + ; + *prl = rl; + break; + case 's': + log_parse_list (&log_data.statelist, optarg); + break; + case 't': + log_data.long_header = 1; + break; + case 'w': + if (optarg != NULL) + log_parse_list (&log_data.authorlist, optarg); + else + log_parse_list (&log_data.authorlist, getcaller ()); + break; + case '?': + default: + usage (log_usage); + break; + } + } wrap_setup (); #ifdef CLIENT_SUPPORT - if (client_active) { + if (client_active) + { + int i; + /* We're the local client. Fire up the remote server. */ start_server (); @@ -71,30 +242,241 @@ cvslog (argc, argv) err = get_responses_and_close (); return err; } - - ac = argc; - av = argv; #endif err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc, - (DIRLEAVEPROC) NULL, argc - i, argv + i, local, + (DIRLEAVEPROC) NULL, (void *) &log_data, + argc - optind, argv + optind, local, W_LOCAL | W_REPOS | W_ATTIC, 0, 1, - (char *) NULL, 1, 0); + (char *) NULL, 1); return (err); } +/* + * Parse a revision list specification. + */ + +static struct option_revlist * +log_parse_revlist (argstring) + const char *argstring; +{ + char *copy; + struct option_revlist *ret, **pr; + + /* Unfortunately, rlog accepts -r without an argument to mean that + latest revision on the default branch, so we must support that + for compatibility. */ + if (argstring == NULL) + { + ret = (struct option_revlist *) xmalloc (sizeof *ret); + ret->first = NULL; + ret->last = NULL; + ret->next = NULL; + ret->branchhead = 0; + return ret; + } + + ret = NULL; + pr = &ret; + + /* Copy the argument into memory so that we can change it. We + don't want to change the argument because, at least as of this + writing, we will use it if we send the arguments to the server. + We never bother to free up our copy. */ + copy = xstrdup (argstring); + while (copy != NULL) + { + char *comma; + char *cp; + char *first, *last; + struct option_revlist *r; + + comma = strchr (copy, ','); + if (comma != NULL) + *comma++ = '\0'; + + first = copy; + cp = strchr (copy, ':'); + if (cp == NULL) + last = copy; + else + { + *cp++ = '\0'; + last = cp; + } + + if (*first == '\0') + first = NULL; + if (*last == '\0') + last = NULL; + + r = (struct option_revlist *) xmalloc (sizeof *r); + r->next = NULL; + r->first = first; + r->last = last; + if (first != last + || first[strlen (first) - 1] != '.') + { + r->branchhead = 0; + } + else + { + r->branchhead = 1; + first[strlen (first) - 1] = '\0'; + } + + *pr = r; + pr = &r->next; + + copy = comma; + } + + return ret; +} + +/* + * Parse a date specification. + */ +static void +log_parse_date (log_data, argstring) + struct log_data *log_data; + const char *argstring; +{ + char *orig_copy, *copy; + + /* Copy the argument into memory so that we can change it. We + don't want to change the argument because, at least as of this + writing, we will use it if we send the arguments to the server. */ + copy = xstrdup (argstring); + orig_copy = copy; + while (copy != NULL) + { + struct datelist *nd, **pd; + char *cpend, *cp, *ds, *de; + + nd = (struct datelist *) xmalloc (sizeof *nd); + + cpend = strchr (copy, ';'); + if (cpend != NULL) + *cpend++ = '\0'; + + pd = &log_data->datelist; + nd->inclusive = 0; + + if ((cp = strchr (copy, '>')) != NULL) + { + *cp++ = '\0'; + if (*cp == '=') + { + ++cp; + nd->inclusive = 1; + } + ds = cp; + de = copy; + } + else if ((cp = strchr (copy, '<')) != NULL) + { + *cp++ = '\0'; + if (*cp == '=') + { + ++cp; + nd->inclusive = 1; + } + ds = copy; + de = cp; + } + else + { + ds = NULL; + de = copy; + pd = &log_data->singledatelist; + } + + if (ds == NULL) + nd->start = NULL; + else if (*ds != '\0') + nd->start = Make_Date (ds); + else + { + /* 1970 was the beginning of time, as far as get_date and + Make_Date are concerned. */ + nd->start = Make_Date ("1/1/1970 UTC"); + } + + if (*de != '\0') + nd->end = Make_Date (de); + else + { + /* We want to set the end date to some time sufficiently far + in the future to pick up all revisions that have been + created since the specified date and the time `cvs log' + completes. */ + nd->end = Make_Date ("next week"); + } + + nd->next = *pd; + *pd = nd; + + copy = cpend; + } + + free (orig_copy); +} + +/* + * Parse a comma separated list of items, and add each one to *PLIST. + */ +static void +log_parse_list (plist, argstring) + List **plist; + const char *argstring; +{ + while (1) + { + Node *p; + char *cp; + + p = getnode (); + + cp = strchr (argstring, ','); + if (cp == NULL) + p->key = xstrdup (argstring); + else + { + size_t len; + + len = cp - argstring; + p->key = xmalloc (len + 1); + strncpy (p->key, argstring, len); + p->key[len + 1] = '\0'; + } + + if (*plist == NULL) + *plist = getlist (); + if (addnode (*plist, p) != 0) + freenode (p); + + if (cp == NULL) + break; + + argstring = cp + 1; + } +} /* * Do an rlog on a file */ -/* ARGSUSED */ static int -log_fileproc (finfo) +log_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { + struct log_data *log_data = (struct log_data *) callerdat; Node *p; RCSNode *rcsfile; - int retcode = 0; + char buf[50]; + struct revlist *revlist; + struct log_data_and_rcs log_data_and_rcs; if ((rcsfile = finfo->rcs) == NULL) { @@ -120,28 +502,882 @@ log_fileproc (finfo) return (1); } - run_setup ("%s%s -x,v/", Rcsbin, RCS_RLOG); + if (log_data->nameonly) { - int i; - for (i = 1; i < ac && av[i][0] == '-'; i++) - if (av[i][1] != 'l') - run_arg (av[i]); + cvs_output (rcsfile->path, 0); + cvs_output ("\n", 1); + return 0; } - run_arg (rcsfile->path); - if (*finfo->update_dir) + /* We will need all the information in the RCS file. */ + RCS_fully_parse (rcsfile); + + /* Turn any symbolic revisions in the revision list into numeric + revisions. */ + revlist = log_expand_revlist (rcsfile, log_data->revlist, + log_data->default_branch); + + /* The output here is intended to be exactly compatible with the + output of rlog. I'm not sure whether this code should be here + or in rcs.c; I put it here because it is specific to the log + function, even though it uses information gathered by the + functions in rcs.c. */ + + cvs_output ("\n", 1); + + cvs_output ("RCS file: ", 0); + cvs_output (rcsfile->path, 0); + + cvs_output ("\nWorking file: ", 0); + if (finfo->update_dir[0] == '\0') + cvs_output (finfo->file, 0); + else { - char *workfile = xmalloc (strlen (finfo->update_dir) + strlen (finfo->file) + 2); - sprintf (workfile, "%s/%s", finfo->update_dir, finfo->file); - run_arg (workfile); - free (workfile); + cvs_output (finfo->update_dir, 0); + cvs_output ("/", 0); + cvs_output (finfo->file, 0); + + } + + cvs_output ("\nhead:", 0); + if (rcsfile->head != NULL) + { + cvs_output (" ", 1); + cvs_output (rcsfile->head, 0); } - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY)) == -1) + cvs_output ("\nbranch:", 0); + if (rcsfile->branch != NULL) { - error (1, errno, "fork failed for rlog on %s", finfo->file); + cvs_output (" ", 1); + cvs_output (rcsfile->branch, 0); } - return (retcode); + + cvs_output ("\nlocks:", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "strict"); + if (p != NULL) + cvs_output (" strict", 0); + p = findnode (rcsfile->other, "locks"); + if (p != NULL && p->data != NULL) + { + char *f, *cp; + + f = xstrdup (p->data); + cp = f; + while (*cp != '\0') + { + char *cp2, *locker, *version; + RCSVers *vnode; + + locker = cp; + + cp2 = strchr (cp, ':'); + if (cp2 == NULL) + { + error (0, 0, "warning: bad locks field in RCS file `%s'", + finfo->fullname); + break; + } + + *cp2 = '\0'; + + cvs_output ("\n\t", 2); + cvs_output (cp, cp2 - cp); + cvs_output (": ", 2); + + cp = cp2 + 1; + while (isspace (*cp) && *cp != '\0') + ++cp; + + version = cp; + + cp2 = cp; + while (! isspace (*cp2) && *cp2 != '\0') + ++cp2; + + cvs_output (cp, cp2 - cp); + + if (*cp2 == '\0') + cp = cp2; + else + { + *cp2 = '\0'; + cp = cp2 + 1; + while (isspace (*cp) && *cp != '\0') + ++cp; + } + + p = findnode (rcsfile->versions, version); + if (p == NULL) + error (0, 0, + "warning: lock for missing version `%s' in `%s'", + version, finfo->fullname); + else + { + vnode = (RCSVers *) p->data; + p = getnode (); + p->type = RCSFIELD; + p->key = xstrdup (";locker"); + p->data = xstrdup (locker); + if (addnode (vnode->other, p) != 0) + { + error (0, 0, + "warning: duplicate lock for `%s' in `%s'", + version, finfo->fullname); + freenode (p); + } + } + } + + free (f); + } + } + + cvs_output ("\naccess list:", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "access"); + if (p != NULL && p->data != NULL) + { + const char *cp; + + cp = p->data; + while (*cp != '\0') + { + const char *cp2; + + cvs_output ("\n\t", 2); + cp2 = cp; + while (! isspace (*cp2) && *cp2 != '\0') + ++cp2; + cvs_output (cp, cp2 - cp); + cp = cp2; + while (isspace (*cp) && *cp != '\0') + ++cp; + } + } + } + + if (! log_data->notags) + { + List *syms; + + cvs_output ("\nsymbolic names:", 0); + syms = RCS_symbols (rcsfile); + walklist (syms, log_symbol, NULL); + } + + cvs_output ("\nkeyword substitution: ", 0); + if (rcsfile->expand == NULL) + cvs_output ("kv", 2); + else + cvs_output (rcsfile->expand, 0); + + cvs_output ("\ntotal revisions: ", 0); + sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL)); + cvs_output (buf, 0); + + if (! log_data->header && ! log_data->long_header) + { + cvs_output (";\tselected revisions: ", 0); + + log_data_and_rcs.log_data = log_data; + log_data_and_rcs.revlist = revlist; + log_data_and_rcs.rcs = rcsfile; + + /* If any single dates were specified, we need to identify the + revisions they select. Each one selects the single + revision, which is otherwise selected, of that date or + earlier. The log_fix_singledate routine will fill in the + start date for each specific revision. */ + if (log_data->singledatelist != NULL) + walklist (rcsfile->versions, log_fix_singledate, + (void *) &log_data_and_rcs); + + sprintf (buf, "%d", walklist (rcsfile->versions, log_count_print, + (void *) &log_data_and_rcs)); + cvs_output (buf, 0); + } + + cvs_output ("\n", 1); + + if (! log_data->header || log_data->long_header) + { + cvs_output ("description:\n", 0); + if (rcsfile->other != NULL) + { + p = findnode (rcsfile->other, "desc"); + if (p != NULL && p->data != NULL) + cvs_output (p->data, 0); + } + } + + if (! log_data->header && ! log_data->long_header && rcsfile->head != NULL) + { + p = findnode (rcsfile->versions, rcsfile->head); + if (p == NULL) + error (1, 0, "can not find head revision in `%s'", + finfo->fullname); + while (p != NULL) + { + RCSVers *vers; + + vers = (RCSVers *) p->data; + log_version (log_data, revlist, rcsfile, vers, 1); + if (vers->next == NULL) + p = NULL; + else + { + p = findnode (rcsfile->versions, vers->next); + if (p == NULL) + error (1, 0, "can not find next revision `%s' in `%s'", + vers->next, finfo->fullname); + } + } + + log_tree (log_data, revlist, rcsfile, rcsfile->head); + } + + cvs_output("\ +=============================================================================\n", + 0); + + /* Free up the new revlist and restore the old one. */ + log_free_revlist (revlist); + + /* If singledatelist is not NULL, free up the start dates we added + to it. */ + if (log_data->singledatelist != NULL) + { + struct datelist *d; + + for (d = log_data->singledatelist; d != NULL; d = d->next) + { + if (d->start != NULL) + free (d->start); + d->start = NULL; + } + } + + return 0; +} + +/* + * Fix up a revision list in order to compare it against versions. + * Expand any symbolic revisions. + */ +static struct revlist * +log_expand_revlist (rcs, revlist, default_branch) + RCSNode *rcs; + struct option_revlist *revlist; + int default_branch; +{ + struct option_revlist *r; + struct revlist *ret, **pr; + + ret = NULL; + pr = &ret; + for (r = revlist; r != NULL; r = r->next) + { + struct revlist *nr; + + nr = (struct revlist *) xmalloc (sizeof *nr); + + if (r->first == NULL && r->last == NULL) + { + /* If both first and last are NULL, it means that we want + just the head of the default branch, which is RCS_head. */ + nr->first = RCS_head (rcs); + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + } + else if (r->branchhead) + { + char *branch; + + /* Print just the head of the branch. */ + if (isdigit (r->first[0])) + nr->first = RCS_getbranch (rcs, r->first, 1); + else + { + branch = RCS_whatbranch (rcs, r->first); + if (branch == NULL) + { + error (0, 0, "warning: `%s' is not a branch in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + nr->first = RCS_getbranch (rcs, branch, 1); + free (branch); + } + if (nr->first == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + } + else + { + if (r->first == NULL || isdigit (r->first[0])) + nr->first = xstrdup (r->first); + else + { + if (RCS_nodeisbranch (rcs, r->first)) + nr->first = RCS_whatbranch (rcs, r->first); + else + nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL); + if (nr->first == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->first, rcs->path); + free (nr); + continue; + } + } + + if (r->last == r->first) + nr->last = xstrdup (nr->first); + else if (r->last == NULL || isdigit (r->last[0])) + nr->last = xstrdup (r->last); + else + { + if (RCS_nodeisbranch (rcs, r->last)) + nr->last = RCS_whatbranch (rcs, r->last); + else + nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL); + if (nr->last == NULL) + { + error (0, 0, "warning: no revision `%s' in `%s'", + r->last, rcs->path); + if (nr->first != NULL) + free (nr->first); + free (nr); + continue; + } + } + + /* Process the revision numbers the same way that rlog + does. This code is a bit cryptic for my tastes, but + keeping the same implementation as rlog ensures a + certain degree of compatibility. */ + if (r->first == NULL) + { + nr->fields = numdots (nr->last) + 1; + if (nr->fields < 2) + nr->first = xstrdup (".0"); + else + { + char *cp; + + nr->first = xstrdup (nr->last); + cp = strrchr (nr->first, '.'); + strcpy (cp, ".0"); + } + } + else if (r->last == NULL) + { + nr->fields = numdots (nr->first) + 1; + nr->last = xstrdup (nr->first); + if (nr->fields < 2) + nr->last[0] = '\0'; + else + { + char *cp; + + cp = strrchr (nr->last, '.'); + *cp = '\0'; + } + } + else + { + nr->fields = numdots (nr->first) + 1; + if (nr->fields != numdots (nr->last) + 1 + || (nr->fields > 2 + && version_compare (nr->first, nr->last, + nr->fields - 1) != 0)) + { + error (0, 0, + "invalid branch or revision pair %s:%s in `%s'", + r->first, r->last, rcs->path); + free (nr->first); + free (nr->last); + free (nr); + continue; + } + if (version_compare (nr->first, nr->last, nr->fields) > 0) + { + char *tmp; + + tmp = nr->first; + nr->first = nr->last; + nr->last = tmp; + } + } + } + + nr->next = NULL; + *pr = nr; + pr = &nr->next; + } + + /* If the default branch was requested, add a revlist entry for + it. This is how rlog handles this option. */ + if (default_branch + && (rcs->head != NULL || rcs->branch != NULL)) + { + struct revlist *nr; + + nr = (struct revlist *) xmalloc (sizeof *nr); + if (rcs->branch != NULL) + nr->first = xstrdup (rcs->branch); + else + { + char *cp; + + nr->first = xstrdup (rcs->head); + cp = strrchr (nr->first, '.'); + *cp = '\0'; + } + nr->last = xstrdup (nr->first); + nr->fields = numdots (nr->first) + 1; + + nr->next = NULL; + *pr = nr; + } + + return ret; +} + +/* + * Free a revlist created by log_expand_revlist. + */ +static void +log_free_revlist (revlist) + struct revlist *revlist; +{ + struct revlist *r; + + r = revlist; + while (r != NULL) + { + struct revlist *next; + + if (r->first != NULL) + free (r->first); + if (r->last != NULL) + free (r->last); + next = r->next; + free (r); + r = next; + } +} + +/* + * Return nonzero if a revision should be printed, based on the + * options provided. + */ +static int +log_version_requested (log_data, revlist, rcs, vnode) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + RCSVers *vnode; +{ + /* Handle the list of states from the -s option. */ + if (log_data->statelist != NULL) + { + Node *p; + + p = findnode (vnode->other, "state"); + if (p != NULL + && findnode (log_data->statelist, p->data) == NULL) + { + return 0; + } + } + + /* Handle the list of authors from the -w option. */ + if (log_data->authorlist != NULL) + { + if (vnode->author != NULL + && findnode (log_data->authorlist, vnode->author) == NULL) + { + return 0; + } + } + + /* rlog considers all the -d options together when it decides + whether to print a revision, so we must be compatible. */ + if (log_data->datelist != NULL || log_data->singledatelist != NULL) + { + struct datelist *d; + + for (d = log_data->datelist; d != NULL; d = d->next) + { + int cmp; + + cmp = RCS_datecmp (vnode->date, d->start); + if (cmp > 0 || (cmp == 0 && d->inclusive)) + { + cmp = RCS_datecmp (vnode->date, d->end); + if (cmp < 0 || (cmp == 0 && d->inclusive)) + break; + } + } + + if (d == NULL) + { + /* Look through the list of specific dates. We want to + select the revision with the exact date found in the + start field. The commit code ensures that it is + impossible to check in multiple revisions of a single + file in a single second, so checking the date this way + should never select more than one revision. */ + for (d = log_data->singledatelist; d != NULL; d = d->next) + { + if (d->start != NULL + && RCS_datecmp (vnode->date, d->start) == 0) + { + break; + } + } + + if (d == NULL) + return 0; + } + } + + /* If the -r or -b options were used, REVLIST will be non NULL, + and we print the union of the specified revisions. */ + if (revlist != NULL) + { + char *v; + int vfields; + struct revlist *r; + + /* This code is taken from rlog. */ + v = vnode->version; + vfields = numdots (v) + 1; + for (r = revlist; r != NULL; r = r->next) + { + if (vfields == r->fields + (r->fields & 1) + && version_compare (v, r->first, r->fields) >= 0 + && version_compare (v, r->last, r->fields) <= 0) + { + return 1; + } + } + + /* If we get here, then the -b and/or the -r option was used, + but did not match this revision, so we reject it. */ + + return 0; + } + + /* By default, we print all revisions. */ + return 1; +} + +/* + * Output a single symbol. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_symbol (p, closure) + Node *p; + void *closure; +{ + cvs_output ("\n\t", 2); + cvs_output (p->key, 0); + cvs_output (": ", 2); + cvs_output (p->data, 0); + return 0; +} + +/* + * Count the number of entries on a list. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_count (p, closure) + Node *p; + void *closure; +{ + return 1; +} + +/* + * Sort out a single date specification by narrowing down the date + * until we find the specific selected revision. + */ +static int +log_fix_singledate (p, closure) + Node *p; + void *closure; +{ + struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; + Node *pv; + RCSVers *vnode; + struct datelist *holdsingle, *holddate; + int requested; + + pv = findnode (data->rcs->versions, p->key); + if (pv == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + p->key, data->rcs->path); + vnode = (RCSVers *) pv->data; + + /* We are only interested if this revision passes any other tests. + Temporarily clear log_data->singledatelist to avoid confusing + log_version_requested. We also clear log_data->datelist, + because rlog considers all the -d options together. We don't + want to reject a revision because it does not match a date pair + if we are going to select it on the basis of the singledate. */ + holdsingle = data->log_data->singledatelist; + data->log_data->singledatelist = NULL; + holddate = data->log_data->datelist; + data->log_data->datelist = NULL; + requested = log_version_requested (data->log_data, data->revlist, + data->rcs, vnode); + data->log_data->singledatelist = holdsingle; + data->log_data->datelist = holddate; + + if (requested) + { + struct datelist *d; + + /* For each single date, if this revision is before the + specified date, but is closer than the previously selected + revision, select it instead. */ + for (d = data->log_data->singledatelist; d != NULL; d = d->next) + { + if (RCS_datecmp (vnode->date, d->end) <= 0 + && (d->start == NULL + || RCS_datecmp (vnode->date, d->start) > 0)) + { + if (d->start != NULL) + free (d->start); + d->start = xstrdup (vnode->date); + } + } + } + + return 0; +} + +/* + * Count the number of revisions we are going to print. + */ +static int +log_count_print (p, closure) + Node *p; + void *closure; +{ + struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; + Node *pv; + + pv = findnode (data->rcs->versions, p->key); + if (pv == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + p->key, data->rcs->path); + if (log_version_requested (data->log_data, data->revlist, data->rcs, + (RCSVers *) pv->data)) + return 1; + else + return 0; +} + +/* + * Print the list of changes, not including the trunk, in reverse + * order for each branch. + */ +static void +log_tree (log_data, revlist, rcs, ver) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + const char *ver; +{ + Node *p; + RCSVers *vnode; + + p = findnode (rcs->versions, ver); + if (p == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + ver, rcs->path); + vnode = (RCSVers *) p->data; + if (vnode->next != NULL) + log_tree (log_data, revlist, rcs, vnode->next); + if (vnode->branches != NULL) + { + Node *head, *branch; + + /* We need to do the branches in reverse order. This breaks + the List abstraction, but so does most of the branch + manipulation in rcs.c. */ + head = vnode->branches->list; + for (branch = head->prev; branch != head; branch = branch->prev) + { + log_abranch (log_data, revlist, rcs, branch->key); + log_tree (log_data, revlist, rcs, branch->key); + } + } +} + +/* + * Log the changes for a branch, in reverse order. + */ +static void +log_abranch (log_data, revlist, rcs, ver) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + const char *ver; +{ + Node *p; + RCSVers *vnode; + + p = findnode (rcs->versions, ver); + if (p == NULL) + error (1, 0, "missing version `%s' in RCS file `%s'", + ver, rcs->path); + vnode = (RCSVers *) p->data; + if (vnode->next != NULL) + log_abranch (log_data, revlist, rcs, vnode->next); + log_version (log_data, revlist, rcs, vnode, 0); +} + +/* + * Print the log output for a single version. + */ +static void +log_version (log_data, revlist, rcs, ver, trunk) + struct log_data *log_data; + struct revlist *revlist; + RCSNode *rcs; + RCSVers *ver; + int trunk; +{ + Node *p; + int year, mon, mday, hour, min, sec; + char buf[100]; + Node *padd, *pdel; + + if (! log_version_requested (log_data, revlist, rcs, ver)) + return; + + cvs_output ("----------------------------\nrevision ", 0); + cvs_output (ver->version, 0); + + p = findnode (ver->other, ";locker"); + if (p != NULL) + { + cvs_output ("\tlocked by: ", 0); + cvs_output (p->data, 0); + cvs_output (";", 1); + } + + cvs_output ("\ndate: ", 0); + (void) sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min, + &sec); + if (year < 1900) + year += 1900; + sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday, + hour, min, sec); + cvs_output (buf, 0); + + cvs_output ("; author: ", 0); + cvs_output (ver->author, 0); + + cvs_output ("; state: ", 0); + p = findnode (ver->other, "state"); + cvs_output (p->data, 0); + cvs_output (";", 1); + + if (! trunk) + { + padd = findnode (ver->other, ";add"); + pdel = findnode (ver->other, ";delete"); + } + else if (ver->next == NULL) + { + padd = NULL; + pdel = NULL; + } + else + { + Node *nextp; + RCSVers *nextver; + + nextp = findnode (rcs->versions, ver->next); + if (nextp == NULL) + error (1, 0, "missing version `%s' in `%s'", ver->next, + rcs->path); + nextver = (RCSVers *) nextp->data; + pdel = findnode (nextver->other, ";add"); + padd = findnode (nextver->other, ";delete"); + } + + if (padd != NULL) + { + cvs_output (" lines: +", 0); + cvs_output (padd->data, 0); + cvs_output (" -", 2); + cvs_output (pdel->data, 0); + } + + if (ver->branches != NULL) + { + cvs_output ("\nbranches:", 0); + walklist (ver->branches, log_branch, (void *) NULL); + } + + cvs_output ("\n", 1); + + p = findnode (ver->other, "log"); + if (p == NULL || p->data[0] == '\0') + cvs_output ("*** empty log message ***\n", 0); + else + { + /* FIXME: Technically, the log message could contain a null + byte. */ + cvs_output (p->data, 0); + if (p->data[strlen (p->data) - 1] != '\n') + cvs_output ("\n", 1); + } +} + +/* + * Output a branch version. This is called via walklist. + */ +/*ARGSUSED*/ +static int +log_branch (p, closure) + Node *p; + void *closure; +{ + cvs_output (" ", 2); + if ((numdots (p->key) & 1) == 0) + cvs_output (p->key, 0); + else + { + char *f, *cp; + + f = xstrdup (p->key); + cp = strrchr (f, '.'); + *cp = '\0'; + cvs_output (f, 0); + free (f); + } + cvs_output (";", 1); + return 0; } /* @@ -149,10 +1385,12 @@ log_fileproc (finfo) */ /* ARGSUSED */ static Dtype -log_dirproc (dir, repository, update_dir) +log_dirproc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { if (!isdir (dir)) return (R_SKIP_ALL); @@ -161,3 +1399,52 @@ log_dirproc (dir, repository, update_dir) error (0, 0, "Logging %s", update_dir); return (R_PROCESS); } + +/* + * Compare versions. This is taken from RCS compartial. + */ +static int +version_compare (v1, v2, len) + const char *v1; + const char *v2; + int len; +{ + while (1) + { + int d1, d2, r; + + if (*v1 == '\0') + return 1; + if (*v2 == '\0') + return -1; + + while (*v1 == '0') + ++v1; + for (d1 = 0; isdigit (v1[d1]); ++d1) + ; + + while (*v2 == '0') + ++v2; + for (d2 = 0; isdigit (v2[d2]); ++d2) + ; + + if (d1 != d2) + return d1 < d2 ? -1 : 1; + + r = memcmp (v1, v2, d1); + if (r != 0) + return r; + + --len; + if (len == 0) + return 0; + + v1 += d1; + v2 += d1; + + if (*v1 == '.') + ++v1; + if (*v2 == '.') + ++v2; + } +} diff --git a/gnu/usr.bin/cvs/src/login.c b/gnu/usr.bin/cvs/src/login.c index fc3a1783665..93e248ce571 100644 --- a/gnu/usr.bin/cvs/src/login.c +++ b/gnu/usr.bin/cvs/src/login.c @@ -25,36 +25,47 @@ static char *cvs_password = NULL; char * construct_cvspass_filename () { - char *homedir; - char *passfile; + char *homedir; + char *passfile; - /* Environment should override file. */ - if ((passfile = getenv ("CVS_PASSFILE")) != NULL) - return xstrdup (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 OS/2 ? */ - homedir = get_homedir (); - if (! homedir) + /* Construct absolute pathname to user's password file. */ + /* todo: does this work under OS/2 ? */ + homedir = get_homedir (); + if (! homedir) { - error (1, errno, "could not find out home directory"); - return (char *) NULL; + 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; + passfile = + (char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3); + strcpy (passfile, homedir); +#ifndef NO_SLASH_AFTER_HOME + /* NO_SLASH_AFTER_HOME is defined for VMS, where foo:[bar]/.cvspass is not + a legal filename but foo:[bar].cvspass is. A more clean solution would + be something more along the lines of a "join a directory to a filename" + kind of thing.... */ + strcat (passfile, "/"); +#endif + strcat (passfile, CVS_PASSWORD_FILE); + + /* Safety first and last, Scouts. */ + if (isfile (passfile)) + /* xchmod() is too polite. */ + chmod (passfile, 0600); + + return passfile; } +static const char *const login_usage[] = +{ + "Usage: %s %s\n", + NULL +}; /* Prompt for a password, and store it in the file "CVS/.cvspass". * @@ -84,219 +95,164 @@ login (argc, argv) int argc; char **argv; { - char *passfile; - FILE *fp; - char *typed_password, *found_password; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, already_entered = 0; + 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; + if (argc < 0) + usage (login_usage); - /* Reset. */ - free (linebuf); - linebuf = (char *) NULL; + if (CVSroot_method != pserver_method) + { + error (0, 0, "can only use pserver method with `login' command"); + error (1, 0, "CVSROOT: %s", CVSroot_original); } - - if (CVSroot[0] != ':') + + if (! CVSroot_username) { - /* Then we need to prepend ":pserver:". */ - char *tmp; - - tmp = xmalloc (strlen (":pserver:") + strlen (CVSroot) + 1); - strcpy (tmp, ":pserver:"); - strcat (tmp, CVSroot); - CVSroot = tmp; + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", + CVSroot_original); + error (1, 0, "Please make sure to specify \"user@host\"!"); } - /* 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"); - } - } - - /* CVSroot is now fully qualified and has ":pserver:" prepended. - We'll print out most of it so user knows exactly what is being - dealt with here. */ - { - char *s; - s = strchr (CVSroot, ':'); - s++; - s = strchr (s, ':'); - s++; - - if (s == NULL) - error (1, 0, "NULL CVSroot"); - - printf ("(Logging in to %s)\n", s); + printf ("(Logging in to %s@%s)\n", CVSroot_username, CVSroot_hostname); fflush (stdout); - } - passfile = construct_cvspass_filename (); - typed_password = getpass ("CVS password: "); - typed_password = scramble (typed_password); + 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(). */ - /* 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); + cvs_password = xstrdup (typed_password); - if (connect_to_pserver (NULL, NULL, 1) == 0) + if (connect_to_pserver (NULL, NULL, 1) == 0) { - /* The password is wrong, according to the server. */ - error (1, 0, "incorrect password"); + /* 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"); - /* FIXME: should be printing a message if fp == NULL and not - existence_error (errno). */ - if (fp != NULL) + /* 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_original); + + /* 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 = CVS_FOPEN (passfile, "r"); + /* FIXME: should be printing a message if fp == NULL and not + existence_error (errno). */ + if (fp != NULL) { - /* Check each line to see if we have this entry already. */ - while (getline (&linebuf, &linebuf_len, fp) >= 0) + /* 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) + if (strncmp (CVSroot_original, linebuf, root_len) == 0) { - already_entered = 1; - break; + already_entered = 1; + break; } - else + else { - free (linebuf); - linebuf = (char *) NULL; + free (linebuf); + linebuf = (char *) NULL; } } - fclose (fp); + fclose (fp); } - - if (already_entered) + + if (already_entered) { - /* This user/host has a password in the file already. */ + /* This user/host has a password in the file already. */ - strtok (linebuf, " "); - found_password = strtok (NULL, "\n"); - if (strcmp (found_password, typed_password)) + 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) + /* 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 = cvs_temp_name (); + if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; + error (1, errno, "unable to open temp file %s", tmp_name); + return 1; } - chmod (tmp_name, 0600); + chmod (tmp_name, 0600); - fp = fopen (passfile, "r"); - if (fp == NULL) + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) { - error (1, errno, "unable to open %s", passfile); - return 1; + error (1, errno, "unable to open %s", passfile); + return 1; } - /* I'm not paranoid, they really ARE out to get me: */ - chmod (passfile, 0600); + /* 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) + free (linebuf); + linebuf = (char *) NULL; + while (getline (&linebuf, &linebuf_len, fp) >= 0) { - if (strncmp (CVSroot, linebuf, root_len)) + if (strncmp (CVSroot_original, linebuf, root_len)) fprintf (tmp_fp, "%s", linebuf); else - fprintf (tmp_fp, "%s %s\n", CVSroot, typed_password); + fprintf (tmp_fp, "%s %s\n", CVSroot_original, typed_password); - free (linebuf); - linebuf = (char *) NULL; + free (linebuf); + linebuf = (char *) NULL; } - fclose (tmp_fp); - fclose (fp); - rename_file (tmp_name, passfile); - chmod (passfile, 0600); + fclose (tmp_fp); + fclose (fp); + copy_file (tmp_name, passfile); + unlink_file (tmp_name); + chmod (passfile, 0600); + free (tmp_name); } } - else + else { - if ((fp = fopen (passfile, "a")) == NULL) + if ((fp = CVS_FOPEN (passfile, "a")) == NULL) { - error (1, errno, "could not open %s", passfile); - free (passfile); - return 1; + error (1, errno, "could not open %s", passfile); + free (passfile); + return 1; } - fprintf (fp, "%s %s\n", CVSroot, typed_password); + fprintf (fp, "%s %s\n", CVSroot_original, typed_password); fclose (fp); } - /* Utter, total, raving paranoia, I know. */ - chmod (passfile, 0600); - memset (typed_password, 0, strlen (typed_password)); - free (typed_password); + /* 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; + free (passfile); + free (cvs_password); + cvs_password = NULL; + return 0; } /* todo: "cvs logout" could erase an entry from the file. @@ -334,9 +290,23 @@ get_cvs_password () return p; } - /* Else get it from the file. */ + /* Else get it from the file. First make sure that the CVSROOT + * variable has the appropriate fields filled in. */ + + if (CVSroot_method != pserver_method) + { + error (0, 0, "can only call GET_CVS_PASSWORD with pserver method"); + error (1, 0, "CVSROOT: %s", CVSroot_original); + } + + if (! CVSroot_username) + { + error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", CVSroot_original); + error (1, 0, "Please make sure to specify \"user@host\"!"); + } + passfile = construct_cvspass_filename (); - fp = fopen (passfile, "r"); + fp = CVS_FOPEN (passfile, "r"); if (fp == NULL) { error (0, errno, "could not open %s", passfile); @@ -344,12 +314,12 @@ get_cvs_password () error (1, 0, "use \"cvs login\" to log in first"); } - root_len = strlen (CVSroot); + root_len = strlen (CVSroot_original); /* 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) + if (strncmp (CVSroot_original, linebuf, root_len) == 0) { /* This is it! So break out and deal with linebuf. */ found_it = 1; diff --git a/gnu/usr.bin/cvs/src/logmsg.c b/gnu/usr.bin/cvs/src/logmsg.c index 370ceab186e..de022b9d6a3 100644 --- a/gnu/usr.bin/cvs/src/logmsg.c +++ b/gnu/usr.bin/cvs/src/logmsg.c @@ -12,8 +12,7 @@ static int find_type PROTO((Node * p, void *closure)); static int fmt_proc PROTO((Node * p, void *closure)); static int logfile_write PROTO((char *repository, char *filter, char *title, - char *message, char *revision, FILE * logfp, - List * changes)); + char *message, FILE * logfp, List * changes)); static int rcsinfo_proc PROTO((char *repository, char *template)); static int title_proc PROTO((Node * p, void *closure)); static int update_logfile_proc PROTO((char *repository, char *filter)); @@ -31,6 +30,7 @@ static Ctype type; * and removed files are included (if any) and formatted to look pretty. */ static char *prefix; static int col; +static char *tag; static void setup_tmpfile (xfp, xprefix, changes) FILE *xfp; @@ -45,28 +45,40 @@ setup_tmpfile (xfp, xprefix, changes) if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sModified Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } type = T_ADDED; if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sAdded Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } type = T_REMOVED; if (walklist (changes, find_type, NULL) != 0) { (void) fprintf (fp, "%sRemoved Files:\n", prefix); - (void) fprintf (fp, "%s\t", prefix); - col = 8; + col = 0; (void) walklist (changes, fmt_proc, NULL); (void) fprintf (fp, "\n"); + if (tag != NULL) + { + free (tag); + tag = NULL; + } } } @@ -78,7 +90,10 @@ find_type (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == type) return (1); else return (0); @@ -94,9 +109,44 @@ fmt_proc (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == type) { - if ((col + (int) strlen (p->key)) > 70) + if (li->tag == NULL + ? tag != NULL + : tag == NULL || strcmp (tag, li->tag) != 0) + { + if (col > 0) + (void) fprintf (fp, "\n"); + (void) fprintf (fp, "%s", prefix); + col = strlen (prefix); + while (col < 6) + { + (void) fprintf (fp, " "); + ++col; + } + + if (li->tag == NULL) + (void) fprintf (fp, "No tag"); + else + (void) fprintf (fp, "Tag: %s", li->tag); + + if (tag != NULL) + free (tag); + tag = xstrdup (li->tag); + + /* Force a new line. */ + col = 70; + } + + if (col == 0) + { + (void) fprintf (fp, "%s\t", prefix); + col = 8; + } + else if (col > 8 && (col + (int) strlen (p->key)) > 70) { (void) fprintf (fp, "\n%s\t", prefix); col = 8; @@ -126,7 +176,7 @@ do_editor (dir, messagep, repository, changes) char *line; int line_length; size_t line_chars_allocated; - char fname[L_tmpnam+1]; + char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; char *p; @@ -139,9 +189,9 @@ do_editor (dir, messagep, repository, changes) error(1, 0, "no editor defined, must use -e or -m"); /* Create a temporary file */ - (void) tmpnam (fname); + fname = cvs_temp_name (); again: - if ((fp = fopen (fname, "w+")) == NULL) + if ((fp = CVS_FOPEN (fname, "w+")) == NULL) error (1, 0, "cannot create temporary file %s", fname); if (*messagep) @@ -166,7 +216,7 @@ do_editor (dir, messagep, repository, changes) size_t nwrite; /* Why "b"? */ - tfp = fopen (CVSADM_TEMPLATE, "rb"); + tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); if (tfp == NULL) { if (!existence_error (errno)) @@ -211,7 +261,7 @@ do_editor (dir, messagep, repository, changes) /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); - if (stat (fname, &pre_stbuf) == -1) + if ( CVS_STAT (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; if (editinfo_editor) @@ -236,7 +286,7 @@ do_editor (dir, messagep, repository, changes) if (*messagep) free (*messagep); - if (stat (fname, &post_stbuf) != 0) + if ( CVS_STAT (fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", fname); if (post_stbuf.st_size == 0) @@ -288,7 +338,11 @@ do_editor (dir, messagep, repository, changes) || *line == '\n' || *line == 'c' || *line == 'C') break; if (*line == 'a' || *line == 'A') - error (1, 0, "aborted by user"); + { + if (unlink_file (fname) < 0) + error (0, errno, "warning: cannot remove temp file %s", fname); + error (1, 0, "aborted by user"); + } if (*line == 'e' || *line == 'E') goto again; if (*line == '!') @@ -303,6 +357,7 @@ do_editor (dir, messagep, repository, changes) free (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); + free (fname); } /* @@ -326,7 +381,7 @@ rcsinfo_proc (repository, template) free (last_template); last_template = xstrdup (template); - if ((tfp = fopen (template, "r")) != NULL) + if ((tfp = CVS_FOPEN (template, "r")) != NULL) { char *line = NULL; size_t line_chars_allocated = 0; @@ -357,14 +412,12 @@ rcsinfo_proc (repository, template) static char *title; static FILE *logfp; static char *message; -static char *revision; static List *changes; void -Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges) +Update_Logfile (repository, xmessage, xlogfp, xchanges) char *repository; char *xmessage; - char *xrevision; FILE *xlogfp; List *xchanges; { @@ -376,7 +429,6 @@ Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges) /* set up static vars for update_logfile_proc */ message = xmessage; - revision = xrevision; logfp = xlogfp; changes = xchanges; @@ -418,8 +470,8 @@ update_logfile_proc (repository, filter) char *repository; char *filter; { - return (logfile_write (repository, filter, title, message, revision, - logfp, changes)); + return (logfile_write (repository, filter, title, message, logfp, + changes)); } /* @@ -430,7 +482,10 @@ title_proc (p, closure) Node *p; void *closure; { - if (p->data == (char *) type) + struct logfile_info *li; + + li = (struct logfile_info *) p->data; + if (li->type == type) { (void) strcat (str_list, " "); (void) strcat (str_list, p->key); @@ -450,12 +505,11 @@ title_proc (p, closure) * filter program. */ static int -logfile_write (repository, filter, title, message, revision, logfp, changes) +logfile_write (repository, filter, title, message, logfp, changes) char *repository; char *filter; char *title; char *message; - char *revision; FILE *logfp; List *changes; { @@ -480,8 +534,6 @@ logfile_write (repository, filter, title, message, revision, logfp, changes) (void) fprintf (pipefp, "Update of %s\n", repository); (void) fprintf (pipefp, "In directory %s:%s\n\n", hostname, ((cp = getwd (cwd)) != NULL) ? cp : cwd); - if (revision && *revision) - (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision); setup_tmpfile (pipefp, "", changes); (void) fprintf (pipefp, "Log Message:\n%s\n", message); if (logfp != (FILE *) 0) diff --git a/gnu/usr.bin/cvs/src/mkmodules.c b/gnu/usr.bin/cvs/src/mkmodules.c index bdd27eb623d..9d4bbd63dfe 100644 --- a/gnu/usr.bin/cvs/src/mkmodules.c +++ b/gnu/usr.bin/cvs/src/mkmodules.c @@ -40,12 +40,11 @@ struct admin_file { }; static const char *const loginfo_contents[] = { - "# The \"loginfo\" file is used to control where \"cvs commit\" log information\n", - "# is sent. The first entry on a line is a regular expression which is tested\n", + "# The \"loginfo\" file is used to control where \"cvs commit\" log information is\n", + "# sent. The first entry on a line is a regular expression which is tested\n", "# against the directory that the change is being made to, relative to the\n", - "# $CVSROOT. For the first match that is found, then the remainder of the\n", - "# line is a filter program that should expect log information on its standard\n", - "# input.\n", + "# $CVSROOT. For the first match that is found, the remainder of the line is a\n", + "# filter program that should expect log information on its standard input\n", "#\n", "# If the repository name does not match any of the regular expressions in the\n", "# first field of this file, the \"DEFAULT\" line is used, if it is specified.\n", @@ -59,7 +58,7 @@ static const char *const loginfo_contents[] = { "# name and listing the modified file names.\n", "#\n", "# For example:\n", - "#DEFAULT (echo \"\"; who am i; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", + "#DEFAULT (echo \"\"; who am i; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", NULL }; @@ -99,8 +98,8 @@ static const char *const editinfo_contents[] = { "# Actions such as mailing a copy of the report to each reviewer are\n", "# better handled by an entry in the loginfo file.\n", "#\n", - "# One thing that should be noted is the the ALL keyword is not\n", - "# supported. There can be only one entry that matches a given\n", + "# One thing that should be noted is the the ALL keyword is not\n", + "# supported. There can be only one entry that matches a given\n", "# repository.\n", NULL }; @@ -222,6 +221,9 @@ static const char *const modules_contents[] = { "# -d dir Place module in directory \"dir\" instead of module name.\n", "# -l Top-level directory only -- do not recurse.\n", "#\n", + "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", + "# release and re-checkout any working directories of these modules.\n", + "#\n", "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", "#\n", "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", @@ -289,7 +291,7 @@ mkmodules (dir) if (save_cwd (&cwd)) exit (EXIT_FAILURE); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) error (1, errno, "cannot chdir to %s", dir); /* @@ -347,7 +349,7 @@ mkmodules (dir) } /* Use 'fopen' instead of 'open_file' because we want to ignore error */ - fp = fopen (CVSROOTADM_CHECKOUTLIST, "r"); + fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); if (fp) { /* @@ -412,7 +414,7 @@ make_tempfile (temp) while (1) { (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); - if ((fd = open (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) + if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) break; if (errno != EEXIST) error (1, errno, "cannot create temporary file %s", temp); @@ -568,12 +570,12 @@ rename_dbmfile (temp) (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */ (void) unlink_file (bakpag); (void) unlink_file (bakdb); - (void) rename (dotdir, bakdir); /* mv modules.dir .#modules.dir */ - (void) rename (dotpag, bakpag); /* mv modules.pag .#modules.pag */ - (void) rename (dotdb, bakdb); /* mv modules.db .#modules.db */ - (void) rename (newdir, dotdir); /* mv "temp".dir modules.dir */ - (void) rename (newpag, dotpag); /* mv "temp".pag modules.pag */ - (void) rename (newdb, dotdb); /* mv "temp".db modules.db */ + (void) CVS_RENAME (dotdir, bakdir); /* mv modules.dir .#modules.dir */ + (void) CVS_RENAME (dotpag, bakpag); /* mv modules.pag .#modules.pag */ + (void) CVS_RENAME (dotdb, bakdb); /* mv modules.db .#modules.db */ + (void) CVS_RENAME (newdir, dotdir); /* mv "temp".dir modules.dir */ + (void) CVS_RENAME (newpag, dotpag); /* mv "temp".pag modules.pag */ + (void) CVS_RENAME (newdb, dotdb); /* mv "temp".db modules.db */ /* OK -- make my day */ SIG_endCrSect (); @@ -593,14 +595,14 @@ rename_rcsfile (temp, real) /* Set "x" bits if set in original. */ (void) sprintf (rcs, "%s%s", real, RCSEXT); statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ - (void) stat (rcs, &statbuf); + (void) CVS_STAT (rcs, &statbuf); if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) error (0, errno, "warning: cannot chmod %s", temp); (void) sprintf (bak, "%s%s", BAKPREFIX, real); (void) unlink_file (bak); /* rm .#loginfo */ - (void) rename (real, bak); /* mv loginfo .#loginfo */ - (void) rename (temp, real); /* mv "temp" loginfo */ + (void) CVS_RENAME (real, bak); /* mv loginfo .#loginfo */ + (void) CVS_RENAME (temp, real); /* mv "temp" loginfo */ } const char *const init_usage[] = { @@ -608,26 +610,6 @@ const char *const init_usage[] = { NULL }; -/* Create directory NAME if it does not already exist; fatal error for - other errors. FIXME: This should be in filesubr.c or thereabouts, - probably. Perhaps it should be further abstracted, though (for example - to handle CVSUMASK where appropriate?). */ -static void -mkdir_if_needed (name) - char *name; -{ - if (CVS_MKDIR (name, 0777) < 0) - { - if (errno != EEXIST -#ifdef EACCESS - /* OS/2; see longer comment in client.c. */ - && errno != EACCESS -#endif - ) - error (1, errno, "cannot mkdir %s", name); - } -} - int init (argc, argv) int argc; @@ -644,9 +626,10 @@ init (argc, argv) umask (cvsumask); - if (argc > 1) + if (argc == -1 || argc > 1) usage (init_usage); +#ifdef CLIENT_SUPPORT if (client_active) { start_server (); @@ -655,21 +638,22 @@ init (argc, argv) send_init_command (); return get_responses_and_close (); } +#endif /* CLIENT_SUPPORT */ /* Note: we do *not* create parent directories as needed like the old cvsinit.sh script did. Few utilities do that, and a non-existent parent directory is as likely to be a typo as something which needs to be created. */ - mkdir_if_needed (CVSroot); + mkdir_if_needed (CVSroot_directory); - adm = xmalloc (strlen (CVSroot) + sizeof (CVSROOTADM) + 10); - strcpy (adm, CVSroot); + adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10); + strcpy (adm, CVSroot_directory); strcat (adm, "/"); strcat (adm, CVSROOTADM); mkdir_if_needed (adm); /* This is needed by the call to "ci" below. */ - if (chdir (adm) < 0) + if ( CVS_CHDIR (adm) < 0) error (1, errno, "cannot change to directory %s", adm); /* 80 is long enough for all the administrative file names, plus diff --git a/gnu/usr.bin/cvs/src/modules.c b/gnu/usr.bin/cvs/src/modules.c index 0e07c0bf2c8..a82415a7857 100644 --- a/gnu/usr.bin/cvs/src/modules.c +++ b/gnu/usr.bin/cvs/src/modules.c @@ -56,14 +56,15 @@ open_module () { char mfile[PATH_MAX]; - if (CVSroot == NULL) + if (CVSroot_original == NULL) { (void) fprintf (stderr, "%s: must set the CVSROOT environment variable\n", program_name); error (1, 0, "or specify the '-d' option to %s", program_name); } - (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES); + (void) sprintf (mfile, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_MODULES); return (dbm_open (mfile, O_RDONLY, 0666)); } @@ -121,13 +122,24 @@ do_module (db, mname, m_type, msg, callback_proc, where, #ifdef SERVER_SUPPORT if (trace) - { - fprintf (stderr, "%s%c-> do_module (%s, %s, %s, %s)\n", - error_use_protocol ? "E " : "", + { + char *buf; + + /* We use cvs_outerr, rather than fprintf to stderr, because + this may be called by server code with error_use_protocol + set. */ + buf = xmalloc (100 + + strlen (mname) + + strlen (msg) + + (where ? strlen (where) : 0) + + (extra_arg ? strlen (extra_arg) : 0)); + sprintf (buf, "%c-> do_module (%s, %s, %s, %s)\n", (server_active) ? 'S' : ' ', mname, msg, where ? where : "", extra_arg ? extra_arg : ""); - } + cvs_outerr (buf, 0); + free (buf); + } #endif /* if this is a directory to ignore, add it to that list */ @@ -188,17 +200,17 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* check to see if mname is a directory or file */ - (void) sprintf (file, "%s/%s", CVSroot, mname); + (void) sprintf (file, "%s/%s", CVSroot_directory, mname); if ((acp = strrchr (mname, '/')) != NULL) { *acp = '\0'; - (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname, - CVSATTIC, acp + 1, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot_directory, + mname, CVSATTIC, acp + 1, RCSEXT); *acp = '/'; } else - (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC, - mname, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot_directory, + CVSATTIC, mname, RCSEXT); if (isdir (file)) { @@ -343,7 +355,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* XXX - think about making null repositories at each dir here instead of just at the bottom */ make_directories (dir); - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); spec_opt = NULL; @@ -354,7 +366,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, { char nullrepos[PATH_MAX]; - (void) sprintf (nullrepos, "%s/%s/%s", CVSroot, + (void) sprintf (nullrepos, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSNULLREPOS); if (!isfile (nullrepos)) { @@ -592,6 +604,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, char *prog = (m_type == TAG ? tag_prog : (m_type == CHECKOUT ? checkout_prog : export_prog)); char *real_where = (where != NULL ? where : mwhere); + char *expanded_path; if ((*prog != '/') && (*prog != '.')) { @@ -600,18 +613,25 @@ do_module (db, mname, m_type, msg, callback_proc, where, prog = real_prog; } - run_setup ("%s %s", prog, real_where); - if (extra_arg) - run_arg (extra_arg); - - if (!quiet) + /* XXX can we determine the line number for this entry??? */ + expanded_path = expand_path (prog, "modules", 0); + if (expanded_path != NULL) { - (void) printf ("%s %s: Executing '", program_name, - command_name); - run_print (stdout); - (void) printf ("'\n"); + run_setup ("%s %s", expanded_path, real_where); + + if (extra_arg) + run_arg (extra_arg); + + if (!quiet) + { + (void) printf ("%s %s: Executing '", program_name, + command_name); + run_print (stdout); + (void) printf ("'\n"); + } + err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (expanded_path); } - err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); } } @@ -777,22 +797,6 @@ cat_module (status) char *line; char *moduleargv[MAXFILEPERDIR]; -#ifdef sun -#ifdef TIOCGSIZE - struct ttysize ts; - - (void) ioctl (0, TIOCGSIZE, &ts); - cols = ts.ts_cols; -#endif -#else -#ifdef TIOCGWINSZ - struct winsize ws; - - (void) ioctl (0, TIOCGWINSZ, &ws); - cols = ws.ws_col; -#endif -#endif - Status = status; /* Read the whole modules file into allocated records */ diff --git a/gnu/usr.bin/cvs/src/myndbm.c b/gnu/usr.bin/cvs/src/myndbm.c index 527f7eee9cb..cae27e7cacd 100644 --- a/gnu/usr.bin/cvs/src/myndbm.c +++ b/gnu/usr.bin/cvs/src/myndbm.c @@ -31,7 +31,7 @@ mydbm_open (file, flags, mode) FILE *fp; DBM *db; - fp = fopen (file, FOPEN_BINARY_READ); + fp = CVS_FOPEN (file, FOPEN_BINARY_READ); if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT))) return ((DBM *) 0); @@ -71,7 +71,7 @@ mydbm_close (db) if (db->modified) { FILE *fp; - fp = fopen (db->name, FOPEN_BINARY_WRITE); + fp = CVS_FOPEN (db->name, FOPEN_BINARY_WRITE); if (fp == NULL) error (1, errno, "cannot write %s", db->name); walklist (db->dbm_list, write_item, (void *)fp); diff --git a/gnu/usr.bin/cvs/src/no_diff.c b/gnu/usr.bin/cvs/src/no_diff.c index a0d00f5a861..31ef057417f 100644 --- a/gnu/usr.bin/cvs/src/no_diff.c +++ b/gnu/usr.bin/cvs/src/no_diff.c @@ -17,15 +17,12 @@ #include "cvs.h" int -No_Difference (file, vers, entries, repository, update_dir) - char *file; +No_Difference (finfo, vers) + struct file_info *finfo; Vers_TS *vers; - List *entries; - char *repository; - char *update_dir; { Node *p; - char tmp[L_tmpnam+1]; + char *tmp; int ret; char *ts, *options; int retcode = 0; @@ -39,33 +36,34 @@ No_Difference (file, vers, entries, repository, update_dir) else options = xstrdup (""); - retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_user, options, - tmpnam (tmp), 0, 0); + tmp = cvs_temp_name (); + retcode = RCS_checkout (vers->srcfile, (char *) NULL, vers->vn_user, + (char *) NULL, options, tmp); if (retcode == 0) { #if 0 /* Why would we want to munge the modes? And only if the timestamps are different? And even for commands like "cvs status"???? */ - if (!iswritable (file)) /* fix the modes as a side effect */ - xchmod (file, 1); + if (!iswritable (finfo->file)) /* fix the modes as a side effect */ + xchmod (finfo->file, 1); #endif - tocvsPath = wrap_tocvs_process_file (file); + tocvsPath = wrap_tocvs_process_file (finfo->file); /* do the byte by byte compare */ - if (xcmp (tocvsPath == NULL ? file : tocvsPath, tmp) == 0) + if (xcmp (tocvsPath == NULL ? finfo->file : tocvsPath, tmp) == 0) { #if 0 /* Why would we want to munge the modes? And only if the timestamps are different? And even for commands like "cvs status"???? */ if (cvswrite == FALSE) /* fix the modes as a side effect */ - xchmod (file, 0); + xchmod (finfo->file, 0); #endif /* no difference was found, so fix the entries file */ - ts = time_stamp (file); - Register (entries, file, + ts = time_stamp (finfo->file); + Register (finfo->entries, finfo->file, vers->vn_user ? vers->vn_user : vers->vn_rcs, ts, options, vers->tag, vers->date, (char *) 0); #ifdef SERVER_SUPPORT @@ -73,13 +71,13 @@ No_Difference (file, vers, entries, repository, update_dir) { /* We need to update the entries line on the client side. */ server_update_entries - (file, update_dir, repository, SERVER_UPDATED); + (finfo->file, finfo->update_dir, finfo->repository, SERVER_UPDATED); } #endif free (ts); /* update the entdata pointer in the vers_ts structure */ - p = findnode (entries, file); + p = findnode (finfo->entries, finfo->file); vers->entdata = (Entnode *) p->data; ret = 0; @@ -98,20 +96,15 @@ No_Difference (file, vers, entries, repository, update_dir) ' ', #endif tocvsPath); - if (unlink (tocvsPath) < 0) + if ( CVS_UNLINK (tocvsPath) < 0) error (0, errno, "could not remove %s", tocvsPath); } } else { - if (update_dir[0] == '\0') - error (0, retcode == -1 ? errno : 0, - "could not check out revision %s of %s", - vers->vn_user, file); - else - error (0, retcode == -1 ? errno : 0, - "could not check out revision %s of %s/%s", - vers->vn_user, update_dir, file); + error (0, retcode == -1 ? errno : 0, + "could not check out revision %s of %s", + vers->vn_user, finfo->fullname); ret = -1; /* different since we couldn't tell */ } @@ -122,8 +115,9 @@ No_Difference (file, vers, entries, repository, update_dir) #else (void) fprintf (stderr, "-> unlink (%s)\n", tmp); #endif - if (unlink (tmp) < 0) + if (CVS_UNLINK (tmp) < 0) error (0, errno, "could not remove %s", tmp); + free (tmp); free (options); return (ret); } diff --git a/gnu/usr.bin/cvs/src/options.h.in b/gnu/usr.bin/cvs/src/options.h.in index 7cb58dcd944..0f6374b8b5a 100644 --- a/gnu/usr.bin/cvs/src/options.h.in +++ b/gnu/usr.bin/cvs/src/options.h.in @@ -16,65 +16,68 @@ */ /* - * CVS provides the most features when used in conjunction with the Version-5 - * release of RCS. Thus, it is the default. This also assumes that GNU diff - * Version-1.15 is being used as well -- you will have to configure your RCS - * V5 release separately to make this the case. If you do not have RCS V5 and - * GNU diff V1.15, comment out this define. You should not try mixing and - * matching other combinations of these tools. + * CVS provides the most features when used in conjunction with the + * Version-5 release of RCS. Thus, it is the default. This also + * assumes that GNU diff Version-1.15 is being used as well -- you + * will have to configure your RCS V5 release separately to make this + * the case. If you do not have RCS V5 and GNU diff V1.15, comment out + * this define. You should not try mixing and matching other + * combinations of these tools. */ #ifndef HAVE_RCS5 #define HAVE_RCS5 #endif /* - * If, before installing this version of CVS, you were running RCS V4 AND you - * are installing this CVS and RCS V5 and GNU diff 1.15 all at the same time, - * you should turn on the following define. It only exists to try to do - * reasonable things with your existing checked out files when you upgrade to - * RCS V5, since the keyword expansion formats have changed with RCS V5. + * If, before installing this version of CVS, you were running RCS V4 + * AND you are installing this CVS and RCS V5 and GNU diff 1.15 all at + * the same time, you should turn on the following define. It only + * exists to try to do reasonable things with your existing checked + * out files when you upgrade to RCS V5, since the keyword expansion + * formats have changed with RCS V5. * - * If you already have been running with RCS5, or haven't been running with CVS - * yet at all, or are sticking with RCS V4 for now, leave the commented out. + * If you already have been running with RCS5, or haven't been running + * with CVS yet at all, or are sticking with RCS V4 for now, leave the + * commented out. */ #ifndef HAD_RCS4 /* #define HAD_RCS4 */ #endif /* - * For portability and heterogeneity reasons, CVS is shipped by default using - * my own text-file version of the ndbm database library in the src/myndbm.c - * file. If you want better performance and are not concerned about - * heterogeneous hosts accessing your modules file, turn this option off. + * For portability and heterogeneity reasons, CVS is shipped by + * default using my own text-file version of the ndbm database library + * in the src/myndbm.c file. If you want better performance and are + * not concerned about heterogeneous hosts accessing your modules + * file, turn this option off. */ #ifndef MY_NDBM #define MY_NDBM #endif /* - * 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. 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). + * 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. 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 * use rcsdiff which will use whatever version of diff was specified * when rcsdiff was built on your system. */ - #ifndef DIFF #define DIFF "diff" #endif /* - * The "grep" program to execute when checking to see if a merged file had - * 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. + * The "grep" program to execute when checking to see if a merged file + * had 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 "grep" #endif @@ -89,13 +92,13 @@ #endif /* - * By default, RCS programs are executed with the shell or through execlp(), - * so the user's PATH environment variable is searched. If you'd like to - * bind all RCS programs to a certain directory (perhaps one not in most - * people's PATH) then set the default in RCSBIN_DFLT. Note that setting - * this here will cause all RCS programs to be executed from this directory, - * unless the user overrides the default with the RCSBIN environment variable - * or the "-b" option to CVS. + * By default, RCS programs are executed with the shell or through + * execlp(), so the user's PATH environment variable is searched. If + * you'd like to bind all RCS programs to a certain directory (perhaps + * one not in most people's PATH) then set the default in RCSBIN_DFLT. + * Note that setting this here will cause all RCS programs to be + * executed from this directory, unless the user overrides the default + * with the RCSBIN environment variable or the "-b" option to CVS. * * If you use the password-authenticating server, then you need to * make sure that the server can find the RCS programs to invoke them. @@ -104,12 +107,16 @@ * complete. But no actual 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. + * would include the directory. * * One way to solve this problem is to set RCSBIN_DFLT here. An * alternative 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. + * specify -b in /etc/inetd.conf. + * + * You may also have to set RCSBIN_DFLT here if there's no global + * start-up script run for users by rshd and your RCS programs are not + * in a directory in the default PATH assigned by rshd. * * This define should be either the empty string ("") or a full * pathname to the directory containing all the installed programs @@ -120,11 +127,22 @@ #endif /* - * The default editor to use, if one does not specify the "-e" option to cvs, - * or does not have an EDITOR environment variable. I set this to just "vi", - * and use the shell to find where "vi" actually is. This allows sites with - * /usr/bin/vi or /usr/ucb/vi to work equally well (assuming that your PATH - * is reasonable). + * The password-authenticating server creates a temporary checkout of + * the affected files. The variable TMPDIR_DFLT (or even better, the + * command-line option "-T" in the line for CVS in /etc/inetd.conf) + * can be used to specify the used directory. This directory will + * also be used for other temporary files. + */ +#ifndef TMPDIR_DFLT +#define TMPDIR_DFLT "/tmp" +#endif + +/* + * The default editor to use, if one does not specify the "-e" option + * to cvs, or does not have an EDITOR environment variable. I set + * this to just "vi", and use the shell to find where "vi" actually + * is. This allows sites with /usr/bin/vi or /usr/ucb/vi to work + * equally well (assuming that your PATH is reasonable). */ #ifndef EDITOR_DFLT #define EDITOR_DFLT "vi" @@ -147,22 +165,23 @@ * 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. + * 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. + * 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. * - * The only advantage that I can see to having a relative path is that One can - * change the physical location of the master source repository, change one's - * CVSROOT environment variable, and CVS will work without problems. I - * recommend using full-paths. + * The only advantage that I can see to having a relative path is that + * one can change the physical location of the master source + * repository, change one's CVSROOT environment variable, and CVS will + * work without problems. I recommend using full-paths. */ #ifndef RELATIVE_REPOS /* #define RELATIVE_REPOS */ @@ -170,25 +189,28 @@ /* * When committing or importing files, you must enter a log message. - * Normally, you can do this either via the -m flag on the command line or an - * editor will be started for you. If you like to use logging templates (the - * rcsinfo file within the $CVSROOT/CVSROOT directory), you might want to - * force people to use the editor even if they specify a message with -m. - * Enabling FORCE_USE_EDITOR will cause the -m message to be appended to the - * temp file when the editor is started. + * Normally, you can do this either via the -m flag on the command + * line or an editor will be started for you. If you like to use + * logging templates (the rcsinfo file within the $CVSROOT/CVSROOT + * directory), you might want to force people to use the editor even + * if they specify a message with -m. Enabling FORCE_USE_EDITOR will + * cause the -m message to be appended to the temp file when the + * editor is started. */ #ifndef FORCE_USE_EDITOR /* #define FORCE_USE_EDITOR */ #endif /* - * When locking the repository, some sites like to remove locks and assume - * the program that created them went away if the lock has existed for a long - * time. This used to be the default for previous versions of CVS. CVS now - * attempts to be much more robust, so lock files should not be left around - * by mistake. The new behaviour will never remove old locks (they must now - * be removed by hand). Enabling CVS_FUDGELOCKS will cause CVS to remove - * locks that are older than CVSLCKAGE seconds. + * When locking the repository, some sites like to remove locks and + * assume the program that created them went away if the lock has + * existed for a long time. This used to be the default for previous + * versions of CVS. CVS now attempts to be much more robust, so lock + * files should not be left around by mistake. The new behaviour will + * never remove old locks (they must now be removed by hand). + * Enabling CVS_FUDGELOCKS will cause CVS to remove locks that are + * older than CVSLCKAGE seconds. + * * Use of this option is NOT recommended. */ #ifndef CVS_FUDGELOCKS @@ -197,73 +219,65 @@ /* * When committing a permanent change, CVS and RCS make a log entry of - * who committed the change. If you are committing the change logged in - * as "root" (not under "su" or other root-priv giving program), CVS/RCS - * cannot determine who is actually making the change. + * who committed the change. If you are committing the change logged + * in as "root" (not under "su" or other root-priv giving program), + * CVS/RCS cannot determine who is actually making the change. * * As such, by default, CVS disallows changes to be committed by users - * logged in as "root". You can disable this option by commenting - * out the lines below. + * logged in as "root". You can disable this option by commenting out + * the lines below. */ #ifndef CVS_BADROOT #define CVS_BADROOT #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 - * -D to specify a free-form date (like "cvs diff -D '1 week ago'"). If - * you would prefer that the -D option of "cvs diff" work like the GNU diff - * option, then comment out this define. + * Define this to enable the SETXID support. The way to use this is + * to create a group with no users in it (except perhaps cvs + * administrators), set the cvs executable to setgid that group, chown + * all the repository files to that group, and change all directory + * permissions in the repository to 770. The last person to modify a + * file will own it, but as long as directory permissions are set + * right that won't matter. You'll need a system which inherits file + * groups from the parent directory. I don't know how carefully this + * has been inspected for security holes. */ -#ifndef CVS_DIFFDATE -#define CVS_DIFFDATE -#endif - -/* Define this to enable the SETXID support. The way to use this is - to create a group with no users in it (except perhaps cvs - administrators), set the cvs executable to setgid that group, chown - all the repository files to that group, and change all directory - permissions in the repository to 770. The last person to modify a - file will own it, but as long as directory permissions are set - right that won't matter. You'll need a system which inherits file - groups from the parent directory. I don't know how carefully this - has been inspected for security holes. */ - #ifndef SETXID_SUPPORT /* #define SETXID_SUPPORT */ #endif -/* Should we build the password-authenticating client? Whether to - include the password-authenticating _server_, on the other hand, is - set in config.h. */ +/* + * Should we build the password-authenticating client? Whether to + * include the password-authenticating _server_, on the other hand, is + * set in config.h. + */ +#ifdef CLIENT_SUPPORT #define AUTH_CLIENT_SUPPORT 1 +#endif /* - * 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. + * 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 probability of + * a consistent checkout (see Concurrency in cvs.texinfo), but CVS + * doesn't try to guarantee that anyway. 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. 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 +#define SERVER_FLOWCONTROL +#define SERVER_HI_WATER (2 * 1024 * 1024) +#define SERVER_LO_WATER (1 * 1024 * 1024) /* End of CVS configuration section */ /* - * Externs that are included in libc, but are used frequently enough to - * warrant defining here. + * Externs that are included in libc, but are used frequently enough + * to warrant defining here. */ #ifndef STDC_HEADERS extern void exit (); diff --git a/gnu/usr.bin/cvs/src/parseinfo.c b/gnu/usr.bin/cvs/src/parseinfo.c index c567ef83e97..e68fd0bfbe7 100644 --- a/gnu/usr.bin/cvs/src/parseinfo.c +++ b/gnu/usr.bin/cvs/src/parseinfo.c @@ -32,7 +32,7 @@ Parse_Info (infofile, repository, callproc, all) char *cp, *exp, *value, *srepos; const char *regex_err; - if (CVSroot == NULL) + if (CVSroot_original == NULL) { /* XXX - should be error maybe? */ error (0, 0, "CVSROOT variable not set"); @@ -40,9 +40,9 @@ Parse_Info (infofile, repository, callproc, all) } /* find the info file and open it */ - (void) sprintf (infopath, "%s/%s/%s", CVSroot, + (void) sprintf (infopath, "%s/%s/%s", CVSroot_directory, CVSROOTADM, infofile); - if ((fp_info = fopen (infopath, "r")) == NULL) + if ((fp_info = CVS_FOPEN (infopath, "r")) == NULL) return (0); /* no file -> nothing special done */ /* strip off the CVSROOT if repository was absolute */ diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c index c68c25524a7..9c7ca375ff6 100644 --- a/gnu/usr.bin/cvs/src/rcs.c +++ b/gnu/usr.bin/cvs/src/rcs.c @@ -12,12 +12,21 @@ #include "cvs.h" static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile)); +static void RCS_reparsercsfile PROTO((RCSNode *, int, FILE **)); static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch)); -static int getrcskey PROTO((FILE * fp, char **keyp, char **valp)); +static int getrcskey PROTO((FILE * fp, char **keyp, char **valp, + size_t *lenp)); +static void getrcsrev PROTO ((FILE *fp, char **revp)); static int checkmagic_proc PROTO((Node *p, void *closure)); static void do_branches PROTO((List * list, char *val)); static void do_symbols PROTO((List * list, char *val)); +static void free_rcsnode_contents PROTO((RCSNode *)); static void rcsvers_delproc PROTO((Node * p)); +static char *translate_symtag PROTO((RCSNode *, const char *)); + +enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; +static void RCS_deltas PROTO ((RCSNode *, FILE *, char *, enum rcs_delta_op, + char **, size_t *)); /* * We don't want to use isspace() from the C library because: @@ -62,7 +71,7 @@ RCS_parse (file, repos) char rcsfile[PATH_MAX]; (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT); - if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) != NULL) + if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL) { rcs = RCS_parsercsfile_i(fp, rcsfile); if (rcs != NULL) @@ -78,7 +87,7 @@ RCS_parse (file, repos) } (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT); - if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) != NULL) + if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL) { rcs = RCS_parsercsfile_i(fp, rcsfile); if (rcs != NULL) @@ -110,7 +119,7 @@ RCS_parsercsfile (rcsfile) RCSNode *rcs; /* open the rcsfile */ - if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) == NULL) + if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL) { error (0, errno, "Couldn't open rcs file `%s'", rcsfile); return (NULL); @@ -145,7 +154,7 @@ RCS_parsercsfile_i (fp, rcsfile) * information. Those that do call XXX to completely parse the * RCS file. */ - if (getrcskey (fp, &key, &value) == -1 || key == NULL) + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) goto l_error; if (strcmp (key, RCSDESC) == 0) goto l_error; @@ -153,7 +162,7 @@ RCS_parsercsfile_i (fp, rcsfile) if (strcmp (RCSHEAD, key) == 0 && value != NULL) rdata->head = xstrdup (value); - if (getrcskey (fp, &key, &value) == -1 || key == NULL) + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL) goto l_error; if (strcmp (key, RCSDESC) == 0) goto l_error; @@ -196,11 +205,15 @@ l_error: On error, die with a fatal error; if it returns at all it was successful. + If ALL is nonzero, remember all keywords and values. Otherwise + only keep the ones we will need. + If PFP is NULL, close the file when done. Otherwise, leave it open and store the FILE * in *PFP. */ static void -RCS_reparsercsfile (rdata, pfp) +RCS_reparsercsfile (rdata, all, pfp) RCSNode *rdata; + int all; FILE **pfp; { FILE *fp; @@ -215,7 +228,7 @@ RCS_reparsercsfile (rdata, pfp) assert (rdata != NULL); rcsfile = rdata->path; - fp = fopen(rcsfile, FOPEN_BINARY_READ); + fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ); if (fp == NULL) error (1, 0, "unable to reopen `%s'", rcsfile); @@ -232,7 +245,7 @@ RCS_reparsercsfile (rdata, pfp) /* if key is NULL here, then the file is missing some headers or we had trouble reading the file. */ - if (getrcskey (fp, &key, &value) == -1 || key == NULL + if (getrcskey (fp, &key, &value, NULL) == -1 || key == NULL || strcmp (key, RCSDESC) == 0) { if (ferror(fp)) @@ -249,10 +262,8 @@ RCS_reparsercsfile (rdata, pfp) if (strcmp (RCSSYMBOLS, key) == 0) { if (value != NULL) - { rdata->symbols_data = xstrdup(value); - continue; - } + continue; } if (strcmp (RCSEXPAND, key) == 0) @@ -271,6 +282,24 @@ RCS_reparsercsfile (rdata, pfp) if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; + if (all) + { + Node *kv; + + if (rdata->other == NULL) + rdata->other = getlist (); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + if (addnode (rdata->other, kv) != 0) + { + error (0, 0, "warning: duplicate key `%s' in RCS file `%s'", + key, rcsfile); + freenode (kv); + } + } + /* if we haven't grabbed it yet, we didn't want it */ } @@ -282,6 +311,7 @@ RCS_reparsercsfile (rdata, pfp) for (;;) { char *valp; + Node *kvstate; vnode = (RCSVers *) xmalloc (sizeof (RCSVers)); memset (vnode, 0, sizeof (RCSVers)); @@ -297,7 +327,7 @@ RCS_reparsercsfile (rdata, pfp) vnode->date = xstrdup (valp); /* Get author field. */ - (void) getrcskey (fp, &key, &value); + (void) getrcskey (fp, &key, &value, NULL); /* FIXME: should be using errno in case of ferror. */ if (key == NULL || strcmp (key, "author") != 0) error (1, 0, "\ @@ -305,7 +335,7 @@ unable to parse rcs file; `author' not in the expected place"); vnode->author = xstrdup (value); /* Get state field. */ - (void) getrcskey (fp, &key, &value); + (void) getrcskey (fp, &key, &value, NULL); /* FIXME: should be using errno in case of ferror. */ if (key == NULL || strcmp (key, "state") != 0) error (1, 0, "\ @@ -314,9 +344,29 @@ unable to parse rcs file; `state' not in the expected place"); { vnode->dead = 1; } + if (! all) + kvstate = NULL; + else + { + if (vnode->other == NULL) + vnode->other = getlist (); + kvstate = getnode (); + kvstate->type = RCSFIELD; + kvstate->key = xstrdup (key); + kvstate->data = xstrdup (value); + if (addnode (vnode->other, kvstate) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcsfile); + freenode (kvstate); + kvstate = NULL; + } + } /* fill in the branch list (if any branches exist) */ - (void) getrcskey (fp, &key, &value); + (void) getrcskey (fp, &key, &value, NULL); /* FIXME: should be handling various error conditions better. */ if (key != NULL && strcmp (key, RCSDESC) == 0) value = NULL; @@ -327,7 +377,7 @@ unable to parse rcs file; `state' not in the expected place"); } /* fill in the next field if there is a next revision */ - (void) getrcskey (fp, &key, &value); + (void) getrcskey (fp, &key, &value, NULL); /* FIXME: should be handling various error conditions better. */ if (key != NULL && strcmp (key, RCSDESC) == 0) value = NULL; @@ -339,7 +389,7 @@ unable to parse rcs file; `state' not in the expected place"); * we put the symbolic link stuff??? */ /* FIXME: Does not correctly handle errors, e.g. from stdio. */ - while ((n = getrcskey (fp, &key, &value)) >= 0) + while ((n = getrcskey (fp, &key, &value, NULL)) >= 0) { assert (key != NULL); @@ -356,6 +406,11 @@ unable to parse rcs file; `state' not in the expected place"); if (strcmp(key, RCSDEAD) == 0) { vnode->dead = 1; + if (kvstate != NULL) + { + free (kvstate->data); + kvstate->data = xstrdup ("dead"); + } continue; } /* if we have a revision, break and do it */ @@ -363,6 +418,26 @@ unable to parse rcs file; `state' not in the expected place"); /* do nothing */ ; if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0) break; + + if (all) + { + Node *kv; + + if (vnode->other == NULL) + vnode->other = getlist (); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcsfile); + freenode (kv); + } + } } /* get the node */ @@ -390,6 +465,28 @@ unable to parse rcs file; `state' not in the expected place"); break; } + if (all && key != NULL && strcmp (key, RCSDESC) == 0) + { + Node *kv; + + if (rdata->other == NULL) + rdata->other = getlist (); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + if (addnode (rdata->other, kv) != 0) + { + error (0, 0, + "warning: duplicate key `%s' in RCS file `%s'", + key, rcsfile); + freenode (kv); + } + } + + rdata->delta_pos = ftell (fp); + rdata->flags &= ~NODELTA; + if (pfp == NULL) { if (fclose (fp) < 0) @@ -403,6 +500,177 @@ unable to parse rcs file; `state' not in the expected place"); } /* + * Fully parse the RCS file. Store all keyword/value pairs, fetch the + * log messages for each revision, and fetch add and delete counts for + * each revision (we could fetch the entire text for each revision, + * but the only caller, log_fileproc, doesn't need that information, + * so we don't waste the memory required to store it). The add and + * delete counts are stored on the OTHER field of the RCSVERSNODE + * structure, under the names ";add" and ";delete", so that we don't + * waste the memory space of extra fields in RCSVERSNODE for code + * which doesn't need this information. + */ + +void +RCS_fully_parse (rcs) + RCSNode *rcs; +{ + FILE *fp; + + RCS_reparsercsfile (rcs, 1, &fp); + + while (1) + { + int c; + char *key, *value; + size_t vallen; + Node *vers; + RCSVers *vnode; + + /* Rather than try to keep track of how much information we + have read, just read to the end of the file. */ + do + { + c = getc (fp); + if (c == EOF) + break; + } while (whitespace (c)); + if (c == EOF) + break; + if (ungetc (c, fp) == EOF) + error (1, errno, "ungetc failed"); + + getrcsrev (fp, &key); + vers = findnode (rcs->versions, key); + if (vers == NULL) + error (1, 0, + "mismatch in rcs file %s between deltas and deltatexts", + rcs->path); + + vnode = (RCSVers *) vers->data; + + while (getrcskey (fp, &key, &value, &vallen) >= 0) + { + if (strcmp (key, "text") != 0) + { + Node *kv; + + if (vnode->other == NULL) + vnode->other = getlist (); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (key); + kv->data = xstrdup (value); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + + continue; + } + + if (strcmp (vnode->version, rcs->head) != 0) + { + unsigned long add, del; + char buf[50]; + Node *kv; + + /* This is a change text. Store the add and delete + counts. */ + add = 0; + del = 0; + if (value != NULL) + { + const char *cp; + + cp = value; + while (cp < value + vallen) + { + char op; + unsigned long count; + + op = *cp++; + if (op != 'a' && op != 'd') + error (1, 0, "unrecognized operation '%c' in %s", + op, rcs->path); + (void) strtoul (cp, (char **) &cp, 10); + if (*cp++ != ' ') + error (1, 0, "space expected in %s", + rcs->path); + count = strtoul (cp, (char **) &cp, 10); + if (*cp++ != '\012') + error (1, 0, "linefeed expected in %s", + rcs->path); + + if (op == 'd') + del += count; + else + { + add += count; + while (count != 0) + { + if (*cp == '\012') + --count; + else if (cp == value + vallen) + { + if (count != 1) + error (1, 0, "\ +invalid rcs file %s: premature end of value", + rcs->path); + else + break; + } + ++cp; + } + } + } + } + + sprintf (buf, "%lu", add); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (";add"); + kv->data = xstrdup (buf); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + + sprintf (buf, "%lu", del); + kv = getnode (); + kv->type = RCSFIELD; + kv->key = xstrdup (";delete"); + kv->data = xstrdup (buf); + if (addnode (vnode->other, kv) != 0) + { + error (0, 0, + "\ +warning: duplicate key `%s' in version `%s' of RCS file `%s'", + key, vnode->version, rcs->path); + freenode (kv); + } + } + + /* We have found the "text" key which ends the data for + this revision. Break out of the loop and go on to the + next revision. */ + break; + } + } + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); +} + +/* * freercsnode - free up the info for an RCSNode */ void @@ -419,22 +687,37 @@ freercsnode (rnodep) return; } free ((*rnodep)->path); - dellist (&(*rnodep)->versions); - if ((*rnodep)->symbols != (List *) NULL) - 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) free ((*rnodep)->branch); + free_rcsnode_contents (*rnodep); free ((char *) *rnodep); *rnodep = (RCSNode *) NULL; } /* + * free_rcsnode_contents - free up the contents of an RCSNode without + * freeing the node itself, or the file name, or the head, or the + * path. This returns the RCSNode to the state it is in immediately + * after a call to RCS_parse. + */ +static void +free_rcsnode_contents (rnode) + RCSNode *rnode; +{ + dellist (&rnode->versions); + if (rnode->symbols != (List *) NULL) + dellist (&rnode->symbols); + if (rnode->symbols_data != (char *) NULL) + free (rnode->symbols_data); + if (rnode->expand != NULL) + free (rnode->expand); + if (rnode->other != (List *) NULL) + dellist (&rnode->other); +} + +/* * rcsvers_delproc - free up an RCSVers type node */ static void @@ -451,6 +734,10 @@ rcsvers_delproc (p) free (rnode->date); if (rnode->next != (char *) NULL) free (rnode->next); + if (rnode->author != (char *) NULL) + free (rnode->author); + if (rnode->other != (List *) NULL) + dellist (&rnode->other); free ((char *) rnode); } @@ -469,8 +756,10 @@ rcsvers_delproc (p) * o return 0 since we found something besides "desc" * * Sets *KEYP and *VALUEP to point to storage managed by the getrcskey - * function; the contents are only valid until the next call to getrcskey - * or getrcsrev. + * function; the contents are only valid until the next call to + * getrcskey or getrcsrev. If LENP is not NULL, this sets *LENP to + * the length of *VALUEP; this is needed if the string might contain + * binary data. */ static char *key = NULL; @@ -481,13 +770,18 @@ static size_t valsize = 0; #define ALLOCINCR 1024 static int -getrcskey (fp, keyp, valp) +getrcskey (fp, keyp, valp, lenp) FILE *fp; char **keyp; char **valp; + size_t *lenp; { char *cur, *max; int c; + int just_string; + + if (lenp != NULL) + *lenp = 0; /* skip leading whitespace */ do @@ -556,6 +850,10 @@ getrcskey (fp, keyp, valp) cur = value; max = value + valsize; + just_string = (strcmp (key, RCSDESC) == 0 + || strcmp (key, "text") == 0 + || strcmp (key, "log") == 0); + /* process the value */ for (;;) { @@ -599,9 +897,7 @@ getrcskey (fp, keyp, valp) /* The syntax for some key-value pairs is different; they don't end with a semicolon. */ - if (strcmp (key, RCSDESC) == 0 - || strcmp (key, "text") == 0 - || strcmp (key, "log") == 0) + if (just_string) break; /* compress whitespace down to a single space */ @@ -660,16 +956,18 @@ getrcskey (fp, keyp, valp) *cur = '\0'; /* if the string is empty, make it null */ - if (value && *value != '\0') + if (value && cur != value) + { *valp = value; + if (lenp != NULL) + *lenp = cur - value; + } else *valp = NULL; *keyp = key; return (0); } -static void getrcsrev PROTO ((FILE *fp, char **revp)); - /* Read an RCS revision number from FP. Put a pointer to it in *REVP; it points to space managed by getrcsrev which is only good until the next call to getrcskey or getrcsrev. */ @@ -810,36 +1108,43 @@ do_branches (list, val) * The result is returned; null-string if error. */ char * -RCS_getversion (rcs, tag, date, force_tag_match, return_both) +RCS_getversion (rcs, tag, date, force_tag_match, simple_tag) RCSNode *rcs; char *tag; char *date; int force_tag_match; - int return_both; + int *simple_tag; { + if (simple_tag != NULL) + *simple_tag = 0; + /* make sure we have something to look at... */ assert (rcs != NULL); if (tag && date) { - char *cp, *rev, *tagrev; + char *branch, *rev; - /* - * 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, 0); - if (tagrev == NULL) - return ((char *) NULL); + if (! RCS_isbranch (rcs, tag)) + { + /* We can't get a particular date if the tag is not a + branch. */ + return NULL; + } - if ((cp = strrchr (tagrev, '.')) != NULL) - *cp = '\0'; - rev = RCS_getdatebranch (rcs, date, tagrev); - free (tagrev); + /* Work out the branch. */ + if (! isdigit (tag[0])) + branch = RCS_whatbranch (rcs, tag); + else + branch = xstrdup (tag); + + /* Fetch the revision of branch as of date. */ + rev = RCS_getdatebranch (rcs, date, branch); + free (branch); return (rev); } else if (tag) - return (RCS_gettag (rcs, tag, force_tag_match, return_both)); + return (RCS_gettag (rcs, tag, force_tag_match, simple_tag)); else if (date) return (RCS_getdate (rcs, date, force_tag_match)); else @@ -856,21 +1161,24 @@ RCS_getversion (rcs, tag, date, force_tag_match, return_both) * If the matched tag is a branch tag, find the head of the branch. */ char * -RCS_gettag (rcs, symtag, force_tag_match, return_both) +RCS_gettag (rcs, symtag, force_tag_match, simple_tag) RCSNode *rcs; char *symtag; int force_tag_match; - int return_both; + int *simple_tag; { - Node *p; char *tag = symtag; + int tag_allocated = 0; + + if (simple_tag != NULL) + *simple_tag = 0; /* make sure we have something to look at... */ assert (rcs != NULL); /* XXX this is probably not necessary, --jtc */ if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); /* If tag is "HEAD", special case to get head RCS revision */ if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0')) @@ -883,18 +1191,17 @@ RCS_gettag (rcs, symtag, force_tag_match, return_both) if (!isdigit (tag[0])) { + char *version; + /* If we got a symbolic tag, resolve it to a numeric */ - if (rcs == NULL) - p = NULL; - else { - p = findnode (RCS_symbols(rcs), tag); - } - if (p != NULL) + version = translate_symtag (rcs, tag); + if (version != NULL) { int dots; char *magic, *branch, *cp; - tag = p->data; + tag = version; + tag_allocated = 1; /* * If this is a magic revision, we turn it into either its @@ -914,21 +1221,17 @@ RCS_gettag (rcs, symtag, force_tag_match, return_both) (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); if (strncmp (magic, cp, strlen (magic)) == 0) { - char *xtag; - /* it's magic. See if the branch exists */ *cp = '\0'; /* turn it into a revision */ - xtag = xstrdup (tag); - *cp = '.'; /* and back again */ - (void) sprintf (magic, "%s.%s", xtag, branch); + (void) sprintf (magic, "%s.%s", tag, branch); branch = RCS_getbranch (rcs, magic, 1); free (magic); if (branch != NULL) { - free (xtag); + free (tag); return (branch); } - return (xtag); + return (tag); } free (magic); } @@ -955,39 +1258,40 @@ RCS_gettag (rcs, symtag, force_tag_match, return_both) if ((numdots (tag) & 1) == 0) { + char *branch; + /* we have a branch tag, so we need to walk the branch */ - return (RCS_getbranch (rcs, tag, force_tag_match)); + branch = RCS_getbranch (rcs, tag, force_tag_match); + if (tag_allocated) + free (tag); + return branch; } else { + Node *p; + /* we have a revision tag, so make sure it exists */ - if (rcs == NULL) - p = NULL; - else - p = findnode (rcs->versions, tag); + p = findnode (rcs->versions, tag); if (p != NULL) { - /* - * we have found a numeric revision for the revision tag. - * To support expanding the RCS keyword Name, 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)); + /* We have found a numeric revision for the revision tag. + To support expanding the RCS keyword Name, if + SIMPLE_TAG is not NULL, tell the the caller that this + is a simple tag which co will recognize. FIXME: Are + there other cases in which we should set this? In + particular, what if we expand RCS keywords internally + without calling co? */ + if (simple_tag != NULL) + *simple_tag = 1; + if (! tag_allocated) + tag = xstrdup (tag); + return (tag); } else { /* The revision wasn't there, so return the head or NULL */ + if (tag_allocated) + free (tag); if (force_tag_match) return (NULL); else @@ -1104,37 +1408,42 @@ RCS_nodeisbranch (rcs, rev) const char *rev; { int dots; - Node *p; + char *version; /* numeric revisions are easy -- even number of dots is a branch */ if (isdigit (*rev)) return ((numdots (rev) & 1) == 0); - p = findnode (RCS_symbols(rcs), rev); - if (p == NULL) + version = translate_symtag (rcs, rev); + if (version == NULL) return (0); - dots = numdots (p->data); + dots = numdots (version); if ((dots & 1) == 0) + { + free (version); return (1); + } /* got a symbolic tag match, but it's not a branch; see if it's magic */ if (dots > 2) { char *magic; - char *branch = strrchr (p->data, '.'); + char *branch = strrchr (version, '.'); char *cp = branch - 1; while (*cp != '.') cp--; /* see if we have .magic-branch. (".0.") */ - magic = xmalloc (strlen (p->data) + 1); + magic = xmalloc (strlen (version) + 1); (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); if (strncmp (magic, cp, strlen (magic)) == 0) { free (magic); + free (version); return (1); } free (magic); + free (version); } return (0); } @@ -1148,7 +1457,7 @@ RCS_whatbranch (rcs, rev) RCSNode *rcs; const char *rev; { - Node *p; + char *version; int dots; /* assume no branch if you can't find the RCS info */ @@ -1156,34 +1465,35 @@ RCS_whatbranch (rcs, rev) return ((char *) NULL); /* now, look for a match in the symbols list */ - p = findnode (RCS_symbols(rcs), rev); - if (p == NULL) + version = translate_symtag (rcs, rev); + if (version == NULL) return ((char *) NULL); - dots = numdots (p->data); + dots = numdots (version); if ((dots & 1) == 0) - return (xstrdup (p->data)); + return (version); /* got a symbolic tag match, but it's not a branch; see if it's magic */ if (dots > 2) { char *magic; - char *branch = strrchr (p->data, '.'); + char *branch = strrchr (version, '.'); char *cp = branch++ - 1; while (*cp != '.') cp--; /* see if we have .magic-branch. (".0.") */ - magic = xmalloc (strlen (p->data) + 1); + magic = xmalloc (strlen (version) + 1); (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH); if (strncmp (magic, cp, strlen (magic)) == 0) { /* yep. it's magic. now, construct the real branch */ *cp = '\0'; /* turn it into a revision */ - (void) sprintf (magic, "%s.%s", p->data, branch); - *cp = '.'; /* and turn it back */ + (void) sprintf (magic, "%s.%s", version, branch); + free (version); return (magic); } free (magic); + free (version); } return ((char *) NULL); } @@ -1208,7 +1518,7 @@ RCS_getbranch (rcs, tag, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); /* find out if the tag contains a dot, or is on the trunk */ cp = strrchr (tag, '.'); @@ -1347,7 +1657,7 @@ RCS_getdate (rcs, date, force_tag_match) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); /* if the head is on a branch, try the branch first */ if (rcs->branch != NULL) @@ -1434,7 +1744,7 @@ RCS_getdatebranch (rcs, date, branch) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); p = findnode (rcs->versions, xrev); free (xrev); @@ -1442,9 +1752,13 @@ RCS_getdatebranch (rcs, date, branch) return (NULL); vers = (RCSVers *) p->data; - /* if no branches list, return NULL */ + /* Tentatively use this revision, if it is early enough. */ + if (RCS_datecmp (vers->date, date) <= 0) + cur_rev = vers->version; + + /* if no branches list, return now */ if (vers->branches == NULL) - return (NULL); + return xstrdup (cur_rev); /* walk the branches list looking for the branch number */ xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */ @@ -1455,7 +1769,11 @@ RCS_getdatebranch (rcs, date, branch) break; free (xbranch); if (p == vers->branches->list) + { + /* FIXME: This case would seem to imply that the RCS file is + somehow invalid. Should we give an error message? */ return (NULL); + } p = findnode (rcs->versions, p->key); @@ -1475,11 +1793,8 @@ RCS_getdatebranch (rcs, date, branch) p = (Node *) NULL; } - /* if we found something acceptable, return it - otherwise NULL */ - if (cur_rev != NULL) - return (xstrdup (cur_rev)); - else - return (NULL); + /* Return whatever we found, which may be NULL. */ + return xstrdup (cur_rev); } /* @@ -1519,7 +1834,7 @@ RCS_getrevtime (rcs, rev, date, fudge) assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); /* look up the revision */ p = findnode (rcs->versions, rev); @@ -1581,7 +1896,7 @@ RCS_symbols(rcs) assert(rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); if (rcs->symbols_data) { rcs->symbols = getlist (); @@ -1594,6 +1909,69 @@ RCS_symbols(rcs) } /* + * Return the version associated with a particular symbolic tag. + */ +static char * +translate_symtag (rcs, tag) + RCSNode *rcs; + const char *tag; +{ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, 0, NULL); + + if (rcs->symbols != NULL) + { + Node *p; + + /* The symbols have already been converted into a list. */ + p = findnode (rcs->symbols, tag); + if (p == NULL) + return NULL; + + return xstrdup (p->data); + } + + if (rcs->symbols_data != NULL) + { + size_t len; + char *cp; + + /* Look through the RCS symbols information. This is like + do_symbols, but we don't add the information to a list. In + most cases, we will only be called once for this file, so + generating the list is unnecessary overhead. */ + + len = strlen (tag); + cp = rcs->symbols_data; + while ((cp = strchr (cp, tag[0])) != NULL) + { + if ((cp == rcs->symbols_data || whitespace (cp[-1])) + && strncmp (cp, tag, len) == 0 + && cp[len] == ':') + { + char *v, *r; + + /* We found the tag. Return the version number. */ + + cp += len + 1; + v = cp; + while (! whitespace (*cp) && *cp != '\0') + ++cp; + r = xmalloc (cp - v + 1); + strncpy (r, v, cp - v); + r[cp - v] = '\0'; + return r; + } + + while (! whitespace (*cp) && *cp != '\0') + ++cp; + } + } + + return NULL; +} + +/* * The argument ARG is the getopt remainder of the -k option specified on the * command line. This function returns malloc'ed space that can be used * directly in calls to RCS V5, with the -k flag munged correctly. @@ -1686,7 +2064,7 @@ RCS_isdead (rcs, tag) RCSVers *version; if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); p = findnode (rcs->versions, tag); if (p == NULL) @@ -1707,13 +2085,475 @@ RCS_getexpand (rcs) { assert (rcs != NULL); if (rcs->flags & PARTIAL) - RCS_reparsercsfile (rcs, NULL); + RCS_reparsercsfile (rcs, 0, NULL); return rcs->expand; } + +/* Check out a revision from RCS. This function optimizes by reading + the head version directly if it is easy. Check out the revision + into WORKFILE, or to standard output if WORKFILE is NULL. REV is + the numeric revision to check out; it may be NULL, which means to + check out the head of the default branch. If NAMETAG is not NULL, + it is the tag that should be used when expanding the RCS Name + keyword. OPTIONS is a string such as -kb or -kkv, for keyword + expansion options, or NULL if there are none. If WORKFILE is NULL, + run regardless of noexec; if non-NULL, noexec inhibits execution. + SOUT is what to do with standard output (typically RUN_TTY). */ + +int +RCS_checkout (rcs, workfile, rev, nametag, options, sout) + RCSNode *rcs; + char *workfile; + char *rev; + char *nametag; + char *options; + char *sout; +{ + int free_rev = 0; + FILE *fp; + struct stat sb; + char *key; + char *value; + size_t len; + int free_value = 0; + char *ouroptions; + int keywords; + int ret; + + if (trace) + { + (void) fprintf (stderr, "%s-> checkout (%s, %s, %s, %s)\n", +#ifdef SERVER_SUPPORT + server_active ? "S" : " ", +#else + "", +#endif + rcs->path, + rev != NULL ? rev : "", + options != NULL ? options : "", + (workfile != NULL + ? workfile + : (sout != RUN_TTY ? sout : "(stdout)"))); + } + + assert (rev == NULL || isdigit (*rev)); + + if (noexec && workfile != NULL) + return 0; + + assert (sout == RUN_TTY || workfile == NULL); + + /* Some callers, such as Checkin or remove_file, will pass us a + branch. */ + if (rev != NULL && (numdots (rev) & 1) == 0) + { + rev = RCS_getbranch (rcs, rev, 1); + if (rev == NULL) + error (1, 0, "internal error: bad branch tag in checkout"); + free_rev = 1; + } + + if (rev == NULL || strcmp (rev, rcs->head) == 0) + { + int gothead; + + /* We want the head revision. Try to read it directly. */ + + if (rcs->flags & NODELTA) + { + free_rcsnode_contents (rcs); + rcs->flags |= PARTIAL; + } + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, 0, &fp); + else + { + fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file"); + } + + gothead = 0; + getrcsrev (fp, &key); + while (getrcskey (fp, &key, &value, &len) >= 0) + { + if (strcmp (key, "text") == 0) + { + gothead = 1; + break; + } + } + + if (! gothead) + { + error (0, 0, "internal error: cannot find head text"); + if (free_rev) + free (rev); + return 1; + } + + if (fstat (fileno (fp), &sb) < 0) + error (1, errno, "cannot fstat %s", rcs->path); + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); + } + else + { + /* It isn't the head revision of the trunk. We'll need to + walk through the deltas. */ + + fp = NULL; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, 0, &fp); + + if (fp == NULL) + { + /* If RCS_deltas didn't close the file, we could use fstat + here too. Probably should change it thusly.... */ + if (stat (rcs->path, &sb) < 0) + error (1, errno, "cannot stat %s", rcs->path); + } + else + { + if (fstat (fileno (fp), &sb) < 0) + error (1, errno, "cannot fstat %s", rcs->path); + } + + RCS_deltas (rcs, fp, rev, RCS_FETCH, &value, &len); + free_value = 1; + } + + /* If OPTIONS is NULL or the empty string, then the old code would + invoke the RCS co program with no -k option, which means that + co would use the string we have stored in rcs->expand. */ + if (options != NULL && options[0] != '\0') + { + assert (options[0] == '-' && options[1] == 'k'); + ouroptions = options + 2; + } + else if (rcs->expand != NULL) + ouroptions = rcs->expand; + else + ouroptions = "kv"; + + keywords = 0; + + if (strcmp (ouroptions, "o") != 0 + && strcmp (ouroptions, "b") != 0) + { + register int inkeyword; + register char *s, *send; + + /* Keyword expansion is being done. Make sure the text does + not contain any keywords. If it does have any, do the + regular checkout. */ + inkeyword = 0; + send = value + len; + for (s = value; s < send; s++) + { + register char c; + + c = *s; + if (c == '$') + { + if (inkeyword) + { + keywords = 1; + break; + } + inkeyword = 1; + } + else if (c == ':') + { + if (inkeyword) + { + keywords = 1; + break; + } + } + else if (inkeyword && ! isalpha ((unsigned char) c)) + inkeyword = 0; + } + } + + if (! keywords) + { + FILE *ofp; + + /* We have the text we want. */ + + if (workfile == NULL) + { + if (sout == RUN_TTY) + ofp = stdout; + else + { + ofp = CVS_FOPEN (sout, + strcmp (ouroptions, "b") == 0 ? "wb" : "w"); + if (ofp == NULL) + error (1, errno, "cannot open %s", sout); + } + } + else + { + ofp = CVS_FOPEN (workfile, + strcmp (ouroptions, "b") == 0 ? "wb" : "w"); + if (ofp == NULL) + error (1, errno, "cannot open %s", workfile); + } + + if (fwrite (value, 1, len, ofp) != len) + error (1, errno, "cannot write %s", + (workfile != NULL + ? workfile + : (sout != RUN_TTY ? sout : "stdout"))); + + if (workfile != NULL) + { + if (fclose (ofp) < 0) + error (1, errno, "cannot close %s", workfile); + if (chmod (workfile, + sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) + error (0, errno, "cannot change mode of file %s", + workfile); + } + else if (sout != RUN_TTY) + { + if (fclose (ofp) < 0) + error (1, errno, "cannot close %s", sout); + } + + if (free_value) + free (value); + if (free_rev) + free (rev); + + return 0; + } + + /* We were not able to optimize retrieving this revision. */ + +#if 0 + /* A bit of debugging code to make sure that NAMETAG corresponds + to REV. */ + if (nametag != NULL && strcmp (nametag, rev) != 0) + { + char *numtag; + + numtag = translate_symtag (rcs, nametag); + assert (rev != NULL && numtag != NULL && strcmp (numtag, rev) == 0); + free (numtag); + } +#endif + + if (free_value) + free (value); + + ret = RCS_exec_checkout (rcs->path, workfile, + nametag != NULL ? nametag : rev, + options, sout); + + if (free_rev) + free (rev); + + return ret; +} + +/* For RCS file RCS, make symbolic tag TAG point to revision REV. + This validates that TAG is OK for a user to use. Return value is + -1 for error (and errno is set to indicate the error), positive for + error (and an error message has been printed), or zero for success. */ + +int +RCS_settag (rcs, tag, rev) + RCSNode *rcs; + const char *tag; + const char *rev; +{ + int ret; + + /* FIXME: This check should be moved to RCS_check_tag. There is no + reason for it to be here. */ + if (strcmp (tag, TAG_BASE) == 0 + || strcmp (tag, TAG_HEAD) == 0) + { + /* Print the name of the tag might be considered redundant + with the caller, which also prints it. Perhaps this helps + clarify why the tag name is considered reserved, I don't + know. */ + error (0, 0, "Attempt to add reserved tag name %s", tag); + return 1; + } + + ret = RCS_exec_settag (rcs->path, tag, rev); + if (ret != 0) + return ret; + + /* If we have already parsed the RCS file, update the tag + information. If we have not yet parsed it (i.e., the PARTIAL + flag is set), the new tag information will be read when and if + we do parse it. */ + if ((rcs->flags & PARTIAL) == 0) + { + List *symbols; + Node *node; + + /* At this point rcs->symbol_data may not have been parsed. + Calling RCS_symbols will force it to be parsed into a list + which we can easily manipulate. */ + symbols = RCS_symbols (rcs); + if (symbols == NULL) + { + symbols = getlist (); + rcs->symbols = symbols; + } + node = findnode (symbols, tag); + if (node != NULL) + { + free (node->data); + node->data = xstrdup (rev); + } + else + { + node = getnode (); + node->key = xstrdup (tag); + node->data = xstrdup (rev); + (void) addnode (symbols, node); + } + } + + /* Setting the tag will most likely have invalidated delta_pos. */ + rcs->flags |= NODELTA; + + return 0; +} + +/* Delete the symbolic tag TAG from the RCS file RCS. NOERR is 1 to + suppress errors--FIXME it would be better to avoid the errors or + some cleaner solution. */ + +int +RCS_deltag (rcs, tag, noerr) + RCSNode *rcs; + const char *tag; + int noerr; +{ + int ret; + + ret = RCS_exec_deltag (rcs->path, tag, noerr); + if (ret != 0) + return ret; + + /* If we have already parsed the RCS file, update the tag + information. If we have not yet parsed it (i.e., the PARTIAL + flag is set), the new tag information will be read when and if + we do parse it. */ + if ((rcs->flags & PARTIAL) == 0) + { + List *symbols; + + /* At this point rcs->symbol_data may not have been parsed. + Calling RCS_symbols will force it to be parsed into a list + which we can easily manipulate. */ + symbols = RCS_symbols (rcs); + if (symbols != NULL) + { + Node *node; + + node = findnode (symbols, tag); + if (node != NULL) + delnode (node); + } + } + + /* Deleting the tag will most likely have invalidated delta_pos. */ + rcs->flags |= NODELTA; + + return 0; +} + +/* Set the default branch of RCS to REV. */ + +int +RCS_setbranch (rcs, rev) + RCSNode *rcs; + const char *rev; +{ + int ret; + + if (rev == NULL && rcs->branch == NULL) + return 0; + if (rev != NULL && rcs->branch != NULL && strcmp (rev, rcs->branch) == 0) + return 0; + + ret = RCS_exec_setbranch (rcs->path, rev); + if (ret != 0) + return ret; + + if (rcs->branch != NULL) + free (rcs->branch); + rcs->branch = xstrdup (rev); + + /* Changing the branch will have changed the data in the file, so + delta_pos will no longer be correct. */ + rcs->flags |= NODELTA; + + return 0; +} + +/* Lock revision REV. NOERR is 1 to suppress errors--FIXME it would + be better to avoid the errors or some cleaner solution. FIXME: + This is only required because the RCS ci program requires a lock. + If we eventually do the checkin ourselves, this can become a no-op. */ + +int +RCS_lock (rcs, rev, noerr) + RCSNode *rcs; + const char *rev; + int noerr; +{ + int ret; + + ret = RCS_exec_lock (rcs->path, rev, noerr); + if (ret != 0) + return ret; + + /* Setting a lock will have changed the data in the file, so + delta_pos will no longer be correct. */ + rcs->flags |= NODELTA; + + return 0; +} + +/* Unlock revision REV. NOERR is 1 to suppress errors--FIXME it would + be better to avoid the errors or some cleaner solution. FIXME: + Like RCS_lock, this can become a no-op if we do the checkin + ourselves. */ + +int +RCS_unlock (rcs, rev, noerr) + RCSNode *rcs; + const char *rev; + int noerr; +{ + int ret; + + ret = RCS_exec_unlock (rcs->path, rev, noerr); + if (ret != 0) + return ret; + + /* Setting a lock will have changed the data in the file, so + delta_pos will no longer be correct. */ + rcs->flags |= NODELTA; + + return 0; +} -/* Stuff related to annotate command. This should perhaps be split - into the stuff which knows about the guts of RCS files, and the - command parsing type stuff. */ +/* RCS_deltas and friends. Processing of the deltas in RCS files. */ /* Linked list of allocated blocks. Seems kind of silly to reinvent the obstack wheel, and this isn't as nice as obstacks @@ -1760,8 +2600,10 @@ block_free () struct line { - /* Text of this line, terminated by \n or \0. */ + /* Text of this line. */ char *text; + /* Length of this line, not counting \n if has_newline is true. */ + size_t len; /* Version in which it was introduced. */ RCSVers *vers; /* Nonzero if this line ends with \n. This will always be true @@ -1786,37 +2628,43 @@ static void linevector_init (vec) struct linevector *vec; { - vec->lines_alloced = 10; + vec->lines_alloced = 0; vec->nlines = 0; - vec->vector = (struct line **) - xmalloc (vec->lines_alloced * sizeof (*vec->vector)); + vec->vector = NULL; } static void linevector_add PROTO ((struct linevector *vec, char *text, - RCSVers *vers, unsigned int pos)); + size_t len, RCSVers *vers, + unsigned int pos)); /* Given some text TEXT, add each of its lines to VEC before line POS (where line 0 is the first line). The last line in TEXT may or may - not be \n terminated. All \n in TEXT are changed to \0. Set the - version for each of the new lines to VERS. */ + not be \n terminated. All \n in TEXT are changed to \0 (FIXME: I + don't think this is needed, or used, now that we have the ->len + field). Set the version for each of the new lines to VERS. */ static void -linevector_add (vec, text, vers, pos) +linevector_add (vec, text, len, vers, pos) struct linevector *vec; char *text; + size_t len; RCSVers *vers; unsigned int pos; { + char *textend; unsigned int i; unsigned int nnew; char *p; struct line *lines; - assert (vec->lines_alloced > 0); + if (len == 0) + return; + + textend = text + len; /* Count the number of lines we will need to add. */ nnew = 1; - for (p = text; *p != '\0'; ++p) - if (*p == '\n' && p[1] != '\0') + for (p = text; p < textend; ++p) + if (*p == '\n' && p + 1 < textend) ++nnew; /* Allocate the struct line's. */ lines = block_alloc (nnew * sizeof (struct line)); @@ -1824,6 +2672,8 @@ linevector_add (vec, text, vers, pos) /* Expand VEC->VECTOR if needed. */ if (vec->nlines + nnew >= vec->lines_alloced) { + if (vec->lines_alloced == 0) + vec->lines_alloced = 10; while (vec->nlines + nnew >= vec->lines_alloced) vec->lines_alloced *= 2; vec->vector = xrealloc (vec->vector, @@ -1843,21 +2693,23 @@ linevector_add (vec, text, vers, pos) lines[0].vers = vers; lines[0].has_newline = 0; vec->vector[i++] = &lines[0]; - for (p = text; *p != '\0'; ++p) + for (p = text; p < textend; ++p) if (*p == '\n') { *p = '\0'; lines[i - pos - 1].has_newline = 1; - if (p[1] == '\0') + if (p + 1 == textend) /* If there are no characters beyond the last newline, we don't consider it another line. */ break; + lines[i - pos - 1].len = p - lines[i - pos - 1].text; lines[i - pos].text = p + 1; lines[i - pos].vers = vers; lines[i - pos].has_newline = 0; vec->vector[i] = &lines[i - pos]; ++i; } + lines[i - pos - 1].len = p - lines[i - pos - 1].text; vec->nlines += nnew; } @@ -1891,6 +2743,8 @@ linevector_copy (to, from) { if (from->nlines > to->lines_alloced) { + if (to->lines_alloced == 0) + to->lines_alloced = 10; while (from->nlines > to->lines_alloced) to->lines_alloced *= 2; to->vector = (struct line **) @@ -1909,7 +2763,8 @@ static void linevector_free (vec) struct linevector *vec; { - free (vec->vector); + if (vec->vector != NULL) + free (vec->vector); } static char *month_printname PROTO ((char *)); @@ -1935,54 +2790,123 @@ month_printname (month) return (char *)months[mnum - 1]; } -static int annotate_fileproc PROTO ((struct file_info *)); +/* Walk the deltas in RCS to get to revision VERSION. -static int -annotate_fileproc (finfo) - struct file_info *finfo; -{ + If OP is RCS_ANNOTATE, then write annotations using cvs_output. + + If OP is RCS_FETCH, then put the contents of VERSION into a + newly-malloc'd array and put a pointer to it in *TEXT. Each line + is \n terminated; the caller is responsible for converting text + files if desired. The total length is put in *LEN. + + If FP is non-NULL, it should be a file descriptor open to the file + RCS with file position pointing to the deltas. We close the file + when we are done. + + On error, give a fatal error. */ + +static void +RCS_deltas (rcs, fp, version, op, text, len) + RCSNode *rcs; FILE *fp; + char *version; + enum rcs_delta_op op; + char **text; + size_t *len; +{ + char *branchversion; + char *cpversion; char *key; char *value; + size_t vallen; RCSVers *vers; RCSVers *prev_vers; + RCSVers *trunk_vers; + char *next; int n; - int ishead; + int ishead, isnext, isversion, onbranch; Node *node; struct linevector headlines; struct linevector curlines; + struct linevector trunklines; + int foundhead; - if (finfo->rcs == NULL) - return (1); - - /* Distinguish output for various files if we are processing - several files. */ - cvs_outerr ("Annotations for ", 0); - cvs_outerr (finfo->fullname, 0); - cvs_outerr ("\n***************\n", 0); - - if (!(finfo->rcs->flags & PARTIAL)) - /* We are leaking memory by calling RCS_reparsefile again. */ - error (0, 0, "internal warning: non-partial rcs in annotate_fileproc"); - RCS_reparsercsfile (finfo->rcs, &fp); + if (fp == NULL) + { + if (rcs->flags & NODELTA) + { + free_rcsnode_contents (rcs); + RCS_reparsercsfile (rcs, 0, &fp); + } + else + { + fp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcs->path); + if (fseek (fp, rcs->delta_pos, SEEK_SET) != 0) + error (1, 0, "cannot fseek RCS file"); + } + } ishead = 1; vers = NULL; + prev_vers = NULL; + trunk_vers = NULL; + next = NULL; + onbranch = 0; + foundhead = 0; + + linevector_init (&curlines); + linevector_init (&headlines); + linevector_init (&trunklines); + + /* We set BRANCHVERSION to the version we are currently looking + for. Initially, this is the version on the trunk from which + VERSION branches off. If VERSION is not a branch, then + BRANCHVERSION is just VERSION. */ + branchversion = xstrdup (version); + cpversion = strchr (branchversion, '.'); + if (cpversion != NULL) + cpversion = strchr (cpversion + 1, '.'); + if (cpversion != NULL) + *cpversion = '\0'; do { getrcsrev (fp, &key); - /* Stash the previous version. */ - prev_vers = vers; + if (next != NULL && strcmp (next, key) != 0) + { + /* This is not the next version we need. It is a branch + version which we want to ignore. */ + isnext = 0; + isversion = 0; + } + else + { + isnext = 1; + + /* look up the revision */ + node = findnode (rcs->versions, key); + if (node == NULL) + error (1, 0, + "mismatch in rcs file %s between deltas and deltatexts", + rcs->path); - /* look up the revision */ - node = findnode (finfo->rcs->versions, key); - if (node == NULL) - error (1, 0, "mismatch in rcs file %s between deltas and deltatexts", - finfo->rcs->path); - vers = (RCSVers *) node->data; + /* Stash the previous version. */ + prev_vers = vers; - while ((n = getrcskey (fp, &key, &value)) >= 0) + vers = (RCSVers *) node->data; + next = vers->next; + + /* Compare key and trunkversion now, because key points to + storage controlled by getrcskey. */ + if (strcmp (branchversion, key) == 0) + isversion = 1; + else + isversion = 0; + } + + while ((n = getrcskey (fp, &key, &value, &vallen)) >= 0) { if (strcmp (key, "text") == 0) { @@ -1990,16 +2914,13 @@ annotate_fileproc (finfo) { char *p; - p = block_alloc (strlen (value) + 1); - strcpy (p, value); + p = block_alloc (vallen); + memcpy (p, value, vallen); - linevector_init (&headlines); - linevector_init (&curlines); - linevector_add (&headlines, p, NULL, 0); - linevector_copy (&curlines, &headlines); + linevector_add (&curlines, p, vallen, NULL, 0); ishead = 0; } - else + else if (isnext) { char *p; char *q; @@ -2013,20 +2934,21 @@ annotate_fileproc (finfo) unsigned long pos; unsigned long nlines; char *new_lines; + size_t len; struct deltafrag *next; }; struct deltafrag *dfhead; struct deltafrag *df; dfhead = NULL; - for (p = value; p != NULL && *p != '\0'; ) + for (p = value; p != NULL && p < value + vallen; ) { op = *p++; if (op != 'a' && op != 'd') /* Can't just skip over the deltafrag, because the value of op determines the syntax. */ error (1, 0, "unrecognized operation '%c' in %s", - op, finfo->rcs->path); + op, rcs->path); df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag)); df->next = dfhead; @@ -2035,19 +2957,19 @@ annotate_fileproc (finfo) if (p == q) error (1, 0, "number expected in %s", - finfo->rcs->path); + rcs->path); p = q; if (*p++ != ' ') error (1, 0, "space expected in %s", - finfo->rcs->path); + rcs->path); df->nlines = strtoul (p, &q, 10); if (p == q) error (1, 0, "number expected in %s", - finfo->rcs->path); + rcs->path); p = q; if (*p++ != '\012') error (1, 0, "linefeed expected in %s", - finfo->rcs->path); + rcs->path); if (op == 'a') { @@ -2063,21 +2985,21 @@ annotate_fileproc (finfo) for (q = p; i != 0; ++q) if (*q == '\n') --i; - else if (*q == '\0') + else if (q == value + vallen) { if (i != 1) error (1, 0, "\ invalid rcs file %s: premature end of value", - finfo->rcs->path); + rcs->path); else break; } /* Copy the text we are adding into allocated space. */ - df->new_lines = block_alloc (q - p + 1); - strncpy (df->new_lines, p, q - p); - df->new_lines[q - p] = '\0'; + df->new_lines = block_alloc (q - p); + memcpy (df->new_lines, p, q - p); + df->len = q - p; p = q; } @@ -2099,16 +3021,21 @@ invalid rcs file %s: premature end of value", { case ADD: linevector_add (&curlines, df->new_lines, - NULL, df->pos); + df->len, + onbranch ? vers : NULL, + df->pos); break; case DELETE: if (df->pos > curlines.nlines || df->pos + df->nlines > curlines.nlines) error (1, 0, "\ invalid rcs file %s (`d' operand out of range)", - finfo->rcs->path); - for (ln = df->pos; ln < df->pos + df->nlines; ++ln) - curlines.vector[ln]->vers = prev_vers; + rcs->path); + if (! onbranch) + for (ln = df->pos; + ln < df->pos + df->nlines; + ++ln) + curlines.vector[ln]->vers = prev_vers; linevector_delete (&curlines, df->pos, df->nlines); break; } @@ -2122,92 +3049,247 @@ invalid rcs file %s (`d' operand out of range)", } if (n < 0) goto l_error; - } while (vers->next != NULL); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", finfo->rcs->path); + if (isversion) + { + /* This is either the version we want, or it is the + branchpoint to the version we want. */ + if (strcmp (branchversion, version) == 0) + { + /* This is the version we want. */ + linevector_copy (&headlines, &curlines); + foundhead = 1; + if (onbranch) + { + /* We have found this version by tracking up a + branch. Restore back to the lines we saved + when we left the trunk, and continue tracking + down the trunk. */ + onbranch = 0; + vers = trunk_vers; + next = vers->next; + linevector_copy (&curlines, &trunklines); + } + } + else + { + Node *p; - /* Now print out the data we have just computed. */ - { - unsigned int ln; + /* We need to look up the branch. */ + onbranch = 1; - for (ln = 0; ln < headlines.nlines; ++ln) - { - char buf[80]; - /* Period which separates year from month in date. */ - char *ym; - /* Period which separates month from day in date. */ - char *md; - RCSVers *prvers; + if (numdots (branchversion) < 2) + { + unsigned int ln; + + /* We are leaving the trunk; save the current + lines so that we can restore them when we + continue tracking down the trunk. */ + trunk_vers = vers; + linevector_copy (&trunklines, &curlines); + + /* Reset the version information we have + accumulated so far. It only applies to the + changes from the head to this version. */ + for (ln = 0; ln < curlines.nlines; ++ln) + curlines.vector[ln]->vers = NULL; + } - prvers = headlines.vector[ln]->vers; - if (prvers == NULL) - prvers = vers; + /* The next version we want is the entry on + VERS->branches which matches this branch. For + example, suppose VERSION is 1.21.4.3 and + BRANCHVERSION was 1.21. Then we look for an entry + starting with "1.21.4" and we'll put it (probably + 1.21.4.1) in NEXT. We'll advance BRANCHVERSION by + two dots (in this example, to 1.21.4.3). */ + + if (vers->branches == NULL) + error (1, 0, "missing expected branches in %s", + rcs->path); + *cpversion = '.'; + ++cpversion; + cpversion = strchr (cpversion, '.'); + if (cpversion == NULL) + error (1, 0, "version number confusion in %s", + rcs->path); + for (p = vers->branches->list->next; + p != vers->branches->list; + p = p->next) + if (strncmp (p->key, branchversion, + cpversion - branchversion) == 0) + break; + if (p == vers->branches->list) + error (1, 0, "missing expected branch in %s", + rcs->path); - sprintf (buf, "%-12s (%-8.8s ", - prvers->version, - prvers->author); - cvs_output (buf, 0); + next = p->key; - /* Now output the date. */ - ym = strchr (prvers->date, '.'); - if (ym == NULL) - cvs_output ("??-???-??", 0); - else - { - md = strchr (ym + 1, '.'); - if (md == NULL) - cvs_output ("??", 0); - else - cvs_output (md + 1, 2); - - cvs_output ("-", 1); - cvs_output (month_printname (ym + 1), 0); - cvs_output ("-", 1); - /* Only output the last two digits of the year. Our output - lines are long enough as it is without printing the - century. */ - cvs_output (ym - 2, 2); + cpversion = strchr (cpversion + 1, '.'); + if (cpversion != NULL) + *cpversion = '\0'; } - cvs_output ("): ", 0); - cvs_output (headlines.vector[ln]->text, 0); - cvs_output ("\n", 1); } - } + if (op == RCS_FETCH && foundhead) + break; + } while (next != NULL); + + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", rcs->path); - if (!ishead) + if (! foundhead) + error (1, 0, "could not find desired version %s in %s", + version, rcs->path); + + /* Now print out or return the data we have just computed. */ + switch (op) { - linevector_free (&curlines); - linevector_free (&headlines); + case RCS_ANNOTATE: + { + unsigned int ln; + + for (ln = 0; ln < headlines.nlines; ++ln) + { + char buf[80]; + /* Period which separates year from month in date. */ + char *ym; + /* Period which separates month from day in date. */ + char *md; + RCSVers *prvers; + + prvers = headlines.vector[ln]->vers; + if (prvers == NULL) + prvers = vers; + + sprintf (buf, "%-12s (%-8.8s ", + prvers->version, + prvers->author); + cvs_output (buf, 0); + + /* Now output the date. */ + ym = strchr (prvers->date, '.'); + if (ym == NULL) + cvs_output ("??-???-??", 0); + else + { + md = strchr (ym + 1, '.'); + if (md == NULL) + cvs_output ("??", 0); + else + cvs_output (md + 1, 2); + + cvs_output ("-", 1); + cvs_output (month_printname (ym + 1), 0); + cvs_output ("-", 1); + /* Only output the last two digits of the year. Our output + lines are long enough as it is without printing the + century. */ + cvs_output (ym - 2, 2); + } + cvs_output ("): ", 0); + cvs_output (headlines.vector[ln]->text, + headlines.vector[ln]->len); + cvs_output ("\n", 1); + } + } + break; + case RCS_FETCH: + { + char *p; + size_t n; + unsigned int ln; + + assert (text != NULL); + assert (len != NULL); + + n = 0; + for (ln = 0; ln < headlines.nlines; ++ln) + /* 1 for \n */ + n += headlines.vector[ln]->len + 1; + p = xmalloc (n); + *text = p; + for (ln = 0; ln < headlines.nlines; ++ln) + { + memcpy (p, headlines.vector[ln]->text, + headlines.vector[ln]->len); + p += headlines.vector[ln]->len; + if (headlines.vector[ln]->has_newline) + *p++ = '\n'; + } + *len = p - *text; + assert (*len <= n); + } + break; } + + linevector_free (&curlines); + linevector_free (&headlines); + linevector_free (&trunklines); + block_free (); - return 0; + return; l_error: if (ferror (fp)) - error (1, errno, "cannot read %s", finfo->rcs->path); + error (1, errno, "cannot read %s", rcs->path); else error (1, 0, "%s does not appear to be a valid rcs file", - finfo->rcs->path); - /* Shut up gcc -Wall. */ + rcs->path); +} + + +/* Annotate command. In rcs.c for historical reasons (from back when + what is now RCS_deltas was part of annotate_fileproc). */ + +/* Options from the command line. */ + +static int force_tag_match = 1; +static char *tag = NULL; +static char *date = NULL; + +static int annotate_fileproc PROTO ((void *callerdat, struct file_info *)); + +static int +annotate_fileproc (callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + FILE *fp = NULL; + char *version; + + if (finfo->rcs == NULL) + return (1); + + if (finfo->rcs->flags & PARTIAL) + RCS_reparsercsfile (finfo->rcs, 0, &fp); + + version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, + (int *) NULL); + if (version == NULL) + return 0; + + /* Distinguish output for various files if we are processing + several files. */ + cvs_outerr ("Annotations for ", 0); + cvs_outerr (finfo->fullname, 0); + cvs_outerr ("\n***************\n", 0); + + RCS_deltas (finfo->rcs, fp, version, RCS_ANNOTATE, NULL, NULL); + free (version); return 0; } static const char *const annotate_usage[] = { - "Usage: %s %s [-l] [files...]\n", + "Usage: %s %s [-lf] [-r rev|-D date] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", + "\t-f\tUse head revision if tag/date not found.\n", + "\t-r rev\tAnnotate file as of specified revision/tag.\n", + "\t-D date\tAnnotate file as of specified date.\n", NULL }; /* Command to show the revision, date, and author where each line of a - file was modified. Currently it will only show the trunk, all the - way to the head, but it would be useful to enhance it to (a) allow - one to specify a revision, and display only as far as that (easy; - just have annotate_fileproc set all the ->vers fields to NULL when - you hit that revision), and (b) handle branches (not as easy, but - doable). The user interface for both (a) and (b) could be a -r - option. */ + file was modified. */ int annotate (argc, argv) @@ -2221,13 +3303,22 @@ annotate (argc, argv) usage (annotate_usage); optind = 0; - while ((c = getopt (argc, argv, "+l")) != -1) + while ((c = getopt (argc, argv, "+lr:D:f")) != -1) { switch (c) { case 'l': local = 1; break; + case 'r': + tag = optarg; + break; + case 'D': + date = Make_Date (optarg); + break; + case 'f': + force_tag_match = 0; + break; case '?': default: usage (annotate_usage); @@ -2245,6 +3336,11 @@ annotate (argc, argv) if (local) send_arg ("-l"); + if (!force_tag_match) + send_arg ("-f"); + option_with_arg ("-r", tag); + if (date) + client_senddate (date); send_file_names (argc, argv, SEND_EXPAND_WILD); /* FIXME: We shouldn't have to send current files, but I'm not sure whether it works. So send the files -- @@ -2256,7 +3352,7 @@ annotate (argc, argv) #endif /* CLIENT_SUPPORT */ return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 1, 0); + 1); } diff --git a/gnu/usr.bin/cvs/src/rcs.h b/gnu/usr.bin/cvs/src/rcs.h index 698a3b17c04..91175309833 100644 --- a/gnu/usr.bin/cvs/src/rcs.h +++ b/gnu/usr.bin/cvs/src/rcs.h @@ -13,7 +13,6 @@ #define RCS "rcs" #define RCS_CI "ci" #define RCS_CO "co" -#define RCS_RLOG "rlog" #define RCS_DIFF "rcsdiff" #define RCS_RCSMERGE "rcsmerge" #define RCS_MERGE_PAT "^>>>>>>> " /* runs "grep" with this pattern */ @@ -41,6 +40,7 @@ #define VALID 0x1 /* flags field contains valid data */ #define INATTIC 0x2 /* RCS file is located in the Attic */ #define PARTIAL 0x4 /* RCS file not completly parsed */ +#define NODELTA 0x8 /* delta_pos no longer valid */ struct rcsnode { @@ -53,6 +53,8 @@ struct rcsnode char *expand; List *symbols; List *versions; + long delta_pos; + List *other; }; typedef struct rcsnode RCSNode; @@ -65,6 +67,7 @@ struct rcsversnode char *next; int dead; List *branches; + List *other; }; typedef struct rcsversnode RCSVers; @@ -82,12 +85,13 @@ typedef struct rcsversnode RCSVers; */ RCSNode *RCS_parse PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); +void RCS_fully_parse PROTO((RCSNode *)); 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 *symtag, int force_tag_match, - int return_both)); + int *simple_tag)); char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date, - int force_tag_match, int return_both)); + int force_tag_match, int *simple_tag)); char *RCS_magicrev PROTO((RCSNode *rcs, char *rev)); int RCS_isbranch PROTO((RCSNode *rcs, const char *rev)); int RCS_nodeisbranch PROTO((RCSNode *rcs, const char *tag)); @@ -102,3 +106,9 @@ char *RCS_getbranch PROTO((RCSNode * rcs, char *tag, int force_tag_match)); int RCS_isdead PROTO((RCSNode *, const char *)); char *RCS_getexpand PROTO ((RCSNode *)); +int RCS_checkout PROTO ((RCSNode *, char *, char *, char *, char *, char *)); +int RCS_settag PROTO ((RCSNode *, const char *, const char *)); +int RCS_deltag PROTO ((RCSNode *, const char *, int)); +int RCS_setbranch PROTO((RCSNode *, const char *)); +int RCS_lock PROTO ((RCSNode *, const char *, int)); +int RCS_unlock PROTO ((RCSNode *, const char *, int)); diff --git a/gnu/usr.bin/cvs/src/recurse.c b/gnu/usr.bin/cvs/src/recurse.c index ec51a9802a8..191156c1ae9 100644 --- a/gnu/usr.bin/cvs/src/recurse.c +++ b/gnu/usr.bin/cvs/src/recurse.c @@ -19,19 +19,6 @@ static void addlist PROTO((List ** listp, char *key)); static int unroll_files_proc PROTO((Node *p, void *closure)); static void addfile PROTO((List **listp, char *dir, char *file)); - -/* - * Local static versions eliminates the need for globals - */ -static FILEPROC fileproc; -static FILESDONEPROC filesdoneproc; -static DIRENTPROC direntproc; -static DIRLEAVEPROC dirleaveproc; -static int which; -static Dtype flags; -static int aflag; -static int readlock; -static int dosrcs; static char update_dir[PATH_MAX]; static char *repository = NULL; static List *filelist = NULL; /* holds list of files on which to operate */ @@ -42,6 +29,7 @@ struct recursion_frame { FILESDONEPROC filesdoneproc; DIRENTPROC direntproc; DIRLEAVEPROC dirleaveproc; + void *callerdat; Dtype flags; int which; int aflag; @@ -49,38 +37,89 @@ struct recursion_frame { int dosrcs; }; -/* - * Called to start a recursive command. - * - * Command line arguments dictate the directories and files on which - * we operate. In the special case of no arguments, we default to - * ".". - * - * The general algorithm is as follows. - */ +static int do_recursion PROTO ((struct recursion_frame *frame)); + +/* I am half tempted to shove a struct file_info * into the struct + recursion_frame (but then we would need to modify or create a + recursion_frame for each file), or shove a struct recursion_frame * + into the struct file_info (more tempting, although it isn't completely + clear that the struct file_info should contain info about recursion + processor internals). So instead use this struct. */ + +struct frame_and_file { + struct recursion_frame *frame; + struct file_info *finfo; +}; + +/* Similarly, we need to pass the entries list to do_dir_proc. */ + +struct frame_and_entries { + struct recursion_frame *frame; + List *entries; +}; + +/* Start a recursive command. + + Command line arguments (ARGC, ARGV) dictate the directories and + files on which we operate. In the special case of no arguments, we + default to ".". */ int -start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, +start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, argc, argv, local, which, aflag, readlock, - update_preload, dosrcs, wd_is_repos) + update_preload, dosrcs) FILEPROC fileproc; FILESDONEPROC filesdoneproc; DIRENTPROC direntproc; DIRLEAVEPROC dirleaveproc; + void *callerdat; + int argc; char **argv; int local; + + /* This specifies the kind of recursion. There are several cases: + + 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current + directory when we are called must be the repository and + recursion proceeds according to what exists in the repository. + + 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The + current directory when we are called must be the working + directory. Recursion proceeds according to what exists in the + working directory, never (I think) consulting any part of the + repository which does not correspond to the working directory + ("correspond" == Name_Repository). + + 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the + weird one. The current directory when we are called must be + the working directory. We recurse through working directories, + but we recurse into a directory if it is exists in the working + directory *or* it exists in the repository. If a directory + does not exist in the working directory, the direntproc must + either tell us to skip it (R_SKIP_ALL), or must create it (I + think those are the only two cases). */ int which; + int aflag; int readlock; char *update_preload; int dosrcs; - int wd_is_repos; /* Set if caller has already cd'd to the repository */ { int i, err = 0; - Dtype flags; List *files_by_dir = NULL; struct recursion_frame frame; + frame.fileproc = fileproc; + frame.filesdoneproc = filesdoneproc; + frame.direntproc = direntproc; + frame.dirleaveproc = dirleaveproc; + frame.callerdat = callerdat; + frame.flags = local ? R_SKIP_DIRS : R_PROCESS; + frame.which = which; + frame.aflag = aflag; + frame.readlock = readlock; + frame.dosrcs = dosrcs; + expand_wild (argc, argv, &argc, &argv); if (update_preload == NULL) @@ -88,11 +127,6 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, else (void) strcpy (update_dir, update_preload); - if (local) - flags = R_SKIP_DIRS; - else - flags = R_PROCESS; - /* clean up from any previous calls to start_recursion */ if (repository) { @@ -116,13 +150,11 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, * called with the list of sub-dirs of the current dir as args */ if ((which & W_LOCAL) && !isdir (CVSADM)) - dirlist = Find_Directories ((char *) NULL, W_LOCAL); + dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL); else addlist (&dirlist, "."); - err += do_recursion (fileproc, filesdoneproc, direntproc, - dirleaveproc, flags, which, aflag, - readlock, dosrcs); + err += do_recursion (&frame); return(err); } @@ -174,20 +206,20 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, /* if this argument exists as a file in the current working directory tree, then add it to the files list. */ - if (wd_is_repos) + if (!(which & W_LOCAL)) { /* If doing rtag, we've done a chdir to the repository. */ sprintf (tmp, "%s%s", argv[i], RCSEXT); file_to_try = tmp; } else - file_to_try = argv[i]; + file_to_try = argv[i]; if(isfile(file_to_try)) addfile (&files_by_dir, dir, comp); else if (isdir (dir)) { - if (isdir (CVSADM)) + if ((which & W_LOCAL) && isdir (CVSADM)) { /* otherwise, look for it in the repository. */ char *save_update_dir; @@ -229,23 +261,11 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, a coupla lists. Now we unroll the lists, setting up and calling do_recursion. */ - frame.fileproc = fileproc; - frame.filesdoneproc = filesdoneproc; - frame.direntproc = direntproc; - frame.dirleaveproc = dirleaveproc; - frame.flags = flags; - frame.which = which; - frame.aflag = aflag; - frame.readlock = readlock; - frame.dosrcs = dosrcs; err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); /* then do_recursion on the dirlist. */ if (dirlist != NULL) - err += do_recursion (frame.fileproc, frame.filesdoneproc, - frame.direntproc, frame.dirleaveproc, - frame.flags, frame.which, frame.aflag, - frame.readlock, frame.dosrcs); + err += do_recursion (&frame); /* Free the data which expand_wild allocated. */ for (i = 0; i < argc; ++i) @@ -259,38 +279,21 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, * Implement the recursive policies on the local directory. This may be * called directly, or may be called by start_recursion */ -int -do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, - xflags, xwhich, xaflag, xreadlock, xdosrcs) - FILEPROC xfileproc; - FILESDONEPROC xfilesdoneproc; - DIRENTPROC xdirentproc; - DIRLEAVEPROC xdirleaveproc; - Dtype xflags; - int xwhich; - int xaflag; - int xreadlock; - int xdosrcs; +static int +do_recursion (frame) + struct recursion_frame *frame; { int err = 0; int dodoneproc = 1; char *srepository; List *entries = NULL; + int should_readlock; /* do nothing if told */ - if (xflags == R_SKIP_ALL) + if (frame->flags == R_SKIP_ALL) return (0); - /* set up the static vars */ - fileproc = xfileproc; - filesdoneproc = xfilesdoneproc; - direntproc = xdirentproc; - dirleaveproc = xdirleaveproc; - flags = xflags; - which = xwhich; - aflag = xaflag; - readlock = noexec ? 0 : xreadlock; - dosrcs = xdosrcs; + should_readlock = noexec ? 0 : frame->readlock; /* The fact that locks are not active here is what makes us fail to have the @@ -332,14 +335,14 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, */ if (server_active /* If there are writelocks around, we cannot pause here. */ - && (readlock || noexec)) + && (should_readlock || noexec)) server_pause_check(); #endif /* * Fill in repository with the current repository */ - if (which & W_LOCAL) + if (frame->which & W_LOCAL) { if (isdir (CVSADM)) repository = Name_Repository ((char *) NULL, update_dir); @@ -373,41 +376,52 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, if (filelist == NULL && dirlist == NULL) { /* both lists were NULL, so start from scratch */ - if (fileproc != NULL && flags != R_SKIP_FILES) + if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) { - int lwhich = which; + int lwhich = frame->which; /* be sure to look in the attic if we have sticky tags/date */ if ((lwhich & W_ATTIC) == 0) if (isreadable (CVSADM_TAG)) lwhich |= W_ATTIC; + /* In the !(which & W_LOCAL) case, we filled in repository + earlier in the function. In the (which & W_LOCAL) case, + the Find_Names function is going to look through the + Entries file. If we do not have a repository, that + does not make sense, so we insist upon having a + repository at this point. Name_Repository will give a + reasonable error message. */ + if (repository == NULL) + repository = Name_Repository ((char *) NULL, update_dir); + /* find the files and fill in entries if appropriate */ - filelist = Find_Names (repository, lwhich, aflag, &entries); + filelist = Find_Names (repository, lwhich, frame->aflag, &entries); } /* find sub-directories if we will recurse */ - if (flags != R_SKIP_DIRS) - dirlist = Find_Directories (repository, which); + if (frame->flags != R_SKIP_DIRS) + dirlist = Find_Directories (repository, frame->which, entries); } else { /* something was passed on the command line */ - if (filelist != NULL && fileproc != NULL) + if (filelist != NULL && frame->fileproc != NULL) { /* we will process files, so pre-parse entries */ - if (which & W_LOCAL) - entries = Entries_Open (aflag); + if (frame->which & W_LOCAL) + entries = Entries_Open (frame->aflag); } } /* process the files (if any) */ - if (filelist != NULL && fileproc) + if (filelist != NULL && frame->fileproc) { struct file_info finfo_struct; + struct frame_and_file frfile; /* read lock it if necessary */ - if (readlock && repository && Reader_Lock (repository) != 0) + if (should_readlock && repository && Reader_Lock (repository) != 0) error (1, 0, "read lock failed - giving up"); #ifdef CLIENT_SUPPORT @@ -424,39 +438,50 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, finfo_struct.entries = entries; /* do_file_proc will fill in finfo_struct.file. */ + frfile.finfo = &finfo_struct; + frfile.frame = frame; + /* process the files */ - err += walklist (filelist, do_file_proc, &finfo_struct); + err += walklist (filelist, do_file_proc, &frfile); /* unlock it */ - if (readlock) + if (should_readlock) Lock_Cleanup (); /* clean up */ dellist (&filelist); } - if (entries) - { - Entries_Close (entries); - entries = NULL; - } - /* call-back files done proc (if any) */ - if (dodoneproc && filesdoneproc != NULL) - err = filesdoneproc (err, repository, update_dir[0] ? update_dir : "."); + if (dodoneproc && frame->filesdoneproc != NULL) + err = frame->filesdoneproc (frame->callerdat, err, repository, + update_dir[0] ? update_dir : ".", + entries); fileattr_write (); fileattr_free (); /* process the directories (if necessary) */ if (dirlist != NULL) - err += walklist (dirlist, do_dir_proc, NULL); -#ifdef notdef - else if (dirleaveproc != NULL) - err += dirleaveproc(".", err, "."); + { + struct frame_and_entries frent; + + frent.frame = frame; + frent.entries = entries; + err += walklist (dirlist, do_dir_proc, (void *) &frent); + } +#if 0 + else if (frame->dirleaveproc != NULL) + err += frame->dirleaveproc (frame->callerdat, ".", err, "."); #endif dellist (&dirlist); + if (entries) + { + Entries_Close (entries); + entries = NULL; + } + /* free the saved copy of the pointer if necessary */ if (srepository) { @@ -475,7 +500,8 @@ do_file_proc (p, closure) Node *p; void *closure; { - struct file_info *finfo = (struct file_info *)closure; + struct frame_and_file *frfile = (struct frame_and_file *)closure; + struct file_info *finfo = frfile->finfo; int ret; finfo->file = p->key; @@ -490,11 +516,11 @@ do_file_proc (p, closure) } strcat (finfo->fullname, finfo->file); - if (dosrcs && repository) + if (frfile->frame->dosrcs && repository) finfo->rcs = RCS_parse (finfo->file, repository); else finfo->rcs = (RCSNode *) NULL; - ret = fileproc (finfo); + ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); freercsnode(&finfo->rcs); free (finfo->fullname); @@ -510,6 +536,9 @@ do_dir_proc (p, closure) Node *p; void *closure; { + struct frame_and_entries *frent = (struct frame_and_entries *) closure; + struct recursion_frame *frame = frent->frame; + struct recursion_frame xframe; char *dir = p->key; char newrepos[PATH_MAX]; List *sdirlist; @@ -555,8 +584,9 @@ do_dir_proc (p, closure) } /* call-back dir entry proc (if any) */ - if (direntproc != NULL) - dir_return = direntproc (dir, newrepos, update_dir); + if (frame->direntproc != NULL) + dir_return = frame->direntproc (frame->callerdat, dir, newrepos, + update_dir, frent->entries); /* only process the dir if the return code was 0 */ if (dir_return != R_SKIP_ALL) @@ -569,11 +599,11 @@ do_dir_proc (p, closure) dirlist = NULL; /* cd to the sub-directory */ - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) error (1, errno, "could not chdir to %s", dir); /* honor the global SKIP_DIRS (a.k.a. local) */ - if (flags == R_SKIP_DIRS) + if (frame->flags == R_SKIP_DIRS) dir_return = R_SKIP_DIRS; /* remember if the `.' will be stripped for subsequent dirs */ @@ -584,16 +614,18 @@ do_dir_proc (p, closure) } /* make the recursive call */ - err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, - dir_return, which, aflag, readlock, dosrcs); + xframe = *frame; + xframe.flags = dir_return; + err += do_recursion (&xframe); /* put the `.' back if necessary */ if (stripped_dot) (void) strcpy (update_dir, "."); /* call-back dir leave proc (if any) */ - if (dirleaveproc != NULL) - err = dirleaveproc (dir, err, update_dir); + if (frame->dirleaveproc != NULL) + err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, + frent->entries); /* get back to where we started and restore state vars */ if (restore_cwd (&cwd, NULL)) @@ -682,7 +714,7 @@ unroll_files_proc (p, closure) { if (save_cwd (&cwd)) exit (EXIT_FAILURE); - if (chdir (p->key) < 0) + if ( CVS_CHDIR (p->key) < 0) error (1, errno, "could not chdir to %s", p->key); save_update_dir = xstrdup (update_dir); @@ -693,10 +725,7 @@ unroll_files_proc (p, closure) (void) strcat (update_dir, p->key); } - err += do_recursion (frame->fileproc, frame->filesdoneproc, - frame->direntproc, frame->dirleaveproc, - frame->flags, frame->which, frame->aflag, - frame->readlock, frame->dosrcs); + err += do_recursion (frame); if (save_update_dir != NULL) { diff --git a/gnu/usr.bin/cvs/src/release.c b/gnu/usr.bin/cvs/src/release.c index b3ebb2be49d..56073a3f0f2 100644 --- a/gnu/usr.bin/cvs/src/release.c +++ b/gnu/usr.bin/cvs/src/release.c @@ -93,7 +93,7 @@ release (argc, argv) */ /* Construct the update command. */ sprintf (update_cmd, "%s -n -q -d %s update", - program_path, CVSroot); + program_path, CVSroot_original); #ifdef CLIENT_SUPPORT /* Start the server; we'll close it after looping. */ @@ -137,7 +137,7 @@ release (argc, argv) */ if (isdir (thisarg)) { - if (chdir (thisarg) < 0) + if ( CVS_CHDIR (thisarg) < 0) { if (!really_quiet) error (0, 0, "can't chdir to: %s", thisarg); @@ -267,10 +267,10 @@ release_delete (dir) struct stat st; ino_t ino; - (void) stat (".", &st); + (void) CVS_STAT (".", &st); ino = st.st_ino; - (void) chdir (".."); - (void) stat (dir, &st); + (void) CVS_CHDIR (".."); + (void) CVS_STAT (dir, &st); if (ino != st.st_ino) { error (0, 0, diff --git a/gnu/usr.bin/cvs/src/remove.c b/gnu/usr.bin/cvs/src/remove.c index 2911bf41ff7..95c395d5d16 100644 --- a/gnu/usr.bin/cvs/src/remove.c +++ b/gnu/usr.bin/cvs/src/remove.c @@ -17,8 +17,10 @@ #include "cvs.h" -static int remove_fileproc PROTO((struct file_info *finfo)); -static Dtype remove_dirproc PROTO((char *dir, char *repos, char *update_dir)); +static int remove_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static Dtype remove_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); static int force; static int local; @@ -71,11 +73,34 @@ cvsremove (argc, argv) #ifdef CLIENT_SUPPORT if (client_active) { + /* Call expand_wild so that the local removal of files will + work. It's ok to do it always because we have to send the + file names expanded anyway. */ + expand_wild (argc, argv, &argc, &argv); + + if (force) + { + if (!noexec) + { + int i; + + for (i = 0; i < argc; i++) + { + if ( CVS_UNLINK (argv[i]) < 0 && ! existence_error (errno)) + { + error (0, errno, "unable to remove %s", argv[i]); + } + } + } + /* else FIXME should probably act as if the file doesn't exist + in doing the following checks. */ + } + start_server (); ign_setup (); if (local) send_arg("-l"); - send_file_names (argc, argv, SEND_EXPAND_WILD); + send_file_names (argc, argv, 0); send_files (argc, argv, local, 0); send_to_server ("remove\012", 0); return get_responses_and_close (); @@ -84,8 +109,9 @@ cvsremove (argc, argv) /* start the recursion processor */ err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL, - remove_dirproc, (DIRLEAVEPROC) NULL, argc, argv, - local, W_LOCAL, 0, 1, (char *) NULL, 1, 0); + remove_dirproc, (DIRLEAVEPROC) NULL, NULL, + argc, argv, + local, W_LOCAL, 0, 1, (char *) NULL, 1); if (removed_files) error (0, 0, "use '%s commit' to remove %s permanently", program_name, @@ -106,7 +132,8 @@ cvsremove (argc, argv) */ /* ARGSUSED */ static int -remove_fileproc (finfo) +remove_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char fname[PATH_MAX]; @@ -116,7 +143,7 @@ remove_fileproc (finfo) { if (!noexec) { - if (unlink (finfo->file) < 0 && ! existence_error (errno)) + if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) { error (0, errno, "unable to remove %s", finfo->fullname); } @@ -125,8 +152,7 @@ remove_fileproc (finfo) in doing the following checks. */ } - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (vers->ts_user != NULL) { @@ -189,10 +215,12 @@ remove_fileproc (finfo) */ /* ARGSUSED */ static Dtype -remove_dirproc (dir, repos, update_dir) +remove_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Removing %s", update_dir); diff --git a/gnu/usr.bin/cvs/src/repos.c b/gnu/usr.bin/cvs/src/repos.c index 7beaabacf80..74d4580c5f3 100644 --- a/gnu/usr.bin/cvs/src/repos.c +++ b/gnu/usr.bin/cvs/src/repos.c @@ -27,7 +27,6 @@ Name_Repository (dir, update_dir) char repos[PATH_MAX]; char path[PATH_MAX]; char tmp[PATH_MAX]; - char cvsadm[PATH_MAX]; char *cp; if (update_dir && *update_dir) @@ -36,45 +35,41 @@ Name_Repository (dir, update_dir) xupdate_dir = "."; if (dir != NULL) - (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); - else - (void) strcpy (cvsadm, CVSADM); - - /* sanity checks */ - if (!isdir (cvsadm)) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "there is no version here; do '%s checkout' first", - program_name); - } - - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); - else - (void) strcpy (tmp, CVSADM_ENT); - - if (!isreadable (tmp)) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "*PANIC* administration files missing"); - } - - if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); else (void) strcpy (tmp, CVSADM_REP); - if (!isreadable (tmp)) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "*PANIC* administration files missing"); - } - /* * The assumption here is that the repository is always contained in the * first line of the "Repository" file. */ - fpin = open_file (tmp, "r"); + fpin = CVS_FOPEN (tmp, "r"); + + if (fpin == NULL) + { + int save_errno = errno; + char cvsadm[PATH_MAX]; + + if (dir != NULL) + (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); + else + (void) strcpy (cvsadm, CVSADM); + + if (!isdir (cvsadm)) + { + error (0, 0, "in directory %s:", xupdate_dir); + error (1, 0, "there is no version here; do '%s checkout' first", + program_name); + } + + if (existence_error (save_errno)) + { + error (0, 0, "in directory %s:", xupdate_dir); + error (1, 0, "*PANIC* administration files missing"); + } + + error (1, save_errno, "cannot open %s", tmp); + } if (fgets (repos, PATH_MAX, fpin) == NULL) { @@ -98,7 +93,7 @@ Name_Repository (dir, update_dir) } if (! isabsolute(repos)) { - if (CVSroot == NULL) + if (CVSroot_original == NULL) { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, "must set the CVSROOT environment variable\n"); @@ -106,16 +101,7 @@ Name_Repository (dir, update_dir) error (1, 0, "illegal repository setting"); } (void) strcpy (path, repos); - (void) sprintf (repos, "%s/%s", CVSroot, path); - } -#ifdef CLIENT_SUPPORT - if (!client_active && !isdir (repos)) -#else - if (!isdir (repos)) -#endif - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "there is no repository %s", repos); + (void) sprintf (repos, "%s/%s", CVSroot_directory, path); } /* allocate space to return and fill it in */ @@ -137,9 +123,10 @@ Short_Repository (repository) /* If repository matches CVSroot at the beginning, strip off CVSroot */ /* And skip leading '/' in rep, in case CVSroot ended with '/'. */ - if (strncmp (CVSroot, repository, strlen (CVSroot)) == 0) + if (strncmp (CVSroot_directory, repository, + strlen (CVSroot_directory)) == 0) { - char *rep = repository + strlen (CVSroot); + char *rep = repository + strlen (CVSroot_directory); return (*rep == '/') ? rep+1 : rep; } else diff --git a/gnu/usr.bin/cvs/src/root.c b/gnu/usr.bin/cvs/src/root.c index 0742cf07fdb..21df17d2bd0 100644 --- a/gnu/usr.bin/cvs/src/root.c +++ b/gnu/usr.bin/cvs/src/root.c @@ -13,6 +13,15 @@ #include "cvs.h" +/* Printable names for things in the CVSroot_method enum variable. + Watch out if the enum is changed in cvs.h! */ + +char *method_names[] = { + "local", "server (rsh)", "pserver", "kserver", "ext" +}; + +#ifndef DEBUG + char * Name_Root(dir, update_dir) char *dir; @@ -48,18 +57,9 @@ Name_Root(dir, update_dir) * 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. - */ + * order to continue. */ if ((!isdir (cvsadm)) || (!isreadable (tmp))) - { - if (CVSroot == NULL) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (0, 0, "must set the CVSROOT environment variable"); - error (0, 0, "or specify the '-d' option to %s.", program_name); - } - return (NULL); - } + return (NULL); /* * The assumption here is that the CVS Root is always contained in the @@ -130,9 +130,9 @@ same_directories (dir1, dir2) struct stat sb2; int ret; - if (stat (dir1, &sb1) < 0) + if ( CVS_STAT (dir1, &sb1) < 0) return (0); - if (stat (dir2, &sb2) < 0) + if ( CVS_STAT (dir2, &sb2) < 0) return (0); ret = 0; @@ -175,3 +175,241 @@ Create_Root (dir, rootdir) error (1, errno, "cannot close %s", tmp); } } + +#endif /* ! DEBUG */ + + +/* Parse a CVSROOT variable into its constituent parts -- method, + * username, hostname, directory. The prototypical CVSROOT variable + * looks like: + * + * :method:user@host:path + * + * Some methods may omit fields; local, for example, doesn't need user + * and host. + * + * Returns zero on success, non-zero on failure. */ + +char *CVSroot_original = NULL; /* the CVSroot that was passed in */ +int client_active; /* nonzero if we are doing remote access */ +CVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ +char *CVSroot_username; /* the username or NULL if method == local */ +char *CVSroot_hostname; /* the hostname or NULL if method == local */ +char *CVSroot_directory; /* the directory name */ + +int +parse_cvsroot (CVSroot) + char *CVSroot; +{ + static int cvsroot_parsed = 0; + char *cvsroot_copy, *p; + + /* Don't go through the trouble twice. */ + if (cvsroot_parsed) + { + error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); + return 0; + } + + CVSroot_original = xstrdup (CVSroot); + cvsroot_copy = xstrdup (CVSroot); + + if ((*cvsroot_copy == ':')) + { + char *method = ++cvsroot_copy; + + /* Access method specified, as in + * "cvs -d :pserver:user@host:/path", + * "cvs -d :local:e:\path", or + * "cvs -d :kserver:user@host:/path". + * We need to get past that part of CVSroot before parsing the + * rest of it. + */ + + if (! (p = strchr (method, ':'))) + { + error (0, 0, "bad CVSroot: %s", CVSroot); + return 1; + } + *p = '\0'; + cvsroot_copy = ++p; + + /* Now we have an access method -- see if it's valid. */ + + if (strcmp (method, "local") == 0) + CVSroot_method = local_method; + else if (strcmp (method, "pserver") == 0) + CVSroot_method = pserver_method; + else if (strcmp (method, "kserver") == 0) + CVSroot_method = kserver_method; + else if (strcmp (method, "server") == 0) + CVSroot_method = server_method; + else if (strcmp (method, "ext") == 0) + CVSroot_method = ext_method; + else + { + error (0, 0, "unknown method in CVSroot: %s", CVSroot); + return 1; + } + } + else + { + /* If the method isn't specified, assume + SERVER_METHOD/EXT_METHOD if the string contains a colon or + LOCAL_METHOD otherwise. */ + + CVSroot_method = ((strchr (cvsroot_copy, ':')) +#ifdef RSH_NOT_TRANSPARENT + ? server_method +#else + ? ext_method +#endif + : local_method); + } + + client_active = (CVSroot_method != local_method); + + /* Check for username/hostname if we're not LOCAL_METHOD. */ + + CVSroot_username = NULL; + CVSroot_hostname = NULL; + + if (CVSroot_method != local_method) + { + /* Check to see if there is a username in the string. */ + + if ((p = strchr (cvsroot_copy, '@'))) + { + CVSroot_username = cvsroot_copy; + *p = '\0'; + cvsroot_copy = ++p; + if (*CVSroot_username == '\0') + CVSroot_username = NULL; + } + + if ((p = strchr (cvsroot_copy, ':'))) + { + CVSroot_hostname = cvsroot_copy; + *p = '\0'; + cvsroot_copy = ++p; + + if (*CVSroot_hostname == '\0') + CVSroot_hostname = NULL; + } + } + + CVSroot_directory = cvsroot_copy; + +#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) + if (CVSroot_method != local_method) + { + error (0, 0, "Your CVSROOT is set for a remote access method"); + error (0, 0, "but your CVS executable doesn't support it"); + error (0, 0, "(%s)", CVSroot); + return 1; + } +#endif + + /* Do various sanity checks. */ + + if (CVSroot_username && ! CVSroot_hostname) + { + error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); + return 1; + } + + switch (CVSroot_method) + { + case local_method: + if (CVSroot_username || CVSroot_hostname) + { + error (0, 0, "can't specify hostname and username in CVSROOT"); + error (0, 0, "when using local access method"); + error (0, 0, "(%s)", CVSroot); + return 1; + } + break; + case kserver_method: +#ifndef HAVE_KERBEROS + error (0, 0, "Your CVSROOT is set for a kerberos access method"); + error (0, 0, "but your CVS executable doesn't support it"); + error (0, 0, "(%s)", CVSroot); + return 1; +#endif + case server_method: + case ext_method: + case pserver_method: + if (! CVSroot_hostname) + { + error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); + return 1; + } + break; + } + + if (*CVSroot_directory == '\0') + { + error (0, 0, "missing directory in CVSROOT: %s", CVSroot); + return 1; + } + + /* Hooray! We finally parsed it! */ + return 0; +} + + +/* Set up the global CVSroot* variables as if we're using the local + repository DIR. */ + +void +set_local_cvsroot (dir) + char *dir; +{ + CVSroot_original = xstrdup (dir); + CVSroot_method = local_method; + CVSroot_directory = CVSroot_original; + CVSroot_username = NULL; + CVSroot_hostname = NULL; + client_active = 0; +} + + +#ifdef DEBUG +/* This is for testing the parsing function. */ + +#include <stdio.h> + +char *CVSroot; +char *program_name = "testing"; +char *command_name = "parse_cvsroot"; /* XXX is this used??? */ + +void +main (argc, argv) + int argc; + char *argv[]; +{ + program_name = argv[0]; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); + exit (2); + } + + if (parse_cvsroot (argv[1])) + { + fprintf (stderr, "%s: Parsing failed.", program_name); + exit (1); + } + printf ("CVSroot: %s\n", argv[1]); + printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); + printf ("CVSroot_username: %s\n", + CVSroot_username ? CVSroot_username : "NULL"); + printf ("CVSroot_hostname: %s\n", + CVSroot_hostname ? CVSroot_hostname : "NULL"); + printf ("CVSroot_directory: %s\n", CVSroot_directory); + + exit (0); + /* NOTREACHED */ +} +#endif diff --git a/gnu/usr.bin/cvs/src/rtag.c b/gnu/usr.bin/cvs/src/rtag.c index 8609647a9e8..552ad32033d 100644 --- a/gnu/usr.bin/cvs/src/rtag.c +++ b/gnu/usr.bin/cvs/src/rtag.c @@ -13,15 +13,22 @@ #include "cvs.h" -static int check_fileproc PROTO((struct file_info *finfo)); -static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int check_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int pretag_proc PROTO((char *repository, char *filter)); static void masterlist_delproc PROTO((Node *p)); static void tag_delproc PROTO((Node *p)); static int pretag_list_proc PROTO((Node *p, void *closure)); -static Dtype rtag_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int rtag_fileproc PROTO((struct file_info *finfo)); +static Dtype rtag_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int rtag_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local_specified, char *mname, char *msg)); @@ -227,7 +234,7 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, char repository[PATH_MAX]; char where[PATH_MAX]; - (void) sprintf (repository, "%s/%s", CVSroot, argv[0]); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); (void) strcpy (where, argv[0]); /* if mfile isn't null, we need to set up to do only part of the module */ @@ -269,7 +276,7 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, } /* chdir to the starting directory */ - if (chdir (repository) < 0) + if ( CVS_CHDIR (repository) < 0) { error (0, errno, "cannot chdir to %s", repository); return (1); @@ -291,9 +298,9 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, mtlist = getlist(); err = start_recursion (check_fileproc, check_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, *pargc - 1, argv + 1, local, which, 0, 1, - where, 1, 1); + where, 1); if (err) { @@ -301,9 +308,10 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, } /* start the recursion processor */ - err = start_recursion (rtag_fileproc, (FILESDONEPROC) NULL, rtag_dirproc, - (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local, - which, 0, 1, where, 1, 1); + err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc, + (DIRLEAVEPROC) NULL, NULL, + *pargc - 1, argv + 1, local, + which, 0, 0, where, 1); dellist(&mtlist); @@ -314,7 +322,8 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, /* All we do here is add it to our list */ static int -check_fileproc (finfo) +check_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *xdir; @@ -349,15 +358,16 @@ check_fileproc (finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = tag_delproc; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, - (char *) NULL, finfo->file, 0, 0, finfo->entries, finfo->rcs); - p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, + (int *) NULL); if (p->data != NULL) { int addit = 1; char *oversion; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion == NULL) { if (delete_flag) @@ -389,10 +399,12 @@ check_fileproc (finfo) } static int -check_filesdoneproc(err, repos, update_dir) +check_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { int n; Node *p; @@ -496,13 +508,18 @@ pretag_list_proc(p, closure) */ /* ARGSUSED */ static int -rtag_fileproc (finfo) +rtag_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { RCSNode *rcsfile; char *version, *rev; int retcode = 0; + /* Lock the directory if it is not already locked. We might be + able to rely on rtag_dirproc for this. */ + tag_lockdir (finfo->repository); + /* find the parsed RCS data */ if ((rcsfile = finfo->rcs) == NULL) return (1); @@ -527,7 +544,8 @@ rtag_fileproc (finfo) return (rtag_delete (rcsfile)); } - version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 0); + version = RCS_getversion (rcsfile, numtag, date, force_tag_match, + (int *) NULL); if (version == NULL) { /* If -a specified, clean up any old tags */ @@ -554,7 +572,7 @@ rtag_fileproc (finfo) * the branch. Use a symbolic tag for that. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; - retcode = RCS_settag(rcsfile->path, symtag, numtag); + retcode = RCS_settag(rcsfile, symtag, numtag); } else { @@ -570,7 +588,8 @@ rtag_fileproc (finfo) * typical tagging operation. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; - oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion != NULL) { int isbranch = RCS_isbranch (finfo->rcs, symtag); @@ -602,7 +621,7 @@ rtag_fileproc (finfo) } free (oversion); } - retcode = RCS_settag(rcsfile->path, symtag, rev); + retcode = RCS_settag(rcsfile, symtag, rev); } if (retcode != 0) @@ -641,18 +660,20 @@ rtag_delete (rcsfile) if (numtag) { - version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0); + version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL) return (0); free (version); } - version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); + version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL) return (0); free (version); - if ((retcode = RCS_deltag(rcsfile->path, symtag, 1)) != 0) + if ((retcode = RCS_deltag(rcsfile, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -663,15 +684,32 @@ rtag_delete (rcsfile) return (0); } +/* Clear any lock we may hold on the current directory. */ + +static int +rtag_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; + int err; + char *repos; + char *update_dir; + List *entries; +{ + tag_unlockdir (); + + return (err); +} + /* * Print a warm fuzzy message */ /* ARGSUSED */ static Dtype -rtag_dirproc (dir, repos, update_dir) +rtag_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); diff --git a/gnu/usr.bin/cvs/src/sanity.sh b/gnu/usr.bin/cvs/src/sanity.sh index 4f80f906abf..8d67c1c6cc4 100644 --- a/gnu/usr.bin/cvs/src/sanity.sh +++ b/gnu/usr.bin/cvs/src/sanity.sh @@ -17,6 +17,10 @@ unset CVSREAD TESTDIR=/tmp/cvs-sanity +# This will show up in cvs history output where it prints the working +# directory. It should *not* appear in any cvs output referring to the +# repository; cvs should use the name of the repository as specified. +TMPPWD=`cd /tmp; /bin/pwd` # "debugger" #set -x @@ -26,6 +30,12 @@ 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 + # If we're going to do remote testing, make sure 'rsh' works first. + host="`hostname`" + if test "x`${CVS_RSH-rsh} $host 'echo hi'`" != "xhi"; then + echo "ERROR: cannot test remote CVS, because \`rsh $host' fails." >&2 + exit 1 + fi else remote=no fi @@ -79,18 +89,79 @@ if test -f check.log; then mv check.log check.plog fi +GEXPRLOCS="`echo $PATH | sed 's/:/ /g'` /usr/local/bin /usr/contrib/bin /usr/gnu/bin /local/bin /local/gnu/bin /gun/bin" + +EXPR=expr + +# Cause NextStep 3.3 users to lose in a more graceful fashion. +if $EXPR 'abc +def' : 'abc +def' >/dev/null; then + : good, it works +else + for path in $GEXPRLOCS ; do + if test -x $path/gexpr ; then + if test "X`$path/gexpr --version`" != "X--version" ; then + EXPR=$path/gexpr + break + fi + fi + if test -x $path/expr ; then + if test "X`$path/expr --version`" != "X--version" ; then + EXPR=$path/expr + break + fi + fi + done + if test -z "$EXPR" ; then + echo 'Running these tests requires an "expr" program that can handle' + echo 'multi-line patterns. Make sure that such an expr (GNU, or many but' + echo 'not all vendor-supplied versions) is in your path.' + exit 1 + fi +fi + +# Warn SunOS, SysVr3.2, etc., users that they may be partially losing +# if we can't find a GNU expr to ease their troubles... +if $EXPR 'a +b' : 'a +c' >/dev/null; then + for path in $GEXPRLOCS ; do + if test -x $path/gexpr ; then + if test "X`$path/gexpr --version`" != "X--version" ; then + EXPR=$path/gexpr + break + fi + fi + if test -x $path/expr ; then + if test "X`$path/expr --version`" != "X--version" ; then + EXPR=$path/expr + break + fi + fi + done + if test -z "$EXPR" ; then + echo 'Warning: you are using a version of expr which does not correctly' + echo 'match multi-line patterns. Some tests may spuriously pass.' + echo 'You may wish to make sure GNU expr is in your path.' + EXPR=expr + fi +else + : good, it works +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 +# version 1.9.4-1.12 uses the emacs definition of "$" instead of the unix # (e.g. SunOS 4.1.3 expr) one. Rumor has it this will be fixed in the # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). ENDANCHOR="$" -if expr 'abc +if $EXPR 'abc def' : 'abc$' >/dev/null; then ENDANCHOR='\'\' fi -# Work around another GNU expr (version 1.10) bug/incompatibility. +# Work around another GNU expr (version 1.10-1.12) bug/incompatibility. # "." doesn't appear to match a newline (it does with SunOS 4.1.3 expr). # Note that the workaround is not a complete equivalent of .* because # the first parenthesized expression in the regexp must match something @@ -99,7 +170,7 @@ fi # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). DOTSTAR='.*' -if expr 'abc +if $EXPR 'abc def' : "a${DOTSTAR}f" >/dev/null; then : good, it works else @@ -115,7 +186,7 @@ fi # next release of GNU expr after 1.12 (but we still have to cater to the old # ones for some time because they are in many linux distributions). PLUS='+' -if expr 'a +b' : "a ${PLUS}b" >/dev/null; then +if $EXPR 'a +b' : "a ${PLUS}b" >/dev/null; then : good, it works else PLUS='\+' @@ -123,35 +194,12 @@ fi # Likewise, for ? QUESTION='?' -if expr 'a?b' : "a${QUESTION}b" >/dev/null; then +if $EXPR 'a?b' : "a${QUESTION}b" >/dev/null; then : good, it works else QUESTION='\?' 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 - -# Warn SunOS, SysVr3.2, etc., users that they may be partially losing -if expr 'a -b' : 'a -c' >/dev/null; then - echo 'Warning: you are using a version of expr which does not correctly' - echo 'match multi-line patterns. Some tests may spuriously pass.' - echo 'You may wish to make sure GNU expr is in your path.' -else - : good, it works -fi - pass () { echo "PASS: $1" >>${LOGFILE} @@ -174,25 +222,109 @@ dotest_internal () if test -s ${TESTDIR}/dotest.tmp; then echo "** expected: " >>${LOGFILE} echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + rm -f ${TESTDIR}/dotest.ex2 echo "** got: " >>${LOGFILE} cat ${TESTDIR}/dotest.tmp >>${LOGFILE} fail "$1" else - cat ${TESTDIR}/dotest.tmp >>${LOGFILE} pass "$1" fi else - if expr "`cat ${TESTDIR}/dotest.tmp`" : \ + 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`" : \ + if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \ "$4"${ENDANCHOR} >/dev/null; then + pass "$1" + else + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.ex1 + echo "** or: " >>${LOGFILE} + echo "$4" >>${LOGFILE} + echo "$4" > ${TESTDIR}/dotest.ex2 + echo "** got: " >>${LOGFILE} cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi + else + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi + fi + fi +} + +dotest_all_in_one () +{ + if $EXPR "`cat ${TESTDIR}/dotest.tmp`" : \ + "`cat ${TESTDIR}/dotest.exp`" >/dev/null; then + return 0 + fi + return 1 +} + +# WARNING: this won't work with REs that match newlines.... +# +dotest_line_by_line () +{ + line=1 + while [ $line -le `wc -l ${TESTDIR}/dotest.tmp` ] ; do + echo "$line matched \c" >>$LOGFILE + if $EXPR "`sed -n ${line}p ${TESTDIR}/dotest.tmp`" : \ + "`sed -n ${line}p ${TESTDIR}/dotest.exp`" >/dev/null; then + : + else + echo "**** expected line: " >>${LOGFILE} + sed -n ${line}p ${TESTDIR}/dotest.exp >>${LOGFILE} + echo "**** got line: " >>${LOGFILE} + sed -n ${line}p ${TESTDIR}/dotest.tmp >>${LOGFILE} + unset line + return 1 + fi + line=`expr $line + 1` + done + unset line + return 0 +} + +# If you are having trouble telling which line of a multi-line +# expression is not being matched, replace calls to dotest_internal() +# with calls to this function: +# +dotest_internal_debug () +{ + if test -z "$3"; then + if test -s ${TESTDIR}/dotest.tmp; then + echo "** expected: " >>${LOGFILE} + echo "$3" >>${LOGFILE} + echo "$3" > ${TESTDIR}/dotest.exp + rm -f ${TESTDIR}/dotest.ex2 + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + else + pass "$1" + fi + else + echo "$3" > ${TESTDIR}/dotest.exp + if dotest_line_by_line "$1" "$2"; then + pass "$1" + else + if test x"$4" != x; then + mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex1 + echo "$4" > ${TESTDIR}/dotest.exp + if dotest_line_by_line "$1" "$2"; then pass "$1" else + mv ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.ex2 echo "** expected: " >>${LOGFILE} echo "$3" >>${LOGFILE} echo "** or: " >>${LOGFILE} @@ -215,7 +347,7 @@ dotest_internal () # 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 +# 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 @@ -227,6 +359,7 @@ dotest_internal () # lack \|). dotest () { + rm -f ${TESTDIR}/dotest.ex? 2>&1 if $2 >${TESTDIR}/dotest.tmp 2>&1; then : so far so good else @@ -238,9 +371,34 @@ dotest () dotest_internal "$@" } +# Like dotest except only 2 args and result must exactly match stdin +dotest_lit () +{ + rm -f ${TESTDIR}/dotest.ex? 2>&1 + 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 + cat >${TESTDIR}/dotest.exp + if cmp ${TESTDIR}/dotest.exp ${TESTDIR}/dotest.tmp >/dev/null 2>&1; then + pass "$1" + else + echo "** expected: " >>${LOGFILE} + cat ${TESTDIR}/dotest.exp >>${LOGFILE} + echo "** got: " >>${LOGFILE} + cat ${TESTDIR}/dotest.tmp >>${LOGFILE} + fail "$1" + fi +} + # Like dotest except exitstatus should be nonzero. dotest_fail () { + rm -f ${TESTDIR}/dotest.ex? 2>&1 if $2 >${TESTDIR}/dotest.tmp 2>&1; then status=$? cat ${TESTDIR}/dotest.tmp >>${LOGFILE} @@ -284,7 +442,13 @@ HOME=${TESTDIR}/home; export HOME # tests. if test x"$*" = x; then - tests="basica basic1 deep basic2 death branches import new conflicts modules mflag errmsg1 devcom ignore binfiles info" + # This doesn't yet include log2, because the bug it tests for + # is not yet fixed, and/or we might want to wait until after 1.9. + # + # We also omit rdiff for now, because we have put off + # committing the changes that make it work until after the 1.9 + # release. + tests="basica basicb basic1 deep basic2 death death2 branches multibranch import join new newb conflicts conflicts2 modules mflag errmsg1 devcom ignore binfiles binwrap info serverpatch log" else tests="$*" fi @@ -350,10 +514,8 @@ 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 +# Returns: {nothing} +# Side Effects: ISDIFF := true|false # directory_cmp () { @@ -380,15 +542,14 @@ directory_cmp () cd $OLDPWD while read a do - if [ -f $DIR_1/"$a" ] ; then + if test -f $DIR_1/"$a" ; then cmp -s $DIR_1/"$a" $DIR_2/"$a" - if [ $? -ne 0 ] ; then + if test $? -ne 0 ; then ISDIFF=true fi fi done < /tmp/dc$$d1 -### FIXME: -### rm -f /tmp/dc$$* + rm -f /tmp/dc$$* } # so much for the setup. Let's try something harder. @@ -398,10 +559,10 @@ directory_cmp () CVSROOT_DIRNAME=${TESTDIR}/cvsroot CVSROOT=${CVSROOT_DIRNAME} ; export CVSROOT if test "x$remote" = xyes; then - 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 + # 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. + CVSROOT=:ext:`hostname`:${CVSROOT_DIRNAME} ; export CVSROOT CVS_SERVER=${testcvs}; export CVS_SERVER fi @@ -434,6 +595,14 @@ for what in $tests; do dotest basica-1a2 "${testcvs} -q status" '' mkdir sdir + # Remote CVS gives the "cannot open CVS/Entries" error, which is + # clearly a bug, but not a simple one to fix. + dotest basica-1a10 "${testcvs} -n add sdir" \ +'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir added to the repository' \ +"${PROG} add: cannot open CVS/Entries for reading: No such file or directory +Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir added to the repository" + dotest_fail basica-1a11 \ + "test -d ${CVSROOT_DIRNAME}/first-dir/sdir" '' dotest basica-2 "${testcvs} add sdir" \ 'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir added to the repository' cd sdir @@ -465,12 +634,12 @@ ${PROG} "'\[[a-z]* aborted\]: correct the above errors first!' done Checking in sdir/ssdir/ssfile; /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v <-- ssfile -initial revision: 1.1 +initial revision: 1\.1 done' dotest_fail basica-5a \ "${testcvs} -q tag BASE sdir/ssdir/ssfile" \ "${PROG} [a-z]*: Attempt to add reserved tag name BASE -${PROG} \[[a-z]* aborted\]: failed to set tag BASE to revision 1.1 in /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v" +${PROG} \[[a-z]* aborted\]: failed to set tag BASE to revision 1\.1 in /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v" dotest basica-5b "${testcvs} -q tag NOT_RESERVED" \ 'T sdir/ssdir/ssfile' @@ -489,32 +658,140 @@ diff -c -r1\.1 ssfile --- 1,2 ---- ssfile '"${PLUS} ssfile line 2" + dotest_status basica-6.3 1 "${testcvs} -q diff -c -rBASE" \ +'Index: sdir/ssdir/ssfile +=================================================================== +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir/ssdir/ssfile,v +retrieving revision 1\.1 +diff -c -r1\.1 ssfile +\*\*\* ssfile [0-9/]* [0-9:]* 1\.1 +--- ssfile [0-9/]* [0-9:]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +--- 1,2 ---- + ssfile +'"${PLUS} ssfile line 2" 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 +new revision: 1\.2; previous revision: 1\.1 done' dotest_fail basica-nonexist "${testcvs} -q ci nonexist" \ "${PROG}"' [a-z]*: nothing known about `nonexist'\'' '"${PROG}"' \[[a-z]* aborted\]: correct above errors first!' dotest basica-8 "${testcvs} -q update" '' + + # The .* here will normally be "No such file or directory", + # but if memory serves some systems (AIX?) have a different message. +: dotest_fail basica-9 \ + "${testcvs} -q -d /tmp/cvs-sanity/nonexist update" \ +"${PROG}: cannot access cvs root /tmp/cvs-sanity/nonexist: .*" dotest_fail basica-9 \ "${testcvs} -q -d /tmp/cvs-sanity/nonexist update" \ -"${PROG}: .*/tmp/cvs-sanity/cvsroot value for CVS Root found in CVS/Root -${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting -'"${PROG}"': you may wish to try the cvs command again without the -d option ' +"${PROG} \[[a-z]* aborted\]: /tmp/cvs-sanity/nonexist/CVSROOT: .*" dotest basica-10 "${testcvs} annotate" \ 'Annotations for sdir/ssdir/ssfile \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* -1.1 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile -1.2 .[a-z@][a-z@ ]* [0-9a-zA-Z-]*.: ssfile line 2' +1\.1 .[a-z0-9@][a-z0-9@ ]* [0-9a-zA-Z-]*.: ssfile +1\.2 .[a-z0-9@][a-z0-9@ ]* [0-9a-zA-Z-]*.: ssfile line 2' cd .. rm -rf ${CVSROOT_DIRNAME}/first-dir rm -r first-dir ;; + basicb) + # More basic tests, including non-branch tags and co -d. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest basicb-1 "${testcvs} -q co first-dir" '' + cd first-dir + mkdir sdir1 sdir2 + dotest basicb-2 "${testcvs} add sdir1 sdir2" \ +'Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir1 added to the repository +Directory /tmp/cvs-sanity/cvsroot/first-dir/sdir2 added to the repository' + cd sdir1 + echo sfile1 starts >sfile1 + dotest basicb-2a10 "${testcvs} -n add sfile1" \ +"${PROG} [a-z]*: scheduling file .sfile1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-2a11 "${testcvs} status sfile1" \ +"${PROG} [a-z]*: use .cvs add' to create an entry for sfile1 +=================================================================== +File: sfile1 Status: Unknown + + Working revision: No entry for sfile1 + Repository revision: No revision control file" + dotest basicb-3 "${testcvs} add sfile1" \ +"${PROG} [a-z]*: scheduling file .sfile1. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest basicb-3a1 "${testcvs} status sfile1" \ +"=================================================================== +File: sfile1 Status: Locally Added + + Working revision: New file! + Repository revision: No revision control file + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none)" + + cd ../sdir2 + echo sfile2 starts >sfile2 + dotest basicb-4 "${testcvs} add sfile2" \ +"${PROG} [a-z]*: scheduling file .sfile2. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + cd .. + dotest basicb-5 "${testcvs} -q ci -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir1/sfile1,v +done +Checking in sdir1/sfile1; +/tmp/cvs-sanity/cvsroot/first-dir/sdir1/sfile1,v <-- sfile1 +initial revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/sdir2/sfile2,v +done +Checking in sdir2/sfile2; +/tmp/cvs-sanity/cvsroot/first-dir/sdir2/sfile2,v <-- sfile2 +initial revision: 1\.1 +done' + echo sfile1 develops >sdir1/sfile1 + dotest basicb-6 "${testcvs} -q ci -m modify" \ +'Checking in sdir1/sfile1; +/tmp/cvs-sanity/cvsroot/first-dir/sdir1/sfile1,v <-- sfile1 +new revision: 1\.2; previous revision: 1\.1 +done' + dotest basicb-7 "${testcvs} -q tag release-1" 'T sdir1/sfile1 +T sdir2/sfile2' + echo not in time for release-1 >sdir2/sfile2 + dotest basicb-8 "${testcvs} -q ci -m modify-2" \ +'Checking in sdir2/sfile2; +/tmp/cvs-sanity/cvsroot/first-dir/sdir2/sfile2,v <-- sfile2 +new revision: 1\.2; previous revision: 1\.1 +done' + cd .. + + # Test that we recurse into the correct directory when checking + # for existing files, even if co -d is in use. + touch first-dir/extra + dotest basicb-cod-1 "${testcvs} -q co -d first-dir1 first-dir" \ +'U first-dir1/sdir1/sfile1 +U first-dir1/sdir2/sfile2' + rm -rf first-dir1 + + rm -rf first-dir + dotest basicb-9 \ +"${testcvs} -q co -d newdir -r release-1 first-dir/sdir1 first-dir/sdir2" \ +'U newdir/sdir1/sfile1 +U newdir/sdir2/sfile2' + dotest basicb-10 "cat newdir/sdir1/sfile1 newdir/sdir2/sfile2" \ +"sfile1 develops +sfile2 starts" + + rm -rf newdir + + rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + basic1) # first dive - add a files, first singly, then in a group. mkdir ${CVSROOT_DIRNAME}/first-dir # check out an empty directory @@ -541,7 +818,7 @@ ${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting fi # update it. - if [ "${do}" = "rm" -a "$j" != "commit -m test" ] || ${CVS} update ${files} ; then + if test "${do}" = "rm" -a "$j" != "commit -m test" || ${CVS} update ${files} ; then echo "PASS: test 15-${do}-$j" >>${LOGFILE} else echo "FAIL: test 15-${do}-$j" | tee -a ${LOGFILE}; exit 1 @@ -639,8 +916,8 @@ ${PROG}"': does not match command line -d /tmp/cvs-sanity/nonexist setting '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' done cd ../../../../../../../../.. - dotest deep-4 "${testcvs} -q ci -m add-them first-dir" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/file1,v + dotest_lit deep-4 "${testcvs} -q ci -m add-them first-dir" <<'HERE' +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/dir1/file1,v done Checking in first-dir/dir1/file1; /tmp/cvs-sanity/cvsroot/first-dir/dir1/file1,v <-- file1 @@ -687,7 +964,23 @@ done Checking in first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1; /tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v <-- file1 initial revision: 1.1 +done +HERE + + cd first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8 + rm file1 + dotest deep-4a0 "${testcvs} rm file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest deep-4a1 "${testcvs} -q ci -m rm-it" 'Removing file1; +/tmp/cvs-sanity/cvsroot/first-dir/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file1,v <-- file1 +new revision: delete; previous revision: 1\.1 done' + cd ../../.. + dotest deep-4a2 "${testcvs} -q update -P dir6/dir7" '' + # Should be using "test -e" if that is portable enough. + dotest_fail deep-4a3 "test -d dir6/dir7/dir8" '' + cd ../../../../../.. if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE}; then pass deep-5 @@ -700,14 +993,21 @@ done' basic2) # Test rtag, import, history, various miscellaneous operations + # NOTE: this section has reached the size and + # complexity where it is getting to be a good idea to + # add new tests to a new section rather than + # continuing to piggyback them onto the tests here. + # First empty the history file rm ${CVSROOT_DIRNAME}/CVSROOT/history touch ${CVSROOT_DIRNAME}/CVSROOT/history +### XXX maybe should use 'cvs imprt -b1 -m new-module first-dir F F1' in an +### empty directory to do this instead of hacking directly into $CVSROOT mkdir ${CVSROOT_DIRNAME}/first-dir dotest basic2-1 "${testcvs} -q co first-dir" '' for i in first-dir dir1 dir2 ; do - if [ ! -d $i ] ; then + if test ! -d $i ; then mkdir $i if ${CVS} add $i >> ${LOGFILE}; then echo "PASS: test 29-$i" >>${LOGFILE} @@ -748,7 +1048,8 @@ done' echo "FAIL: test 33" | tee -a ${LOGFILE} ; exit 1 fi -# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then +# XXX why is this commented out??? +# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ; then # echo "PASS: test 34" >>${LOGFILE} # else # echo "FAIL: test 34" | tee -a ${LOGFILE} # ; exit 1 @@ -800,7 +1101,7 @@ done' echo "FAIL: test 39" | tee -a ${LOGFILE} ; exit 1 fi - # 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 40" >>${LOGFILE} else @@ -813,7 +1114,8 @@ done' echo "FAIL: test 41" | tee -a ${LOGFILE} ; exit 1 fi -# if ${CVS} diff -u first-dir >> ${LOGFILE} || [ $? = 1 ] ; then +# XXX why is this commented out? +# if ${CVS} diff -u first-dir >> ${LOGFILE} || test $? = 1 ; then # echo "PASS: test 42" >>${LOGFILE} # else # echo "FAIL: test 42" | tee -a ${LOGFILE} # ; exit 1 @@ -839,7 +1141,7 @@ done' fi # end of third dive - if [ -d first-dir ] ; then + if test -d first-dir ; then echo "FAIL: test 45.5" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 45.5" >>${LOGFILE} @@ -869,7 +1171,7 @@ done' fi # rdiff by revision - if ${CVS} rdiff -r1.1 -rrtagged-by-head first-dir >> ${LOGFILE} || [ $? = 1 ] ; then + if ${CVS} rdiff -r1.1 -rrtagged-by-head first-dir >> ${LOGFILE} || test $? = 1 ; then echo "PASS: test 49" >>${LOGFILE} else echo "FAIL: test 49" | tee -a ${LOGFILE} ; exit 1 @@ -917,13 +1219,14 @@ done' directory_cmp first-dir export-dir - if $ISDIFF ; then + if $ISDIFF ; then echo "FAIL: test 55" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 55" >>${LOGFILE} fi - # interrupt, while we've got a clean 1.1 here, let's import it into another tree. + # interrupt, while we've got a clean 1.1 here, let's import it + # into a couple of other modules. cd export-dir dotest 56 "${testcvs} import -m first-import second-dir first-immigration immigration1 immigration1_0" \ 'N second-dir/file14 @@ -939,7 +1242,6 @@ N second-dir/dir1/dir2/file6 N second-dir/dir1/dir2/file7 No conflicts created by this import' - cd .. if ${CVS} export -r HEAD second-dir ; then @@ -957,6 +1259,7 @@ No conflicts created by this import' fi rm -rf second-dir + rm -rf export-dir first-dir mkdir first-dir (cd first-dir.cpy ; tar cf - * | (cd ../first-dir ; tar xf -)) @@ -1020,50 +1323,188 @@ No conflicts created by this import' # which don't exist in the remote output? would seem to be # a CVS bug. dotest basic2-64 "${testcvs} his -e -a" \ -'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir =first-dir= /tmp/cvs-sanity/\* -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1/dir2 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1/dir2 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1 == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1 == /tmp/cvs-sanity -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1/dir2 == /tmp/cvs-sanity -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1/dir2 == /tmp/cvs-sanity -F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* =first-dir= /tmp/cvs-sanity/\* -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-revision:1.1\] -O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =first-dir= /tmp/cvs-sanity/\* -U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == /tmp/cvs-sanity/first-dir -U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file7 first-dir == /tmp/cvs-sanity/first-dir' \ -'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir =first-dir= <remote>/\* -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file6 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file7 first-dir/dir1/dir2 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1 == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1 == <remote> -A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.1 file14 first-dir/dir1/dir2 == <remote> -M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* 1.2 file6 first-dir/dir1/dir2 == <remote> -F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* =first-dir= <remote>/\* -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-head:A\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-tag:rtagged-by-head\] -T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* first-dir \[rtagged-by-revision:1.1\] -O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =first-dir= <remote>/\*' +'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir =first-dir= '"${TMPPWD}"'/cvs-sanity/\* +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1 == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1 == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1/dir2 == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1/dir2 == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir == '"${TMPPWD}"'/cvs-sanity +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1 == '"${TMPPWD}"'/cvs-sanity +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1 == '"${TMPPWD}"'/cvs-sanity +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1/dir2 == '"${TMPPWD}"'/cvs-sanity +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1/dir2 == '"${TMPPWD}"'/cvs-sanity +F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* =first-dir= '"${TMPPWD}"'/cvs-sanity/\* +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-head:A\] +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-revision:1\.1\] +O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* \[1\.1\] first-dir =first-dir= '"${TMPPWD}"'/cvs-sanity/\* +U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == '"${TMPPWD}"'/cvs-sanity/first-dir +U [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file7 first-dir == '"${TMPPWD}"'/cvs-sanity/first-dir' \ +'O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir =first-dir= <remote>/\* +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file6 first-dir/dir1/dir2 == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file7 first-dir/dir1/dir2 == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir == <remote> +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1 == <remote> +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1 == <remote> +A [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.1 file14 first-dir/dir1/dir2 == <remote> +M [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* 1\.2 file6 first-dir/dir1/dir2 == <remote> +F [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* =first-dir= <remote>/\* +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-head:A\] +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-tag:rtagged-by-head\] +T [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* first-dir \[rtagged-by-revision:1\.1\] +O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z0-9@][a-z0-9@]* \[1\.1\] first-dir =first-dir= <remote>/\*' rm -rf ${CVSROOT_DIRNAME}/first-dir rm -rf ${CVSROOT_DIRNAME}/second-dir ;; - death) # next dive. test death support. + rdiff) + # Test rdiff + # XXX for now this is just the most essential test... + cd ${TESTDIR} + + mkdir testimport + cd testimport + echo '$''Id$' > foo + echo '$''Name$' >> foo + echo '$''Id$' > bar + echo '$''Name$' >> bar + dotest rdiff-1 \ + "${testcvs} import -I ! -m test-import-with-keyword trdiff TRDIFF T1" \ +'N trdiff/foo +N trdiff/bar + +No conflicts created by this import' + dotest rdiff-2 \ + "${testcvs} co -ko trdiff" \ +'cvs [a-z]*: Updating trdiff +U trdiff/bar +U trdiff/foo' + cd trdiff + echo something >> foo + dotest rdiff-3 \ + "${testcvs} ci -m added-something foo" \ +'Checking in foo; +/tmp/cvs-sanity/cvsroot/trdiff/foo,v <-- foo +new revision: 1\.2; previous revision: 1\.1 +done' + echo '#ident "@(#)trdiff:$''Name$:$''Id$"' > new + echo "new file" >> new + dotest rdiff-4 \ + "${testcvs} add -m new-file-description new" \ +"cvs [a-z]*: scheduling file \`new' for addition +cvs [a-z]*: use 'cvs commit' to add this file permanently" + dotest rdiff-5 \ + "${testcvs} commit -m added-new-file new" \ +'RCS file: /tmp/cvs-sanity/cvsroot/trdiff/new,v +done +Checking in new; +/tmp/cvs-sanity/cvsroot/trdiff/new,v <-- new +initial revision: 1\.1 +done' + dotest rdiff-6 \ + "${testcvs} tag local-v0" \ +'cvs [a-z]*: Tagging . +T bar +T foo +T new' + dotest rdiff-7 \ + "${testcvs} status -v foo" \ +'=================================================================== +File: foo Status: Up-to-date + + Working revision: 1\.2.* + Repository revision: 1\.2 /tmp/cvs-sanity/cvsroot/trdiff/foo,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -ko + + Existing Tags: + local-v0 (revision: 1\.2) + T1 (revision: 1\.1\.1\.1) + TRDIFF (branch: 1\.1\.1)' + + cd .. + rm -rf trdiff + + dotest rdiff-8 \ + "${testcvs} rdiff -r T1 -r local-v0 trdiff" \ +'cvs [a-z]*: Diffing trdiff +Index: trdiff/foo +diff -c trdiff/foo:1\.1\.1\.1 trdiff/foo:1\.2 +\*\*\* trdiff/foo:1\.1\.1\.1 .* +--- trdiff/foo .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1,2 \*\*\*\* +! \$''Id\$ +! \$''Name\$ +--- 1,3 ---- +! \$''Id: foo,v 1\.2 [0-9/]* [0-9:]* [a-zA-Z0-9][a-zA-Z0-9]* Exp \$ +! \$''Name: local-v0 \$ +! something +Index: trdiff/new +diff -c /dev/null trdiff/new:1\.1 +\*\*\* /dev/null .* +--- trdiff/new .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1,2 ---- +'"${PLUS}"' #ident "@(#)trdiff:\$''Name: local-v0 \$:\$''Id: new,v 1\.1 [0-9/]* [0-9:]* [a-zA-Z0-9][a-zA-Z0-9]* Exp \$" +'"${PLUS}"' new file' + + # This appears to be broken client/server + if test "x$remote" = xno; then + dotest rdiff-9 \ + "${testcvs} rdiff -Ko -kv -r T1 -r local-v0 trdiff" \ +'cvs [a-z]*: Diffing trdiff +Index: trdiff/foo +diff -c trdiff/foo:1\.1\.1\.1 trdiff/foo:1\.2 +\*\*\* trdiff/foo:1\.1\.1\.1 .* +--- trdiff/foo .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1,2 \*\*\*\* +! \$''Id\$ +! \$''Name\$ +--- 1,3 ---- +! foo,v 1\.2 .* Exp +! local-v0 +! something +Index: trdiff/new +diff -c /dev/null trdiff/new:1\.1 +\*\*\* /dev/null .* +--- trdiff/new .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1,2 ---- +'"${PLUS}"' #ident "@(#)trdiff:local-v0:new,v 1\.1 .* Exp" +'"${PLUS}"' new file' + fi # end tests we are skipping for client/server + +# FIXME: will this work here? +# if test "$keep" = yes; then +# echo Keeping /tmp/cvs-sanity and exiting due to --keep +# exit 0 +# fi + + rm -rf ${CVSROOT_DIRNAME}/trdiff + ;; + + death) + # next dive. test death support. + + # NOTE: this section has reached the size and + # complexity where it is getting to be a good idea to + # add new death support tests to a new section rather + # than continuing to piggyback them onto the tests here. + mkdir ${CVSROOT_DIRNAME}/first-dir if ${CVS} co first-dir ; then echo "PASS: test 65" >>${LOGFILE} @@ -1088,7 +1529,7 @@ O [0-9/]* [0-9:]* '"${PLUS}"'0000 [a-z@][a-z@]* \[1.1\] first-dir =fir done Checking in sfile; /tmp/cvs-sanity/cvsroot/first-dir/subdir/sfile,v <-- sfile -initial revision: 1.1 +initial revision: 1\.1 done' rm sfile dotest 65a3 "${testcvs} rm sfile" \ @@ -1097,7 +1538,7 @@ done' 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 +new revision: delete; previous revision: 1\.1 done' cd .. dotest 65a5 "${testcvs} -q update -P" '' @@ -1174,7 +1615,7 @@ done' done Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -initial revision: 1.1 +initial revision: 1\.1 done' rm file4 dotest death-file4-rm "${testcvs} remove file4" \ @@ -1183,9 +1624,13 @@ done' 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 +new revision: delete; previous revision: 1\.1 done' + # Tag the branchpoint. + dotest death-72a "${testcvs} -q tag bp_branch1" 'T file1 +T file2' + # branch1 if ${CVS} tag -b branch1 ; then echo "PASS: test 73" >>${LOGFILE} @@ -1217,6 +1662,31 @@ done' echo "FAIL: test 76" | tee -a ${LOGFILE} ; exit 1 fi + # Remote CVS outputs nothing for 76a0 and 76a1; until + # this bug is fixed just skip those tests for remote. + if test "x$remote" = xno; then + dotest death-76a0 \ +"${testcvs} -q rdiff -r bp_branch1 -r branch1 first-dir" \ +"Index: first-dir/file3 +diff -c /dev/null first-dir/file3:1\.1\.2\.1 +\*\*\* /dev/null .* +--- first-dir/file3 .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} line1 from branch1" + dotest death-76a1 \ +"${testcvs} -q rdiff -r branch1 -r bp_branch1 first-dir" \ +'Index: first-dir/file3 +diff -c first-dir/file3:1\.1\.2\.1 first-dir/file3:removed +\*\*\* first-dir/file3:1\.1\.2\.1 .* +--- first-dir/file3 .* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- line1 from branch1 +--- 0 ----' + fi + # remove rm file3 if ${CVS} rm file3 2>> ${LOGFILE}; then @@ -1281,22 +1751,24 @@ done' dotest_fail death-file4-4 "test -f file4" '' - if [ -f file3 ] ; then + if test -f file3 ; then echo "FAIL: test 85" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 85" >>${LOGFILE} fi # join - if ${CVS} update -j branch1 >> ${LOGFILE} 2>&1; then - echo "PASS: test 86" >>${LOGFILE} - else - echo "FAIL: test 86" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 86 "${testcvs} -q update -j branch1" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +retrieving revision 1\.3 +retrieving revision 1\.3\.2\.1 +Merging differences between 1\.3 and 1\.3\.2\.1 into file1 +'"${PROG}"' [a-z]*: scheduling file2 for removal +U file3' dotest_fail death-file4-5 "test -f file4" '' - if [ -f file3 ] ; then + if test -f file3 ; then echo "PASS: test 87" >>${LOGFILE} else echo "FAIL: test 87" | tee -a ${LOGFILE} ; exit 1 @@ -1318,11 +1790,27 @@ done' fi # commit - if ${CVS} ci -m test >>${LOGFILE} 2>&1; then - echo "PASS: test 89" >>${LOGFILE} - else - echo "FAIL: test 89" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 89 "${testcvs} -q ci -m test" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.4; previous revision: 1\.3 +done +Removing file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +new revision: delete; previous revision: 1\.1 +done +Checking in file3; +/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3 +new revision: 1\.2; previous revision: 1\.1 +done' + cd .. + mkdir 2 + cd 2 + dotest 89a "${testcvs} -q co first-dir" 'U first-dir/file1 +U first-dir/file3' + cd .. + rm -rf 2 + cd first-dir # remove first file. rm file1 @@ -1339,7 +1827,7 @@ done' echo "FAIL: test 91" | tee -a ${LOGFILE} ; exit 1 fi - if [ -f file1 ] ; then + if test -f file1 ; then echo "FAIL: test 92" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 92" >>${LOGFILE} @@ -1354,7 +1842,7 @@ done' else echo "PASS: 92.1b" >>${LOGFILE} fi - if test -f file2 ; then + if test -f file3 ; then echo "PASS: 92.1c" >>${LOGFILE} else echo "FAIL: 92.1c" | tee -a ${LOGFILE} ; exit 1 @@ -1369,23 +1857,280 @@ done' dotest_fail death-file4-6 "test -f file4" '' - if [ -f file1 ] ; then + if test -f file1 ; then echo "PASS: test 94" >>${LOGFILE} else echo "FAIL: test 94" | tee -a ${LOGFILE} ; exit 1 fi # and join - if ${CVS} update -j HEAD >> ${LOGFILE} 2>&1; then - echo "PASS: test 95" >>${LOGFILE} - else - echo "FAIL: test 95" | tee -a ${LOGFILE} ; exit 1 - fi + dotest 95 "${testcvs} -q update -j HEAD" \ +"${PROG}"' [a-z]*: file file1 has been modified, but has been removed in revision HEAD +'"${PROG}"' [a-z]*: file file3 exists, but has been added in revision HEAD' dotest_fail death-file4-7 "test -f file4" '' + # file2 should not have been recreated. It was + # deleted on the branch, and has not been modified on + # the trunk. That means that there have been no + # changes between the greatest common ancestor (the + # trunk version) and HEAD. + dotest_fail death-file2-1 "test -f file2" '' + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir ;; + + death2) + # More tests of death support. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest death2-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add a file on the trunk. + echo "first revision" > file1 + dotest death2-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest death2-3 "${testcvs} -q commit -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done' + + # Make a branch and a non-branch tag. + dotest death2-4 "${testcvs} -q tag -b branch" 'T file1' + dotest death2-5 "${testcvs} -q tag tag" 'T file1' + + # Switch over to the branch. + dotest death2-6 "${testcvs} -q update -r branch" '' + + # Delete the file on the branch. + rm file1 + dotest death2-7 "${testcvs} rm file1" \ +"${PROG} [a-z]*: scheduling .file1. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest death2-8 "${testcvs} -q ci -m removed" \ +'Removing file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: delete; previous revision: 1\.1\.2 +done' + + # Test diff of a dead file. + dotest_fail death2-diff-1 \ +"${testcvs} -q diff -r1.1 -rbranch -c file1" \ +"${PROG} [a-z]*: file1 was removed, no comparison available" + + dotest_fail death2-diff-2 \ +"${testcvs} -q diff -r1.1 -rbranch -N -c file1" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + dotest_fail death2-diff-3 "${testcvs} -q diff -rtag -c ." \ +"${PROG} [a-z]*: file1 no longer exists, no comparison available" + + dotest_fail death2-diff-4 "${testcvs} -q diff -rtag -N -c ." \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +--- /dev/null[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + # Test rdiff of a dead file. + dotest death2-rdiff-1 \ +"${testcvs} -q rtag -rbranch rdiff-tag first-dir" '' + + dotest death2-rdiff-2 "${testcvs} -q rdiff -rtag -rbranch first-dir" \ +"Index: first-dir/file1 +diff -c first-dir/file1:1\.1 first-dir/file1:removed +\*\*\* first-dir/file1:1\.1[ ][ ]*[a-zA-Z0-9: ]* +--- first-dir/file1[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +- first revision +--- 0 ----" + + # Readd the file to the branch. + echo "second revision" > file1 + dotest death2-9 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: file `file1'\'' will be added on branch `branch'\'' from version 1\.1\.2\.1 +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-10 "${testcvs} -q commit -m add" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.2; previous revision: 1\.1\.2\.1 +done' + + # Back to the trunk. + dotest death2-11 "${testcvs} -q update -A" 'U file1' 'P file1' + + # Add another file on the trunk. + echo "first revision" > file2 + dotest death2-12 "${testcvs} add file2" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-13 "${testcvs} -q commit -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +done +Checking in file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done' + + # Back to the branch. + # The ``no longer in the repository'' message doesn't really + # look right to me, but that's what CVS currently prints for + # this case. + dotest death2-14 "${testcvs} -q update -r branch" \ +"U file1 +${PROG} [a-z]*: file2 is no longer in the repository" \ +"P file1 +${PROG} [a-z]*: file2 is no longer in the repository" + + # Add a file on the branch with the same name. + echo "branch revision" > file2 + dotest death2-15 "${testcvs} add file2" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-16 "${testcvs} -q commit -m add" \ +'Checking in file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done' + + # Add a new file on the branch. + echo "first revision" > file3 + dotest death2-17 "${testcvs} add file3" \ +"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest death2-18 "${testcvs} -q commit -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file3,v +done +Checking in file3; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file3,v <-- file3 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done' + + # Test diff of a nonexistent tag + dotest_fail death2-diff-5 "${testcvs} -q diff -rtag -c file3" \ +"${PROG} [a-z]*: tag tag is not in file file3" + + dotest_fail death2-diff-6 "${testcvs} -q diff -rtag -N -c file3" \ +"Index: file3 +=================================================================== +RCS file: file3 +diff -N file3 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + dotest_fail death2-diff-7 "${testcvs} -q diff -rtag -c ." \ +"Index: file1 +=================================================================== +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.2 +diff -c -r1\.1 -r1\.1\.2\.2 +\*\*\* file1[ ][ ]*[a-zA-Z0-9:./ ]* +--- file1[ ][ ]*[a-zA-Z0-9:./ ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +! first revision +--- 1 ---- +! second revision +${PROG} [a-z]*: tag tag is not in file file2 +${PROG} [a-z]*: tag tag is not in file file3" + + dotest_fail death2-diff-8 "${testcvs} -q diff -rtag -c -N ." \ +"Index: file1 +=================================================================== +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.2 +diff -c -r1\.1 -r1\.1\.2\.2 +\*\*\* file1[ ][ ]*[a-zA-Z0-9:./ ]* +--- file1[ ][ ]*[a-zA-Z0-9:./ ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 1 \*\*\*\* +! first revision +--- 1 ---- +! second revision +Index: file2 +=================================================================== +RCS file: file2 +diff -N file2 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} branch revision +Index: file3 +=================================================================== +RCS file: file3 +diff -N file3 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + # Switch to the nonbranch tag. + dotest death2-19 "${testcvs} -q update -r tag" \ +"U file1 +${PROG} [a-z]*: file2 is no longer in the repository +${PROG} [a-z]*: file3 is no longer in the repository" \ +"P file1 +${PROG} [a-z]*: file2 is no longer in the repository +${PROG} [a-z]*: file3 is no longer in the repository" + + dotest_fail death2-20 "test -f file2" + + # Make sure we can't add a file on this nonbranch tag. + # FIXME: Right now CVS will let you add a file on a + # nonbranch tag, so this test is commented out. + # echo "bad revision" > file2 + # dotest death2-21 "${testcvs} add file2" "some error message" + + # Make sure diff only reports appropriate files. + dotest_fail death2-diff-9 "${testcvs} -q diff -r rdiff-tag" \ +"${PROG} [a-z]*: file1 is a new entry, no comparison available" + + dotest_fail death2-diff-10 "${testcvs} -q diff -r rdiff-tag -c -N" \ +"Index: file1 +=================================================================== +RCS file: file1 +diff -N file1 +\*\*\* /dev/null[ ][ ]*[a-zA-Z0-9: ]* +--- [a-zA-Z0-9/.]*[ ][ ]*[a-zA-Z0-9: ]* +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* +\*\*\* 0 \*\*\*\* +--- 1 ---- +${PLUS} first revision" + + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + ;; + branches) # More branch tests, including branches off of branches mkdir ${CVSROOT_DIRNAME}/first-dir @@ -1401,8 +2146,8 @@ done' '"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition '"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition '"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' - dotest branches-3 "${testcvs} -q ci -m add-it" \ -'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v + dotest_lit branches-3 "${testcvs} -q ci -m add-it" <<'HERE' +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v done Checking in file1; /tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 @@ -1425,12 +2170,13 @@ done Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 initial revision: 1.1 -done' +done +HERE echo 4:trunk-2 >file4 dotest branches-3.2 "${testcvs} -q ci -m trunk-before-branch" \ 'Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2; previous revision: 1.1 +new revision: 1\.2; previous revision: 1\.1 done' dotest branches-4 "${testcvs} tag -b br1" "${PROG}"' [a-z]*: Tagging \. T file1 @@ -1445,15 +2191,15 @@ T file4' dotest branches-6 "${testcvs} -q ci -m modify" \ 'Checking in file1; /tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.1.2.1; previous revision: 1.1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 done Checking in file2; /tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 -new revision: 1.1.2.1; previous revision: 1.1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 done Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.1; previous revision: 1.2 +new revision: 1\.2\.2\.1; previous revision: 1\.2 done' dotest branches-7 "${testcvs} -q tag -b brbr" 'T file1 T file2 @@ -1465,11 +2211,11 @@ T file4' dotest branches-9 "${testcvs} -q ci -m modify" \ 'Checking in file1; /tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.1.2.1.2.1; previous revision: 1.1.2.1 +new revision: 1\.1\.2\.1\.2\.1; previous revision: 1\.1\.2\.1 done Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.1.2.1; previous revision: 1.2.2.1 +new revision: 1\.2\.2\.1\.2\.1; previous revision: 1\.2\.2\.1 done' dotest branches-10 "cat file1 file2 file3 file4" '1:brbr 2:br1 @@ -1486,7 +2232,7 @@ done' dotest branches-12.2 "${testcvs} -q ci -m change-on-br1" \ 'Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.2.2.2; previous revision: 1.2.2.1 +new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 done' dotest branches-13 "${testcvs} -q update -A" '[UP] file1 [UP] file2 @@ -1500,7 +2246,7 @@ done' "${testcvs} -q ci -m trunk-change-after-branch" \ 'Checking in file4; /tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 -new revision: 1.3; previous revision: 1.2 +new revision: 1\.3; previous revision: 1\.2 done' dotest branches-14.3 "${testcvs} log file4" \ ' @@ -1518,29 +2264,29 @@ total revisions: 6; selected revisions: 6 description: ---------------------------- revision 1\.3 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: '"${PLUS}"'1 -1 trunk-change-after-branch ---------------------------- revision 1\.2 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: '"${PLUS}"'1 -1 branches: 1\.2\.2; trunk-before-branch ---------------------------- revision 1\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; add-it ---------------------------- revision 1\.2\.2\.2 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: '"${PLUS}"'1 -1 change-on-br1 ---------------------------- revision 1\.2\.2\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: '"${PLUS}"'1 -1 branches: 1\.2\.2\.1\.2; modify ---------------------------- revision 1\.2\.2\.1\.2\.1 -date: [0-9/: ]*; author: [a-z@][a-z@]*; state: Exp; lines: '"${PLUS}"'1 -1 +date: [0-9/: ]*; author: [a-z0-9@][a-z0-9@]*; state: Exp; lines: '"${PLUS}"'1 -1 modify =============================================================================' dotest_status branches-14.4 1 \ @@ -1576,15 +2322,98 @@ diff -c -r1\.1 -r1\.2\.2\.1 dotest branches-15 \ "${testcvs} update -j 1.1.2.1 -j 1.1.2.1.2.1 file1" \ 'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v -retrieving revision 1.1.2.1 -retrieving revision 1.1.2.1.2.1 -Merging differences between 1.1.2.1 and 1.1.2.1.2.1 into file1 +retrieving revision 1\.1\.2\.1 +retrieving revision 1\.1\.2\.1\.2\.1 +Merging differences between 1\.1\.2\.1 and 1\.1\.2\.1\.2\.1 into file1 rcsmerge: warning: conflicts during merge' dotest branches-16 "cat file1" '<<<<<<< file1 1:ancest ======= 1:brbr ->>>>>>> 1.1.2.1.2.1' +[>]>>>>>> 1\.1\.2\.1\.2\.1' + cd .. + + if test "$keep" = yes; then + echo Keeping /tmp/cvs-sanity and exiting due to --keep + exit 0 + fi + + rm -rf ${CVSROOT_DIRNAME}/first-dir + rm -r first-dir + ;; + + multibranch) + # Test the ability to have several branchpoints coming off the + # same revision. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest multibranch-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 1:trunk-1 >file1 + dotest multibranch-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest_lit multibranch-3 "${testcvs} -q ci -m add-it" <<'HERE' +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1.1 +done +HERE + dotest multibranch-4 "${testcvs} tag -b br1" \ +"${PROG} [a-z]*: Tagging \. +T file1" + dotest multibranch-5 "${testcvs} tag -b br2" \ +"${PROG} [a-z]*: Tagging \. +T file1" + dotest multibranch-6 "${testcvs} -q update -r br1" '' + echo on-br1 >file1 + dotest multibranch-7 "${testcvs} -q ci -m modify-on-br1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done' + dotest multibranch-8 "${testcvs} -q update -r br2" '[UP] file1' + echo br2 adds a line >>file1 + dotest multibranch-9 "${testcvs} -q ci -m modify-on-br2" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.1\.4\.1; previous revision: 1\.1 +done' + dotest multibranch-10 "${testcvs} -q update -r br1" '[UP] file1' + dotest multibranch-11 "cat file1" 'on-br1' + dotest multibranch-12 "${testcvs} -q update -r br2" '[UP] file1' + dotest multibranch-13 "cat file1" '1:trunk-1 +br2 adds a line' + + dotest multibranch-14 "${testcvs} log file1" \ +" +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.1 +branch: +locks: strict +access list: +symbolic names: + br2: 1\.1\.0\.4 + br1: 1\.1\.0\.2 +keyword substitution: kv +total revisions: 3; selected revisions: 3 +description: +---------------------------- +revision 1\.1 +date: [0-9/]* [0-9:]*; author: [0-9a-zA-Z-]*; state: Exp; +branches: 1\.1\.2; 1\.1\.4; +add-it +---------------------------- +revision 1\.1\.4\.1 +date: [0-9/]* [0-9:]*; author: [0-9a-zA-Z-]*; state: Exp; lines: ${PLUS}1 -0 +modify-on-br2 +---------------------------- +revision 1\.1\.2\.1 +date: [0-9/]* [0-9:]*; author: [0-9a-zA-Z-]*; state: Exp; lines: ${PLUS}1 -1 +modify-on-br1 +=============================================================================" cd .. if test "$keep" = yes; then @@ -1634,7 +2463,7 @@ rcsmerge: warning: conflicts during merge' cd first-dir for i in 1 2 3 4 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-file"$i" ; then echo "PASS: test 98-$i" >>${LOGFILE} else echo "FAIL: test 98-$i" | tee -a ${LOGFILE} ; exit 1 @@ -1655,9 +2484,6 @@ rcsmerge: warning: conflicts during merge' fi # change - # this sleep is significant. Otherwise, on some machines, things happen so - # fast that the file mod times do not differ. - sleep 1 echo local-change >> imported-file2 # commit @@ -1732,14 +2558,14 @@ rcsmerge: warning: conflicts during merge' cd first-dir - if [ -f imported-file1 ] ; then + if test -f imported-file1 ; then echo "FAIL: test 108" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 108" >>${LOGFILE} fi for i in 2 3 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-file"$i" ; then echo "PASS: test 109-$i" >>${LOGFILE} else echo "FAIL: test 109-$i" | tee -a ${LOGFILE} ; exit 1 @@ -1753,7 +2579,7 @@ rcsmerge: warning: conflicts during merge' echo "FAIL: test 110" | tee -a ${LOGFILE} ; exit 1 fi - if [ -f imported-file4 ] ; then + if test -f imported-file4 ; then echo "PASS: test 111" >>${LOGFILE} else echo "FAIL: test 111" | tee -a ${LOGFILE} ; exit 1 @@ -1768,37 +2594,385 @@ rcsmerge: warning: conflicts during merge' cd .. - if ${CVS} co -jjunk-1_0 -jjunk-2_0 first-dir >>${LOGFILE} 2>&1; then - echo "PASS: test 113" >>${LOGFILE} - else - echo "FAIL: test 113" | tee -a ${LOGFILE} ; exit 1 - fi + dotest import-113 \ +"${testcvs} -q co -jjunk-1_0 -jjunk-2_0 first-dir" \ +"${PROG}"' [a-z]*: file first-dir/imported-file1 is present in revision junk-2_0 +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/imported-file2,v +retrieving revision 1\.1\.1\.1 +retrieving revision 1\.1\.1\.2 +Merging differences between 1\.1\.1\.1 and 1\.1\.1\.2 into imported-file2 +rcsmerge: warning: conflicts during merge' cd first-dir - if [ -f imported-file1 ] ; then + if test -f imported-file1 ; then echo "FAIL: test 114" | tee -a ${LOGFILE} ; exit 1 else echo "PASS: test 114" >>${LOGFILE} fi for i in 2 3 ; do - if [ -f imported-file"$i" ] ; then + if test -f imported-file"$i" ; then echo "PASS: test 115-$i" >>${LOGFILE} else echo "FAIL: test 115-$i" | tee -a ${LOGFILE} ; exit 1 fi done - if cat imported-file2 | grep '====' >> ${LOGFILE}; then - echo "PASS: test 116" >>${LOGFILE} - else - echo "FAIL: test 116" | tee -a ${LOGFILE} ; exit 1 - fi + dotest import-116 'cat imported-file2' \ +'imported file2 +[<]<<<<<< imported-file2 +import should not expand \$''Id: imported-file2,v 1\.2 [0-9/]* [0-9:]* [a-z0-9@][a-z0-9@]* Exp \$ +local-change +[=]====== +import should not expand \$''Id: imported-file2,v 1\.1\.1\.2 [0-9/]* [0-9:]* [a-z0-9@][a-z0-9@]* Exp \$ +rev 2 of file 2 +[>]>>>>>> 1\.1\.1\.2' + cd .. ; rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir rm -rf import-dir ;; + join) + # Test doing joins which involve adding and removing files. + + # We check merging changes from T1 to T2 into the main line. + # Here are the interesting cases I can think of: + # 1) File added between T1 and T2, not on main line. + # File should be marked for addition. + # 2) File added between T1 and T2, also added on main line. + # Conflict. + # 3) File removed between T1 and T2, unchanged on main line. + # File should be marked for removal. + # 4) File removed between T1 and T2, modified on main line. + # If mod checked in, file should be marked for removal. + # If mod still in working directory, conflict. + # 5) File removed between T1 and T2, was never on main line. + # Nothing should happen. + # 6) File removed between T1 and T2, also removed on main line. + # Nothing should happen. + # 7) File added on main line, not added between T1 and T2. + # Nothing should happen. + # 8) File removed on main line, not modified between T1 and T2. + # Nothing should happen. + + # We also check merging changes from a branch into the main + # line. Here are the interesting cases: + # 1) File added on branch, not on main line. + # File should be marked for addition. + # 2) File added on branch, also added on main line. + # Conflict. + # 3) File removed on branch, unchanged on main line. + # File should be marked for removal. + # 4) File removed on branch, modified on main line. + # Conflict. + # 5) File removed on branch, was never on main line. + # Nothing should happen. + # 6) File removed on branch, also removed on main line. + # Nothing should happen. + # 7) File added on main line, not added on branch. + # Nothing should happen. + # 8) File removed on main line, not modified on branch. + # Nothing should happen. + + # In the tests below, fileN represents case N in the above + # lists. + + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest join-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add two files. + echo 'first revision of file3' > file3 + echo 'first revision of file4' > file4 + echo 'first revision of file6' > file6 + echo 'first revision of file8' > file8 + dotest join-2 "${testcvs} add file3 file4 file6 file8" \ +"${PROG}"' [a-z]*: scheduling file `file3'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file4'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file6'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file8'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + + dotest join-3 "${testcvs} -q commit -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file3,v +done +Checking in file3; +/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3 +initial revision: 1\.1 +done +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 +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file6,v +done +Checking in file6; +/tmp/cvs-sanity/cvsroot/first-dir/file6,v <-- file6 +initial revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file8,v +done +Checking in file8; +/tmp/cvs-sanity/cvsroot/first-dir/file8,v <-- file8 +initial revision: 1\.1 +done' + + # Make a branch. + dotest join-4 "${testcvs} -q tag -b branch ." \ +'T file3 +T file4 +T file6 +T file8' + + # Add file2 and file7, modify file4, and remove file6 and file8. + echo 'first revision of file2' > file2 + echo 'second revision of file4' > file4 + echo 'first revision of file7' > file7 + rm file6 file8 + dotest join-5 "${testcvs} add file2 file7" \ +"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file7'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest join-6 "${testcvs} rm file6 file8" \ +"${PROG}"' [a-z]*: scheduling `file6'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file8'\'' for removal +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to remove these files permanently' + dotest join-7 "${testcvs} -q ci -mx ." \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +done +Checking in file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done +Checking in file4; +/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.2; previous revision: 1\.1 +done +Removing file6; +/tmp/cvs-sanity/cvsroot/first-dir/file6,v <-- file6 +new revision: delete; previous revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file7,v +done +Checking in file7; +/tmp/cvs-sanity/cvsroot/first-dir/file7,v <-- file7 +initial revision: 1\.1 +done +Removing file8; +/tmp/cvs-sanity/cvsroot/first-dir/file8,v <-- file8 +new revision: delete; previous revision: 1\.1 +done' + + # Check out the branch. + cd ../.. + mkdir 2 + cd 2 + dotest join-8 "${testcvs} -q co -r branch first-dir" \ +'U first-dir/file3 +U first-dir/file4 +U first-dir/file6 +U first-dir/file8' + + cd first-dir + + # Modify the files on the branch, so that T1 is not an + # ancestor of the main line, and add file5 + echo 'first branch revision of file3' > file3 + echo 'first branch revision of file4' > file4 + echo 'first branch revision of file6' > file6 + echo 'first branch revision of file5' > file5 + dotest join-9 "${testcvs} add file5" \ +"${PROG}"' [a-z]*: scheduling file `file5'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest join-10 "${testcvs} -q ci -mx ." \ +'Checking in file3; +/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file4; +/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file5,v +done +Checking in file5; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file5,v <-- file5 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file6; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file6,v <-- file6 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done' + + # Tag the current revisions on the branch. + dotest join-11 "${testcvs} -q tag T1 ." \ +'T file3 +T file4 +T file5 +T file6 +T file8' + + # Add file1 and file2, and remove the other files. + echo 'first branch revision of file1' > file1 + echo 'first branch revision of file2' > file2 + rm file3 file4 file5 file6 + dotest join-12 "${testcvs} add file1 file2" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition on branch `branch'\'' +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest join-13 "${testcvs} rm file3 file4 file5 file6" \ +"${PROG}"' [a-z]*: scheduling `file3'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file4'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file5'\'' for removal +'"${PROG}"' [a-z]*: scheduling `file6'\'' for removal +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to remove these files permanently' + dotest join-14 "${testcvs} -q ci -mx ." \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file1,v <-- file1 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Checking in file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +new revision: 1\.1\.2\.1; previous revision: 1\.1 +done +Removing file3; +/tmp/cvs-sanity/cvsroot/first-dir/file3,v <-- file3 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file4; +/tmp/cvs-sanity/cvsroot/first-dir/file4,v <-- file4 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file5; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file5,v <-- file5 +new revision: delete; previous revision: 1\.1\.2\.1 +done +Removing file6; +/tmp/cvs-sanity/cvsroot/first-dir/Attic/file6,v <-- file6 +new revision: delete; previous revision: 1\.1\.2\.1 +done' + + # Tag the current revisions on the branch. + dotest join-15 "${testcvs} -q tag T2 ." \ +'T file1 +T file2 +T file8' + + # Do a checkout with a merge. + cd ../.. + mkdir 3 + cd 3 + dotest join-16 "${testcvs} -q co -jT1 -jT2 first-dir" \ +'U first-dir/file1 +U first-dir/file2 +'"${PROG}"' [a-z]*: file first-dir/file2 exists, but has been added in revision T2 +U first-dir/file3 +'"${PROG}"' [a-z]*: scheduling first-dir/file3 for removal +U first-dir/file4 +'"${PROG}"' [a-z]*: scheduling first-dir/file4 for removal +U first-dir/file7' + + # Verify that the right changes have been scheduled. + cd first-dir + dotest join-17 "${testcvs} -q update" \ +'A file1 +R file3 +R file4' + + # Modify file4 locally, and do an update with a merge. + cd ../../1/first-dir + echo 'third revision of file4' > file4 + dotest join-18 "${testcvs} -q update -jT1 -jT2 ." \ +'U file1 +'"${PROG}"' [a-z]*: file file2 exists, but has been added in revision T2 +'"${PROG}"' [a-z]*: scheduling file3 for removal +M file4 +'"${PROG}"' [a-z]*: file file4 is locally modified, but has been removed in revision T2' + + # Verify that the right changes have been scheduled. + dotest join-19 "${testcvs} -q update" \ +'A file1 +R file3 +M file4' + + # Do a checkout with a merge from a single revision. + + # FIXME: CVS currently gets this wrong. file2 has been + # added on both the branch and the main line, and so should + # be regarded as a conflict. However, given the way that + # CVS sets up the RCS file, there is no way to distinguish + # this case from the case of file2 having existed before the + # branch was made. This could be fixed by reserving + # a revision somewhere, perhaps 1.1, as an always dead + # revision which can be used as the source for files added + # on branches. + cd ../../3 + rm -rf first-dir + dotest join-20 "${testcvs} -q co -jbranch first-dir" \ +'U first-dir/file1 +U first-dir/file2 +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.1 +Merging differences between 1\.1 and 1\.1\.2\.1 into file2 +U first-dir/file3 +'"${PROG}"' [a-z]*: scheduling first-dir/file3 for removal +U first-dir/file4 +'"${PROG}"' [a-z]*: file first-dir/file4 has been modified, but has been removed in revision branch +U first-dir/file7' + + # Verify that the right changes have been scheduled. + # The M file2 line is a bug; see above join-20. + cd first-dir + dotest join-21 "${testcvs} -q update" \ +'A file1 +M file2 +R file3' + + # Checkout the main line again. + cd ../../1 + rm -rf first-dir + dotest join-22 "${testcvs} -q co first-dir" \ +'U first-dir/file2 +U first-dir/file3 +U first-dir/file4 +U first-dir/file7' + + # Modify file4 locally, and do an update with a merge from a + # single revision. + # The file2 handling is a bug; see above join-20. + cd first-dir + echo 'third revision of file4' > file4 + dotest join-23 "${testcvs} -q update -jbranch ." \ +'U file1 +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +retrieving revision 1\.1 +retrieving revision 1\.1\.2\.1 +Merging differences between 1\.1 and 1\.1\.2\.1 into file2 +'"${PROG}"' [a-z]*: scheduling file3 for removal +M file4 +'"${PROG}"' [a-z]*: file file4 is locally modified, but has been removed in revision branch' + + # Verify that the right changes have been scheduled. + # The M file2 line is a bug; see above join-20 + dotest join-24 "${testcvs} -q update" \ +'A file1 +M file2 +R file3 +M file4' + + cd ../.. + rm -rf 1 2 3 ${CVSROOT_DIRNAME}/first-dir + ;; + new) # look for stray "no longer pertinent" messages. mkdir ${CVSROOT_DIRNAME}/first-dir @@ -1852,38 +3026,129 @@ rcsmerge: warning: conflicts during merge' cd .. ; rm -rf first-dir ; rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + newb) + # Test removing a file on a branch and then checking it out. + + # We call this "newb" only because it, like the "new" tests, + # has something to do with "no longer pertinent" messages. + # Not necessarily the most brilliant nomenclature. + + # Create file 'a'. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest newb-123a "${testcvs} -q co first-dir" '' + cd first-dir + touch a + dotest newb-123b "${testcvs} add a" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest newb-123c "${testcvs} -q ci -m added" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v +done +Checking in a; +/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done' + + # Make a branch. + dotest newb-123d "${testcvs} -q tag -b branch" "T a" + + # Check out the branch. + cd .. + rm -rf first-dir + mkdir 1 + cd 1 + dotest newb-123e "${testcvs} -q co -r branch first-dir" \ +"U first-dir/a" + + # Remove 'a' on another copy of the branch. + cd .. + mkdir 2 + cd 2 + dotest newb-123f "${testcvs} -q co -r branch first-dir" \ +"U first-dir/a" + cd first-dir + rm a + dotest newb-123g "${testcvs} rm a" \ +"${PROG} [a-z]*: scheduling .a. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest newb-123h "${testcvs} -q ci -m removed" \ +'Removing a; +/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a +new revision: delete; previous revision: 1\.1\.2 +done' + + # Check out the file on the branch. This should report + # that the file is not pertinent, but it should not + # say anything else. + cd .. + rm -rf first-dir + dotest newb-123i "${testcvs} -q co -r branch first-dir/a" \ +"${PROG} [a-z]*: warning: first-dir/a is not (any longer) pertinent" + + # Update the other copy, and make sure that a is removed. + cd ../1/first-dir + # "Needs Patch" is a rather strange output here. Something like + # "Removed in Repository" would make more sense. + dotest newb-123j0 "${testcvs} status a" \ +"=================================================================== +File: a Status: Needs Patch + + Working revision: 1\.1.* + Repository revision: 1\.1\.2\.1 /tmp/cvs-sanity/cvsroot/first-dir/a,v + Sticky Tag: branch (branch: 1\.1\.2) + Sticky Date: (none) + Sticky Options: (none)" + dotest newb-123j "${testcvs} -q update" \ +"${PROG} [a-z]*: warning: a is not (any longer) pertinent" + + if test -f a; then + fail newb-123k + else + pass newb-123k + fi + + cd ../.. + rm -rf 1 2 ; rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + conflicts) - rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir mkdir ${CVSROOT_DIRNAME}/first-dir mkdir 1 cd 1 - if ${CVS} co first-dir ; then - echo 'PASS: test 124' >>${LOGFILE} - else - echo 'FAIL: test 124' | tee -a ${LOGFILE} - fi + dotest conflicts-124 "${testcvs} -q co first-dir" '' cd first-dir touch a - if ${CVS} add a 2>>${LOGFILE} ; then - echo 'PASS: test 125' >>${LOGFILE} - else - echo 'FAIL: test 125' | tee -a ${LOGFILE} - fi - - if ${CVS} ci -m added >>${LOGFILE} 2>&1; then - echo 'PASS: test 126' >>${LOGFILE} - else - echo 'FAIL: test 126' | tee -a ${LOGFILE} - fi + dotest conflicts-125 "${testcvs} add a" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest conflicts-126 "${testcvs} -q ci -m added" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v +done +Checking in a; +/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done' cd ../.. mkdir 2 cd 2 + # TODO-maybe: we could also check this (also in an empty + # directory) after the file has nonempty contents. + # + # The need for TMPPWD here is a (minor) CVS bug; the + # output should use the name of the repository as specified. + dotest conflicts-126.5 "${testcvs} co -p first-dir" \ +"${PROG} [a-z]*"': Updating first-dir +=================================================================== +Checking out first-dir/a +RCS: '"${TMPPWD}"'/cvs-sanity/cvsroot/first-dir/a,v +VERS: 1\.1 +\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' if ${CVS} co first-dir ; then echo 'PASS: test 127' >>${LOGFILE} else @@ -1904,7 +3169,7 @@ rcsmerge: warning: conflicts during merge' dotest conflicts-128 "${testcvs} -q ci -m changed" \ 'Checking in a; /tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a -new revision: 1.2; previous revision: 1.1 +new revision: 1\.2; previous revision: 1\.1 done' cd ../../2/first-dir echo add a conflicting line >>a @@ -1915,9 +3180,9 @@ done' mkdir sdir dotest conflicts-130 "${testcvs} -q update" \ 'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v -retrieving revision 1.1 -retrieving revision 1.2 -Merging differences between 1.1 and 1.2 into a +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into a rcsmerge: warning: conflicts during merge '"${PROG}"' [a-z]*: conflicts found in a C a @@ -1926,12 +3191,13 @@ C a ''"${QUESTION}"' dir1 '"${QUESTION}"' sdir RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v -retrieving revision 1.1 -retrieving revision 1.2 -Merging differences between 1.1 and 1.2 into a +retrieving revision 1\.1 +retrieving revision 1\.2 +Merging differences between 1\.1 and 1\.2 into a rcsmerge: warning: conflicts during merge '"${PROG}"' [a-z]*: conflicts found in a C a' + rmdir dir1 sdir # Try to check in the file with the conflict markers in it. if ${CVS} ci -m try 2>>${LOGFILE}; then @@ -1972,6 +3238,7 @@ C a' echo 'FAIL: test 135' | tee -a ${LOGFILE} fi cd ../../2 + mkdir first-dir/dir1 first-dir/sdir dotest conflicts-136 "${testcvs} -q update" \ '[UP] first-dir/abc '"${QUESTION}"' first-dir/dir1 @@ -2024,10 +3291,130 @@ C a' else echo 'FAIL: test 142' | tee -a ${LOGFILE} fi - - cd ../.. + cd ../.. rm -rf 1 2 3 ; rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + + conflicts2) + # More conflicts tests; separate from conflicts to keep each + # test a manageable size. + mkdir ${CVSROOT_DIRNAME}/first-dir + + mkdir 1 + cd 1 + + dotest conflicts2-142a1 "${testcvs} -q co first-dir" '' + + cd first-dir + touch a abc + + dotest conflicts2-142a2 "${testcvs} add a abc" \ +"${PROG} [a-z]*: scheduling file .a. for addition +${PROG} [a-z]*: scheduling file .abc. for addition +${PROG} [a-z]*: use .cvs commit. to add these files permanently" + dotest conflicts2-142a3 "${testcvs} -q ci -m added" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/a,v +done +Checking in a; +/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a +initial revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/abc,v +done +Checking in abc; +/tmp/cvs-sanity/cvsroot/first-dir/abc,v <-- abc +initial revision: 1\.1 +done' + + cd ../.. + mkdir 2 + cd 2 + + dotest conflicts2-142a4 "${testcvs} -q co first-dir" 'U first-dir/a +U first-dir/abc' + cd .. + + # Now test that if one person modifies and commits a + # file and a second person removes it, it is a + # conflict + cd 1/first-dir + echo modify a >>a + dotest conflicts2-142b2 "${testcvs} -q ci -m modify-a" \ +'Checking in a; +/tmp/cvs-sanity/cvsroot/first-dir/a,v <-- a +new revision: 1\.2; previous revision: 1\.1 +done' + cd ../../2/first-dir + rm a + dotest conflicts2-142b3 "${testcvs} rm a" \ +"${PROG} [a-z]*: scheduling .a. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest_fail conflicts2-142b4 "${testcvs} -q update" \ +"${PROG} [a-z]*: conflict: removed a was modified by second party +C a" + # Resolve the conflict by deciding not to remove the file + # after all. + dotest conflicts2-142b5 "${testcvs} add a" "U a +${PROG} [a-z]*: a, version 1\.1, resurrected" + dotest conflicts2-142b6 "${testcvs} -q update" '' + cd ../.. + + # Now test that if one person removes a file and + # commits it, and a second person removes it, is it + # not a conflict. + cd 1/first-dir + rm abc + dotest conflicts2-142c0 "${testcvs} rm abc" \ +"${PROG} [a-z]*: scheduling .abc. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest conflicts2-142c1 "${testcvs} -q ci -m remove-abc" \ +'Removing abc; +/tmp/cvs-sanity/cvsroot/first-dir/abc,v <-- abc +new revision: delete; previous revision: 1\.1 +done' + cd ../../2/first-dir + rm abc + dotest conflicts2-142c2 "${testcvs} rm abc" \ +"${PROG} [a-z]*: scheduling .abc. for removal +${PROG} [a-z]*: use .cvs commit. to remove this file permanently" + dotest conflicts2-142c3 "${testcvs} update" \ +"${PROG} [a-z]*: Updating \." + cd ../.. + + # conflicts2-142d*: test that if one party adds a file, and another + # party has a file of the same name, cvs notices + cd 1/first-dir + touch aa.c + dotest conflicts2-142d0 "${testcvs} add aa.c" \ +"${PROG} [a-z]*: scheduling file .aa\.c. for addition +${PROG} [a-z]*: use .cvs commit. to add this file permanently" + dotest conflicts2-142d1 "${testcvs} -q ci -m added" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/aa.c,v +done +Checking in aa.c; +/tmp/cvs-sanity/cvsroot/first-dir/aa.c,v <-- aa.c +initial revision: 1\.1 +done' + cd ../../2/first-dir + echo "don't you dare obliterate this text" >aa.c + # Doing this test separately for remote and local is a fair + # bit of a kludge, but the exit status differs. I'm not sure + # which exit status is the more appropriate one. + if test "$remote" = yes; then + dotest conflicts2-142d2 "${testcvs} -q update" \ +"${QUESTION} aa\.c +U aa\.c +${PROG} update: move away \./aa\.c; it is in the way" + else + dotest_fail conflicts2-142d2 "${testcvs} -q update" \ +"${PROG} [a-z]*: move away aa\.c; it is in the way +C aa\.c" + fi + cd ../.. + + rm -rf 1 2 ; rm -rf ${CVSROOT_DIRNAME}/first-dir + ;; + modules) rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir mkdir ${CVSROOT_DIRNAME}/first-dir @@ -2090,6 +3477,8 @@ C a' echo namedmodule -d nameddir first-dir/subdir >>CVSROOT/modules echo aliasmodule -a first-dir/subdir/a >>CVSROOT/modules echo aliasnested -a first-dir/subdir/ssdir >>CVSROOT/modules + echo topfiles -a first-dir/file1 first-dir/file2 >>CVSROOT/modules + echo world -a . >>CVSROOT/modules # Options must come before arguments. It is possible this should # be relaxed at some point (though the result would be bizarre for @@ -2109,7 +3498,9 @@ aliasnested -a first-dir/subdir/ssdir bogusalias first-dir/subdir/a -a dirmodule first-dir/subdir namedmodule -d nameddir first-dir/subdir -realmodule first-dir/subdir a' +realmodule first-dir/subdir a +topfiles -a first-dir/file1 first-dir/file2 +world -a .' # I don't know why aliasmodule isn't printed (I would have thought # that it gets printed without the -a; although I'm not sure that # printing expansions without options is useful). @@ -2258,10 +3649,59 @@ U nameddir/b' dotest modules-155a2 "test -d first-dir/subdir" '' dotest modules-155a3 "test -d first-dir/subdir/ssdir" '' # Test that nothing extraneous got created. - dotest modules-155a4 "ls -1" "first-dir" + dotest modules-155a4 "ls" "first-dir" cd .. rm -rf 2 + # Test checking out everything. + mkdir 1 + cd 1 + dotest modules-155b "${testcvs} -q co world" \ +"U CVSROOT/modules +U first-dir/subdir/a +U first-dir/subdir/b" + cd .. + rm -rf 1 + + # Test checking out a module which lists at least two + # specific files twice. At one time, this failed over + # remote CVS. + mkdir 1 + cd 1 + dotest modules-155c1 "${testcvs} -q co first-dir" \ +"U first-dir/subdir/a +U first-dir/subdir/b" + + cd first-dir + echo 'first revision' > file1 + echo 'first revision' > file2 + dotest modules-155c2 "${testcvs} add file1 file2" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: scheduling file `file2'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add these files permanently' + dotest modules-155c3 "${testcvs} -q ci -m add-it" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file2,v +done +Checking in file2; +/tmp/cvs-sanity/cvsroot/first-dir/file2,v <-- file2 +initial revision: 1\.1 +done' + + cd .. + rm -rf first-dir + dotest modules-155c4 "${testcvs} -q co topfiles" \ +"U first-dir/file1 +U first-dir/file2" + dotest modules-155c5 "${testcvs} -q co topfiles" "" + cd .. + rm -rf 1 + rm -rf ${CVSROOT_DIRNAME}/first-dir ;; mflag) @@ -2355,7 +3795,7 @@ U nameddir/b' fi chmod a-w 1dir cd ../1/1dir - rm foo; + rm foo; if ${testcvs} rm foo >>${LOGFILE} 2>&1; then echo 'PASS: test 166' >>${LOGFILE} else @@ -2564,8 +4004,26 @@ abc [a-z0-9]* edit unedit commit' dotest devcom-a4 "${testcvs} watchers abb" \ 'abb [a-z0-9]* edit commit' + # Check tagging and checking out while we have a CVS + # directory in the repository. + dotest devcom-t0 "${testcvs} -q tag tag" \ +'T abb +T abc' cd ../.. - rm -rf 1 2 ${CVSROOT_DIRNAME}/first-dir + mkdir 3 + cd 3 + dotest devcom-t1 "${testcvs} -q co -rtag first-dir/abb" \ +'U first-dir/abb' + + # Now remove all the file attributes + cd ../2/first-dir + dotest devcom-b0 "${testcvs} watch off" '' + dotest devcom-b1 "${testcvs} watch remove" '' + # Test that CVS 1.6 and earlier can handle the repository. + dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" + + cd ../.. + rm -rf 1 2 3 ${CVSROOT_DIRNAME}/first-dir ;; ignore) @@ -2582,7 +4040,7 @@ abc [a-z0-9]* edit unedit commit' done Checking in cvsignore; /tmp/cvs-sanity/cvsroot/CVSROOT/cvsignore,v <-- cvsignore -initial revision: 1.1 +initial revision: 1\.1 done '"${PROG}"' [a-z]*: Rebuilding administrative file database' @@ -2638,7 +4096,6 @@ 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 @@ -2651,9 +4108,57 @@ ${QUESTION} defig.o ${QUESTION} envig.c ${QUESTION} optig.c ${QUESTION} notig.c" - cd .. - rm -rf first-dir + # Now test that commands other than update also print "? notig.c" + # where appropriate. Only test this for remote, because local + # CVS only prints it on update. + rm optig.c + if test "x$remote" = xyes; then + dotest 189e "${testcvs} -q diff" "${QUESTION} notig.c" + + # Force the server to be contacted. Ugh. Having CVS + # contact the server for the sole purpose of checking + # the CVSROOT/cvsignore file does not seem like such a + # good idea, so I imagine this will continue to be + # necessary. Oh well, at least we test CVS's ablity to + # handle a file with a modified timestamp but unmodified + # contents. + touch bar.c + + dotest 189f "${testcvs} -q ci -m commit-it" "${QUESTION} notig.c" + fi + + # now test .cvsignore files + cd .. + echo notig.c >first-dir/.cvsignore + echo foobar.c >second-dir/.cvsignore + touch first-dir/notig.c second-dir/notig.c second-dir/foobar.c + dotest 190 "${testcvs} -qn update" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" \ +"${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/notig.c +${QUESTION} second-dir/.cvsignore" + dotest 191 "${testcvs} -qn update -I!" \ +"${QUESTION} first-dir/CVS +${QUESTION} first-dir/rootig.c +${QUESTION} first-dir/defig.o +${QUESTION} first-dir/envig.c +${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/CVS +${QUESTION} second-dir/.cvsignore +${QUESTION} second-dir/notig.c" \ +"${QUESTION} first-dir/CVS +${QUESTION} first-dir/rootig.c +${QUESTION} first-dir/defig.o +${QUESTION} first-dir/envig.c +${QUESTION} first-dir/.cvsignore +${QUESTION} second-dir/CVS +${QUESTION} second-dir/notig.c +${QUESTION} second-dir/.cvsignore" + + rm -rf first-dir second-dir rm -rf ${CVSROOT_DIRNAME}/first-dir ${CVSROOT_DIRNAME}/second-dir ;; @@ -2675,7 +4180,7 @@ ${QUESTION} notig.c" done Checking in binfile; /tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile -initial revision: 1.1 +initial revision: 1\.1 done' cd ../.. mkdir 2; cd 2 @@ -2694,16 +4199,65 @@ File: binfile Status: Up-to-date Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kb' + + # Test whether the default options from the RCS file are + # also used when operating on files instead of whole + # directories + cd ../.. + mkdir 3; cd 3 + dotest binfiles-5.5b0 "${testcvs} -q co first-dir/binfile" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-5.5b1 "${testcvs} status binfile" \ +'=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb' + cd ../.. + rm -rf 3 + cd 2/first-dir + 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 +new revision: 1\.2; previous revision: 1\.1 done' cd ../../1/first-dir dotest binfiles-7 "${testcvs} -q update" '[UP] binfile' dotest binfiles-8 "cmp ../binfile2.dat binfile" '' + # Now test handling of conflicts with binary files. + cp ../binfile.dat binfile + dotest binfiles-con0 "${testcvs} -q ci -m modify-it" \ +'Checking in binfile; +/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.3; previous revision: 1\.2 +done' + cd ../../2/first-dir + echo 'edits in dir 2' >binfile + dotest binfiles-con1 "${testcvs} -q update" \ +'U binfile +cvs [a-z]*: binary file needs merge +cvs [a-z]*: revision 1\.3 from repository is now in binfile +cvs [a-z]*: file from working directory is now in \.#binfile\.1\.2 +C binfile' + dotest binfiles-con2 "cmp binfile ../../1/binfile.dat" '' + dotest binfiles-con3 "cat .#binfile.1.2" 'edits in dir 2' + + cp ../../1/binfile2.dat binfile + dotest binfiles-con4 "${testcvs} -q ci -m resolve-it" \ +'Checking in binfile; +/tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile +new revision: 1\.4; previous revision: 1\.3 +done' + cd ../../1/first-dir + dotest binfiles-con5 "${testcvs} -q update" '[UP] binfile' + # The bugs which these test for are apparently not fixed for remote. if test "$remote" = no; then dotest binfiles-9 "${testcvs} -q update -A" '' @@ -2713,12 +4267,32 @@ done' dotest binfiles-13 "${testcvs} -q update -A" '' fi - cd ../../2/first-dir + cd ../.. + rm -rf 1 + + mkdir 3 + cd 3 + dotest binfiles-13a0 "${testcvs} -q co -r HEAD first-dir" \ +'U first-dir/binfile' + cd first-dir + dotest binfiles-13a1 "${testcvs} status binfile" \ +'=================================================================== +File: binfile Status: Up-to-date + + Working revision: 1\.4.* + Repository revision: 1\.4 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Sticky Tag: HEAD (revision: 1\.4) + Sticky Date: (none) + Sticky Options: -kb' + cd ../.. + rm -rf 3 + + cd 2/first-dir echo 'this file is $''RCSfile$' >binfile dotest binfiles-14a "${testcvs} -q ci -m modify-it" \ 'Checking in binfile; /tmp/cvs-sanity/cvsroot/first-dir/binfile,v <-- binfile -new revision: 1.3; previous revision: 1.2 +new revision: 1\.5; previous revision: 1\.4 done' dotest binfiles-14b "cat binfile" 'this file is $''RCSfile$' # See binfiles-5.5 for discussion of -kb. @@ -2726,8 +4300,8 @@ done' '=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kb' @@ -2743,8 +4317,8 @@ done' '=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kb' @@ -2754,16 +4328,80 @@ File: binfile Status: Up-to-date '=================================================================== File: binfile Status: Up-to-date - Working revision: 1\.3.* - Repository revision: 1\.3 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v + Working revision: 1\.5.* + Repository revision: 1\.5 /tmp/cvs-sanity/cvsroot/first-dir/binfile,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kv' + # Do sticky options work when used with 'cvs update'? + echo "Not a binary file." > nibfile + dotest binfiles-sticky1 "${testcvs} -q add nibfile" \ + 'cvs [a-z]*: use '\''cvs commit'\'' to add this file permanently' + dotest binfiles-sticky2 "${testcvs} -q ci -m add-it nibfile" \ + 'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/nibfile,v +done +Checking in nibfile; +/tmp/cvs-sanity/cvsroot/first-dir/nibfile,v <-- nibfile +initial revision: 1\.1 +done' + dotest binfiles-sticky3 "${testcvs} -q update -kb nibfile" \ + '[UP] nibfile' + dotest binfiles-sticky4 "${testcvs} -q status nibfile" \ +'=================================================================== +File: nibfile Status: Up-to-date + + Working revision: 1\.1.* + Repository revision: 1\.1 /tmp/cvs-sanity/cvsroot/first-dir/nibfile,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb' + # Eventually we should test that -A removes the -kb here... + cd ../.. rm -rf ${CVSROOT_DIRNAME}/first-dir - rm -r 1 2 + rm -r 2 + ;; + + binwrap) + # Test the ability to specify binary-ness based on file name. + # We could also be testing the ability to use the other + # ways to specify a wrapper (CVSROOT/cvswrappers, etc.). + + mkdir dir-to-import + cd dir-to-import + touch foo.c foo.exe + if ${testcvs} import -m message -I ! -W "*.exe -k 'b'" \ + first-dir tag1 tag2 >>${LOGFILE}; then + pass binwrap-1 + else + fail binwrap-1 + fi + cd .. + rm -rf dir-to-import + dotest binwrap-2 "${testcvs} -q co first-dir" 'U first-dir/foo.c +U first-dir/foo.exe' + dotest binwrap-3 "${testcvs} -q status first-dir" \ +'=================================================================== +File: foo\.c Status: Up-to-date + + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 /tmp/cvs-sanity/cvsroot/first-dir/foo\.c,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: (none) + +=================================================================== +File: foo\.exe Status: Up-to-date + + Working revision: 1\.1\.1\.1.* + Repository revision: 1\.1\.1\.1 /tmp/cvs-sanity/cvsroot/first-dir/foo\.exe,v + Sticky Tag: (none) + Sticky Date: (none) + Sticky Options: -kb' + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir ;; + info) # Test CVS's ability to handle *info files. dotest info-1 "${testcvs} -q co CVSROOT" "[UP] CVSROOT${DOTSTAR}" @@ -2777,7 +4415,7 @@ File: binfile Status: Up-to-date done Checking in loginfo; /tmp/cvs-sanity/cvsroot/CVSROOT/loginfo,v <-- loginfo -initial revision: 1.1 +initial revision: 1\.1 done '"${PROG}"' [a-z]*: Rebuilding administrative file database' cd .. @@ -2800,14 +4438,14 @@ done done Checking in file1; /tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -initial revision: 1.1 +initial revision: 1\.1 done '"${PROG}"' [a-z]*: loginfo:1: no such user variable ${=ZEE}' echo line1 >>file1 dotest info-7 "${testcvs} -q -s OTHER=value -s ZEE=z ci -m mod-it" \ 'Checking in file1; /tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 -new revision: 1.2; previous revision: 1.1 +new revision: 1\.2; previous revision: 1\.1 done' cd .. if echo "yes" | ${testcvs} release -d first-dir >>${LOGFILE} ; then @@ -2815,7 +4453,7 @@ done' else fail info-8 fi - dotest info-9 "cat $TESTDIR/testlog" 'xenv-valueyz=[a-z@][a-z@]*=/tmp/cvs-sanity/cvsroot=' + dotest info-9 "cat $TESTDIR/testlog" 'xenv-valueyz=[a-z0-9@][a-z0-9@]*=/tmp/cvs-sanity/cvsroot=' # I think this might be doable with cvs remove, or at least # checking in a version with only comments, but I'm too lazy @@ -2824,6 +4462,290 @@ done' rm -rf ${CVSROOT_DIRNAME}/first-dir ;; + + serverpatch) + # Test remote CVS handling of unpatchable files. This isn't + # much of a test for local CVS. + mkdir ${CVSROOT_DIRNAME}/first-dir + mkdir 1 + cd 1 + dotest serverpatch-1 "${testcvs} -q co first-dir" '' + + cd first-dir + + # Add a file with an RCS keyword. + echo '$''Name$' > file1 + echo '1' >> file1 + dotest serverpatch-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest serverpatch-3 "${testcvs} -q commit -m add" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done' + + # Tag the file. + dotest serverpatch-4 "${testcvs} -q tag tag file1" 'T file1' + + # Check out a tagged copy of the file. + cd ../.. + mkdir 2 + cd 2 + dotest serverpatch-5 "${testcvs} -q co -r tag first-dir" \ +'U first-dir/file1' + + # Remove the tag. This will leave the tag string in the + # expansion of the Name keyword. + dotest serverpatch-6 "${testcvs} -q update -A" '' + + # Modify and check in the first copy. + cd ../1/first-dir + echo '2' >> file1 + dotest serverpatch-7 "${testcvs} -q ci -mx file1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done' + + # Now update the second copy. When using remote CVS, the + # patch will fail, forcing the file to be refetched. + cd ../../2/first-dir + dotest serverpatch-8 "${testcvs} -q update" \ +'U file1' \ +'P file1 +'"${PROG}"' [a-z]*: checksum failure after patch to ./file1; will refetch +'"${PROG}"' [a-z]*: refetching unpatchable files +U file1' + + cd ../.. + rm -rf 1 2 ${CVSROOT_DIRNAME}/first-dir + ;; + + log) + # Test selecting revisions with cvs log. + + # Check in a file with a few revisions and branches. + mkdir ${CVSROOT_DIRNAME}/first-dir + dotest log-1 "${testcvs} -q co first-dir" '' + cd first-dir + echo 'first revision' > file1 + dotest log-2 "${testcvs} add file1" \ +"${PROG}"' [a-z]*: scheduling file `file1'\'' for addition +'"${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + dotest log-3 "${testcvs} -q commit -m 1" \ +'RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +done +Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +initial revision: 1\.1 +done' + + echo 'second revision' > file1 + dotest log-4 "${testcvs} -q ci -m2 file1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2; previous revision: 1\.1 +done' + + dotest log-5 "${testcvs} -q tag -b branch file1" 'T file1' + + echo 'third revision' > file1 + dotest log-6 "${testcvs} -q ci -m3 file1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.3; previous revision: 1\.2 +done' + + dotest log-7 "${testcvs} -q update -r branch" '[UP] file1' + + echo 'first branch revision' > file1 + dotest log-8 "${testcvs} -q ci -m1b file1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.1; previous revision: 1\.2 +done' + + dotest log-9 "${testcvs} -q tag tag file1" 'T file1' + + echo 'second branch revision' > file1 + dotest log-10 "${testcvs} -q ci -m2b file1" \ +'Checking in file1; +/tmp/cvs-sanity/cvsroot/first-dir/file1,v <-- file1 +new revision: 1\.2\.2\.2; previous revision: 1\.2\.2\.1 +done' + + # Set up a bunch of shell variables to make the later tests + # easier to describe.= + log_header=' +RCS file: /tmp/cvs-sanity/cvsroot/first-dir/file1,v +Working file: file1 +head: 1\.3 +branch: +locks: strict +access list:' + log_tags='symbolic names: + tag: 1\.2\.2\.1 + branch: 1\.2\.0\.2' + log_header2='keyword substitution: kv' + log_dash='---------------------------- +revision' + log_date='date: [0-9/]* [0-9:]*; author: [a-zA-Z0-9@]*; state: Exp;' + log_lines=" lines: ${PLUS}1 -1" + log_rev1="${log_dash} 1\.1 +${log_date} +1" + log_rev2="${log_dash} 1\.2 +${log_date}${log_lines} +branches: 1\.2\.2; +2" + log_rev3="${log_dash} 1\.3 +${log_date}${log_lines} +3" + log_rev1b="${log_dash} 1\.2\.2\.1 +${log_date}${log_lines} +1b" + log_rev2b="${log_dash} 1\.2\.2\.2 +${log_date}${log_lines} +2b" + log_trailer='=============================================================================' + + # Now, finally, test the log output. + + dotest log-11 "${testcvs} log file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-12 "${testcvs} log -N file1" \ +"${log_header} +${log_header2} +total revisions: 5; selected revisions: 5 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-13 "${testcvs} log -b file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 3 +description: +${log_rev3} +${log_rev2} +${log_rev1} +${log_trailer}" + + dotest log-14 "${testcvs} log -r file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev3} +${log_trailer}" + + dotest log-15 "${testcvs} log -r1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2} +${log_trailer}" + + dotest log-16 "${testcvs} log -r1.2.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" + + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-17 "${testcvs} log -rbranch file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2b} +${log_rev1b} +${log_trailer}" + + dotest log-18 "${testcvs} log -r1.2.2. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" + + # This test would fail with the old invocation of rlog, but it + # works with the builtin log support. + dotest log-19 "${testcvs} log -rbranch. file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 1 +description: +${log_rev2b} +${log_trailer}" + + dotest log-20 "${testcvs} log -r1.2: file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev3} +${log_rev2} +${log_trailer}" + + dotest log-21 "${testcvs} log -r:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" + + dotest log-22 "${testcvs} log -r1.1:1.2 file1" \ +"${log_header} +${log_tags} +${log_header2} +total revisions: 5; selected revisions: 2 +description: +${log_rev2} +${log_rev1} +${log_trailer}" + + cd .. + rm -rf first-dir ${CVSROOT_DIRNAME}/first-dir + ;; + *) echo $what is not the name of a test -- ignored ;; @@ -2833,6 +4755,7 @@ done echo "OK, all tests completed." # TODO: +# * use "test" not "[" and see if all test's support `-z' # * Test `cvs admin'. # * Test `cvs update -d foo' (where foo does not exist). # * Test `cvs update foo bar' (where foo and bar are both from the same @@ -2844,7 +4767,7 @@ echo "OK, all tests completed." # Test that ciprog gets run both on checkin in that directory, or a # higher-level checkin which recurses into it. # * Test that $ followed by "Header" followed by $ gets expanded on checkin. -# * Test operations on a directory that contains other directories but has +# * Test operations on a directory that contains other directories but has # no files of its own. # * -t global option # * cvs rm followed by cvs add or vice versa (with no checkin in between). @@ -2852,7 +4775,7 @@ echo "OK, all tests completed." # * -P option to checkout--(a) refrains from checking out new empty dirs, # (b) prunes empty dirs already there. # * Test that cvs -d `hostname`:/tmp/cvs-sanity/non/existent co foo -# gives an appropriate error (e.g. +# gives an appropriate error (e.g. # Cannot access /tmp/cvs-sanity/non-existent/CVSROOT # No such file or directory). # * Test ability to send notifications in response to watches. (currently @@ -2860,6 +4783,7 @@ echo "OK, all tests completed." # same). # * Test that remote edit and/or unedit works when disconnected from # server (e.g. set CVS_SERVER to "foobar"). +# * Test things to do with the CVS/* files, esp. CVS/Root.... # End of TODO list. # Remove the test directory, but first change out of it. diff --git a/gnu/usr.bin/cvs/src/server.h b/gnu/usr.bin/cvs/src/server.h index 30ddb8c4ae6..25ce596a60d 100644 --- a/gnu/usr.bin/cvs/src/server.h +++ b/gnu/usr.bin/cvs/src/server.h @@ -49,19 +49,20 @@ extern void server_checked_in extern void server_copy_file PROTO((char *file, char *update_dir, char *repository, char *newfile)); -/* - * We just successfully updated FILE (bare filename, no directory). - * REPOSITORY is the directory for the repository. This is called - * after server_register or server_scratch, in the latter case the - * file is to be removed. UPDATED indicates whether the file is now - * up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, SERVER_PATCHED, - * yes, but file is a diff from user version to repository version). - */ +/* Send the appropriate responses for a file described by FILE, + UPDATE_DIR, REPOSITORY, and VERS. FILE_INFO is the result of + statting the file, or NULL if it hasn't been statted yet. This is + called after server_register or server_scratch. In the latter case + the file is to be removed (and vers can be NULL). In the former + case, vers must be non-NULL, and UPDATED indicates whether the file + is now up to date (SERVER_UPDATED, yes, SERVER_MERGED, no, + SERVER_PATCHED, yes, but file is a diff from user version to + repository version). */ enum server_updated_arg4 {SERVER_UPDATED, SERVER_MERGED, SERVER_PATCHED}; extern void server_updated - PROTO((char *file, char *update_dir, char *repository, - enum server_updated_arg4 updated, struct stat *, - unsigned char *checksum)); + PROTO((struct file_info *finfo, Vers_TS *vers, + enum server_updated_arg4 updated, struct stat *, + unsigned char *checksum)); /* Set the Entries.Static flag. */ extern void server_set_entstat PROTO((char *update_dir, char *repository)); @@ -77,7 +78,7 @@ extern void server_template PROTO ((char *, char *)); extern void server_update_entries PROTO((char *file, char *update_dir, char *repository, - enum server_updated_arg4 updated)); + enum server_updated_arg4 updated)); enum progs {PROG_CHECKIN, PROG_UPDATE}; extern void server_prog PROTO((char *, char *, enum progs)); diff --git a/gnu/usr.bin/cvs/src/status.c b/gnu/usr.bin/cvs/src/status.c index 277da0c2944..203e0aae84d 100644 --- a/gnu/usr.bin/cvs/src/status.c +++ b/gnu/usr.bin/cvs/src/status.c @@ -10,8 +10,10 @@ #include "cvs.h" -static Dtype status_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int status_fileproc PROTO((struct file_info *finfo)); +static Dtype status_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int status_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int tag_list_proc PROTO((Node * p, void *closure)); static int local = 0; @@ -87,9 +89,10 @@ status (argc, argv) #endif /* start the recursion processor */ - err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, status_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, local, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, + status_dirproc, (DIRLEAVEPROC) NULL, NULL, + argc, argv, local, + W_LOCAL, 0, 1, (char *) NULL, 1); return (err); } @@ -99,16 +102,17 @@ status (argc, argv) */ /* ARGSUSED */ static int -status_fileproc (finfo) +status_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { Ctype status; char *sstat; Vers_TS *vers; - status = Classify_File (finfo->file, (char *) NULL, (char *) NULL, (char *) NULL, - 1, 0, finfo->repository, finfo->entries, finfo->rcs, &vers, - finfo->update_dir, 0); + status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, + 1, 0, &vers, 0); + sstat = "Classify Error"; switch (status) { case T_UNKNOWN: @@ -146,34 +150,66 @@ status_fileproc (finfo) case T_NEEDS_MERGE: sstat = "Needs Merge"; break; - default: - sstat = "Classify Error"; + case T_TITLE: + /* I don't think this case can occur here. Just print + "Classify Error". */ break; } - (void) printf ("===================================================================\n"); + cvs_output ("\ +===================================================================\n", 0); if (vers->ts_user == NULL) - (void) printf ("File: no file %s\t\tStatus: %s\n\n", finfo->file, sstat); + { + cvs_output ("File: no file ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\t\tStatus: ", 0); + cvs_output (sstat, 0); + cvs_output ("\n\n", 0); + } else - (void) printf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat); + { + char *buf; + buf = xmalloc (strlen (finfo->file) + strlen (sstat) + 80); + sprintf (buf, "File: %-17s\tStatus: %s\n\n", finfo->file, sstat); + cvs_output (buf, 0); + free (buf); + } if (vers->vn_user == NULL) - (void) printf (" Working revision:\tNo entry for %s\n", finfo->file); + { + cvs_output (" Working revision:\tNo entry for ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\n", 0); + } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') - (void) printf (" Working revision:\tNew file!\n"); + cvs_output (" Working revision:\tNew file!\n", 0); #ifdef SERVER_SUPPORT else if (server_active) - (void) printf (" Working revision:\t%s\n", vers->vn_user); + { + cvs_output (" Working revision:\t", 0); + cvs_output (vers->vn_user, 0); + cvs_output ("\n", 0); + } #endif else - (void) printf (" Working revision:\t%s\t%s\n", vers->vn_user, - vers->ts_rcs); + { + cvs_output (" Working revision:\t", 0); + cvs_output (vers->vn_user, 0); + cvs_output ("\t", 0); + cvs_output (vers->ts_rcs, 0); + cvs_output ("\n", 0); + } if (vers->vn_rcs == NULL) - (void) printf (" Repository revision:\tNo revision control file\n"); + cvs_output (" Repository revision:\tNo revision control file\n", 0); else - (void) printf (" Repository revision:\t%s\t%s\n", vers->vn_rcs, - vers->srcfile->path); + { + cvs_output (" Repository revision:\t", 0); + cvs_output (vers->vn_rcs, 0); + cvs_output ("\t", 0); + cvs_output (vers->srcfile->path, 0); + cvs_output ("\n", 0); + } if (vers->entdata) { @@ -183,24 +219,33 @@ status_fileproc (finfo) if (edata->tag) { if (vers->vn_rcs == NULL) - (void) printf ( - " Sticky Tag:\t\t%s - MISSING from RCS file!\n", - edata->tag); + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output (" - MISSING from RCS file!\n", 0); + } else { if (isdigit (edata->tag[0])) - (void) printf (" Sticky Tag:\t\t%s\n", edata->tag); + { + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output ("\n", 0); + } else { char *branch = NULL; - + if (RCS_isbranch (finfo->rcs, edata->tag)) branch = RCS_whatbranch(finfo->rcs, edata->tag); - (void) printf (" Sticky Tag:\t\t%s (%s: %s)\n", - edata->tag, - branch ? "branch" : "revision", - branch ? branch : vers->vn_rcs); + cvs_output (" Sticky Tag:\t\t", 0); + cvs_output (edata->tag, 0); + cvs_output (" (", 0); + cvs_output (branch ? "branch" : "revision", 0); + cvs_output (": ", 0); + cvs_output (branch ? branch : vers->vn_rcs, 0); + cvs_output (")\n", 0); if (branch) free (branch); @@ -208,34 +253,42 @@ status_fileproc (finfo) } } else if (!really_quiet) - (void) printf (" Sticky Tag:\t\t(none)\n"); + cvs_output (" Sticky Tag:\t\t(none)\n", 0); if (edata->date) - (void) printf (" Sticky Date:\t\t%s\n", edata->date); + { + cvs_output (" Sticky Date:\t\t", 0); + cvs_output (edata->date, 0); + cvs_output ("\n", 0); + } else if (!really_quiet) - (void) printf (" Sticky Date:\t\t(none)\n"); + cvs_output (" Sticky Date:\t\t(none)\n", 0); if (edata->options && edata->options[0]) - (void) printf (" Sticky Options:\t%s\n", edata->options); + { + cvs_output (" Sticky Options:\t", 0); + cvs_output (edata->options, 0); + cvs_output ("\n", 0); + } else if (!really_quiet) - (void) printf (" Sticky Options:\t(none)\n"); + cvs_output (" Sticky Options:\t(none)\n", 0); if (long_format && vers->srcfile) { List *symbols = RCS_symbols(vers->srcfile); - (void) printf ("\n Existing Tags:\n"); + cvs_output ("\n Existing Tags:\n", 0); if (symbols) { xrcsnode = finfo->rcs; (void) walklist (symbols, tag_list_proc, NULL); } else - (void) printf ("\tNo Tags Exist\n"); + cvs_output ("\tNo Tags Exist\n", 0); } } - (void) printf ("\n"); + cvs_output ("\n", 0); freevers_ts (&vers); return (0); } @@ -245,10 +298,12 @@ status_fileproc (finfo) */ /* ARGSUSED */ static Dtype -status_dirproc (dir, repos, update_dir) +status_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "Examining %s", update_dir); @@ -264,13 +319,18 @@ tag_list_proc (p, closure) void *closure; { char *branch = NULL; + char *buf; if (RCS_isbranch (xrcsnode, p->key)) branch = RCS_whatbranch(xrcsnode, p->key) ; - (void) printf ("\t%-25.25s\t(%s: %s)\n", p->key, - branch ? "branch" : "revision", - branch ? branch : p->data); + buf = xmalloc (80 + strlen (p->key) + + (branch ? strlen (branch) : strlen (p->data))); + sprintf (buf, "\t%-25.25s\t(%s: %s)\n", p->key, + branch ? "branch" : "revision", + branch ? branch : p->data); + cvs_output (buf, 0); + free (buf); if (branch) free (branch); diff --git a/gnu/usr.bin/cvs/src/tag.c b/gnu/usr.bin/cvs/src/tag.c index 2e3000945de..312c8820b6c 100644 --- a/gnu/usr.bin/cvs/src/tag.c +++ b/gnu/usr.bin/cvs/src/tag.c @@ -14,15 +14,22 @@ #include "cvs.h" #include "savecwd.h" -static int check_fileproc PROTO((struct file_info *finfo)); -static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int check_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static int pretag_proc PROTO((char *repository, char *filter)); static void masterlist_delproc PROTO((Node *p)); static void tag_delproc PROTO((Node *p)); static int pretag_list_proc PROTO((Node *p, void *closure)); -static Dtype tag_dirproc PROTO((char *dir, char *repos, char *update_dir)); -static int tag_fileproc PROTO((struct file_info *finfo)); +static Dtype tag_dirproc PROTO ((void *callerdat, char *dir, + char *repos, char *update_dir, + List *entries)); +static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); +static int tag_filesdoneproc PROTO ((void *callerdat, int err, + char *repos, char *update_dir, + List *entries)); static char *numtag; static char *date = NULL; @@ -32,6 +39,7 @@ static int branch_mode; /* make an automagic "branch" tag */ static int local; /* recursive by default */ static int force_tag_match = 1; /* force tag to match by default */ static int force_tag_move; /* don't force tag to move by default */ +static int check_uptodate; /* no uptodate-check by default */ struct tag_info { @@ -51,14 +59,15 @@ static List *tlist; static const char *const tag_usage[] = { - "Usage: %s %s [-lRF] [-b] [-d] [-r tag|-D date] tag [files...]\n", + "Usage: %s %s [-lRF] [-b] [-d] [-c] [-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", + "\t-d\tDelete the given tag.\n", "\t-[rD]\tExisting tag or date.\n", - "\t-f\tForce a head revision if tag etc not found.\n", + "\t-f\tForce a head revision if specified tag not found.\n", "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", - "\t-F\tMove tag if it already exists\n", + "\t-F\tMove tag if it already exists.\n", + "\t-c\tCheck that working files are unmodified.\n", NULL }; @@ -74,7 +83,7 @@ tag (argc, argv) usage (tag_usage); optind = 1; - while ((c = getopt (argc, argv, "FQqlRdr:D:bf")) != -1) + while ((c = getopt (argc, argv, "FQqlRcdr:D:bf")) != -1) { switch (c) { @@ -98,6 +107,9 @@ tag (argc, argv) case 'd': delete_flag = 1; break; + case 'c': + check_uptodate = 1; + break; case 'r': numtag = optarg; break; @@ -148,6 +160,8 @@ tag (argc, argv) send_arg("-l"); if (delete_flag) send_arg("-d"); + if (check_uptodate) + send_arg("-c"); if (branch_mode) send_arg("-b"); if (force_tag_move) @@ -178,9 +192,9 @@ tag (argc, argv) mtlist = getlist(); err = start_recursion (check_fileproc, check_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, - (char *) NULL, 1, 0); + (char *) NULL, 1); if (err) { @@ -188,9 +202,9 @@ tag (argc, argv) } /* start the recursion processor */ - err = start_recursion (tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc, - (DIRLEAVEPROC) NULL, argc, argv, local, - W_LOCAL, 0, 1, (char *) NULL, 1, 0); + err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc, + (DIRLEAVEPROC) NULL, NULL, argc, argv, local, + W_LOCAL, 0, 0, (char *) NULL, 1); dellist(&mtlist); return (err); } @@ -199,13 +213,25 @@ tag (argc, argv) /* All we do here is add it to our list */ static int -check_fileproc (finfo) +check_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *xdir; Node *p; Vers_TS *vers; + if (check_uptodate) + { + Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL, + (char *) NULL, 1, 0, &vers, 0); + if ((status != T_UPTODATE) && (status != T_CHECKOUT)) + { + error (0, 0, "%s is locally modified", finfo->fullname); + return (1); + } + } + if (finfo->update_dir[0] == '\0') xdir = "."; else @@ -234,22 +260,22 @@ check_fileproc (finfo) p->key = xstrdup (finfo->file); p->type = UPDATE; p->delproc = tag_delproc; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, - (char *) NULL, finfo->file, 0, 0, - finfo->entries, finfo->rcs); + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if (vers->srcfile == NULL) { if (!really_quiet) error (0, 0, "nothing known about %s", finfo->file); return (1); } - p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, + (int *) NULL); if (p->data != NULL) { int addit = 1; char *oversion; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion == NULL) { if (delete_flag) @@ -281,10 +307,12 @@ check_fileproc (finfo) } static int -check_filesdoneproc(err, repos, update_dir) +check_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; int err; char *repos; char *update_dir; + List *entries; { int n; Node *p; @@ -389,7 +417,8 @@ pretag_list_proc(p, closure) */ /* ARGSUSED */ static int -tag_fileproc (finfo) +tag_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *version, *oversion; @@ -398,15 +427,20 @@ tag_fileproc (finfo) Vers_TS *vers; int retcode = 0; - vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, - finfo->file, 0, 0, finfo->entries, finfo->rcs); + /* Lock the directory if it is not already locked. We can't rely + on tag_dirproc because it won't handle the case where the user + specifies a list of files on the command line. */ + tag_lockdir (finfo->repository); + + vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); if ((numtag != NULL) || (date != NULL)) { nversion = RCS_getversion(vers->srcfile, numtag, date, - force_tag_match, 0); + force_tag_match, + (int *) NULL); if (nversion == NULL) { freevers_ts (&vers); @@ -425,7 +459,8 @@ tag_fileproc (finfo) * "rcs" to remove the tag... trust me. */ - version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (version == NULL || vers->srcfile == NULL) { freevers_ts (&vers); @@ -433,7 +468,7 @@ tag_fileproc (finfo) } free (version); - if ((retcode = RCS_deltag(vers->srcfile->path, symtag, 1)) != 0) + if ((retcode = RCS_deltag(vers->srcfile, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -501,7 +536,8 @@ tag_fileproc (finfo) * 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, 0); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, + (int *) NULL); if (oversion != NULL) { int isbranch = RCS_isbranch (finfo->rcs, symtag); @@ -533,7 +569,7 @@ tag_fileproc (finfo) free (oversion); } - if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0) + if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) { error (1, retcode == -1 ? errno : 0, "failed to set tag %s to revision %s in %s", @@ -556,21 +592,98 @@ tag_fileproc (finfo) return (0); } +/* Clear any lock we may hold on the current directory. */ + +static int +tag_filesdoneproc (callerdat, err, repos, update_dir, entries) + void *callerdat; + int err; + char *repos; + char *update_dir; + List *entries; +{ + tag_unlockdir (); + + return (err); +} + /* * Print a warm fuzzy message */ /* ARGSUSED */ static Dtype -tag_dirproc (dir, repos, update_dir) +tag_dirproc (callerdat, dir, repos, update_dir, entries) + void *callerdat; char *dir; char *repos; char *update_dir; + List *entries; { if (!quiet) error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); return (R_PROCESS); } +/* We do not need to acquire a full write lock for the tag operation: + the revisions are obtained from the working directory, so we do not + require consistency across the entire repository. However, we do + need to prevent simultaneous tag operations from interfering with + each other. Therefore, we write lock each directory as we enter + it, and unlock it as we leave it. + + In the rtag case, it would be nice to provide consistency with + respect to commits; however CVS lacks the infrastructure to do that + (see Concurrency in cvs.texinfo and comment in do_recursion). We + can and will prevent simultaneous tag operations from interfering + with each other, by write locking each directory as we enter it, + and unlocking it as we leave it. */ +static char *locked_dir; +static List *locked_list; + +/* + * Lock the directory for a tag operation. This is also called by the + * rtag code. + */ +void +tag_lockdir (repository) + char *repository; +{ + if (repository != NULL + && (locked_dir == NULL + || strcmp (locked_dir, repository) != 0)) + { + Node *node; + + if (locked_dir != NULL) + tag_unlockdir (); + + locked_dir = xstrdup (repository); + locked_list = getlist (); + node = getnode (); + node->type = LOCK; + node->key = xstrdup (repository); + (void) addnode (locked_list, node); + Writer_Lock (locked_list); + } +} + +/* + * Unlock the directory for a tag operation. This is also called by + * the rtag code. + */ +void +tag_unlockdir () +{ + if (locked_dir != NULL) + { + Lock_Cleanup (); + dellist (&locked_list); + free (locked_dir); + locked_dir = NULL; + locked_list = NULL; + } +} + /* 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 @@ -584,18 +697,15 @@ struct val_args { 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 ((struct file_info *finfo)); +static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static int -val_fileproc (finfo) +val_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { RCSNode *rcsdata; - struct val_args *args = val_args_static; + struct val_args *args = (struct val_args *)callerdat; char *tag; if ((rcsdata = finfo->rcs) == NULL) @@ -603,7 +713,7 @@ val_fileproc (finfo) W_REPOS | W_ATTIC. */ return 0; - tag = RCS_gettag (rcsdata, args->name, 1, 0); + tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); if (tag != NULL) { /* FIXME: should find out a way to stop the search at this point. */ @@ -613,13 +723,15 @@ val_fileproc (finfo) return 0; } -static Dtype val_direntproc PROTO ((char *, char *, char *)); +static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *)); static Dtype -val_direntproc (dir, repository, update_dir) +val_direntproc (callerdat, dir, repository, update_dir, entries) + void *callerdat; char *dir; char *repository; char *update_dir; + List *entries; { /* This is not quite right--it doesn't get right the case of "cvs update -d -r foobar" where foobar is a tag which exists only in @@ -672,12 +784,18 @@ Numeric tag %s contains characters other than digits and '.'", name); return; } + /* Special tags are always valid. */ + if (strcmp (name, TAG_BASE) == 0 + || strcmp (name, TAG_HEAD) == 0) + return; + mytag.dptr = name; mytag.dsize = strlen (name); - valtags_filename = xmalloc (strlen (CVSroot) + sizeof CVSROOTADM - + sizeof CVSROOTADM_HISTORY + 20); - strcpy (valtags_filename, CVSroot); + valtags_filename = xmalloc (strlen (CVSroot_directory) + + sizeof CVSROOTADM + + sizeof CVSROOTADM_VALTAGS + 20); + strcpy (valtags_filename, CVSroot_directory); strcat (valtags_filename, "/"); strcat (valtags_filename, CVSROOTADM); strcat (valtags_filename, "/"); @@ -709,11 +827,15 @@ Numeric tag %s contains characters other than digits and '.'", name); /* 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. */ + by an old version of CVS, from before val-tags was invented. + + Since we need this code anyway, we also use it to create + entries in val-tags in general (that is, the val-tags entry + will get created the first time the tag is used, not when the + tag is created). */ the_val_args.name = name; the_val_args.found = 0; - val_args_static = &the_val_args; which = W_REPOS | W_ATTIC; @@ -725,15 +847,16 @@ Numeric tag %s contains characters other than digits and '.'", name); { if (save_cwd (&cwd)) exit (EXIT_FAILURE); - if (chdir (repository) < 0) + if ( CVS_CHDIR (repository) < 0) error (1, errno, "cannot change to %s directory", repository); } } err = start_recursion (val_fileproc, (FILESDONEPROC) NULL, val_direntproc, (DIRLEAVEPROC) NULL, + (void *)&the_val_args, argc, argv, local, which, aflag, - 1, NULL, 1, 0); + 1, NULL, 1); if (repository != NULL && repository[0] != '\0') { if (restore_cwd (&cwd, NULL)) @@ -779,3 +902,36 @@ Numeric tag %s contains characters other than digits and '.'", name); } free (valtags_filename); } + +/* + * Check whether a join tag is valid. This is just like + * tag_check_valid, but we must stop before the colon if there is one. + */ + +void +tag_check_valid_join (join_tag, argc, argv, local, aflag, repository) + char *join_tag; + int argc; + char **argv; + int local; + int aflag; + char *repository; +{ + char *c, *s; + + c = xstrdup (join_tag); + s = strchr (c, ':'); + if (s != NULL) + { + if (isdigit (join_tag[0])) + error (1, 0, + "Numeric join tag %s may not contain a date specifier", + join_tag); + + *s = '\0'; + } + + tag_check_valid (c, argc, argv, local, aflag, repository); + + free (c); +} diff --git a/gnu/usr.bin/cvs/src/vers_ts.c b/gnu/usr.bin/cvs/src/vers_ts.c index 34983a125d8..415b68785b6 100644 --- a/gnu/usr.bin/cvs/src/vers_ts.c +++ b/gnu/usr.bin/cvs/src/vers_ts.c @@ -18,17 +18,13 @@ static void time_stamp_server PROTO((char *, Vers_TS *)); * the current source control file - preparsed for our pleasure. */ Vers_TS * -Version_TS (repository, options, tag, date, user, force_tag_match, - set_time, entries, rcs) - char *repository; +Version_TS (finfo, options, tag, date, force_tag_match, set_time) + struct file_info *finfo; char *options; char *tag; char *date; - char *user; int force_tag_match; int set_time; - List *entries; - RCSNode *rcs; { Node *p; RCSNode *rcsdata; @@ -44,15 +40,15 @@ Version_TS (repository, options, tag, date, user, force_tag_match, * if entries is NULL, there is no entries file so don't bother trying to * look it up (used by checkout -P) */ - if (entries == NULL) + if (finfo->entries == NULL) { sdtp = NULL; p = NULL; } else { - p = findnode_fn (entries, user); - sdtp = (struct stickydirtag *) entries->list->data; /* list-private */ + p = findnode_fn (finfo->entries, finfo->file); + sdtp = (struct stickydirtag *) finfo->entries->list->data; /* list-private */ } if (p != NULL) @@ -88,15 +84,13 @@ Version_TS (repository, options, tag, date, user, force_tag_match, vers_ts->options = xstrdup (options); else if (!vers_ts->options) { - if (sdtp && sdtp->aflag == 0) - vers_ts->options = xstrdup (sdtp->options); - else if (rcs != NULL) + if (finfo->rcs != NULL) { /* If no keyword expansion was specified on command line, use whatever was in the rcs file (if there is one). This is how we, if we are the server, tell the client whether a file is binary. */ - char *rcsexpand = RCS_getexpand (rcs); + char *rcsexpand = RCS_getexpand (finfo->rcs); if (rcsexpand != NULL) { vers_ts->options = xmalloc (strlen (rcsexpand) + 3); @@ -126,13 +120,13 @@ Version_TS (repository, options, tag, date, user, force_tag_match, } /* Now look up the info on the source controlled file */ - if (rcs != NULL) + if (finfo->rcs != NULL) { - rcsdata = rcs; + rcsdata = finfo->rcs; rcsdata->refcount++; } - else if (repository != NULL) - rcsdata = RCS_parse (user, repository); + else if (finfo->repository != NULL) + rcsdata = RCS_parse (finfo->file, finfo->repository); else rcsdata = NULL; @@ -148,21 +142,17 @@ Version_TS (repository, options, tag, date, user, force_tag_match, } else { + int simple; + vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag, - vers_ts->date, force_tag_match, 1); + vers_ts->date, force_tag_match, + &simple); if (vers_ts->vn_rcs == NULL) vers_ts->vn_tag = NULL; + else if (simple) + vers_ts->vn_tag = xstrdup (vers_ts->tag); else - { - 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); - } + vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs); } /* @@ -178,19 +168,19 @@ Version_TS (repository, options, tag, date, user, force_tag_match, if (vers_ts->vn_rcs && (t.actime = t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, (char *) 0, 0)) != -1) - (void) utime (user, &t); + (void) utime (finfo->file, &t); } } /* get user file time-stamp in ts_user */ - if (entries != (List *) NULL) + if (finfo->entries != (List *) NULL) { #ifdef SERVER_SUPPORT if (server_active) - time_stamp_server (user, vers_ts); + time_stamp_server (finfo->file, vers_ts); else #endif - vers_ts->ts_user = time_stamp (user); + vers_ts->ts_user = time_stamp (finfo->file); } return (vers_ts); @@ -212,7 +202,7 @@ time_stamp_server (file, vers_ts) struct stat sb; char *cp; - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) { if (! existence_error (errno)) error (1, errno, "cannot stat temp file"); @@ -290,7 +280,7 @@ time_stamp (file) char *cp; char *ts; - if (stat (file, &sb) < 0) + if ( CVS_STAT (file, &sb) < 0) { ts = NULL; } diff --git a/gnu/usr.bin/cvs/src/version.c b/gnu/usr.bin/cvs/src/version.c index 4848e82b1f1..d74012af9fd 100644 --- a/gnu/usr.bin/cvs/src/version.c +++ b/gnu/usr.bin/cvs/src/version.c @@ -12,7 +12,7 @@ #include "cvs.h" -char *version_string = "\nConcurrent Versions System (CVS) 1.8.1"; +char *version_string = "\nConcurrent Versions System (CVS) 1.9"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT diff --git a/gnu/usr.bin/cvs/src/watch.c b/gnu/usr.bin/cvs/src/watch.c index 08734895d28..80626e3d508 100644 --- a/gnu/usr.bin/cvs/src/watch.c +++ b/gnu/usr.bin/cvs/src/watch.c @@ -215,23 +215,28 @@ watch_modify_watchers (file, what) free (mynewattr); } -static int addremove_fileproc PROTO ((struct file_info *finfo)); +static int addremove_fileproc PROTO ((void *callerdat, + struct file_info *finfo)); static int -addremove_fileproc (finfo) +addremove_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { watch_modify_watchers (finfo->file, &the_args); return 0; } -static int addremove_filesdoneproc PROTO ((int, char *, char *)); +static int addremove_filesdoneproc PROTO ((void *, int, char *, char *, + List *)); static int -addremove_filesdoneproc (err, repository, update_dir) +addremove_filesdoneproc (callerdat, err, repository, update_dir, entries) + void *callerdat; int err; char *repository; char *update_dir; + List *entries; { if (the_args.setting_default) watch_modify_watchers (NULL, &the_args); @@ -348,9 +353,9 @@ watch_addremove (argc, argv) lock_tree_for_write (argc, argv, local, 0); err = start_recursion (addremove_fileproc, addremove_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 0, (char *)NULL, - 1, 0); + 1); lock_tree_cleanup (); return err; @@ -416,10 +421,12 @@ static const char *const watchers_usage[] = NULL }; -static int watchers_fileproc PROTO ((struct file_info *finfo)); +static int watchers_fileproc PROTO ((void *callerdat, + struct file_info *finfo)); static int -watchers_fileproc (finfo) +watchers_fileproc (callerdat, finfo) + void *callerdat; struct file_info *finfo; { char *them; @@ -515,7 +522,7 @@ watchers (argc, argv) #endif /* CLIENT_SUPPORT */ return start_recursion (watchers_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 1, 0); + 1); } diff --git a/gnu/usr.bin/cvs/src/wrapper.c b/gnu/usr.bin/cvs/src/wrapper.c index 8a6ff9471e4..580b8d3488f 100644 --- a/gnu/usr.bin/cvs/src/wrapper.c +++ b/gnu/usr.bin/cvs/src/wrapper.c @@ -22,6 +22,7 @@ -f from cvs filter value: path to filter -t to cvs filter value: path to filter -m update methodology value: MERGE or COPY + -k default -k rcs option to use on import or add and value is a single-quote delimited value. @@ -34,7 +35,7 @@ typedef struct { char *wildCard; char *tocvsFilter; char *fromcvsFilter; - char *conflictHook; + char *rcsOption; WrapMergeMethod mergeMethod; } WrapperEntry; @@ -44,7 +45,16 @@ static WrapperEntry **wrap_saved_list=NULL; static int wrap_size=0; static int wrap_count=0; static int wrap_tempcount=0; + +/* FIXME: wrap_saved_count is never set to any non-zero value. + wrap_name_has and wrap_matching_entry should be using + wrap_tempcount instead. I believe the consequence of this is that + .cvswrappers files are ignored (that was my own experience when I + tried to use one). If this bug is fixed, would be nice to write a + sanity.sh testcase for .cvswrappers files. */ + static int wrap_saved_count=0; + static int wrap_saved_tempcount=0; #define WRAPPER_GROW 8 @@ -61,24 +71,68 @@ void wrap_setup() char file[PATH_MAX]; struct passwd *pw; - /* Then add entries found in repository, if it exists */ - (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_WRAPPER); - if (isfile (file)){ - wrap_add_file(file,0); +#ifdef CLIENT_SUPPORT + if (!client_active) +#endif + { + /* Then add entries found in repository, if it exists. */ + (void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM, + CVSROOTADM_WRAPPER); + if (isfile (file)) + { + wrap_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){ + /* Then add entries found in home dir, (if user has one) and file + exists. (FIXME: I think this probably should be using + get_homedir, i.e. $HOME). */ + if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) + { (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER); - if (isfile (file)){ + if (isfile (file)) + { wrap_add_file (file, 0); } } - /* Then add entries found in CVSWRAPPERS environment variable. */ + /* Then add entries found in CVSWRAPPERS environment variable. */ wrap_add (getenv (WRAPPER_ENV), 0); } +#ifdef CLIENT_SUPPORT +/* Send -W arguments for the wrappers to the server. The command must + be one that accepts them (e.g. update, import). */ +void +wrap_send () +{ + int i; + + for (i = 0; i < wrap_count + wrap_tempcount; ++i) + { + if (wrap_list[i]->tocvsFilter != NULL + || wrap_list[i]->fromcvsFilter != NULL) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-t and -f wrapper options are not supported remotely; ignored"); + if (wrap_list[i]->mergeMethod == WRAP_COPY) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-m wrapper option is not supported remotely; ignored"); + if (wrap_list[i]->rcsOption != NULL) + { + send_to_server ("Argument -W\012Argument ", 0); + send_to_server (wrap_list[i]->wildCard, 0); + send_to_server (" -k '", 0); + send_to_server (wrap_list[i]->rcsOption, 0); + send_to_server ("'\012", 0); + } + } +} +#endif /* CLIENT_SUPPORT */ + /* * Open a file and read lines, feeding each line to a line parser. Arrange * for keeping a temporary list of wrappers at the end, if the "temp" @@ -96,7 +150,7 @@ wrap_add_file (file, temp) wrap_kill_temp(); /* load the file */ - if (!(fp = fopen (file, "r"))) + if (!(fp = CVS_FOPEN (file, "r"))) return; while (fgets (line, sizeof (line), fp)) wrap_add (line, temp); @@ -132,13 +186,13 @@ void wrap_free_entry_internal(e) WrapperEntry *e; { - free(e->wildCard); - if(e->tocvsFilter) - free(e->tocvsFilter); - if(e->fromcvsFilter) - free(e->fromcvsFilter); - if(e->conflictHook) - free(e->conflictHook); + free (e->wildCard); + if (e->tocvsFilter) + free (e->tocvsFilter); + if (e->fromcvsFilter) + free (e->fromcvsFilter); + if (e->rcsOption) + free (e->rcsOption); } void @@ -208,7 +262,15 @@ wrap_add (line, isTemp) for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) ; - if(line==temp+1) + /* This used to "break;" (ignore the option) if there was a + single character between the single quotes (I'm guessing + that was accidental). Now it "break;"s if there are no + characters. I'm not sure either behavior is particularly + necessary--the current options might not require '' + arguments, but surely some future option legitimately + might. Also I'm not sure that ignoring the option is a + swift way to handle syntax errors in general. */ + if (line==temp) break; ctemp=*line; @@ -232,21 +294,19 @@ wrap_add (line, isTemp) if (!e.tocvsFilter) error (1, 0, "Correct above errors first"); break; - case 'c': - if(e.conflictHook) - free(e.conflictHook); - /* FIXME: error message should say where the bad value - came from. */ - e.conflictHook=expand_path (temp, "<wrapper>", 0); - if (!e.conflictHook) - error (1, 0, "Correct above errors first"); - break; case 'm': + /* FIXME: look into whether this option is still relevant given + the 24 Jun 96 change to merge_file. */ if(*temp=='C' || *temp=='c') e.mergeMethod=WRAP_COPY; else e.mergeMethod=WRAP_MERGE; break; + case 'k': + if (e.rcsOption) + free (e.rcsOption); + e.rcsOption = xstrdup (temp); + break; default: break; } @@ -281,8 +341,8 @@ wrap_add_entry(e, temp) wrap_list[x]->wildCard=e->wildCard; wrap_list[x]->fromcvsFilter=e->fromcvsFilter; wrap_list[x]->tocvsFilter=e->tocvsFilter; - wrap_list[x]->conflictHook=e->conflictHook; wrap_list[x]->mergeMethod=e->mergeMethod; + wrap_list[x]->rcsOption = e->rcsOption; } /* Return 1 if the given filename is a wrapper filename */ @@ -303,8 +363,8 @@ wrap_name_has (name,has) case WRAP_FROMCVS: temp=wrap_list[x]->fromcvsFilter; break; - case WRAP_CONFLICT: - temp=wrap_list[x]->conflictHook; + case WRAP_RCSOPTION: + temp = wrap_list[x]->rcsOption; break; default: abort (); @@ -329,17 +389,46 @@ wrap_matching_entry (name) return (WrapperEntry *)NULL; } +/* Return the RCS options for FILENAME in a newly malloc'd string. If + ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise + just give the option itself (e.g. "b"). */ +char * +wrap_rcsoption (filename, asflag) + const char *filename; + int asflag; +{ + WrapperEntry *e = wrap_matching_entry (filename); + char *buf; + + if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) + return NULL; + + buf = xmalloc (strlen (e->rcsOption) + 3); + if (asflag) + { + strcpy (buf, "-k"); + strcat (buf, e->rcsOption); + } + else + { + strcpy (buf, e->rcsOption); + } + return buf; +} + char * wrap_tocvs_process_file(fileName) const char *fileName; { WrapperEntry *e=wrap_matching_entry(fileName); - static char buf[L_tmpnam+1]; + static char *buf = NULL; if(e==NULL || e->tocvsFilter==NULL) return NULL; - tmpnam(buf); + if (buf != NULL) + free (buf); + buf = cvs_temp_name (); run_setup(e->tocvsFilter,fileName,buf); run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); diff --git a/gnu/usr.bin/cvs/src/zlib.c b/gnu/usr.bin/cvs/src/zlib.c new file mode 100644 index 00000000000..776e1bfb5e4 --- /dev/null +++ b/gnu/usr.bin/cvs/src/zlib.c @@ -0,0 +1,433 @@ +/* zlib.c --- interface to the zlib compression library + Ian Lance Taylor <ian@cygnus.com> + + This file is part of GNU CVS. + + GNU CVS 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* The routines in this file are the interface between the CVS + client/server support and the zlib compression library. */ + +#include <assert.h> +#include "cvs.h" +#include "buffer.h" + +#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) + +#include "zlib.h" + +/* OS/2 doesn't have EIO. FIXME: this whole notion of turning + a different error into EIO strikes me as pretty dubious. */ +#if !defined (EIO) +#define EIO EBADPOS +#endif + +/* The compression interface is built upon the buffer data structure. + We provide a buffer type which compresses or decompresses the data + which passes through it. An input buffer decompresses the data + read from an underlying buffer, and an output buffer compresses the + data before writing it to an underlying buffer. */ + +/* This structure is the closure field of the buffer. */ + +struct compress_buffer +{ + /* The underlying buffer. */ + struct buffer *buf; + /* The compression information. */ + z_stream zstr; +}; + +static void compress_error PROTO((int, int, z_stream *, const char *)); +static int compress_buffer_input PROTO((void *, char *, int, int, int *)); +static int compress_buffer_output PROTO((void *, const char *, int, int *)); +static int compress_buffer_flush PROTO((void *)); +static int compress_buffer_block PROTO((void *, int)); +static int compress_buffer_shutdown_input PROTO((void *)); +static int compress_buffer_shutdown_output PROTO((void *)); + +/* Report an error from one of the zlib functions. */ + +static void +compress_error (status, zstatus, zstr, msg) + int status; + int zstatus; + z_stream *zstr; + const char *msg; +{ + int hold_errno; + const char *zmsg; + char buf[100]; + + hold_errno = errno; + + zmsg = zstr->msg; + if (zmsg == NULL) + { + sprintf (buf, "error %d", zstatus); + zmsg = buf; + } + + error (status, + zstatus == Z_ERRNO ? hold_errno : 0, + "%s: %s", msg, zmsg); +} + +/* Create a compression buffer. */ + +struct buffer * +compress_buffer_initialize (buf, input, level, memory) + struct buffer *buf; + int input; + int level; + void (*memory) PROTO((struct buffer *)); +{ + struct compress_buffer *n; + int zstatus; + + n = (struct compress_buffer *) xmalloc (sizeof *n); + memset (n, 0, sizeof *n); + + n->buf = buf; + + if (input) + zstatus = inflateInit (&n->zstr); + else + zstatus = deflateInit (&n->zstr, level); + if (zstatus != Z_OK) + compress_error (1, zstatus, &n->zstr, "compression initialization"); + + /* There may already be data buffered on BUF. For an output + buffer, this is OK, because these routines will just use the + buffer routines to append data to the (uncompressed) data + already on BUF. An input buffer expects to handle a single + buffer_data of buffered input to be uncompressed, so that is OK + provided there is only one buffer. At present that is all + there ever will be; if this changes, compress_buffer_input must + be modified to handle multiple input buffers. */ + assert (! input || buf->data == NULL || buf->data->next == NULL); + + return buf_initialize (input ? compress_buffer_input : NULL, + input ? NULL : compress_buffer_output, + input ? NULL : compress_buffer_flush, + compress_buffer_block, + (input + ? compress_buffer_shutdown_input + : compress_buffer_shutdown_output), + memory, + n); +} + +/* Input data from a compression buffer. */ + +static int +compress_buffer_input (closure, data, need, size, got) + void *closure; + char *data; + int need; + int size; + int *got; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + struct buffer_data *bd; + + if (cb->buf->input == NULL) + abort (); + + /* We use a single buffer_data structure to buffer up data which + the z_stream structure won't use yet. We can safely store this + on cb->buf->data, because we never call the buffer routines on + cb->buf; we only call the buffer input routine, since that + gives us the semantics we want. As noted in + compress_buffer_initialize, the buffer_data structure may + already exist, and hold data which was already read and + buffered before the decompression began. */ + bd = cb->buf->data; + if (bd == NULL) + { + bd = ((struct buffer_data *) malloc (sizeof (struct buffer_data))); + if (bd == NULL) + return -2; + bd->text = (char *) malloc (BUFFER_DATA_SIZE); + if (bd->text == NULL) + { + free (bd); + return -2; + } + bd->bufp = bd->text; + bd->size = 0; + cb->buf->data = bd; + } + + cb->zstr.avail_out = size; + cb->zstr.next_out = (Bytef *) data; + + while (1) + { + int zstatus, sofar, status, nread; + + /* First try to inflate any data we already have buffered up. + This is useful even if we don't have any buffered data, + because there may be data buffered inside the z_stream + structure. */ + + cb->zstr.avail_in = bd->size; + cb->zstr.next_in = (Bytef *) bd->bufp; + + do + { + zstatus = inflate (&cb->zstr, Z_NO_FLUSH); + if (zstatus == Z_STREAM_END) + break; + if (zstatus != Z_OK && zstatus != Z_BUF_ERROR) + { + compress_error (0, zstatus, &cb->zstr, "inflate"); + return EIO; + } + } while (cb->zstr.avail_in > 0 + && cb->zstr.avail_out > 0); + + bd->size = cb->zstr.avail_in; + bd->bufp = (char *) cb->zstr.next_in; + + if (zstatus == Z_STREAM_END) + return -1; + + /* If we have obtained NEED bytes, then return, unless NEED is + zero and we haven't obtained anything at all. If NEED is + zero, we will keep reading from the underlying buffer until + we either can't read anything, or we have managed to + inflate at least one byte. */ + sofar = size - cb->zstr.avail_out; + if (sofar > 0 && sofar >= need) + break; + + /* All our buffered data should have been processed at this + point. */ + assert (bd->size == 0); + + /* This will work well in the server, because this call will + do an unblocked read and fetch all the available data. In + the client, this will read a single byte from the stdio + stream, which will cause us to call inflate once per byte. + It would be more efficient if we could make a call which + would fetch all the available bytes, and at least one byte. */ + + status = (*cb->buf->input) (cb->buf->closure, bd->text, + need > 0 ? 1 : 0, + BUFFER_DATA_SIZE, &nread); + if (status != 0) + return status; + + /* If we didn't read anything, then presumably the buffer is + in nonblocking mode, and we should just get out now with + whatever we've inflated. */ + if (nread == 0) + { + assert (need == 0); + break; + } + + bd->bufp = bd->text; + bd->size = nread; + } + + *got = size - cb->zstr.avail_out; + + return 0; +} + +/* Output data to a compression buffer. */ + +static int +compress_buffer_output (closure, data, have, wrote) + void *closure; + const char *data; + int have; + int *wrote; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + cb->zstr.avail_in = have; + cb->zstr.next_in = (unsigned char *) data; + + while (cb->zstr.avail_in > 0) + { + char buffer[BUFFER_DATA_SIZE]; + int zstatus; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_NO_FLUSH); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflate"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + } + + *wrote = have; + + /* We will only be here because buf_send_output was called on the + compression buffer. That means that we should now call + buf_send_output on the underlying buffer. */ + return buf_send_output (cb->buf); +} + +/* Flush a compression buffer. */ + +static int +compress_buffer_flush (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + cb->zstr.avail_in = 0; + cb->zstr.next_in = NULL; + + while (1) + { + char buffer[BUFFER_DATA_SIZE]; + int zstatus; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_SYNC_FLUSH); + + /* The deflate function will return Z_BUF_ERROR if it can't do + anything, which in this case means that all data has been + flushed. */ + if (zstatus == Z_BUF_ERROR) + break; + + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflate flush"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + + /* If the deflate function did not fill the output buffer, + then all data has been flushed. */ + if (cb->zstr.avail_out > 0) + break; + } + + /* Now flush the underlying buffer. Note that if the original + call to buf_flush passed 1 for the BLOCK argument, then the + buffer will already have been set into blocking mode, so we + should always pass 0 here. */ + return buf_flush (cb->buf, 0); +} + +/* The block routine for a compression buffer. */ + +static int +compress_buffer_block (closure, block) + void *closure; + int block; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + + if (block) + return set_block (cb->buf); + else + return set_nonblock (cb->buf); +} + +/* Shut down an input buffer. */ + +static int +compress_buffer_shutdown_input (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + int zstatus; + + /* Pick up any trailing data, such as the checksum. */ + while (1) + { + int status, nread; + char buf[100]; + + status = compress_buffer_input (cb, buf, 0, sizeof buf, &nread); + if (status == -1) + break; + if (status != 0) + return status; + } + + zstatus = inflateEnd (&cb->zstr); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "inflateEnd"); + return EIO; + } + + return buf_shutdown (cb->buf); +} + +/* Shut down an output buffer. */ + +static int +compress_buffer_shutdown_output (closure) + void *closure; +{ + struct compress_buffer *cb = (struct compress_buffer *) closure; + int zstatus, status; + + do + { + char buffer[BUFFER_DATA_SIZE]; + + cb->zstr.avail_out = BUFFER_DATA_SIZE; + cb->zstr.next_out = (unsigned char *) buffer; + + zstatus = deflate (&cb->zstr, Z_FINISH); + if (zstatus != Z_OK && zstatus != Z_STREAM_END) + { + compress_error (0, zstatus, &cb->zstr, "deflate finish"); + return EIO; + } + + if (cb->zstr.avail_out != BUFFER_DATA_SIZE) + buf_output (cb->buf, buffer, + BUFFER_DATA_SIZE - cb->zstr.avail_out); + } while (zstatus != Z_STREAM_END); + + zstatus = deflateEnd (&cb->zstr); + if (zstatus != Z_OK) + { + compress_error (0, zstatus, &cb->zstr, "deflateEnd"); + return EIO; + } + + status = buf_flush (cb->buf, 1); + if (status != 0) + return status; + + return buf_shutdown (cb->buf); +} + +#endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */ |