xref: /netbsd-src/external/gpl2/xcvs/dist/src/commit.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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     getoptreset ();
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 	    /* If the file was re-added, we want the revision in the commitlog
1050 	       to be NONE, not the previous dead revision. */
1051 	    li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs);
1052 	    li->rev_new = NULL;
1053 	    p->data = li;
1054 	    (void) addnode (ulist, p);
1055 
1056 	    p = getnode ();
1057 	    p->key = xstrdup (finfo->file);
1058 	    p->type = UPDATE;
1059 	    p->delproc = ci_delproc;
1060 	    ci = xmalloc (sizeof (struct commit_info));
1061 	    ci->status = status;
1062 	    if (vers->tag)
1063 		if (isdigit ((unsigned char) *vers->tag))
1064 		    ci->rev = xstrdup (vers->tag);
1065 		else
1066 		    ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
1067 	    else
1068 		ci->rev = NULL;
1069 	    ci->tag = xstrdup (vers->tag);
1070 	    ci->options = xstrdup (vers->options);
1071 	    p->data = ci;
1072 	    (void) addnode (cilist, p);
1073 
1074 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1075 	    if (preserve_perms)
1076 	    {
1077 		/* Add this file to hardlist, indexed on its inode.  When
1078 		   we are done, we can find out what files are hardlinked
1079 		   to a given file by looking up its inode in hardlist. */
1080 		char *fullpath;
1081 		Node *linkp;
1082 		struct hardlink_info *hlinfo;
1083 
1084 		/* Get the full pathname of the current file. */
1085 		fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
1086 
1087 		/* To permit following links in subdirectories, files
1088                    are keyed on finfo->fullname, not on finfo->name. */
1089 		linkp = lookup_file_by_inode (fullpath);
1090 
1091 		/* If linkp is NULL, the file doesn't exist... maybe
1092 		   we're doing a remove operation? */
1093 		if (linkp != NULL)
1094 		{
1095 		    /* Create a new hardlink_info node, which will record
1096 		       the current file's status and the links listed in its
1097 		       `hardlinks' delta field.  We will append this
1098 		       hardlink_info node to the appropriate hardlist entry. */
1099 		    hlinfo = xmalloc (sizeof (struct hardlink_info));
1100 		    hlinfo->status = status;
1101 		    linkp->data = hlinfo;
1102 		}
1103 	    }
1104 #endif
1105 
1106 	    break;
1107         }
1108 
1109 	case T_UNKNOWN:
1110 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
1111 	    goto out;
1112 	case T_UPTODATE:
1113 	    break;
1114 	default:
1115 	    error (0, 0, "CVS internal error: unknown status %d", status);
1116 	    break;
1117     }
1118 
1119     retval = 0;
1120 
1121  out:
1122 
1123     freevers_ts (&vers);
1124     return retval;
1125 }
1126 
1127 
1128 
1129 /*
1130  * By default, return the code that tells do_recursion to examine all
1131  * directories
1132  */
1133 /* ARGSUSED */
1134 static Dtype
1135 check_direntproc (void *callerdat, const char *dir, const char *repos,
1136                   const char *update_dir, List *entries)
1137 {
1138     if (!isdir (dir))
1139 	return R_SKIP_ALL;
1140 
1141     if (!quiet)
1142 	error (0, 0, "Examining %s", update_dir);
1143 
1144     return R_PROCESS;
1145 }
1146 
1147 
1148 
1149 /*
1150  * Walklist proc to generate an arg list from the line in commitinfo
1151  */
1152 static int
1153 precommit_list_to_args_proc (p, closure)
1154     Node *p;
1155     void *closure;
1156 {
1157     struct format_cmdline_walklist_closure *c = closure;
1158     struct logfile_info *li;
1159     char *arg = NULL;
1160     const char *f;
1161     char *d;
1162     size_t doff;
1163 
1164     if (p->data == NULL) return 1;
1165 
1166     f = c->format;
1167     d = *c->d;
1168     /* foreach requested attribute */
1169     while (*f)
1170     {
1171    	switch (*f++)
1172 	{
1173 	    case 's':
1174 		li = p->data;
1175 		if (li->type == T_ADDED
1176 			|| li->type == T_MODIFIED
1177 			|| li->type == T_REMOVED)
1178 		{
1179 		    arg = p->key;
1180 		}
1181 		break;
1182 	    default:
1183 		error (1, 0,
1184 		       "Unknown format character or not a list attribute: %c",
1185 		       f[-1]);
1186 		/* NOTREACHED */
1187 		break;
1188 	}
1189 	/* copy the attribute into an argument */
1190 	if (c->quotes)
1191 	{
1192 	    arg = cmdlineescape (c->quotes, arg);
1193 	}
1194 	else
1195 	{
1196 	    arg = cmdlinequote ('"', arg);
1197 	}
1198 	doff = d - *c->buf;
1199 	expand_string (c->buf, c->length, doff + strlen (arg));
1200 	d = *c->buf + doff;
1201 	strncpy (d, arg, strlen (arg));
1202 	d += strlen (arg);
1203 	free (arg);
1204 
1205 	/* and always put the extra space on.  we'll have to back up a char
1206 	 * when we're done, but that seems most efficient
1207 	 */
1208 	doff = d - *c->buf;
1209 	expand_string (c->buf, c->length, doff + 1);
1210 	d = *c->buf + doff;
1211 	*d++ = ' ';
1212     }
1213     /* correct our original pointer into the buff */
1214     *c->d = d;
1215     return 0;
1216 }
1217 
1218 
1219 
1220 /*
1221  * Callback proc for pre-commit checking
1222  */
1223 static int
1224 precommit_proc (const char *repository, const char *filter, void *closure)
1225 {
1226     char *newfilter = NULL;
1227     char *cmdline;
1228     const char *srepos = Short_Repository (repository);
1229     List *ulist = closure;
1230 
1231 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1232     if (!strchr (filter, '%'))
1233     {
1234 	error (0, 0,
1235                "warning: commitinfo line contains no format strings:\n"
1236                "    \"%s\"\n"
1237                "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n"
1238                "deprecated.", filter);
1239 	newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
1240 	filter = newfilter;
1241     }
1242 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1243 
1244     /*
1245      * Cast any NULL arguments as appropriate pointers as this is an
1246      * stdarg function and we need to be certain the caller gets what
1247      * is expected.
1248      */
1249     cmdline = format_cmdline (
1250 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1251 			      false, srepos,
1252 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1253 			      filter,
1254 			      "c", "s", cvs_cmd_name,
1255 #ifdef SERVER_SUPPORT
1256 			      "R", "s", referrer ? referrer->original : "NONE",
1257 #endif /* SERVER_SUPPORT */
1258 			      "p", "s", srepos,
1259 			      "r", "s", current_parsed_root->directory,
1260 			      "s", ",", ulist, precommit_list_to_args_proc,
1261 			      (void *) NULL,
1262 			      (char *) NULL);
1263 
1264     if (newfilter) free (newfilter);
1265 
1266     if (!cmdline || !strlen (cmdline))
1267     {
1268 	if (cmdline) free (cmdline);
1269 	error (0, 0, "precommit proc resolved to the empty string!");
1270 	return 1;
1271     }
1272 
1273     run_setup (cmdline);
1274     free (cmdline);
1275 
1276     return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY|
1277 	(server_active ? 0 : RUN_UNSETXID));
1278 }
1279 
1280 
1281 
1282 /*
1283  * Run the pre-commit checks for the dir
1284  */
1285 /* ARGSUSED */
1286 static int
1287 check_filesdoneproc (void *callerdat, int err, const char *repos,
1288                      const char *update_dir, List *entries)
1289 {
1290     int n;
1291     Node *p;
1292     List *saved_ulist;
1293 
1294     /* find the update list for this dir */
1295     p = findnode (mulist, update_dir);
1296     if (p != NULL)
1297 	saved_ulist = ((struct master_lists *) p->data)->ulist;
1298     else
1299 	saved_ulist = NULL;
1300 
1301     /* skip the checks if there's nothing to do */
1302     if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
1303 	return err;
1304 
1305     /* run any pre-commit checks */
1306     n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
1307                     saved_ulist);
1308     if (n > 0)
1309     {
1310 	error (0, 0, "Pre-commit check failed");
1311 	err += n;
1312     }
1313 
1314     return err;
1315 }
1316 
1317 
1318 
1319 /*
1320  * Do the work of committing a file
1321  */
1322 static int maxrev;
1323 static char *sbranch;
1324 
1325 /* ARGSUSED */
1326 static int
1327 commit_fileproc (void *callerdat, struct file_info *finfo)
1328 {
1329     Node *p;
1330     int err = 0;
1331     List *ulist, *cilist;
1332     struct commit_info *ci;
1333 
1334     /* Keep track of whether write_dirtag is a branch tag.
1335        Note that if it is a branch tag in some files and a nonbranch tag
1336        in others, treat it as a nonbranch tag.  It is possible that case
1337        should elicit a warning or an error.  */
1338     if (write_dirtag != NULL
1339 	&& finfo->rcs != NULL)
1340     {
1341 	char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
1342 	if (rev != NULL
1343 	    && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
1344 	    write_dirnonbranch = 1;
1345 	if (rev != NULL)
1346 	    free (rev);
1347     }
1348 
1349     if (finfo->update_dir[0] == '\0')
1350 	p = findnode (mulist, ".");
1351     else
1352 	p = findnode (mulist, finfo->update_dir);
1353 
1354     /*
1355      * if p is null, there were file type command line args which were
1356      * all up-to-date so nothing really needs to be done
1357      */
1358     if (p == NULL)
1359 	return 0;
1360     ulist = ((struct master_lists *) p->data)->ulist;
1361     cilist = ((struct master_lists *) p->data)->cilist;
1362 
1363     /*
1364      * At this point, we should have the commit message unless we were called
1365      * with files as args from the command line.  In that latter case, we
1366      * need to get the commit message ourselves
1367      */
1368     if (!got_message)
1369     {
1370 	got_message = 1;
1371 	if (!server_active && use_editor)
1372 	    do_editor (finfo->update_dir, &saved_message,
1373 		       finfo->repository, ulist);
1374 	do_verify (&saved_message, finfo->repository, ulist);
1375     }
1376 
1377     p = findnode (cilist, finfo->file);
1378     if (p == NULL)
1379 	return 0;
1380 
1381     ci = p->data;
1382 
1383 /* cvsacl patch */
1384 #ifdef SERVER_SUPPORT
1385     if (use_cvs_acl /* && server_active */)
1386     {
1387 	int whichperm;
1388 	if (ci->status == T_MODIFIED)
1389 	    whichperm = 3;
1390 	else if (ci->status == T_ADDED)
1391 	    whichperm = 6;
1392 	else if (ci->status == T_REMOVED)
1393 	    whichperm = 7;
1394 
1395 	if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm,
1396 			     NULL, NULL, 1))
1397 	{
1398 	    if (stop_at_first_permission_denied)
1399 		error (1, 0, "permission denied for %s",
1400 		       Short_Repository (finfo->repository));
1401 	    else
1402 		error (0, 0, "permission denied for %s/%s",
1403 		       Short_Repository (finfo->repository), finfo->file);
1404 
1405 		return (0);
1406 	}
1407     }
1408 #endif
1409 
1410     if (ci->status == T_MODIFIED)
1411     {
1412 	if (finfo->rcs == NULL)
1413 	    error (1, 0, "internal error: no parsed RCS file");
1414 	if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
1415 		      finfo->repository) != 0)
1416 	{
1417 	    unlockrcs (finfo->rcs);
1418 	    err = 1;
1419 	    goto out;
1420 	}
1421     }
1422     else if (ci->status == T_ADDED)
1423     {
1424 	if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
1425 			  &finfo->rcs) != 0)
1426 	{
1427 	    if (finfo->rcs != NULL)
1428 		fixaddfile (finfo->rcs->path);
1429 	    err = 1;
1430 	    goto out;
1431 	}
1432 
1433 	/* adding files with a tag, now means adding them on a branch.
1434 	   Since the branch test was done in check_fileproc for
1435 	   modified files, we need to stub it in again here. */
1436 
1437 	if (ci->tag
1438 
1439 	    /* If numeric, it is on the trunk; check_fileproc enforced
1440 	       this.  */
1441 	    && !isdigit ((unsigned char) ci->tag[0]))
1442 	{
1443 	    if (finfo->rcs == NULL)
1444 		error (1, 0, "internal error: no parsed RCS file");
1445 	    if (ci->rev)
1446 		free (ci->rev);
1447 	    ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
1448 	    err = Checkin ('A', finfo, ci->rev,
1449 			   ci->tag, ci->options, saved_message);
1450 	    if (err != 0)
1451 	    {
1452 		unlockrcs (finfo->rcs);
1453 		fixbranch (finfo->rcs, sbranch);
1454 	    }
1455 
1456 	    (void) time (&last_register_time);
1457 
1458 	    ci->status = T_UPTODATE;
1459 	}
1460     }
1461 
1462     /*
1463      * Add the file for real
1464      */
1465     if (ci->status == T_ADDED)
1466     {
1467 	char *xrev = NULL;
1468 
1469 	if (ci->rev == NULL)
1470 	{
1471 	    /* find the max major rev number in this directory */
1472 	    maxrev = 0;
1473 	    (void) walklist (finfo->entries, findmaxrev, NULL);
1474 	    if (finfo->rcs->head)
1475 	    {
1476 		/* resurrecting: include dead revision */
1477 		int thisrev = atoi (finfo->rcs->head);
1478 		if (thisrev > maxrev)
1479 		    maxrev = thisrev;
1480 	    }
1481 	    if (maxrev == 0)
1482 		maxrev = 1;
1483 	    xrev = Xasprintf ("%d", maxrev);
1484 	}
1485 
1486 	/* XXX - an added file with symbolic -r should add tag as well */
1487 	err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
1488 	if (xrev)
1489 	    free (xrev);
1490     }
1491     else if (ci->status == T_MODIFIED)
1492     {
1493 	err = Checkin ('M', finfo, ci->rev, ci->tag,
1494 		       ci->options, saved_message);
1495 
1496 	(void) time (&last_register_time);
1497 
1498 	if (err != 0)
1499 	{
1500 	    unlockrcs (finfo->rcs);
1501 	    fixbranch (finfo->rcs, sbranch);
1502 	}
1503     }
1504     else if (ci->status == T_REMOVED)
1505     {
1506 	err = remove_file (finfo, ci->tag, saved_message);
1507 #ifdef SERVER_SUPPORT
1508 	if (server_active)
1509 	{
1510 	    server_scratch_entry_only ();
1511 	    server_updated (finfo,
1512 			    NULL,
1513 
1514 			    /* Doesn't matter, it won't get checked.  */
1515 			    SERVER_UPDATED,
1516 
1517 			    (mode_t) -1,
1518 			    NULL,
1519 			    NULL);
1520 	}
1521 #endif
1522     }
1523 
1524     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
1525        about T_ADDED or T_REMOVED.  */
1526     notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
1527 	       finfo->repository);
1528 
1529 out:
1530     if (err != 0)
1531     {
1532 	/* on failure, remove the file from ulist */
1533 	p = findnode (ulist, finfo->file);
1534 	if (p)
1535 	    delnode (p);
1536     }
1537     else
1538     {
1539 	/* On success, retrieve the new version number of the file and
1540            copy it into the log information (see logmsg.c
1541            (logfile_write) for more details).  We should only update
1542            the version number for files that have been added or
1543            modified but not removed since classify_file_internal
1544            will return the version number of a file even after it has
1545            been removed from the archive, which is not the behavior we
1546            want for our commitlog messages; we want the old version
1547            number and then "NONE." */
1548 
1549 	if (ci->status != T_REMOVED)
1550 	{
1551 	    p = findnode (ulist, finfo->file);
1552 	    if (p)
1553 	    {
1554 		Vers_TS *vers;
1555 		struct logfile_info *li;
1556 
1557 		(void) classify_file_internal (finfo, &vers);
1558 		li = p->data;
1559 		li->rev_new = xstrdup (vers->vn_rcs);
1560 		freevers_ts (&vers);
1561 	    }
1562 	}
1563     }
1564     if (SIG_inCrSect ())
1565 	SIG_endCrSect ();
1566 
1567     return err;
1568 }
1569 
1570 
1571 
1572 /*
1573  * Log the commit and clean up the update list
1574  */
1575 /* ARGSUSED */
1576 static int
1577 commit_filesdoneproc (void *callerdat, int err, const char *repository,
1578                       const char *update_dir, List *entries)
1579 {
1580     Node *p;
1581     List *ulist;
1582 
1583     assert (repository);
1584 
1585     p = findnode (mulist, update_dir);
1586     if (p == NULL)
1587 	return err;
1588 
1589     ulist = ((struct master_lists *) p->data)->ulist;
1590 
1591     got_message = 0;
1592 
1593     /* Build the administrative files if necessary.  */
1594     {
1595 	const char *p;
1596 
1597 	if (strncmp (current_parsed_root->directory, repository,
1598 		     strlen (current_parsed_root->directory)) != 0)
1599 	    error (0, 0,
1600 		 "internal error: repository (%s) doesn't begin with root (%s)",
1601 		   repository, current_parsed_root->directory);
1602 	p = repository + strlen (current_parsed_root->directory);
1603 	if (*p == '/')
1604 	    ++p;
1605 	if (strcmp ("CVSROOT", p) == 0
1606 	    /* Check for subdirectories because people may want to create
1607 	       subdirectories and list files therein in checkoutlist.  */
1608 	    || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
1609 	    )
1610 	{
1611 	    /* "Database" might a little bit grandiose and/or vague,
1612 	       but "checked-out copies of administrative files, unless
1613 	       in the case of modules and you are using ndbm in which
1614 	       case modules.{pag,dir,db}" is verbose and excessively
1615 	       focused on how the database is implemented.  */
1616 
1617 	    /* mkmodules requires the absolute name of the CVSROOT directory.
1618 	       Remove anything after the `CVSROOT' component -- this is
1619 	       necessary when committing in a subdirectory of CVSROOT.  */
1620 	    char *admin_dir = xstrdup (repository);
1621 	    int cvsrootlen = strlen ("CVSROOT");
1622 	    assert (admin_dir[p - repository + cvsrootlen] == '\0'
1623 		    || admin_dir[p - repository + cvsrootlen] == '/');
1624 	    admin_dir[p - repository + cvsrootlen] = '\0';
1625 
1626 	    if (!really_quiet)
1627 	    {
1628 		cvs_output (program_name, 0);
1629 		cvs_output (" ", 1);
1630 		cvs_output (cvs_cmd_name, 0);
1631 		cvs_output (": Rebuilding administrative file database\n", 0);
1632 	    }
1633 	    mkmodules (admin_dir);
1634 	    free (admin_dir);
1635 	    WriteTemplate (".", 1, repository);
1636 	}
1637     }
1638 
1639     /* FIXME: This used to be above the block above.  The advantage of being
1640      * here is that it is not called until after all possible writes from this
1641      * process are complete.  The disadvantage is that a fatal error during
1642      * update of CVSROOT can prevent the loginfo script from being called.
1643      *
1644      * A more general solution I have been considering is calling a generic
1645      * "postwrite" hook from the remove write lock routine.
1646      */
1647     Update_Logfile (repository, saved_message, NULL, ulist);
1648 
1649     return err;
1650 }
1651 
1652 
1653 
1654 /*
1655  * Get the log message for a dir
1656  */
1657 /* ARGSUSED */
1658 static Dtype
1659 commit_direntproc (void *callerdat, const char *dir, const char *repos,
1660                    const char *update_dir, List *entries)
1661 {
1662     Node *p;
1663     List *ulist;
1664     char *real_repos;
1665 
1666     if (!isdir (dir))
1667 	return R_SKIP_ALL;
1668 
1669     /* find the update list for this dir */
1670     p = findnode (mulist, update_dir);
1671     if (p != NULL)
1672 	ulist = ((struct master_lists *) p->data)->ulist;
1673     else
1674 	ulist = NULL;
1675 
1676     /* skip the files as an optimization */
1677     if (ulist == NULL || ulist->list->next == ulist->list)
1678 	return R_SKIP_FILES;
1679 
1680     /* get commit message */
1681     got_message = 1;
1682     real_repos = Name_Repository (dir, update_dir);
1683     if (!server_active && use_editor)
1684 	do_editor (update_dir, &saved_message, real_repos, ulist);
1685     do_verify (&saved_message, real_repos, ulist);
1686     free (real_repos);
1687     return R_PROCESS;
1688 }
1689 
1690 
1691 
1692 /*
1693  * Process the post-commit proc if necessary
1694  */
1695 /* ARGSUSED */
1696 static int
1697 commit_dirleaveproc (void *callerdat, const char *dir, int err,
1698                      const char *update_dir, List *entries)
1699 {
1700     /* update the per-directory tag info */
1701     /* FIXME?  Why?  The "commit examples" node of cvs.texinfo briefly
1702        mentions commit -r being sticky, but apparently in the context of
1703        this being a confusing feature!  */
1704     if (err == 0 && write_dirtag != NULL)
1705     {
1706 	char *repos = Name_Repository (NULL, update_dir);
1707 	WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
1708 		  update_dir, repos);
1709 	free (repos);
1710     }
1711 
1712     return err;
1713 }
1714 
1715 
1716 
1717 /*
1718  * find the maximum major rev number in an entries file
1719  */
1720 static int
1721 findmaxrev (Node *p, void *closure)
1722 {
1723     int thisrev;
1724     Entnode *entdata = p->data;
1725 
1726     if (entdata->type != ENT_FILE)
1727 	return 0;
1728     thisrev = atoi (entdata->version);
1729     if (thisrev > maxrev)
1730 	maxrev = thisrev;
1731     return 0;
1732 }
1733 
1734 /*
1735  * Actually remove a file by moving it to the attic
1736  * XXX - if removing a ,v file that is a relative symbolic link to
1737  * another ,v file, we probably should add a ".." component to the
1738  * link to keep it relative after we move it into the attic.
1739 
1740    Return value is 0 on success, or >0 on error (in which case we have
1741    printed an error message).  */
1742 static int
1743 remove_file (struct file_info *finfo, char *tag, char *message)
1744 {
1745     int retcode;
1746 
1747     int branch;
1748     int lockflag;
1749     char *corev;
1750     char *rev;
1751     char *prev_rev;
1752     char *old_path;
1753 
1754     corev = NULL;
1755     rev = NULL;
1756     prev_rev = NULL;
1757 
1758     retcode = 0;
1759 
1760     if (finfo->rcs == NULL)
1761 	error (1, 0, "internal error: no parsed RCS file");
1762 
1763     branch = 0;
1764     if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
1765     {
1766 	/* a symbolic tag is specified; just remove the tag from the file */
1767 	if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
1768 	{
1769 	    if (!quiet)
1770 		error (0, retcode == -1 ? errno : 0,
1771 		       "failed to remove tag `%s' from `%s'", tag,
1772 		       finfo->fullname);
1773 	    return 1;
1774 	}
1775 	RCS_rewrite (finfo->rcs, NULL, NULL);
1776 	Scratch_Entry (finfo->entries, finfo->file);
1777 	return 0;
1778     }
1779 
1780     /* we are removing the file from either the head or a branch */
1781     /* commit a new, dead revision. */
1782 
1783     rev = NULL;
1784     lockflag = 1;
1785     if (branch)
1786     {
1787 	char *branchname;
1788 
1789 	rev = RCS_whatbranch (finfo->rcs, tag);
1790 	if (rev == NULL)
1791 	{
1792 	    error (0, 0, "cannot find branch \"%s\".", tag);
1793 	    return 1;
1794 	}
1795 
1796 	branchname = RCS_getbranch (finfo->rcs, rev, 1);
1797 	if (branchname == NULL)
1798 	{
1799 	    /* no revision exists on this branch.  use the previous
1800 	       revision but do not lock. */
1801 	    corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
1802 	    prev_rev = xstrdup (corev);
1803 	    lockflag = 0;
1804 	} else
1805 	{
1806 	    corev = xstrdup (rev);
1807 	    prev_rev = xstrdup (branchname);
1808 	    free (branchname);
1809 	}
1810 
1811     } else  /* Not a branch */
1812     {
1813         /* Get current head revision of file. */
1814 	prev_rev = RCS_head (finfo->rcs);
1815     }
1816 
1817     /* if removing without a tag or a branch, then make sure the default
1818        branch is the trunk. */
1819     if (!tag && !branch)
1820     {
1821         if (RCS_setbranch (finfo->rcs, NULL) != 0)
1822 	{
1823 	    error (0, 0, "cannot change branch to default for %s",
1824 		   finfo->fullname);
1825 	    return 1;
1826 	}
1827 	RCS_rewrite (finfo->rcs, NULL, NULL);
1828     }
1829 
1830     /* check something out.  Generally this is the head.  If we have a
1831        particular rev, then name it.  */
1832     retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
1833 			    NULL, NULL, RUN_TTY, NULL, NULL);
1834     if (retcode != 0)
1835     {
1836 	error (0, 0,
1837 	       "failed to check out `%s'", finfo->fullname);
1838 	return 1;
1839     }
1840 
1841     /* Except when we are creating a branch, lock the revision so that
1842        we can check in the new revision.  */
1843     if (lockflag)
1844     {
1845 	if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
1846 	    RCS_rewrite (finfo->rcs, NULL, NULL);
1847     }
1848 
1849     if (corev != NULL)
1850 	free (corev);
1851 
1852     retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
1853 			   rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1854     if (retcode	!= 0)
1855     {
1856 	if (!quiet)
1857 	    error (0, retcode == -1 ? errno : 0,
1858 		   "failed to commit dead revision for `%s'", finfo->fullname);
1859 	return 1;
1860     }
1861     /* At this point, the file has been committed as removed.  We should
1862        probably tell the history file about it  */
1863     history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository);
1864 
1865     if (rev != NULL)
1866 	free (rev);
1867 
1868     old_path = xstrdup (finfo->rcs->path);
1869     if (!branch)
1870 	RCS_setattic (finfo->rcs, 1);
1871 
1872     /* Print message that file was removed. */
1873     if (!really_quiet)
1874     {
1875 	cvs_output (old_path, 0);
1876 	cvs_output ("  <--  ", 0);
1877 	if (finfo->update_dir && strlen (finfo->update_dir))
1878 	{
1879 	    cvs_output (finfo->update_dir, 0);
1880 	    cvs_output ("/", 1);
1881 	}
1882 	cvs_output (finfo->file, 0);
1883 	cvs_output ("\nnew revision: delete; previous revision: ", 0);
1884 	cvs_output (prev_rev, 0);
1885 	cvs_output ("\n", 0);
1886     }
1887 
1888     free (prev_rev);
1889 
1890     free (old_path);
1891 
1892     Scratch_Entry (finfo->entries, finfo->file);
1893     return 0;
1894 }
1895 
1896 
1897 
1898 /*
1899  * Do the actual checkin for added files
1900  */
1901 static int
1902 finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
1903 {
1904     int ret;
1905 
1906     ret = Checkin ('A', finfo, rev, tag, options, saved_message);
1907     if (ret == 0)
1908     {
1909 	char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
1910 	if (unlink_file (tmp) < 0
1911 	    && !existence_error (errno))
1912 	    error (0, errno, "cannot remove %s", tmp);
1913 	free (tmp);
1914     }
1915     else if (finfo->rcs != NULL)
1916 	fixaddfile (finfo->rcs->path);
1917 
1918     (void) time (&last_register_time);
1919 
1920     return ret;
1921 }
1922 
1923 
1924 
1925 /*
1926  * Unlock an rcs file
1927  */
1928 static void
1929 unlockrcs (RCSNode *rcs)
1930 {
1931     int retcode;
1932 
1933     if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
1934 	error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1935 	       "could not unlock %s", rcs->path);
1936     else
1937 	RCS_rewrite (rcs, NULL, NULL);
1938 }
1939 
1940 
1941 
1942 /*
1943  * remove a partially added file.  if we can parse it, leave it alone.
1944  *
1945  * FIXME: Every caller that calls this function can access finfo->rcs (the
1946  * parsed RCSNode data), so we should be able to detect that the file needs
1947  * to be removed without reparsing the file as we do below.
1948  */
1949 static void
1950 fixaddfile (const char *rcs)
1951 {
1952     RCSNode *rcsfile;
1953     int save_really_quiet;
1954 
1955     save_really_quiet = really_quiet;
1956     really_quiet = 1;
1957     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
1958     {
1959 	if (unlink_file (rcs) < 0)
1960 	    error (0, errno, "cannot remove %s", rcs);
1961     }
1962     else
1963 	freercsnode (&rcsfile);
1964     really_quiet = save_really_quiet;
1965 }
1966 
1967 
1968 
1969 /*
1970  * put the branch back on an rcs file
1971  */
1972 static void
1973 fixbranch (RCSNode *rcs, char *branch)
1974 {
1975     int retcode;
1976 
1977     if (branch != NULL)
1978     {
1979 	if ((retcode = RCS_setbranch (rcs, branch)) != 0)
1980 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1981 		   "cannot restore branch to %s for %s", branch, rcs->path);
1982 	RCS_rewrite (rcs, NULL, NULL);
1983     }
1984 }
1985 
1986 
1987 
1988 /*
1989  * do the initial part of a file add for the named file.  if adding
1990  * with a tag, put the file in the Attic and point the symbolic tag
1991  * at the committed revision.
1992  *
1993  * INPUTS
1994  *   file	The name of the file in the workspace.
1995  *   repository	The repository directory to expect to find FILE,v in.
1996  *   tag	The name or rev num of the branch being added to, if any.
1997  *   options	Any RCS keyword expansion options specified by the user.
1998  *   rcsnode	A pointer to the pre-parsed RCSNode for this file, if the file
1999  *		exists in the repository.  If this is NULL, assume the file
2000  *		does not yet exist.
2001  *
2002  * RETURNS
2003  *   0 on success.
2004  *   1 on errors, after printing any appropriate error messages.
2005  *
2006  * ERRORS
2007  *   This function will return an error when any of the following functions do:
2008  *     add_rcs_file
2009  *     RCS_setattic
2010  *     lock_RCS
2011  *     RCS_checkin
2012  *     RCS_parse (called to verify the newly created archive file)
2013  *     RCS_settag
2014  */
2015 
2016 static int
2017 checkaddfile (const char *file, const char *repository, const char *tag,
2018               const char *options, RCSNode **rcsnode)
2019 {
2020     RCSNode *rcs;
2021     char *fname;
2022     int newfile = 0;		/* Set to 1 if we created a new RCS archive. */
2023     int retval = 1;
2024     int adding_on_branch;
2025 
2026     assert (rcsnode != NULL);
2027 
2028     /* Callers expect to be able to use either "" or NULL to mean the
2029        default keyword expansion.  */
2030     if (options != NULL && options[0] == '\0')
2031 	options = NULL;
2032     if (options != NULL)
2033 	assert (options[0] == '-' && options[1] == 'k');
2034 
2035     /* If numeric, it is on the trunk; check_fileproc enforced
2036        this.  */
2037     adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
2038 
2039     if (*rcsnode == NULL)
2040     {
2041 	char *rcsname;
2042 	char *desc = NULL;
2043 	size_t descalloc = 0;
2044 	size_t desclen = 0;
2045 	const char *opt;
2046 
2047 	if (adding_on_branch)
2048 	{
2049 	    mode_t omask;
2050 	    rcsname = xmalloc (strlen (repository)
2051 			       + sizeof (CVSATTIC)
2052 			       + strlen (file)
2053 			       + sizeof (RCSEXT)
2054 			       + 3);
2055 	    (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
2056 	    omask = umask (cvsumask);
2057 	    if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
2058 		error (1, errno, "cannot make directory `%s'", rcsname);
2059 	    (void) umask (omask);
2060 	    (void) sprintf (rcsname,
2061 			    "%s/%s/%s%s",
2062 			    repository,
2063 			    CVSATTIC,
2064 			    file,
2065 			    RCSEXT);
2066 	}
2067 	else
2068 	    rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
2069 
2070 	/* this is the first time we have ever seen this file; create
2071 	   an RCS file.  */
2072 	fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
2073 	/* If the file does not exist, no big deal.  In particular, the
2074 	   server does not (yet at least) create CVSEXT_LOG files.  */
2075 	if (isfile (fname))
2076 	    /* FIXME: Should be including update_dir in the appropriate
2077 	       place here.  */
2078 	    get_file (fname, fname, "r", &desc, &descalloc, &desclen);
2079 	free (fname);
2080 
2081 	/* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
2082 	   end of the log message if the message is nonempty.
2083 	   Do it.  RCS also deletes certain whitespace, in cleanlogmsg,
2084 	   which we don't try to do here.  */
2085 	if (desclen > 0)
2086 	{
2087 	    expand_string (&desc, &descalloc, desclen + 1);
2088 	    desc[desclen++] = '\012';
2089 	}
2090 
2091 	/* Set RCS keyword expansion options.  */
2092 	if (options != NULL)
2093 	    opt = options + 2;
2094 	else
2095 	    opt = NULL;
2096 
2097 	if (add_rcs_file (NULL, rcsname, file, NULL, opt,
2098 			  NULL, NULL, 0, NULL,
2099 			  desc, desclen, NULL, 0) != 0)
2100 	{
2101 	    if (rcsname != NULL)
2102 	        free (rcsname);
2103 	    goto out;
2104 	}
2105 	rcs = RCS_parsercsfile (rcsname);
2106 	newfile = 1;
2107 	if (rcsname != NULL)
2108 	    free (rcsname);
2109 	if (desc != NULL)
2110 	    free (desc);
2111 	*rcsnode = rcs;
2112     }
2113     else
2114     {
2115 	/* file has existed in the past.  Prepare to resurrect. */
2116 	char *rev;
2117 	char *oldexpand;
2118 
2119 	rcs = *rcsnode;
2120 
2121 	oldexpand = RCS_getexpand (rcs);
2122 	if ((oldexpand != NULL
2123 	     && options != NULL
2124 	     && strcmp (options + 2, oldexpand) != 0)
2125 	    || (oldexpand == NULL && options != NULL))
2126 	{
2127 	    /* We tell the user about this, because it means that the
2128 	       old revisions will no longer retrieve the way that they
2129 	       used to.  */
2130 	    error (0, 0, "changing keyword expansion mode to %s", options);
2131 	    RCS_setexpand (rcs, options + 2);
2132 	}
2133 
2134 	if (!adding_on_branch)
2135 	{
2136 	    /* We are adding on the trunk, so move the file out of the
2137 	       Attic.  */
2138 	    if (!(rcs->flags & INATTIC))
2139 	    {
2140 		error (0, 0, "warning: expected %s to be in Attic",
2141 		       rcs->path);
2142 	    }
2143 
2144 	    /* Begin a critical section around the code that spans the
2145 	       first commit on the trunk of a file that's already been
2146 	       committed on a branch.  */
2147 	    SIG_beginCrSect ();
2148 
2149 	    if (RCS_setattic (rcs, 0))
2150 	    {
2151 		goto out;
2152 	    }
2153 	}
2154 
2155 	rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
2156 	/* and lock it */
2157 	if (lock_RCS (file, rcs, rev, repository))
2158 	{
2159 	    error (0, 0, "cannot lock revision %s in `%s'.",
2160 		   rev ? rev : tag ? tag : "HEAD", rcs->path);
2161 	    if (rev != NULL)
2162 		free (rev);
2163 	    goto out;
2164 	}
2165 
2166 	if (rev != NULL)
2167 	    free (rev);
2168     }
2169 
2170     /* when adding a file for the first time, and using a tag, we need
2171        to create a dead revision on the trunk.  */
2172     if (adding_on_branch)
2173     {
2174 	if (newfile)
2175 	{
2176 	    char *tmp;
2177 	    FILE *fp;
2178 	    int retcode;
2179 
2180 	    /* move the new file out of the way. */
2181 	    fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2182 	    rename_file (file, fname);
2183 
2184 	    /* Create empty FILE.  Can't use copy_file with a DEVNULL
2185 	       argument -- copy_file now ignores device files. */
2186 	    fp = fopen (file, "w");
2187 	    if (fp == NULL)
2188 		error (1, errno, "cannot open %s for writing", file);
2189 	    if (fclose (fp) < 0)
2190 		error (0, errno, "cannot close %s", file);
2191 
2192 	    tmp = Xasprintf ("file %s was initially added on branch %s.",
2193 			     file, tag);
2194 	    /* commit a dead revision. */
2195 	    retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
2196 				   RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
2197 	    free (tmp);
2198 	    if (retcode != 0)
2199 	    {
2200 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2201 		       "could not create initial dead revision %s", rcs->path);
2202 		free (fname);
2203 		goto out;
2204 	    }
2205 
2206 	    /* put the new file back where it was */
2207 	    rename_file (fname, file);
2208 	    free (fname);
2209 
2210 	    /* double-check that the file was written correctly */
2211 	    freercsnode (&rcs);
2212 	    rcs = RCS_parse (file, repository);
2213 	    if (rcs == NULL)
2214 	    {
2215 		error (0, 0, "could not read %s", rcs->path);
2216 		goto out;
2217 	    }
2218 	    *rcsnode = rcs;
2219 
2220 	    /* and lock it once again. */
2221 	    if (lock_RCS (file, rcs, NULL, repository))
2222 	    {
2223 		error (0, 0, "cannot lock initial revision in `%s'.",
2224 		       rcs->path);
2225 		goto out;
2226 	    }
2227 	}
2228 
2229 	/* when adding with a tag, we need to stub a branch, if it
2230 	   doesn't already exist.  */
2231 	if (!RCS_nodeisbranch (rcs, tag))
2232 	{
2233 	    /* branch does not exist.  Stub it.  */
2234 	    char *head;
2235 	    char *magicrev;
2236 	    int retcode;
2237 	    time_t headtime = -1;
2238 	    char *revnum, *tmp;
2239 	    FILE *fp;
2240 	    time_t t = -1;
2241 	    struct tm *ct;
2242 
2243 	    fixbranch (rcs, sbranch);
2244 
2245 	    head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
2246 	    if (!head)
2247 		error (1, 0, "No head revision in archive file `%s'.",
2248 		       rcs->print_path);
2249 	    magicrev = RCS_magicrev (rcs, head);
2250 
2251 	    /* If this is not a new branch, then we will want a dead
2252 	       version created before this one. */
2253 	    if (!newfile)
2254 		headtime = RCS_getrevtime (rcs, head, 0, 0);
2255 
2256 	    retcode = RCS_settag (rcs, tag, magicrev);
2257 	    RCS_rewrite (rcs, NULL, NULL);
2258 
2259 	    free (head);
2260 	    free (magicrev);
2261 
2262 	    if (retcode != 0)
2263 	    {
2264 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2265 		       "could not stub branch %s for %s", tag, rcs->path);
2266 		goto out;
2267 	    }
2268 	    /* We need to add a dead version here to avoid -rtag -Dtime
2269 	       checkout problems between when the head version was
2270 	       created and now. */
2271 	    if (!newfile && headtime != -1)
2272 	    {
2273 		/* move the new file out of the way. */
2274 		fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2275 		rename_file (file, fname);
2276 
2277 		/* Create empty FILE.  Can't use copy_file with a DEVNULL
2278 		   argument -- copy_file now ignores device files. */
2279 		fp = fopen (file, "w");
2280 		if (fp == NULL)
2281 		    error (1, errno, "cannot open %s for writing", file);
2282 		if (fclose (fp) < 0)
2283 		    error (0, errno, "cannot close %s", file);
2284 
2285 		/* As we will be hacking the delta date, put the time
2286 		   this was added into the log message. */
2287 		t = time (NULL);
2288 		ct = gmtime (&t);
2289 		tmp = Xasprintf ("file %s was added on branch %s on %d-%02d-%02d %02d:%02d:%02d +0000",
2290 				 file, tag,
2291 				 ct->tm_year + (ct->tm_year < 100 ? 0 : 1900),
2292 				 ct->tm_mon + 1, ct->tm_mday,
2293 				 ct->tm_hour, ct->tm_min, ct->tm_sec);
2294 
2295 		/* commit a dead revision. */
2296 		revnum = RCS_whatbranch (rcs, tag);
2297 		retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
2298 				       RCS_FLAGS_DEAD |
2299 				       RCS_FLAGS_QUIET |
2300 				       RCS_FLAGS_USETIME);
2301 		free (revnum);
2302 		free (tmp);
2303 
2304 		if (retcode != 0)
2305 		{
2306 		    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2307 			   "could not created dead stub %s for %s", tag,
2308 			   rcs->path);
2309 		    goto out;
2310 		}
2311 
2312 		/* put the new file back where it was */
2313 		rename_file (fname, file);
2314 		free (fname);
2315 
2316 		/* double-check that the file was written correctly */
2317 		freercsnode (&rcs);
2318 		rcs = RCS_parse (file, repository);
2319 		if (rcs == NULL)
2320 		{
2321 		    error (0, 0, "could not read %s", rcs->path);
2322 		    goto out;
2323 		}
2324 		*rcsnode = rcs;
2325 	    }
2326 	}
2327 	else
2328 	{
2329 	    /* lock the branch. (stubbed branches need not be locked.)  */
2330 	    if (lock_RCS (file, rcs, NULL, repository))
2331 	    {
2332 		error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
2333 		goto out;
2334 	    }
2335 	}
2336 
2337 	if (*rcsnode != rcs)
2338 	{
2339 	    freercsnode (rcsnode);
2340 	    *rcsnode = rcs;
2341 	}
2342     }
2343 
2344     fileattr_newfile (file);
2345 
2346     /* At this point, we used to set the file mode of the RCS file
2347        based on the mode of the file in the working directory.  If we
2348        are creating the RCS file for the first time, add_rcs_file does
2349        this already.  If we are re-adding the file, then perhaps it is
2350        consistent to preserve the old file mode, just as we preserve
2351        the old keyword expansion mode.
2352 
2353        If we decide that we should change the modes, then we can't do
2354        it here anyhow.  At this point, the RCS file may be owned by
2355        somebody else, so a chmod will fail.  We need to instead do the
2356        chmod after rewriting it.
2357 
2358        FIXME: In general, I think the file mode (and the keyword
2359        expansion mode) should be associated with a particular revision
2360        of the file, so that it is possible to have different revisions
2361        of a file have different modes.  */
2362 
2363     retval = 0;
2364 
2365  out:
2366     if (retval != 0 && SIG_inCrSect ())
2367 	SIG_endCrSect ();
2368     return retval;
2369 }
2370 
2371 
2372 
2373 /*
2374  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
2375  * couldn't.  If the RCS file currently has a branch as the head, we must
2376  * move the head back to the trunk before locking the file, and be sure to
2377  * put the branch back as the head if there are any errors.
2378  */
2379 static int
2380 lock_RCS (const char *user, RCSNode *rcs, const char *rev,
2381           const char *repository)
2382 {
2383     char *branch = NULL;
2384     int err = 0;
2385 
2386     /*
2387      * For a specified, numeric revision of the form "1" or "1.1", (or when
2388      * no revision is specified ""), definitely move the branch to the trunk
2389      * before locking the RCS file.
2390      *
2391      * The assumption is that if there is more than one revision on the trunk,
2392      * the head points to the trunk, not a branch... and as such, it's not
2393      * necessary to move the head in this case.
2394      */
2395     if (rev == NULL
2396 	|| (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
2397     {
2398 	branch = xstrdup (rcs->branch);
2399 	if (branch != NULL)
2400 	{
2401 	    if (RCS_setbranch (rcs, NULL) != 0)
2402 	    {
2403 		error (0, 0, "cannot change branch to default for %s",
2404 		       rcs->path);
2405 		if (branch)
2406 		    free (branch);
2407 		return 1;
2408 	    }
2409 	}
2410 	err = RCS_lock (rcs, NULL, 1);
2411     }
2412     else
2413     {
2414 	RCS_lock (rcs, rev, 1);
2415     }
2416 
2417     /* We used to call RCS_rewrite here, and that might seem
2418        appropriate in order to write out the locked revision
2419        information.  However, such a call would actually serve no
2420        purpose.  CVS locks will prevent any interference from other
2421        CVS processes.  The comment above rcs_internal_lockfile
2422        explains that it is already unsafe to use RCS and CVS
2423        simultaneously.  It follows that writing out the locked
2424        revision information here would add no additional security.
2425 
2426        If we ever do care about it, the proper fix is to create the
2427        RCS lock file before calling this function, and maintain it
2428        until the checkin is complete.
2429 
2430        The call to RCS_lock is still required at present, since in
2431        some cases RCS_checkin will determine which revision to check
2432        in by looking for a lock.  FIXME: This is rather roundabout,
2433        and a more straightforward approach would probably be easier to
2434        understand.  */
2435 
2436     if (err == 0)
2437     {
2438 	if (sbranch != NULL)
2439 	    free (sbranch);
2440 	sbranch = branch;
2441 	return 0;
2442     }
2443 
2444     /* try to restore the branch if we can on error */
2445     if (branch != NULL)
2446 	fixbranch (rcs, branch);
2447 
2448     if (branch)
2449 	free (branch);
2450     return 1;
2451 }
2452 
2453 
2454 
2455 /*
2456  * free an UPDATE node's data
2457  */
2458 void
2459 update_delproc (Node *p)
2460 {
2461     struct logfile_info *li = p->data;
2462 
2463     if (li->tag)
2464 	free (li->tag);
2465     if (li->rev_old)
2466 	free (li->rev_old);
2467     if (li->rev_new)
2468 	free (li->rev_new);
2469     free (li);
2470 }
2471 
2472 /*
2473  * Free the commit_info structure in p.
2474  */
2475 static void
2476 ci_delproc (Node *p)
2477 {
2478     struct commit_info *ci = p->data;
2479 
2480     if (ci->rev)
2481 	free (ci->rev);
2482     if (ci->tag)
2483 	free (ci->tag);
2484     if (ci->options)
2485 	free (ci->options);
2486     free (ci);
2487 }
2488 
2489 /*
2490  * Free the commit_info structure in p.
2491  */
2492 static void
2493 masterlist_delproc (Node *p)
2494 {
2495     struct master_lists *ml = p->data;
2496 
2497     dellist (&ml->ulist);
2498     dellist (&ml->cilist);
2499     free (ml);
2500 }
2501