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