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