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