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