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