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