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