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