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