diff options
author | peter <peter@FreeBSD.org> | 1999-03-18 09:32:46 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1999-03-18 09:32:46 +0000 |
commit | ba72c1aa08d98b8737d59c98c4587c5809cbffb2 (patch) | |
tree | 2725d3395b8624bfcf8eabe1f71dad0e5087df6f /contrib | |
parent | 27155f7a1d4379301d235ed68ce8122166056427 (diff) | |
download | FreeBSD-src-ba72c1aa08d98b8737d59c98c4587c5809cbffb2.zip FreeBSD-src-ba72c1aa08d98b8737d59c98c4587c5809cbffb2.tar.gz |
Merge cvs-1.9.xx -> 1.10 changes onto mainline.
Changes of significance include the top level CVS directory being optional
and defaulting to off..
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/cvs/contrib/log.pl | 11 | ||||
-rw-r--r-- | contrib/cvs/man/cvs.1 | 111 | ||||
-rw-r--r-- | contrib/cvs/src/commit.c | 184 | ||||
-rw-r--r-- | contrib/cvs/src/cvs.h | 7 | ||||
-rw-r--r-- | contrib/cvs/src/filesubr.c | 30 | ||||
-rw-r--r-- | contrib/cvs/src/import.c | 32 | ||||
-rw-r--r-- | contrib/cvs/src/main.c | 41 | ||||
-rw-r--r-- | contrib/cvs/src/mkmodules.c | 5 | ||||
-rw-r--r-- | contrib/cvs/src/rcs.c | 1061 | ||||
-rw-r--r-- | contrib/cvs/src/rcs.h | 9 | ||||
-rw-r--r-- | contrib/cvs/src/rcscmds.c | 116 | ||||
-rw-r--r-- | contrib/cvs/src/recurse.c | 52 | ||||
-rw-r--r-- | contrib/cvs/src/server.c | 96 | ||||
-rw-r--r-- | contrib/cvs/src/update.c | 82 |
14 files changed, 1385 insertions, 452 deletions
diff --git a/contrib/cvs/contrib/log.pl b/contrib/cvs/contrib/log.pl index 4bb935a..89eba48 100644 --- a/contrib/cvs/contrib/log.pl +++ b/contrib/cvs/contrib/log.pl @@ -146,7 +146,16 @@ if ($dostatus != 0) { } last; } - open(RCS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $file; + $pid = open(RCS, "-|"); + if ( !defined $pid ) + { + die "fork failed: $!"; + } + if ($pid == 0) + { + exec 'cvs', '-nQq', 'status', '-v', $file; + die "cvs exec failed: $!"; + } while (<RCS>) { print OUT; if (MAIL) { diff --git a/contrib/cvs/man/cvs.1 b/contrib/cvs/man/cvs.1 index bbcc569..b52c6ce 100644 --- a/contrib/cvs/man/cvs.1 +++ b/contrib/cvs/man/cvs.1 @@ -35,19 +35,15 @@ described in the SEE ALSO section of this manpage). .IX "release control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system" .IX "source control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system" .IX revisions "cvs command" "" "\fLcvs\fP \- source control" -.B cvs -is a front end to the -.BR rcs ( 1 ) -revision control system which extends -the notion of revision control from a collection of files in a single -directory to a hierarchical collection of directories consisting of -revision controlled files. -These directories and files can be combined together to form a software -release. -.B cvs -provides the functions necessary to manage these software releases and to -control the concurrent editing of source files among multiple software -developers. +CVS is a version control system, which allows you to keep old versions +of files (usually source code), keep a log of who, when, and why +changes occurred, etc., like RCS or SCCS. Unlike the simpler systems, +CVS does not just operate on one file at a time or one directory at a +time, but operates on hierarchical collections of directories +consisting of version controlled files. CVS helps to manage releases +and to control the concurrent editing of source files among multiple +authors. CVS allows triggers to enable/log/control various +operations and works well over a wide area network. .SP .B cvs keeps a single copy of the master sources. @@ -175,7 +171,7 @@ Use .I bindir as the directory where .SM RCS -programs are located. +programs are located (CVS 1.9 and older). Overrides the setting of the .SM RCSBIN environment variable. @@ -185,7 +181,6 @@ This value should be specified as an absolute pathname. Use .I CVS_root_directory as the root directory pathname of the master -.SM RCS source repository. Overrides the setting of the .SM CVSROOT @@ -337,7 +332,6 @@ working directory.) .TP .B admin Execute -.SM RCS control functions on the source repository. (Changes repository directly; uses working directory without changing it.) .TP @@ -385,7 +379,6 @@ as a ``vendor branch''. (Changes repository.) .TP .B log Display -.SM RCS log information. (Does not change repository or working directory.) .TP @@ -454,11 +447,8 @@ even with these standard options. \fB\-D\fP \fIdate_spec\fP Use the most recent revision no later than \fIdate_spec\fP (a single argument, date description specifying a date in the -past). A wide variety of date formats are supported by the underlying -.SM RCS -facilities, similar to those described in -.BR co ( 1 ), -but not exactly the same. +past). A wide variety of date formats are supported, in particular +ISO ("1972-09-24 20:05") or Internet ("24 Sep 1972 20:05"). The \fIdate_spec\fP is interpreted as being in the local timezone, unless a specific timezone is specified. The specification is ``sticky'' when you use it to make a @@ -511,12 +501,8 @@ commands. .TP \fB\-k\fP \fIkflag\fP Alter the default -.SM RCS -processing of keywords; all the -.B \-k -options described in -.BR co ( 1 ) -are available. The \fB\-k\fP option is available with the +processing of keywords. +The \fB\-k\fP option is available with the .BR add ", " checkout ", " diff ", " export ", " .BR rdiff ", and " update commands. Your \fIkflag\fP specification is ``sticky'' when you use @@ -526,10 +512,8 @@ this option with the \fBcheckout\fP or \fBupdate\fP commands, continues to use it with future \fBupdate\fP commands on the same file until you specify otherwise. .SP -Some of the more useful \fIkflag\fPs are \-ko and \-kb (for binary files, -only compatible with -.SM RCS -version 5.7 or later), and \-kv which is useful for an +Some of the more useful \fIkflag\fPs are \-ko and \-kb (for binary files), +and \-kv which is useful for an .B export where you wish to retain keyword information after an .B import @@ -619,9 +603,7 @@ make your own copy of a file: \fBcvs\fP remembers the \fItag\fP and continues to use it on future \fBupdate\fP commands, until you specify otherwise. .I tag -can be either a symbolic or numeric tag, in -.SM RCS -fashion. +can be either a symbolic or numeric tag. Specifying the .B \-q global option along with the @@ -678,7 +660,6 @@ working directory. Use the .B add command to create a new file or directory in the -.SM RCS source repository. The files or directories specified with .B add @@ -695,7 +676,6 @@ If the argument to .` "cvs add" refers to an immediate sub-directory, the directory is created at the correct place in the -.SM RCS source repository, and the necessary .B cvs administration files are created in your working directory. @@ -738,7 +718,6 @@ or .` "cvs update -d".) .SP The added files are not placed in the -.SM RCS source repository until you use .` "cvs commit" to make the change permanent. @@ -776,9 +755,8 @@ file and can be changed with Specifying .` "-ko" is useful for checking in binaries that -shouldn't have the -.SM RCS -id strings expanded. +shouldn't have +keywords expanded. .TP \fBadmin\fP [\fIrcs-options\fP] \fIfiles.\|.\|.\fP .I Requires: @@ -793,14 +771,9 @@ repository. This is the .B cvs interface to assorted administrative -.SM RCS -facilities, documented in +facilities, similar to .BR rcs ( 1 ). -.` "cvs admin" -simply passes all its options and arguments to the -.B rcs -command; it does no filtering or other processing. -This command does work recursively, however, so extreme care should be +This command works recursively, so extreme care should be used. .TP \fBcheckout\fP [\fBoptions\fP] \fImodules\fP.\|.\|. @@ -839,7 +812,6 @@ You can then edit these source files at any time (regardless of whether other software developers are editing their own copies of the sources); update them to include new changes applied by others to the source repository; or commit your work as a permanent change to the -.SM RCS repository. .SP Note that @@ -1006,7 +978,6 @@ the time is right. .SP When all is well, an editor is invoked to allow you to enter a log message that will be written to one or more logging programs and placed in the -.SM RCS source repository file. You can instead specify the log message on the command line with the .B \-m @@ -1016,12 +987,8 @@ option to specify that the argument \fIfile\fP contains the log message. .SP The .B \-r -option can be used to commit to a particular symbolic or numeric revision -within the -.SM RCS -file. +option can be used to commit to a particular symbolic or numeric revision. For example, to bring all your files up to the -.SM RCS revision ``3.0'' (including those that haven't changed), you might do: .SP .in +1i @@ -1173,7 +1140,6 @@ option is useful when .B export is used. This causes any -.SM RCS keywords to be expanded such that an .B import done at some other site will not lose the keyword revision information. @@ -1349,7 +1315,6 @@ For an up to date list of ignored file names, see the Cederqvist manual (as described in the SEE ALSO section of this manpage). .SP The outside source is saved in a first-level -.SM RCS branch, by default .` "1.1.1". Updates are leaves of this @@ -1409,13 +1374,7 @@ nothing. .B rlog .br Display log information for \fIfiles\fP. -.` "cvs log" -calls -the -.SM RCS -utility \fBrlog\fP; all the options described in -.BR rlog ( 1 ) -are available. Among the more useful \fBrlog\fP options are \fB\-h\fP +Among the more useful options are \fB\-h\fP to display only the header (including tag definitions, but omitting most of the full log); \fB\-r\fP to select logs on particular revisions or ranges of revisions; and \fB\-d\fP to select particular @@ -1463,14 +1422,6 @@ command when patching the old sources, so that .B patch is able to find the files that are located in other directories. .SP -If you use the option \fB\-V\fP \fIvn\fP, -.SM RCS -keywords are expanded according to the rules current in -.SM RCS -version \fIvn\fP (the expansion format changed with -.SM RCS -version 5). -.SP The standard option \fIflags\fP \fB\-f\fP, and \fB\-l\fP are available with this command. There are also several special options flags: @@ -1788,7 +1739,6 @@ recent versions available in the repository. \fBA\fP \fIfile\fP The file has been \fIadded\fP to your private copy of the sources, and will be added to the -.SM RCS source repository when you run .` "cvs commit" on the file. @@ -1797,7 +1747,6 @@ This is a reminder to you that the file needs to be committed. \fBR\fP \fIfile\fP The file has been \fIremoved\fP from your private copy of the sources, and will be removed from the -.SM RCS source repository when you run .` "cvs commit" on the file. @@ -1816,14 +1765,12 @@ directory. \fBC\fP \fIfile\fP A \fIconflict\fP was detected while trying to merge your changes to \fIfile\fP with changes from the source repository. \fIfile\fP (the -copy in your working directory) is now the output of the -.BR rcsmerge ( 1 ) -command on the two versions; an unmodified copy of your file is also +copy in your working directory) is now the result of merging +the two versions; an unmodified copy of your file is also in your working directory, with the name `\fB.#\fP\fIfile\fP\fB.\fP\fIversion\fP', where .I version is the -.SM RCS revision that your modified file started from. (Note that some systems automatically purge files that begin with \& @@ -2056,7 +2003,6 @@ Directory for removed source files. A lock directory created by .B cvs when doing sensitive changes to the -.SM RCS source repository. .TP #cvs.tfl.\fIpid\fP @@ -2099,9 +2045,8 @@ Specifies the full pathname where to find programs, such as .BR co ( 1 ) and -.BR ci ( 1 ). -If not set, a compiled-in value is used; see the display from -.` "cvs \-v". +.BR ci ( 1 ) +(CVS 1.9 and older). .TP .SM CVSEDITOR Specifies the program to use for recording log messages during @@ -2167,6 +2112,8 @@ module and vendor branch support and author of the .BR checkin ( 1 ) shell script (the ancestor of .` "cvs import"). +.TP +And many others too numerous to mention here. .SH "SEE ALSO" The most comprehensive manual for CVS is Version Management with CVS by Per Cederqvist et al. Depending on diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index 4488e2a..ac7c7fa 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -1,17 +1,17 @@ /* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner - * + * * You may distribute under the terms of the GNU General Public License as * specified in the README file that comes with the CVS source distribution. - * + * * Commit Files - * + * * "commit" commits the present version to the RCS repository, AFTER * having done a test on conflicts. * * The call is: cvs commit [options] files... - * + * */ #include <assert.h> @@ -29,7 +29,7 @@ static int check_filesdoneproc PROTO ((void *callerdat, int err, char *repos, char *update_dir, List *entries)); static int checkaddfile PROTO((char *file, char *repository, char *tag, - char *options, RCSNode **rcsnode)); + char *options, RCSNode **rcsnode)); static Dtype commit_direntproc PROTO ((void *callerdat, char *dir, char *repos, char *update_dir, List *entries)); @@ -74,12 +74,13 @@ static int force_ci = 0; static int got_message; static int run_module_prog = 1; static int aflag; -static char *tag; +static char *saved_tag; static char *write_dirtag; static int write_dirnonbranch; static char *logfile; static List *mulist; -static char *message; +static List *saved_ulist; +static char *saved_message; static time_t last_register_time; static const char *const commit_usage[] = @@ -247,7 +248,7 @@ find_fileproc (callerdat, finfo) xfinfo.repository = NULL; xfinfo.rcs = NULL; - vers = Version_TS (&xfinfo, NULL, tag, NULL, 0, 0); + vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0); if (vers->ts_user == NULL && vers->vn_user != NULL && vers->vn_user[0] == '-') @@ -364,18 +365,18 @@ commit (argc, argv) #else use_editor = 0; #endif - if (message) + if (saved_message) { - free (message); - message = NULL; + free (saved_message); + saved_message = NULL; } - message = xstrdup(optarg); + saved_message = xstrdup(optarg); break; case 'r': - if (tag) - free (tag); - tag = xstrdup (optarg); + if (saved_tag) + free (saved_tag); + saved_tag = xstrdup (optarg); break; case 'l': local = 1; @@ -405,12 +406,12 @@ commit (argc, argv) argv += optind; /* numeric specified revision means we ignore sticky tags... */ - if (tag && isdigit (*tag)) + if (saved_tag && isdigit (*saved_tag)) { aflag = 1; /* strip trailing dots */ - while (tag[strlen (tag) - 1] == '.') - tag[strlen (tag) - 1] = '\0'; + while (saved_tag[strlen (saved_tag) - 1] == '.') + saved_tag[strlen (saved_tag) - 1] = '\0'; } /* some checks related to the "-F logfile" option */ @@ -419,7 +420,7 @@ commit (argc, argv) int n, logfd; struct stat statbuf; - if (message) + if (saved_message) error (1, 0, "cannot specify both a message and a log file"); /* FIXME: Why is this binary? Needs more investigation. */ @@ -429,21 +430,20 @@ commit (argc, argv) if (fstat(logfd, &statbuf) < 0) error (1, errno, "cannot find size of log file %s", logfile); - message = xmalloc (statbuf.st_size + 1); + saved_message = xmalloc (statbuf.st_size + 1); /* FIXME: Should keep reading until EOF, rather than assuming the first read gets the whole thing. */ - if ((n = read (logfd, message, statbuf.st_size + 1)) < 0) + if ((n = read (logfd, saved_message, statbuf.st_size + 1)) < 0) error (1, errno, "cannot read log message from %s", logfile); (void) close (logfd); - message[n] = '\0'; + saved_message[n] = '\0'; } #ifdef CLIENT_SUPPORT - if (client_active) + if (client_active) { - int err; struct find_data find_args; ign_setup (); @@ -458,7 +458,7 @@ commit (argc, argv) I haven't really thought about it much. Anyway, I suspect that setting it unnecessarily only causes a little unneeded network traffic. */ - find_args.force = force_ci || tag != NULL; + find_args.force = force_ci || saved_tag != NULL; err = start_recursion (find_fileproc, find_filesdoneproc, find_dirent_proc, (DIRLEAVEPROC) NULL, @@ -500,17 +500,17 @@ commit (argc, argv) * The protocol is designed this way. This is a feature. */ if (use_editor) - do_editor (".", &message, (char *)NULL, find_args.ulist); + do_editor (".", &saved_message, (char *)NULL, find_args.ulist); /* Run the user-defined script to verify/check information in *the log message */ - do_verify (&message, (char *)NULL); + do_verify (&saved_message, (char *)NULL); /* We always send some sort of message, even if empty. */ /* FIXME: is that true? There seems to be some code in do_editor which can leave the message NULL. */ - option_with_arg ("-m", message); + option_with_arg ("-m", saved_message); /* OK, now process all the questionable files we have been saving up. */ @@ -559,7 +559,7 @@ commit (argc, argv) send_arg("-f"); if (!run_module_prog) send_arg("-n"); - option_with_arg ("-r", tag); + option_with_arg ("-r", saved_tag); /* Sending only the names of the files which were modified, added, or removed means that the server will only do an up-to-date @@ -582,7 +582,7 @@ commit (argc, argv) send_to_server ("ci\012", 0); err = get_responses_and_close (); - if (err != 0 && use_editor && message != NULL) + if (err != 0 && use_editor && saved_message != NULL) { /* If there was an error, don't nuke the user's carefully constructed prose. This is something of a kludge; a better @@ -600,7 +600,8 @@ commit (argc, argv) fp = CVS_FOPEN (fname, "w+"); if (fp == NULL) error (1, 0, "cannot create temporary file %s", fname); - if (fwrite (message, 1, strlen (message), fp) != strlen (message)) + if (fwrite (saved_message, 1, strlen (saved_message), fp) + != strlen (saved_message)) error (1, errno, "cannot write temporary file %s", fname); if (fclose (fp) < 0) error (0, errno, "cannot close temporary file %s", fname); @@ -610,12 +611,12 @@ commit (argc, argv) } #endif - if (tag != NULL) - tag_check_valid (tag, argc, argv, local, aflag, ""); + if (saved_tag != NULL) + tag_check_valid (saved_tag, argc, argv, local, aflag, ""); /* XXX - this is not the perfect check for this */ if (argc <= 0) - write_dirtag = tag; + write_dirtag = saved_tag; wrap_setup (); @@ -651,17 +652,6 @@ commit (argc, argv) error (1, 0, "correct above errors first!"); } -#ifdef PRESERVE_PERMISSIONS_SUPPORT - if (preserve_perms) - { - /* hardlist now includes a complete index of the files - to be committed, indexed by inode. For each inode, - compile a list of the files that are linked to it, - and save this list in each file's hardlink_info node. */ - (void) walklist (hardlist, cache_hardlinks_proc, NULL); - } -#endif - /* * Run the recursion processor to commit the files */ @@ -683,7 +673,7 @@ commit (argc, argv) time_t now; (void) time (&now); - if (now == last_register_time) + if (now == last_register_time) { sleep (1); /* to avoid time-stamp races */ } @@ -713,10 +703,10 @@ classify_file_internal (finfo, vers) noexec = quiet = really_quiet = 1; /* handle specified numeric revision specially */ - if (tag && isdigit (*tag)) + if (saved_tag && isdigit (*saved_tag)) { /* If the tag is for the trunk, make sure we're at the head */ - if (numdots (tag) < 2) + if (numdots (saved_tag) < 2) { status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, 1, aflag, vers, 0); @@ -726,7 +716,7 @@ classify_file_internal (finfo, vers) Ctype xstatus; freevers_ts (vers); - xstatus = Classify_File (finfo, tag, (char *) NULL, + xstatus = Classify_File (finfo, saved_tag, (char *) NULL, (char *) NULL, 1, aflag, vers, 0); if (xstatus == T_REMOVE_ENTRY) status = T_MODIFIED; @@ -744,7 +734,7 @@ classify_file_internal (finfo, vers) * The revision is off the main trunk; make sure we're * up-to-date with the head of the specified branch. */ - xtag = xstrdup (tag); + xtag = xstrdup (saved_tag); if ((numdots (xtag) & 1) != 0) { cp = strrchr (xtag, '.'); @@ -765,12 +755,12 @@ classify_file_internal (finfo, vers) } /* now, muck with vers to make the tag correct */ free ((*vers)->tag); - (*vers)->tag = xstrdup (tag); + (*vers)->tag = xstrdup (saved_tag); free (xtag); } } else - status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL, + status = Classify_File (finfo, saved_tag, (char *) NULL, (char *) NULL, 1, 0, vers, 0); noexec = save_noexec; quiet = save_quiet; @@ -796,7 +786,7 @@ check_fileproc (callerdat, finfo) Vers_TS *vers; struct commit_info *ci; struct logfile_info *li; - + status = classify_file_internal (finfo, &vers); /* @@ -835,7 +825,7 @@ check_fileproc (callerdat, finfo) * allow the commit if timestamp is identical or if we find * an RCS_MERGE_PAT in the file. */ - if (!tag || !isdigit (*tag)) + if (!saved_tag || !isdigit (*saved_tag)) { if (vers->date) { @@ -1047,7 +1037,6 @@ warning: file `%s' seems to still contain conflict indicators", hlinfo = (struct hardlink_info *) xmalloc (sizeof (struct hardlink_info)); hlinfo->status = status; - hlinfo->links = NULL; linkp->data = (char *) hlinfo; } } @@ -1114,7 +1103,6 @@ precommit_list_proc (p, closure) /* * Callback proc for pre-commit checking */ -static List *ulist; static int precommit_proc (repository, filter) char *repository; @@ -1124,7 +1112,7 @@ precommit_proc (repository, filter) if (isabsolute (filter)) { char *s, *cp; - + s = xstrdup (filter); for (cp = s; *cp; cp++) if (isspace (*cp)) @@ -1143,7 +1131,7 @@ precommit_proc (repository, filter) run_setup (filter); run_arg (repository); - (void) walklist (ulist, precommit_list_proc, NULL); + (void) walklist (saved_ulist, precommit_list_proc, NULL); return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); } @@ -1165,12 +1153,12 @@ check_filesdoneproc (callerdat, err, repos, update_dir, entries) /* find the update list for this dir */ p = findnode (mulist, update_dir); if (p != NULL) - ulist = ((struct master_lists *) p->data)->ulist; + saved_ulist = ((struct master_lists *) p->data)->ulist; else - ulist = (List *) NULL; + saved_ulist = (List *) NULL; /* skip the checks if there's nothing to do */ - if (ulist == NULL || ulist->list->next == ulist->list) + if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list) return (err); /* run any pre-commit checks */ @@ -1238,8 +1226,9 @@ commit_fileproc (callerdat, finfo) { got_message = 1; if (use_editor) - do_editor (finfo->update_dir, &message, finfo->repository, ulist); - do_verify (&message, finfo->repository); + do_editor (finfo->update_dir, &saved_message, + finfo->repository, ulist); + do_verify (&saved_message, finfo->repository); } p = findnode (cilist, finfo->file); @@ -1279,7 +1268,7 @@ commit_fileproc (callerdat, finfo) error (1, 0, "internal error: no parsed RCS file"); ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); err = Checkin ('A', finfo, finfo->rcs->path, ci->rev, - ci->tag, ci->options, message); + ci->tag, ci->options, saved_message); if (err != 0) { unlockrcs (finfo->rcs); @@ -1319,7 +1308,7 @@ commit_fileproc (callerdat, finfo) { err = Checkin ('M', finfo, finfo->rcs->path, ci->rev, ci->tag, - ci->options, message); + ci->options, saved_message); (void) time (&last_register_time); @@ -1331,7 +1320,7 @@ commit_fileproc (callerdat, finfo) } else if (ci->status == T_REMOVED) { - err = remove_file (finfo, ci->tag, message); + err = remove_file (finfo, ci->tag, saved_message); #ifdef SERVER_SUPPORT if (server_active) { server_scratch_entry_only (); @@ -1371,7 +1360,7 @@ out: been removed from the archive, which is not the behavior we want for our commitlog messages; we want the old version number and then "NONE." */ - + if (ci->status != T_REMOVED) { p = findnode (ulist, finfo->file); @@ -1379,7 +1368,7 @@ out: { Vers_TS *vers; struct logfile_info *li; - + (void) classify_file_internal (finfo, &vers); li = (struct logfile_info *) p->data; li->rev_new = xstrdup (vers->vn_rcs); @@ -1415,7 +1404,7 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) got_message = 0; - Update_Logfile (repository, message, (FILE *) 0, ulist); + Update_Logfile (repository, saved_message, (FILE *) 0, ulist); /* Build the administrative files if necessary. */ { @@ -1423,11 +1412,17 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) if (strncmp (CVSroot_directory, repository, strlen (CVSroot_directory)) != 0) - error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", repository, CVSroot_directory); + error (0, 0, + "internal error: repository (%s) doesn't begin with root (%s)", + repository, CVSroot_directory); p = repository + strlen (CVSroot_directory); if (*p == '/') ++p; - if (strcmp ("CVSROOT", p) == 0) + if (strcmp ("CVSROOT", p) == 0 + /* Check for subdirectories because people may want to create + subdirectories and list files therein in checkoutlist. */ + || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0 + ) { /* "Database" might a little bit grandiose and/or vague, but "checked-out copies of administrative files, unless @@ -1435,11 +1430,21 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) case modules.{pag,dir,db}" is verbose and excessively focused on how the database is implemented. */ + /* mkmodules requires the absolute name of the CVSROOT directory. + Remove anything after the `CVSROOT' component -- this is + necessary when committing in a subdirectory of CVSROOT. */ + char *admin_dir = xstrdup (repository); + int cvsrootlen = strlen ("CVSROOT"); + assert (admin_dir[p - repository + cvsrootlen] == '\0' + || admin_dir[p - repository + cvsrootlen] == '/'); + admin_dir[p - repository + cvsrootlen] = '\0'; + cvs_output (program_name, 0); cvs_output (" ", 1); cvs_output (command_name, 0); cvs_output (": Rebuilding administrative file database\n", 0); - mkmodules (repository); + mkmodules (admin_dir); + free (admin_dir); } } @@ -1452,7 +1457,7 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) char *line; int line_length; size_t line_chars_allocated; - char *repository; + char *repos; line = NULL; line_chars_allocated = 0; @@ -1462,9 +1467,9 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) /* Remove any trailing newline. */ if (line[line_length - 1] == '\n') line[--line_length] = '\0'; - repository = Name_Repository ((char *) NULL, update_dir); + repos = Name_Repository ((char *) NULL, update_dir); run_setup (line); - run_arg (repository); + run_arg (repos); cvs_output (program_name, 0); cvs_output (" ", 1); cvs_output (command_name, 0); @@ -1472,7 +1477,7 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) run_print (stdout); cvs_output ("'\n", 0); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); - free (repository); + free (repos); } else { @@ -1529,8 +1534,8 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries) real_repos = Name_Repository (dir, update_dir); got_message = 1; if (use_editor) - do_editor (update_dir, &message, real_repos, ulist); - do_verify (&message, real_repos); + do_editor (update_dir, &saved_message, real_repos, ulist); + do_verify (&saved_message, real_repos); free (real_repos); return (R_PROCESS); } @@ -1622,7 +1627,7 @@ remove_file (finfo, tag, message) if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag))) { /* a symbolic tag is specified; just remove the tag from the file */ - if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) + if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -1655,7 +1660,7 @@ remove_file (finfo, tag, message) error (0, 0, "cannot find branch \"%s\".", tag); return (1); } - + branchname = RCS_getbranch (finfo->rcs, rev, 1); if (branchname == NULL) { @@ -1676,12 +1681,12 @@ remove_file (finfo, tag, message) /* Get current head revision of file. */ prev_rev = RCS_head (finfo->rcs); } - + /* if removing without a tag or a branch, then make sure the default branch is the trunk. */ if (!tag && !branch) { - if (RCS_setbranch (finfo->rcs, NULL) != 0) + if (RCS_setbranch (finfo->rcs, NULL) != 0) { error (0, 0, "cannot change branch to default for %s", finfo->fullname); @@ -1740,7 +1745,7 @@ remove_file (finfo, tag, message) if (!branch) { /* this was the head; really move it into the Attic */ - tmp = xmalloc(strlen(finfo->repository) + + tmp = xmalloc(strlen(finfo->repository) + sizeof('/') + sizeof(CVSATTIC) + sizeof('/') + @@ -1750,8 +1755,9 @@ remove_file (finfo, tag, message) omask = umask (cvsumask); (void) CVS_MKDIR (tmp, 0777); (void) umask (omask); - (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, finfo->file, RCSEXT); - + (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, + finfo->file, RCSEXT); + if (strcmp (finfo->rcs->path, tmp) != 0 && CVS_RENAME (finfo->rcs->path, tmp) == -1 && (isreadable (finfo->rcs->path) || !isreadable (tmp))) @@ -1794,7 +1800,7 @@ finaladd (finfo, rev, tag, options) char *rcs; rcs = locate_rcs (finfo->file, finfo->repository); - ret = Checkin ('A', finfo, rcs, rev, tag, options, message); + ret = Checkin ('A', finfo, rcs, rev, tag, options, saved_message); if (ret == 0) { char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM) @@ -1931,7 +1937,7 @@ checkaddfile (file, repository, tag, options, rcsnode) Attic. */ oldfile = xstrdup (rcs); sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); - + if (strcmp (oldfile, rcs) == 0) { error (0, 0, "internal error: confused about attic for %s", @@ -2156,7 +2162,7 @@ internal error: `%s' didn't move out of the attic", retval = 1; goto out; } - } + } if (rcsnode && *rcsnode != rcsfile) { @@ -2202,7 +2208,7 @@ lock_RCS (user, rcs, rev, repository) * For a specified, numeric revision of the form "1" or "1.1", (or when * no revision is specified ""), definitely move the branch to the trunk * before locking the RCS file. - * + * * The assumption is that if there is more than one revision on the trunk, * the head points to the trunk, not a branch... and as such, it's not * necessary to move the head in this case. diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index b194d16..9e0b057 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -391,6 +391,8 @@ extern int readonlyfs; /* fail on all write locks; succeed all read locks */ extern int logoff; /* Don't write history entry */ extern int require_real_user; /* skip CVSROOT/passwd, /etc/passwd users only*/ +extern int top_level_admin; + #ifdef AUTH_SERVER_SUPPORT extern char *Pserver_Repos; /* used to check that same repos is transmitted in pserver auth and in @@ -435,6 +437,8 @@ void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); char *Make_Date PROTO((char *rawdate)); +char *date_from_time_t PROTO ((time_t)); + char *Name_Repository PROTO((char *dir, char *update_dir)); char *Short_Repository PROTO((char *repository)); void Sanitize_Repository_Name PROTO((char *repository)); @@ -483,7 +487,6 @@ int numdots PROTO((const char *s)); char *increment_revnum PROTO ((const char *)); int compare_revnums PROTO ((const char *, const char *)); int unlink_file PROTO((const char *f)); -int link_file PROTO ((const char *from, const char *to)); int unlink_file_dir PROTO((const char *f)); int update PROTO((int argc, char *argv[])); int xcmp PROTO((const char *file1, const char *file2)); @@ -841,7 +844,7 @@ extern int patch PROTO((int argc, char **argv)); extern int release PROTO((int argc, char **argv)); extern int cvsremove PROTO((int argc, char **argv)); extern int rtag PROTO((int argc, char **argv)); -extern int status PROTO((int argc, char **argv)); +extern int cvsstatus PROTO((int argc, char **argv)); extern int cvstag PROTO((int argc, char **argv)); extern unsigned long int lookup_command_attribute PROTO((char *)); diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c index c85fe60..91af048 100644 --- a/contrib/cvs/src/filesubr.c +++ b/contrib/cvs/src/filesubr.c @@ -415,29 +415,6 @@ rename_file (from, to) } /* - * link a file, if possible. Warning: the Windows NT version of this - * function just copies the file, so only use this function in ways - * that can deal with either a link or a copy. - */ -int -link_file (from, to) - const char *from; - const char *to; -{ - if (trace) -#ifdef SERVER_SUPPORT - (void) fprintf (stderr, "%c-> link(%s,%s)\n", - (server_active) ? 'S' : ' ', from, to); -#else - (void) fprintf (stderr, "-> link(%s,%s)\n", from, to); -#endif - if (noexec) - return (0); - - return (link (from, to)); -} - -/* * unlink a file, if possible. */ int @@ -770,6 +747,7 @@ xreadlink (link) const char *link; { char *file = NULL; + char *tfile; int buflen = BUFSIZ; int linklen; @@ -792,9 +770,11 @@ xreadlink (link) error (1, errno, "cannot readlink %s", link); file[linklen] = '\0'; - return file; -} + tfile = xstrdup (file); + free (file); + return tfile; +} /* Return a pointer into PATH's last component. */ diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index 18cd482..cc84258 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -20,8 +20,6 @@ #include "savecwd.h" #include <assert.h> -#define FILE_HOLDER ".#cvsxxx" - static char *get_comment PROTO((char *user)); static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile, char *vers)); @@ -655,39 +653,15 @@ add_rev (message, rcs, vfile, vers) RCS_rewrite (rcs, NULL, NULL); } tocvsPath = wrap_tocvs_process_file (vfile); - if (tocvsPath == NULL) - { - /* We play with hard links rather than passing -u to ci to avoid - expanding RCS keywords (see test 106.5 in sanity.sh). */ - if (link_file (vfile, FILE_HOLDER) < 0) - { - if (errno == EEXIST) - { - (void) unlink_file (FILE_HOLDER); - (void) link_file (vfile, FILE_HOLDER); - } - else - { - ierrno = errno; - fperror (logfp, 0, ierrno, - "ERROR: cannot create link to %s", vfile); - error (0, ierrno, "ERROR: cannot create link to %s", vfile); - return (1); - } - } - } status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath, message, vbranch, - (RCS_FLAGS_QUIET + (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE | (use_file_modtime ? RCS_FLAGS_MODTIME : 0))); ierrno = errno; - if (tocvsPath == NULL) - rename_file (FILE_HOLDER, vfile); - else - if (unlink_file_dir (tocvsPath) < 0) - error (0, errno, "cannot remove %s", tocvsPath); + if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0)) + error (0, errno, "cannot remove %s", tocvsPath); if (status) { diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c index 9cb7bc0..c016a02 100644 --- a/contrib/cvs/src/main.c +++ b/contrib/cvs/src/main.c @@ -43,6 +43,12 @@ int noexec = 0; int readonlyfs = 0; int require_real_user = 0; int logoff = 0; + +/* Set if we should be writing CVSADM directories at top level. At + least for now we'll make the default be off (the CVS 1.9, not CVS + 1.9.2, behavior). */ +int top_level_admin = 0; + mode_t cvsumask = UMASK_DFLT; char *CurDir; @@ -110,7 +116,7 @@ static const struct cmd { "rdiff", "patch", "pa", patch }, { "release", "re", "rel", release }, { "remove", "rm", "delete", cvsremove }, - { "status", "st", "stat", status }, + { "status", "st", "stat", cvsstatus }, { "rtag", "rt", "rfreeze", rtag }, { "tag", "ta", "freeze", cvstag }, { "unedit", NULL, NULL, unedit }, @@ -285,6 +291,10 @@ lookup_command_attribute (cmd_name) } + /* The following commands do not use a checked-out working + directory. We conservatively assume that everything else does. + Feel free to add to this list if you are _certain_ something + something doesn't use the WD. */ if ((strcmp (cmd_name, "checkout") != 0) && (strcmp (cmd_name, "init") != 0) && (strcmp (cmd_name, "login") != 0) && @@ -300,8 +310,10 @@ lookup_command_attribute (cmd_name) /* The following commands do not modify the repository; we conservatively assume that everything else does. Feel free to add to this list if you are _certain_ something is safe. */ - if ((strcmp (cmd_name, "checkout") != 0) && + if ((strcmp (cmd_name, "annotate") != 0) && + (strcmp (cmd_name, "checkout") != 0) && (strcmp (cmd_name, "diff") != 0) && + (strcmp (cmd_name, "rdiff") != 0) && (strcmp (cmd_name, "update") != 0) && (strcmp (cmd_name, "history") != 0) && (strcmp (cmd_name, "editors") != 0) && @@ -1000,20 +1012,37 @@ char * Make_Date (rawdate) char *rawdate; { - struct tm *ftm; time_t unixtime; - char date[MAXDATELEN]; - char *ret; unixtime = get_date (rawdate, (struct timeb *) NULL); if (unixtime == (time_t) - 1) error (1, 0, "Can't parse date/time: %s", rawdate); + return date_from_time_t (unixtime); +} + +/* Convert a time_t to an RCS format date. This is mainly for the + use of "cvs history", because the CVSROOT/history file contains + time_t format dates; most parts of CVS will want to avoid using + time_t's directly, and instead use RCS_datecmp, Make_Date, &c. + Assuming that the time_t is in GMT (as it generally should be), + then the result will be in GMT too. + + Returns a newly malloc'd string. */ + +char * +date_from_time_t (unixtime) + time_t unixtime; +{ + struct tm *ftm; + char date[MAXDATELEN]; + char *ret; ftm = gmtime (&unixtime); if (ftm == NULL) /* This is a system, like VMS, where the system clock is in local time. Hopefully using localtime here matches the "zero timezone" - hack I added to get_date. */ + hack I added to get_date (get_date of course being the relevant + issue for Make_Date, and for history.c too I think). */ ftm = localtime (&unixtime); (void) sprintf (date, DATEFORM, diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c index c3c530d..e716222 100644 --- a/contrib/cvs/src/mkmodules.c +++ b/contrib/cvs/src/mkmodules.c @@ -283,6 +283,11 @@ static const char *const config_contents[] = { "# Set `PreservePermissions' to `yes' to save file status information\n", "# in the repository.\n", "#PreservePermissions=no\n", + "\n", + "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", + "# level of the new working directory when using the `cvs checkout'\n", + "# command.\n", + "#TopLevelAdmin=no\n", NULL }; diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c index 735502a..d5a17a2 100644 --- a/contrib/cvs/src/rcs.c +++ b/contrib/cvs/src/rcs.c @@ -81,6 +81,9 @@ static void free_rcsnode_contents PROTO((RCSNode *)); static void free_rcsvers_contents PROTO((RCSVers *)); static void rcsvers_delproc PROTO((Node * p)); static char *translate_symtag PROTO((RCSNode *, const char *)); +static char *RCS_addbranch PROTO ((RCSNode *, const char *)); +static char *truncate_revnum_in_place PROTO ((char *)); +static char *truncate_revnum PROTO ((const char *)); static char *printable_date PROTO((const char *)); static char *escape_keyword_value PROTO ((const char *, int *)); static void expand_keywords PROTO((RCSNode *, RCSVers *, const char *, @@ -565,7 +568,9 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) key, rcsfile); free (rdata->desc); } - rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL); + /* Don't need to rcsbuf_valcopy `value' because + getdelta already did that. */ + rdata->desc = xstrdup (value); } rdata->delta_pos = rcsbuf_ftell (&rcsbuf); @@ -801,6 +806,9 @@ free_rcsnode_contents (rnode) /* free_rcsvers_contents -- free up the contents of an RCSVers node, but also free the pointer to the node itself. */ +/* Note: The `hardlinks' list is *not* freed, since it is merely a + pointer into the `hardlist' structure (defined in hardlink.c), and + that structure is freed elsewhere in the program. */ static void free_rcsvers_contents (rnode) @@ -1283,6 +1291,480 @@ rcsbuf_getkey (rcsbuf, keyp, valp) #undef my_whitespace } +/* TODO: Eliminate redundant code in rcsbuf_getkey, rcsbuf_getid, + rcsbuf_getstring, rcsbuf_getword. These last three functions were + all created by hacking monstrous swaths of code from rcsbuf_getkey, + and some engineering would make the code easier to read and + maintain. + + Note that the extreme hair in rcsbuf_getkey is because profiling + statistics show that it was worth it. + + We probably could be processing "hardlinks" by first calling + rcsbuf_getkey, and breaking up the value afterwards; the code to + break it up would not need to be hacked for speed. This would + remove the need for rcsbuf_getword, rcsbuf_getid, and + rcsbuf_getstring, as the other calls are easy to remove. */ + +/* Read an `id' (in the sense of rcsfile(5)) from RCSBUF, and store in + IDP. */ + +static int +rcsbuf_getid (rcsbuf, idp) + struct rcsbuffer *rcsbuf; + char **idp; +{ + register const char * const my_spacetab = spacetab; + register char *ptr, *ptrend; + char c; + +#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) + + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + /* Sanity check. */ + if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size) + abort (); + + /* If the pointer is more than RCSBUF_BUFSIZE bytes into the + buffer, move back to the start of the buffer. This keeps the + buffer from growing indefinitely. */ + if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) + { + int len; + + len = ptrend - ptr; + + /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes + at a time, so we can't have more bytes than that past PTR. */ + if (len > RCSBUF_BUFSIZE) + abort (); + + /* Update the POS field, which holds the file offset of the + first byte in the RCSBUF_BUFFER buffer. */ + rcsbuf->pos += ptr - rcsbuf_buffer; + + memcpy (rcsbuf_buffer, ptr, len); + ptr = rcsbuf_buffer; + ptrend = ptr + len; + rcsbuf->ptrend = ptrend; + } + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + return 0; + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! my_whitespace (c)) + break; + + ++ptr; + } + + /* We've found the start of the key. */ + + *idp = ptr; + + if (c != ';') + { + while (1) + { + ++ptr; + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, idp, (char **) NULL); + if (ptr == NULL) + error (1, 0, "EOF in key in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + c = *ptr; + if (c == ';' || my_whitespace (c)) + break; + } + } + + /* Here *IDP points to the id in the buffer, C is the character + we found at the end of the key, and PTR points to the location in + the buffer where we found C. We may not set *PTR to \0, because + it may overwrite a terminating semicolon. The calling function + must copy and terminate the id on its own. */ + + rcsbuf->ptr = ptr; + return 1; + +#undef my_whitespace +} + +/* Read an RCS @-delimited string. Store the result in STRP. */ + +static int +rcsbuf_getstring (rcsbuf, strp) + struct rcsbuffer *rcsbuf; + char **strp; +{ + register const char * const my_spacetab = spacetab; + register char *ptr, *ptrend; + char *pat; + size_t vlen; + char c; + +#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) + + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + /* Sanity check. */ + if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size) + abort (); + + /* If the pointer is more than RCSBUF_BUFSIZE bytes into the + buffer, move back to the start of the buffer. This keeps the + buffer from growing indefinitely. */ + if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) + { + int len; + + len = ptrend - ptr; + + /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes + at a time, so we can't have more bytes than that past PTR. */ + if (len > RCSBUF_BUFSIZE) + abort (); + + /* Update the POS field, which holds the file offset of the + first byte in the RCSBUF_BUFFER buffer. */ + rcsbuf->pos += ptr - rcsbuf_buffer; + + memcpy (rcsbuf_buffer, ptr, len); + ptr = rcsbuf_buffer; + ptrend = ptr + len; + rcsbuf->ptrend = ptrend; + } + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + error (1, 0, "unexpected end of file reading %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! my_whitespace (c)) + break; + + ++ptr; + } + + /* PTR should now point to the start of a string. */ + if (c != '@') + error (1, 0, "expected @-string at `%c' in %s", c, rcsbuf->filename); + + /* Optimize the common case of a value composed of a single + '@' string. */ + + rcsbuf->at_string = 1; + + ++ptr; + + *strp = ptr; + + while (1) + { + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) + { + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to + the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, NULL, strp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + /* Note that we pass PAT, not PTR, here. */ + pat = rcsbuf_fill (rcsbuf, pat, NULL, strp); + if (pat == NULL) + { + /* EOF here is OK; it just means that the last + character of the file was an '@' terminating a + value for a key type which does not require a + trailing ';'. */ + pat = rcsbuf->ptrend - 1; + + } + ptrend = rcsbuf->ptrend; + + /* Note that the value of PTR is bogus here. This is + OK, because we don't use it. */ + } + + if (pat + 1 >= ptrend || pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ++rcsbuf->embedded_at; + ptr = pat + 2; + } + + /* Here PAT points to the final '@' in the string. */ + + *pat = '\0'; + + vlen = pat - *strp; + if (vlen == 0) + *strp = NULL; + rcsbuf->vlen = vlen; + rcsbuf->ptr = pat + 1; + + return 1; + +#undef my_whitespace +} + +/* Read an RCS `word', in the sense of rcsfile(5) (an id, a num, a + @-delimited string, or `:'). Store the result in WORDP. If a + `;' is reached without reading any text, the result is NULL. */ + +static int +rcsbuf_getword (rcsbuf, wordp) + struct rcsbuffer *rcsbuf; + char **wordp; +{ + register const char * const my_spacetab = spacetab; + register char *ptr, *ptrend; + char c; + +#define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0) + + rcsbuf->vlen = 0; + rcsbuf->at_string = 0; + rcsbuf->embedded_at = 0; + + ptr = rcsbuf->ptr; + ptrend = rcsbuf->ptrend; + + /* Sanity check. */ + if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size) + abort (); + + /* If the pointer is more than RCSBUF_BUFSIZE bytes into the + buffer, move back to the start of the buffer. This keeps the + buffer from growing indefinitely. */ + if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE) + { + int len; + + len = ptrend - ptr; + + /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes + at a time, so we can't have more bytes than that past PTR. */ + if (len > RCSBUF_BUFSIZE) + abort (); + + /* Update the POS field, which holds the file offset of the + first byte in the RCSBUF_BUFFER buffer. */ + rcsbuf->pos += ptr - rcsbuf_buffer; + + memcpy (rcsbuf_buffer, ptr, len); + ptr = rcsbuf_buffer; + ptrend = ptr + len; + rcsbuf->ptrend = ptrend; + } + + /* Skip leading whitespace. */ + + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL); + if (ptr == NULL) + error (1, 0, "unexpected end of file reading %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + c = *ptr; + if (! my_whitespace (c)) + break; + + ++ptr; + } + + /* If we have reached `;', there is no value. */ + if (c == ';') + { + *wordp = NULL; + *ptr++ = '\0'; + rcsbuf->ptr = ptr; + rcsbuf->vlen = 0; + return 1; + } + + /* PTR now points to the start of a value. Find out whether it is + a num, an id, a string or a colon. */ + if (c == ':') + { + *wordp = ptr++; + rcsbuf->ptr = ptr; + rcsbuf->vlen = 1; + return 1; + } + + if (c == '@') + { + char *pat; + size_t vlen; + + /* Optimize the common case of a value composed of a single + '@' string. */ + + rcsbuf->at_string = 1; + + ++ptr; + + *wordp = ptr; + + while (1) + { + while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL) + { + /* Note that we pass PTREND as the PTR value to + rcsbuf_fill, so that we will wind up setting PTR to + the location corresponding to the old PTREND, so + that we don't search the same bytes again. */ + ptr = rcsbuf_fill (rcsbuf, ptrend, NULL, wordp); + if (ptr == NULL) + error (1, 0, + "EOF while looking for end of string in RCS file %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Handle the special case of an '@' right at the end of + the known bytes. */ + if (pat + 1 >= ptrend) + { + /* Note that we pass PAT, not PTR, here. */ + pat = rcsbuf_fill (rcsbuf, pat, NULL, wordp); + if (pat == NULL) + { + /* EOF here is OK; it just means that the last + character of the file was an '@' terminating a + value for a key type which does not require a + trailing ';'. */ + pat = rcsbuf->ptrend - 1; + + } + ptrend = rcsbuf->ptrend; + + /* Note that the value of PTR is bogus here. This is + OK, because we don't use it. */ + } + + if (pat + 1 >= ptrend || pat[1] != '@') + break; + + /* We found an '@' pair in the string. Keep looking. */ + ++rcsbuf->embedded_at; + ptr = pat + 2; + } + + /* Here PAT points to the final '@' in the string. */ + + *pat = '\0'; + + vlen = pat - *wordp; + if (vlen == 0) + *wordp = NULL; + rcsbuf->vlen = vlen; + rcsbuf->ptr = pat + 1; + + return 1; + } + + /* C is neither `:', `;' nor `@', so it should be the start of a num + or an id. Make sure it is not another special character. */ + if (c == '$' || c == '.' || c == ',') + { + error (1, 0, "illegal special character in RCS field in %s", + rcsbuf->filename); + } + + *wordp = ptr; + while (1) + { + if (ptr >= ptrend) + { + ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, wordp); + if (ptr == NULL) + error (1, 0, "unexpected end of file reading %s", + rcsbuf->filename); + ptrend = rcsbuf->ptrend; + } + + /* Legitimate ID characters are digits, dots and any `graphic + printing character that is not a special.' This test ought + to do the trick. */ + c = *ptr; + if (isprint (c) && + c != ';' && c != '$' && c != ',' && c != '@' && c != ':') + { + ++ptr; + continue; + } + break; + } + + /* PTR points to the last non-id character in this word, and C is + the character in its memory cell. Check to make sure that it + is a legitimate word delimiter -- whitespace or semicolon. */ + if (c == ';' || my_whitespace (c)) + { + rcsbuf->vlen = ptr - *wordp; + rcsbuf->ptr = ptr; + return 1; + } + + error (1, 0, "illegal special character in RCS field in %s", + rcsbuf->filename); + /* Shut up compiler warnings. */ + return 0; + +#undef my_whitespace +} + /* Read an RCS revision number from an RCS file. This sets *REVP to point to the revision number; it will point to space that is managed by the rcsbuf functions, and is only good until the next @@ -1877,6 +2359,89 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag) } /* + * Get existing revision number corresponding to tag or revision. + * Similar to RCS_gettag but less interpretation imposed. + * For example: + * -- If tag designates a magic branch, RCS_tag2rev + * returns the magic branch number. + * -- If tag is a branch tag, returns the branch number, not + * the revision of the head of the branch. + * If tag or revision is not valid or does not exist in file, + * exit with error. + */ +char * +RCS_tag2rev (rcs, tag) + RCSNode *rcs; + char *tag; +{ + char *rev, *pa, *pb; + int i; + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + /* If a valid revision, try to look it up */ + if ( RCS_valid_rev (tag) ) + { + /* Make a copy so we can scribble on it */ + rev = xstrdup (tag); + + /* If revision exists, return the copy */ + if (RCS_exist_rev (rcs, tag)) + return rev; + + /* Nope, none such. If tag is not a branch we're done. */ + i = numdots (rev); + if ((i & 1) == 1 ) + { + pa = strrchr (rev, '.'); + if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.') + { + free (rev); + error (1, 0, "revision `%s' does not exist", tag); + } + } + + /* Tag is branch, but does not exist, try corresponding + * magic branch tag. + * + * FIXME: assumes all magic branches are of + * form "n.n.n ... .0.n". I'll fix if somebody can + * send me a method to get a magic branch tag with + * the 0 in some other position -- <dan@gasboy.com> + */ + pa = strrchr (rev, '.'); + pb = xmalloc (strlen (rev) + 3); + *pa++ = 0; + (void) sprintf (pb, "%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa); + free (rev); + rev = pb; + if (RCS_exist_rev (rcs, rev)) + return rev; + error (1, 0, "revision `%s' does not exist", tag); + } + + + RCS_check_tag (tag); /* exit if not a valid tag */ + + /* If tag is "HEAD", special case to get head RCS revision */ + if (tag && (strcmp (tag, TAG_HEAD) == 0)) + return (RCS_head (rcs)); + + /* If valid tag let translate_symtag say yea or nay. */ + rev = translate_symtag (rcs, tag); + + if (rev) + return rev; + + error (1, 0, "tag `%s' does not exist", tag); + /* NOT REACHED -- error (1 ... ) does not return here */ + return 0; +} + +/* * Find the revision for a specific tag. * If force_tag_match is set, return NULL if an exact match is not * possible otherwise return RCS_head (). We are careful to look for @@ -2358,6 +2923,41 @@ RCS_getbranch (rcs, tag, force_tag_match) return (xstrdup (vn->version)); } +/* Returns the head of the branch which REV is on. REV can be a + branch tag or non-branch tag; symbolic or numeric. + + Returns a newly malloc'd string. Returns NULL if a symbolic name + isn't found. */ + +char * +RCS_branch_head (rcs, rev) + RCSNode *rcs; + char *rev; +{ + char *num; + char *br; + char *retval; + + assert (rcs != NULL); + + if (RCS_nodeisbranch (rcs, rev)) + return RCS_getbranch (rcs, rev, 1); + + if (isdigit (*rev)) + num = xstrdup (rev); + else + { + num = translate_symtag (rcs, rev); + if (num == NULL) + return NULL; + } + br = truncate_revnum (num); + retval = RCS_getbranch (rcs, br, 1); + free (br); + free (num); + return retval; +} + /* Get the branch point for a particular branch, that is the first revision on that branch. For example, RCS_getbranchpoint (rcs, "1.3.2") will normally return "1.3.2.1". TARGET may be either a @@ -2885,6 +3485,40 @@ RCS_check_tag (tag) } /* + * TRUE if argument has valid syntax for an RCS revision or + * branch number. All characters must be digits or dots, first + * and last characters must be digits, and no two consecutive + * characters may be dots. + * + * Intended for classifying things, so this function doesn't + * call error. + */ +int +RCS_valid_rev (rev) + char *rev; +{ + char last, c; + last = *rev++; + if (!isdigit (last)) + return 0; + while ((c = *rev++)) /* Extra parens placate -Wall gcc option */ + { + if (c == '.') + { + if (last == '.') + return 0; + continue; + } + last = c; + if (!isdigit (c)) + return 0; + } + if (!isdigit (last)) + return 0; + return 1; +} + +/* * Return true if RCS revision with TAG is a dead revision. */ int @@ -3558,11 +4192,10 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) size_t loglen; Node *vp = NULL; #ifdef PRESERVE_PERMISSIONS_SUPPORT - uid_t rcs_owner; - gid_t rcs_group; + uid_t rcs_owner = (uid_t) -1; + gid_t rcs_group = (gid_t) -1; mode_t rcs_mode; - int change_rcs_owner = 0; - int change_rcs_group = 0; + int change_rcs_owner_or_group = 0; int change_rcs_mode = 0; int special_file = 0; unsigned long devnum_long; @@ -3714,7 +4347,6 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) { RCSVers *vers; Node *info; - struct hardlink_info *hlinfo; vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev); if (vp == NULL) @@ -3763,64 +4395,36 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (workfile != NULL) { - info = findnode (vers->other_delta, "hardlinks"); - if (info != NULL) + List *links = vers->hardlinks; + if (links != NULL) { - char *links = xstrdup (info->data); - char *working_dir = xgetwd(); - char *p, *file = NULL; - Node *n, *uptodate_link; + Node *uptodate_link; /* For each file in the hardlinks field, check to see if it exists, and if so, if it has been checked out - this iteration. */ - uptodate_link = NULL; - for (p = strtok (links, " "); - p != NULL && uptodate_link == NULL; - p = strtok (NULL, " ")) - { - file = (char *) - xmalloc (sizeof(char) * - (strlen(working_dir) + strlen(p) + 2)); - sprintf (file, "%s/%s", working_dir, p); - n = lookup_file_by_inode (file); - if (n == NULL) - { - if (strcmp (p, workfile) != 0) - { - /* One of the files that WORKFILE should be - linked to is not even in the working directory. - The user should probably be warned. */ - error (0, 0, - "warning: %s should be hardlinked to %s, but is missing", - p, workfile); - } - free (file); - continue; - } + this iteration. When walklist returns, uptodate_link + should point to a hardlist node representing a file + in `links' which has recently been checked out, or + NULL if no file in `links' has yet been checked out. */ - /* hlinfo may be NULL if, for instance, a file is being - removed. */ - hlinfo = (struct hardlink_info *) n->data; - if (hlinfo && hlinfo->checked_out) - uptodate_link = n; - free (file); - } - free (links); - free (working_dir); + uptodate_link = NULL; + (void) walklist (links, find_checkedout_proc, &uptodate_link); + dellist (&links); /* If we've found a file that `workfile' is supposed to be linked to, and it has been checked out since CVS was - invoked, then simply link workfile to that file. + invoked, then simply link workfile to that file and return. - If one of these conditions is not met, then we're - checking out workfile to a temp file or stdout, or - workfile is the first one in its hardlink group to be - checked out. Either way we must continue with a full + If one of these conditions is not met, then + workfile is the first one in its hardlink group to + be checked out, and we must continue with a full checkout. */ if (uptodate_link != NULL) { + struct hardlink_info *hlinfo = + (struct hardlink_info *) uptodate_link->data; + if (link (uptodate_link->key, workfile) < 0) error (1, errno, "cannot link %s to %s", workfile, uptodate_link->key); @@ -3837,13 +4441,13 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) info = findnode (vers->other_delta, "owner"); if (info != NULL) { - change_rcs_owner = 1; + change_rcs_owner_or_group = 1; rcs_owner = (uid_t) strtoul (info->data, NULL, 10); } info = findnode (vers->other_delta, "group"); if (info != NULL) { - change_rcs_group = 1; + change_rcs_owner_or_group = 1; rcs_group = (gid_t) strtoul (info->data, NULL, 10); } info = findnode (vers->other_delta, "permissions"); @@ -3899,6 +4503,9 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } } + if (free_rev) + free (rev); + if (log != NULL) { free (log); @@ -3968,9 +4575,25 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (islink (workfile)) if (unlink_file (workfile) < 0) error (1, errno, "cannot remove %s", workfile); + ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); + + /* If the open failed because the existing workfile was not + writable, try to chmod the file and retry the open. */ + if (ofp == NULL && errno == EACCES + && isfile (workfile) && !iswritable (workfile)) + { + xchmod (workfile, 1); + ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w"); + } + if (ofp == NULL) - error (1, errno, "cannot open %s", workfile); + { + error (0, errno, "cannot open %s", workfile); + if (free_value) + free (value); + return 1; + } } if (workfile == NULL && sout == RUN_TTY) @@ -4002,10 +4625,15 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) while (nleft > 0) { if (fwrite (p, 1, nstep, ofp) != nstep) - error (1, errno, "cannot write %s", + { + error (0, errno, "cannot write %s", (workfile != NULL ? workfile : (sout != RUN_TTY ? sout : "stdout"))); + if (free_value) + free (value); + return 1; + } p += nstep; nleft -= nstep; if (nleft < nstep) @@ -4014,18 +4642,24 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) } } + if (free_value) + free (value); + if (workfile != NULL) { int ret; #ifdef PRESERVE_PERMISSIONS_SUPPORT if (!special_file && fclose (ofp) < 0) - error (1, errno, "cannot close %s", workfile); + { + error (0, errno, "cannot close %s", workfile); + return 1; + } - if (change_rcs_owner || change_rcs_group) + if (change_rcs_owner_or_group) { if (chown (workfile, rcs_owner, rcs_group) < 0) - error (0, errno, "could not change file ownership on %s", + error (0, errno, "could not change owner or group of %s", workfile); } @@ -4035,7 +4669,10 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); #else if (fclose (ofp) < 0) - error (1, errno, "cannot close %s", workfile); + { + error (0, errno, "cannot close %s", workfile); + return 1; + } ret = chmod (workfile, sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)); @@ -4053,7 +4690,10 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) !special_file && #endif fclose (ofp) < 0) - error (1, errno, "cannot close %s", sout); + { + error (0, errno, "cannot close %s", sout); + return 1; + } } #ifdef PRESERVE_PERMISSIONS_SUPPORT @@ -4063,11 +4703,6 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) update_hardlink_info (workfile); #endif - if (free_value) - free (value); - if (free_rev) - free (rev); - return 0; } @@ -4394,6 +5029,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) int status, checkin_quiet, allocated_workfile; struct tm *ftm; time_t modtime; + int adding_branch = 0; commitpt = NULL; @@ -4414,6 +5050,41 @@ RCS_checkin (rcs, workfile, message, rev, flags) allocated_workfile = 1; } + /* Is the backend file a symbolic link? Follow it and replace the + filename with the destination of the link. */ + + while (islink (rcs->path)) + { + char *newname; +#ifdef HAVE_READLINK + /* The clean thing to do is probably to have each filesubr.c + implement this (with an error if not supported by the + platform, in which case islink would presumably return 0). + But that would require editing each filesubr.c and so the + expedient hack seems to be looking at HAVE_READLINK. */ + newname = xreadlink (rcs->path); +#else + error (1, 0, "internal error: islink doesn't like readlink"); +#endif + + if (isabsolute (newname)) + { + free (rcs->path); + rcs->path = newname; + } + else + { + char *oldname = last_component (rcs->path); + int dirlen = oldname - rcs->path; + char *fullnewname = xmalloc (dirlen + strlen (newname) + 1); + strncpy (fullnewname, rcs->path, dirlen); + strcpy (fullnewname + dirlen, newname); + free (newname); + free (rcs->path); + rcs->path = fullnewname; + } + } + checkin_quiet = flags & RCS_FLAGS_QUIET; if (!checkin_quiet) { @@ -4460,7 +5131,6 @@ RCS_checkin (rcs, workfile, message, rev, flags) Node *np; struct stat sb; char buf[64]; /* static buffer should be safe: see usage. -twp */ - char *fullpath; delta->other_delta = getlist(); @@ -4515,25 +5185,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) } /* Save hardlinks. */ - fullpath = xgetwd(); - fullpath = xrealloc (fullpath, - strlen(fullpath) + strlen(workfile) + 2); - sprintf (fullpath + strlen(fullpath), "/%s", workfile); - - np = lookup_file_by_inode (fullpath); - if (np == NULL) - { - error (1, 0, "lost information on %s's linkage", workfile); - } - else - { - struct hardlink_info *hlinfo; - hlinfo = (struct hardlink_info *) np->data; - np = getnode(); - np->key = xstrdup ("hardlinks"); - np->data = xstrdup (hlinfo->links); - (void) addnode (delta->other_delta, np); - } + delta->hardlinks = list_linked_files_on_disk (workfile); } } #endif @@ -4707,6 +5359,7 @@ RCS_checkin (rcs, workfile, message, rev, flags) goto checkin_done; } delta->version = RCS_addbranch (rcs, branch); + adding_branch = 1; p = strrchr (branch, '.'); *p = '\0'; tip = xstrdup (branch); @@ -4752,13 +5405,26 @@ RCS_checkin (rcs, workfile, message, rev, flags) { if (! STREQ (nodep->data, delta->author)) { - error (0, 0, "%s: revision %s locked by %s", - rcs->path, - nodep->key, nodep->data); - status = 1; - goto checkin_done; + /* If we are adding a branch, then leave the old lock around. + That is sensible in the sense that when adding a branch, + we don't need to use the lock to tell us where to check + in. It is fishy in the sense that if it is our own lock, + we break it. However, this is the RCS 5.7 behavior (at + the end of addbranch in ci.c in RCS 5.7, it calls + removelock only if it is our own lock, not someone + else's). */ + + if (!adding_branch) + { + error (0, 0, "%s: revision %s locked by %s", + rcs->path, + nodep->key, nodep->data); + status = 1; + goto checkin_done; + } } - delnode (nodep); + else + delnode (nodep); } dtext->version = xstrdup (delta->version); @@ -5025,6 +5691,9 @@ RCS_cmp_file (rcs, rev, options, filename) #endif { fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r"); + if (fp == NULL) + /* FIXME-update-dir: should include update_dir in message. */ + error (1, errno, "cannot open file %s for comparing", filename); data.filename = filename; data.fp = fp; @@ -5216,8 +5885,11 @@ RCS_setbranch (rcs, rev) } /* Lock revision REV. LOCK_QUIET is 1 to suppress output. 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. */ + Most of the callers only call us because RCS_checkin still tends to + like a lock (a relic of old behavior inherited from the RCS ci + program). If we clean this up, only "cvs admin -l" will still need + to call RCS_lock. */ + /* FIXME-twp: if a lock owned by someone else is broken, should this send mail to the lock owner? Prompt user? It seems like such an obscure situation for CVS as almost not worth worrying much @@ -5286,6 +5958,18 @@ RCS_lock (rcs, rev, lock_quiet) return 0; } +#if 0 + /* Well, first of all, "rev" below should be "xrev" to avoid + core dumps. But more importantly, should we really be + breaking the lock unconditionally? What CVS 1.9 does (via + RCS) is to prompt "Revision 1.1 is already locked by fred. + Do you want to break the lock? [ny](n): ". Well, we don't + want to interact with the user (certainly not at the + server/protocol level, and probably not in the command-line + client), but isn't it more sensible to give an error and + let the user run "cvs admin -u" if they want to break the + lock? */ + /* Break the lock. */ if (!lock_quiet) { @@ -5293,6 +5977,9 @@ RCS_lock (rcs, rev, lock_quiet) cvs_output (" unlocked\n", 0); } delnode (p); +#else + error (1, 0, "Revision %s is already locked by %s", xrev, p->data); +#endif } /* Create a new lock. */ @@ -5823,6 +6510,18 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) goto delrev_done; } + if (after == NULL && before == NULL) + { + /* The user is trying to delete all revisions. While an + RCS file without revisions makes sense to RCS (e.g. the + state after "rcs -i"), CVS has never been able to cope with + it. So at least for now we just make this an error. + + We don't include rcs->path in the message since "cvs admin" + already printed "RCS file:" and the name. */ + error (1, 0, "attempt to delete all revisions"); + } + /* The conditionals at this point get really hairy. Here is the general idea: @@ -5835,8 +6534,6 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) RCS_exec_rcsdiff with some changes, like being able to suppress diagnostic messages and to direct output. */ - assert (before != NULL || after != NULL); - if (after != NULL) { char *diffbuf; @@ -6008,6 +6705,52 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) return status; } + +/* + * TRUE if there exists a symbolic tag "tag" in file. + */ +int +RCS_exist_tag (rcs, tag) + RCSNode *rcs; + char *tag; +{ + + assert (rcs != NULL); + + if (findnode (RCS_symbols (rcs), tag)) + return 1; + return 0; + +} + +/* + * TRUE if RCS revision number "rev" exists. + * This includes magic branch revisions, not found in rcs->versions, + * but only in rcs->symbols, requiring a list walk to find them. + * Take advantage of list walk callback function already used by + * RCS_delete_revs, above. + */ +int +RCS_exist_rev (rcs, rev) + RCSNode *rcs; + char *rev; +{ + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL); + + if (findnode(rcs->versions, rev) != 0) + return 1; + + if (walklist (RCS_symbols(rcs), findtag, rev) != 0) + return 1; + + return 0; + +} + /* RCS_deltas and friends. Processing of the deltas in RCS files. */ @@ -6804,7 +7547,7 @@ getdelta (rcsbuf, rcsfile, keyp, valp) char **valp; { RCSVers *vnode; - char *key, *value, *cp; + char *key, *value, *keybuf, *valbuf, *cp; Node *kv; /* Get revision number if it wasn't passed in. This uses @@ -6868,7 +7611,8 @@ unable to parse %s; `author' not in the expected place", rcsfile); error (1, 0, "\ unable to parse %s; `state' not in the expected place", rcsfile); vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL); - if (STREQ (value, "dead")) + /* The value is optional, according to rcsfile(5). */ + if (value != NULL && STREQ (value, "dead")) { vnode->dead = 1; } @@ -6919,11 +7663,80 @@ unable to parse %s; `state' not in the expected place", rcsfile); */ while (1) { - if (! rcsbuf_getkey (rcsbuf, &key, &value)) + int len; + size_t valbuflen; + + key = NULL; + + if (! rcsbuf_getid (rcsbuf, &keybuf)) error (1, 0, "unexpected end of file reading %s", rcsfile); + /* rcsbuf_getid did not terminate the key, so copy it to new space. */ + len = rcsbuf->ptr - keybuf; + key = (char *) xmalloc (sizeof(char) * (len + 1)); + strncpy (key, keybuf, len); + key[len] = '\0'; + + /* The `desc' keyword has only a single string value, with no + trailing semicolon, so it must be handled specially. */ if (STREQ (key, RCSDESC)) + { + (void) rcsbuf_getstring (rcsbuf, &valbuf); + value = rcsbuf_valcopy (rcsbuf, valbuf, 1, &valbuflen); break; + } + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* The `hardlinks' value is a group of words, which must + be parsed separately and added as a list to vnode->hardlinks. */ + if (STREQ (key, "hardlinks")) + { + Node *n; + + vnode->hardlinks = getlist(); + while (1) + { + if (! rcsbuf_getword (rcsbuf, &valbuf)) + error (1, 0, "unexpected end of file reading %s", rcsfile); + if (valbuf == NULL) + break; + n = getnode(); + n->key = rcsbuf_valcopy (rcsbuf, valbuf, 1, NULL); + addnode (vnode->hardlinks, n); + } + continue; + } +#endif + + /* Get the value. */ + value = NULL; + while (1) + { + if (! rcsbuf_getword (rcsbuf, &valbuf)) + error (1, 0, "unexpected end of file reading %s", rcsfile); + if (valbuf == NULL) + break; + + /* Copy valbuf to new space so we can polish it, then + append it to value. */ + + if (value == NULL) + { + value = rcsbuf_valcopy (rcsbuf, valbuf, 1, &valbuflen); + } + else + { + char *temp_value; + + temp_value = rcsbuf_valcopy (rcsbuf, valbuf, 1, &valbuflen); + len = strlen (value); + value = (char *) xrealloc + (value, sizeof(char) * (len + valbuflen + 2)); + value[len] = ' '; + strcpy (value + len + 1, temp_value); + free (temp_value); + } + } /* Enable use of repositories created by certain obsolete versions of CVS. This code should remain indefinately; @@ -6952,8 +7765,8 @@ unable to parse %s; `state' not in the expected place", rcsfile); vnode->other_delta = getlist (); kv = getnode (); kv->type = RCSFIELD; - kv->key = xstrdup (key); - kv->data = rcsbuf_valcopy (rcsbuf, value, 1, (size_t *) NULL); + kv->key = key; + kv->data = value; if (addnode (vnode->other_delta, kv) != 0) { /* Complaining about duplicate keys in newphrases seems @@ -7143,6 +7956,36 @@ putrcsfield_proc (node, vfp) return 0; } +#ifdef PRESERVE_PERMISSIONS_SUPPORT + +/* Save a filename in a `hardlinks' RCS field. NODE->KEY will contain + a full pathname, but currently only basenames are stored in the RCS + node. Assume that the filename includes nasty characters and + @-escape it. */ + +static int +puthardlink_proc (node, vfp) + Node *node; + void *vfp; +{ + FILE *fp = (FILE *) vfp; + char *basename = strrchr (node->key, '/'); + + if (basename == NULL) + basename = node->key; + else + ++basename; + + putc ('\t', fp); + putc ('@', fp); + (void) expand_at_signs (basename, strlen (basename), fp); + putc ('@', fp); + + return 0; +} + +#endif + /* Output the admin node for RCS into stream FP. */ static void @@ -7228,6 +8071,14 @@ putdelta (vers, fp) walklist (vers->other_delta, putrcsfield_proc, fp); +#ifdef PRESERVE_PERMISSIONS_SUPPORT + if (vers->hardlinks) + { + fprintf (fp, "\nhardlinks"); + walklist (vers->hardlinks, puthardlink_proc, fp); + putc (';', fp); + } +#endif putc ('\n', fp); } @@ -7461,7 +8312,7 @@ RCS_copydeltas (rcs, fin, rcsbufin, fout, newdtext, insertpt) to count the number of RCS revisions for which some special action is required. */ -int +static int count_delta_actions (np, ignore) Node *np; void *ignore; @@ -7557,8 +8408,6 @@ rcs_internal_lockfile (rcsfile) error (1, errno, "could not open lock file `%s'", lockfile); } - free (lockfile); - /* Force the file permissions, and return a stream object. */ /* Because we change the modes later, we don't worry about this in the non-HAVE_FCHMOD case. */ @@ -7569,6 +8418,9 @@ rcs_internal_lockfile (rcsfile) fp = fdopen (fd, FOPEN_BINARY_WRITE); if (fp == NULL) error (1, errno, "cannot fdopen %s", lockfile); + + free (lockfile); + return fp; } @@ -7800,6 +8652,9 @@ annotate (argc, argv) } #endif /* CLIENT_SUPPORT */ + if (tag != NULL) + tag_check_valid (tag, argc, argv, local, 0, ""); + return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index fab0f0b..dc0c7be 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -154,6 +154,10 @@ struct rcsversnode List *other; /* Newphrase fields from delta nodes. */ List *other_delta; +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Hard link information for each revision. */ + List *hardlinks; +#endif }; typedef struct rcsversnode RCSVers; @@ -185,6 +189,9 @@ 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 *simple_tag)); +int RCS_exist_rev PROTO((RCSNode *rcs, char *rev)); +int RCS_exist_tag PROTO((RCSNode *rcs, char *tag)); +char *RCS_tag2rev PROTO((RCSNode *rcs, char *tag)); char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date, int force_tag_match, int *simple_tag)); char *RCS_magicrev PROTO((RCSNode *rcs, char *rev)); @@ -196,9 +203,11 @@ int RCS_datecmp PROTO((char *date1, char *date2)); time_t RCS_getrevtime PROTO((RCSNode * rcs, char *rev, char *date, int fudge)); List *RCS_symbols PROTO((RCSNode *rcs)); void RCS_check_tag PROTO((const char *tag)); +int RCS_valid_rev PROTO ((char *rev)); List *RCS_getlocks PROTO((RCSNode *rcs)); void freercsnode PROTO((RCSNode ** rnodep)); char *RCS_getbranch PROTO((RCSNode * rcs, char *tag, int force_tag_match)); +char *RCS_branch_head PROTO ((RCSNode *rcs, char *rev)); int RCS_isdead PROTO((RCSNode *, const char *)); char *RCS_getexpand PROTO ((RCSNode *)); diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index 490bf68..f889233 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -11,6 +11,8 @@ #include "cvs.h" #include <assert.h> +#include <stdio.h> +#include "diffrun.h" /* This file, rcs.h, and rcs.c, together sometimes known as the "RCS library", are intended to define our interface to RCS files. @@ -75,6 +77,11 @@ static void call_diff_setup PROTO ((const char *prog)); static int call_diff PROTO ((char *out)); static int call_diff3 PROTO ((char *out)); +static void call_diff_write_output PROTO((const char *, size_t)); +static void call_diff_flush_output PROTO((void)); +static void call_diff_write_stdout PROTO((const char *)); +static void call_diff_error PROTO((const char *, const char *, const char *)); + /* VARARGS */ static void call_diff_setup (prog) @@ -132,59 +139,92 @@ call_diff_add_arg (s) call_diff_argv[call_diff_argc] = (char *) 0; } -/* diff_run is imported from libdiff.a. */ -extern int diff_run PROTO ((int argc, char **argv, char *out)); +/* Callback function for the diff library to write data to the output + file. This is used when we are producing output to stdout. */ + +static void +call_diff_write_output (text, len) + const char *text; + size_t len; +{ + cvs_output (text, len); +} + +/* Call back function for the diff library to flush the output file. + This is used when we are producing output to stdout. */ + +static void +call_diff_flush_output () +{ + cvs_flushout (); +} + +/* Call back function for the diff library to write to stdout. */ + +static void +call_diff_write_stdout (text) + const char *text; +{ + cvs_output (text, 0); +} + +/* Call back function for the diff library to write to stderr. */ + +static void +call_diff_error (format, a1, a2) + const char *format; + const char *a1; + const char *a2; +{ + /* FIXME: Should we somehow indicate that this error is coming from + the diff library? */ + error (0, 0, format, a1, a2); +} + +/* This set of callback functions is used if we are sending the diff + to stdout. */ + +static struct diff_callbacks call_diff_stdout_callbacks = +{ + call_diff_write_output, + call_diff_flush_output, + call_diff_write_stdout, + call_diff_error +}; + +/* This set of callback functions is used if we are sending the diff + to a file. */ + +static struct diff_callbacks call_diff_file_callbacks = +{ + (void (*) PROTO((const char *, size_t))) NULL, + (void (*) PROTO((void))) NULL, + call_diff_write_stdout, + call_diff_error +}; static int call_diff (out) char *out; { - /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output - with has "Index: foo" and such; stdout and/or stderr for diff's - output). I think the only reason that this used to not be such - a problem is that the time spent on the fork() and exec() of diff - slowed us down enough to let the "Index:" make it through first. - - The real fix, of course, will be to have the diff library do all - its output through callbacks (which CVS will supply as cvs_output - and cvs_outerr). */ -#if defined(SERVER_SUPPORT) - /* only do this on the server if it's in protocol mode */ - if (error_use_protocol || server_active) - usleep (50); -#endif - if (out == RUN_TTY) - return diff_run (call_diff_argc, call_diff_argv, NULL); + return diff_run (call_diff_argc, call_diff_argv, NULL, + &call_diff_stdout_callbacks); else - return diff_run (call_diff_argc, call_diff_argv, out); + return diff_run (call_diff_argc, call_diff_argv, out, + &call_diff_file_callbacks); } -extern int diff3_run PROTO ((int argc, char **argv, char *out)); - static int call_diff3 (out) char *out; { - /* Try to keep the out-of-order bugs at bay (protocol_pipe for cvs_output - with has "Index: foo" and such; stdout and/or stderr for diff's - output). I think the only reason that this used to not be such - a problem is that the time spent on the fork() and exec() of diff - slowed us down enough to let the "Index:" make it through first. - - The real fix, of course, will be to have the diff library do all - its output through callbacks (which CVS will supply as cvs_output - and cvs_outerr). */ -#if defined(SERVER_SUPPORT) - /* only do this on the server if it's in protocol mode */ - if (error_use_protocol || server_active) - usleep (50); -#endif - if (out == RUN_TTY) - return diff3_run (call_diff_argc, call_diff_argv, NULL); + return diff3_run (call_diff_argc, call_diff_argv, NULL, + &call_diff_stdout_callbacks); else - return diff3_run (call_diff_argc, call_diff_argv, out); + return diff3_run (call_diff_argc, call_diff_argv, out, + &call_diff_file_callbacks); } diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index 484c048..d88bf2b 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -13,6 +13,9 @@ #include "fileattr.h" #include "edit.h" +#ifdef CLIENT_SUPPORT +static int do_argument_proc PROTO((Node * p, void *closure)); +#endif static int do_dir_proc PROTO((Node * p, void *closure)); static int do_file_proc PROTO((Node * p, void *closure)); static void addlist PROTO((List ** listp, char *key)); @@ -58,6 +61,24 @@ struct frame_and_entries { List *entries; }; +#ifdef CLIENT_SUPPORT +/* This is a callback to send "Argument" commands to the server in the + case we've done a "cvs update" or "cvs commit" in a top-level + directory where there is no CVSADM directory. */ + +static int +do_argument_proc (p, closure) + Node *p; + void *closure; +{ + char *dir = p->key; + send_to_server ("Argument ", 0); + send_to_server (dir, 0); + send_to_server ("\012", 1); + return 0; +} +#endif + /* Start a recursive command. Command line arguments (ARGC, ARGV) dictate the directories and @@ -175,6 +196,19 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, "there is no version here; run '%s checkout' first", program_name); } +#ifdef CLIENT_SUPPORT + else if (client_active && server_started) + { + /* In the the case "cvs update foo bar baz", a call to + send_file_names in update.c will have sent the + appropriate "Argument" commands to the server. In + this case, that won't have happened, so we need to + do it here. While this example uses "update", this + generalizes to other commands. */ + + err += walklist (dirlist, do_argument_proc, NULL); + } +#endif } else addlist (&dirlist, "."); @@ -586,7 +620,6 @@ do_dir_proc (p, closure) char *newrepos; List *sdirlist; char *srepository; - char *cp; Dtype dir_return = R_PROCESS; int stripped_dot = 0; int err = 0; @@ -790,25 +823,8 @@ but CVS uses %s for its own purposes; skipping %s directory", repository = srepository; } -#if 0 - /* Put back update_dir. I think this is the same as just setting - update_dir back to saved_update_dir, but there are a few cases I'm - not sure about (in particular, if DIR is "." and update_dir is - not ""), so for conservatism I'm leaving this here. */ - cp = last_component (update_dir); - if (cp > update_dir) - cp[-1] = '\0'; - else - update_dir[0] = '\0'; - free (saved_update_dir); -#else - /* The above code is cactus!!! - it doesn't handle descending - multiple directories at once! ie: it recurses down several - dirs and then back up one. This breaks 'diff', 'update', - 'commit', etc. */ free (update_dir); update_dir = saved_update_dir; -#endif return (err); } diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index 5d4ffbc..4c18fc9 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -42,8 +42,20 @@ static Key_schedule sched; #ifdef HAVE_GSSAPI #include <netdb.h> + +#ifdef HAVE_GSSAPI_H +#include <gssapi.h> +#endif +#ifdef HAVE_GSSAPI_GSSAPI_H #include <gssapi/gssapi.h> +#endif +#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H #include <gssapi/gssapi_generic.h> +#endif + +#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#endif /* We use Kerberos 5 routines to map the GSSAPI credential to a user name. */ @@ -366,7 +378,8 @@ mkdir_p (dir) int saved_errno = errno; if (saved_errno != EEXIST - && (saved_errno != EACCES || !isdir (q))) + && ((saved_errno != EACCES && saved_errno != EROFS) + || !isdir (q))) { retval = saved_errno; goto done; @@ -638,6 +651,18 @@ server_pathname_check (path) and is unlikely to do us any good here. It also is probably capable of being a security hole in the anonymous readonly case. */ if (isabsolute (path)) + /* Giving an error is actually kind of a cop-out, in the sense + that it would be nice for "cvs co -d /foo/bar/baz" to work. + A quick fix in the server would be requiring Max-dotdot of + at least one if pathnames are absolute, and then putting + /abs/foo/bar/baz in the temp dir beside the /d/d/d stuff. + A cleaner fix in the server might be to decouple the + pathnames we pass back to the client from pathnames in our + temp directory (this would also probably remove the need + for Max-dotdot). A fix in the client would have the client + turn it into "cd /foo/bar; cvs co -d baz" (more or less). + This probably has some problems with pathnames which appear + in messages. */ error (1, 0, "absolute pathname `%s' illegal for server", path); if (pathname_levels (path) > max_dotdot_limit) { @@ -2259,6 +2284,9 @@ error \n"); /* We shouldn't have any partial lines from cvs_output and cvs_outerr, but we handle them here in case there is a bug. */ + /* FIXME: appending a newline, rather than using "MT" as we + do in the child process, is probably not really a very good + way to "handle" them. */ if (! buf_empty_p (saved_output)) { buf_append_char (saved_output, '\n'); @@ -2329,6 +2357,19 @@ error \n"); exitstatus = (*command) (argument_count, argument_vector); + /* Output any partial lines. If the client doesn't support + "MT", we just throw out the partial line, like old versions + of CVS did, since the protocol can't support this. */ + if (supported_response ("MT") && ! buf_empty_p (saved_output)) + { + buf_output0 (protocol, "MT text "); + buf_append_buffer (protocol, saved_output); + buf_output (protocol, "\n", 1); + buf_send_counted (protocol); + } + /* For now we just discard partial lines on stderr. I suspect + that CVS can't write such lines unless there is a bug. */ + /* * When we exit, that will close the pipes, giving an EOF to * the parent. @@ -2843,8 +2884,9 @@ server_register (name, version, timestamp, options, tag, date, conflict) (void) fprintf (stderr, "%c-> server_register(%s, %s, %s, %s, %s, %s, %s)\n", (server_active) ? 'S' : ' ', /* silly */ - name, version, timestamp, options, tag ? tag : "", - date ? date : "", conflict ? conflict : ""); + name, version, timestamp ? timestamp : "", options, + tag ? tag : "", date ? date : "", + conflict ? conflict : ""); } if (entries_line != NULL) @@ -3074,21 +3116,21 @@ static void serve_remove (arg) char *arg; { - do_cvs_command ("cvsremove", cvsremove); + do_cvs_command ("remove", cvsremove); } static void serve_status (arg) char *arg; { - do_cvs_command ("status", status); + do_cvs_command ("status", cvsstatus); } static void serve_rdiff (arg) char *arg; { - do_cvs_command ("patch", patch); + do_cvs_command ("rdiff", patch); } static void @@ -3357,6 +3399,17 @@ server_modtime (finfo, vers_ts) /* See server.h for description. */ +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +/* Need to prototype because mode_t might be smaller than int. */ +void +server_updated ( + struct file_info *finfo, + Vers_TS *vers, + enum server_updated_arg4 updated, + mode_t mode, + unsigned char *checksum, + struct buffer *filebuf) +#else void server_updated (finfo, vers, updated, mode, checksum, filebuf) struct file_info *finfo; @@ -3365,6 +3418,7 @@ server_updated (finfo, vers, updated, mode, checksum, filebuf) mode_t mode; unsigned char *checksum; struct buffer *filebuf; +#endif { if (noexec) { @@ -3583,7 +3637,7 @@ CVS server internal error: unhandled case in server_updated"); if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED || updated == SERVER_RCS_DIFF) - && filebuf != NULL + && filebuf == NULL /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) @@ -4788,25 +4842,23 @@ check_password (username, password, repository) /* No cvs password found, so try /etc/passwd. */ const char *found_passwd = NULL; + struct passwd *pw; #ifdef HAVE_GETSPNAM - struct spwd *pw; + struct spwd *spw; - pw = getspnam (username); - if (pw != NULL) + spw = getspnam (username); + if (spw != NULL) { - found_passwd = pw->sp_pwdp; + found_passwd = spw->sp_pwdp; } -#else - struct passwd *pw; +#endif - pw = getpwnam (username); - if (pw != NULL) + if (found_passwd == NULL && (pw = getpwnam (username)) != NULL) { found_passwd = pw->pw_passwd; } -#endif - if (pw == NULL) + if (found_passwd == NULL) { printf ("E Fatal error, aborting.\n\ error 0 %s: no such user\n", username); @@ -4824,8 +4876,9 @@ error 0 %s: no such user\n", username); exit (EXIT_FAILURE); } - if (found_passwd && *found_passwd) + if (*found_passwd) { + /* user exists and has a password */ host_user = ((! strcmp (found_passwd, crypt (password, found_passwd))) ? username : NULL); @@ -4833,11 +4886,14 @@ error 0 %s: no such user\n", username); } else if (password && *password) { + /* user exists and has no system password, but we got + one as parameter */ host_user = username; goto handle_return; } else { + /* user exists but has no password at all */ host_user = NULL; goto handle_return; } @@ -5195,7 +5251,7 @@ gserver_authenticate_connection () tok_in.value = buf; tok_in.length = strlen (buf); - if (gss_import_name (&stat_min, &tok_in, gss_nt_service_name, + if (gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE, &server_name) != GSS_S_COMPLETE) error (1, 0, "could not import GSSAPI service name %s", buf); @@ -5596,7 +5652,7 @@ cvs_output_binary (str, len) if (error_use_protocol) buf = buf_to_net; - else if (server_active) + else buf = protocol; if (!supported_response ("Mbinary")) diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index a329d49..dd64ab7 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -530,7 +530,6 @@ get_linkinfo_proc (callerdat, finfo) hlinfo->status = (Ctype) 0; /* is this dumb? */ hlinfo->checked_out = 0; - hlinfo->links = NULL; linkp->data = (char *) hlinfo; @@ -1639,8 +1638,6 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) { char *diff_options; - /* FIXME: It might be better to come up with a diff library - which can be shared with the diffutils. */ /* If the client does not support the Rcs-diff command, we send a context diff, and the client must invoke patch. That approach was problematical for various reasons. The @@ -1649,8 +1646,10 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) program. */ if (! rcs_diff_patches) { - /* We use -c, not -u, because we have no way of knowing - which DIFF is in use. */ + /* We use -c, not -u, because that is what CVS has + traditionally used. Kind of a moot point, now that + Rcs-diff is preferred, so there is no point in making + the compatibility issues worse. */ diff_options = "-c"; } else @@ -1922,14 +1921,21 @@ merge_file (finfo, vers) if (strcmp (vers->options, "-V4") == 0) vers->options[0] = '\0'; - (void) time (&last_register_time); + + /* This file is the result of a merge, which means that it has + been modified. We use a special timestamp string which will + not compare equal to any actual timestamp. */ { char *cp = 0; if (status) + { + (void) time (&last_register_time); cp = time_stamp (finfo->file); - Register (finfo->entries, finfo->file, vers->vn_rcs, vers->ts_rcs, vers->options, - vers->tag, vers->date, cp); + } + Register (finfo->entries, finfo->file, vers->vn_rcs, + "Result of merge", vers->options, vers->tag, + vers->date, cp); if (cp) free (cp); } @@ -2428,25 +2434,27 @@ join_file (finfo, vers) free (rev1); free (rev2); -#ifdef SERVER_SUPPORT - /* - * If we're in server mode, then we need to re-register the file - * even if there were no conflicts (status == 0). - * This tells server_updated() to send the modified file back to - * the client. - */ - if (status == 1 || (status == 0 && server_active)) -#else - if (status == 1) -#endif + /* The file has changed, but if we just checked it out it may + still have the same timestamp it did when it was first + registered above in checkout_file. We register it again with a + dummy timestamp to make sure that later runs of CVS will + recognize that it has changed. + + We don't actually need to register again if we called + RCS_checkout above, and we aren't running as the server. + However, that is not the normal case, and calling Register + again won't cost much in that case. */ { char *cp = 0; if (status) + { + (void) time (&last_register_time); cp = time_stamp (finfo->file); - Register (finfo->entries, finfo->file, - vers->vn_rcs, vers->ts_rcs, vers->options, - vers->tag, vers->date, cp); + } + Register (finfo->entries, finfo->file, vers->vn_rcs, + "Result of merge", vers->options, vers->tag, + vers->date, cp); if (cp) free(cp); } @@ -2494,8 +2502,8 @@ special_file_mismatch (finfo, rev1, rev2) dev_t rev1_dev, rev2_dev; char *rev1_symlink = NULL; char *rev2_symlink = NULL; - char *rev1_hardlinks = NULL; - char *rev2_hardlinks = NULL; + List *rev1_hardlinks; + List *rev2_hardlinks; int check_uids, check_gids, check_modes; int result; @@ -2533,7 +2541,7 @@ special_file_mismatch (finfo, rev1, rev2) if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode)) rev1_dev = sb.st_rdev; } - rev1_hardlinks = list_files_linked_to (finfo->file); + rev1_hardlinks = list_linked_files_on_disk (finfo->file); } else { @@ -2584,11 +2592,9 @@ special_file_mismatch (finfo, rev1, rev2) finfo->file, rev1, ftype); } - n = findnode (vp->other_delta, "hardlinks"); - if (n == NULL) - rev1_hardlinks = xstrdup (""); - else - rev1_hardlinks = xstrdup (n->data); + rev1_hardlinks = vp->hardlinks; + if (rev1_hardlinks == NULL) + rev1_hardlinks = getlist(); } } @@ -2608,7 +2614,7 @@ special_file_mismatch (finfo, rev1, rev2) if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode)) rev2_dev = sb.st_rdev; } - rev2_hardlinks = list_files_linked_to (finfo->file); + rev2_hardlinks = list_linked_files_on_disk (finfo->file); } else { @@ -2659,11 +2665,9 @@ special_file_mismatch (finfo, rev1, rev2) finfo->file, rev2, ftype); } - n = findnode (vp->other_delta, "hardlinks"); - if (n == NULL) - rev2_hardlinks = xstrdup (""); - else - rev2_hardlinks = xstrdup (n->data); + rev2_hardlinks = vp->hardlinks; + if (rev2_hardlinks == NULL) + rev2_hardlinks = getlist(); } } @@ -2744,7 +2748,7 @@ special_file_mismatch (finfo, rev1, rev2) } /* Compare hard links. */ - if (strcmp (rev1_hardlinks, rev2_hardlinks) != 0) + if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0) { error (0, 0, "%s: hard linkage of %s and %s do not match", finfo->file, @@ -2759,9 +2763,9 @@ special_file_mismatch (finfo, rev1, rev2) if (rev2_symlink != NULL) free (rev2_symlink); if (rev1_hardlinks != NULL) - free (rev1_hardlinks); + dellist (&rev1_hardlinks); if (rev2_hardlinks != NULL) - free (rev2_hardlinks); + dellist (&rev2_hardlinks); return result; #else |