File: | src/gnu/usr.bin/cvs/src/update.c |
Warning: | line 2021, column 2 Value stored to 'status' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
48 | static 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 |
51 | static void checkout_to_buffer PROTO ((void *, const char *, size_t))(void *, const char *, size_t); |
52 | #endif |
53 | #ifdef SERVER_SUPPORT1 |
54 | static 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); |
58 | static void patch_file_write PROTO ((void *, const char *, size_t))(void *, const char *, size_t); |
59 | #endif |
60 | static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers))(struct file_info *finfo, Vers_TS *vers); |
61 | static int scratch_file PROTO((struct file_info *finfo, Vers_TS *vers))(struct file_info *finfo, Vers_TS *vers); |
62 | static 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); |
65 | static 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); |
68 | static int update_fileproc PROTO ((void *callerdat, struct file_info *))(void *callerdat, struct file_info *); |
69 | static 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 |
73 | static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *))(void *callerdat, struct file_info *); |
74 | #endif |
75 | static void write_letter PROTO ((struct file_info *finfo, int letter))(struct file_info *finfo, int letter); |
76 | static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts))(struct file_info *finfo, Vers_TS *vers_ts); |
77 | |
78 | static char *options = NULL((void*)0); |
79 | static char *tag = NULL((void*)0); |
80 | static 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. */ |
87 | static int rewrite_tag; |
88 | static 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. */ |
92 | static char *tag_update_dir; |
93 | |
94 | static char *join_rev1, *date_rev1; |
95 | static char *join_rev2, *date_rev2; |
96 | static int aflag = 0; |
97 | static int toss_local_changes = 0; |
98 | static int force_tag_match = 1; |
99 | static int update_build_dirs = 0; |
100 | static int update_prune_dirs = 0; |
101 | static int pipeout = 0; |
102 | static int dotemplate = 0; |
103 | #ifdef SERVER_SUPPORT1 |
104 | static int patches = 0; |
105 | static int rcs_diff_patches = 0; |
106 | #endif |
107 | static List *ignlist = (List *) NULL((void*)0); |
108 | static time_t last_register_time; |
109 | static 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 | */ |
135 | int |
136 | update (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 | */ |
431 | int |
432 | do_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 | |
536 | static int |
537 | get_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 | */ |
584 | static int |
585 | update_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 | |
817 | static void update_ignproc PROTO ((char *, char *))(char *, char *); |
818 | |
819 | static void |
820 | update_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 */ |
844 | static int |
845 | update_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 | */ |
896 | static Dtype |
897 | update_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 */ |
1051 | static int |
1052 | update_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 | |
1144 | static int isremoved PROTO ((Node *, void *))(Node *, void *); |
1145 | |
1146 | /* Returns 1 if the file indicated by node has been removed. */ |
1147 | static int |
1148 | isremoved (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. */ |
1162 | int |
1163 | isemptydir (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 | */ |
1238 | static int |
1239 | scratch_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 | */ |
1285 | static int |
1286 | checkout_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\ |
1346 | Checking out ", 0); |
1347 | cvs_outerr (finfo->fullname, 0); |
1348 | cvs_outerr ("\n\ |
1349 | RCS: ", 0); |
1350 | cvs_outerr (vers_ts->srcfile->path, 0); |
1351 | cvs_outerr ("\n\ |
1352 | VERS: ", 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 | |
1570 | static void |
1571 | checkout_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 | |
1588 | struct 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 | */ |
1606 | static int |
1607 | patch_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 | |
1901 | static void |
1902 | patch_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 | */ |
1924 | static void |
1925 | write_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 | */ |
1968 | static int |
1969 | merge_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 | */ |
2142 | static void |
2143 | join_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 | |
2651 | int |
2652 | special_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 | |
2949 | int |
2950 | joining () |
2951 | { |
2952 | return (join_rev1 != NULL((void*)0)); |
2953 | } |