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