]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/update.c
This commit was generated by cvs2svn to compensate for changes in r175296,
[FreeBSD/FreeBSD.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: Throwing away the original revision info is almost
1452                    certainly wrong -- what if join_rev1 is "BASE"?  */
1453                 if (vers_ts->vn_user != NULL)
1454                     free (vers_ts->vn_user);
1455                 if (vers_ts->vn_rcs != NULL)
1456                     free (vers_ts->vn_rcs);
1457                 vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
1458                 vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
1459             }
1460
1461             /* If this is really Update and not Checkout, recode history */
1462             if (strcmp (cvs_cmd_name, "update") == 0)
1463                 history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
1464                                finfo->repository);
1465
1466             freevers_ts (&xvers_ts);
1467
1468             if (!really_quiet && !file_is_dead)
1469             {
1470                 write_letter (finfo, 'U');
1471             }
1472         }
1473
1474 #ifdef SERVER_SUPPORT
1475         if (update_server && server_active)
1476             server_updated (finfo, vers_ts,
1477                             merging ? SERVER_MERGED : SERVER_UPDATED,
1478                             mode, (unsigned char *) NULL, revbuf);
1479 #endif
1480     }
1481     else
1482     {
1483         if (backup != NULL)
1484         {
1485             rename_file (backup, finfo->file);
1486             free (backup);
1487             backup = NULL;
1488         }
1489
1490         error (0, 0, "could not check out %s", finfo->fullname);
1491
1492         retval = status;
1493     }
1494
1495     if (backup != NULL)
1496     {
1497         /* If -f/-t wrappers are being used to wrap up a directory,
1498            then backup might be a directory instead of just a file.  */
1499         if (unlink_file_dir (backup) < 0)
1500         {
1501             /* Not sure if the existence_error check is needed here.  */
1502             if (!existence_error (errno))
1503                 /* FIXME: should include update_dir in message.  */
1504                 error (0, errno, "error removing %s", backup);
1505         }
1506         free (backup);
1507     }
1508
1509 #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
1510     if (revbuf != NULL)
1511         buf_free (revbuf);
1512 #endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */
1513     return retval;
1514 }
1515
1516
1517
1518 #ifdef SERVER_SUPPORT
1519
1520 /* This function is used to write data from a file being checked out
1521    into a buffer.  */
1522
1523 static void
1524 checkout_to_buffer (callerdat, data, len)
1525      void *callerdat;
1526      const char *data;
1527      size_t len;
1528 {
1529     struct buffer *buf = (struct buffer *) callerdat;
1530
1531     buf_output (buf, data, len);
1532 }
1533
1534 #endif /* SERVER_SUPPORT */
1535
1536 #ifdef SERVER_SUPPORT
1537
1538 /* This structure is used to pass information between patch_file and
1539    patch_file_write.  */
1540
1541 struct patch_file_data
1542 {
1543     /* File name, for error messages.  */
1544     const char *filename;
1545     /* File to which to write.  */
1546     FILE *fp;
1547     /* Whether to compute the MD5 checksum.  */
1548     int compute_checksum;
1549     /* Data structure for computing the MD5 checksum.  */
1550     struct cvs_MD5Context context;
1551     /* Set if the file has a final newline.  */
1552     int final_nl;
1553 };
1554
1555 /* Patch a file.  Runs diff.  This is only done when running as the
1556  * server.  The hope is that the diff will be smaller than the file
1557  * itself.
1558  */
1559 static int
1560 patch_file (finfo, vers_ts, docheckout, file_info, checksum)
1561     struct file_info *finfo;
1562     Vers_TS *vers_ts;
1563     int *docheckout;
1564     struct stat *file_info;
1565     unsigned char *checksum;
1566 {
1567     char *backup;
1568     char *file1;
1569     char *file2;
1570     int retval = 0;
1571     int retcode = 0;
1572     int fail;
1573     FILE *e;
1574     struct patch_file_data data;
1575
1576     *docheckout = 0;
1577
1578     if (noexec || pipeout || joining ())
1579     {
1580         *docheckout = 1;
1581         return 0;
1582     }
1583
1584     /* If this file has been marked as being binary, then never send a
1585        patch.  */
1586     if (strcmp (vers_ts->options, "-kb") == 0)
1587     {
1588         *docheckout = 1;
1589         return 0;
1590     }
1591
1592     /* First check that the first revision exists.  If it has been nuked
1593        by cvs admin -o, then just fall back to checking out entire
1594        revisions.  In some sense maybe we don't have to do this; after
1595        all cvs.texinfo says "Make sure that no-one has checked out a
1596        copy of the revision you outdate" but then again, that advice
1597        doesn't really make complete sense, because "cvs admin" operates
1598        on a working directory and so _someone_ will almost always have
1599        _some_ revision checked out.  */
1600     {
1601         char *rev;
1602
1603         rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
1604         if (rev == NULL)
1605         {
1606             *docheckout = 1;
1607             return 0;
1608         }
1609         else
1610             free (rev);
1611     }
1612
1613     /* If the revision is dead, let checkout_file handle it rather
1614        than duplicating the processing here.  */
1615     if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
1616     {
1617         *docheckout = 1;
1618         return 0;
1619     }
1620
1621     backup = xmalloc (strlen (finfo->file)
1622                       + sizeof (CVSADM)
1623                       + sizeof (CVSPREFIX)
1624                       + 10);
1625     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
1626     if (isfile (finfo->file))
1627         rename_file (finfo->file, backup);
1628     else
1629     {
1630         if (unlink_file (backup) < 0
1631             && !existence_error (errno))
1632             error (0, errno, "cannot remove %s", backup);
1633     }
1634
1635     file1 = xmalloc (strlen (finfo->file)
1636                      + sizeof (CVSADM)
1637                      + sizeof (CVSPREFIX)
1638                      + 10);
1639     (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
1640     file2 = xmalloc (strlen (finfo->file)
1641                      + sizeof (CVSADM)
1642                      + sizeof (CVSPREFIX)
1643                      + 10);
1644     (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
1645
1646     fail = 0;
1647
1648     /* We need to check out both revisions first, to see if either one
1649        has a trailing newline.  Because of this, we don't use rcsdiff,
1650        but just use diff.  */
1651
1652     e = CVS_FOPEN (file1, "w");
1653     if (e == NULL)
1654         error (1, errno, "cannot open %s", file1);
1655
1656     data.filename = file1;
1657     data.fp = e;
1658     data.final_nl = 0;
1659     data.compute_checksum = 0;
1660
1661     /* FIXME - Passing vers_ts->tag here is wrong in the least number
1662      * of cases.  Since we don't know whether vn_user was checked out
1663      * using a tag, we pass vers_ts->tag, which, assuming the user did
1664      * not specify a new TAG to -r, will be the branch we are on.
1665      *
1666      * The only thing it is used for is to substitute in for the Name
1667      * RCS keyword, so in the error case, the patch fails to apply on
1668      * the client end and we end up resending the whole file.
1669      *
1670      * At least, if we are keeping track of the tag vn_user came from,
1671      * I don't know where yet. -DRP
1672      */
1673     retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1674                             vers_ts->vn_user, vers_ts->tag,
1675                             vers_ts->options, RUN_TTY,
1676                             patch_file_write, (void *) &data);
1677
1678     if (fclose (e) < 0)
1679         error (1, errno, "cannot close %s", file1);
1680
1681     if (retcode != 0 || ! data.final_nl)
1682         fail = 1;
1683
1684     if (! fail)
1685     {
1686         e = CVS_FOPEN (file2, "w");
1687         if (e == NULL)
1688             error (1, errno, "cannot open %s", file2);
1689
1690         data.filename = file2;
1691         data.fp = e;
1692         data.final_nl = 0;
1693         data.compute_checksum = 1;
1694         cvs_MD5Init (&data.context);
1695
1696         retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
1697                                 vers_ts->vn_rcs, vers_ts->tag,
1698                                 vers_ts->options, RUN_TTY,
1699                                 patch_file_write, (void *) &data);
1700
1701         if (fclose (e) < 0)
1702             error (1, errno, "cannot close %s", file2);
1703
1704         if (retcode != 0 || ! data.final_nl)
1705             fail = 1;
1706         else
1707             cvs_MD5Final (checksum, &data.context);
1708     }     
1709
1710     retcode = 0;
1711     if (! fail)
1712     {
1713         int dargc = 0;
1714         size_t darg_allocated = 0;
1715         char **dargv = NULL;
1716
1717         /* If the client does not support the Rcs-diff command, we
1718            send a context diff, and the client must invoke patch.
1719            That approach was problematical for various reasons.  The
1720            new approach only requires running diff in the server; the
1721            client can handle everything without invoking an external
1722            program.  */
1723         if (!rcs_diff_patches)
1724             /* We use -c, not -u, because that is what CVS has
1725                traditionally used.  Kind of a moot point, now that
1726                Rcs-diff is preferred, so there is no point in making
1727                the compatibility issues worse.  */
1728             run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
1729         else
1730             /* Now that diff is librarified, we could be passing -a if
1731                we wanted to.  However, it is unclear to me whether we
1732                would want to.  Does diff -a, in any significant
1733                percentage of cases, produce patches which are smaller
1734                than the files it is patching?  I guess maybe text
1735                files with character sets which diff regards as
1736                'binary'.  Conversely, do they tend to be much larger
1737                in the bad cases?  This needs some more
1738                thought/investigation, I suspect.  */
1739             run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
1740         retcode = diff_exec (file1, file2, NULL, NULL, dargc, dargv,
1741                              finfo->file);
1742         run_arg_free_p (dargc, dargv);
1743         free (dargv);
1744
1745         /* A retcode of 0 means no differences.  1 means some differences.  */
1746         if (retcode != 0
1747             && retcode != 1)
1748         {
1749             fail = 1;
1750         }
1751     }
1752
1753     if (! fail)
1754     {
1755         struct stat file2_info;
1756
1757         /* Check to make sure the patch is really shorter */
1758         if (CVS_STAT (file2, &file2_info) < 0)
1759             error (1, errno, "could not stat %s", file2);
1760         if (CVS_STAT (finfo->file, file_info) < 0)
1761             error (1, errno, "could not stat %s", finfo->file);
1762         if (file2_info.st_size <= file_info->st_size)
1763             fail = 1;
1764     }
1765
1766     if (! fail)
1767     {
1768 # define BINARY "Binary"
1769         char buf[sizeof BINARY];
1770         unsigned int c;
1771
1772         /* Check the diff output to make sure patch will be handle it.  */
1773         e = CVS_FOPEN (finfo->file, "r");
1774         if (e == NULL)
1775             error (1, errno, "could not open diff output file %s",
1776                    finfo->fullname);
1777         c = fread (buf, 1, sizeof BINARY - 1, e);
1778         buf[c] = '\0';
1779         if (strcmp (buf, BINARY) == 0)
1780         {
1781             /* These are binary files.  We could use diff -a, but
1782                patch can't handle that.  */
1783             fail = 1;
1784         }
1785         fclose (e);
1786     }
1787
1788     if (! fail)
1789     {
1790         Vers_TS *xvers_ts;
1791
1792         /* Stat the original RCS file, and then adjust it the way
1793            that RCS_checkout would.  FIXME: This is an abstraction
1794            violation.  */
1795         if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0)
1796             error (1, errno, "could not stat %s", vers_ts->srcfile->path);
1797         if (chmod (finfo->file,
1798                    file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
1799             < 0)
1800             error (0, errno, "cannot change mode of file %s", finfo->file);
1801         if (cvswrite
1802             && !fileattr_get (finfo->file, "_watched"))
1803             xchmod (finfo->file, 1);
1804
1805         /* This stuff is just copied blindly from checkout_file.  I
1806            don't really know what it does.  */
1807         xvers_ts = Version_TS (finfo, options, tag, date,
1808                                force_tag_match, 0);
1809         if (strcmp (xvers_ts->options, "-V4") == 0)
1810             xvers_ts->options[0] = '\0';
1811
1812         Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
1813                   xvers_ts->ts_user, xvers_ts->options,
1814                   xvers_ts->tag, xvers_ts->date, NULL);
1815
1816         if (CVS_STAT (finfo->file, file_info) < 0)
1817             error (1, errno, "could not stat %s", finfo->file);
1818
1819         /* If this is really Update and not Checkout, record history.  */
1820         if (strcmp (cvs_cmd_name, "update") == 0)
1821             history_write ('P', finfo->update_dir, xvers_ts->vn_rcs,
1822                            finfo->file, finfo->repository);
1823
1824         freevers_ts (&xvers_ts);
1825
1826         if (!really_quiet)
1827         {
1828             write_letter (finfo, 'P');
1829         }
1830     }
1831     else
1832     {
1833         int old_errno = errno;          /* save errno value over the rename */
1834
1835         if (isfile (backup))
1836             rename_file (backup, finfo->file);
1837
1838         if (retcode != 0 && retcode != 1)
1839             error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
1840                    "could not diff %s", finfo->fullname);
1841
1842         *docheckout = 1;
1843         retval = retcode;
1844     }
1845
1846     if (unlink_file (backup) < 0
1847         && !existence_error (errno))
1848         error (0, errno, "cannot remove %s", backup);
1849     if (unlink_file (file1) < 0
1850         && !existence_error (errno))
1851         error (0, errno, "cannot remove %s", file1);
1852     if (unlink_file (file2) < 0
1853         && !existence_error (errno))
1854         error (0, errno, "cannot remove %s", file2);
1855
1856     free (backup);
1857     free (file1);
1858     free (file2);
1859     return retval;
1860 }
1861
1862
1863
1864 /* Write data to a file.  Record whether the last byte written was a
1865    newline.  Optionally compute a checksum.  This is called by
1866    patch_file via RCS_checkout.  */
1867
1868 static void
1869 patch_file_write (callerdat, buffer, len)
1870      void *callerdat;
1871      const char *buffer;
1872      size_t len;
1873 {
1874     struct patch_file_data *data = (struct patch_file_data *) callerdat;
1875
1876     if (fwrite (buffer, 1, len, data->fp) != len)
1877         error (1, errno, "cannot write %s", data->filename);
1878
1879     data->final_nl = (buffer[len - 1] == '\n');
1880
1881     if (data->compute_checksum)
1882         cvs_MD5Update (&data->context, (unsigned char *) buffer, len);
1883 }
1884
1885 #endif /* SERVER_SUPPORT */
1886
1887 /*
1888  * Several of the types we process only print a bit of information consisting
1889  * of a single letter and the name.
1890  */
1891 void
1892 write_letter (finfo, letter)
1893     struct file_info *finfo;
1894     int letter;
1895 {
1896     if (!really_quiet)
1897     {
1898         char *tag = NULL;
1899         /* Big enough for "+updated" or any of its ilk.  */
1900         char buf[80];
1901
1902         switch (letter)
1903         {
1904             case 'U':
1905                 tag = "updated";
1906                 break;
1907             default:
1908                 /* We don't yet support tagged output except for "U".  */
1909                 break;
1910         }
1911
1912         if (tag != NULL)
1913         {
1914             sprintf (buf, "+%s", tag);
1915             cvs_output_tagged (buf, NULL);
1916         }
1917         buf[0] = letter;
1918         buf[1] = ' ';
1919         buf[2] = '\0';
1920         cvs_output_tagged ("text", buf);
1921         cvs_output_tagged ("fname", finfo->fullname);
1922         cvs_output_tagged ("newline", NULL);
1923         if (tag != NULL)
1924         {
1925             sprintf (buf, "-%s", tag);
1926             cvs_output_tagged (buf, NULL);
1927         }
1928     }
1929     return;
1930 }
1931
1932
1933
1934 /* Reregister a file after a merge.  */
1935 static void
1936 RegisterMerge PROTO((struct file_info *finfo, Vers_TS *vers,
1937                      const char *backup, int has_conflicts));
1938 static void
1939 RegisterMerge (finfo, vers, backup, has_conflicts)
1940     struct file_info *finfo;
1941     Vers_TS *vers;
1942     const char *backup;
1943     int has_conflicts;
1944 {
1945     /* This file is the result of a merge, which means that it has
1946        been modified.  We use a special timestamp string which will
1947        not compare equal to any actual timestamp.  */
1948     char *cp = NULL;
1949
1950     if (has_conflicts)
1951     {
1952         time (&last_register_time);
1953         cp = time_stamp (finfo->file);
1954     }
1955     Register (finfo->entries, finfo->file, vers->vn_rcs ? vers->vn_rcs : "0",
1956               "Result of merge", vers->options, vers->tag, vers->date, cp);
1957     if (cp)
1958         free (cp);
1959
1960 #ifdef SERVER_SUPPORT
1961     /* Send the new contents of the file before the message.  If we
1962        wanted to be totally correct, we would have the client write
1963        the message only after the file has safely been written.  */
1964     if (server_active)
1965     {
1966         server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
1967                           backup);
1968         server_updated (finfo, vers, SERVER_MERGED, (mode_t) -1, NULL, NULL);
1969     }
1970 #endif
1971 }
1972
1973
1974
1975 /*
1976  * Do all the magic associated with a file which needs to be merged
1977  */
1978 static int
1979 merge_file (finfo, vers)
1980     struct file_info *finfo;
1981     Vers_TS *vers;
1982 {
1983     char *backup;
1984     int status;
1985     int retcode = 0;
1986     int retval;
1987
1988     assert (vers->vn_user);
1989
1990     /*
1991      * The users currently modified file is moved to a backup file name
1992      * ".#filename.version", so that it will stay around for a few days
1993      * before being automatically removed by some cron daemon.  The "version"
1994      * is the version of the file that the user was most up-to-date with
1995      * before the merge.
1996      */
1997     backup = xmalloc (strlen (finfo->file)
1998                       + strlen (vers->vn_user)
1999                       + sizeof (BAKPREFIX)
2000                       + 10);
2001     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
2002
2003     if (unlink_file (backup) && !existence_error (errno))
2004         error (0, errno, "unable to remove %s", backup);
2005     copy_file (finfo->file, backup);
2006     xchmod (finfo->file, 1);
2007
2008     if (strcmp (vers->options, "-kb") == 0
2009         || wrap_merge_is_copy (finfo->file)
2010         || special_file_mismatch (finfo, NULL, vers->vn_rcs))
2011     {
2012         /* For binary files, a merge is always a conflict.  Same for
2013            files whose permissions or linkage do not match.  We give the
2014            user the two files, and let them resolve it.  It is possible
2015            that we should require a "touch foo" or similar step before
2016            we allow a checkin.  */
2017
2018         /* TODO: it may not always be necessary to regard a permission
2019            mismatch as a conflict.  The working file and the RCS file
2020            have a common ancestor `A'; if the working file's permissions
2021            match A's, then it's probably safe to overwrite them with the
2022            RCS permissions.  Only if the working file, the RCS file, and
2023            A all disagree should this be considered a conflict.  But more
2024            thought needs to go into this, and in the meantime it is safe
2025            to treat any such mismatch as an automatic conflict. -twp */
2026
2027         retcode = RCS_checkout (finfo->rcs, finfo->file,
2028                                 vers->vn_rcs, vers->tag,
2029                                 vers->options, NULL, NULL, NULL);
2030         if (retcode)
2031         {
2032             error (0, 0, "failed to check out `%s' file", finfo->fullname);
2033             error (0, 0, "restoring `%s' from backup file `%s'",
2034                    finfo->fullname, backup);
2035             rename_file (backup, finfo->file);
2036             retval = 1;
2037             goto out;
2038         }
2039         xchmod (finfo->file, 1);
2040
2041         RegisterMerge (finfo, vers, backup, 1);
2042
2043         /* Is there a better term than "nonmergeable file"?  What we
2044            really mean is, not something that CVS cannot or does not
2045            want to merge (there might be an external manual or
2046            automatic merge process).  */
2047         error (0, 0, "nonmergeable file needs merge");
2048         error (0, 0, "revision %s from repository is now in %s",
2049                vers->vn_rcs, finfo->fullname);
2050         error (0, 0, "file from working directory is now in %s", backup);
2051         write_letter (finfo, 'C');
2052
2053         history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
2054                        finfo->repository);
2055         retval = 0;
2056         goto out;
2057     }
2058
2059     status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
2060                         vers->options, vers->vn_user, vers->vn_rcs);
2061     if (status != 0 && status != 1)
2062     {
2063         error (0, status == -1 ? errno : 0,
2064                "could not merge revision %s of %s", vers->vn_user, finfo->fullname);
2065         error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
2066                finfo->fullname, backup);
2067         rename_file (backup, finfo->file);
2068         retval = 1;
2069         goto out;
2070     }
2071
2072     if (strcmp (vers->options, "-V4") == 0)
2073         vers->options[0] = '\0';
2074
2075     /* fix up the vers structure, in case it is used by join */
2076     if (join_rev1)
2077     {
2078         /* FIXME: Throwing away the original revision info is almost
2079            certainly wrong -- what if join_rev1 is "BASE"?  */
2080         if (vers->vn_user != NULL)
2081             free (vers->vn_user);
2082         vers->vn_user = xstrdup (vers->vn_rcs);
2083     }
2084
2085     RegisterMerge (finfo, vers, backup, status);
2086
2087     /* FIXME: the noexec case is broken.  RCS_merge could be doing the
2088        xcmp on the temporary files without much hassle, I think.  */
2089     if (!noexec && !xcmp (backup, finfo->file))
2090     {
2091         cvs_output (finfo->fullname, 0);
2092         cvs_output (" already contains the differences between ", 0);
2093         cvs_output (vers->vn_user, 0);
2094         cvs_output (" and ", 0);
2095         cvs_output (vers->vn_rcs, 0);
2096         cvs_output ("\n", 1);
2097
2098         history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
2099                        finfo->repository);
2100         retval = 0;
2101         goto out;
2102     }
2103
2104     if (status == 1)
2105     {
2106         error (0, 0, "conflicts found in %s", finfo->fullname);
2107
2108         write_letter (finfo, 'C');
2109
2110         history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
2111                        finfo->repository);
2112
2113     }
2114     else if (retcode == -1)
2115     {
2116         error (1, errno, "fork failed while examining update of %s",
2117                finfo->fullname);
2118     }
2119     else
2120     {
2121         write_letter (finfo, 'M');
2122         history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
2123                        finfo->repository);
2124     }
2125     retval = 0;
2126  out:
2127     free (backup);
2128     return retval;
2129 }
2130
2131
2132
2133 /*
2134  * Do all the magic associated with a file which needs to be joined
2135  * (reached via the -j option to checkout or update).
2136  *
2137  * INPUTS
2138  *   finfo              File information about the destination file.
2139  *   vers               The Vers_TS structure for finfo.
2140  *
2141  * GLOBALS
2142  *   join_rev1          From the command line.
2143  *   join_rev2          From the command line.
2144  *   server_active      Natch.
2145  *
2146  * ASSUMPTIONS
2147  *   1.  Is not called in client mode.
2148  */
2149 static void
2150 join_file (finfo, vers)
2151     struct file_info *finfo;
2152     Vers_TS *vers;
2153 {
2154     char *backup;
2155     char *t_options;
2156     int status;
2157
2158     char *rev1;
2159     char *rev2;
2160     char *jrev1;
2161     char *jrev2;
2162     char *jdate1;
2163     char *jdate2;
2164
2165     if (trace)
2166         fprintf (stderr, "%s-> join_file(%s, %s%s%s%s, %s, %s)\n",
2167                 CLIENT_SERVER_STR,
2168                 finfo->file,
2169                 vers->tag ? vers->tag : "",
2170                 vers->tag ? " (" : "",
2171                 vers->vn_rcs ? vers->vn_rcs : "",
2172                 vers->tag ? ")" : "",
2173                 join_rev1 ? join_rev1 : "",
2174                 join_rev2 ? join_rev2 : "");
2175
2176     jrev1 = join_rev1;
2177     jrev2 = join_rev2;
2178     jdate1 = date_rev1;
2179     jdate2 = date_rev2;
2180
2181     /* Determine if we need to do anything at all.  */
2182     if (vers->srcfile == NULL ||
2183         vers->srcfile->path == NULL)
2184     {
2185         return;
2186     }
2187
2188     /* If only one join revision is specified, it becomes the second
2189        revision.  */
2190     if (jrev2 == NULL)
2191     {
2192         jrev2 = jrev1;
2193         jrev1 = NULL;
2194         jdate2 = jdate1;
2195         jdate1 = NULL;
2196     }
2197
2198     /* FIXME: Need to handle "BASE" for jrev1 and/or jrev2.  Note caveat
2199        below about vn_user.  */
2200
2201     /* Convert the second revision, walking branches and dates.  */
2202     rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, (int *) NULL);
2203
2204     /* If this is a merge of two revisions, get the first revision.
2205        If only one join tag was specified, then the first revision is
2206        the greatest common ancestor of the second revision and the
2207        working file.  */
2208     if (jrev1 != NULL)
2209         rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, (int *) NULL);
2210     else
2211     {
2212         /* Note that we use vn_rcs here, since vn_user may contain a
2213            special string such as "-nn".  */
2214         if (vers->vn_rcs == NULL)
2215             rev1 = NULL;
2216         else if (rev2 == NULL)
2217         {
2218             /* This means that the file never existed on the branch.
2219                It does not mean that the file was removed on the
2220                branch: that case is represented by a dead rev2.  If
2221                the file never existed on the branch, then we have
2222                nothing to merge, so we just return.  */
2223             return;
2224         }
2225         else
2226             rev1 = gca (vers->vn_rcs, rev2);
2227     }
2228
2229     /* Handle a nonexistent or dead merge target.  */
2230     if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
2231     {
2232         char *mrev;
2233
2234         if (rev2 != NULL)
2235             free (rev2);
2236
2237         /* If the first revision doesn't exist either, then there is
2238            no change between the two revisions, so we don't do
2239            anything.  */
2240         if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2241         {
2242             if (rev1 != NULL)
2243                 free (rev1);
2244             return;
2245         }
2246
2247         /* If we are merging two revisions, then the file was removed
2248            between the first revision and the second one.  In this
2249            case we want to mark the file for removal.
2250
2251            If we are merging one revision, then the file has been
2252            removed between the greatest common ancestor and the merge
2253            revision.  From the perspective of the branch on to which
2254            we ar emerging, which may be the trunk, either 1) the file
2255            does not currently exist on the target, or 2) the file has
2256            not been modified on the target branch since the greatest
2257            common ancestor, or 3) the file has been modified on the
2258            target branch since the greatest common ancestor.  In case
2259            1 there is nothing to do.  In case 2 we mark the file for
2260            removal.  In case 3 we have a conflict.
2261
2262            Note that the handling is slightly different depending upon
2263            whether one or two join targets were specified.  If two
2264            join targets were specified, we don't check whether the
2265            file was modified since a given point.  My reasoning is
2266            that if you ask for an explicit merge between two tags,
2267            then you want to merge in whatever was changed between
2268            those two tags.  If a file was removed between the two
2269            tags, then you want it to be removed.  However, if you ask
2270            for a merge of a branch, then you want to merge in all
2271            changes which were made on the branch.  If a file was
2272            removed on the branch, that is a change to the file.  If
2273            the file was also changed on the main line, then that is
2274            also a change.  These two changes--the file removal and the
2275            modification--must be merged.  This is a conflict.  */
2276
2277         /* If the user file is dead, or does not exist, or has been
2278            marked for removal, then there is nothing to do.  */
2279         if (vers->vn_user == NULL
2280             || vers->vn_user[0] == '-'
2281             || RCS_isdead (vers->srcfile, vers->vn_user))
2282         {
2283             if (rev1 != NULL)
2284                 free (rev1);
2285             return;
2286         }
2287
2288         /* If the user file has been marked for addition, or has been
2289            locally modified, then we have a conflict which we can not
2290            resolve.  No_Difference will already have been called in
2291            this case, so comparing the timestamps is sufficient to
2292            determine whether the file is locally modified.  */
2293         if (strcmp (vers->vn_user, "0") == 0
2294             || (vers->ts_user != NULL
2295                 && strcmp (vers->ts_user, vers->ts_rcs) != 0))
2296         {
2297             if (jdate2 != NULL)
2298                 error (0, 0,
2299                        "file %s is locally modified, but has been removed in revision %s as of %s",
2300                        finfo->fullname, jrev2, jdate2);
2301             else
2302                 error (0, 0,
2303                        "file %s is locally modified, but has been removed in revision %s",
2304                        finfo->fullname, jrev2);
2305
2306             /* FIXME: Should we arrange to return a non-zero exit
2307                status?  */
2308
2309             if (rev1 != NULL)
2310                 free (rev1);
2311
2312             return;
2313         }
2314
2315         /* If only one join tag was specified, and the user file has
2316            been changed since the greatest common ancestor (rev1),
2317            then there is a conflict we can not resolve.  See above for
2318            the rationale.  */
2319         if (join_rev2 == NULL
2320             && strcmp (rev1, vers->vn_user) != 0)
2321         {
2322             if (jdate2 != NULL)
2323                 error (0, 0,
2324                        "file %s has been modified, but has been removed in revision %s as of %s",
2325                        finfo->fullname, jrev2, jdate2);
2326             else
2327                 error (0, 0,
2328                        "file %s has been modified, but has been removed in revision %s",
2329                        finfo->fullname, jrev2);
2330
2331             /* FIXME: Should we arrange to return a non-zero exit
2332                status?  */
2333
2334             if (rev1 != NULL)
2335                 free (rev1);
2336
2337             return;
2338         }
2339
2340         if (rev1 != NULL)
2341             free (rev1);
2342
2343         /* The user file exists and has not been modified.  Mark it
2344            for removal.  FIXME: If we are doing a checkout, this has
2345            the effect of first checking out the file, and then
2346            removing it.  It would be better to just register the
2347            removal. 
2348         
2349            The same goes for a removal then an add.  e.g.
2350            cvs up -rbr -jbr2 could remove and readd the same file
2351          */
2352         /* save the rev since server_updated might invalidate it */
2353         mrev = xmalloc (strlen (vers->vn_user) + 2);
2354         sprintf (mrev, "-%s", vers->vn_user);
2355 #ifdef SERVER_SUPPORT
2356         if (server_active)
2357         {
2358             server_scratch (finfo->file);
2359             server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
2360                             (unsigned char *) NULL, (struct buffer *) NULL);
2361         }
2362 #endif
2363         Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
2364                   vers->options, vers->tag, vers->date, vers->ts_conflict);
2365         free (mrev);
2366         /* We need to check existence_error here because if we are
2367            running as the server, and the file is up to date in the
2368            working directory, the client will not have sent us a copy.  */
2369         if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
2370             error (0, errno, "cannot remove file %s", finfo->fullname);
2371 #ifdef SERVER_SUPPORT
2372         if (server_active)
2373             server_checked_in (finfo->file, finfo->update_dir,
2374                                finfo->repository);
2375 #endif
2376         if (! really_quiet)
2377             error (0, 0, "scheduling %s for removal", finfo->fullname);
2378
2379         return;
2380     }
2381
2382     /* If the two merge revisions are the same, then there is nothing
2383      * to do.  This needs to be checked before the rev2 == up-to-date base
2384      * revision check tha comes next.  Otherwise, rev1 can == rev2 and get an
2385      * "already contains the changes between <rev1> and <rev1>" message.
2386      */
2387     if (rev1 && strcmp (rev1, rev2) == 0)
2388     {
2389         free (rev1);
2390         free (rev2);
2391         return;
2392     }
2393
2394     /* If we know that the user file is up-to-date, then it becomes an
2395      * optimization to skip the merge when rev2 is the same as the base
2396      * revision.  i.e. we know that diff3(file2,file1,file2) will produce
2397      * file2.
2398      */
2399     if (vers->vn_user != NULL && vers->ts_user != NULL
2400         && strcmp (vers->ts_user, vers->ts_rcs) == 0
2401         && strcmp (rev2, vers->vn_user) == 0)
2402     {
2403         if (!really_quiet)
2404         {
2405             cvs_output (finfo->fullname, 0);
2406             cvs_output (" already contains the differences between ", 0);
2407             cvs_output (rev1 ? rev1 : "creation", 0);
2408             cvs_output (" and ", 0);
2409             cvs_output (rev2, 0);
2410             cvs_output ("\n", 1);
2411         }
2412
2413         if (rev1 != NULL)
2414             free (rev1);
2415         free (rev2);
2416
2417         return;
2418     }
2419
2420     /* If rev1 is dead or does not exist, then the file was added
2421        between rev1 and rev2.  */
2422     if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
2423     {
2424         if (rev1 != NULL)
2425             free (rev1);
2426         free (rev2);
2427
2428         /* If the file does not exist in the working directory, then
2429            we can just check out the new revision and mark it for
2430            addition.  */
2431         if (vers->vn_user == NULL)
2432         {
2433             char *saved_options = options;
2434             Vers_TS *xvers;
2435
2436             xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
2437
2438             /* Reset any keyword expansion option.  Otherwise, when a
2439                command like `cvs update -kk -jT1 -jT2' creates a new file
2440                (because a file had the T2 tag, but not T1), the subsequent
2441                commit of that just-added file effectively would set the
2442                admin `-kk' option for that file in the repository.  */
2443             options = NULL;
2444
2445             /* FIXME: If checkout_file fails, we should arrange to
2446                return a non-zero exit status.  */
2447             status = checkout_file (finfo, xvers, 1, 0, 1);
2448             options = saved_options;
2449
2450             freevers_ts (&xvers);
2451
2452             return;
2453         }
2454
2455         /* The file currently exists in the working directory, so we
2456            have a conflict which we can not resolve.  Note that this
2457            is true even if the file is marked for addition or removal.  */
2458
2459         if (jdate2 != NULL)
2460             error (0, 0,
2461                    "file %s exists, but has been added in revision %s as of %s",
2462                    finfo->fullname, jrev2, jdate2);
2463         else
2464             error (0, 0,
2465                    "file %s exists, but has been added in revision %s",
2466                    finfo->fullname, jrev2);
2467
2468         return;
2469     }
2470
2471     /* If there is no working file, then we can't do the merge.  */
2472     if (vers->vn_user == NULL || vers->vn_user[0] == '-')
2473     {
2474         free (rev1);
2475         free (rev2);
2476
2477         if (jdate2 != NULL)
2478             error (0, 0,
2479                    "file %s does not exist, but is present in revision %s as of %s",
2480                    finfo->fullname, jrev2, jdate2);
2481         else
2482             error (0, 0,
2483                    "file %s does not exist, but is present in revision %s",
2484                    finfo->fullname, jrev2);
2485
2486         /* FIXME: Should we arrange to return a non-zero exit status?  */
2487
2488         return;
2489     }
2490
2491 #ifdef SERVER_SUPPORT
2492     if (server_active && !isreadable (finfo->file))
2493     {
2494         int retcode;
2495         /* The file is up to date.  Need to check out the current contents.  */
2496         /* FIXME - see the FIXME comment above the call to RCS_checkout in the
2497          * patch_file function.
2498          */
2499         retcode = RCS_checkout (vers->srcfile, finfo->file,
2500                                 vers->vn_user, vers->tag,
2501                                 (char *) NULL, RUN_TTY,
2502                                 (RCSCHECKOUTPROC) NULL, (void *) NULL);
2503         if (retcode != 0)
2504             error (1, 0,
2505                    "failed to check out %s file", finfo->fullname);
2506     }
2507 #endif
2508
2509     /*
2510      * The users currently modified file is moved to a backup file name
2511      * ".#filename.version", so that it will stay around for a few days
2512      * before being automatically removed by some cron daemon.  The "version"
2513      * is the version of the file that the user was most up-to-date with
2514      * before the merge.
2515      */
2516     backup = xmalloc (strlen (finfo->file)
2517                       + strlen (vers->vn_user)
2518                       + sizeof (BAKPREFIX)
2519                       + 10);
2520     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
2521
2522     if (unlink_file (backup) < 0
2523         && !existence_error (errno))
2524         error (0, errno, "cannot remove %s", backup);
2525     copy_file (finfo->file, backup);
2526     xchmod (finfo->file, 1);
2527
2528     t_options = vers->options;
2529 #if 0
2530     if (*t_options == '\0')
2531         t_options = "-kk";              /* to ignore keyword expansions */
2532 #endif
2533
2534     /* If the source of the merge is the same as the working file
2535        revision, then we can just RCS_checkout the target (no merging
2536        as such).  In the text file case, this is probably quite
2537        similar to the RCS_merge, but in the binary file case,
2538        RCS_merge gives all kinds of trouble.  */
2539     if (vers->vn_user != NULL
2540         && strcmp (rev1, vers->vn_user) == 0
2541         /* See comments above about how No_Difference has already been
2542            called.  */
2543         && vers->ts_user != NULL
2544         && strcmp (vers->ts_user, vers->ts_rcs) == 0
2545
2546         /* Avoid this in the text file case.  See below for why.
2547          */
2548         && (strcmp (t_options, "-kb") == 0
2549             || wrap_merge_is_copy (finfo->file)))
2550     {
2551         /* FIXME: Verify my comment below:
2552          *
2553          * RCS_merge does nothing with keywords.  It merges the changes between
2554          * two revisions without expanding the keywords (it might expand in
2555          * -kk mode before computing the diff between rev1 and rev2 - I'm not
2556          * sure).  In other words, the keyword lines in the current work file
2557          * get left alone.
2558          *
2559          * Therfore, checking out the destination revision (rev2) is probably
2560          * incorrect in the text case since we should see the keywords that were
2561          * substituted into the original file at the time it was checked out
2562          * and not the keywords from rev2.
2563          *
2564          * Also, it is safe to pass in NULL for nametag since we know no
2565          * substitution is happening during the binary mode checkout.
2566          */
2567         if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL, t_options,
2568                            RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0 )
2569             status = 2;
2570         else
2571             status = 0;
2572
2573         /* OK, this is really stupid.  RCS_checkout carefully removes
2574            write permissions, and we carefully put them back.  But
2575            until someone gets around to fixing it, that seems like the
2576            easiest way to get what would seem to be the right mode.
2577            I don't check CVSWRITE or _watched; I haven't thought about
2578            that in great detail, but it seems like a watched file should
2579            be checked out (writable) after a merge.  */
2580         xchmod (finfo->file, 1);
2581
2582         /* Traditionally, the text file case prints a whole bunch of
2583            scary looking and verbose output which fails to tell the user
2584            what is really going on (it gives them rev1 and rev2 but doesn't
2585            indicate in any way that rev1 == vn_user).  I think just a
2586            simple "U foo" is good here; it seems analogous to the case in
2587            which the file was added on the branch in terms of what to
2588            print.  */
2589         write_letter (finfo, 'U');
2590     }
2591     else if (strcmp (t_options, "-kb") == 0
2592              || wrap_merge_is_copy (finfo->file)
2593              || special_file_mismatch (finfo, rev1, rev2))
2594     {
2595         /* We are dealing with binary files, or files with a
2596            permission/linkage mismatch (this second case only occurs when
2597            PRESERVE_PERMISSIONS_SUPPORT is enabled), and real merging would
2598            need to take place.  This is a conflict.  We give the user
2599            the two files, and let them resolve it.  It is possible
2600            that we should require a "touch foo" or similar step before
2601            we allow a checkin.  */
2602         if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL,
2603                            t_options, RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
2604             status = 2;
2605         else
2606             status = 0;
2607
2608         /* OK, this is really stupid.  RCS_checkout carefully removes
2609            write permissions, and we carefully put them back.  But
2610            until someone gets around to fixing it, that seems like the
2611            easiest way to get what would seem to be the right mode.
2612            I don't check CVSWRITE or _watched; I haven't thought about
2613            that in great detail, but it seems like a watched file should
2614            be checked out (writable) after a merge.  */
2615         xchmod (finfo->file, 1);
2616
2617         /* Hmm.  We don't give them REV1 anywhere.  I guess most people
2618            probably don't have a 3-way merge tool for the file type in
2619            question, and might just get confused if we tried to either
2620            provide them with a copy of the file from REV1, or even just
2621            told them what REV1 is so they can get it themself, but it
2622            might be worth thinking about.  */
2623         /* See comment in merge_file about the "nonmergeable file"
2624            terminology.  */
2625         error (0, 0, "nonmergeable file needs merge");
2626         error (0, 0, "revision %s from repository is now in %s",
2627                rev2, finfo->fullname);
2628         error (0, 0, "file from working directory is now in %s", backup);
2629         write_letter (finfo, 'C');
2630     }
2631     else
2632         status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
2633                             t_options, rev1, rev2);
2634
2635     if (status != 0)
2636     {
2637         if (status != 1)
2638         {
2639             error (0, status == -1 ? errno : 0,
2640                    "could not merge revision %s of %s", rev2, finfo->fullname);
2641             error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
2642                    finfo->fullname, backup);
2643             rename_file (backup, finfo->file);
2644         }
2645     }
2646     else /* status == 0 */
2647     {
2648         /* FIXME: the noexec case is broken.  RCS_merge could be doing the
2649            xcmp on the temporary files without much hassle, I think.  */
2650         if (!noexec && !xcmp (backup, finfo->file))
2651         {
2652             if (!really_quiet)
2653             {
2654                 cvs_output (finfo->fullname, 0);
2655                 cvs_output (" already contains the differences between ", 0);
2656                 cvs_output (rev1, 0);
2657                 cvs_output (" and ", 0);
2658                 cvs_output (rev2, 0);
2659                 cvs_output ("\n", 1);
2660             }
2661
2662             /* and skip the registering and sending the new file since it
2663              * hasn't been updated.
2664              */
2665             goto out;
2666         }
2667     }
2668
2669     /* The file has changed, but if we just checked it out it may
2670        still have the same timestamp it did when it was first
2671        registered above in checkout_file.  We register it again with a
2672        dummy timestamp to make sure that later runs of CVS will
2673        recognize that it has changed.
2674
2675        We don't actually need to register again if we called
2676        RCS_checkout above, and we aren't running as the server.
2677        However, that is not the normal case, and calling Register
2678        again won't cost much in that case.  */
2679     RegisterMerge (finfo, vers, backup, status);
2680
2681 out:
2682     free (rev1);
2683     free (rev2);
2684     free (backup);
2685 }
2686
2687
2688
2689 /*
2690  * Report whether revisions REV1 and REV2 of FINFO agree on:
2691  *   . file ownership
2692  *   . permissions
2693  *   . major and minor device numbers
2694  *   . symbolic links
2695  *   . hard links
2696  *
2697  * If either REV1 or REV2 is NULL, the working copy is used instead.
2698  *
2699  * Return 1 if the files differ on these data.
2700  */
2701
2702 int
2703 special_file_mismatch (finfo, rev1, rev2)
2704     struct file_info *finfo;
2705     char *rev1;
2706     char *rev2;
2707 {
2708 #ifdef PRESERVE_PERMISSIONS_SUPPORT
2709     struct stat sb;
2710     RCSVers *vp;
2711     Node *n;
2712     uid_t rev1_uid, rev2_uid;
2713     gid_t rev1_gid, rev2_gid;
2714     mode_t rev1_mode, rev2_mode;
2715     unsigned long dev_long;
2716     dev_t rev1_dev, rev2_dev;
2717     char *rev1_symlink = NULL;
2718     char *rev2_symlink = NULL;
2719     List *rev1_hardlinks = NULL;
2720     List *rev2_hardlinks = NULL;
2721     int check_uids, check_gids, check_modes;
2722     int result;
2723
2724     /* If we don't care about special file info, then
2725        don't report a mismatch in any case. */
2726     if (!preserve_perms)
2727         return 0;
2728
2729     /* When special_file_mismatch is called from No_Difference, the
2730        RCS file has been only partially parsed.  We must read the
2731        delta tree in order to compare special file info recorded in
2732        the delta nodes.  (I think this is safe. -twp) */
2733     if (finfo->rcs->flags & PARTIAL)
2734         RCS_reparsercsfile (finfo->rcs, NULL, NULL);
2735
2736     check_uids = check_gids = check_modes = 1;
2737
2738     /* Obtain file information for REV1.  If this is null, then stat
2739        finfo->file and use that info. */
2740     /* If a revision does not know anything about its status,
2741        then presumably it doesn't matter, and indicates no conflict. */
2742
2743     if (rev1 == NULL)
2744     {
2745         if (islink (finfo->file))
2746             rev1_symlink = xreadlink (finfo->file);
2747         else
2748         {
2749 # ifdef HAVE_STRUCT_STAT_ST_RDEV
2750             if (CVS_LSTAT (finfo->file, &sb) < 0)
2751                 error (1, errno, "could not get file information for %s",
2752                        finfo->file);
2753             rev1_uid = sb.st_uid;
2754             rev1_gid = sb.st_gid;
2755             rev1_mode = sb.st_mode;
2756             if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
2757                 rev1_dev = sb.st_rdev;
2758 # else
2759             error (1, 0, "cannot handle device files on this system (%s)",
2760                    finfo->file);
2761 # endif
2762         }
2763         rev1_hardlinks = list_linked_files_on_disk (finfo->file);
2764     }
2765     else
2766     {
2767         n = findnode (finfo->rcs->versions, rev1);
2768         vp = n->data;
2769
2770         n = findnode (vp->other_delta, "symlink");
2771         if (n != NULL)
2772             rev1_symlink = xstrdup (n->data);
2773         else
2774         {
2775             n = findnode (vp->other_delta, "owner");
2776             if (n == NULL)
2777                 check_uids = 0; /* don't care */
2778             else
2779                 rev1_uid = strtoul (n->data, NULL, 10);
2780
2781             n = findnode (vp->other_delta, "group");
2782             if (n == NULL)
2783                 check_gids = 0; /* don't care */
2784             else
2785                 rev1_gid = strtoul (n->data, NULL, 10);
2786
2787             n = findnode (vp->other_delta, "permissions");
2788             if (n == NULL)
2789                 check_modes = 0;        /* don't care */
2790             else
2791                 rev1_mode = strtoul (n->data, NULL, 8);
2792
2793             n = findnode (vp->other_delta, "special");
2794             if (n == NULL)
2795                 rev1_mode |= S_IFREG;
2796             else
2797             {
2798                 /* If the size of `ftype' changes, fix the sscanf call also */
2799                 char ftype[16];
2800                 if (sscanf (n->data, "%15s %lu", ftype,
2801                             &dev_long) < 2)
2802                     error (1, 0, "%s:%s has bad `special' newphrase %s",
2803                            finfo->file, rev1, (char *)n->data);
2804                 rev1_dev = dev_long;
2805                 if (strcmp (ftype, "character") == 0)
2806                     rev1_mode |= S_IFCHR;
2807                 else if (strcmp (ftype, "block") == 0)
2808                     rev1_mode |= S_IFBLK;
2809                 else
2810                     error (0, 0, "%s:%s unknown file type `%s'",
2811                            finfo->file, rev1, ftype);
2812             }
2813
2814             rev1_hardlinks = vp->hardlinks;
2815             if (rev1_hardlinks == NULL)
2816                 rev1_hardlinks = getlist();
2817         }
2818     }
2819
2820     /* Obtain file information for REV2. */
2821     if (rev2 == NULL)
2822     {
2823         if (islink (finfo->file))
2824             rev2_symlink = xreadlink (finfo->file);
2825         else
2826         {
2827 # ifdef HAVE_STRUCT_STAT_ST_RDEV
2828             if (CVS_LSTAT (finfo->file, &sb) < 0)
2829                 error (1, errno, "could not get file information for %s",
2830                        finfo->file);
2831             rev2_uid = sb.st_uid;
2832             rev2_gid = sb.st_gid;
2833             rev2_mode = sb.st_mode;
2834             if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
2835                 rev2_dev = sb.st_rdev;
2836 # else
2837             error (1, 0, "cannot handle device files on this system (%s)",
2838                    finfo->file);
2839 # endif
2840         }
2841         rev2_hardlinks = list_linked_files_on_disk (finfo->file);
2842     }
2843     else
2844     {
2845         n = findnode (finfo->rcs->versions, rev2);
2846         vp = n->data;
2847
2848         n = findnode (vp->other_delta, "symlink");
2849         if (n != NULL)
2850             rev2_symlink = xstrdup (n->data);
2851         else
2852         {
2853             n = findnode (vp->other_delta, "owner");
2854             if (n == NULL)
2855                 check_uids = 0; /* don't care */
2856             else
2857                 rev2_uid = strtoul (n->data, NULL, 10);
2858
2859             n = findnode (vp->other_delta, "group");
2860             if (n == NULL)
2861                 check_gids = 0; /* don't care */
2862             else
2863                 rev2_gid = strtoul (n->data, NULL, 10);
2864
2865             n = findnode (vp->other_delta, "permissions");
2866             if (n == NULL)
2867                 check_modes = 0;        /* don't care */
2868             else
2869                 rev2_mode = strtoul (n->data, NULL, 8);
2870
2871             n = findnode (vp->other_delta, "special");
2872             if (n == NULL)
2873                 rev2_mode |= S_IFREG;
2874             else
2875             {
2876                 /* If the size of `ftype' changes, fix the sscanf call also */
2877                 char ftype[16];
2878                 if (sscanf (n->data, "%15s %lu", ftype,
2879                             &dev_long) < 2)
2880                     error (1, 0, "%s:%s has bad `special' newphrase %s",
2881                            finfo->file, rev2, (char *)n->data);
2882                 rev2_dev = dev_long;
2883                 if (strcmp (ftype, "character") == 0)
2884                     rev2_mode |= S_IFCHR;
2885                 else if (strcmp (ftype, "block") == 0)
2886                     rev2_mode |= S_IFBLK;
2887                 else
2888                     error (0, 0, "%s:%s unknown file type `%s'",
2889                            finfo->file, rev2, ftype);
2890             }
2891
2892             rev2_hardlinks = vp->hardlinks;
2893             if (rev2_hardlinks == NULL)
2894                 rev2_hardlinks = getlist();
2895         }
2896     }
2897
2898     /* Check the user/group ownerships and file permissions, printing
2899        an error for each mismatch found.  Return 0 if all characteristics
2900        matched, and 1 otherwise. */
2901
2902     result = 0;
2903
2904     /* Compare symlinks first, since symlinks are simpler (don't have
2905        any other characteristics). */
2906     if (rev1_symlink != NULL && rev2_symlink == NULL)
2907     {
2908         error (0, 0, "%s is a symbolic link",
2909                (rev1 == NULL ? "working file" : rev1));
2910         result = 1;
2911     }
2912     else if (rev1_symlink == NULL && rev2_symlink != NULL)
2913     {
2914         error (0, 0, "%s is a symbolic link",
2915                (rev2 == NULL ? "working file" : rev2));
2916         result = 1;
2917     }
2918     else if (rev1_symlink != NULL)
2919         result = (strcmp (rev1_symlink, rev2_symlink) == 0);
2920     else
2921     {
2922         /* Compare user ownership. */
2923         if (check_uids && rev1_uid != rev2_uid)
2924         {
2925             error (0, 0, "%s: owner mismatch between %s and %s",
2926                    finfo->file,
2927                    (rev1 == NULL ? "working file" : rev1),
2928                    (rev2 == NULL ? "working file" : rev2));
2929             result = 1;
2930         }
2931
2932         /* Compare group ownership. */
2933         if (check_gids && rev1_gid != rev2_gid)
2934         {
2935             error (0, 0, "%s: group mismatch between %s and %s",
2936                    finfo->file,
2937                    (rev1 == NULL ? "working file" : rev1),
2938                    (rev2 == NULL ? "working file" : rev2));
2939             result = 1;
2940         }
2941     
2942         /* Compare permissions. */
2943         if (check_modes &&
2944             (rev1_mode & 07777) != (rev2_mode & 07777))
2945         {
2946             error (0, 0, "%s: permission mismatch between %s and %s",
2947                    finfo->file,
2948                    (rev1 == NULL ? "working file" : rev1),
2949                    (rev2 == NULL ? "working file" : rev2));
2950             result = 1;
2951         }
2952
2953         /* Compare device file characteristics. */
2954         if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
2955         {
2956             error (0, 0, "%s: %s and %s are different file types",
2957                    finfo->file,
2958                    (rev1 == NULL ? "working file" : rev1),
2959                    (rev2 == NULL ? "working file" : rev2));
2960             result = 1;
2961         }
2962         else if (S_ISBLK (rev1_mode))
2963         {
2964             if (rev1_dev != rev2_dev)
2965             {
2966                 error (0, 0, "%s: device numbers of %s and %s do not match",
2967                        finfo->file,
2968                        (rev1 == NULL ? "working file" : rev1),
2969                        (rev2 == NULL ? "working file" : rev2));
2970                 result = 1;
2971             }
2972         }
2973
2974         /* Compare hard links. */
2975         if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0)
2976         {
2977             error (0, 0, "%s: hard linkage of %s and %s do not match",
2978                    finfo->file,
2979                    (rev1 == NULL ? "working file" : rev1),
2980                    (rev2 == NULL ? "working file" : rev2));
2981             result = 1;
2982         }
2983     }
2984
2985     if (rev1_symlink != NULL)
2986         free (rev1_symlink);
2987     if (rev2_symlink != NULL)
2988         free (rev2_symlink);
2989     if (rev1_hardlinks != NULL)
2990         dellist (&rev1_hardlinks);
2991     if (rev2_hardlinks != NULL)
2992         dellist (&rev2_hardlinks);
2993
2994     return result;
2995 #else
2996     return 0;
2997 #endif
2998 }
2999
3000
3001
3002 int
3003 joining ()
3004 {
3005     return join_rev1 != NULL;
3006 }