xref: /dragonfly/contrib/cvs-1.12/src/commit.c (revision f02303f9)
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  *
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  *
13  * Commit Files
14  *
15  * "commit" commits the present version to the RCS repository, AFTER
16  * having done a test on conflicts.
17  *
18  * The call is: cvs commit [options] files...
19  *
20  */
21 
22 #include "cvs.h"
23 #include "getline.h"
24 #include "edit.h"
25 #include "fileattr.h"
26 #include "hardlink.h"
27 
28 static Dtype check_direntproc (void *callerdat, const char *dir,
29                                const char *repos, const char *update_dir,
30                                List *entries);
31 static int check_fileproc (void *callerdat, struct file_info *finfo);
32 static int check_filesdoneproc (void *callerdat, int err, const char *repos,
33 				const char *update_dir, List *entries);
34 static int checkaddfile (const char *file, const char *repository,
35                          const char *tag, const char *options,
36                          RCSNode **rcsnode);
37 static Dtype commit_direntproc (void *callerdat, const char *dir,
38                                 const char *repos, const char *update_dir,
39                                 List *entries);
40 static int commit_dirleaveproc (void *callerdat, const char *dir, int err,
41 				const char *update_dir, List *entries);
42 static int commit_fileproc (void *callerdat, struct file_info *finfo);
43 static int commit_filesdoneproc (void *callerdat, int err,
44                                  const char *repository,
45 				 const char *update_dir, List *entries);
46 static int finaladd (struct file_info *finfo, char *revision, char *tag,
47 		     char *options);
48 static int findmaxrev (Node * p, void *closure);
49 static int lock_RCS (const char *user, RCSNode *rcs, const char *rev,
50                      const char *repository);
51 static int precommit_list_to_args_proc (Node * p, void *closure);
52 static int precommit_proc (const char *repository, const char *filter,
53                            void *closure);
54 static int remove_file (struct file_info *finfo, char *tag,
55 			char *message);
56 static void fixaddfile (const char *rcs);
57 static void fixbranch (RCSNode *, char *branch);
58 static void unlockrcs (RCSNode *rcs);
59 static void ci_delproc (Node *p);
60 static void masterlist_delproc (Node *p);
61 
62 struct commit_info
63 {
64     Ctype status;			/* as returned from Classify_File() */
65     char *rev;				/* a numeric rev, if we know it */
66     char *tag;				/* any sticky tag, or -r option */
67     char *options;			/* Any sticky -k option */
68 };
69 struct master_lists
70 {
71     List *ulist;			/* list for Update_Logfile */
72     List *cilist;			/* list with commit_info structs */
73 };
74 
75 static int check_valid_edit = 0;
76 static int force_ci = 0;
77 static int got_message;
78 static int aflag;
79 static char *saved_tag;
80 static char *write_dirtag;
81 static int write_dirnonbranch;
82 static char *logfile;
83 static List *mulist;
84 static char *saved_message;
85 static time_t last_register_time;
86 
87 static const char *const commit_usage[] =
88 {
89     "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n",
90     "    -c          Check for valid edits before committing.\n",
91     "    -R          Process directories recursively.\n",
92     "    -l          Local directory only (not recursive).\n",
93     "    -f          Force the file to be committed; disables recursion.\n",
94     "    -F logfile  Read the log message from file.\n",
95     "    -m msg      Log message.\n",
96     "    -r rev      Commit to this branch or trunk revision.\n",
97     "(Specify the --help global option for a list of other help options)\n",
98     NULL
99 };
100 
101 #ifdef CLIENT_SUPPORT
102 /* Identify a file which needs "? foo" or a Questionable request.  */
103 struct question
104 {
105     /* The two fields for the Directory request.  */
106     char *dir;
107     char *repos;
108 
109     /* The file name.  */
110     char *file;
111 
112     struct question *next;
113 };
114 
115 struct find_data
116 {
117     List *ulist;
118     int argc;
119     char **argv;
120 
121     /* This is used from dirent to filesdone time, for each directory,
122        to make a list of files we have already seen.  */
123     List *ignlist;
124 
125     /* Linked list of files which need "? foo" or a Questionable request.  */
126     struct question *questionables;
127 
128     /* Only good within functions called from the filesdoneproc.  Stores
129        the repository (pointer into storage managed by the recursion
130        processor.  */
131     const char *repository;
132 
133     /* Non-zero if we should force the commit.  This is enabled by
134        either -f or -r options, unlike force_ci which is just -f.  */
135     int force;
136 };
137 
138 
139 
140 static Dtype
141 find_dirent_proc (void *callerdat, const char *dir, const char *repository,
142                   const char *update_dir, List *entries)
143 {
144     struct find_data *find_data = callerdat;
145 
146     /* This check seems to slowly be creeping throughout CVS (update
147        and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995.  My guess
148        is that it (or some variant thereof) should go in all the
149        dirent procs.  Unless someone has some better idea...  */
150     if (!isdir (dir))
151 	return R_SKIP_ALL;
152 
153     /* initialize the ignore list for this directory */
154     find_data->ignlist = getlist ();
155 
156     /* Print the same warm fuzzy as in check_direntproc, since that
157        code will never be run during client/server operation and we
158        want the messages to match. */
159     if (!quiet)
160 	error (0, 0, "Examining %s", update_dir);
161 
162     return R_PROCESS;
163 }
164 
165 
166 
167 /* Here as a static until we get around to fixing ignore_files to pass
168    it along as an argument.  */
169 static struct find_data *find_data_static;
170 
171 
172 
173 static void
174 find_ignproc (const char *file, const char *dir)
175 {
176     struct question *p;
177 
178     p = xmalloc (sizeof (struct question));
179     p->dir = xstrdup (dir);
180     p->repos = xstrdup (find_data_static->repository);
181     p->file = xstrdup (file);
182     p->next = find_data_static->questionables;
183     find_data_static->questionables = p;
184 }
185 
186 
187 
188 static int
189 find_filesdoneproc (void *callerdat, int err, const char *repository,
190                     const char *update_dir, List *entries)
191 {
192     struct find_data *find_data = callerdat;
193     find_data->repository = repository;
194 
195     /* if this directory has an ignore list, process it then free it */
196     if (find_data->ignlist)
197     {
198 	find_data_static = find_data;
199 	ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
200 	dellist (&find_data->ignlist);
201     }
202 
203     find_data->repository = NULL;
204 
205     return err;
206 }
207 
208 
209 
210 /* Machinery to find out what is modified, added, and removed.  It is
211    possible this should be broken out into a new client_classify function;
212    merging it with classify_file is almost sure to be a mess, though,
213    because classify_file has all kinds of repository processing.  */
214 static int
215 find_fileproc (void *callerdat, struct file_info *finfo)
216 {
217     Vers_TS *vers;
218     enum classify_type status;
219     Node *node;
220     struct find_data *args = callerdat;
221     struct logfile_info *data;
222     struct file_info xfinfo;
223 
224     /* if this directory has an ignore list, add this file to it */
225     if (args->ignlist)
226     {
227 	Node *p;
228 
229 	p = getnode ();
230 	p->type = FILES;
231 	p->key = xstrdup (finfo->file);
232 	if (addnode (args->ignlist, p) != 0)
233 	    freenode (p);
234     }
235 
236     xfinfo = *finfo;
237     xfinfo.repository = NULL;
238     xfinfo.rcs = NULL;
239 
240     vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0);
241     if (vers->vn_user == NULL)
242     {
243 	if (vers->ts_user == NULL)
244 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
245 	else
246 	    error (0, 0, "use `%s add' to create an entry for `%s'",
247 		   program_name, finfo->fullname);
248 	freevers_ts (&vers);
249 	return 1;
250     }
251     if (vers->vn_user[0] == '-')
252     {
253 	if (vers->ts_user != NULL)
254 	{
255 	    error (0, 0,
256 		   "`%s' should be removed and is still there (or is back"
257 		   " again)", finfo->fullname);
258 	    freevers_ts (&vers);
259 	    return 1;
260 	}
261 	/* else */
262 	status = T_REMOVED;
263     }
264     else if (strcmp (vers->vn_user, "0") == 0)
265     {
266 	if (vers->ts_user == NULL)
267 	{
268 	    /* This happens when one has `cvs add'ed a file, but it no
269 	       longer exists in the working directory at commit time.
270 	       FIXME: What classify_file does in this case is print
271 	       "new-born %s has disappeared" and removes the entry.
272 	       We probably should do the same.  */
273 	    if (!really_quiet)
274 		error (0, 0, "warning: new-born %s has disappeared",
275 		       finfo->fullname);
276 	    status = T_REMOVE_ENTRY;
277 	}
278 	else
279 	    status = T_ADDED;
280     }
281     else if (vers->ts_user == NULL)
282     {
283 	/* FIXME: What classify_file does in this case is print
284 	   "%s was lost".  We probably should do the same.  */
285 	freevers_ts (&vers);
286 	return 0;
287     }
288     else if (vers->ts_rcs != NULL
289 	     && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0))
290 	/* If we are forcing commits, pretend that the file is
291            modified.  */
292 	status = T_MODIFIED;
293     else
294     {
295 	/* This covers unmodified files, as well as a variety of other
296 	   cases.  FIXME: we probably should be printing a message and
297 	   returning 1 for many of those cases (but I'm not sure
298 	   exactly which ones).  */
299 	freevers_ts (&vers);
300 	return 0;
301     }
302 
303     node = getnode ();
304     node->key = xstrdup (finfo->fullname);
305 
306     data = xmalloc (sizeof (struct logfile_info));
307     data->type = status;
308     data->tag = xstrdup (vers->tag);
309     data->rev_old = data->rev_new = NULL;
310 
311     node->type = UPDATE;
312     node->delproc = update_delproc;
313     node->data = data;
314     (void)addnode (args->ulist, node);
315 
316     ++args->argc;
317 
318     freevers_ts (&vers);
319     return 0;
320 }
321 
322 
323 
324 static int
325 copy_ulist (Node *node, void *data)
326 {
327     struct find_data *args = data;
328     args->argv[args->argc++] = node->key;
329     return 0;
330 }
331 #endif /* CLIENT_SUPPORT */
332 
333 
334 
335 #ifdef SERVER_SUPPORT
336 # define COMMIT_OPTIONS "+cnlRm:fF:r:"
337 #else /* !SERVER_SUPPORT */
338 # define COMMIT_OPTIONS "+clRm:fF:r:"
339 #endif /* SERVER_SUPPORT */
340 int
341 commit (int argc, char **argv)
342 {
343     int c;
344     int err = 0;
345     int local = 0;
346 
347     if (argc == -1)
348 	usage (commit_usage);
349 
350 #ifdef CVS_BADROOT
351     /*
352      * For log purposes, do not allow "root" to commit files.  If you look
353      * like root, but are really logged in as a non-root user, it's OK.
354      */
355     /* FIXME: Shouldn't this check be much more closely related to the
356        readonly user stuff (CVSROOT/readers, &c).  That is, why should
357        root be able to "cvs init", "cvs import", &c, but not "cvs ci"?  */
358     /* Who we are on the client side doesn't affect logging.  */
359     if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote)
360     {
361 	struct passwd *pw;
362 
363 	if ((pw = getpwnam (getcaller ())) == NULL)
364 	    error (1, 0,
365                    "your apparent username (%s) is unknown to this system",
366                    getcaller ());
367 	if (pw->pw_uid == (uid_t) 0)
368 	    error (1, 0, "'root' is not allowed to commit files");
369     }
370 #endif /* CVS_BADROOT */
371 
372     optind = 0;
373     while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1)
374     {
375 	switch (c)
376 	{
377             case 'c':
378                 check_valid_edit = 1;
379                 break;
380 #ifdef SERVER_SUPPORT
381 	    case 'n':
382 		/* Silently ignore -n for compatibility with old
383 		 * clients.
384 		 */
385 		break;
386 #endif /* SERVER_SUPPORT */
387 	    case 'm':
388 #ifdef FORCE_USE_EDITOR
389 		use_editor = 1;
390 #else
391 		use_editor = 0;
392 #endif
393 		if (saved_message)
394 		{
395 		    free (saved_message);
396 		    saved_message = NULL;
397 		}
398 
399 		saved_message = xstrdup (optarg);
400 		break;
401 	    case 'r':
402 		if (saved_tag)
403 		    free (saved_tag);
404 		saved_tag = xstrdup (optarg);
405 		break;
406 	    case 'l':
407 		local = 1;
408 		break;
409 	    case 'R':
410 		local = 0;
411 		break;
412 	    case 'f':
413 		force_ci = 1;
414                 check_valid_edit = 0;
415 		local = 1;		/* also disable recursion */
416 		break;
417 	    case 'F':
418 #ifdef FORCE_USE_EDITOR
419 		use_editor = 1;
420 #else
421 		use_editor = 0;
422 #endif
423 		logfile = optarg;
424 		break;
425 	    case '?':
426 	    default:
427 		usage (commit_usage);
428 		break;
429 	}
430     }
431     argc -= optind;
432     argv += optind;
433 
434     /* numeric specified revision means we ignore sticky tags... */
435     if (saved_tag && isdigit ((unsigned char) *saved_tag))
436     {
437 	char *p = saved_tag + strlen (saved_tag);
438 	aflag = 1;
439 	/* strip trailing dots and leading zeros */
440 	while (*--p == '.') ;
441 	p[1] = '\0';
442 	while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1]))
443 	    ++saved_tag;
444     }
445 
446     /* some checks related to the "-F logfile" option */
447     if (logfile)
448     {
449 	size_t size = 0, len;
450 
451 	if (saved_message)
452 	    error (1, 0, "cannot specify both a message and a log file");
453 
454 	get_file (logfile, logfile, "r", &saved_message, &size, &len);
455     }
456 
457 #ifdef CLIENT_SUPPORT
458     if (current_parsed_root->isremote)
459     {
460 	struct find_data find_args;
461 
462 	ign_setup ();
463 
464 	find_args.ulist = getlist ();
465 	find_args.argc = 0;
466 	find_args.questionables = NULL;
467 	find_args.ignlist = NULL;
468 	find_args.repository = NULL;
469 
470 	/* It is possible that only a numeric tag should set this.
471 	   I haven't really thought about it much.
472 	   Anyway, I suspect that setting it unnecessarily only causes
473 	   a little unneeded network traffic.  */
474 	find_args.force = force_ci || saved_tag != NULL;
475 
476 	err = start_recursion
477 	    (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL,
478 	     &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
479 	     NULL, 0, NULL );
480 	if (err)
481 	    error (1, 0, "correct above errors first!");
482 
483 	if (find_args.argc == 0)
484 	{
485 	    /* Nothing to commit.  Exit now without contacting the
486 	       server (note that this means that we won't print "?
487 	       foo" for files which merit it, because we don't know
488 	       what is in the CVSROOT/cvsignore file).  */
489 	    dellist (&find_args.ulist);
490 	    return 0;
491 	}
492 
493 	/* Now we keep track of which files we actually are going to
494 	   operate on, and only work with those files in the future.
495 	   This saves time--we don't want to search the file system
496 	   of the working directory twice.  */
497 	if (size_overflow_p (xtimes (find_args.argc, sizeof (char **))))
498 	{
499 	    find_args.argc = 0;
500 	    return 0;
501 	}
502 	find_args.argv = xnmalloc (find_args.argc, sizeof (char **));
503 	find_args.argc = 0;
504 	walklist (find_args.ulist, copy_ulist, &find_args);
505 
506 	/* Do this before calling do_editor; don't ask for a log
507 	   message if we can't talk to the server.  But do it after we
508 	   have made the checks that we can locally (to more quickly
509 	   catch syntax errors, the case where no files are modified,
510 	   added or removed, etc.).
511 
512 	   On the other hand, calling start_server before do_editor
513 	   means that we chew up server resources the whole time that
514 	   the user has the editor open (hours or days if the user
515 	   forgets about it), which seems dubious.  */
516 	start_server ();
517 
518 	/*
519 	 * We do this once, not once for each directory as in normal CVS.
520 	 * The protocol is designed this way.  This is a feature.
521 	 */
522 	if (use_editor)
523 	    do_editor (".", &saved_message, NULL, find_args.ulist);
524 
525 	/* We always send some sort of message, even if empty.  */
526 	option_with_arg ("-m", saved_message ? saved_message : "");
527 
528 	/* OK, now process all the questionable files we have been saving
529 	   up.  */
530 	{
531 	    struct question *p;
532 	    struct question *q;
533 
534 	    p = find_args.questionables;
535 	    while (p != NULL)
536 	    {
537 		if (ign_inhibit_server || !supported_request ("Questionable"))
538 		{
539 		    cvs_output ("? ", 2);
540 		    if (p->dir[0] != '\0')
541 		    {
542 			cvs_output (p->dir, 0);
543 			cvs_output ("/", 1);
544 		    }
545 		    cvs_output (p->file, 0);
546 		    cvs_output ("\n", 1);
547 		}
548 		else
549 		{
550 		    /* This used to send the Directory line of its own accord,
551 		     * but skipped some of the other processing like checking
552 		     * for whether the server would accept "Relative-directory"
553 		     * requests.  Relying on send_a_repository() to do this
554 		     * picks up these checks but also:
555 		     *
556 		     *   1. Causes the "Directory" request to be sent only once
557 		     *      per directory.
558 		     *   2. Causes the global TOPLEVEL_REPOS to be set.
559 		     *   3. Causes "Static-directory" and "Sticky" requests
560 		     *      to sometimes be sent.
561 		     *
562 		     * (1) is almost certainly a plus.  (2) & (3) may or may
563 		     * not be useful sometimes, and will ocassionally cause a
564 		     * little extra network traffic.  The additional network
565 		     * traffic is probably already saved several times over and
566 		     * certainly cancelled out via the multiple "Directory"
567 		     * request suppression of (1).
568 		     */
569 		    send_a_repository (p->dir, p->repos, p->dir);
570 
571 		    send_to_server ("Questionable ", 0);
572 		    send_to_server (p->file, 0);
573 		    send_to_server ("\012", 1);
574 		}
575 		free (p->dir);
576 		free (p->repos);
577 		free (p->file);
578 		q = p->next;
579 		free (p);
580 		p = q;
581 	    }
582 	}
583 
584 	if (local)
585 	    send_arg ("-l");
586         if (check_valid_edit)
587             send_arg ("-c");
588 	if (force_ci)
589 	    send_arg ("-f");
590 	option_with_arg ("-r", saved_tag);
591 	send_arg ("--");
592 
593 	/* FIXME: This whole find_args.force/SEND_FORCE business is a
594 	   kludge.  It would seem to be a server bug that we have to
595 	   say that files are modified when they are not.  This makes
596 	   "cvs commit -r 2" across a whole bunch of files a very slow
597 	   operation (and it isn't documented in cvsclient.texi).  I
598 	   haven't looked at the server code carefully enough to be
599 	   _sure_ why this is needed, but if it is because the "ci"
600 	   program, which we used to call, wanted the file to exist,
601 	   then it would be relatively simple to fix in the server.  */
602 	send_files (find_args.argc, find_args.argv, local, 0,
603 		    find_args.force ? SEND_FORCE : 0);
604 
605 	/* Sending only the names of the files which were modified, added,
606 	   or removed means that the server will only do an up-to-date
607 	   check on those files.  This is different from local CVS and
608 	   previous versions of client/server CVS, but it probably is a Good
609 	   Thing, or at least Not Such A Bad Thing.  */
610 	send_file_names (find_args.argc, find_args.argv, 0);
611 	free (find_args.argv);
612 	dellist (&find_args.ulist);
613 
614 	send_to_server ("ci\012", 0);
615 	err = get_responses_and_close ();
616 	if (err != 0 && use_editor && saved_message != NULL)
617 	{
618 	    /* If there was an error, don't nuke the user's carefully
619 	       constructed prose.  This is something of a kludge; a better
620 	       solution is probably more along the lines of #150 in TODO
621 	       (doing a second up-to-date check before accepting the
622 	       log message has also been suggested, but that seems kind of
623 	       iffy because the real up-to-date check could still fail,
624 	       another error could occur, &c.  Also, a second check would
625 	       slow things down).  */
626 
627 	    char *fname;
628 	    FILE *fp;
629 
630 	    fp = cvs_temp_file (&fname);
631 	    if (fp == NULL)
632 		error (1, 0, "cannot create temporary file %s", fname);
633 	    if (fwrite (saved_message, 1, strlen (saved_message), fp)
634 		!= strlen (saved_message))
635 		error (1, errno, "cannot write temporary file %s", fname);
636 	    if (fclose (fp) < 0)
637 		error (0, errno, "cannot close temporary file %s", fname);
638 	    error (0, 0, "saving log message in %s", fname);
639 	    free (fname);
640 	}
641 	return err;
642     }
643 #endif
644 
645     if (saved_tag != NULL)
646 	tag_check_valid (saved_tag, argc, argv, local, aflag, "", false);
647 
648     /* XXX - this is not the perfect check for this */
649     if (argc <= 0)
650 	write_dirtag = saved_tag;
651 
652     wrap_setup ();
653 
654     lock_tree_promotably (argc, argv, local, W_LOCAL, aflag);
655 
656     /*
657      * Set up the master update list and hard link list
658      */
659     mulist = getlist ();
660 
661 #ifdef PRESERVE_PERMISSIONS_SUPPORT
662     if (preserve_perms)
663     {
664 	hardlist = getlist ();
665 
666 	/*
667 	 * We need to save the working directory so that
668 	 * check_fileproc can construct a full pathname for each file.
669 	 */
670 	working_dir = xgetcwd ();
671     }
672 #endif
673 
674     /*
675      * Run the recursion processor to verify the files are all up-to-date
676      */
677     err = start_recursion (check_fileproc, check_filesdoneproc,
678                            check_direntproc, NULL, NULL, argc, argv, local,
679                            W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL);
680     if (err)
681 	error (1, 0, "correct above errors first!");
682 
683     /*
684      * Run the recursion processor to commit the files
685      */
686     write_dirnonbranch = 0;
687     if (noexec == 0)
688 	err = start_recursion (commit_fileproc, commit_filesdoneproc,
689                                commit_direntproc, commit_dirleaveproc, NULL,
690                                argc, argv, local, W_LOCAL, aflag,
691                                CVS_LOCK_WRITE, NULL, 1, NULL);
692 
693     /*
694      * Unlock all the dirs and clean up
695      */
696     Lock_Cleanup ();
697     dellist (&mulist);
698 
699     /* see if we need to sleep before returning to avoid time-stamp races */
700     if (!server_active && last_register_time)
701     {
702 	sleep_past (last_register_time);
703     }
704 
705     return err;
706 }
707 
708 
709 
710 /* This routine determines the status of a given file and retrieves
711    the version information that is associated with that file. */
712 
713 static
714 Ctype
715 classify_file_internal (struct file_info *finfo, Vers_TS **vers)
716 {
717     int save_noexec, save_quiet, save_really_quiet;
718     Ctype status;
719 
720     /* FIXME: Do we need to save quiet as well as really_quiet?  Last
721        time I glanced at Classify_File I only saw it looking at really_quiet
722        not quiet.  */
723     save_noexec = noexec;
724     save_quiet = quiet;
725     save_really_quiet = really_quiet;
726     noexec = quiet = really_quiet = 1;
727 
728     /* handle specified numeric revision specially */
729     if (saved_tag && isdigit ((unsigned char) *saved_tag))
730     {
731 	/* If the tag is for the trunk, make sure we're at the head */
732 	if (numdots (saved_tag) < 2)
733 	{
734 	    status = Classify_File (finfo, NULL, NULL,
735 				    NULL, 1, aflag, vers, 0);
736 	    if (status == T_UPTODATE || status == T_MODIFIED ||
737 		status == T_ADDED)
738 	    {
739 		Ctype xstatus;
740 
741 		freevers_ts (vers);
742 		xstatus = Classify_File (finfo, saved_tag, NULL,
743 					 NULL, 1, aflag, vers, 0);
744 		if (xstatus == T_REMOVE_ENTRY)
745 		    status = T_MODIFIED;
746 		else if (status == T_MODIFIED && xstatus == T_CONFLICT)
747 		    status = T_MODIFIED;
748 		else
749 		    status = xstatus;
750 	    }
751 	}
752 	else
753 	{
754 	    char *xtag, *cp;
755 
756 	    /*
757 	     * The revision is off the main trunk; make sure we're
758 	     * up-to-date with the head of the specified branch.
759 	     */
760 	    xtag = xstrdup (saved_tag);
761 	    if ((numdots (xtag) & 1) != 0)
762 	    {
763 		cp = strrchr (xtag, '.');
764 		*cp = '\0';
765 	    }
766 	    status = Classify_File (finfo, xtag, NULL,
767 				    NULL, 1, aflag, vers, 0);
768 	    if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
769 		&& (cp = strrchr (xtag, '.')) != NULL)
770 	    {
771 		/* pluck one more dot off the revision */
772 		*cp = '\0';
773 		freevers_ts (vers);
774 		status = Classify_File (finfo, xtag, NULL,
775 					NULL, 1, aflag, vers, 0);
776 		if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
777 		    status = T_MODIFIED;
778 	    }
779 	    /* now, muck with vers to make the tag correct */
780 	    free ((*vers)->tag);
781 	    (*vers)->tag = xstrdup (saved_tag);
782 	    free (xtag);
783 	}
784     }
785     else
786 	status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0);
787     noexec = save_noexec;
788     quiet = save_quiet;
789     really_quiet = save_really_quiet;
790 
791     return status;
792 }
793 
794 
795 
796 /*
797  * Check to see if a file is ok to commit and make sure all files are
798  * up-to-date
799  */
800 /* ARGSUSED */
801 static int
802 check_fileproc (void *callerdat, struct file_info *finfo)
803 {
804     Ctype status;
805     const char *xdir;
806     Node *p;
807     List *ulist, *cilist;
808     Vers_TS *vers;
809     struct commit_info *ci;
810     struct logfile_info *li;
811     int retval = 1;
812 
813     size_t cvsroot_len = strlen (current_parsed_root->directory);
814 
815     if (!finfo->repository)
816     {
817 	error (0, 0, "nothing known about `%s'", finfo->fullname);
818 	return 1;
819     }
820 
821     if (strncmp (finfo->repository, current_parsed_root->directory,
822                  cvsroot_len) == 0
823 	&& ISSLASH (finfo->repository[cvsroot_len])
824 	&& strncmp (finfo->repository + cvsroot_len + 1,
825 		    CVSROOTADM,
826 		    sizeof (CVSROOTADM) - 1) == 0
827 	&& ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
828 	&& strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
829 		   CVSNULLREPOS) == 0
830 	)
831 	error (1, 0, "cannot check in to %s", finfo->repository);
832 
833     status = classify_file_internal (finfo, &vers);
834 
835     /*
836      * If the force-commit option is enabled, and the file in question
837      * appears to be up-to-date, just make it look modified so that
838      * it will be committed.
839      */
840     if (force_ci && status == T_UPTODATE)
841 	status = T_MODIFIED;
842 
843     switch (status)
844     {
845 	case T_CHECKOUT:
846 	case T_PATCH:
847 	case T_NEEDS_MERGE:
848 	case T_REMOVE_ENTRY:
849 	    error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
850 	    goto out;
851 	case T_CONFLICT:
852 	case T_MODIFIED:
853 	case T_ADDED:
854 	case T_REMOVED:
855         {
856             char *editor;
857 
858 	    /*
859 	     * some quick sanity checks; if no numeric -r option specified:
860 	     *	- can't have a sticky date
861 	     *	- can't have a sticky tag that is not a branch
862 	     * Also,
863 	     *	- if status is T_REMOVED, file must not exist and its entry
864 	     *	  can't have a numeric sticky tag.
865 	     *	- if status is T_ADDED, rcs file must not exist unless on
866 	     *    a branch or head is dead
867 	     *	- if status is T_ADDED, can't have a non-trunk numeric rev
868 	     *	- if status is T_MODIFIED and a Conflict marker exists, don't
869 	     *    allow the commit if timestamp is identical or if we find
870 	     *    an RCS_MERGE_PAT in the file.
871 	     */
872 	    if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
873 	    {
874 		if (vers->date)
875 		{
876 		    error (0, 0,
877 			   "cannot commit with sticky date for file `%s'",
878 			   finfo->fullname);
879 		    goto out;
880 		}
881 		if (status == T_MODIFIED && vers->tag &&
882 		    !RCS_isbranch (finfo->rcs, vers->tag))
883 		{
884 		    error (0, 0,
885 			   "sticky tag `%s' for file `%s' is not a branch",
886 			   vers->tag, finfo->fullname);
887 		    goto out;
888 		}
889 	    }
890 	    if (status == T_CONFLICT && !force_ci)
891 	    {
892 		error (0, 0,
893 		      "file `%s' had a conflict and has not been modified",
894 		       finfo->fullname);
895 		goto out;
896 	    }
897 	    if (status == T_MODIFIED && !force_ci && file_has_markers (finfo))
898 	    {
899 		/* Make this a warning, not an error, because we have
900 		   no way of knowing whether the "conflict indicators"
901 		   are really from a conflict or whether they are part
902 		   of the document itself (cvs.texinfo and sanity.sh in
903 		   CVS itself, for example, tend to want to have strings
904 		   like ">>>>>>>" at the start of a line).  Making people
905 		   kludge this the way they need to kludge keyword
906 		   expansion seems undesirable.  And it is worse than
907 		   keyword expansion, because there is no -ko
908 		   analogue.  */
909 		error (0, 0,
910 		       "\
911 warning: file `%s' seems to still contain conflict indicators",
912 		       finfo->fullname);
913 	    }
914 
915 	    if (status == T_REMOVED)
916 	    {
917 		if (vers->ts_user != NULL)
918 		{
919 		    error (0, 0,
920 			   "`%s' should be removed and is still there (or is"
921 			   " back again)", finfo->fullname);
922 		    goto out;
923 		}
924 
925 		if (vers->tag && isdigit ((unsigned char) *vers->tag))
926 		{
927 		    /* Remove also tries to forbid this, but we should check
928 		       here.  I'm only _sure_ about somewhat obscure cases
929 		       (hacking the Entries file, using an old version of
930 		       CVS for the remove and a new one for the commit), but
931 		       there might be other cases.  */
932 		    error (0, 0,
933 			   "cannot remove file `%s' which has a numeric sticky"
934 			   " tag of `%s'", finfo->fullname, vers->tag);
935 		    freevers_ts (&vers);
936 		    goto out;
937 		}
938 	    }
939 	    if (status == T_ADDED)
940 	    {
941 	        if (vers->tag == NULL)
942 		{
943 		    if (finfo->rcs != NULL &&
944 			!RCS_isdead (finfo->rcs, finfo->rcs->head))
945 		    {
946 			error (0, 0,
947 		    "cannot add file `%s' when RCS file `%s' already exists",
948 			       finfo->fullname, finfo->rcs->path);
949 			goto out;
950 		    }
951 		}
952 		else if (isdigit ((unsigned char) *vers->tag) &&
953 		    numdots (vers->tag) > 1)
954 		{
955 		    error (0, 0,
956 		"cannot add file `%s' with revision `%s'; must be on trunk",
957 			       finfo->fullname, vers->tag);
958 		    goto out;
959 		}
960 	    }
961 
962 	    /* done with consistency checks; now, to get on with the commit */
963 	    if (finfo->update_dir[0] == '\0')
964 		xdir = ".";
965 	    else
966 		xdir = finfo->update_dir;
967 	    if ((p = findnode (mulist, xdir)) != NULL)
968 	    {
969 		ulist = ((struct master_lists *) p->data)->ulist;
970 		cilist = ((struct master_lists *) p->data)->cilist;
971 	    }
972 	    else
973 	    {
974 		struct master_lists *ml;
975 
976 		ml = xmalloc (sizeof (struct master_lists));
977 		ulist = ml->ulist = getlist ();
978 		cilist = ml->cilist = getlist ();
979 
980 		p = getnode ();
981 		p->key = xstrdup (xdir);
982 		p->type = UPDATE;
983 		p->data = ml;
984 		p->delproc = masterlist_delproc;
985 		(void) addnode (mulist, p);
986 	    }
987 
988 	    /* first do ulist, then cilist */
989 	    p = getnode ();
990 	    p->key = xstrdup (finfo->file);
991 	    p->type = UPDATE;
992 	    p->delproc = update_delproc;
993 	    li = xmalloc (sizeof (struct logfile_info));
994 	    li->type = status;
995 
996 	    if (check_valid_edit)
997             {
998                 char *editors = NULL;
999 
1000 		editor = NULL;
1001                 editors = fileattr_get0 (finfo->file, "_editors");
1002                 if (editors != NULL)
1003                 {
1004                     char *caller = getcaller ();
1005                     char *p = NULL;
1006                     char *p0 = NULL;
1007 
1008                     p = editors;
1009                     p0 = p;
1010                     while (*p != '\0')
1011                     {
1012                         p = strchr (p, '>');
1013                         if (p == NULL)
1014                         {
1015                             break;
1016                         }
1017                         *p = '\0';
1018                         if (strcmp (caller, p0) == 0)
1019                         {
1020                             break;
1021                         }
1022                         p = strchr (p + 1, ',');
1023                         if (p == NULL)
1024                         {
1025                             break;
1026                         }
1027                         ++p;
1028                         p0 = p;
1029                     }
1030 
1031                     if (strcmp (caller, p0) == 0)
1032                     {
1033                         editor = caller;
1034                     }
1035 
1036                     free (editors);
1037                 }
1038             }
1039 
1040             if (check_valid_edit && editor == NULL)
1041             {
1042                 error (0, 0, "Valid edit does not exist for %s",
1043                        finfo->fullname);
1044                 freevers_ts (&vers);
1045                 return 1;
1046             }
1047 
1048 	    li->tag = xstrdup (vers->tag);
1049 	    li->rev_old = xstrdup (vers->vn_rcs);
1050 	    li->rev_new = NULL;
1051 	    p->data = li;
1052 	    (void) addnode (ulist, p);
1053 
1054 	    p = getnode ();
1055 	    p->key = xstrdup (finfo->file);
1056 	    p->type = UPDATE;
1057 	    p->delproc = ci_delproc;
1058 	    ci = xmalloc (sizeof (struct commit_info));
1059 	    ci->status = status;
1060 	    if (vers->tag)
1061 		if (isdigit ((unsigned char) *vers->tag))
1062 		    ci->rev = xstrdup (vers->tag);
1063 		else
1064 		    ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
1065 	    else
1066 		ci->rev = NULL;
1067 	    ci->tag = xstrdup (vers->tag);
1068 	    ci->options = xstrdup (vers->options);
1069 	    p->data = ci;
1070 	    (void) addnode (cilist, p);
1071 
1072 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1073 	    if (preserve_perms)
1074 	    {
1075 		/* Add this file to hardlist, indexed on its inode.  When
1076 		   we are done, we can find out what files are hardlinked
1077 		   to a given file by looking up its inode in hardlist. */
1078 		char *fullpath;
1079 		Node *linkp;
1080 		struct hardlink_info *hlinfo;
1081 
1082 		/* Get the full pathname of the current file. */
1083 		fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
1084 
1085 		/* To permit following links in subdirectories, files
1086                    are keyed on finfo->fullname, not on finfo->name. */
1087 		linkp = lookup_file_by_inode (fullpath);
1088 
1089 		/* If linkp is NULL, the file doesn't exist... maybe
1090 		   we're doing a remove operation? */
1091 		if (linkp != NULL)
1092 		{
1093 		    /* Create a new hardlink_info node, which will record
1094 		       the current file's status and the links listed in its
1095 		       `hardlinks' delta field.  We will append this
1096 		       hardlink_info node to the appropriate hardlist entry. */
1097 		    hlinfo = xmalloc (sizeof (struct hardlink_info));
1098 		    hlinfo->status = status;
1099 		    linkp->data = hlinfo;
1100 		}
1101 	    }
1102 #endif
1103 
1104 	    break;
1105         }
1106 
1107 	case T_UNKNOWN:
1108 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
1109 	    goto out;
1110 	case T_UPTODATE:
1111 	    break;
1112 	default:
1113 	    error (0, 0, "CVS internal error: unknown status %d", status);
1114 	    break;
1115     }
1116 
1117     retval = 0;
1118 
1119  out:
1120 
1121     freevers_ts (&vers);
1122     return retval;
1123 }
1124 
1125 
1126 
1127 /*
1128  * By default, return the code that tells do_recursion to examine all
1129  * directories
1130  */
1131 /* ARGSUSED */
1132 static Dtype
1133 check_direntproc (void *callerdat, const char *dir, const char *repos,
1134                   const char *update_dir, List *entries)
1135 {
1136     if (!isdir (dir))
1137 	return R_SKIP_ALL;
1138 
1139     if (!quiet)
1140 	error (0, 0, "Examining %s", update_dir);
1141 
1142     return R_PROCESS;
1143 }
1144 
1145 
1146 
1147 /*
1148  * Walklist proc to generate an arg list from the line in commitinfo
1149  */
1150 static int
1151 precommit_list_to_args_proc (p, closure)
1152     Node *p;
1153     void *closure;
1154 {
1155     struct format_cmdline_walklist_closure *c = closure;
1156     struct logfile_info *li;
1157     char *arg = NULL;
1158     const char *f;
1159     char *d;
1160     size_t doff;
1161 
1162     if (p->data == NULL) return 1;
1163 
1164     f = c->format;
1165     d = *c->d;
1166     /* foreach requested attribute */
1167     while (*f)
1168     {
1169    	switch (*f++)
1170 	{
1171 	    case 's':
1172 		li = p->data;
1173 		if (li->type == T_ADDED
1174 			|| li->type == T_MODIFIED
1175 			|| li->type == T_REMOVED)
1176 		{
1177 		    arg = p->key;
1178 		}
1179 		break;
1180 	    default:
1181 		error (1, 0,
1182 		       "Unknown format character or not a list attribute: %c",
1183 		       f[-1]);
1184 		/* NOTREACHED */
1185 		break;
1186 	}
1187 	/* copy the attribute into an argument */
1188 	if (c->quotes)
1189 	{
1190 	    arg = cmdlineescape (c->quotes, arg);
1191 	}
1192 	else
1193 	{
1194 	    arg = cmdlinequote ('"', arg);
1195 	}
1196 	doff = d - *c->buf;
1197 	expand_string (c->buf, c->length, doff + strlen (arg));
1198 	d = *c->buf + doff;
1199 	strncpy (d, arg, strlen (arg));
1200 	d += strlen (arg);
1201 	free (arg);
1202 
1203 	/* and always put the extra space on.  we'll have to back up a char
1204 	 * when we're done, but that seems most efficient
1205 	 */
1206 	doff = d - *c->buf;
1207 	expand_string (c->buf, c->length, doff + 1);
1208 	d = *c->buf + doff;
1209 	*d++ = ' ';
1210     }
1211     /* correct our original pointer into the buff */
1212     *c->d = d;
1213     return 0;
1214 }
1215 
1216 
1217 
1218 /*
1219  * Callback proc for pre-commit checking
1220  */
1221 static int
1222 precommit_proc (const char *repository, const char *filter, void *closure)
1223 {
1224     char *newfilter = NULL;
1225     char *cmdline;
1226     const char *srepos = Short_Repository (repository);
1227     List *ulist = closure;
1228 
1229 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1230     if (!strchr (filter, '%'))
1231     {
1232 	error (0, 0,
1233                "warning: commitinfo line contains no format strings:\n"
1234                "    \"%s\"\n"
1235                "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n"
1236                "deprecated.", filter);
1237 	newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
1238 	filter = newfilter;
1239     }
1240 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1241 
1242     /*
1243      * Cast any NULL arguments as appropriate pointers as this is an
1244      * stdarg function and we need to be certain the caller gets what
1245      * is expected.
1246      */
1247     cmdline = format_cmdline (
1248 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1249 			      false, srepos,
1250 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1251 			      filter,
1252 			      "c", "s", cvs_cmd_name,
1253 #ifdef SERVER_SUPPORT
1254 			      "R", "s", referrer ? referrer->original : "NONE",
1255 #endif /* SERVER_SUPPORT */
1256 			      "p", "s", srepos,
1257 			      "r", "s", current_parsed_root->directory,
1258 			      "s", ",", ulist, precommit_list_to_args_proc,
1259 			      (void *) NULL,
1260 			      (char *) NULL);
1261 
1262     if (newfilter) free (newfilter);
1263 
1264     if (!cmdline || !strlen (cmdline))
1265     {
1266 	if (cmdline) free (cmdline);
1267 	error (0, 0, "precommit proc resolved to the empty string!");
1268 	return 1;
1269     }
1270 
1271     run_setup (cmdline);
1272     free (cmdline);
1273 
1274     return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_REALLY);
1275 }
1276 
1277 
1278 
1279 /*
1280  * Run the pre-commit checks for the dir
1281  */
1282 /* ARGSUSED */
1283 static int
1284 check_filesdoneproc (void *callerdat, int err, const char *repos,
1285                      const char *update_dir, List *entries)
1286 {
1287     int n;
1288     Node *p;
1289     List *saved_ulist;
1290 
1291     /* find the update list for this dir */
1292     p = findnode (mulist, update_dir);
1293     if (p != NULL)
1294 	saved_ulist = ((struct master_lists *) p->data)->ulist;
1295     else
1296 	saved_ulist = NULL;
1297 
1298     /* skip the checks if there's nothing to do */
1299     if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
1300 	return err;
1301 
1302     /* run any pre-commit checks */
1303     n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
1304                     saved_ulist);
1305     if (n > 0)
1306     {
1307 	error (0, 0, "Pre-commit check failed");
1308 	err += n;
1309     }
1310 
1311     return err;
1312 }
1313 
1314 
1315 
1316 /*
1317  * Do the work of committing a file
1318  */
1319 static int maxrev;
1320 static char *sbranch;
1321 
1322 /* ARGSUSED */
1323 static int
1324 commit_fileproc (void *callerdat, struct file_info *finfo)
1325 {
1326     Node *p;
1327     int err = 0;
1328     List *ulist, *cilist;
1329     struct commit_info *ci;
1330 
1331     /* Keep track of whether write_dirtag is a branch tag.
1332        Note that if it is a branch tag in some files and a nonbranch tag
1333        in others, treat it as a nonbranch tag.  It is possible that case
1334        should elicit a warning or an error.  */
1335     if (write_dirtag != NULL
1336 	&& finfo->rcs != NULL)
1337     {
1338 	char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
1339 	if (rev != NULL
1340 	    && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
1341 	    write_dirnonbranch = 1;
1342 	if (rev != NULL)
1343 	    free (rev);
1344     }
1345 
1346     if (finfo->update_dir[0] == '\0')
1347 	p = findnode (mulist, ".");
1348     else
1349 	p = findnode (mulist, finfo->update_dir);
1350 
1351     /*
1352      * if p is null, there were file type command line args which were
1353      * all up-to-date so nothing really needs to be done
1354      */
1355     if (p == NULL)
1356 	return 0;
1357     ulist = ((struct master_lists *) p->data)->ulist;
1358     cilist = ((struct master_lists *) p->data)->cilist;
1359 
1360     /*
1361      * At this point, we should have the commit message unless we were called
1362      * with files as args from the command line.  In that latter case, we
1363      * need to get the commit message ourselves
1364      */
1365     if (!got_message)
1366     {
1367 	got_message = 1;
1368 	if (!server_active && use_editor)
1369 	    do_editor (finfo->update_dir, &saved_message,
1370 		       finfo->repository, ulist);
1371 	do_verify (&saved_message, finfo->repository, ulist);
1372     }
1373 
1374     p = findnode (cilist, finfo->file);
1375     if (p == NULL)
1376 	return 0;
1377 
1378     ci = p->data;
1379     if (ci->status == T_MODIFIED)
1380     {
1381 	if (finfo->rcs == NULL)
1382 	    error (1, 0, "internal error: no parsed RCS file");
1383 	if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
1384 		      finfo->repository) != 0)
1385 	{
1386 	    unlockrcs (finfo->rcs);
1387 	    err = 1;
1388 	    goto out;
1389 	}
1390     }
1391     else if (ci->status == T_ADDED)
1392     {
1393 	if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
1394 			  &finfo->rcs) != 0)
1395 	{
1396 	    if (finfo->rcs != NULL)
1397 		fixaddfile (finfo->rcs->path);
1398 	    err = 1;
1399 	    goto out;
1400 	}
1401 
1402 	/* adding files with a tag, now means adding them on a branch.
1403 	   Since the branch test was done in check_fileproc for
1404 	   modified files, we need to stub it in again here. */
1405 
1406 	if (ci->tag
1407 
1408 	    /* If numeric, it is on the trunk; check_fileproc enforced
1409 	       this.  */
1410 	    && !isdigit ((unsigned char) ci->tag[0]))
1411 	{
1412 	    if (finfo->rcs == NULL)
1413 		error (1, 0, "internal error: no parsed RCS file");
1414 	    if (ci->rev)
1415 		free (ci->rev);
1416 	    ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
1417 	    err = Checkin ('A', finfo, ci->rev,
1418 			   ci->tag, ci->options, saved_message);
1419 	    if (err != 0)
1420 	    {
1421 		unlockrcs (finfo->rcs);
1422 		fixbranch (finfo->rcs, sbranch);
1423 	    }
1424 
1425 	    (void) time (&last_register_time);
1426 
1427 	    ci->status = T_UPTODATE;
1428 	}
1429     }
1430 
1431     /*
1432      * Add the file for real
1433      */
1434     if (ci->status == T_ADDED)
1435     {
1436 	char *xrev = NULL;
1437 
1438 	if (ci->rev == NULL)
1439 	{
1440 	    /* find the max major rev number in this directory */
1441 	    maxrev = 0;
1442 	    (void) walklist (finfo->entries, findmaxrev, NULL);
1443 	    if (finfo->rcs->head)
1444 	    {
1445 		/* resurrecting: include dead revision */
1446 		int thisrev = atoi (finfo->rcs->head);
1447 		if (thisrev > maxrev)
1448 		    maxrev = thisrev;
1449 	    }
1450 	    if (maxrev == 0)
1451 		maxrev = 1;
1452 	    xrev = Xasprintf ("%d", maxrev);
1453 	}
1454 
1455 	/* XXX - an added file with symbolic -r should add tag as well */
1456 	err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
1457 	if (xrev)
1458 	    free (xrev);
1459     }
1460     else if (ci->status == T_MODIFIED)
1461     {
1462 	err = Checkin ('M', finfo, ci->rev, ci->tag,
1463 		       ci->options, saved_message);
1464 
1465 	(void) time (&last_register_time);
1466 
1467 	if (err != 0)
1468 	{
1469 	    unlockrcs (finfo->rcs);
1470 	    fixbranch (finfo->rcs, sbranch);
1471 	}
1472     }
1473     else if (ci->status == T_REMOVED)
1474     {
1475 	err = remove_file (finfo, ci->tag, saved_message);
1476 #ifdef SERVER_SUPPORT
1477 	if (server_active)
1478 	{
1479 	    server_scratch_entry_only ();
1480 	    server_updated (finfo,
1481 			    NULL,
1482 
1483 			    /* Doesn't matter, it won't get checked.  */
1484 			    SERVER_UPDATED,
1485 
1486 			    (mode_t) -1,
1487 			    NULL,
1488 			    NULL);
1489 	}
1490 #endif
1491     }
1492 
1493     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
1494        about T_ADDED or T_REMOVED.  */
1495     notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
1496 	       finfo->repository);
1497 
1498 out:
1499     if (err != 0)
1500     {
1501 	/* on failure, remove the file from ulist */
1502 	p = findnode (ulist, finfo->file);
1503 	if (p)
1504 	    delnode (p);
1505     }
1506     else
1507     {
1508 	/* On success, retrieve the new version number of the file and
1509            copy it into the log information (see logmsg.c
1510            (logfile_write) for more details).  We should only update
1511            the version number for files that have been added or
1512            modified but not removed since classify_file_internal
1513            will return the version number of a file even after it has
1514            been removed from the archive, which is not the behavior we
1515            want for our commitlog messages; we want the old version
1516            number and then "NONE." */
1517 
1518 	if (ci->status != T_REMOVED)
1519 	{
1520 	    p = findnode (ulist, finfo->file);
1521 	    if (p)
1522 	    {
1523 		Vers_TS *vers;
1524 		struct logfile_info *li;
1525 
1526 		(void) classify_file_internal (finfo, &vers);
1527 		li = p->data;
1528 		li->rev_new = xstrdup (vers->vn_rcs);
1529 		freevers_ts (&vers);
1530 	    }
1531 	}
1532     }
1533     if (SIG_inCrSect ())
1534 	SIG_endCrSect ();
1535 
1536     return err;
1537 }
1538 
1539 
1540 
1541 /*
1542  * Log the commit and clean up the update list
1543  */
1544 /* ARGSUSED */
1545 static int
1546 commit_filesdoneproc (void *callerdat, int err, const char *repository,
1547                       const char *update_dir, List *entries)
1548 {
1549     Node *p;
1550     List *ulist;
1551 
1552     assert (repository);
1553 
1554     p = findnode (mulist, update_dir);
1555     if (p == NULL)
1556 	return err;
1557 
1558     ulist = ((struct master_lists *) p->data)->ulist;
1559 
1560     got_message = 0;
1561 
1562     /* Build the administrative files if necessary.  */
1563     {
1564 	const char *p;
1565 
1566 	if (strncmp (current_parsed_root->directory, repository,
1567 		     strlen (current_parsed_root->directory)) != 0)
1568 	    error (0, 0,
1569 		 "internal error: repository (%s) doesn't begin with root (%s)",
1570 		   repository, current_parsed_root->directory);
1571 	p = repository + strlen (current_parsed_root->directory);
1572 	if (*p == '/')
1573 	    ++p;
1574 	if (strcmp ("CVSROOT", p) == 0
1575 	    /* Check for subdirectories because people may want to create
1576 	       subdirectories and list files therein in checkoutlist.  */
1577 	    || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
1578 	    )
1579 	{
1580 	    /* "Database" might a little bit grandiose and/or vague,
1581 	       but "checked-out copies of administrative files, unless
1582 	       in the case of modules and you are using ndbm in which
1583 	       case modules.{pag,dir,db}" is verbose and excessively
1584 	       focused on how the database is implemented.  */
1585 
1586 	    /* mkmodules requires the absolute name of the CVSROOT directory.
1587 	       Remove anything after the `CVSROOT' component -- this is
1588 	       necessary when committing in a subdirectory of CVSROOT.  */
1589 	    char *admin_dir = xstrdup (repository);
1590 	    int cvsrootlen = strlen ("CVSROOT");
1591 	    assert (admin_dir[p - repository + cvsrootlen] == '\0'
1592 		    || admin_dir[p - repository + cvsrootlen] == '/');
1593 	    admin_dir[p - repository + cvsrootlen] = '\0';
1594 
1595 	    if (!really_quiet)
1596 	    {
1597 		cvs_output (program_name, 0);
1598 		cvs_output (" ", 1);
1599 		cvs_output (cvs_cmd_name, 0);
1600 		cvs_output (": Rebuilding administrative file database\n", 0);
1601 	    }
1602 	    mkmodules (admin_dir);
1603 	    free (admin_dir);
1604 	    WriteTemplate (".", 1, repository);
1605 	}
1606     }
1607 
1608     /* FIXME: This used to be above the block above.  The advantage of being
1609      * here is that it is not called until after all possible writes from this
1610      * process are complete.  The disadvantage is that a fatal error during
1611      * update of CVSROOT can prevent the loginfo script from being called.
1612      *
1613      * A more general solution I have been considering is calling a generic
1614      * "postwrite" hook from the remove write lock routine.
1615      */
1616     Update_Logfile (repository, saved_message, NULL, ulist);
1617 
1618     return err;
1619 }
1620 
1621 
1622 
1623 /*
1624  * Get the log message for a dir
1625  */
1626 /* ARGSUSED */
1627 static Dtype
1628 commit_direntproc (void *callerdat, const char *dir, const char *repos,
1629                    const char *update_dir, List *entries)
1630 {
1631     Node *p;
1632     List *ulist;
1633     char *real_repos;
1634 
1635     if (!isdir (dir))
1636 	return R_SKIP_ALL;
1637 
1638     /* find the update list for this dir */
1639     p = findnode (mulist, update_dir);
1640     if (p != NULL)
1641 	ulist = ((struct master_lists *) p->data)->ulist;
1642     else
1643 	ulist = NULL;
1644 
1645     /* skip the files as an optimization */
1646     if (ulist == NULL || ulist->list->next == ulist->list)
1647 	return R_SKIP_FILES;
1648 
1649     /* get commit message */
1650     got_message = 1;
1651     real_repos = Name_Repository (dir, update_dir);
1652     if (!server_active && use_editor)
1653 	do_editor (update_dir, &saved_message, real_repos, ulist);
1654     do_verify (&saved_message, real_repos, ulist);
1655     free (real_repos);
1656     return R_PROCESS;
1657 }
1658 
1659 
1660 
1661 /*
1662  * Process the post-commit proc if necessary
1663  */
1664 /* ARGSUSED */
1665 static int
1666 commit_dirleaveproc (void *callerdat, const char *dir, int err,
1667                      const char *update_dir, List *entries)
1668 {
1669     /* update the per-directory tag info */
1670     /* FIXME?  Why?  The "commit examples" node of cvs.texinfo briefly
1671        mentions commit -r being sticky, but apparently in the context of
1672        this being a confusing feature!  */
1673     if (err == 0 && write_dirtag != NULL)
1674     {
1675 	char *repos = Name_Repository (NULL, update_dir);
1676 	WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
1677 		  update_dir, repos);
1678 	free (repos);
1679     }
1680 
1681     return err;
1682 }
1683 
1684 
1685 
1686 /*
1687  * find the maximum major rev number in an entries file
1688  */
1689 static int
1690 findmaxrev (Node *p, void *closure)
1691 {
1692     int thisrev;
1693     Entnode *entdata = p->data;
1694 
1695     if (entdata->type != ENT_FILE)
1696 	return 0;
1697     thisrev = atoi (entdata->version);
1698     if (thisrev > maxrev)
1699 	maxrev = thisrev;
1700     return 0;
1701 }
1702 
1703 /*
1704  * Actually remove a file by moving it to the attic
1705  * XXX - if removing a ,v file that is a relative symbolic link to
1706  * another ,v file, we probably should add a ".." component to the
1707  * link to keep it relative after we move it into the attic.
1708 
1709    Return value is 0 on success, or >0 on error (in which case we have
1710    printed an error message).  */
1711 static int
1712 remove_file (struct file_info *finfo, char *tag, char *message)
1713 {
1714     int retcode;
1715 
1716     int branch;
1717     int lockflag;
1718     char *corev;
1719     char *rev;
1720     char *prev_rev;
1721     char *old_path;
1722 
1723     corev = NULL;
1724     rev = NULL;
1725     prev_rev = NULL;
1726 
1727     retcode = 0;
1728 
1729     if (finfo->rcs == NULL)
1730 	error (1, 0, "internal error: no parsed RCS file");
1731 
1732     branch = 0;
1733     if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
1734     {
1735 	/* a symbolic tag is specified; just remove the tag from the file */
1736 	if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
1737 	{
1738 	    if (!quiet)
1739 		error (0, retcode == -1 ? errno : 0,
1740 		       "failed to remove tag `%s' from `%s'", tag,
1741 		       finfo->fullname);
1742 	    return 1;
1743 	}
1744 	RCS_rewrite (finfo->rcs, NULL, NULL);
1745 	Scratch_Entry (finfo->entries, finfo->file);
1746 	return 0;
1747     }
1748 
1749     /* we are removing the file from either the head or a branch */
1750     /* commit a new, dead revision. */
1751 
1752     rev = NULL;
1753     lockflag = 1;
1754     if (branch)
1755     {
1756 	char *branchname;
1757 
1758 	rev = RCS_whatbranch (finfo->rcs, tag);
1759 	if (rev == NULL)
1760 	{
1761 	    error (0, 0, "cannot find branch \"%s\".", tag);
1762 	    return 1;
1763 	}
1764 
1765 	branchname = RCS_getbranch (finfo->rcs, rev, 1);
1766 	if (branchname == NULL)
1767 	{
1768 	    /* no revision exists on this branch.  use the previous
1769 	       revision but do not lock. */
1770 	    corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
1771 	    prev_rev = xstrdup (corev);
1772 	    lockflag = 0;
1773 	} else
1774 	{
1775 	    corev = xstrdup (rev);
1776 	    prev_rev = xstrdup (branchname);
1777 	    free (branchname);
1778 	}
1779 
1780     } else  /* Not a branch */
1781     {
1782         /* Get current head revision of file. */
1783 	prev_rev = RCS_head (finfo->rcs);
1784     }
1785 
1786     /* if removing without a tag or a branch, then make sure the default
1787        branch is the trunk. */
1788     if (!tag && !branch)
1789     {
1790         if (RCS_setbranch (finfo->rcs, NULL) != 0)
1791 	{
1792 	    error (0, 0, "cannot change branch to default for %s",
1793 		   finfo->fullname);
1794 	    return 1;
1795 	}
1796 	RCS_rewrite (finfo->rcs, NULL, NULL);
1797     }
1798 
1799     /* check something out.  Generally this is the head.  If we have a
1800        particular rev, then name it.  */
1801     retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
1802 			    NULL, NULL, RUN_TTY, NULL, NULL);
1803     if (retcode != 0)
1804     {
1805 	error (0, 0,
1806 	       "failed to check out `%s'", finfo->fullname);
1807 	return 1;
1808     }
1809 
1810     /* Except when we are creating a branch, lock the revision so that
1811        we can check in the new revision.  */
1812     if (lockflag)
1813     {
1814 	if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
1815 	    RCS_rewrite (finfo->rcs, NULL, NULL);
1816     }
1817 
1818     if (corev != NULL)
1819 	free (corev);
1820 
1821     retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
1822 			   rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1823     if (retcode	!= 0)
1824     {
1825 	if (!quiet)
1826 	    error (0, retcode == -1 ? errno : 0,
1827 		   "failed to commit dead revision for `%s'", finfo->fullname);
1828 	return 1;
1829     }
1830     /* At this point, the file has been committed as removed.  We should
1831        probably tell the history file about it  */
1832     history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository);
1833 
1834     if (rev != NULL)
1835 	free (rev);
1836 
1837     old_path = xstrdup (finfo->rcs->path);
1838     if (!branch)
1839 	RCS_setattic (finfo->rcs, 1);
1840 
1841     /* Print message that file was removed. */
1842     if (!really_quiet)
1843     {
1844 	cvs_output (old_path, 0);
1845 	cvs_output ("  <--  ", 0);
1846 	if (finfo->update_dir && strlen (finfo->update_dir))
1847 	{
1848 	    cvs_output (finfo->update_dir, 0);
1849 	    cvs_output ("/", 1);
1850 	}
1851 	cvs_output (finfo->file, 0);
1852 	cvs_output ("\nnew revision: delete; previous revision: ", 0);
1853 	cvs_output (prev_rev, 0);
1854 	cvs_output ("\n", 0);
1855     }
1856 
1857     free (prev_rev);
1858 
1859     free (old_path);
1860 
1861     Scratch_Entry (finfo->entries, finfo->file);
1862     return 0;
1863 }
1864 
1865 
1866 
1867 /*
1868  * Do the actual checkin for added files
1869  */
1870 static int
1871 finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
1872 {
1873     int ret;
1874 
1875     ret = Checkin ('A', finfo, rev, tag, options, saved_message);
1876     if (ret == 0)
1877     {
1878 	char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
1879 	if (unlink_file (tmp) < 0
1880 	    && !existence_error (errno))
1881 	    error (0, errno, "cannot remove %s", tmp);
1882 	free (tmp);
1883     }
1884     else if (finfo->rcs != NULL)
1885 	fixaddfile (finfo->rcs->path);
1886 
1887     (void) time (&last_register_time);
1888 
1889     return ret;
1890 }
1891 
1892 
1893 
1894 /*
1895  * Unlock an rcs file
1896  */
1897 static void
1898 unlockrcs (RCSNode *rcs)
1899 {
1900     int retcode;
1901 
1902     if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
1903 	error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1904 	       "could not unlock %s", rcs->path);
1905     else
1906 	RCS_rewrite (rcs, NULL, NULL);
1907 }
1908 
1909 
1910 
1911 /*
1912  * remove a partially added file.  if we can parse it, leave it alone.
1913  *
1914  * FIXME: Every caller that calls this function can access finfo->rcs (the
1915  * parsed RCSNode data), so we should be able to detect that the file needs
1916  * to be removed without reparsing the file as we do below.
1917  */
1918 static void
1919 fixaddfile (const char *rcs)
1920 {
1921     RCSNode *rcsfile;
1922     int save_really_quiet;
1923 
1924     save_really_quiet = really_quiet;
1925     really_quiet = 1;
1926     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
1927     {
1928 	if (unlink_file (rcs) < 0)
1929 	    error (0, errno, "cannot remove %s", rcs);
1930     }
1931     else
1932 	freercsnode (&rcsfile);
1933     really_quiet = save_really_quiet;
1934 }
1935 
1936 
1937 
1938 /*
1939  * put the branch back on an rcs file
1940  */
1941 static void
1942 fixbranch (RCSNode *rcs, char *branch)
1943 {
1944     int retcode;
1945 
1946     if (branch != NULL)
1947     {
1948 	if ((retcode = RCS_setbranch (rcs, branch)) != 0)
1949 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1950 		   "cannot restore branch to %s for %s", branch, rcs->path);
1951 	RCS_rewrite (rcs, NULL, NULL);
1952     }
1953 }
1954 
1955 
1956 
1957 /*
1958  * do the initial part of a file add for the named file.  if adding
1959  * with a tag, put the file in the Attic and point the symbolic tag
1960  * at the committed revision.
1961  *
1962  * INPUTS
1963  *   file	The name of the file in the workspace.
1964  *   repository	The repository directory to expect to find FILE,v in.
1965  *   tag	The name or rev num of the branch being added to, if any.
1966  *   options	Any RCS keyword expansion options specified by the user.
1967  *   rcsnode	A pointer to the pre-parsed RCSNode for this file, if the file
1968  *		exists in the repository.  If this is NULL, assume the file
1969  *		does not yet exist.
1970  *
1971  * RETURNS
1972  *   0 on success.
1973  *   1 on errors, after printing any appropriate error messages.
1974  *
1975  * ERRORS
1976  *   This function will return an error when any of the following functions do:
1977  *     add_rcs_file
1978  *     RCS_setattic
1979  *     lock_RCS
1980  *     RCS_checkin
1981  *     RCS_parse (called to verify the newly created archive file)
1982  *     RCS_settag
1983  */
1984 
1985 static int
1986 checkaddfile (const char *file, const char *repository, const char *tag,
1987               const char *options, RCSNode **rcsnode)
1988 {
1989     RCSNode *rcs;
1990     char *fname;
1991     int newfile = 0;		/* Set to 1 if we created a new RCS archive. */
1992     int retval = 1;
1993     int adding_on_branch;
1994 
1995     assert (rcsnode != NULL);
1996 
1997     /* Callers expect to be able to use either "" or NULL to mean the
1998        default keyword expansion.  */
1999     if (options != NULL && options[0] == '\0')
2000 	options = NULL;
2001     if (options != NULL)
2002 	assert (options[0] == '-' && options[1] == 'k');
2003 
2004     /* If numeric, it is on the trunk; check_fileproc enforced
2005        this.  */
2006     adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
2007 
2008     if (*rcsnode == NULL)
2009     {
2010 	char *rcsname;
2011 	char *desc = NULL;
2012 	size_t descalloc = 0;
2013 	size_t desclen = 0;
2014 	const char *opt;
2015 
2016 	if (adding_on_branch)
2017 	{
2018 	    mode_t omask;
2019 	    rcsname = xmalloc (strlen (repository)
2020 			       + sizeof (CVSATTIC)
2021 			       + strlen (file)
2022 			       + sizeof (RCSEXT)
2023 			       + 3);
2024 	    (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
2025 	    omask = umask (cvsumask);
2026 	    if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
2027 		error (1, errno, "cannot make directory `%s'", rcsname);
2028 	    (void) umask (omask);
2029 	    (void) sprintf (rcsname,
2030 			    "%s/%s/%s%s",
2031 			    repository,
2032 			    CVSATTIC,
2033 			    file,
2034 			    RCSEXT);
2035 	}
2036 	else
2037 	    rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
2038 
2039 	/* this is the first time we have ever seen this file; create
2040 	   an RCS file.  */
2041 	fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
2042 	/* If the file does not exist, no big deal.  In particular, the
2043 	   server does not (yet at least) create CVSEXT_LOG files.  */
2044 	if (isfile (fname))
2045 	    /* FIXME: Should be including update_dir in the appropriate
2046 	       place here.  */
2047 	    get_file (fname, fname, "r", &desc, &descalloc, &desclen);
2048 	free (fname);
2049 
2050 	/* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
2051 	   end of the log message if the message is nonempty.
2052 	   Do it.  RCS also deletes certain whitespace, in cleanlogmsg,
2053 	   which we don't try to do here.  */
2054 	if (desclen > 0)
2055 	{
2056 	    expand_string (&desc, &descalloc, desclen + 1);
2057 	    desc[desclen++] = '\012';
2058 	}
2059 
2060 	/* Set RCS keyword expansion options.  */
2061 	if (options != NULL)
2062 	    opt = options + 2;
2063 	else
2064 	    opt = NULL;
2065 
2066 	if (add_rcs_file (NULL, rcsname, file, NULL, opt,
2067 			  NULL, NULL, 0, NULL,
2068 			  desc, desclen, NULL, 0) != 0)
2069 	{
2070 	    if (rcsname != NULL)
2071 	        free (rcsname);
2072 	    goto out;
2073 	}
2074 	rcs = RCS_parsercsfile (rcsname);
2075 	newfile = 1;
2076 	if (rcsname != NULL)
2077 	    free (rcsname);
2078 	if (desc != NULL)
2079 	    free (desc);
2080 	*rcsnode = rcs;
2081     }
2082     else
2083     {
2084 	/* file has existed in the past.  Prepare to resurrect. */
2085 	char *rev;
2086 	char *oldexpand;
2087 
2088 	rcs = *rcsnode;
2089 
2090 	oldexpand = RCS_getexpand (rcs);
2091 	if ((oldexpand != NULL
2092 	     && options != NULL
2093 	     && strcmp (options + 2, oldexpand) != 0)
2094 	    || (oldexpand == NULL && options != NULL))
2095 	{
2096 	    /* We tell the user about this, because it means that the
2097 	       old revisions will no longer retrieve the way that they
2098 	       used to.  */
2099 	    error (0, 0, "changing keyword expansion mode to %s", options);
2100 	    RCS_setexpand (rcs, options + 2);
2101 	}
2102 
2103 	if (!adding_on_branch)
2104 	{
2105 	    /* We are adding on the trunk, so move the file out of the
2106 	       Attic.  */
2107 	    if (!(rcs->flags & INATTIC))
2108 	    {
2109 		error (0, 0, "warning: expected %s to be in Attic",
2110 		       rcs->path);
2111 	    }
2112 
2113 	    /* Begin a critical section around the code that spans the
2114 	       first commit on the trunk of a file that's already been
2115 	       committed on a branch.  */
2116 	    SIG_beginCrSect ();
2117 
2118 	    if (RCS_setattic (rcs, 0))
2119 	    {
2120 		goto out;
2121 	    }
2122 	}
2123 
2124 	rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
2125 	/* and lock it */
2126 	if (lock_RCS (file, rcs, rev, repository))
2127 	{
2128 	    error (0, 0, "cannot lock revision %s in `%s'.",
2129 		   rev ? rev : tag ? tag : "HEAD", rcs->path);
2130 	    if (rev != NULL)
2131 		free (rev);
2132 	    goto out;
2133 	}
2134 
2135 	if (rev != NULL)
2136 	    free (rev);
2137     }
2138 
2139     /* when adding a file for the first time, and using a tag, we need
2140        to create a dead revision on the trunk.  */
2141     if (adding_on_branch)
2142     {
2143 	if (newfile)
2144 	{
2145 	    char *tmp;
2146 	    FILE *fp;
2147 	    int retcode;
2148 
2149 	    /* move the new file out of the way. */
2150 	    fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2151 	    rename_file (file, fname);
2152 
2153 	    /* Create empty FILE.  Can't use copy_file with a DEVNULL
2154 	       argument -- copy_file now ignores device files. */
2155 	    fp = fopen (file, "w");
2156 	    if (fp == NULL)
2157 		error (1, errno, "cannot open %s for writing", file);
2158 	    if (fclose (fp) < 0)
2159 		error (0, errno, "cannot close %s", file);
2160 
2161 	    tmp = Xasprintf ("file %s was initially added on branch %s.",
2162 			     file, tag);
2163 	    /* commit a dead revision. */
2164 	    retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
2165 				   RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
2166 	    free (tmp);
2167 	    if (retcode != 0)
2168 	    {
2169 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2170 		       "could not create initial dead revision %s", rcs->path);
2171 		free (fname);
2172 		goto out;
2173 	    }
2174 
2175 	    /* put the new file back where it was */
2176 	    rename_file (fname, file);
2177 	    free (fname);
2178 
2179 	    /* double-check that the file was written correctly */
2180 	    freercsnode (&rcs);
2181 	    rcs = RCS_parse (file, repository);
2182 	    if (rcs == NULL)
2183 	    {
2184 		error (0, 0, "could not read %s", rcs->path);
2185 		goto out;
2186 	    }
2187 	    *rcsnode = rcs;
2188 
2189 	    /* and lock it once again. */
2190 	    if (lock_RCS (file, rcs, NULL, repository))
2191 	    {
2192 		error (0, 0, "cannot lock initial revision in `%s'.",
2193 		       rcs->path);
2194 		goto out;
2195 	    }
2196 	}
2197 
2198 	/* when adding with a tag, we need to stub a branch, if it
2199 	   doesn't already exist.  */
2200 	if (!RCS_nodeisbranch (rcs, tag))
2201 	{
2202 	    /* branch does not exist.  Stub it.  */
2203 	    char *head;
2204 	    char *magicrev;
2205 	    int retcode;
2206 	    time_t headtime = -1;
2207 	    char *revnum, *tmp;
2208 	    FILE *fp;
2209 	    time_t t = -1;
2210 	    struct tm *ct;
2211 
2212 	    fixbranch (rcs, sbranch);
2213 
2214 	    head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
2215 	    if (!head)
2216 		error (1, 0, "No head revision in archive file `%s'.",
2217 		       rcs->print_path);
2218 	    magicrev = RCS_magicrev (rcs, head);
2219 
2220 	    /* If this is not a new branch, then we will want a dead
2221 	       version created before this one. */
2222 	    if (!newfile)
2223 		headtime = RCS_getrevtime (rcs, head, 0, 0);
2224 
2225 	    retcode = RCS_settag (rcs, tag, magicrev);
2226 	    RCS_rewrite (rcs, NULL, NULL);
2227 
2228 	    free (head);
2229 	    free (magicrev);
2230 
2231 	    if (retcode != 0)
2232 	    {
2233 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2234 		       "could not stub branch %s for %s", tag, rcs->path);
2235 		goto out;
2236 	    }
2237 	    /* We need to add a dead version here to avoid -rtag -Dtime
2238 	       checkout problems between when the head version was
2239 	       created and now. */
2240 	    if (!newfile && headtime != -1)
2241 	    {
2242 		/* move the new file out of the way. */
2243 		fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2244 		rename_file (file, fname);
2245 
2246 		/* Create empty FILE.  Can't use copy_file with a DEVNULL
2247 		   argument -- copy_file now ignores device files. */
2248 		fp = fopen (file, "w");
2249 		if (fp == NULL)
2250 		    error (1, errno, "cannot open %s for writing", file);
2251 		if (fclose (fp) < 0)
2252 		    error (0, errno, "cannot close %s", file);
2253 
2254 		/* As we will be hacking the delta date, put the time
2255 		   this was added into the log message. */
2256 		t = time (NULL);
2257 		ct = gmtime (&t);
2258 		tmp = Xasprintf ("file %s was added on branch %s on %d-%02d-%02d %02d:%02d:%02d +0000",
2259 				 file, tag,
2260 				 ct->tm_year + (ct->tm_year < 100 ? 0 : 1900),
2261 				 ct->tm_mon + 1, ct->tm_mday,
2262 				 ct->tm_hour, ct->tm_min, ct->tm_sec);
2263 
2264 		/* commit a dead revision. */
2265 		revnum = RCS_whatbranch (rcs, tag);
2266 		retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
2267 				       RCS_FLAGS_DEAD |
2268 				       RCS_FLAGS_QUIET |
2269 				       RCS_FLAGS_USETIME);
2270 		free (revnum);
2271 		free (tmp);
2272 
2273 		if (retcode != 0)
2274 		{
2275 		    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2276 			   "could not created dead stub %s for %s", tag,
2277 			   rcs->path);
2278 		    goto out;
2279 		}
2280 
2281 		/* put the new file back where it was */
2282 		rename_file (fname, file);
2283 		free (fname);
2284 
2285 		/* double-check that the file was written correctly */
2286 		freercsnode (&rcs);
2287 		rcs = RCS_parse (file, repository);
2288 		if (rcs == NULL)
2289 		{
2290 		    error (0, 0, "could not read %s", rcs->path);
2291 		    goto out;
2292 		}
2293 		*rcsnode = rcs;
2294 	    }
2295 	}
2296 	else
2297 	{
2298 	    /* lock the branch. (stubbed branches need not be locked.)  */
2299 	    if (lock_RCS (file, rcs, NULL, repository))
2300 	    {
2301 		error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
2302 		goto out;
2303 	    }
2304 	}
2305 
2306 	if (*rcsnode != rcs)
2307 	{
2308 	    freercsnode (rcsnode);
2309 	    *rcsnode = rcs;
2310 	}
2311     }
2312 
2313     fileattr_newfile (file);
2314 
2315     /* At this point, we used to set the file mode of the RCS file
2316        based on the mode of the file in the working directory.  If we
2317        are creating the RCS file for the first time, add_rcs_file does
2318        this already.  If we are re-adding the file, then perhaps it is
2319        consistent to preserve the old file mode, just as we preserve
2320        the old keyword expansion mode.
2321 
2322        If we decide that we should change the modes, then we can't do
2323        it here anyhow.  At this point, the RCS file may be owned by
2324        somebody else, so a chmod will fail.  We need to instead do the
2325        chmod after rewriting it.
2326 
2327        FIXME: In general, I think the file mode (and the keyword
2328        expansion mode) should be associated with a particular revision
2329        of the file, so that it is possible to have different revisions
2330        of a file have different modes.  */
2331 
2332     retval = 0;
2333 
2334  out:
2335     if (retval != 0 && SIG_inCrSect ())
2336 	SIG_endCrSect ();
2337     return retval;
2338 }
2339 
2340 
2341 
2342 /*
2343  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
2344  * couldn't.  If the RCS file currently has a branch as the head, we must
2345  * move the head back to the trunk before locking the file, and be sure to
2346  * put the branch back as the head if there are any errors.
2347  */
2348 static int
2349 lock_RCS (const char *user, RCSNode *rcs, const char *rev,
2350           const char *repository)
2351 {
2352     char *branch = NULL;
2353     int err = 0;
2354 
2355     /*
2356      * For a specified, numeric revision of the form "1" or "1.1", (or when
2357      * no revision is specified ""), definitely move the branch to the trunk
2358      * before locking the RCS file.
2359      *
2360      * The assumption is that if there is more than one revision on the trunk,
2361      * the head points to the trunk, not a branch... and as such, it's not
2362      * necessary to move the head in this case.
2363      */
2364     if (rev == NULL
2365 	|| (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
2366     {
2367 	branch = xstrdup (rcs->branch);
2368 	if (branch != NULL)
2369 	{
2370 	    if (RCS_setbranch (rcs, NULL) != 0)
2371 	    {
2372 		error (0, 0, "cannot change branch to default for %s",
2373 		       rcs->path);
2374 		if (branch)
2375 		    free (branch);
2376 		return 1;
2377 	    }
2378 	}
2379 	err = RCS_lock (rcs, NULL, 1);
2380     }
2381     else
2382     {
2383 	RCS_lock (rcs, rev, 1);
2384     }
2385 
2386     /* We used to call RCS_rewrite here, and that might seem
2387        appropriate in order to write out the locked revision
2388        information.  However, such a call would actually serve no
2389        purpose.  CVS locks will prevent any interference from other
2390        CVS processes.  The comment above rcs_internal_lockfile
2391        explains that it is already unsafe to use RCS and CVS
2392        simultaneously.  It follows that writing out the locked
2393        revision information here would add no additional security.
2394 
2395        If we ever do care about it, the proper fix is to create the
2396        RCS lock file before calling this function, and maintain it
2397        until the checkin is complete.
2398 
2399        The call to RCS_lock is still required at present, since in
2400        some cases RCS_checkin will determine which revision to check
2401        in by looking for a lock.  FIXME: This is rather roundabout,
2402        and a more straightforward approach would probably be easier to
2403        understand.  */
2404 
2405     if (err == 0)
2406     {
2407 	if (sbranch != NULL)
2408 	    free (sbranch);
2409 	sbranch = branch;
2410 	return 0;
2411     }
2412 
2413     /* try to restore the branch if we can on error */
2414     if (branch != NULL)
2415 	fixbranch (rcs, branch);
2416 
2417     if (branch)
2418 	free (branch);
2419     return 1;
2420 }
2421 
2422 
2423 
2424 /*
2425  * free an UPDATE node's data
2426  */
2427 void
2428 update_delproc (Node *p)
2429 {
2430     struct logfile_info *li = p->data;
2431 
2432     if (li->tag)
2433 	free (li->tag);
2434     if (li->rev_old)
2435 	free (li->rev_old);
2436     if (li->rev_new)
2437 	free (li->rev_new);
2438     free (li);
2439 }
2440 
2441 /*
2442  * Free the commit_info structure in p.
2443  */
2444 static void
2445 ci_delproc (Node *p)
2446 {
2447     struct commit_info *ci = p->data;
2448 
2449     if (ci->rev)
2450 	free (ci->rev);
2451     if (ci->tag)
2452 	free (ci->tag);
2453     if (ci->options)
2454 	free (ci->options);
2455     free (ci);
2456 }
2457 
2458 /*
2459  * Free the commit_info structure in p.
2460  */
2461 static void
2462 masterlist_delproc (Node *p)
2463 {
2464     struct master_lists *ml = p->data;
2465 
2466     dellist (&ml->ulist);
2467     dellist (&ml->cilist);
2468     free (ml);
2469 }
2470