Bring cvs-1.12.9 into the CVS repository
[dragonfly.git] / contrib / cvs-1.12.9 / src / update.c
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  *
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  *
8  * "update" updates the version in the present directory with respect to the RCS
9  * repository.  The present version must have been created by "checkout". The
10  * user can keep up-to-date by calling "update" whenever he feels like it.
11  *
12  * The present version can be committed by "commit", but this keeps the version
13  * in tact.
14  *
15  * Arguments following the options are taken to be file names to be updated,
16  * rather than updating the entire directory.
17  *
18  * Modified or non-existent RCS files are checked out and reported as U
19  * <user_file>
20  *
21  * Modified user files are reported as M <user_file>.  If both the RCS file and
22  * the user file have been modified, the user file is replaced by the result
23  * of rcsmerge, and a backup file is written for the user in .#file.version.
24  * If this throws up irreconcilable differences, the file is reported as C
25  * <user_file>, and as M <user_file> otherwise.
26  *
27  * Files added but not yet committed are reported as A <user_file>. Files
28  * removed but not yet committed are reported as R <user_file>.
29  *
30  * If the current directory contains subdirectories that hold concurrent
31  * versions, these are updated too.  If the -d option was specified, new
32  * directories added to the repository are automatically created and updated
33  * as well.
34  */
35
36 #include "cvs.h"
37 #include "savecwd.h"
38 #ifdef SERVER_SUPPORT
39 # include "md5.h"
40 #endif
41 #include "watch.h"
42 #include "fileattr.h"
43 #include "edit.h"
44 #include "getline.h"
45 #include "buffer.h"
46 #include "hardlink.h"
47
48 static int checkout_file (struct file_info *finfo, Vers_TS *vers_ts,
49                                  int adding, int merging, int update_server);
50 #ifdef SERVER_SUPPORT
51 static void checkout_to_buffer (void *, const char *, size_t);
52 static int patch_file (struct file_info *finfo,
53                        Vers_TS *vers_ts, 
54                        int *docheckout, struct stat *file_info,
55                        unsigned char *checksum);
56 static void patch_file_write (void *, const char *, size_t);
57 #endif
58 static int merge_file (struct file_info *finfo, Vers_TS *vers);
59 static int scratch_file (struct file_info *finfo, Vers_TS *vers);
60 static Dtype update_dirent_proc (void *callerdat, const char *dir,
61                                  const char *repository,
62                                  const char *update_dir,
63                                  List *entries);
64 static int update_dirleave_proc (void *callerdat, const char *dir,
65                                  int err, const char *update_dir,
66                                  List *entries);
67 static int update_fileproc (void *callerdat, struct file_info *);
68 static int update_filesdone_proc (void *callerdat, int err,
69                                   const char *repository,
70                                   const char *update_dir, List *entries);
71 #ifdef PRESERVE_PERMISSIONS_SUPPORT
72 static int get_linkinfo_proc( void *_callerdat, struct _finfo * );
73 #endif
74 static void join_file (struct file_info *finfo, Vers_TS *vers_ts);
75
76 static char *options = NULL;
77 static char *tag = NULL;
78 static char *date = NULL;
79 /* This is a bit of a kludge.  We call WriteTag at the beginning
80    before we know whether nonbranch is set or not.  And then at the
81    end, once we have the right value for nonbranch, we call WriteTag
82    again.  I don't know whether the first call is necessary or not.
83    rewrite_tag is nonzero if we are going to have to make that second
84    call.  warned is nonzero if we've already warned the user that the
85    tag occurs as both a revision tag and a branch tag.  */
86 static int rewrite_tag;
87 static int nonbranch;
88 static int warned;
89
90 /* If we set the tag or date for a subdirectory, we use this to undo
91    the setting.  See update_dirent_proc.  */
92 static char *tag_update_dir;
93
94 static char *join_rev1, *date_rev1;
95 static char *join_rev2, *date_rev2;
96 static int aflag = 0;
97 static int toss_local_changes = 0;
98 static int force_tag_match = 1;
99 static int update_build_dirs = 0;
100 static int update_prune_dirs = 0;
101 static int pipeout = 0;
102 static int dotemplate = 0;
103 #ifdef SERVER_SUPPORT
104 static int patches = 0;
105 static int rcs_diff_patches = 0;
106 #endif
107 static List *ignlist = (List *) NULL;
108 static time_t last_register_time;
109 static const char *const update_usage[] =
110 {
111     "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n",
112     "    [-I ign] [-W spec] [files...]\n",
113     "\t-A\tReset any sticky tags/date/kopts.\n",
114     "\t-P\tPrune empty directories.\n",
115     "\t-C\tOverwrite locally modified files with clean repository copies.\n",
116     "\t-d\tBuild directories, like checkout does.\n",
117     "\t-f\tForce a head revision match if tag/date not found.\n",
118     "\t-l\tLocal directory only, no recursion.\n",
119     "\t-R\tProcess directories recursively.\n",
120     "\t-p\tSend updates to standard output (avoids stickiness).\n",
121     "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
122     "\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
123     "\t-D date\tSet date to update from (is sticky).\n",
124     "\t-j rev\tMerge in changes made between current revision and rev.\n",
125     "\t-I ign\tMore files to ignore (! to reset).\n",
126     "\t-W spec\tWrappers specification line.\n",
127     "(Specify the --help global option for a list of other help options)\n",
128     NULL
129 };
130
131
132
133 /*
134  * update is the argv,argc based front end for arg parsing
135  */
136 int
137 update (int argc, char **argv)
138 {
139     int c, err;
140     int local = 0;                      /* recursive by default */
141     int which;                          /* where to look for files and dirs */
142
143     if (argc == -1)
144         usage (update_usage);
145
146     ign_setup ();
147     wrap_setup ();
148
149     /* parse the args */
150     optind = 0;
151     while ((c = getopt (argc, argv, "+ApCPflRQqduk:r:D:j:I:W:")) != -1)
152     {
153         switch (c)
154         {
155             case 'A':
156                 aflag = 1;
157                 break;
158             case 'C':
159                 toss_local_changes = 1;
160                 break;
161             case 'I':
162                 ign_add (optarg, 0);
163                 break;
164             case 'W':
165                 wrap_add (optarg, 0);
166                 break;
167             case 'k':
168                 if (options)
169                     free (options);
170                 options = RCS_check_kflag (optarg);
171                 break;
172             case 'l':
173                 local = 1;
174                 break;
175             case 'R':
176                 local = 0;
177                 break;
178             case 'Q':
179             case 'q':
180 #ifdef SERVER_SUPPORT
181                 /* The CVS 1.5 client sends these options (in addition to
182                    Global_option requests), so we must ignore them.  */
183                 if (!server_active)
184 #endif
185                     error (1, 0,
186                            "-q or -Q must be specified before \"%s\"",
187                            cvs_cmd_name);
188                 break;
189             case 'd':
190                 update_build_dirs = 1;
191                 break;
192             case 'f':
193                 force_tag_match = 0;
194                 break;
195             case 'r':
196                 tag = optarg;
197                 break;
198             case 'D':
199                 date = Make_Date (optarg);
200                 break;
201             case 'P':
202                 update_prune_dirs = 1;
203                 break;
204             case 'p':
205                 pipeout = 1;
206                 noexec = 1;             /* so no locks will be created */
207                 break;
208             case 'j':
209                 if (join_rev2)
210                     error (1, 0, "only two -j options can be specified");
211                 if (join_rev1)
212                     join_rev2 = optarg;
213                 else
214                     join_rev1 = optarg;
215                 break;
216             case 'u':
217 #ifdef SERVER_SUPPORT
218                 if (server_active)
219                 {
220                     patches = 1;
221                     rcs_diff_patches = server_use_rcs_diff ();
222                 }
223                 else
224 #endif
225                     usage (update_usage);
226                 break;
227             case '?':
228             default:
229                 usage (update_usage);
230                 break;
231         }
232     }
233     argc -= optind;
234     argv += optind;
235
236 #ifdef CLIENT_SUPPORT
237     if (current_parsed_root->isremote) 
238     {
239         int pass;
240
241         /* The first pass does the regular update.  If we receive at least
242            one patch which failed, we do a second pass and just fetch
243            those files whose patches failed.  */
244         pass = 1;
245         do
246         {
247             int status;
248
249             start_server ();
250
251             if (local)
252                 send_arg("-l");
253             if (update_build_dirs)
254                 send_arg("-d");
255             if (pipeout)
256                 send_arg("-p");
257             if (!force_tag_match)
258                 send_arg("-f");
259             if (aflag)
260                 send_arg("-A");
261             if (toss_local_changes)
262                 send_arg("-C");
263             if (update_prune_dirs)
264                 send_arg("-P");
265             client_prune_dirs = update_prune_dirs;
266             option_with_arg ("-r", tag);
267             if (options && options[0] != '\0')
268                 send_arg (options);
269             if (date)
270                 client_senddate (date);
271             if (join_rev1)
272                 option_with_arg ("-j", join_rev1);
273             if (join_rev2)
274                 option_with_arg ("-j", join_rev2);
275             wrap_send ();
276
277             if (failed_patches_count == 0)
278             {
279                 unsigned int flags = 0;
280
281                 /* If the server supports the command "update-patches", that 
282                    means that it knows how to handle the -u argument to update,
283                    which means to send patches instead of complete files.
284
285                    We don't send -u if failed_patches != NULL, so that the
286                    server doesn't try to send patches which will just fail
287                    again.  At least currently, the client also clobbers the
288                    file and tells the server it is lost, which also will get
289                    a full file instead of a patch, but it seems clean to omit
290                    -u.  */
291                 if (supported_request ("update-patches"))
292                     send_arg ("-u");
293
294                 send_arg ("--");
295
296                 if (update_build_dirs)
297                     flags |= SEND_BUILD_DIRS;
298
299                 if (toss_local_changes) {
300                     flags |= SEND_NO_CONTENTS;
301                     flags |= BACKUP_MODIFIED_FILES;
302                 }
303
304                 /* If noexec, probably could be setting SEND_NO_CONTENTS.
305                    Same caveats as for "cvs status" apply.  */
306
307                 send_files (argc, argv, local, aflag, flags);
308                 send_file_names (argc, argv, SEND_EXPAND_WILD);
309             }
310             else
311             {
312                 int i;
313
314                 (void) printf ("%s client: refetching unpatchable files\n",
315                                program_name);
316
317                 if (toplevel_wd != NULL
318                     && CVS_CHDIR (toplevel_wd) < 0)
319                 {
320                     error (1, errno, "could not chdir to %s", toplevel_wd);
321                 }
322
323                 send_arg ("--");
324
325                 for (i = 0; i < failed_patches_count; i++)
326                     if (unlink_file (failed_patches[i]) < 0
327                         && !existence_error (errno))
328                         error (0, errno, "cannot remove %s",
329                                failed_patches[i]);
330                 send_files (failed_patches_count, failed_patches, local,
331                             aflag, update_build_dirs ? SEND_BUILD_DIRS : 0);
332                 send_file_names (failed_patches_count, failed_patches, 0);
333                 free_names (&failed_patches_count, failed_patches);
334             }
335
336             send_to_server ("update\012", 0);
337
338             status = get_responses_and_close ();
339
340             /* If there are any conflicts, the server will return a
341                non-zero exit status.  If any patches failed, we still
342                want to run the update again.  We use a pass count to
343                avoid an endless loop.  */
344
345             /* Notes: (1) assuming that status != 0 implies a
346                potential conflict is the best we can cleanly do given
347                the current protocol.  I suppose that trying to
348                re-fetch in cases where there was a more serious error
349                is probably more or less harmless, but it isn't really
350                ideal.  (2) it would be nice to have a testsuite case for the
351                conflict-and-patch-failed case.  */
352
353             if (status != 0
354                 && (failed_patches_count == 0 || pass > 1))
355             {
356                 if (failed_patches_count > 0)
357                     free_names (&failed_patches_count, failed_patches);
358                 return status;
359             }
360
361             ++pass;
362         } while (failed_patches_count > 0);
363
364         return 0;
365     }
366 #endif
367
368     if (tag != NULL)
369         tag_check_valid (tag, argc, argv, local, aflag, "");
370     if (join_rev1 != NULL)
371         tag_check_valid_join (join_rev1, argc, argv, local, aflag, "");
372     if (join_rev2 != NULL)
373         tag_check_valid_join (join_rev2, argc, argv, local, aflag, "");
374
375     /*
376      * If we are updating the entire directory (for real) and building dirs
377      * as we go, we make sure there is no static entries file and write the
378      * tag file as appropriate
379      */
380     if (argc <= 0 && !pipeout)
381     {
382         if (update_build_dirs)
383         {
384             if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
385                 error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
386 #ifdef SERVER_SUPPORT
387             if (server_active)
388             {
389                 char *repos = Name_Repository (NULL, NULL);
390                 server_clear_entstat (".", repos);
391                 free (repos);
392             }
393 #endif
394         }
395
396         /* keep the CVS/Tag file current with the specified arguments */
397         if (aflag || tag || date)
398         {
399             char *repos = Name_Repository (NULL, NULL);
400             WriteTag (NULL, tag, date, 0, ".", repos);
401             free (repos);
402             rewrite_tag = 1;
403             nonbranch = -1;
404             warned = 0;
405         }
406     }
407
408     /* look for files/dirs locally and in the repository */
409     which = W_LOCAL | W_REPOS;
410
411     /* look in the attic too if a tag or date is specified */
412     if (tag != NULL || date != NULL || joining())
413         which |= W_ATTIC;
414
415     /* call the command line interface */
416     err = do_update (argc, argv, options, tag, date, force_tag_match,
417                      local, update_build_dirs, aflag, update_prune_dirs,
418                      pipeout, which, join_rev1, join_rev2, NULL, 1, NULL);
419
420     /* free the space Make_Date allocated if necessary */
421     if (date != NULL)
422         free (date);
423
424     return err;
425 }
426
427
428
429 /*
430  * Command line interface to update (used by checkout)
431  *
432  * repository = cvsroot->repository + update_dir.  This is necessary for
433  * checkout so that start_recursion can determine our repository.  In the
434  * update case, start_recursion can use the CVS/Root & CVS/Repository file
435  * to determine this value.
436  */
437 int
438 do_update (int argc, char **argv, char *xoptions, char *xtag, char *xdate,
439            int xforce, int local, int xbuild, int xaflag, int xprune,
440            int xpipeout, int which, char *xjoin_rev1, char *xjoin_rev2,
441            char *preload_update_dir, int xdotemplate, char *repository)
442 {
443     int err = 0;
444     char *cp;
445
446     /* fill in the statics */
447     options = xoptions;
448     tag = xtag;
449     date = xdate;
450     force_tag_match = xforce;
451     update_build_dirs = xbuild;
452     aflag = xaflag;
453     update_prune_dirs = xprune;
454     pipeout = xpipeout;
455     dotemplate = xdotemplate;
456
457     /* setup the join support */
458     join_rev1 = xjoin_rev1;
459     join_rev2 = xjoin_rev2;
460     if (join_rev1 && (cp = strchr (join_rev1, ':')) != NULL)
461     {
462         *cp++ = '\0';
463         date_rev1 = Make_Date (cp);
464     }
465     else
466         date_rev1 = NULL;
467     if (join_rev2 && (cp = strchr (join_rev2, ':')) != NULL)
468     {
469         *cp++ = '\0';
470         date_rev2 = Make_Date (cp);
471     }
472     else
473         date_rev2 = NULL;
474
475 #ifdef PRESERVE_PERMISSIONS_SUPPORT
476     if (preserve_perms)
477     {
478         /* We need to do an extra recursion, bleah.  It's to make sure
479            that we know as much as possible about file linkage. */
480         hardlist = getlist();
481         working_dir = xgetwd();         /* save top-level working dir */
482
483         /* FIXME-twp: the arguments to start_recursion make me dizzy.  This
484            function call was copied from the update_fileproc call that
485            follows it; someone should make sure that I did it right. */
486         err = start_recursion
487             (get_linkinfo_proc, NULL, NULL, NULL, NULL,
488              argc, argv, local, which, aflag, CVS_LOCK_READ,
489              preload_update_dir, 1, NULL);
490         if (err)
491             return err;
492
493         /* FIXME-twp: at this point we should walk the hardlist
494            and update the `links' field of each hardlink_info struct
495            to list the files that are linked on dist.  That would make
496            it easier & more efficient to compare the disk linkage with
497            the repository linkage (a simple strcmp). */
498     }
499 #endif
500
501     /* call the recursion processor */
502     err = start_recursion (update_fileproc, update_filesdone_proc,
503                            update_dirent_proc, update_dirleave_proc, NULL,
504                            argc, argv, local, which, aflag, CVS_LOCK_READ,
505                            preload_update_dir, 1, repository);
506
507 #ifdef SERVER_SUPPORT
508     if (server_active)
509         return err;
510 #endif
511
512     /* see if we need to sleep before returning to avoid time-stamp races */
513     if (last_register_time)
514     {
515         sleep_past (last_register_time);
516     }
517
518     return err;
519 }
520
521
522
523 #ifdef PRESERVE_PERMISSIONS_SUPPORT
524 /*
525  * The get_linkinfo_proc callback adds each file to the hardlist
526  * (see hardlink.c).
527  */
528
529 static int
530 get_linkinfo_proc( void *callerdat, struct file_info *finfo )
531 {
532     char *fullpath;
533     Node *linkp;
534     struct hardlink_info *hlinfo;
535
536     /* Get the full pathname of the current file. */
537     fullpath = xmalloc (strlen(working_dir) +
538                         strlen(finfo->fullname) + 2);
539     sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
540
541     /* To permit recursing into subdirectories, files
542        are keyed on the full pathname and not on the basename. */
543     linkp = lookup_file_by_inode (fullpath);
544     if (linkp == NULL)
545     {
546         /* The file isn't on disk; we are probably restoring
547            a file that was removed. */
548         return 0;
549     }
550     
551     /* Create a new, empty hardlink_info node. */
552     hlinfo = (struct hardlink_info *)
553         xmalloc (sizeof (struct hardlink_info));
554
555     hlinfo->status = (Ctype) 0; /* is this dumb? */
556     hlinfo->checked_out = 0;
557
558     linkp->data = hlinfo;
559
560     return 0;
561 }
562 #endif
563
564
565
566 /*
567  * This is the callback proc for update.  It is called for each file in each
568  * directory by the recursion code.  The current directory is the local
569  * instantiation.  file is the file name we are to operate on. update_dir is
570  * set to the path relative to where we started (for pretty printing).
571  * repository is the repository. entries and srcfiles are the pre-parsed
572  * entries and source control files.
573  * 
574  * This routine decides what needs to be done for each file and does the
575  * appropriate magic for checkout
576  */
577 static int
578 update_fileproc (void *callerdat, struct file_info *finfo)
579 {
580     int retval, nb;
581     Ctype status;
582     Vers_TS *vers;
583
584     status = Classify_File (finfo, tag, date, options, force_tag_match,
585                             aflag, &vers, pipeout);
586
587     /* Keep track of whether TAG is a branch tag.
588        Note that if it is a branch tag in some files and a nonbranch tag
589        in others, treat it as a nonbranch tag.  */
590     if (rewrite_tag
591         && tag != NULL
592         && finfo->rcs != NULL)
593     {
594         char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL);
595         if (rev != NULL
596             && nonbranch != (nb = !RCS_nodeisbranch (finfo->rcs, tag)))
597         {
598             if (nonbranch >= 0 && !warned && !quiet)
599             {
600                 error (0, 0,
601 "warning: %s is a branch tag in some files and a revision tag in others.",
602                         tag);
603                 warned = 1;
604             }
605             if (nonbranch < nb) nonbranch = nb;
606         }
607         if (rev != NULL)
608             free (rev);
609     }
610
611     if (pipeout)
612     {
613         /*
614          * We just return success without doing anything if any of the really
615          * funky cases occur
616          * 
617          * If there is still a valid RCS file, do a regular checkout type
618          * operation
619          */
620         switch (status)
621         {
622             case T_UNKNOWN:             /* unknown file was explicitly asked
623                                          * about */
624             case T_REMOVE_ENTRY:        /* needs to be un-registered */
625             case T_ADDED:               /* added but not committed */
626                 retval = 0;
627                 break;
628             case T_CONFLICT:            /* old punt-type errors */
629                 retval = 1;
630                 break;
631             case T_UPTODATE:            /* file was already up-to-date */
632             case T_NEEDS_MERGE:         /* needs merging */
633             case T_MODIFIED:            /* locally modified */
634             case T_REMOVED:             /* removed but not committed */
635             case T_CHECKOUT:            /* needs checkout */
636             case T_PATCH:               /* needs patch */
637                 retval = checkout_file (finfo, vers, 0, 0, 0);
638                 break;
639
640             default:                    /* can't ever happen :-) */
641                 error (0, 0,
642                        "unknown file status %d for file %s", status, finfo->file);
643                 retval = 0;
644                 break;
645         }
646     }
647     else
648     {
649         switch (status)
650         {
651             case T_UNKNOWN:             /* unknown file was explicitly asked
652                                          * about */
653             case T_UPTODATE:            /* file was already up-to-date */
654                 retval = 0;
655                 break;
656             case T_CONFLICT:            /* old punt-type errors */
657                 retval = 1;
658                 write_letter (finfo, 'C');
659                 break;
660             case T_NEEDS_MERGE:         /* needs merging */
661                 if (! toss_local_changes)
662                 {
663                     retval = merge_file (finfo, vers);
664                     break;
665                 }
666                 /* else FALL THROUGH */
667             case T_MODIFIED:            /* locally modified */
668                 retval = 0;
669                 if (toss_local_changes)
670                 {
671                     char *bakname;
672                     bakname = backup_file (finfo->file, vers->vn_user);
673                     /* This behavior is sufficiently unexpected to
674                        justify overinformativeness, I think. */
675 #ifdef SERVER_SUPPORT
676                     if ((! really_quiet) && (! server_active))
677 #else /* ! SERVER_SUPPORT */
678                     if (! really_quiet)
679 #endif /* SERVER_SUPPORT */
680                         (void) printf ("(Locally modified %s moved to %s)\n",
681                                        finfo->file, bakname);
682                     free (bakname);
683
684                     /* The locally modified file is still present, but
685                        it will be overwritten by the repository copy
686                        after this. */
687                     status = T_CHECKOUT;
688                     retval = checkout_file (finfo, vers, 0, 0, 1);
689                 }
690                 else 
691                 {
692                     if (vers->ts_conflict)
693                     {
694                         if (file_has_conflict (finfo, vers->ts_conflict)
695                             || file_has_markers (finfo))
696                         {
697                             write_letter (finfo, 'C');
698                             retval = 1;
699                         }
700                         else
701                         {
702                             /* Reregister to clear conflict flag. */
703                             Register (finfo->entries, finfo->file, 
704                                       vers->vn_rcs, vers->ts_rcs,
705                                       vers->options, vers->tag,
706                                       vers->date, (char *)0);
707                         }
708                     }
709                     if (!retval)
710                         write_letter (finfo, 'M');
711                 }
712                 break;
713             case T_PATCH:               /* needs patch */
714 #ifdef SERVER_SUPPORT
715                 if (patches)
716                 {
717                     int docheckout;
718                     struct stat file_info;
719                     unsigned char checksum[16];
720
721                     retval = patch_file (finfo,
722                                          vers, &docheckout,
723                                          &file_info, checksum);
724                     if (! docheckout)
725                     {
726                         if (server_active && retval == 0)
727                             server_updated (finfo, vers,
728                                             (rcs_diff_patches
729                                              ? SERVER_RCS_DIFF
730                                              : SERVER_PATCHED),
731                                             file_info.st_mode, checksum,
732                                             (struct buffer *) NULL);
733                         break;
734                     }
735                 }
736 #endif
737                 /* If we're not running as a server, just check the
738                    file out.  It's simpler and faster than producing
739                    and applying patches.  */
740                 /* Fall through.  */
741             case T_CHECKOUT:            /* needs checkout */
742                 retval = checkout_file (finfo, vers, 0, 0, 1);
743                 break;
744             case T_ADDED:               /* added but not committed */
745                 write_letter (finfo, 'A');
746                 retval = 0;
747                 break;
748             case T_REMOVED:             /* removed but not committed */
749                 write_letter (finfo, 'R');
750                 retval = 0;
751                 break;
752             case T_REMOVE_ENTRY:        /* needs to be un-registered */
753                 retval = scratch_file (finfo, vers);
754                 break;
755             default:                    /* can't ever happen :-) */
756                 error (0, 0,
757                        "unknown file status %d for file %s", status, finfo->file);
758                 retval = 0;
759                 break;
760         }
761     }
762
763     /* only try to join if things have gone well thus far */
764     if (retval == 0 && join_rev1)
765         join_file (finfo, vers);
766
767     /* if this directory has an ignore list, add this file to it */
768     if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL))
769     {
770         Node *p;
771
772         p = getnode ();
773         p->type = FILES;
774         p->key = xstrdup (finfo->file);
775         if (addnode (ignlist, p) != 0)
776             freenode (p);
777     }
778
779     freevers_ts (&vers);
780     return retval;
781 }
782
783
784
785 static void
786 update_ignproc (const char *file, const char *dir)
787 {
788     struct file_info finfo;
789     char *tmp;
790
791     memset (&finfo, 0, sizeof (finfo));
792     finfo.file = file;
793     finfo.update_dir = dir;
794     if (dir[0] == '\0')
795         tmp = xstrdup (file);
796     else
797     {
798         tmp = xmalloc (strlen (file) + strlen (dir) + 10);
799         strcpy (tmp, dir);
800         strcat (tmp, "/");
801         strcat (tmp, file);
802     }
803
804     finfo.fullname = tmp;
805     write_letter (&finfo, '?');
806     free (tmp);
807 }
808
809
810
811 /* ARGSUSED */
812 static int
813 update_filesdone_proc (void *callerdat, int err, const char *repository,
814                        const char *update_dir, List *entries)
815 {
816     if (nonbranch < 0) nonbranch = 0;
817     if (rewrite_tag)
818     {
819         WriteTag (NULL, tag, date, nonbranch, update_dir, repository);
820         rewrite_tag = 0;
821     }
822
823     /* if this directory has an ignore list, process it then free it */
824     if (ignlist)
825     {
826         ignore_files (ignlist, entries, update_dir, update_ignproc);
827         dellist (&ignlist);
828     }
829
830     /* Clean up CVS admin dirs if we are export */
831     if (strcmp (cvs_cmd_name, "export") == 0)
832     {
833         /* I'm not sure the existence_error is actually possible (except
834            in cases where we really should print a message), but since
835            this code used to ignore all errors, I'll play it safe.  */
836         if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
837             error (0, errno, "cannot remove %s directory", CVSADM);
838     }
839 #ifdef SERVER_SUPPORT
840     else if (!server_active && !pipeout)
841 #else
842     else if (!pipeout)
843 #endif /* SERVER_SUPPORT */
844     {
845         /* If there is no CVS/Root file, add one */
846         if (!isfile (CVSADM_ROOT))
847             Create_Root (NULL, current_parsed_root->original);
848     }
849
850     return err;
851 }
852
853
854
855 /*
856  * update_dirent_proc () is called back by the recursion processor before a
857  * sub-directory is processed for update.  In this case, update_dirent proc
858  * will probably create the directory unless -d isn't specified and this is a
859  * new directory.  A return code of 0 indicates the directory should be
860  * processed by the recursion code.  A return of non-zero indicates the
861  * recursion code should skip this directory.
862  */
863 static Dtype
864 update_dirent_proc (void *callerdat, const char *dir, const char *repository,
865                     const char *update_dir, List *entries)
866 {
867     if (ignore_directory (update_dir))
868     {
869         /* print the warm fuzzy message */
870         if (!quiet)
871           error (0, 0, "Ignoring %s", update_dir);
872         return R_SKIP_ALL;
873     }
874
875     if (!isdir (dir))
876     {
877         /* if we aren't building dirs, blow it off */
878         if (!update_build_dirs)
879             return R_SKIP_ALL;
880
881         /* Various CVS administrators are in the habit of removing
882            the repository directory for things they don't want any
883            more.  I've even been known to do it myself (on rare
884            occasions).  Not the usual recommended practice, but we
885            want to try to come up with some kind of
886            reasonable/documented/sensible behavior.  Generally
887            the behavior is to just skip over that directory (see
888            dirs test in sanity.sh; the case which reaches here
889            is when update -d is specified, and the working directory
890            is gone but the subdirectory is still mentioned in
891            CVS/Entries).  */
892         if (1
893 #ifdef SERVER_SUPPORT
894             /* In the remote case, the client should refrain from
895                sending us the directory in the first place.  So we
896                want to continue to give an error, so clients make
897                sure to do this.  */
898             && !server_active
899 #endif
900             && !isdir (repository))
901             return R_SKIP_ALL;
902
903         if (noexec)
904         {
905             /* ignore the missing dir if -n is specified */
906             error (0, 0, "New directory `%s' -- ignored", update_dir);
907             return R_SKIP_ALL;
908         }
909         else
910         {
911             /* otherwise, create the dir and appropriate adm files */
912
913             /* If no tag or date were specified on the command line,
914                and we're not using -A, we want the subdirectory to use
915                the tag and date, if any, of the current directory.
916                That way, update -d will work correctly when working on
917                a branch.
918
919                We use TAG_UPDATE_DIR to undo the tag setting in
920                update_dirleave_proc.  If we did not do this, we would
921                not correctly handle a working directory with multiple
922                tags (and maybe we should prohibit such working
923                directories, but they work now and we shouldn't make
924                them stop working without more thought).  */
925             if ((tag == NULL && date == NULL) && ! aflag)
926             {
927                 ParseTag (&tag, &date, &nonbranch);
928                 if (tag != NULL || date != NULL)
929                     tag_update_dir = xstrdup (update_dir);
930             }
931
932             make_directory (dir);
933             Create_Admin (dir, update_dir, repository, tag, date,
934                           /* This is a guess.  We will rewrite it later
935                              via WriteTag.  */
936                           0,
937                           0,
938                           dotemplate);
939             rewrite_tag = 1;
940             nonbranch = -1;
941             warned = 0;
942             Subdir_Register (entries, NULL, dir);
943         }
944     }
945     /* Do we need to check noexec here? */
946     else if (!pipeout)
947     {
948         char *cvsadmdir;
949
950         /* The directory exists.  Check to see if it has a CVS
951            subdirectory.  */
952
953         cvsadmdir = xmalloc (strlen (dir) + 80);
954         strcpy (cvsadmdir, dir);
955         strcat (cvsadmdir, "/");
956         strcat (cvsadmdir, CVSADM);
957
958         if (!isdir (cvsadmdir))
959         {
960             /* We cannot successfully recurse into a directory without a CVS
961                subdirectory.  Generally we will have already printed
962                "? foo".  */
963             free (cvsadmdir);
964             return R_SKIP_ALL;
965         }
966         free (cvsadmdir);
967     }
968
969     /*
970      * If we are building dirs and not going to stdout, we make sure there is
971      * no static entries file and write the tag file as appropriate
972      */
973     if (!pipeout)
974     {
975         if (update_build_dirs)
976         {
977             char *tmp;
978
979             tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ENTSTAT) + 10);
980             (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
981             if (unlink_file (tmp) < 0 && ! existence_error (errno))
982                 error (1, errno, "cannot remove file %s", tmp);
983 #ifdef SERVER_SUPPORT
984             if (server_active)
985                 server_clear_entstat (update_dir, repository);
986 #endif
987             free (tmp);
988         }
989
990         /* keep the CVS/Tag file current with the specified arguments */
991         if (aflag || tag || date)
992         {
993             WriteTag (dir, tag, date, 0, update_dir, repository);
994             rewrite_tag = 1;
995             nonbranch = -1;
996             warned = 0;
997         }
998
999         WriteTemplate (update_dir, dotemplate, repository);
1000
1001         /* initialize the ignore list for this directory */
1002         ignlist = getlist ();
1003     }
1004
1005     /* print the warm fuzzy message */
1006     if (!quiet)
1007         error (0, 0, "Updating %s", update_dir);
1008
1009     return R_PROCESS;
1010 }
1011
1012
1013
1014 /*
1015  * update_dirleave_proc () is called back by the recursion code upon leaving
1016  * a directory.  It will prune empty directories if needed and will execute
1017  * any appropriate update programs.
1018  */
1019 /* ARGSUSED */
1020 static int
1021 update_dirleave_proc (void *callerdat, const char *dir, int err,
1022                       const char *update_dir, List *entries)
1023 {
1024     /* Delete the ignore list if it hasn't already been done.  */
1025     if (ignlist)
1026         dellist (&ignlist);
1027
1028     /* If we set the tag or date for a new subdirectory in
1029        update_dirent_proc, and we're now done with that subdirectory,
1030        undo the tag/date setting.  Note that we know that the tag and
1031        date were both originally NULL in this case.  */
1032     if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
1033     {
1034         if (tag != NULL)
1035         {
1036             free (tag);
1037             tag = NULL;
1038         }
1039         if (date != NULL)
1040         {
1041             free (date);
1042             date = NULL;
1043         }
1044         nonbranch = -1;
1045         warned = 0;
1046         free (tag_update_dir);
1047         tag_update_dir = NULL;
1048     }
1049
1050     if (strchr (dir, '/') == NULL)
1051     {
1052         /* FIXME: chdir ("..") loses with symlinks.  */
1053         /* Prune empty dirs on the way out - if necessary */
1054         (void) CVS_CHDIR ("..");
1055         if (update_prune_dirs && isemptydir (dir, 0))
1056         {
1057             /* I'm not sure the existence_error is actually possible (except
1058                in cases where we really should print a message), but since
1059                this code used to ignore all errors, I'll play it safe.  */
1060             if (unlink_file_dir (dir) < 0 && !existence_error (errno))
1061                 error (0, errno, "cannot remove %s directory", dir);
1062             Subdir_Deregister (entries, NULL, dir);
1063         }
1064     }
1065
1066     return err;
1067 }
1068
1069
1070
1071 /* Returns 1 if the file indicated by node has been removed.  */
1072 static int
1073 isremoved (Node *node, void *closure)
1074 {
1075     Entnode *entdata = node->data;
1076
1077     /* If the first character of the version is a '-', the file has been
1078        removed. */
1079     return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
1080 }
1081
1082
1083
1084 /* Returns 1 if the argument directory is completely empty, other than the
1085    existence of the CVS directory entry.  Zero otherwise.  If MIGHT_NOT_EXIST
1086    and the directory doesn't exist, then just return 0.  */
1087 int
1088 isemptydir (const char *dir, int might_not_exist)
1089 {
1090     DIR *dirp;
1091     struct dirent *dp;
1092
1093     if ((dirp = CVS_OPENDIR (dir)) == NULL)
1094     {
1095         if (might_not_exist && existence_error (errno))
1096             return 0;
1097         error (0, errno, "cannot open directory %s for empty check", dir);
1098         return 0;
1099     }
1100     errno = 0;
1101     while ((dp = CVS_READDIR (dirp)) != NULL)
1102     {
1103         if (strcmp (dp->d_name, ".") != 0
1104             && strcmp (dp->d_name, "..") != 0)
1105         {
1106             if (strcmp (dp->d_name, CVSADM) != 0)
1107             {
1108                 /* An entry other than the CVS directory.  The directory
1109                    is certainly not empty. */
1110                 (void) CVS_CLOSEDIR (dirp);
1111                 return 0;
1112             }
1113             else
1114             {
1115                 /* The CVS directory entry.  We don't have to worry about
1116                    this unless the Entries file indicates that files have
1117                    been removed, but not committed, in this directory.
1118                    (Removing the directory would prevent people from
1119                    comitting the fact that they removed the files!) */
1120                 List *l;
1121                 int files_removed;
1122                 struct saved_cwd cwd;
1123
1124                 if (save_cwd (&cwd))
1125                     exit (EXIT_FAILURE);
1126
1127                 if (CVS_CHDIR (dir) < 0)
1128                     error (1, errno, "cannot change directory to %s", dir);
1129                 l = Entries_Open (0, NULL);
1130                 files_removed = walklist (l, isremoved, 0);
1131                 Entries_Close (l);
1132
1133                 if (restore_cwd (&cwd, NULL))
1134                     exit (EXIT_FAILURE);
1135                 free_cwd (&cwd);
1136
1137                 if (files_removed != 0)
1138                 {
1139                     /* There are files that have been removed, but not
1140                        committed!  Do not consider the directory empty. */
1141                     (void) CVS_CLOSEDIR (dirp);
1142                     return 0;
1143                 }
1144             }
1145         }
1146         errno = 0;
1147     }
1148     if (errno != 0)
1149     {
1150         error (0, errno, "cannot read directory %s", dir);
1151         (void) CVS_CLOSEDIR (dirp);
1152         return 0;
1153     }
1154     (void) CVS_CLOSEDIR (dirp);
1155     return 1;
1156 }
1157
1158
1159
1160 /*
1161  * scratch the Entries file entry associated with a file
1162  */
1163 static int
1164 scratch_file (struct file_info *finfo, Vers_TS *vers)
1165 {
1166     history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
1167     Scratch_Entry (finfo->entries, finfo->file);
1168 #ifdef SERVER_SUPPORT
1169     if (server_active)
1170     {
1171         if (vers->ts_user == NULL)
1172             server_scratch_entry_only ();
1173         server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1, NULL, NULL);
1174     }
1175 #endif
1176     if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1177         error (0, errno, "unable to remove %s", finfo->fullname);
1178     else
1179 #ifdef SERVER_SUPPORT
1180         /* skip this step when the server is running since
1181          * server_updated should have handled it */
1182         if (!server_active)
1183 #endif
1184     {
1185         /* keep the vers structure up to date in case we do a join
1186          * - if there isn't a file, it can't very well have a version number, can it?
1187          */
1188         if (vers->vn_user != NULL)
1189         {
1190             free (vers->vn_user);
1191             vers->vn_user = NULL;
1192         }
1193         if (vers->ts_user != NULL)
1194         {
1195             free (vers->ts_user);
1196             vers->ts_user = NULL;
1197         }
1198     }
1199     return 0;
1200 }
1201
1202
1203
1204 /*
1205  * Check out a file.
1206  */
1207 static int
1208 checkout_file (struct file_info *finfo, Vers_TS *vers_ts, int adding,
1209                int merging, int update_server)
1210 {
1211     char *backup;
1212     int set_time, retval = 0;
1213     int status;
1214     int file_is_dead;
1215     struct buffer *revbuf;
1216
1217     backup = NULL;
1218     revbuf = NULL;
1219
1220     /* Don't screw with backup files if we're going to stdout, or if
1221        we are the server.  */
1222     if (!pipeout
1223 #ifdef SERVER_SUPPORT
1224         && ! server_active
1225 #endif
1226         )
1227     {
1228         backup = xmalloc (strlen (finfo->file)
1229                           + sizeof (CVSADM)
1230                           + sizeof (CVSPREFIX)
1231                           + 10);
1232         (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1233         if (isfile (finfo->file))
1234             rename_file (finfo->file, backup);
1235         else
1236         {
1237             /* If -f/-t wrappers are being used to wrap up a directory,
1238                then backup might be a directory instead of just a file.  */
1239             if (unlink_file_dir (backup) < 0)
1240             {
1241                 /* Not sure if the existence_error check is needed here.  */
1242                 if (!existence_error (errno))
1243                     /* FIXME: should include update_dir in message.  */
1244                     error (0, errno, "error removing %s", backup);
1245             }
1246             free (backup);
1247             backup = NULL;
1248         }
1249     }
1250
1251     file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
1252
1253     if (!file_is_dead)
1254     {
1255         /*
1256          * if we are checking out to stdout, print a nice message to
1257          * stderr, and add the -p flag to the command */
1258         if (pipeout)
1259         {
1260             if (!quiet)
1261             {
1262                 cvs_outerr ("\
1263 ===================================================================\n\
1264 Checking out ", 0);
1265                 cvs_outerr (finfo->fullname, 0);
1266                 cvs_outerr ("\n\
1267 RCS:  ", 0);
1268                 cvs_outerr (vers_ts->srcfile->path, 0);
1269                 cvs_outerr ("\n\
1270 VERS: ", 0);
1271                 cvs_outerr (vers_ts->vn_rcs, 0);
1272                 cvs_outerr ("\n***************\n", 0);
1273             }
1274         }
1275
1276 #ifdef SERVER_SUPPORT
1277         if (update_server
1278             && server_active
1279             && ! pipeout
1280             && ! file_gzip_level
1281             && ! joining ()
1282             && ! wrap_name_has (finfo->file, WRAP_FROMCVS))
1283         {
1284             revbuf = buf_nonio_initialize (NULL);
1285             status = RCS_checkout (vers_ts->srcfile, NULL,
1286                                    vers_ts->vn_rcs, vers_ts->tag,
1287                                    vers_ts->options, RUN_TTY,
1288                                    checkout_to_buffer, revbuf);
1289         }
1290         else
1291 #endif
1292             status = RCS_checkout (vers_ts->srcfile,
1293                                    pipeout ? NULL : finfo->file,
1294                                    vers_ts->vn_rcs, vers_ts->tag,
1295                                    vers_ts->options, RUN_TTY, NULL, NULL);
1296     }
1297     if (file_is_dead || status == 0)
1298     {
1299         mode_t mode;
1300
1301         mode = (mode_t) -1;
1302
1303         if (!pipeout)
1304         {
1305             Vers_TS *xvers_ts;
1306
1307             if (revbuf != NULL && !noexec)
1308             {
1309                 struct stat sb;
1310
1311                 /* FIXME: We should have RCS_checkout return the mode.
1312                    That would also fix the kludge with noexec, above, which
1313                    is here only because noexec doesn't write srcfile->path
1314                    for us to stat.  */
1315                 if( CVS_STAT( vers_ts->srcfile->path, &sb ) < 0 )
1316                 {
1317                     buf_free (revbuf);
1318                     error (1, errno, "cannot stat %s",
1319                            vers_ts->srcfile->path);
1320                 }
1321                 mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
1322             }
1323
1324             if (cvswrite
1325                 && !file_is_dead
1326                 && !fileattr_get (finfo->file, "_watched"))
1327             {
1328                 if (revbuf == NULL)
1329                     xchmod (finfo->file, 1);
1330                 else
1331                 {
1332                     /* We know that we are the server here, so
1333                        although xchmod checks umask, we don't bother.  */
1334                     mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
1335                              | ((mode & S_IRGRP) ? S_IWGRP : 0)
1336                              | ((mode & S_IROTH) ? S_IWOTH : 0));
1337                 }
1338             }
1339
1340             {
1341                 /* A newly checked out file is never under the spell
1342                    of "cvs edit".  If we think we were editing it
1343                    from a previous life, clean up.  Would be better to
1344                    check for same the working directory instead of
1345                    same user, but that is hairy.  */
1346
1347                 struct addremove_args args;
1348
1349                 editor_set (finfo->file, getcaller (), NULL);
1350
1351                 memset (&args, 0, sizeof args);
1352                 args.remove_temp = 1;
1353                 watch_modify_watchers (finfo->file, &args);
1354             }
1355
1356             /* set the time from the RCS file iff it was unknown before */
1357             set_time =
1358                 (!noexec
1359                  && (vers_ts->vn_user == NULL ||
1360                      strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
1361                  && !file_is_dead);
1362
1363             wrap_fromcvs_process_file (finfo->file);
1364
1365             xvers_ts = Version_TS (finfo, options, tag, date, 
1366                                    force_tag_match, set_time);
1367             if (strcmp (xvers_ts->options, "-V4") == 0)
1368                 xvers_ts->options[0] = '\0';
1369
1370             if (revbuf != NULL)
1371             {
1372                 /* If we stored the file data into a buffer, then we
1373                    didn't create a file at all, so xvers_ts->ts_user
1374                    is wrong.  The correct value is to have it be the
1375                    same as xvers_ts->ts_rcs, meaning that the working
1376                    file is unchanged from the RCS file.
1377
1378                    FIXME: We should tell Version_TS not to waste time
1379                    statting the nonexistent file.
1380
1381                    FIXME: Actually, I don't think the ts_user value
1382                    matters at all here.  The only use I know of is
1383                    that it is printed in a trace message by
1384                    Server_Register.  */
1385
1386                 if (xvers_ts->ts_user != NULL)
1387                     free (xvers_ts->ts_user);
1388                 xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
1389             }
1390
1391             (void) time (&last_register_time);
1392
1393             if (file_is_dead)
1394             {
1395                 if (xvers_ts->vn_user != NULL)
1396                 {
1397                     error (0, 0,
1398                            "warning: %s is not (any longer) pertinent",
1399                            finfo->fullname);
1400                 }
1401                 Scratch_Entry (finfo->entries, finfo->file);
1402 #ifdef SERVER_SUPPORT
1403                 if (server_active && xvers_ts->ts_user == NULL)
1404                     server_scratch_entry_only ();
1405 #endif
1406                 /* FIXME: Rather than always unlink'ing, and ignoring the
1407                    existence_error, we should do the unlink only if
1408                    vers_ts->ts_user is non-NULL.  Then there would be no
1409                    need to ignore an existence_error (for example, if the
1410                    user removes the file while we are running).  */
1411                 if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
1412                 {
1413                     error (0, errno, "cannot remove %s", finfo->fullname);
1414                 }
1415             }
1416             else
1417                 Register (finfo->entries, finfo->file,
1418                           adding ? "0" : xvers_ts->vn_rcs,
1419                           xvers_ts->ts_user, xvers_ts->options,
1420                           xvers_ts->tag, xvers_ts->date,
1421                           NULL); /* Clear conflict flag on fresh checkout */
1422
1423             /* fix up the vers structure, in case it is used by join */
1424             if (join_rev1)
1425             {
1426                 /* FIXME: Throwing away the original revision info is almost
1427                    certainly wrong -- what if join_rev1 is "BASE"?  */
1428                 if (vers_ts->vn_user != NULL)
1429                     free (vers_ts->vn_user);
1430                 if (vers_ts->vn_rcs != NULL)
1431                     free (vers_ts->vn_rcs);
1432                 vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1433                 vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1434             }
1435
1436             /* If this is really Update and not Checkout, recode history */
1437             if (strcmp (cvs_cmd_name, "update") == 0)
1438                 history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1439                                finfo->repository);
1440
1441             freevers_ts (&xvers_ts);
1442
1443             if (!really_quiet && !file_is_dead)
1444             {
1445                 write_letter (finfo, 'U');
1446             }
1447         }
1448
1449 #ifdef SERVER_SUPPORT
1450         if (update_server && server_active)
1451             server_updated (finfo, vers_ts,
1452                             merging ? SERVER_MERGED : SERVER_UPDATED,
1453                             mode, (unsigned char *) NULL, revbuf);
1454 #endif
1455     }
1456     else
1457     {
1458         if (backup != NULL)
1459         {
1460             rename_file (backup, finfo->file);
1461             free (backup);
1462             backup = NULL;
1463         }
1464
1465         error (0, 0, "could not check out %s", finfo->fullname);
1466
1467         retval = status;
1468     }
1469
1470     if (backup != NULL)
1471     {
1472         /* If -f/-t wrappers are being used to wrap up a directory,
1473            then backup might be a directory instead of just a file.  */
1474         if (unlink_file_dir (backup) < 0)
1475         {
1476             /* Not sure if the existence_error check is needed here.  */
1477             if (!existence_error (errno))
1478                 /* FIXME: should include update_dir in message.  */
1479                 error (0, errno, "error removing %s", backup);
1480         }
1481         free (backup);
1482     }
1483
1484     if (revbuf != NULL)
1485         buf_free (revbuf);
1486     return retval;
1487 }
1488
1489
1490
1491 #ifdef SERVER_SUPPORT
1492
1493 /* This function is used to write data from a file being checked out
1494    into a buffer.  */
1495
1496 static void
1497 checkout_to_buffer (void *callerdat, const char *data, size_t len)
1498 {
1499     struct buffer *buf = (struct buffer *) callerdat;
1500
1501     buf_output (buf, data, len);
1502 }
1503
1504 #endif /* SERVER_SUPPORT */
1505
1506 #ifdef SERVER_SUPPORT
1507
1508 /* This structure is used to pass information between patch_file and
1509    patch_file_write.  */
1510
1511 struct patch_file_data
1512 {
1513     /* File name, for error messages.  */
1514     const char *filename;
1515     /* File to which to write.  */
1516     FILE *fp;
1517     /* Whether to compute the MD5 checksum.  */
1518     int compute_checksum;
1519     /* Data structure for computing the MD5 checksum.  */
1520     struct cvs_MD5Context context;
1521     /* Set if the file has a final newline.  */
1522     int final_nl;
1523 };
1524
1525 /* Patch a file.  Runs diff.  This is only done when running as the
1526  * server.  The hope is that the diff will be smaller than the file
1527  * itself.
1528  */
1529 static int
1530 patch_file (struct file_info *finfo, Vers_TS *vers_ts, int *docheckout, struct stat *file_info, unsigned char *checksum)
1531 {
1532     char *backup;
1533     char *file1;
1534     char *file2;
1535     int retval = 0;
1536     int retcode = 0;
1537     int fail;
1538     FILE *e;
1539     struct patch_file_data data;
1540
1541     *docheckout = 0;
1542
1543     if (noexec || pipeout || joining ())
1544     {
1545         *docheckout = 1;
1546         return 0;
1547     }
1548
1549     /* If this file has been marked as being binary, then never send a
1550        patch.  */
1551     if (strcmp (vers_ts->options, "-kb") == 0)
1552     {
1553         *docheckout = 1;
1554         return 0;
1555     }
1556
1557     /* First check that the first revision exists.  If it has been nuked
1558        by cvs admin -o, then just fall back to checking out entire
1559        revisions.  In some sense maybe we don't have to do this; after
1560        all cvs.texinfo says "Make sure that no-one has checked out a
1561        copy of the revision you outdate" but then again, that advice
1562        doesn't really make complete sense, because "cvs admin" operates
1563        on a working directory and so _someone_ will almost always have
1564        _some_ revision checked out.  */
1565     {
1566         char *rev;
1567
1568         rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
1569         if (rev == NULL)
1570         {
1571             *docheckout = 1;
1572             return 0;
1573         }
1574         else
1575             free (rev);
1576     }
1577
1578     /* If the revision is dead, let checkout_file handle it rather
1579        than duplicating the processing here.  */
1580     if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
1581     {
1582         *docheckout = 1;
1583         return 0;
1584     }
1585
1586     backup = xmalloc (strlen (finfo->file)
1587                       + sizeof (CVSADM)
1588                       + sizeof (CVSPREFIX)
1589                       + 10);
1590     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1591     if (isfile (finfo->file))
1592         rename_file (finfo->file, backup);
1593     else
1594     {
1595         if (unlink_file (backup) < 0
1596             && !existence_error (errno))
1597             error (0, errno, "cannot remove %s", backup);
1598     }
1599
1600     file1 = xmalloc (strlen (finfo->file)
1601                      + sizeof (CVSADM)
1602                      + sizeof (CVSPREFIX)
1603                      + 10);
1604     (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
1605     file2 = xmalloc (strlen (finfo->file)
1606                      + sizeof (CVSADM)
1607                      + sizeof (CVSPREFIX)
1608                      + 10);
1609     (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
1610
1611     fail = 0;
1612
1613     /* We need to check out both revisions first, to see if either one
1614        has a trailing newline.  Because of this, we don't use rcsdiff,
1615        but just use diff.  */
1616
1617     e = CVS_FOPEN (file1, "w");
1618     if (e == NULL)
1619         error (1, errno, "cannot open %s", file1);
1620
1621     data.filename = file1;
1622     data.fp = e;
1623     data.final_nl = 0;
1624     data.compute_checksum = 0;
1625
1626     /* FIXME - Passing vers_ts->tag here is wrong in the least number
1627      * of cases.  Since we don't know whether vn_user was checked out
1628      * using a tag, we pass vers_ts->tag, which, assuming the user did
1629      * not specify a new TAG to -r, will be the branch we are on.
1630      *
1631      * The only thing it is used for is to substitute in for the Name
1632      * RCS keyword, so in the error case, the patch fails to apply on
1633      * the client end and we end up resending the whole file.
1634      *
1635      * At least, if we are keeping track of the tag vn_user came from,
1636      * I don't know where yet. -DRP
1637      */
1638     retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1639                             vers_ts->vn_user, vers_ts->tag,
1640                             vers_ts->options, RUN_TTY,
1641                             patch_file_write, (void *) &data);
1642
1643     if (fclose (e) < 0)
1644         error (1, errno, "cannot close %s", file1);
1645
1646     if (retcode != 0 || ! data.final_nl)
1647         fail = 1;
1648
1649     if (! fail)
1650     {
1651         e = CVS_FOPEN (file2, "w");
1652         if (e == NULL)
1653             error (1, errno, "cannot open %s", file2);
1654
1655         data.filename = file2;
1656         data.fp = e;
1657         data.final_nl = 0;
1658         data.compute_checksum = 1;
1659         cvs_MD5Init (&data.context);
1660
1661         retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1662                                 vers_ts->vn_rcs, vers_ts->tag,
1663                                 vers_ts->options, RUN_TTY,
1664                                 patch_file_write, (void *) &data);
1665
1666         if (fclose (e) < 0)
1667             error (1, errno, "cannot close %s", file2);
1668
1669         if (retcode != 0 || ! data.final_nl)
1670             fail = 1;
1671         else
1672             cvs_MD5Final (checksum, &data.context);
1673     }     
1674
1675     retcode = 0;
1676     if (! fail)
1677     {
1678         char *diff_options;
1679
1680         /* If the client does not support the Rcs-diff command, we
1681            send a context diff, and the client must invoke patch.
1682            That approach was problematical for various reasons.  The
1683            new approach only requires running diff in the server; the
1684            client can handle everything without invoking an external
1685            program.  */
1686         if (! rcs_diff_patches)
1687         {
1688             /* We use -c, not -u, because that is what CVS has
1689                traditionally used.  Kind of a moot point, now that
1690                Rcs-diff is preferred, so there is no point in making
1691                the compatibility issues worse.  */
1692             diff_options = "-c";
1693         }
1694         else
1695         {
1696             /* Now that diff is librarified, we could be passing -a if
1697                we wanted to.  However, it is unclear to me whether we
1698                would want to.  Does diff -a, in any significant
1699                percentage of cases, produce patches which are smaller
1700                than the files it is patching?  I guess maybe text
1701                files with character sets which diff regards as
1702                'binary'.  Conversely, do they tend to be much larger
1703                in the bad cases?  This needs some more
1704                thought/investigation, I suspect.  */
1705
1706             diff_options = "-n";
1707         }
1708         retcode = diff_exec (file1, file2, NULL, NULL, diff_options, finfo->file);
1709
1710         /* A retcode of 0 means no differences.  1 means some differences.  */
1711         if (retcode != 0
1712             && retcode != 1)
1713         {
1714             fail = 1;
1715         }
1716     }
1717
1718     if (! fail)
1719     {
1720         struct stat file2_info;
1721
1722         /* Check to make sure the patch is really shorter */
1723         if (CVS_STAT (file2, &file2_info) < 0)
1724             error (1, errno, "could not stat %s", file2);
1725         if (CVS_STAT (finfo->file, file_info) < 0)
1726             error (1, errno, "could not stat %s", finfo->file);
1727         if (file2_info.st_size <= file_info->st_size)
1728             fail = 1;
1729     }
1730
1731     if (! fail)
1732     {
1733 # define BINARY "Binary"
1734         char buf[sizeof BINARY];
1735         unsigned int c;
1736
1737         /* Check the diff output to make sure patch will be handle it.  */
1738         e = CVS_FOPEN (finfo->file, "r");
1739         if (e == NULL)
1740             error (1, errno, "could not open diff output file %s",
1741                    finfo->fullname);
1742         c = fread (buf, 1, sizeof BINARY - 1, e);
1743         buf[c] = '\0';
1744         if (strcmp (buf, BINARY) == 0)
1745         {
1746             /* These are binary files.  We could use diff -a, but
1747                patch can't handle that.  */
1748             fail = 1;
1749         }
1750         fclose (e);
1751     }
1752
1753     if (! fail)
1754     {
1755         Vers_TS *xvers_ts;
1756
1757         /* Stat the original RCS file, and then adjust it the way
1758            that RCS_checkout would.  FIXME: This is an abstraction
1759            violation.  */
1760         if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0)
1761             error (1, errno, "could not stat %s", vers_ts->srcfile->path);
1762         if (chmod (finfo->file,
1763                    file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
1764             < 0)
1765             error (0, errno, "cannot change mode of file %s", finfo->file);
1766         if (cvswrite
1767             && !fileattr_get (finfo->file, "_watched"))
1768             xchmod (finfo->file, 1);
1769
1770         /* This stuff is just copied blindly from checkout_file.  I
1771            don't really know what it does.  */
1772         xvers_ts = Version_TS (finfo, options, tag, date,
1773                                force_tag_match, 0);
1774         if (strcmp (xvers_ts->options, "-V4") == 0)
1775             xvers_ts->options[0] = '\0';
1776
1777         Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
1778                   xvers_ts->ts_user, xvers_ts->options,
1779                   xvers_ts->tag, xvers_ts->date, NULL);
1780
1781         if (CVS_STAT (finfo->file, file_info) < 0)
1782             error (1, errno, "could not stat %s", finfo->file);
1783
1784         /* If this is really Update and not Checkout, record history.  */
1785         if (strcmp (cvs_cmd_name, "update") == 0)
1786             history_write ('P', finfo->update_dir, xvers_ts->vn_rcs,
1787                            finfo->file, finfo->repository);
1788
1789         freevers_ts (&xvers_ts);
1790
1791         if (!really_quiet)
1792         {
1793             write_letter (finfo, 'P');
1794         }
1795     }
1796     else
1797     {
1798         int old_errno = errno;          /* save errno value over the rename */
1799
1800         if (isfile (backup))
1801             rename_file (backup, finfo->file);
1802
1803         if (retcode != 0 && retcode != 1)
1804             error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1805                    "could not diff %s", finfo->fullname);
1806
1807         *docheckout = 1;
1808         retval = retcode;
1809     }
1810
1811     if (unlink_file (backup) < 0
1812         && !existence_error (errno))
1813         error (0, errno, "cannot remove %s", backup);
1814     if (unlink_file (file1) < 0
1815         && !existence_error (errno))
1816         error (0, errno, "cannot remove %s", file1);
1817     if (unlink_file (file2) < 0
1818         && !existence_error (errno))
1819         error (0, errno, "cannot remove %s", file2);
1820
1821     free (backup);
1822     free (file1);
1823     free (file2);
1824     return retval;
1825 }
1826
1827
1828
1829 /* Write data to a file.  Record whether the last byte written was a
1830    newline.  Optionally compute a checksum.  This is called by
1831    patch_file via RCS_checkout.  */
1832
1833 static void
1834 patch_file_write (void *callerdat, const char *buffer, size_t len)
1835 {
1836     struct patch_file_data *data = (struct patch_file_data *) callerdat;
1837
1838     if (fwrite (buffer, 1, len, data->fp) != len)
1839         error (1, errno, "cannot write %s", data->filename);
1840
1841     data->final_nl = (buffer[len - 1] == '\n');
1842
1843     if (data->compute_checksum)
1844         cvs_MD5Update (&data->context, (unsigned char *) buffer, len);
1845 }
1846
1847 #endif /* SERVER_SUPPORT */
1848
1849 /*
1850  * Several of the types we process only print a bit of information consisting
1851  * of a single letter and the name.
1852  */
1853 void
1854 write_letter (struct file_info *finfo, int letter)
1855 {
1856     if (!really_quiet)
1857     {
1858         char *tag = NULL;
1859         /* Big enough for "+updated" or any of its ilk.  */
1860         char buf[80];
1861
1862         switch (letter)
1863         {
1864             case 'U':
1865                 tag = "updated";
1866                 break;
1867             default:
1868                 /* We don't yet support tagged output except for "U".  */
1869                 break;
1870         }
1871
1872         if (tag != NULL)
1873         {
1874             sprintf (buf, "+%s", tag);
1875             cvs_output_tagged (buf, NULL);
1876         }
1877         buf[0] = letter;
1878         buf[1] = ' ';
1879         buf[2] = '\0';
1880         cvs_output_tagged ("text", buf);
1881         cvs_output_tagged ("fname", finfo->fullname);
1882         cvs_output_tagged ("newline", NULL);
1883         if (tag != NULL)
1884         {
1885             sprintf (buf, "-%s", tag);
1886             cvs_output_tagged (buf, NULL);
1887         }
1888     }
1889     return;
1890 }
1891
1892
1893
1894 /*
1895  * Do all the magic associated with a file which needs to be merged
1896  */
1897 static int
1898 merge_file (struct file_info *finfo, Vers_TS *vers)
1899 {
1900     char *backup;
1901     int status;
1902     int retval;
1903
1904     /*
1905      * The users currently modified file is moved to a backup file name
1906      * ".#filename.version", so that it will stay around for a few days
1907      * before being automatically removed by some cron daemon.  The "version"
1908      * is the version of the file that the user was most up-to-date with
1909      * before the merge.
1910      */
1911     backup = xmalloc (strlen (finfo->file)
1912                       + strlen (vers->vn_user)
1913                       + sizeof (BAKPREFIX)
1914                       + 10);
1915     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
1916
1917     if (unlink_file (backup) && !existence_error (errno))
1918         error (0, errno, "unable to remove %s", backup);
1919     copy_file (finfo->file, backup);
1920     xchmod (finfo->file, 1);
1921
1922     if (strcmp (vers->options, "-kb") == 0
1923         || wrap_merge_is_copy (finfo->file)
1924         || special_file_mismatch (finfo, NULL, vers->vn_rcs))
1925     {
1926         /* For binary files, a merge is always a conflict.  Same for
1927            files whose permissions or linkage do not match.  We give the
1928            user the two files, and let them resolve it.  It is possible
1929            that we should require a "touch foo" or similar step before
1930            we allow a checkin.  */
1931
1932         /* TODO: it may not always be necessary to regard a permission
1933            mismatch as a conflict.  The working file and the RCS file
1934            have a common ancestor `A'; if the working file's permissions
1935            match A's, then it's probably safe to overwrite them with the
1936            RCS permissions.  Only if the working file, the RCS file, and
1937            A all disagree should this be considered a conflict.  But more
1938            thought needs to go into this, and in the meantime it is safe
1939            to treat any such mismatch as an automatic conflict. -twp */
1940
1941 #ifdef SERVER_SUPPORT
1942         if (server_active)
1943             server_copy_file (finfo->file, finfo->update_dir,
1944                               finfo->repository, backup);
1945 #endif
1946
1947         status = checkout_file (finfo, vers, 0, 1, 1);
1948
1949         /* Is there a better term than "nonmergeable file"?  What we
1950            really mean is, not something that CVS cannot or does not
1951            want to merge (there might be an external manual or
1952            automatic merge process).  */
1953         error (0, 0, "nonmergeable file needs merge");
1954         error (0, 0, "revision %s from repository is now in %s",
1955                vers->vn_rcs, finfo->fullname);
1956         error (0, 0, "file from working directory is now in %s", backup);
1957         write_letter (finfo, 'C');
1958
1959         history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
1960                        finfo->repository);
1961         retval = 0;
1962         goto out;
1963     }
1964
1965     status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
1966                         vers->options, vers->vn_user, vers->vn_rcs);
1967     if (status != 0 && status != 1)
1968     {
1969         error (0, status == -1 ? errno : 0,
1970                "could not merge revision %s of %s", vers->vn_user, finfo->fullname);
1971         error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
1972                finfo->fullname, backup);
1973         rename_file (backup, finfo->file);
1974         retval = 1;
1975         goto out;
1976     }
1977
1978     if (strcmp (vers->options, "-V4") == 0)
1979         vers->options[0] = '\0';
1980
1981     /* This file is the result of a merge, which means that it has
1982        been modified.  We use a special timestamp string which will
1983        not compare equal to any actual timestamp.  */
1984     {
1985         char *cp = 0;
1986
1987         if (status)
1988         {
1989             (void) time (&last_register_time);
1990             cp = time_stamp (finfo->file);
1991         }
1992         Register (finfo->entries, finfo->file, vers->vn_rcs,
1993                   "Result of merge", vers->options, vers->tag,
1994                   vers->date, cp);
1995         if (cp)
1996             free (cp);
1997     }
1998
1999     /* fix up the vers structure, in case it is used by join */
2000     if (join_rev1)
2001     {
2002         /* FIXME: Throwing away the original revision info is almost
2003            certainly wrong -- what if join_rev1 is "BASE"?  */
2004         if (vers->vn_user != NULL)
2005             free (vers->vn_user);
2006         vers->vn_user = xstrdup (vers->vn_rcs);
2007     }
2008
2009 #ifdef SERVER_SUPPORT
2010     /* Send the new contents of the file before the message.  If we
2011        wanted to be totally correct, we would have the client write
2012        the message only after the file has safely been written.  */
2013     if (server_active)
2014     {
2015         server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
2016                           backup);
2017         server_updated (finfo, vers, SERVER_MERGED,
2018                         (mode_t) -1, (unsigned char *) NULL,
2019                         (struct buffer *) NULL);
2020     }
2021 #endif
2022
2023     if (status == 1)
2024     {
2025         error (0, 0, "conflicts found in %s", finfo->fullname);
2026
2027         write_letter (finfo, 'C');
2028
2029         history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
2030                        finfo->repository);
2031
2032     }
2033     else /* status == 0 */
2034     {
2035         history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
2036                        finfo->repository);
2037
2038         /* FIXME: the noexec case is broken.  RCS_merge could be doing the
2039            xcmp on the temporary files without much hassle, I think.  */
2040         if (!noexec && !xcmp (backup, finfo->file))
2041         {
2042             cvs_output (finfo->fullname, 0);
2043             cvs_output (" already contains the differences between ", 0);
2044             cvs_output (vers->vn_user, 0);
2045             cvs_output (" and ", 0);
2046             cvs_output (vers->vn_rcs, 0);
2047             cvs_output ("\n", 1);
2048
2049             retval = 0;
2050             goto out;
2051         }
2052
2053         write_letter (finfo, 'M');
2054     }
2055     retval = 0;
2056  out:
2057     free (backup);
2058     return retval;
2059 }
2060
2061
2062
2063 /*
2064  * Do all the magic associated with a file which needs to be joined
2065  * (reached via the -j option to checkout or update).
2066  *
2067  * INPUTS
2068  *   finfo              File information about the destination file.
2069  *   vers               The Vers_TS structure for finfo.
2070  *
2071  * GLOBALS
2072  *   join_rev1          From the command line.
2073  *   join_rev2          From the command line.
2074  *   server_active      Natch.
2075  *
2076  * ASSUMPTIONS
2077  *   1.  Is not called in client mode.
2078  */
2079 static void
2080 join_file (struct file_info *finfo, Vers_TS *vers)
2081 {
2082     char *backup;
2083     char *t_options;
2084     int status;
2085
2086     char *rev1;
2087     char *rev2;
2088     char *jrev1;
2089     char *jrev2;
2090     char *jdate1;
2091     char *jdate2;
2092
2093     TRACE ( 1, "join_file(%s, %s%s%s%s, %s, %s)",
2094             finfo->file,
2095             vers->tag ? vers->tag : "",
2096             vers->tag ? " (" : "",
2097             vers->vn_rcs ? vers->vn_rcs : "",
2098             vers->tag ? ")" : "",
2099             join_rev1 ? join_rev1 : "",
2100             join_rev2 ? join_rev2 : "" );
2101
2102     jrev1 = join_rev1;
2103     jrev2 = join_rev2;
2104     jdate1 = date_rev1;
2105     jdate2 = date_rev2;
2106
2107     /* Determine if we need to do anything at all.  */
2108     if (vers->srcfile == NULL ||
2109         vers->srcfile->path == NULL)
2110     {
2111         return;
2112     }
2113
2114     /* If only one join revision is specified, it becomes the second
2115        revision.  */
2116     if (jrev2 == NULL)
2117     {
2118         jrev2 = jrev1;
2119         jrev1 = NULL;
2120         jdate2 = jdate1;
2121         jdate1 = NULL;
2122     }
2123
2124     /* FIXME: Need to handle "BASE" for jrev1 and/or jrev2.  Note caveat
2125        below about vn_user.  */
2126
2127     /* Convert the second revision, walking branches and dates.  */
2128     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, (int *) NULL);
2129
2130     /* If this is a merge of two revisions, get the first revision.
2131        If only one join tag was specified, then the first revision is
2132        the greatest common ancestor of the second revision and the
2133        working file.  */
2134     if (jrev1 != NULL)
2135         rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, (int *) NULL);
2136     else
2137     {
2138         /* Note that we use vn_rcs here, since vn_user may contain a
2139            special string such as "-nn".  */
2140         if (vers->vn_rcs == NULL)
2141             rev1 = NULL;
2142         else if (rev2 == NULL)
2143         {
2144             /* This means that the file never existed on the branch.
2145                It does not mean that the file was removed on the
2146                branch: that case is represented by a dead rev2.  If
2147                the file never existed on the branch, then we have
2148                nothing to merge, so we just return.  */
2149             return;
2150         }
2151         else
2152             rev1 = gca (vers->vn_rcs, rev2);
2153     }
2154
2155     /* Handle a nonexistent or dead merge target.  */
2156     if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
2157     {
2158         char *mrev;
2159
2160         if (rev2 != NULL)
2161             free (rev2);
2162
2163         /* If the first revision doesn't exist either, then there is
2164            no change between the two revisions, so we don't do
2165            anything.  */
2166         if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2167         {
2168             if (rev1 != NULL)
2169                 free (rev1);
2170             return;
2171         }
2172
2173         /* If we are merging two revisions, then the file was removed
2174            between the first revision and the second one.  In this
2175            case we want to mark the file for removal.
2176
2177            If we are merging one revision, then the file has been
2178            removed between the greatest common ancestor and the merge
2179            revision.  From the perspective of the branch on to which
2180            we ar emerging, which may be the trunk, either 1) the file
2181            does not currently exist on the target, or 2) the file has
2182            not been modified on the target branch since the greatest
2183            common ancestor, or 3) the file has been modified on the
2184            target branch since the greatest common ancestor.  In case
2185            1 there is nothing to do.  In case 2 we mark the file for
2186            removal.  In case 3 we have a conflict.
2187
2188            Note that the handling is slightly different depending upon
2189            whether one or two join targets were specified.  If two
2190            join targets were specified, we don't check whether the
2191            file was modified since a given point.  My reasoning is
2192            that if you ask for an explicit merge between two tags,
2193            then you want to merge in whatever was changed between
2194            those two tags.  If a file was removed between the two
2195            tags, then you want it to be removed.  However, if you ask
2196            for a merge of a branch, then you want to merge in all
2197            changes which were made on the branch.  If a file was
2198            removed on the branch, that is a change to the file.  If
2199            the file was also changed on the main line, then that is
2200            also a change.  These two changes--the file removal and the
2201            modification--must be merged.  This is a conflict.  */
2202
2203         /* If the user file is dead, or does not exist, or has been
2204            marked for removal, then there is nothing to do.  */
2205         if (vers->vn_user == NULL
2206             || vers->vn_user[0] == '-'
2207             || RCS_isdead (vers->srcfile, vers->vn_user))
2208         {
2209             if (rev1 != NULL)
2210                 free (rev1);
2211             return;
2212         }
2213
2214         /* If the user file has been marked for addition, or has been
2215            locally modified, then we have a conflict which we can not
2216            resolve.  No_Difference will already have been called in
2217            this case, so comparing the timestamps is sufficient to
2218            determine whether the file is locally modified.  */
2219         if (strcmp (vers->vn_user, "0") == 0
2220             || (vers->ts_user != NULL
2221                 && strcmp (vers->ts_user, vers->ts_rcs) != 0))
2222         {
2223             if (jdate2 != NULL)
2224                 error (0, 0,
2225                        "file %s is locally modified, but has been removed in revision %s as of %s",
2226                        finfo->fullname, jrev2, jdate2);
2227             else
2228                 error (0, 0,
2229                        "file %s is locally modified, but has been removed in revision %s",
2230                        finfo->fullname, jrev2);
2231
2232             /* FIXME: Should we arrange to return a non-zero exit
2233                status?  */
2234
2235             if (rev1 != NULL)
2236                 free (rev1);
2237
2238             return;
2239         }
2240
2241         /* If only one join tag was specified, and the user file has
2242            been changed since the greatest common ancestor (rev1),
2243            then there is a conflict we can not resolve.  See above for
2244            the rationale.  */
2245         if (join_rev2 == NULL
2246             && strcmp (rev1, vers->vn_user) != 0)
2247         {
2248             if (jdate2 != NULL)
2249                 error (0, 0,
2250                        "file %s has been modified, but has been removed in revision %s as of %s",
2251                        finfo->fullname, jrev2, jdate2);
2252             else
2253                 error (0, 0,
2254                        "file %s has been modified, but has been removed in revision %s",
2255                        finfo->fullname, jrev2);
2256
2257             /* FIXME: Should we arrange to return a non-zero exit
2258                status?  */
2259
2260             if (rev1 != NULL)
2261                 free (rev1);
2262
2263             return;
2264         }
2265
2266         if (rev1 != NULL)
2267             free (rev1);
2268
2269         /* The user file exists and has not been modified.  Mark it
2270            for removal.  FIXME: If we are doing a checkout, this has
2271            the effect of first checking out the file, and then
2272            removing it.  It would be better to just register the
2273            removal. 
2274         
2275            The same goes for a removal then an add.  e.g.
2276            cvs up -rbr -jbr2 could remove and readd the same file
2277          */
2278         /* save the rev since server_updated might invalidate it */
2279         mrev = xmalloc (strlen (vers->vn_user) + 2);
2280         sprintf (mrev, "-%s", vers->vn_user);
2281 #ifdef SERVER_SUPPORT
2282         if (server_active)
2283         {
2284             server_scratch (finfo->file);
2285             server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
2286                             (unsigned char *) NULL, (struct buffer *) NULL);
2287         }
2288 #endif
2289         Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
2290                   vers->options, vers->tag, vers->date, vers->ts_conflict);
2291         free (mrev);
2292         /* We need to check existence_error here because if we are
2293            running as the server, and the file is up to date in the
2294            working directory, the client will not have sent us a copy.  */
2295         if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
2296             error (0, errno, "cannot remove file %s", finfo->fullname);
2297 #ifdef SERVER_SUPPORT
2298         if (server_active)
2299             server_checked_in (finfo->file, finfo->update_dir,
2300                                finfo->repository);
2301 #endif
2302         if (! really_quiet)
2303             error (0, 0, "scheduling `%s' for removal", finfo->fullname);
2304
2305         return;
2306     }
2307
2308     /* If the two merge revisions are the same, then there is nothing
2309      * to do.  This needs to be checked before the rev2 == up-to-date base
2310      * revision check tha comes next.  Otherwise, rev1 can == rev2 and get an
2311      * "already contains the changes between <rev1> and <rev1>" message.
2312      */
2313     if (rev1 && strcmp (rev1, rev2) == 0)
2314     {
2315         free (rev1);
2316         free (rev2);
2317         return;
2318     }
2319
2320     /* If we know that the user file is up-to-date, then it becomes an
2321      * optimization to skip the merge when rev2 is the same as the base
2322      * revision.  i.e. we know that diff3(file2,file1,file2) will produce
2323      * file2.
2324      */
2325     if (vers->vn_user != NULL && vers->ts_user != NULL
2326         && strcmp (vers->ts_user, vers->ts_rcs) == 0
2327         && strcmp (rev2, vers->vn_user) == 0)
2328     {
2329         if (!really_quiet)
2330         {
2331             cvs_output (finfo->fullname, 0);
2332             cvs_output (" already contains the differences between ", 0);
2333             cvs_output (rev1 ? rev1 : "creation", 0);
2334             cvs_output (" and ", 0);
2335             cvs_output (rev2, 0);
2336             cvs_output ("\n", 1);
2337         }
2338
2339         if (rev1 != NULL)
2340             free (rev1);
2341         free (rev2);
2342
2343         return;
2344     }
2345
2346     /* If rev1 is dead or does not exist, then the file was added
2347        between rev1 and rev2.  */
2348     if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2349     {
2350         if (rev1 != NULL)
2351             free (rev1);
2352         free (rev2);
2353
2354         /* If the file does not exist in the working directory, then
2355            we can just check out the new revision and mark it for
2356            addition.  */
2357         if (vers->vn_user == NULL)
2358         {
2359             char *saved_options = options;
2360             Vers_TS *xvers;
2361
2362             xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
2363
2364             /* Reset any keyword expansion option.  Otherwise, when a
2365                command like `cvs update -kk -jT1 -jT2' creates a new file
2366                (because a file had the T2 tag, but not T1), the subsequent
2367                commit of that just-added file effectively would set the
2368                admin `-kk' option for that file in the repository.  */
2369             options = NULL;
2370
2371             /* FIXME: If checkout_file fails, we should arrange to
2372                return a non-zero exit status.  */
2373             status = checkout_file (finfo, xvers, 1, 0, 1);
2374             options = saved_options;
2375
2376             freevers_ts (&xvers);
2377
2378             return;
2379         }
2380
2381         /* The file currently exists in the working directory, so we
2382            have a conflict which we can not resolve.  Note that this
2383            is true even if the file is marked for addition or removal.  */
2384
2385         if (jdate2 != NULL)
2386             error (0, 0,
2387                    "file %s exists, but has been added in revision %s as of %s",
2388                    finfo->fullname, jrev2, jdate2);
2389         else
2390             error (0, 0,
2391                    "file %s exists, but has been added in revision %s",
2392                    finfo->fullname, jrev2);
2393
2394         return;
2395     }
2396
2397     /* If there is no working file, then we can't do the merge.  */
2398     if (vers->vn_user == NULL || vers->vn_user[0] == '-')
2399     {
2400         free (rev1);
2401         free (rev2);
2402
2403         if (jdate2 != NULL)
2404             error (0, 0,
2405                    "file %s does not exist, but is present in revision %s as of %s",
2406                    finfo->fullname, jrev2, jdate2);
2407         else
2408             error (0, 0,
2409                    "file %s does not exist, but is present in revision %s",
2410                    finfo->fullname, jrev2);
2411
2412         /* FIXME: Should we arrange to return a non-zero exit status?  */
2413
2414         return;
2415     }
2416
2417 #ifdef SERVER_SUPPORT
2418     if (server_active && !isreadable (finfo->file))
2419     {
2420         int retcode;
2421         /* The file is up to date.  Need to check out the current contents.  */
2422         /* FIXME - see the FIXME comment above the call to RCS_checkout in the
2423          * patch_file function.
2424          */
2425         retcode = RCS_checkout (vers->srcfile, finfo->file,
2426                                 vers->vn_user, vers->tag,
2427                                 (char *) NULL, RUN_TTY,
2428                                 (RCSCHECKOUTPROC) NULL, (void *) NULL);
2429         if (retcode != 0)
2430             error (1, 0,
2431                    "failed to check out %s file", finfo->fullname);
2432     }
2433 #endif
2434
2435     /*
2436      * The users currently modified file is moved to a backup file name
2437      * ".#filename.version", so that it will stay around for a few days
2438      * before being automatically removed by some cron daemon.  The "version"
2439      * is the version of the file that the user was most up-to-date with
2440      * before the merge.
2441      */
2442     backup = xmalloc (strlen (finfo->file)
2443                       + strlen (vers->vn_user)
2444                       + sizeof (BAKPREFIX)
2445                       + 10);
2446     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
2447
2448     if (unlink_file (backup) < 0
2449         && !existence_error (errno))
2450         error (0, errno, "cannot remove %s", backup);
2451     copy_file (finfo->file, backup);
2452     xchmod (finfo->file, 1);
2453
2454     t_options = vers->options;
2455 #if 0
2456     if (*t_options == '\0')
2457         t_options = "-kk";              /* to ignore keyword expansions */
2458 #endif
2459
2460     /* If the source of the merge is the same as the working file
2461        revision, then we can just RCS_checkout the target (no merging
2462        as such).  In the text file case, this is probably quite
2463        similar to the RCS_merge, but in the binary file case,
2464        RCS_merge gives all kinds of trouble.  */
2465     if (vers->vn_user != NULL
2466         && strcmp (rev1, vers->vn_user) == 0
2467         /* See comments above about how No_Difference has already been
2468            called.  */
2469         && vers->ts_user != NULL
2470         && strcmp (vers->ts_user, vers->ts_rcs) == 0
2471
2472         /* Avoid this in the text file case.  See below for why.
2473          */
2474         && (strcmp (t_options, "-kb") == 0
2475             || wrap_merge_is_copy (finfo->file)))
2476     {
2477         /* FIXME: Verify my comment below:
2478          *
2479          * RCS_merge does nothing with keywords.  It merges the changes between
2480          * two revisions without expanding the keywords (it might expand in
2481          * -kk mode before computing the diff between rev1 and rev2 - I'm not
2482          * sure).  In other words, the keyword lines in the current work file
2483          * get left alone.
2484          *
2485          * Therfore, checking out the destination revision (rev2) is probably
2486          * incorrect in the text case since we should see the keywords that were
2487          * substituted into the original file at the time it was checked out
2488          * and not the keywords from rev2.
2489          *
2490          * Also, it is safe to pass in NULL for nametag since we know no
2491          * substitution is happening during the binary mode checkout.
2492          */
2493         if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL, t_options,
2494                            RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0 )
2495             status = 2;
2496         else
2497             status = 0;
2498
2499         /* OK, this is really stupid.  RCS_checkout carefully removes
2500            write permissions, and we carefully put them back.  But
2501            until someone gets around to fixing it, that seems like the
2502            easiest way to get what would seem to be the right mode.
2503            I don't check CVSWRITE or _watched; I haven't thought about
2504            that in great detail, but it seems like a watched file should
2505            be checked out (writable) after a merge.  */
2506         xchmod (finfo->file, 1);
2507
2508         /* Traditionally, the text file case prints a whole bunch of
2509            scary looking and verbose output which fails to tell the user
2510            what is really going on (it gives them rev1 and rev2 but doesn't
2511            indicate in any way that rev1 == vn_user).  I think just a
2512            simple "U foo" is good here; it seems analogous to the case in
2513            which the file was added on the branch in terms of what to
2514            print.  */
2515         write_letter (finfo, 'U');
2516     }
2517     else if (strcmp (t_options, "-kb") == 0
2518              || wrap_merge_is_copy (finfo->file)
2519              || special_file_mismatch (finfo, rev1, rev2))
2520     {
2521         /* We are dealing with binary files, or files with a
2522            permission/linkage mismatch (this second case only occurs when
2523            PRESERVE_PERMISSIONS_SUPPORT is enabled), and real merging would
2524            need to take place.  This is a conflict.  We give the user
2525            the two files, and let them resolve it.  It is possible
2526            that we should require a "touch foo" or similar step before
2527            we allow a checkin.  */
2528         if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL,
2529                            t_options, RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
2530             status = 2;
2531         else
2532             status = 0;
2533
2534         /* OK, this is really stupid.  RCS_checkout carefully removes
2535            write permissions, and we carefully put them back.  But
2536            until someone gets around to fixing it, that seems like the
2537            easiest way to get what would seem to be the right mode.
2538            I don't check CVSWRITE or _watched; I haven't thought about
2539            that in great detail, but it seems like a watched file should
2540            be checked out (writable) after a merge.  */
2541         xchmod (finfo->file, 1);
2542
2543         /* Hmm.  We don't give them REV1 anywhere.  I guess most people
2544            probably don't have a 3-way merge tool for the file type in
2545            question, and might just get confused if we tried to either
2546            provide them with a copy of the file from REV1, or even just
2547            told them what REV1 is so they can get it themself, but it
2548            might be worth thinking about.  */
2549         /* See comment in merge_file about the "nonmergeable file"
2550            terminology.  */
2551         error (0, 0, "nonmergeable file needs merge");
2552         error (0, 0, "revision %s from repository is now in %s",
2553                rev2, finfo->fullname);
2554         error (0, 0, "file from working directory is now in %s", backup);
2555         write_letter (finfo, 'C');
2556     }
2557     else
2558         status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
2559                             t_options, rev1, rev2);
2560
2561     if (status != 0)
2562     {
2563         if (status != 1)
2564         {
2565             error (0, status == -1 ? errno : 0,
2566                    "could not merge revision %s of %s", rev2, finfo->fullname);
2567             error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
2568                    finfo->fullname, backup);
2569             rename_file (backup, finfo->file);
2570         }
2571     }
2572     else /* status == 0 */
2573     {
2574         /* FIXME: the noexec case is broken.  RCS_merge could be doing the
2575            xcmp on the temporary files without much hassle, I think.  */
2576         if (!noexec && !xcmp (backup, finfo->file))
2577         {
2578             if (!really_quiet)
2579             {
2580                 cvs_output (finfo->fullname, 0);
2581                 cvs_output (" already contains the differences between ", 0);
2582                 cvs_output (rev1, 0);
2583                 cvs_output (" and ", 0);
2584                 cvs_output (rev2, 0);
2585                 cvs_output ("\n", 1);
2586             }
2587
2588             /* and skip the registering and sending the new file since it
2589              * hasn't been updated.
2590              */
2591             goto out;
2592         }
2593     }
2594
2595     /* The file has changed, but if we just checked it out it may
2596        still have the same timestamp it did when it was first
2597        registered above in checkout_file.  We register it again with a
2598        dummy timestamp to make sure that later runs of CVS will
2599        recognize that it has changed.
2600
2601        We don't actually need to register again if we called
2602        RCS_checkout above, and we aren't running as the server.
2603        However, that is not the normal case, and calling Register
2604        again won't cost much in that case.  */
2605     {
2606         char *cp = 0;
2607
2608         if (status)
2609         {
2610             (void) time (&last_register_time);
2611             cp = time_stamp (finfo->file);
2612         }
2613         Register (finfo->entries, finfo->file,
2614                   vers->vn_rcs ? vers->vn_rcs : "0", "Result of merge",
2615                   vers->options, vers->tag, vers->date, cp);
2616         if (cp)
2617             free(cp);
2618     }
2619
2620 #ifdef SERVER_SUPPORT
2621     if (server_active)
2622     {
2623         server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
2624                           backup);
2625         server_updated (finfo, vers, SERVER_MERGED,
2626                         (mode_t) -1, (unsigned char *) NULL,
2627                         (struct buffer *) NULL);
2628     }
2629 #endif
2630
2631 out:
2632     free (rev1);
2633     free (rev2);
2634     free (backup);
2635 }
2636
2637
2638
2639 /*
2640  * Report whether revisions REV1 and REV2 of FINFO agree on:
2641  *   . file ownership
2642  *   . permissions
2643  *   . major and minor device numbers
2644  *   . symbolic links
2645  *   . hard links
2646  *
2647  * If either REV1 or REV2 is NULL, the working copy is used instead.
2648  *
2649  * Return 1 if the files differ on these data.
2650  */
2651
2652 int
2653 special_file_mismatch (struct file_info *finfo, char *rev1, char *rev2)
2654 {
2655 #ifdef PRESERVE_PERMISSIONS_SUPPORT
2656     struct stat sb;
2657     RCSVers *vp;
2658     Node *n;
2659     uid_t rev1_uid, rev2_uid;
2660     gid_t rev1_gid, rev2_gid;
2661     mode_t rev1_mode, rev2_mode;
2662     unsigned long dev_long;
2663     dev_t rev1_dev, rev2_dev;
2664     char *rev1_symlink = NULL;
2665     char *rev2_symlink = NULL;
2666     List *rev1_hardlinks = NULL;
2667     List *rev2_hardlinks = NULL;
2668     int check_uids, check_gids, check_modes;
2669     int result;
2670
2671     /* If we don't care about special file info, then
2672        don't report a mismatch in any case. */
2673     if (!preserve_perms)
2674         return 0;
2675
2676     /* When special_file_mismatch is called from No_Difference, the
2677        RCS file has been only partially parsed.  We must read the
2678        delta tree in order to compare special file info recorded in
2679        the delta nodes.  (I think this is safe. -twp) */
2680     if (finfo->rcs->flags & PARTIAL)
2681         RCS_reparsercsfile (finfo->rcs, NULL, NULL);
2682
2683     check_uids = check_gids = check_modes = 1;
2684
2685     /* Obtain file information for REV1.  If this is null, then stat
2686        finfo->file and use that info. */
2687     /* If a revision does not know anything about its status,
2688        then presumably it doesn't matter, and indicates no conflict. */
2689
2690     if (rev1 == NULL)
2691     {
2692         if (islink (finfo->file))
2693             rev1_symlink = xreadlink (finfo->file);
2694         else
2695         {
2696 # ifdef HAVE_STRUCT_STAT_ST_RDEV
2697             if (CVS_LSTAT (finfo->file, &sb) < 0)
2698                 error (1, errno, "could not get file information for %s",
2699                        finfo->file);
2700             rev1_uid = sb.st_uid;
2701             rev1_gid = sb.st_gid;
2702             rev1_mode = sb.st_mode;
2703             if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
2704                 rev1_dev = sb.st_rdev;
2705 # else
2706             error (1, 0, "cannot handle device files on this system (%s)",
2707                    finfo->file);
2708 # endif
2709         }
2710         rev1_hardlinks = list_linked_files_on_disk (finfo->file);
2711     }
2712     else
2713     {
2714         n = findnode (finfo->rcs->versions, rev1);
2715         vp = n->data;
2716
2717         n = findnode (vp->other_delta, "symlink");
2718         if (n != NULL)
2719             rev1_symlink = xstrdup (n->data);
2720         else
2721         {
2722             n = findnode (vp->other_delta, "owner");
2723             if (n == NULL)
2724                 check_uids = 0; /* don't care */
2725             else
2726                 rev1_uid = strtoul (n->data, NULL, 10);
2727
2728             n = findnode (vp->other_delta, "group");
2729             if (n == NULL)
2730                 check_gids = 0; /* don't care */
2731             else
2732                 rev1_gid = strtoul (n->data, NULL, 10);
2733
2734             n = findnode (vp->other_delta, "permissions");
2735             if (n == NULL)
2736                 check_modes = 0;        /* don't care */
2737             else
2738                 rev1_mode = strtoul (n->data, NULL, 8);
2739
2740             n = findnode (vp->other_delta, "special");
2741             if (n == NULL)
2742                 rev1_mode |= S_IFREG;
2743             else
2744             {
2745                 /* If the size of `ftype' changes, fix the sscanf call also */
2746                 char ftype[16];
2747                 if (sscanf (n->data, "%15s %lu", ftype,
2748                             &dev_long) < 2)
2749                     error (1, 0, "%s:%s has bad `special' newphrase %s",
2750                            finfo->file, rev1, (char *)n->data);
2751                 rev1_dev = dev_long;
2752                 if (strcmp (ftype, "character") == 0)
2753                     rev1_mode |= S_IFCHR;
2754                 else if (strcmp (ftype, "block") == 0)
2755                     rev1_mode |= S_IFBLK;
2756                 else
2757                     error (0, 0, "%s:%s unknown file type `%s'",
2758                            finfo->file, rev1, ftype);
2759             }
2760
2761             rev1_hardlinks = vp->hardlinks;
2762             if (rev1_hardlinks == NULL)
2763                 rev1_hardlinks = getlist();
2764         }
2765     }
2766
2767     /* Obtain file information for REV2. */
2768     if (rev2 == NULL)
2769     {
2770         if (islink (finfo->file))
2771             rev2_symlink = xreadlink (finfo->file);
2772         else
2773         {
2774 # ifdef HAVE_STRUCT_STAT_ST_RDEV
2775             if (CVS_LSTAT (finfo->file, &sb) < 0)
2776                 error (1, errno, "could not get file information for %s",
2777                        finfo->file);
2778             rev2_uid = sb.st_uid;
2779             rev2_gid = sb.st_gid;
2780             rev2_mode = sb.st_mode;
2781             if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
2782                 rev2_dev = sb.st_rdev;
2783 # else
2784             error (1, 0, "cannot handle device files on this system (%s)",
2785                    finfo->file);
2786 # endif
2787         }
2788         rev2_hardlinks = list_linked_files_on_disk (finfo->file);
2789     }
2790     else
2791     {
2792         n = findnode (finfo->rcs->versions, rev2);
2793         vp = n->data;
2794
2795         n = findnode (vp->other_delta, "symlink");
2796         if (n != NULL)
2797             rev2_symlink = xstrdup (n->data);
2798         else
2799         {
2800             n = findnode (vp->other_delta, "owner");
2801             if (n == NULL)
2802                 check_uids = 0; /* don't care */
2803             else
2804                 rev2_uid = strtoul (n->data, NULL, 10);
2805
2806             n = findnode (vp->other_delta, "group");
2807             if (n == NULL)
2808                 check_gids = 0; /* don't care */
2809             else
2810                 rev2_gid = strtoul (n->data, NULL, 10);
2811
2812             n = findnode (vp->other_delta, "permissions");
2813             if (n == NULL)
2814                 check_modes = 0;        /* don't care */
2815             else
2816                 rev2_mode = strtoul (n->data, NULL, 8);
2817
2818             n = findnode (vp->other_delta, "special");
2819             if (n == NULL)
2820                 rev2_mode |= S_IFREG;
2821             else
2822             {
2823                 /* If the size of `ftype' changes, fix the sscanf call also */
2824                 char ftype[16];
2825                 if (sscanf (n->data, "%15s %lu", ftype,
2826                             &dev_long) < 2)
2827                     error (1, 0, "%s:%s has bad `special' newphrase %s",
2828                            finfo->file, rev2, (char *)n->data);
2829                 rev2_dev = dev_long;
2830                 if (strcmp (ftype, "character") == 0)
2831                     rev2_mode |= S_IFCHR;
2832                 else if (strcmp (ftype, "block") == 0)
2833                     rev2_mode |= S_IFBLK;
2834                 else
2835                     error (0, 0, "%s:%s unknown file type `%s'",
2836                            finfo->file, rev2, ftype);
2837             }
2838
2839             rev2_hardlinks = vp->hardlinks;
2840             if (rev2_hardlinks == NULL)
2841                 rev2_hardlinks = getlist();
2842         }
2843     }
2844
2845     /* Check the user/group ownerships and file permissions, printing
2846        an error for each mismatch found.  Return 0 if all characteristics
2847        matched, and 1 otherwise. */
2848
2849     result = 0;
2850
2851     /* Compare symlinks first, since symlinks are simpler (don't have
2852        any other characteristics). */
2853     if (rev1_symlink != NULL && rev2_symlink == NULL)
2854     {
2855         error (0, 0, "%s is a symbolic link",
2856                (rev1 == NULL ? "working file" : rev1));
2857         result = 1;
2858     }
2859     else if (rev1_symlink == NULL && rev2_symlink != NULL)
2860     {
2861         error (0, 0, "%s is a symbolic link",
2862                (rev2 == NULL ? "working file" : rev2));
2863         result = 1;
2864     }
2865     else if (rev1_symlink != NULL)
2866         result = (strcmp (rev1_symlink, rev2_symlink) == 0);
2867     else
2868     {
2869         /* Compare user ownership. */
2870         if (check_uids && rev1_uid != rev2_uid)
2871         {
2872             error (0, 0, "%s: owner mismatch between %s and %s",
2873                    finfo->file,
2874                    (rev1 == NULL ? "working file" : rev1),
2875                    (rev2 == NULL ? "working file" : rev2));
2876             result = 1;
2877         }
2878
2879         /* Compare group ownership. */
2880         if (check_gids && rev1_gid != rev2_gid)
2881         {
2882             error (0, 0, "%s: group mismatch between %s and %s",
2883                    finfo->file,
2884                    (rev1 == NULL ? "working file" : rev1),
2885                    (rev2 == NULL ? "working file" : rev2));
2886             result = 1;
2887         }
2888     
2889         /* Compare permissions. */
2890         if (check_modes &&
2891             (rev1_mode & 07777) != (rev2_mode & 07777))
2892         {
2893             error (0, 0, "%s: permission mismatch between %s and %s",
2894                    finfo->file,
2895                    (rev1 == NULL ? "working file" : rev1),
2896                    (rev2 == NULL ? "working file" : rev2));
2897             result = 1;
2898         }
2899
2900         /* Compare device file characteristics. */
2901         if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
2902         {
2903             error (0, 0, "%s: %s and %s are different file types",
2904                    finfo->file,
2905                    (rev1 == NULL ? "working file" : rev1),
2906                    (rev2 == NULL ? "working file" : rev2));
2907             result = 1;
2908         }
2909         else if (S_ISBLK (rev1_mode))
2910         {
2911             if (rev1_dev != rev2_dev)
2912             {
2913                 error (0, 0, "%s: device numbers of %s and %s do not match",
2914                        finfo->file,
2915                        (rev1 == NULL ? "working file" : rev1),
2916                        (rev2 == NULL ? "working file" : rev2));
2917                 result = 1;
2918             }
2919         }
2920
2921         /* Compare hard links. */
2922         if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0)
2923         {
2924             error (0, 0, "%s: hard linkage of %s and %s do not match",
2925                    finfo->file,
2926                    (rev1 == NULL ? "working file" : rev1),
2927                    (rev2 == NULL ? "working file" : rev2));
2928             result = 1;
2929         }
2930     }
2931
2932     if (rev1_symlink != NULL)
2933         free (rev1_symlink);
2934     if (rev2_symlink != NULL)
2935         free (rev2_symlink);
2936     if (rev1_hardlinks != NULL)
2937         dellist (&rev1_hardlinks);
2938     if (rev2_hardlinks != NULL)
2939         dellist (&rev2_hardlinks);
2940
2941     return result;
2942 #else
2943     return 0;
2944 #endif
2945 }
2946
2947
2948
2949 int
2950 joining (void)
2951 {
2952     return join_rev1 != NULL;
2953 }