xref: /openbsd-src/gnu/usr.bin/cvs/src/commit.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  *
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS 1.4 kit.
7  *
8  * Commit Files
9  *
10  * "commit" commits the present version to the RCS repository, AFTER
11  * having done a test on conflicts.
12  *
13  * The call is: cvs commit [options] files...
14  *
15  */
16 
17 #include <assert.h>
18 #include "cvs.h"
19 #include "getline.h"
20 #include "edit.h"
21 #include "fileattr.h"
22 
23 static Dtype check_direntproc PROTO ((void *callerdat, char *dir,
24 				      char *repos, char *update_dir,
25 				      List *entries));
26 static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
27 static int check_filesdoneproc PROTO ((void *callerdat, int err,
28 				       char *repos, char *update_dir,
29 				       List *entries));
30 static int checkaddfile PROTO((char *file, char *repository, char *tag,
31 			       char *options, RCSNode **rcsnode));
32 static Dtype commit_direntproc PROTO ((void *callerdat, char *dir,
33 				       char *repos, char *update_dir,
34 				       List *entries));
35 static int commit_dirleaveproc PROTO ((void *callerdat, char *dir,
36 				       int err, char *update_dir,
37 				       List *entries));
38 static int commit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
39 static int commit_filesdoneproc PROTO ((void *callerdat, int err,
40 					char *repository, char *update_dir,
41 					List *entries));
42 static int finaladd PROTO((struct file_info *finfo, char *revision, char *tag,
43 			   char *options));
44 static int findmaxrev PROTO((Node * p, void *closure));
45 static int lock_RCS PROTO((char *user, RCSNode *rcs, char *rev,
46 			   char *repository));
47 static int precommit_list_proc PROTO((Node * p, void *closure));
48 static int precommit_proc PROTO((char *repository, char *filter));
49 static int remove_file PROTO ((struct file_info *finfo, char *tag,
50 			       char *message));
51 static void fix_rcs_modes PROTO((char *rcs, char *user));
52 static void fixaddfile PROTO((char *file, char *repository));
53 static void fixbranch PROTO((RCSNode *, char *branch));
54 static void unlockrcs PROTO((RCSNode *rcs));
55 static void ci_delproc PROTO((Node *p));
56 static void masterlist_delproc PROTO((Node *p));
57 static void locate_rcs PROTO((char *file, char *repository, char *rcs));
58 
59 struct commit_info
60 {
61     Ctype status;			/* as returned from Classify_File() */
62     char *rev;				/* a numeric rev, if we know it */
63     char *tag;				/* any sticky tag, or -r option */
64     char *options;			/* Any sticky -k option */
65 };
66 struct master_lists
67 {
68     List *ulist;			/* list for Update_Logfile */
69     List *cilist;			/* list with commit_info structs */
70 };
71 
72 static int force_ci = 0;
73 static int got_message;
74 static int run_module_prog = 1;
75 static int aflag;
76 static char *tag;
77 static char *write_dirtag;
78 static char *logfile;
79 static List *mulist;
80 static char *message;
81 static time_t last_register_time;
82 
83 
84 static const char *const commit_usage[] =
85 {
86     "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n",
87     "\t-n\tDo not run the module program (if any).\n",
88     "\t-R\tProcess directories recursively.\n",
89     "\t-l\tLocal directory only (not recursive).\n",
90     "\t-f\tForce the file to be committed; disables recursion.\n",
91     "\t-F file\tRead the log message from file.\n",
92     "\t-m msg\tLog message.\n",
93     "\t-r rev\tCommit to this branch or trunk revision.\n",
94     NULL
95 };
96 
97 #ifdef CLIENT_SUPPORT
98 /* Identify a file which needs "? foo" or a Questionable request.  */
99 struct question {
100     /* The two fields for the Directory request.  */
101     char *dir;
102     char *repos;
103 
104     /* The file name.  */
105     char *file;
106 
107     struct question *next;
108 };
109 
110 struct find_data {
111     List *ulist;
112     int argc;
113     char **argv;
114 
115     /* This is used from dirent to filesdone time, for each directory,
116        to make a list of files we have already seen.  */
117     List *ignlist;
118 
119     /* Linked list of files which need "? foo" or a Questionable request.  */
120     struct question *questionables;
121 
122     /* Only good within functions called from the filesdoneproc.  Stores
123        the repository (pointer into storage managed by the recursion
124        processor.  */
125     char *repository;
126 };
127 
128 static Dtype find_dirent_proc PROTO ((void *callerdat, char *dir,
129 				      char *repository, char *update_dir,
130 				      List *entries));
131 
132 static Dtype
133 find_dirent_proc (callerdat, dir, repository, update_dir, entries)
134     void *callerdat;
135     char *dir;
136     char *repository;
137     char *update_dir;
138     List *entries;
139 {
140     struct find_data *find_data = (struct find_data *)callerdat;
141 
142     /* initialize the ignore list for this directory */
143     find_data->ignlist = getlist ();
144     return R_PROCESS;
145 }
146 
147 /* Here as a static until we get around to fixing ignore_files to pass
148    it along as an argument.  */
149 static struct find_data *find_data_static;
150 
151 static void find_ignproc PROTO ((char *, char *));
152 
153 static void
154 find_ignproc (file, dir)
155     char *file;
156     char *dir;
157 {
158     struct question *p;
159 
160     p = (struct question *) xmalloc (sizeof (struct question));
161     p->dir = xstrdup (dir);
162     p->repos = xstrdup (find_data_static->repository);
163     p->file = xstrdup (file);
164     p->next = find_data_static->questionables;
165     find_data_static->questionables = p;
166 }
167 
168 static int find_filesdoneproc PROTO ((void *callerdat, int err,
169 				      char *repository, char *update_dir,
170 				      List *entries));
171 
172 static int
173 find_filesdoneproc (callerdat, err, repository, update_dir, entries)
174     void *callerdat;
175     int err;
176     char *repository;
177     char *update_dir;
178     List *entries;
179 {
180     struct find_data *find_data = (struct find_data *)callerdat;
181     find_data->repository = repository;
182 
183     /* if this directory has an ignore list, process it then free it */
184     if (find_data->ignlist)
185     {
186 	find_data_static = find_data;
187 	ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
188 	dellist (&find_data->ignlist);
189     }
190 
191     find_data->repository = NULL;
192 
193     return err;
194 }
195 
196 static int find_fileproc PROTO ((void *callerdat, struct file_info *finfo));
197 
198 /* Machinery to find out what is modified, added, and removed.  It is
199    possible this should be broken out into a new client_classify function;
200    merging it with classify_file is almost sure to be a mess, though,
201    because classify_file has all kinds of repository processing.  */
202 static int
203 find_fileproc (callerdat, finfo)
204     void *callerdat;
205     struct file_info *finfo;
206 {
207     Vers_TS *vers;
208     enum classify_type status;
209     Node *node;
210     struct find_data *args = (struct find_data *)callerdat;
211     struct logfile_info *data;
212     struct file_info xfinfo;
213 
214     /* if this directory has an ignore list, add this file to it */
215     if (args->ignlist)
216     {
217 	Node *p;
218 
219 	p = getnode ();
220 	p->type = FILES;
221 	p->key = xstrdup (finfo->file);
222 	if (addnode (args->ignlist, p) != 0)
223 	    freenode (p);
224     }
225 
226     xfinfo = *finfo;
227     xfinfo.repository = NULL;
228     xfinfo.rcs = NULL;
229 
230     vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
231     if (vers->ts_user == NULL
232 	&& vers->vn_user != NULL
233 	&& vers->vn_user[0] == '-')
234 	/* FIXME: If vn_user is starts with "-" but ts_user is
235 	   non-NULL, what classify_file does is print "%s should be
236 	   removed and is still there".  I'm not sure what it does
237 	   then.  We probably should do the same.  */
238 	status = T_REMOVED;
239     else if (vers->vn_user == NULL)
240     {
241 	if (vers->ts_user == NULL)
242 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
243 	else
244 	    error (0, 0, "use `cvs add' to create an entry for %s",
245 		   finfo->fullname);
246 	return 1;
247     }
248     else if (vers->ts_user != NULL
249 	     && vers->vn_user != NULL
250 	     && vers->vn_user[0] == '0')
251 	/* FIXME: If vn_user is "0" but ts_user is NULL, what classify_file
252 	   does is print "new-born %s has disappeared" and removes the entry.
253 	   We probably should do the same.  */
254 	status = T_ADDED;
255     else if (vers->ts_user != NULL
256 	     && vers->ts_rcs != NULL
257 	     && strcmp (vers->ts_user, vers->ts_rcs) != 0)
258 	status = T_MODIFIED;
259     else
260     {
261 	/* This covers unmodified files, as well as a variety of other
262 	   cases.  FIXME: we probably should be printing a message and
263 	   returning 1 for many of those cases (but I'm not sure
264 	   exactly which ones).  */
265 	return 0;
266     }
267 
268     node = getnode ();
269     node->key = xstrdup (finfo->fullname);
270 
271     data = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
272     data->type = status;
273     data->tag = xstrdup (vers->tag);
274 
275     node->type = UPDATE;
276     node->delproc = update_delproc;
277     node->data = (char *) data;
278     (void)addnode (args->ulist, node);
279 
280     ++args->argc;
281 
282     freevers_ts (&vers);
283     return 0;
284 }
285 
286 static int copy_ulist PROTO ((Node *, void *));
287 
288 static int
289 copy_ulist (node, data)
290     Node *node;
291     void *data;
292 {
293     struct find_data *args = (struct find_data *)data;
294     args->argv[args->argc++] = node->key;
295     return 0;
296 }
297 #endif /* CLIENT_SUPPORT */
298 
299 int
300 commit (argc, argv)
301     int argc;
302     char **argv;
303 {
304     int c;
305     int err = 0;
306     int local = 0;
307 
308     if (argc == -1)
309 	usage (commit_usage);
310 
311 #ifdef CVS_BADROOT
312     /*
313      * For log purposes, do not allow "root" to commit files.  If you look
314      * like root, but are really logged in as a non-root user, it's OK.
315      */
316     if (geteuid () == (uid_t) 0)
317     {
318 	struct passwd *pw;
319 
320 	if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL)
321 	    error (1, 0, "you are unknown to this system");
322 	if (pw->pw_uid == (uid_t) 0)
323 	    error (1, 0, "cannot commit files as 'root'");
324     }
325 #endif /* CVS_BADROOT */
326 
327     optind = 1;
328     while ((c = getopt (argc, argv, "nlRm:fF:r:")) != -1)
329     {
330 	switch (c)
331 	{
332 	    case 'n':
333 		run_module_prog = 0;
334 		break;
335 	    case 'm':
336 #ifdef FORCE_USE_EDITOR
337 		use_editor = TRUE;
338 #else
339 		use_editor = FALSE;
340 #endif
341 		if (message)
342 		{
343 		    free (message);
344 		    message = NULL;
345 		}
346 
347 		message = xstrdup(optarg);
348 		break;
349 	    case 'r':
350 		if (tag)
351 		    free (tag);
352 		tag = xstrdup (optarg);
353 		break;
354 	    case 'l':
355 		local = 1;
356 		break;
357 	    case 'R':
358 		local = 0;
359 		break;
360 	    case 'f':
361 		force_ci = 1;
362 		local = 1;		/* also disable recursion */
363 		break;
364 	    case 'F':
365 #ifdef FORCE_USE_EDITOR
366 		use_editor = TRUE;
367 #else
368 		use_editor = FALSE;
369 #endif
370 		logfile = optarg;
371 		break;
372 	    case '?':
373 	    default:
374 		usage (commit_usage);
375 		break;
376 	}
377     }
378     argc -= optind;
379     argv += optind;
380 
381     /* numeric specified revision means we ignore sticky tags... */
382     if (tag && isdigit (*tag))
383     {
384 	aflag = 1;
385 	/* strip trailing dots */
386 	while (tag[strlen (tag) - 1] == '.')
387 	    tag[strlen (tag) - 1] = '\0';
388     }
389 
390     /* some checks related to the "-F logfile" option */
391     if (logfile)
392     {
393 	int n, logfd;
394 	struct stat statbuf;
395 
396 	if (message)
397 	    error (1, 0, "cannot specify both a message and a log file");
398 
399 	/* FIXME: Why is this binary?  Needs more investigation.  */
400 	if ((logfd = CVS_OPEN (logfile, O_RDONLY | OPEN_BINARY)) < 0)
401 	    error (1, errno, "cannot open log file %s", logfile);
402 
403 	if (fstat(logfd, &statbuf) < 0)
404 	    error (1, errno, "cannot find size of log file %s", logfile);
405 
406 	message = xmalloc (statbuf.st_size + 1);
407 
408 	/* FIXME: Should keep reading until EOF, rather than assuming the
409 	   first read gets the whole thing.  */
410 	if ((n = read (logfd, message, statbuf.st_size + 1)) < 0)
411 	    error (1, errno, "cannot read log message from %s", logfile);
412 
413 	(void) close (logfd);
414 	message[n] = '\0';
415     }
416 
417 #ifdef CLIENT_SUPPORT
418     if (client_active)
419     {
420 	struct find_data find_args;
421 
422 	ign_setup ();
423 
424 	find_args.ulist = getlist ();
425 	find_args.argc = 0;
426 	find_args.questionables = NULL;
427 	find_args.ignlist = NULL;
428 	find_args.repository = NULL;
429 
430 	err = start_recursion (find_fileproc, find_filesdoneproc,
431 			       find_dirent_proc, (DIRLEAVEPROC) NULL,
432 			       (void *)&find_args,
433 			       argc, argv, local, W_LOCAL, 0, 0,
434 			       (char *)NULL, 0);
435 	if (err)
436 	    error (1, 0, "correct above errors first!");
437 
438 	if (find_args.argc == 0)
439 	    /* Nothing to commit.  Exit now without contacting the
440 	       server (note that this means that we won't print "?
441 	       foo" for files which merit it, because we don't know
442 	       what is in the CVSROOT/cvsignore file).  */
443 	    return 0;
444 
445 	/* Now we keep track of which files we actually are going to
446 	   operate on, and only work with those files in the future.
447 	   This saves time--we don't want to search the file system
448 	   of the working directory twice.  */
449 	find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **));
450 	find_args.argc = 0;
451 	walklist (find_args.ulist, copy_ulist, &find_args);
452 
453 	/* Do this before calling do_editor; don't ask for a log
454 	   message if we can't talk to the server.  But do it after we
455 	   have made the checks that we can locally (to more quickly
456 	   catch syntax errors, the case where no files are modified,
457 	   added or removed, etc.).
458 
459 	   On the other hand, calling start_server before do_editor
460 	   means that we chew up server resources the whole time that
461 	   the user has the editor open (hours or days if the user
462 	   forgets about it), which seems dubious.  */
463 	start_server ();
464 
465 	/*
466 	 * We do this once, not once for each directory as in normal CVS.
467 	 * The protocol is designed this way.  This is a feature.
468 	 */
469 	if (use_editor)
470 	    do_editor (".", &message, (char *)NULL, find_args.ulist);
471 
472 	/* We always send some sort of message, even if empty.  */
473 	option_with_arg ("-m", message);
474 
475 	/* OK, now process all the questionable files we have been saving
476 	   up.  */
477 	{
478 	    struct question *p;
479 	    struct question *q;
480 
481 	    p = find_args.questionables;
482 	    while (p != NULL)
483 	    {
484 		if (ign_inhibit_server || !supported_request ("Questionable"))
485 		{
486 		    cvs_output ("? ", 2);
487 		    if (p->dir[0] != '\0')
488 		    {
489 			cvs_output (p->dir, 0);
490 			cvs_output ("/", 1);
491 		    }
492 		    cvs_output (p->file, 0);
493 		    cvs_output ("\n", 1);
494 		}
495 		else
496 		{
497 		    send_to_server ("Directory ", 0);
498 		    send_to_server (p->dir[0] == '\0' ? "." : p->dir, 0);
499 		    send_to_server ("\012", 1);
500 		    send_to_server (p->repos, 0);
501 		    send_to_server ("\012", 1);
502 
503 		    send_to_server ("Questionable ", 0);
504 		    send_to_server (p->file, 0);
505 		    send_to_server ("\012", 1);
506 		}
507 		free (p->dir);
508 		free (p->repos);
509 		free (p->file);
510 		q = p->next;
511 		free (p);
512 		p = q;
513 	    }
514 	}
515 
516 	if (local)
517 	    send_arg("-l");
518 	if (force_ci)
519 	    send_arg("-f");
520 	if (!run_module_prog)
521 	    send_arg("-n");
522 	option_with_arg ("-r", tag);
523 
524 	/* Sending only the names of the files which were modified, added,
525 	   or removed means that the server will only do an up-to-date
526 	   check on those files.  This is different from local CVS and
527 	   previous versions of client/server CVS, but it probably is a Good
528 	   Thing, or at least Not Such A Bad Thing.  */
529 	send_file_names (find_args.argc, find_args.argv, 0);
530 	send_files (find_args.argc, find_args.argv, local, 0);
531 
532 	send_to_server ("ci\012", 0);
533 	return get_responses_and_close ();
534     }
535 #endif
536 
537     if (tag != NULL)
538 	tag_check_valid (tag, argc, argv, local, aflag, "");
539 
540     /* XXX - this is not the perfect check for this */
541     if (argc <= 0)
542 	write_dirtag = tag;
543 
544     wrap_setup ();
545 
546     lock_tree_for_write (argc, argv, local, aflag);
547 
548     /*
549      * Set up the master update list
550      */
551     mulist = getlist ();
552 
553     /*
554      * Run the recursion processor to verify the files are all up-to-date
555      */
556     err = start_recursion (check_fileproc, check_filesdoneproc,
557 			   check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc,
558 			   argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1);
559     if (err)
560     {
561 	lock_tree_cleanup ();
562 	error (1, 0, "correct above errors first!");
563     }
564 
565     /*
566      * Run the recursion processor to commit the files
567      */
568     if (noexec == 0)
569 	err = start_recursion (commit_fileproc, commit_filesdoneproc,
570 			       commit_direntproc, commit_dirleaveproc, NULL,
571 			       argc, argv, local, W_LOCAL, aflag, 0,
572 			       (char *) NULL, 1);
573 
574     /*
575      * Unlock all the dirs and clean up
576      */
577     lock_tree_cleanup ();
578     dellist (&mulist);
579 
580     if (last_register_time)
581     {
582 	time_t now;
583 
584 	(void) time (&now);
585 	if (now == last_register_time)
586 	{
587 	    sleep (1);			/* to avoid time-stamp races */
588 	}
589     }
590 
591     return (err);
592 }
593 
594 /*
595  * Check to see if a file is ok to commit and make sure all files are
596  * up-to-date
597  */
598 /* ARGSUSED */
599 static int
600 check_fileproc (callerdat, finfo)
601     void *callerdat;
602     struct file_info *finfo;
603 {
604     Ctype status;
605     char *xdir;
606     Node *p;
607     List *ulist, *cilist;
608     Vers_TS *vers;
609     struct commit_info *ci;
610     struct logfile_info *li;
611     int save_noexec, save_quiet, save_really_quiet;
612 
613     save_noexec = noexec;
614     save_quiet = quiet;
615     save_really_quiet = really_quiet;
616     noexec = quiet = really_quiet = 1;
617 
618     /* handle specified numeric revision specially */
619     if (tag && isdigit (*tag))
620     {
621 	/* If the tag is for the trunk, make sure we're at the head */
622 	if (numdots (tag) < 2)
623 	{
624 	    status = Classify_File (finfo, (char *) NULL, (char *) NULL,
625 				    (char *) NULL, 1, aflag, &vers, 0);
626 	    if (status == T_UPTODATE || status == T_MODIFIED ||
627 		status == T_ADDED)
628 	    {
629 		Ctype xstatus;
630 
631 		freevers_ts (&vers);
632 		xstatus = Classify_File (finfo, tag, (char *) NULL,
633 					 (char *) NULL, 1, aflag, &vers, 0);
634 		if (xstatus == T_REMOVE_ENTRY)
635 		    status = T_MODIFIED;
636 		else if (status == T_MODIFIED && xstatus == T_CONFLICT)
637 		    status = T_MODIFIED;
638 		else
639 		    status = xstatus;
640 	    }
641 	}
642 	else
643 	{
644 	    char *xtag, *cp;
645 
646 	    /*
647 	     * The revision is off the main trunk; make sure we're
648 	     * up-to-date with the head of the specified branch.
649 	     */
650 	    xtag = xstrdup (tag);
651 	    if ((numdots (xtag) & 1) != 0)
652 	    {
653 		cp = strrchr (xtag, '.');
654 		*cp = '\0';
655 	    }
656 	    status = Classify_File (finfo, xtag, (char *) NULL,
657 				    (char *) NULL, 1, aflag, &vers, 0);
658 	    if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
659 		&& (cp = strrchr (xtag, '.')) != NULL)
660 	    {
661 		/* pluck one more dot off the revision */
662 		*cp = '\0';
663 		freevers_ts (&vers);
664 		status = Classify_File (finfo, xtag, (char *) NULL,
665 					(char *) NULL, 1, aflag, &vers, 0);
666 		if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
667 		    status = T_MODIFIED;
668 	    }
669 	    /* now, muck with vers to make the tag correct */
670 	    free (vers->tag);
671 	    vers->tag = xstrdup (tag);
672 	    free (xtag);
673 	}
674     }
675     else
676 	status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL,
677 				1, 0, &vers, 0);
678     noexec = save_noexec;
679     quiet = save_quiet;
680     really_quiet = save_really_quiet;
681 
682     /*
683      * If the force-commit option is enabled, and the file in question
684      * appears to be up-to-date, just make it look modified so that
685      * it will be committed.
686      */
687     if (force_ci && status == T_UPTODATE)
688 	status = T_MODIFIED;
689 
690     switch (status)
691     {
692 	case T_CHECKOUT:
693 #ifdef SERVER_SUPPORT
694 	case T_PATCH:
695 #endif
696 	case T_NEEDS_MERGE:
697 	case T_CONFLICT:
698 	case T_REMOVE_ENTRY:
699 	    error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
700 	    freevers_ts (&vers);
701 	    return (1);
702 	case T_MODIFIED:
703 	case T_ADDED:
704 	case T_REMOVED:
705 	    /*
706 	     * some quick sanity checks; if no numeric -r option specified:
707 	     *	- can't have a sticky date
708 	     *	- can't have a sticky tag that is not a branch
709 	     * Also,
710 	     *	- if status is T_REMOVED, can't have a numeric tag
711 	     *	- if status is T_ADDED, rcs file must not exist unless on
712 	     *    a branch
713 	     *	- if status is T_ADDED, can't have a non-trunk numeric rev
714 	     *	- if status is T_MODIFIED and a Conflict marker exists, don't
715 	     *    allow the commit if timestamp is identical or if we find
716 	     *    an RCS_MERGE_PAT in the file.
717 	     */
718 	    if (!tag || !isdigit (*tag))
719 	    {
720 		if (vers->date)
721 		{
722 		    error (0, 0,
723 			   "cannot commit with sticky date for file `%s'",
724 			   finfo->fullname);
725 		    freevers_ts (&vers);
726 		    return (1);
727 		}
728 		if (status == T_MODIFIED && vers->tag &&
729 		    !RCS_isbranch (finfo->rcs, vers->tag))
730 		{
731 		    error (0, 0,
732 			   "sticky tag `%s' for file `%s' is not a branch",
733 			   vers->tag, finfo->fullname);
734 		    freevers_ts (&vers);
735 		    return (1);
736 		}
737 	    }
738 	    if (status == T_MODIFIED && !force_ci && vers->ts_conflict)
739 	    {
740 		char *filestamp;
741 		int retcode;
742 
743 		/*
744 		 * We found a "conflict" marker.
745 		 *
746 		 * If the timestamp on the file is the same as the
747 		 * timestamp stored in the Entries file, we block the commit.
748 		 */
749 #ifdef SERVER_SUPPORT
750 		if (server_active)
751 		    retcode = vers->ts_conflict[0] != '=';
752 		else {
753 		    filestamp = time_stamp (finfo->file);
754 		    retcode = strcmp (vers->ts_conflict, filestamp);
755 		    free (filestamp);
756 		}
757 #else
758 		filestamp = time_stamp (finfo->file);
759 		retcode = strcmp (vers->ts_conflict, filestamp);
760 		free (filestamp);
761 #endif
762 		if (retcode == 0)
763 		{
764 		    error (0, 0,
765 			  "file `%s' had a conflict and has not been modified",
766 			   finfo->fullname);
767 		    freevers_ts (&vers);
768 		    return (1);
769 		}
770 
771 		/*
772 		 * If the timestamps differ, look for Conflict indicators
773 		 * in the file to see if we should block the commit anyway
774 		 */
775 		run_setup ("%s", GREP);
776 		run_arg (RCS_MERGE_PAT);
777 		run_arg (finfo->file);
778 		retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY);
779 
780 		if (retcode == -1)
781 		{
782 		    error (1, errno,
783 			   "fork failed while examining conflict in `%s'",
784 			   finfo->fullname);
785 		}
786 		else if (retcode == 0)
787 		{
788 		    error (0, 0,
789 			   "file `%s' still contains conflict indicators",
790 			   finfo->fullname);
791 		    freevers_ts (&vers);
792 		    return (1);
793 		}
794 	    }
795 
796 	    if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
797 	    {
798 		error (0, 0,
799 	"cannot remove file `%s' which has a numeric sticky tag of `%s'",
800 			   finfo->fullname, vers->tag);
801 		freevers_ts (&vers);
802 		return (1);
803 	    }
804 	    if (status == T_ADDED)
805 	    {
806 	        if (vers->tag == NULL)
807 		{
808 		    char rcs[PATH_MAX];
809 
810 		    /* Don't look in the attic; if it exists there we
811 		       will move it back out in checkaddfile.  */
812 		    sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file,
813 			    RCSEXT);
814 		    if (isreadable (rcs))
815 		    {
816 			error (0, 0,
817 		    "cannot add file `%s' when RCS file `%s' already exists",
818 			       finfo->fullname, rcs);
819 			freevers_ts (&vers);
820 			return (1);
821 		    }
822 		}
823 		if (vers->tag && isdigit (*vers->tag) &&
824 		    numdots (vers->tag) > 1)
825 		{
826 		    error (0, 0,
827 		"cannot add file `%s' with revision `%s'; must be on trunk",
828 			       finfo->fullname, vers->tag);
829 		    freevers_ts (&vers);
830 		    return (1);
831 		}
832 	    }
833 
834 	    /* done with consistency checks; now, to get on with the commit */
835 	    if (finfo->update_dir[0] == '\0')
836 		xdir = ".";
837 	    else
838 		xdir = finfo->update_dir;
839 	    if ((p = findnode (mulist, xdir)) != NULL)
840 	    {
841 		ulist = ((struct master_lists *) p->data)->ulist;
842 		cilist = ((struct master_lists *) p->data)->cilist;
843 	    }
844 	    else
845 	    {
846 		struct master_lists *ml;
847 
848 		ulist = getlist ();
849 		cilist = getlist ();
850 		p = getnode ();
851 		p->key = xstrdup (xdir);
852 		p->type = UPDATE;
853 		ml = (struct master_lists *)
854 		    xmalloc (sizeof (struct master_lists));
855 		ml->ulist = ulist;
856 		ml->cilist = cilist;
857 		p->data = (char *) ml;
858 		p->delproc = masterlist_delproc;
859 		(void) addnode (mulist, p);
860 	    }
861 
862 	    /* first do ulist, then cilist */
863 	    p = getnode ();
864 	    p->key = xstrdup (finfo->file);
865 	    p->type = UPDATE;
866 	    p->delproc = update_delproc;
867 	    li = ((struct logfile_info *)
868 		  xmalloc (sizeof (struct logfile_info)));
869 	    li->type = status;
870 	    li->tag = xstrdup (vers->tag);
871 	    p->data = (char *) li;
872 	    (void) addnode (ulist, p);
873 
874 	    p = getnode ();
875 	    p->key = xstrdup (finfo->file);
876 	    p->type = UPDATE;
877 	    p->delproc = ci_delproc;
878 	    ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
879 	    ci->status = status;
880 	    if (vers->tag)
881 		if (isdigit (*vers->tag))
882 		    ci->rev = xstrdup (vers->tag);
883 		else
884 		    ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
885 	    else
886 		ci->rev = (char *) NULL;
887 	    ci->tag = xstrdup (vers->tag);
888 	    ci->options = xstrdup(vers->options);
889 	    p->data = (char *) ci;
890 	    (void) addnode (cilist, p);
891 	    break;
892 	case T_UNKNOWN:
893 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
894 	    freevers_ts (&vers);
895 	    return (1);
896 	case T_UPTODATE:
897 	    break;
898 	default:
899 	    error (0, 0, "CVS internal error: unknown status %d", status);
900 	    break;
901     }
902 
903     freevers_ts (&vers);
904     return (0);
905 }
906 
907 /*
908  * Print warm fuzzies while examining the dirs
909  */
910 /* ARGSUSED */
911 static Dtype
912 check_direntproc (callerdat, dir, repos, update_dir, entries)
913     void *callerdat;
914     char *dir;
915     char *repos;
916     char *update_dir;
917     List *entries;
918 {
919     if (!quiet)
920 	error (0, 0, "Examining %s", update_dir);
921 
922     return (R_PROCESS);
923 }
924 
925 /*
926  * Walklist proc to run pre-commit checks
927  */
928 static int
929 precommit_list_proc (p, closure)
930     Node *p;
931     void *closure;
932 {
933     struct logfile_info *li;
934 
935     li = (struct logfile_info *) p->data;
936     if (li->type == T_ADDED
937 	|| li->type == T_MODIFIED
938 	|| li->type == T_REMOVED)
939     {
940 	run_arg (p->key);
941     }
942     return (0);
943 }
944 
945 /*
946  * Callback proc for pre-commit checking
947  */
948 static List *ulist;
949 static int
950 precommit_proc (repository, filter)
951     char *repository;
952     char *filter;
953 {
954     /* see if the filter is there, only if it's a full path */
955     if (isabsolute (filter))
956     {
957     	char *s, *cp;
958 
959 	s = xstrdup (filter);
960 	for (cp = s; *cp; cp++)
961 	    if (isspace (*cp))
962 	    {
963 		*cp = '\0';
964 		break;
965 	    }
966 	if (!isfile (s))
967 	{
968 	    error (0, errno, "cannot find pre-commit filter `%s'", s);
969 	    free (s);
970 	    return (1);			/* so it fails! */
971 	}
972 	free (s);
973     }
974 
975     run_setup ("%s %s", filter, repository);
976     (void) walklist (ulist, precommit_list_proc, NULL);
977     return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
978 }
979 
980 /*
981  * Run the pre-commit checks for the dir
982  */
983 /* ARGSUSED */
984 static int
985 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
986     void *callerdat;
987     int err;
988     char *repos;
989     char *update_dir;
990     List *entries;
991 {
992     int n;
993     Node *p;
994 
995     /* find the update list for this dir */
996     p = findnode (mulist, update_dir);
997     if (p != NULL)
998 	ulist = ((struct master_lists *) p->data)->ulist;
999     else
1000 	ulist = (List *) NULL;
1001 
1002     /* skip the checks if there's nothing to do */
1003     if (ulist == NULL || ulist->list->next == ulist->list)
1004 	return (err);
1005 
1006     /* run any pre-commit checks */
1007     if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0)
1008     {
1009 	error (0, 0, "Pre-commit check failed");
1010 	err += n;
1011     }
1012 
1013     return (err);
1014 }
1015 
1016 /*
1017  * Do the work of committing a file
1018  */
1019 static int maxrev;
1020 static char sbranch[PATH_MAX];
1021 
1022 /* ARGSUSED */
1023 static int
1024 commit_fileproc (callerdat, finfo)
1025     void *callerdat;
1026     struct file_info *finfo;
1027 {
1028     Node *p;
1029     int err = 0;
1030     List *ulist, *cilist;
1031     struct commit_info *ci;
1032 
1033     if (finfo->update_dir[0] == '\0')
1034 	p = findnode (mulist, ".");
1035     else
1036 	p = findnode (mulist, finfo->update_dir);
1037 
1038     /*
1039      * if p is null, there were file type command line args which were
1040      * all up-to-date so nothing really needs to be done
1041      */
1042     if (p == NULL)
1043 	return (0);
1044     ulist = ((struct master_lists *) p->data)->ulist;
1045     cilist = ((struct master_lists *) p->data)->cilist;
1046 
1047     /*
1048      * At this point, we should have the commit message unless we were called
1049      * with files as args from the command line.  In that latter case, we
1050      * need to get the commit message ourselves
1051      */
1052     if (use_editor && !got_message)
1053       {
1054 	got_message = 1;
1055 	do_editor (finfo->update_dir, &message, finfo->repository, ulist);
1056       }
1057 
1058     p = findnode (cilist, finfo->file);
1059     if (p == NULL)
1060 	return (0);
1061 
1062     ci = (struct commit_info *) p->data;
1063     if (ci->status == T_MODIFIED)
1064     {
1065 	if (finfo->rcs == NULL)
1066 	    error (1, 0, "internal error: no parsed RCS file");
1067 	if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
1068 		      finfo->repository) != 0)
1069 	{
1070 	    unlockrcs (finfo->rcs);
1071 	    err = 1;
1072 	    goto out;
1073 	}
1074     }
1075     else if (ci->status == T_ADDED)
1076     {
1077 	if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
1078 			  &finfo->rcs) != 0)
1079 	{
1080 	    fixaddfile (finfo->file, finfo->repository);
1081 	    err = 1;
1082 	    goto out;
1083 	}
1084 
1085 	/* adding files with a tag, now means adding them on a branch.
1086 	   Since the branch test was done in check_fileproc for
1087 	   modified files, we need to stub it in again here. */
1088 
1089 	if (ci->tag)
1090 	{
1091 	    if (finfo->rcs == NULL)
1092 		error (1, 0, "internal error: no parsed RCS file");
1093 	    ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
1094 	    err = Checkin ('A', finfo, finfo->rcs->path, ci->rev,
1095 			   ci->tag, ci->options, message);
1096 	    if (err != 0)
1097 	    {
1098 		unlockrcs (finfo->rcs);
1099 		fixbranch (finfo->rcs, sbranch);
1100 	    }
1101 
1102 	    (void) time (&last_register_time);
1103 
1104 	    ci->status = T_UPTODATE;
1105 	}
1106     }
1107 
1108     /*
1109      * Add the file for real
1110      */
1111     if (ci->status == T_ADDED)
1112     {
1113 	char *xrev = (char *) NULL;
1114 
1115 	if (ci->rev == NULL)
1116 	{
1117 	    /* find the max major rev number in this directory */
1118 	    maxrev = 0;
1119 	    (void) walklist (finfo->entries, findmaxrev, NULL);
1120 	    if (maxrev == 0)
1121 		maxrev = 1;
1122 	    xrev = xmalloc (20);
1123 	    (void) sprintf (xrev, "%d", maxrev);
1124 	}
1125 
1126 	/* XXX - an added file with symbolic -r should add tag as well */
1127 	err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
1128 	if (xrev)
1129 	    free (xrev);
1130     }
1131     else if (ci->status == T_MODIFIED)
1132     {
1133 	err = Checkin ('M', finfo,
1134 		       finfo->rcs->path, ci->rev, ci->tag,
1135 		       ci->options, message);
1136 
1137 	(void) time (&last_register_time);
1138 
1139 	if (err != 0)
1140 	{
1141 	    unlockrcs (finfo->rcs);
1142 	    fixbranch (finfo->rcs, sbranch);
1143 	}
1144     }
1145     else if (ci->status == T_REMOVED)
1146     {
1147 	err = remove_file (finfo, ci->tag, message);
1148 #ifdef SERVER_SUPPORT
1149 	if (server_active) {
1150 	    server_scratch_entry_only ();
1151 	    server_updated (finfo,
1152 			    NULL,
1153 
1154 			    /* Doesn't matter, it won't get checked.  */
1155 			    SERVER_UPDATED,
1156 
1157 			    (struct stat *) NULL,
1158 			    (unsigned char *) NULL);
1159 	}
1160 #endif
1161     }
1162 
1163     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
1164        about T_ADDED or T_REMOVED.  */
1165     notify_do ('C', finfo->file, getcaller (), NULL, NULL, finfo->repository);
1166 
1167 out:
1168     if (err != 0)
1169     {
1170 	/* on failure, remove the file from ulist */
1171 	p = findnode (ulist, finfo->file);
1172 	if (p)
1173 	    delnode (p);
1174     }
1175 
1176     return (err);
1177 }
1178 
1179 /*
1180  * Log the commit and clean up the update list
1181  */
1182 /* ARGSUSED */
1183 static int
1184 commit_filesdoneproc (callerdat, err, repository, update_dir, entries)
1185     void *callerdat;
1186     int err;
1187     char *repository;
1188     char *update_dir;
1189     List *entries;
1190 {
1191     Node *p;
1192     List *ulist;
1193 
1194     p = findnode (mulist, update_dir);
1195     if (p == NULL)
1196 	return (err);
1197 
1198     ulist = ((struct master_lists *) p->data)->ulist;
1199 
1200     got_message = 0;
1201 
1202 
1203     Update_Logfile (repository, message, (FILE *) 0, ulist);
1204 
1205     /* Build the administrative files if necessary.  */
1206     {
1207 	char *p;
1208 
1209 	if (strncmp (CVSroot_directory, repository,
1210 		     strlen (CVSroot_directory)) != 0)
1211 	    error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", repository, CVSroot_directory);
1212 	p = repository + strlen (CVSroot_directory);
1213 	if (*p == '/')
1214 	    ++p;
1215 	if (strcmp ("CVSROOT", p) == 0)
1216 	{
1217 	    /* "Database" might a little bit grandiose and/or vague,
1218 	       but "checked-out copies of administrative files, unless
1219 	       in the case of modules and you are using ndbm in which
1220 	       case modules.{pag,dir,db}" is verbose and excessively
1221 	       focused on how the database is implemented.  */
1222 
1223 	    cvs_output (program_name, 0);
1224 	    cvs_output (" ", 1);
1225 	    cvs_output (command_name, 0);
1226 	    cvs_output (": Rebuilding administrative file database\n", 0);
1227 	    mkmodules (repository);
1228 	}
1229     }
1230 
1231     if (err == 0 && run_module_prog)
1232     {
1233 	FILE *fp;
1234 
1235 	if ((fp = CVS_FOPEN (CVSADM_CIPROG, "r")) != NULL)
1236 	{
1237 	    char *line;
1238 	    int line_length;
1239 	    size_t line_chars_allocated;
1240 	    char *repository;
1241 
1242 	    line = NULL;
1243 	    line_chars_allocated = 0;
1244 	    line_length = getline (&line, &line_chars_allocated, fp);
1245 	    if (line_length > 0)
1246 	    {
1247 		/* Remove any trailing newline.  */
1248 		if (line[line_length - 1] == '\n')
1249 		    line[--line_length] = '\0';
1250 		repository = Name_Repository ((char *) NULL, update_dir);
1251 		run_setup ("%s %s", line, repository);
1252 		(void) printf ("%s %s: Executing '", program_name,
1253 			       command_name);
1254 		run_print (stdout);
1255 		(void) printf ("'\n");
1256 		(void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
1257 		free (repository);
1258 	    }
1259 	    else
1260 	    {
1261 		if (ferror (fp))
1262 		    error (0, errno, "warning: error reading %s",
1263 			   CVSADM_CIPROG);
1264 	    }
1265 	    if (line != NULL)
1266 		free (line);
1267 	    if (fclose (fp) < 0)
1268 		error (0, errno, "warning: cannot close %s", CVSADM_CIPROG);
1269 	}
1270 	else
1271 	{
1272 	    if (! existence_error (errno))
1273 		error (0, errno, "warning: cannot open %s", CVSADM_CIPROG);
1274 	}
1275     }
1276 
1277     return (err);
1278 }
1279 
1280 /*
1281  * Get the log message for a dir and print a warm fuzzy
1282  */
1283 /* ARGSUSED */
1284 static Dtype
1285 commit_direntproc (callerdat, dir, repos, update_dir, entries)
1286     void *callerdat;
1287     char *dir;
1288     char *repos;
1289     char *update_dir;
1290     List *entries;
1291 {
1292     Node *p;
1293     List *ulist;
1294     char *real_repos;
1295 
1296     /* find the update list for this dir */
1297     p = findnode (mulist, update_dir);
1298     if (p != NULL)
1299 	ulist = ((struct master_lists *) p->data)->ulist;
1300     else
1301 	ulist = (List *) NULL;
1302 
1303     /* skip the files as an optimization */
1304     if (ulist == NULL || ulist->list->next == ulist->list)
1305 	return (R_SKIP_FILES);
1306 
1307     /* print the warm fuzzy */
1308     if (!quiet)
1309 	error (0, 0, "Committing %s", update_dir);
1310 
1311     /* get commit message */
1312     if (use_editor)
1313     {
1314 	got_message = 1;
1315 	real_repos = Name_Repository (dir, update_dir);
1316 	do_editor (update_dir, &message, real_repos, ulist);
1317 	free (real_repos);
1318     }
1319     return (R_PROCESS);
1320 }
1321 
1322 /*
1323  * Process the post-commit proc if necessary
1324  */
1325 /* ARGSUSED */
1326 static int
1327 commit_dirleaveproc (callerdat, dir, err, update_dir, entries)
1328     void *callerdat;
1329     char *dir;
1330     int err;
1331     char *update_dir;
1332     List *entries;
1333 {
1334     /* update the per-directory tag info */
1335     if (err == 0 && write_dirtag != NULL)
1336     {
1337 	WriteTag ((char *) NULL, write_dirtag, (char *) NULL);
1338 #ifdef SERVER_SUPPORT
1339 	if (server_active)
1340 	    server_set_sticky (update_dir, Name_Repository (dir, update_dir),
1341 			       write_dirtag, (char *) NULL);
1342 #endif
1343     }
1344 
1345     return (err);
1346 }
1347 
1348 /*
1349  * find the maximum major rev number in an entries file
1350  */
1351 static int
1352 findmaxrev (p, closure)
1353     Node *p;
1354     void *closure;
1355 {
1356     char *cp;
1357     int thisrev;
1358     Entnode *entdata;
1359 
1360     entdata = (Entnode *) p->data;
1361     if (entdata->type != ENT_FILE)
1362 	return (0);
1363     cp = strchr (entdata->version, '.');
1364     if (cp != NULL)
1365 	*cp = '\0';
1366     thisrev = atoi (entdata->version);
1367     if (cp != NULL)
1368 	*cp = '.';
1369     if (thisrev > maxrev)
1370 	maxrev = thisrev;
1371     return (0);
1372 }
1373 
1374 /*
1375  * Actually remove a file by moving it to the attic
1376  * XXX - if removing a ,v file that is a relative symbolic link to
1377  * another ,v file, we probably should add a ".." component to the
1378  * link to keep it relative after we move it into the attic.
1379  */
1380 static int
1381 remove_file (finfo, tag, message)
1382     struct file_info *finfo;
1383     char *tag;
1384     char *message;
1385 {
1386     mode_t omask;
1387     int retcode;
1388     char *tmp;
1389 
1390     int branch;
1391     int lockflag;
1392     char *corev;
1393     char *rev;
1394     char *prev_rev;
1395     char *old_path;
1396 
1397     corev = NULL;
1398     rev = NULL;
1399     prev_rev = NULL;
1400 
1401     retcode = 0;
1402 
1403     if (finfo->rcs == NULL)
1404 	error (1, 0, "internal error: no parsed RCS file");
1405 
1406     branch = 0;
1407     if (tag && !(branch = RCS_isbranch (finfo->rcs, tag)))
1408     {
1409 	/* a symbolic tag is specified; just remove the tag from the file */
1410 	if ((retcode = RCS_deltag (finfo->rcs, tag, 1)) != 0)
1411 	{
1412 	    if (!quiet)
1413 		error (0, retcode == -1 ? errno : 0,
1414 		       "failed to remove tag `%s' from `%s'", tag,
1415 		       finfo->fullname);
1416 	    return (1);
1417 	}
1418 	Scratch_Entry (finfo->entries, finfo->file);
1419 	return (0);
1420     }
1421 
1422     /* we are removing the file from either the head or a branch */
1423     /* commit a new, dead revision. */
1424 
1425     /* Print message indicating that file is going to be removed. */
1426     (void) printf ("Removing %s;\n", finfo->fullname);
1427 
1428     rev = NULL;
1429     lockflag = 1;
1430     if (branch)
1431     {
1432 	char *branchname;
1433 
1434 	rev = RCS_whatbranch (finfo->rcs, tag);
1435 	if (rev == NULL)
1436 	{
1437 	    error (0, 0, "cannot find branch \"%s\".", tag);
1438 	    return (1);
1439 	}
1440 
1441 	branchname = RCS_getbranch (finfo->rcs, rev, 1);
1442 	if (branchname == NULL)
1443 	{
1444 	    /* no revision exists on this branch.  use the previous
1445 	       revision but do not lock. */
1446 	    corev = RCS_gettag (finfo->rcs, tag, 1, (int *) NULL);
1447 	    prev_rev = xstrdup(rev);
1448 	    lockflag = 0;
1449 	} else
1450 	{
1451 	    corev = xstrdup (rev);
1452 	    prev_rev = xstrdup(branchname);
1453 	    free (branchname);
1454 	}
1455 
1456     } else  /* Not a branch */
1457     {
1458         /* Get current head revision of file. */
1459 	prev_rev = RCS_head (finfo->rcs);
1460     }
1461 
1462     /* if removing without a tag or a branch, then make sure the default
1463        branch is the trunk. */
1464     if (!tag && !branch)
1465     {
1466         if (RCS_setbranch (finfo->rcs, NULL) != 0)
1467 	{
1468 	    error (0, 0, "cannot change branch to default for %s",
1469 		   finfo->fullname);
1470 	    return (1);
1471 	}
1472     }
1473 
1474 #ifdef SERVER_SUPPORT
1475     if (server_active) {
1476 	/* If this is the server, there will be a file sitting in the
1477 	   temp directory which is the kludgy way in which server.c
1478 	   tells time_stamp that the file is no longer around.  Remove
1479 	   it so we can create temp files with that name (ignore errors).  */
1480 	unlink_file (finfo->file);
1481     }
1482 #endif
1483 
1484     /* check something out.  Generally this is the head.  If we have a
1485        particular rev, then name it.  */
1486     retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
1487 			    (char *) NULL, (char *) NULL, RUN_TTY);
1488     if (retcode != 0)
1489     {
1490 	if (!quiet)
1491 	    error (0, retcode == -1 ? errno : 0,
1492 		   "failed to check out `%s'", finfo->fullname);
1493 	return (1);
1494     }
1495 
1496     /* Except when we are creating a branch, lock the revision so that
1497        we can check in the new revision.  */
1498     if (lockflag)
1499 	RCS_lock (finfo->rcs, rev ? corev : NULL, 0);
1500 
1501     if (corev != NULL)
1502 	free (corev);
1503 
1504     retcode = RCS_checkin (finfo->rcs->path, finfo->file, message, rev,
1505 			   RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1506     if (retcode	!= 0)
1507     {
1508 	if (!quiet)
1509 	    error (0, retcode == -1 ? errno : 0,
1510 		   "failed to commit dead revision for `%s'", finfo->fullname);
1511 	return (1);
1512     }
1513 
1514     if (rev != NULL)
1515 	free (rev);
1516 
1517     old_path = finfo->rcs->path;
1518     if (!branch)
1519     {
1520 	/* this was the head; really move it into the Attic */
1521 	tmp = xmalloc(strlen(finfo->repository) +
1522 		      sizeof('/') +
1523 		      sizeof(CVSATTIC) +
1524 		      sizeof('/') +
1525 		      strlen(finfo->file) +
1526 		      sizeof(RCSEXT) + 1);
1527 	(void) sprintf (tmp, "%s/%s", finfo->repository, CVSATTIC);
1528 	omask = umask (cvsumask);
1529 	(void) CVS_MKDIR (tmp, 0777);
1530 	(void) umask (omask);
1531 	(void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, finfo->file, RCSEXT);
1532 
1533 	if (strcmp (finfo->rcs->path, tmp) != 0
1534 	    && CVS_RENAME (finfo->rcs->path, tmp) == -1
1535 	    && (isreadable (finfo->rcs->path) || !isreadable (tmp)))
1536 	{
1537 	    free(tmp);
1538 	    return (1);
1539 	}
1540 	/* The old value of finfo->rcs->path is in old_path, and is
1541            freed below.  */
1542 	finfo->rcs->path = tmp;
1543     }
1544 
1545     /* Print message that file was removed. */
1546     (void) printf ("%s  <--  %s\n", old_path, finfo->file);
1547     (void) printf ("new revision: delete; ");
1548     (void) printf ("previous revision: %s\n", prev_rev);
1549     (void) printf ("done\n");
1550     free(prev_rev);
1551 
1552     if (old_path != finfo->rcs->path)
1553 	free (old_path);
1554 
1555     Scratch_Entry (finfo->entries, finfo->file);
1556     return (0);
1557 }
1558 
1559 /*
1560  * Do the actual checkin for added files
1561  */
1562 static int
1563 finaladd (finfo, rev, tag, options)
1564     struct file_info *finfo;
1565     char *rev;
1566     char *tag;
1567     char *options;
1568 {
1569     int ret;
1570     char tmp[PATH_MAX];
1571     char rcs[PATH_MAX];
1572 
1573     locate_rcs (finfo->file, finfo->repository, rcs);
1574     ret = Checkin ('A', finfo, rcs, rev, tag, options, message);
1575     if (ret == 0)
1576     {
1577 	(void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
1578 	(void) unlink_file (tmp);
1579     }
1580     else
1581 	fixaddfile (finfo->file, finfo->repository);
1582 
1583     (void) time (&last_register_time);
1584 
1585     return (ret);
1586 }
1587 
1588 /*
1589  * Unlock an rcs file
1590  */
1591 static void
1592 unlockrcs (rcs)
1593     RCSNode *rcs;
1594 {
1595     int retcode;
1596 
1597     if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0)
1598 	error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1599 	       "could not unlock %s", rcs->path);
1600 }
1601 
1602 /*
1603  * remove a partially added file.  if we can parse it, leave it alone.
1604  */
1605 static void
1606 fixaddfile (file, repository)
1607     char *file;
1608     char *repository;
1609 {
1610     RCSNode *rcsfile;
1611     char rcs[PATH_MAX];
1612     int save_really_quiet;
1613 
1614     locate_rcs (file, repository, rcs);
1615     save_really_quiet = really_quiet;
1616     really_quiet = 1;
1617     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
1618 	(void) unlink_file (rcs);
1619     else
1620 	freercsnode (&rcsfile);
1621     really_quiet = save_really_quiet;
1622 }
1623 
1624 /*
1625  * put the branch back on an rcs file
1626  */
1627 static void
1628 fixbranch (rcs, branch)
1629     RCSNode *rcs;
1630     char *branch;
1631 {
1632     int retcode;
1633 
1634     if (branch != NULL && branch[0] != '\0')
1635     {
1636 	if ((retcode = RCS_setbranch (rcs, branch)) != 0)
1637 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1638 		   "cannot restore branch to %s for %s", branch, rcs->path);
1639     }
1640 }
1641 
1642 /*
1643  * do the initial part of a file add for the named file.  if adding
1644  * with a tag, put the file in the Attic and point the symbolic tag
1645  * at the committed revision.
1646  */
1647 
1648 static int
1649 checkaddfile (file, repository, tag, options, rcsnode)
1650     char *file;
1651     char *repository;
1652     char *tag;
1653     char *options;
1654     RCSNode **rcsnode;
1655 {
1656     char rcs[PATH_MAX];
1657     char fname[PATH_MAX];
1658     mode_t omask;
1659     int retcode = 0;
1660     int newfile = 0;
1661     RCSNode *rcsfile = NULL;
1662 
1663     if (tag)
1664     {
1665         (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
1666 	if (! isreadable (rcs))
1667 	{
1668 	    (void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
1669 	    omask = umask (cvsumask);
1670 	    if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST)
1671 		error (1, errno, "cannot make directory `%s'", rcs);;
1672 	    (void) umask (omask);
1673 	    (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file,
1674 			    RCSEXT);
1675 	}
1676     }
1677     else
1678 	locate_rcs (file, repository, rcs);
1679 
1680     if (isreadable(rcs))
1681     {
1682 	/* file has existed in the past.  Prepare to resurrect. */
1683 	char oldfile[PATH_MAX];
1684 	char *rev;
1685 
1686 	if ((rcsfile = *rcsnode) == NULL)
1687 	{
1688 	    error (0, 0, "could not find parsed rcsfile %s", file);
1689 	    return (1);
1690 	}
1691 
1692 	if (tag == NULL)
1693 	{
1694 	    /* we are adding on the trunk, so move the file out of the
1695 	       Attic. */
1696 	    strcpy (oldfile, rcs);
1697 	    sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
1698 
1699 	    if (strcmp (oldfile, rcs) == 0
1700 		|| CVS_RENAME (oldfile, rcs) != 0
1701 		|| isreadable (oldfile)
1702 		|| !isreadable (rcs))
1703 	    {
1704 		error (0, 0, "failed to move `%s' out of the attic.",
1705 		       file);
1706 		return (1);
1707 	    }
1708 	    free (rcsfile->path);
1709 	    rcsfile->path = xstrdup (rcs);
1710 	}
1711 
1712 	rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL);
1713 	/* and lock it */
1714 	if (lock_RCS (file, rcsfile, rev, repository)) {
1715 	    error (0, 0, "cannot lock `%s'.", rcs);
1716 	    free (rev);
1717 	    return (1);
1718 	}
1719 
1720 	free (rev);
1721     } else {
1722 	/* this is the first time we have ever seen this file; create
1723 	   an rcs file.  */
1724 	run_setup ("%s%s -x,v/ -i", Rcsbin, RCS);
1725 
1726 	(void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
1727 	/* If the file does not exist, no big deal.  In particular, the
1728 	   server does not (yet at least) create CVSEXT_LOG files.  */
1729 	if (isfile (fname))
1730 	    run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
1731 
1732 	/* Set RCS keyword expansion options.  */
1733 	if (options && options[0] == '-' && options[1] == 'k')
1734 	    run_arg (options);
1735 	run_arg (rcs);
1736 	if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
1737 	{
1738 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1739 		   "could not create %s", rcs);
1740 	    return (1);
1741 	}
1742 	newfile = 1;
1743     }
1744 
1745     /* when adding a file for the first time, and using a tag, we need
1746        to create a dead revision on the trunk.  */
1747     if (tag && newfile)
1748     {
1749 	char *tmp;
1750 
1751 	/* move the new file out of the way. */
1752 	(void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
1753 	rename_file (file, fname);
1754 	copy_file (DEVNULL, file);
1755 
1756 	tmp = xmalloc (strlen (file) + strlen (tag) + 80);
1757 	/* commit a dead revision. */
1758 	(void) sprintf (tmp, "file %s was initially added on branch %s.",
1759 			file, tag);
1760 	retcode = RCS_checkin (rcs, NULL, tmp, NULL,
1761 			       RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1762 	free (tmp);
1763 	if (retcode != 0)
1764 	{
1765 	    error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1766 		   "could not create initial dead revision %s", rcs);
1767 	    return (1);
1768 	}
1769 
1770 	/* put the new file back where it was */
1771 	rename_file (fname, file);
1772 
1773 	assert (rcsfile == NULL);
1774 	rcsfile = RCS_parse (file, repository);
1775 	if (rcsfile == NULL)
1776 	{
1777 	    error (0, 0, "could not read %s", rcs);
1778 	    return (1);
1779 	}
1780 	if (rcsnode != NULL)
1781 	{
1782 	    assert (*rcsnode == NULL);
1783 	    *rcsnode = rcsfile;
1784 	}
1785 
1786 	/* and lock it once again. */
1787 	if (lock_RCS (file, rcsfile, NULL, repository)) {
1788 	    error (0, 0, "cannot lock `%s'.", rcs);
1789 	    return (1);
1790 	}
1791     }
1792 
1793     if (tag != NULL)
1794     {
1795 	/* when adding with a tag, we need to stub a branch, if it
1796 	   doesn't already exist.  */
1797 
1798 	if (rcsfile == NULL)
1799 	{
1800 	    if (rcsnode != NULL && *rcsnode != NULL)
1801 		rcsfile = *rcsnode;
1802 	    else
1803 	    {
1804 		rcsfile = RCS_parse (file, repository);
1805 		if (rcsfile == NULL)
1806 		{
1807 		    error (0, 0, "could not read %s", rcs);
1808 		    return (1);
1809 		}
1810 	    }
1811 	}
1812 
1813 	if (!RCS_nodeisbranch (rcsfile, tag)) {
1814 	    /* branch does not exist.  Stub it.  */
1815 	    char *head;
1816 	    char *magicrev;
1817 
1818 	    head = RCS_getversion (rcsfile, NULL, NULL, 0, (int *) NULL);
1819 	    magicrev = RCS_magicrev (rcsfile, head);
1820 
1821 	    retcode = RCS_settag (rcsfile, tag, magicrev);
1822 
1823 	    free (head);
1824 	    free (magicrev);
1825 
1826 	    if (retcode != 0)
1827 	    {
1828 		error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1829 		       "could not stub branch %s for %s", tag, rcs);
1830 		return (1);
1831 	    }
1832 	}
1833 	else
1834 	{
1835 	    /* lock the branch. (stubbed branches need not be locked.)  */
1836 	    if (lock_RCS (file, rcsfile, NULL, repository)) {
1837 		error (0, 0, "cannot lock `%s'.", rcs);
1838 		return (1);
1839 	    }
1840 	}
1841 
1842 	if (rcsnode && *rcsnode != rcsfile)
1843 	{
1844 	    freercsnode(rcsnode);
1845 	    *rcsnode = rcsfile;
1846 	}
1847     }
1848 
1849     fileattr_newfile (file);
1850 
1851     fix_rcs_modes (rcs, file);
1852     return (0);
1853 }
1854 
1855 /*
1856  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
1857  * couldn't.  If the RCS file currently has a branch as the head, we must
1858  * move the head back to the trunk before locking the file, and be sure to
1859  * put the branch back as the head if there are any errors.
1860  */
1861 static int
1862 lock_RCS (user, rcs, rev, repository)
1863     char *user;
1864     RCSNode *rcs;
1865     char *rev;
1866     char *repository;
1867 {
1868     char *branch = NULL;
1869     int err = 0;
1870 
1871     /*
1872      * For a specified, numeric revision of the form "1" or "1.1", (or when
1873      * no revision is specified ""), definitely move the branch to the trunk
1874      * before locking the RCS file.
1875      *
1876      * The assumption is that if there is more than one revision on the trunk,
1877      * the head points to the trunk, not a branch... and as such, it's not
1878      * necessary to move the head in this case.
1879      */
1880     if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
1881     {
1882 	branch = xstrdup (rcs->branch);
1883 	if (branch != NULL)
1884 	{
1885 	    if (RCS_setbranch (rcs, NULL) != 0)
1886 	    {
1887 		error (0, 0, "cannot change branch to default for %s",
1888 		       rcs->path);
1889 		if (branch)
1890 		    free (branch);
1891 		return (1);
1892 	    }
1893 	}
1894 	err = RCS_lock(rcs, NULL, 0);
1895     }
1896     else
1897     {
1898 	(void) RCS_lock(rcs, rev, 1);
1899     }
1900 
1901     if (err == 0)
1902     {
1903 	if (branch)
1904 	{
1905 	    (void) strcpy (sbranch, branch);
1906 	    free (branch);
1907 	}
1908 	else
1909 	    sbranch[0] = '\0';
1910 	return (0);
1911     }
1912 
1913     /* try to restore the branch if we can on error */
1914     if (branch != NULL)
1915 	fixbranch (rcs, branch);
1916 
1917     if (branch)
1918 	free (branch);
1919     return (1);
1920 }
1921 
1922 /*
1923  * Called when "add"ing files to the RCS respository, as it is necessary to
1924  * preserve the file modes in the same fashion that RCS does.  This would be
1925  * automatic except that we are placing the RCS ,v file very far away from
1926  * the user file, and I can't seem to convince RCS of the location of the
1927  * user file.  So we munge it here, after the ,v file has been successfully
1928  * initialized with "rcs -i".
1929  */
1930 static void
1931 fix_rcs_modes (rcs, user)
1932     char *rcs;
1933     char *user;
1934 {
1935     struct stat sb;
1936 
1937     if ( CVS_STAT (user, &sb) != -1)
1938 	(void) chmod (rcs, (int) sb.st_mode & ~0222);
1939 }
1940 
1941 /*
1942  * free an UPDATE node's data
1943  */
1944 void
1945 update_delproc (p)
1946     Node *p;
1947 {
1948     struct logfile_info *li;
1949 
1950     li = (struct logfile_info *) p->data;
1951     if (li->tag)
1952 	free (li->tag);
1953     free (li);
1954 }
1955 
1956 /*
1957  * Free the commit_info structure in p.
1958  */
1959 static void
1960 ci_delproc (p)
1961     Node *p;
1962 {
1963     struct commit_info *ci;
1964 
1965     ci = (struct commit_info *) p->data;
1966     if (ci->rev)
1967 	free (ci->rev);
1968     if (ci->tag)
1969 	free (ci->tag);
1970     if (ci->options)
1971 	free (ci->options);
1972     free (ci);
1973 }
1974 
1975 /*
1976  * Free the commit_info structure in p.
1977  */
1978 static void
1979 masterlist_delproc (p)
1980     Node *p;
1981 {
1982     struct master_lists *ml;
1983 
1984     ml = (struct master_lists *) p->data;
1985     dellist (&ml->ulist);
1986     dellist (&ml->cilist);
1987     free (ml);
1988 }
1989 
1990 /*
1991  * Find an RCS file in the repository.
1992  */
1993 static void
1994 locate_rcs (file, repository, rcs)
1995     char *file;
1996     char *repository;
1997     char *rcs;
1998 {
1999     (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
2000     if (!isreadable (rcs))
2001     {
2002 	(void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
2003 	if (!isreadable (rcs))
2004 	    (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
2005     }
2006 }
2007