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