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