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