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