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