Bug Summary

File:src/gnu/usr.bin/cvs/src/update.c
Warning:line 2021, column 2
Value stored to 'status' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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