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