File: | src/gnu/usr.bin/cvs/src/diff.c |
Warning: | line 432, column 3 Value stored to 'exists' 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 | * Difference |
9 | * |
10 | * Run diff against versions in the repository. Options that are specified are |
11 | * passed on directly to "rcsdiff". |
12 | * |
13 | * Without any file arguments, runs diff against all the currently modified |
14 | * files. |
15 | */ |
16 | |
17 | #include "cvs.h" |
18 | |
19 | enum diff_file |
20 | { |
21 | DIFF_ERROR, |
22 | DIFF_ADDED, |
23 | DIFF_REMOVED, |
24 | DIFF_DIFFERENT, |
25 | DIFF_SAME |
26 | }; |
27 | |
28 | static Dtype diff_dirproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries) |
29 | char *pos_repos, char *update_dir,(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries) |
30 | List *entries))(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries); |
31 | static int diff_filesdoneproc PROTO ((void *callerdat, int err,(void *callerdat, int err, char *repos, char *update_dir, List *entries) |
32 | char *repos, char *update_dir,(void *callerdat, int err, char *repos, char *update_dir, List *entries) |
33 | List *entries))(void *callerdat, int err, char *repos, char *update_dir, List *entries); |
34 | static int diff_dirleaveproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, int err, char *update_dir, List * entries) |
35 | int err, char *update_dir,(void *callerdat, char *dir, int err, char *update_dir, List * entries) |
36 | List *entries))(void *callerdat, char *dir, int err, char *update_dir, List * entries); |
37 | static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo,(struct file_info *finfo, Vers_TS *vers, enum diff_file) |
38 | Vers_TS *vers,(struct file_info *finfo, Vers_TS *vers, enum diff_file) |
39 | enum diff_file))(struct file_info *finfo, Vers_TS *vers, enum diff_file); |
40 | static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); |
41 | static void diff_mark_errors PROTO((int err))(int err); |
42 | |
43 | |
44 | /* Global variables. Would be cleaner if we just put this stuff in a |
45 | struct like log.c does. */ |
46 | |
47 | /* Command line tags, from -r option. Points into argv. */ |
48 | static char *diff_rev1, *diff_rev2; |
49 | /* Command line dates, from -D option. Malloc'd. */ |
50 | static char *diff_date1, *diff_date2; |
51 | static char *use_rev1, *use_rev2; |
52 | static int have_rev1_label, have_rev2_label; |
53 | |
54 | /* Revision of the user file, if it is unchanged from something in the |
55 | repository and we want to use that fact. */ |
56 | static char *user_file_rev; |
57 | |
58 | static char *options; |
59 | static char *opts; |
60 | static size_t opts_allocated = 1; |
61 | static int diff_errors; |
62 | static int empty_files = 0; |
63 | |
64 | /* FIXME: should be documenting all the options here. They don't |
65 | perfectly match rcsdiff options (for example, we always support |
66 | --ifdef and --context, but rcsdiff only does if diff does). */ |
67 | static const char *const diff_usage[] = |
68 | { |
69 | "Usage: %s %s [-lNR] [rcsdiff-options]\n", |
70 | " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n", |
71 | "\t-l\tLocal directory only, not recursive\n", |
72 | "\t-R\tProcess directories recursively.\n", |
73 | "\t-D d1\tDiff revision for date against working file.\n", |
74 | "\t-D d2\tDiff rev1/date1 against date2.\n", |
75 | "\t-N\tinclude diffs for added and removed files.\n", |
76 | "\t-r rev1\tDiff revision for rev1 against working file.\n", |
77 | "\t-r rev2\tDiff rev1/date1 against rev2.\n", |
78 | "\t--ifdef=arg\tOutput diffs in ifdef format.\n", |
79 | "(consult the documentation for your diff program for rcsdiff-options.\n", |
80 | "The most popular is -c for context diffs but there are many more).\n", |
81 | "(Specify the --help global option for a list of other help options)\n", |
82 | NULL((void*)0) |
83 | }; |
84 | |
85 | /* I copied this array directly out of diff.c in diffutils 2.7, after |
86 | removing the following entries, none of which seem relevant to use |
87 | with CVS: |
88 | --help |
89 | --version |
90 | --recursive |
91 | --unidirectional-new-file |
92 | --starting-file |
93 | --exclude |
94 | --exclude-from |
95 | --sdiff-merge-assist |
96 | |
97 | I changed the options which take optional arguments (--context and |
98 | --unified) to return a number rather than a letter, so that the |
99 | optional argument could be handled more easily. I changed the |
100 | --paginate and --brief options to return a number, since -l and -q |
101 | mean something else to cvs diff. |
102 | |
103 | The numbers 129- that appear in the fourth element of some entries |
104 | tell the big switch in `diff' how to process those options. -- Ian |
105 | |
106 | The following options, which diff lists as "An alias, no longer |
107 | recommended" have been removed: --file-label --entire-new-file |
108 | --ascii --print. */ |
109 | |
110 | static struct option const longopts[] = |
111 | { |
112 | {"ignore-blank-lines", 0, 0, 'B'}, |
113 | {"context", 2, 0, 143}, |
114 | {"ifdef", 1, 0, 131}, |
115 | {"show-function-line", 1, 0, 'F'}, |
116 | {"speed-large-files", 0, 0, 'H'}, |
117 | {"ignore-matching-lines", 1, 0, 'I'}, |
118 | {"label", 1, 0, 'L'}, |
119 | {"new-file", 0, 0, 'N'}, |
120 | {"initial-tab", 0, 0, 'T'}, |
121 | {"width", 1, 0, 'W'}, |
122 | {"text", 0, 0, 'a'}, |
123 | {"ignore-space-change", 0, 0, 'b'}, |
124 | {"minimal", 0, 0, 'd'}, |
125 | {"ed", 0, 0, 'e'}, |
126 | {"forward-ed", 0, 0, 'f'}, |
127 | {"ignore-case", 0, 0, 'i'}, |
128 | {"paginate", 0, 0, 144}, |
129 | {"rcs", 0, 0, 'n'}, |
130 | {"show-c-function", 0, 0, 'p'}, |
131 | |
132 | /* This is a potentially very useful option, except the output is so |
133 | silly. It would be much better for it to look like "cvs rdiff -s" |
134 | which displays all the same info, minus quite a few lines of |
135 | extraneous garbage. */ |
136 | {"brief", 0, 0, 145}, |
137 | |
138 | {"report-identical-files", 0, 0, 's'}, |
139 | {"expand-tabs", 0, 0, 't'}, |
140 | {"ignore-all-space", 0, 0, 'w'}, |
141 | {"side-by-side", 0, 0, 'y'}, |
142 | {"unified", 2, 0, 146}, |
143 | {"left-column", 0, 0, 129}, |
144 | {"suppress-common-lines", 0, 0, 130}, |
145 | {"old-line-format", 1, 0, 132}, |
146 | {"new-line-format", 1, 0, 133}, |
147 | {"unchanged-line-format", 1, 0, 134}, |
148 | {"line-format", 1, 0, 135}, |
149 | {"old-group-format", 1, 0, 136}, |
150 | {"new-group-format", 1, 0, 137}, |
151 | {"unchanged-group-format", 1, 0, 138}, |
152 | {"changed-group-format", 1, 0, 139}, |
153 | {"horizon-lines", 1, 0, 140}, |
154 | {"binary", 0, 0, 142}, |
155 | {0, 0, 0, 0} |
156 | }; |
157 | |
158 | /* CVS 1.9 and similar versions seemed to have pretty weird handling |
159 | of -y and -T. In the cases where it called rcsdiff, |
160 | they would have the meanings mentioned below. In the cases where it |
161 | called diff, they would have the meanings mentioned in "longopts". |
162 | Noone seems to have missed them, so I think the right thing to do is |
163 | just to remove the options altogether (which I have done). |
164 | |
165 | In the case of -z and -q, "cvs diff" did not accept them even back |
166 | when we called rcsdiff (at least, it hasn't accepted them |
167 | recently). |
168 | |
169 | In comparing rcsdiff to the new CVS implementation, I noticed that |
170 | the following rcsdiff flags are not handled by CVS diff: |
171 | |
172 | -y: perform diff even when the requested revisions are the |
173 | same revision number |
174 | -q: run quietly |
175 | -T: preserve modification time on the RCS file |
176 | -z: specify timezone for use in file labels |
177 | |
178 | I think these are not really relevant. -y is undocumented even in |
179 | RCS 5.7, and seems like a minor change at best. According to RCS |
180 | documentation, -T only applies when a RCS file has been modified |
181 | because of lock changes; doesn't CVS sidestep RCS's entire lock |
182 | structure? -z seems to be unsupported by CVS diff, and has a |
183 | different meaning as a global option anyway. (Adding it could be |
184 | a feature, but if it is left out for now, it should not break |
185 | anything.) For the purposes of producing output, CVS diff appears |
186 | mostly to ignore -q. Maybe this should be fixed, but I think it's |
187 | a larger issue than the changes included here. */ |
188 | |
189 | int |
190 | diff (argc, argv) |
191 | int argc; |
192 | char **argv; |
193 | { |
194 | char tmp[50]; |
195 | int c, err = 0; |
196 | int local = 0; |
197 | int which; |
198 | int option_index; |
199 | |
200 | if (argc == -1) |
201 | usage (diff_usage); |
202 | |
203 | have_rev1_label = have_rev2_label = 0; |
204 | |
205 | /* |
206 | * Note that we catch all the valid arguments here, so that we can |
207 | * intercept the -r arguments for doing revision diffs; and -l/-R for a |
208 | * non-recursive/recursive diff. |
209 | */ |
210 | |
211 | /* Clean out our global variables (multiroot can call us multiple |
212 | times and the server can too, if the client sends several |
213 | diff commands). */ |
214 | if (opts == NULL((void*)0)) |
215 | { |
216 | opts_allocated = 1; |
217 | opts = xmalloc (opts_allocated); |
218 | } |
219 | opts[0] = '\0'; |
220 | diff_rev1 = NULL((void*)0); |
221 | diff_rev2 = NULL((void*)0); |
222 | diff_date1 = NULL((void*)0); |
223 | diff_date2 = NULL((void*)0); |
224 | |
225 | optind = 0; |
226 | while ((c = getopt_long (argc, argv, |
227 | "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", |
228 | longopts, &option_index)) != -1) |
229 | { |
230 | switch (c) |
231 | { |
232 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
233 | case 'h': case 'i': case 'n': case 'p': case 's': case 't': |
234 | case 'u': case 'w': case 'y': |
235 | case '0': case '1': case '2': case '3': case '4': case '5': |
236 | case '6': case '7': case '8': case '9': |
237 | case 'B': case 'H': case 'T': |
238 | (void) sprintf (tmp, " -%c", (char) c); |
239 | allocate_and_strcat (&opts, &opts_allocated, tmp); |
240 | break; |
241 | case 'L': |
242 | if (have_rev1_label++) |
243 | if (have_rev2_label++) |
244 | { |
245 | error (0, 0, "extra -L arguments ignored"); |
246 | break; |
247 | } |
248 | |
249 | allocate_and_strcat (&opts, &opts_allocated, " -L"); |
250 | allocate_and_strcat (&opts, &opts_allocated, optarg); |
251 | break; |
252 | case 'C': case 'F': case 'I': case 'U': case 'V': case 'W': |
253 | (void) sprintf (tmp, " -%c", (char) c); |
254 | allocate_and_strcat (&opts, &opts_allocated, tmp); |
255 | allocate_and_strcat (&opts, &opts_allocated, optarg); |
256 | break; |
257 | case 131: |
258 | /* --ifdef. */ |
259 | allocate_and_strcat (&opts, &opts_allocated, " --ifdef="); |
260 | allocate_and_strcat (&opts, &opts_allocated, optarg); |
261 | break; |
262 | case 129: case 130: case 132: case 133: case 134: |
263 | case 135: case 136: case 137: case 138: case 139: case 140: |
264 | case 141: case 142: case 143: case 144: case 145: case 146: |
265 | allocate_and_strcat (&opts, &opts_allocated, " --"); |
266 | allocate_and_strcat (&opts, &opts_allocated, |
267 | longopts[option_index].name); |
268 | if (longopts[option_index].has_arg == 1 |
269 | || (longopts[option_index].has_arg == 2 |
270 | && optarg != NULL((void*)0))) |
271 | { |
272 | allocate_and_strcat (&opts, &opts_allocated, "="); |
273 | allocate_and_strcat (&opts, &opts_allocated, optarg); |
274 | } |
275 | break; |
276 | case 'R': |
277 | local = 0; |
278 | break; |
279 | case 'l': |
280 | local = 1; |
281 | break; |
282 | case 'k': |
283 | if (options) |
284 | free (options); |
285 | options = RCS_check_kflag (optarg); |
286 | break; |
287 | case 'r': |
288 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) |
289 | error (1, 0, |
290 | "no more than two revisions/dates can be specified"); |
291 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) |
292 | diff_rev2 = optarg; |
293 | else |
294 | diff_rev1 = optarg; |
295 | break; |
296 | case 'D': |
297 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) |
298 | error (1, 0, |
299 | "no more than two revisions/dates can be specified"); |
300 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) |
301 | diff_date2 = Make_Date (optarg); |
302 | else |
303 | diff_date1 = Make_Date (optarg); |
304 | break; |
305 | case 'N': |
306 | empty_files = 1; |
307 | break; |
308 | case '?': |
309 | default: |
310 | usage (diff_usage); |
311 | break; |
312 | } |
313 | } |
314 | argc -= optind; |
315 | argv += optind; |
316 | |
317 | /* make sure options is non-null */ |
318 | if (!options) |
319 | options = xstrdup (""); |
320 | |
321 | #ifdef CLIENT_SUPPORT1 |
322 | if (current_parsed_root->isremote) { |
323 | /* We're the client side. Fire up the remote server. */ |
324 | start_server (); |
325 | |
326 | ign_setup (); |
327 | |
328 | if (local) |
329 | send_arg("-l"); |
330 | if (empty_files) |
331 | send_arg("-N"); |
332 | send_option_string (opts); |
333 | if (options[0] != '\0') |
334 | send_arg (options); |
335 | if (diff_rev1) |
336 | option_with_arg ("-r", diff_rev1); |
337 | if (diff_date1) |
338 | client_senddate (diff_date1); |
339 | if (diff_rev2) |
340 | option_with_arg ("-r", diff_rev2); |
341 | if (diff_date2) |
342 | client_senddate (diff_date2); |
343 | |
344 | /* Send the current files unless diffing two revs from the archive */ |
345 | if (diff_rev2 == NULL((void*)0) && diff_date2 == NULL((void*)0)) |
346 | send_files (argc, argv, local, 0, 0); |
347 | else |
348 | send_files (argc, argv, local, 0, SEND_NO_CONTENTS4); |
349 | |
350 | send_file_names (argc, argv, SEND_EXPAND_WILD1); |
351 | |
352 | send_to_server ("diff\012", 0); |
353 | err = get_responses_and_close (); |
354 | free (options); |
355 | options = NULL((void*)0); |
356 | return (err); |
357 | } |
358 | #endif |
359 | |
360 | if (diff_rev1 != NULL((void*)0)) |
361 | tag_check_valid (diff_rev1, argc, argv, local, 0, ""); |
362 | if (diff_rev2 != NULL((void*)0)) |
363 | tag_check_valid (diff_rev2, argc, argv, local, 0, ""); |
364 | |
365 | which = W_LOCAL0x01; |
366 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) |
367 | which |= W_REPOS0x02 | W_ATTIC0x04; |
368 | |
369 | wrap_setup (); |
370 | |
371 | /* start the recursion processor */ |
372 | err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc, |
373 | diff_dirleaveproc, NULL((void*)0), argc, argv, local, |
374 | which, 0, 1, (char *) NULL((void*)0), 1); |
375 | |
376 | /* clean up */ |
377 | free (options); |
378 | options = NULL((void*)0); |
379 | |
380 | if (diff_date1 != NULL((void*)0)) |
381 | free (diff_date1); |
382 | if (diff_date2 != NULL((void*)0)) |
383 | free (diff_date2); |
384 | |
385 | return (err); |
386 | } |
387 | |
388 | /* |
389 | * Do a file diff |
390 | */ |
391 | /* ARGSUSED */ |
392 | static int |
393 | diff_fileproc (callerdat, finfo) |
394 | void *callerdat; |
395 | struct file_info *finfo; |
396 | { |
397 | int status, err = 2; /* 2 == trouble, like rcsdiff */ |
398 | Vers_TS *vers; |
399 | enum diff_file empty_file = DIFF_DIFFERENT; |
400 | char *tmp; |
401 | char *tocvsPath; |
402 | char *fname; |
403 | char *label1; |
404 | char *label2; |
405 | |
406 | /* Initialize these solely to avoid warnings from gcc -Wall about |
407 | variables that might be used uninitialized. */ |
408 | tmp = NULL((void*)0); |
409 | fname = NULL((void*)0); |
410 | |
411 | user_file_rev = 0; |
412 | vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 1, 0); |
413 | |
414 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) |
415 | { |
416 | /* Skip all the following checks regarding the user file; we're |
417 | not using it. */ |
418 | } |
419 | else if (vers->vn_user == NULL((void*)0)) |
420 | { |
421 | /* The file does not exist in the working directory. */ |
422 | if ((diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) |
423 | && vers->srcfile != NULL((void*)0)) |
424 | { |
425 | /* The file does exist in the repository. */ |
426 | if (empty_files) |
427 | empty_file = DIFF_REMOVED; |
428 | else |
429 | { |
430 | int exists; |
431 | |
432 | exists = 0; |
Value stored to 'exists' is never read | |
433 | /* special handling for TAG_HEAD */ |
434 | if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD"HEAD") == 0) |
435 | { |
436 | char *head = |
437 | (vers->vn_rcs == NULL((void*)0) |
438 | ? NULL((void*)0) |
439 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); |
440 | exists = head != NULL((void*)0); |
441 | if (head != NULL((void*)0)) |
442 | free (head); |
443 | } |
444 | else |
445 | { |
446 | Vers_TS *xvers; |
447 | |
448 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev1, diff_date1, |
449 | 1, 0); |
450 | exists = xvers->vn_rcs != NULL((void*)0); |
451 | freevers_ts (&xvers); |
452 | } |
453 | if (exists) |
454 | error (0, 0, |
455 | "%s no longer exists, no comparison available", |
456 | finfo->fullname); |
457 | freevers_ts (&vers); |
458 | diff_mark_errors (err); |
459 | return (err); |
460 | } |
461 | } |
462 | else |
463 | { |
464 | error (0, 0, "I know nothing about %s", finfo->fullname); |
465 | freevers_ts (&vers); |
466 | diff_mark_errors (err); |
467 | return (err); |
468 | } |
469 | } |
470 | else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') |
471 | { |
472 | if (empty_files) |
473 | empty_file = DIFF_ADDED; |
474 | else |
475 | { |
476 | error (0, 0, "%s is a new entry, no comparison available", |
477 | finfo->fullname); |
478 | freevers_ts (&vers); |
479 | diff_mark_errors (err); |
480 | return (err); |
481 | } |
482 | } |
483 | else if (vers->vn_user[0] == '-') |
484 | { |
485 | if (empty_files) |
486 | empty_file = DIFF_REMOVED; |
487 | else |
488 | { |
489 | error (0, 0, "%s was removed, no comparison available", |
490 | finfo->fullname); |
491 | freevers_ts (&vers); |
492 | diff_mark_errors (err); |
493 | return (err); |
494 | } |
495 | } |
496 | else |
497 | { |
498 | if (vers->vn_rcs == NULL((void*)0) && vers->srcfile == NULL((void*)0)) |
499 | { |
500 | error (0, 0, "cannot find revision control file for %s", |
501 | finfo->fullname); |
502 | freevers_ts (&vers); |
503 | diff_mark_errors (err); |
504 | return (err); |
505 | } |
506 | else |
507 | { |
508 | if (vers->ts_user == NULL((void*)0)) |
509 | { |
510 | error (0, 0, "cannot find %s", finfo->fullname); |
511 | freevers_ts (&vers); |
512 | diff_mark_errors (err); |
513 | return (err); |
514 | } |
515 | else if (!strcmp (vers->ts_user, vers->ts_rcs)) |
516 | { |
517 | /* The user file matches some revision in the repository |
518 | Diff against the repository (for remote CVS, we might not |
519 | have a copy of the user file around). */ |
520 | user_file_rev = vers->vn_user; |
521 | } |
522 | } |
523 | } |
524 | |
525 | empty_file = diff_file_nodiff (finfo, vers, empty_file); |
526 | if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR) |
527 | { |
528 | freevers_ts (&vers); |
529 | if (empty_file == DIFF_SAME) |
530 | { |
531 | /* In the server case, would be nice to send a "Checked-in" |
532 | response, so that the client can rewrite its timestamp. |
533 | server_checked_in by itself isn't the right thing (it |
534 | needs a server_register), but I'm not sure what is. |
535 | It isn't clear to me how "cvs status" handles this (that |
536 | is, for a client which sends Modified not Is-modified to |
537 | "cvs status"), but it does. */ |
538 | return (0); |
539 | } |
540 | else |
541 | { |
542 | diff_mark_errors (err); |
543 | return (err); |
544 | } |
545 | } |
546 | |
547 | if (empty_file == DIFF_DIFFERENT) |
548 | { |
549 | int dead1, dead2; |
550 | |
551 | if (use_rev1 == NULL((void*)0)) |
552 | dead1 = 0; |
553 | else |
554 | dead1 = RCS_isdead (vers->srcfile, use_rev1); |
555 | if (use_rev2 == NULL((void*)0)) |
556 | dead2 = 0; |
557 | else |
558 | dead2 = RCS_isdead (vers->srcfile, use_rev2); |
559 | |
560 | if (dead1 && dead2) |
561 | { |
562 | freevers_ts (&vers); |
563 | return (0); |
564 | } |
565 | else if (dead1) |
566 | { |
567 | if (empty_files) |
568 | empty_file = DIFF_ADDED; |
569 | else |
570 | { |
571 | error (0, 0, "%s is a new entry, no comparison available", |
572 | finfo->fullname); |
573 | freevers_ts (&vers); |
574 | diff_mark_errors (err); |
575 | return (err); |
576 | } |
577 | } |
578 | else if (dead2) |
579 | { |
580 | if (empty_files) |
581 | empty_file = DIFF_REMOVED; |
582 | else |
583 | { |
584 | error (0, 0, "%s was removed, no comparison available", |
585 | finfo->fullname); |
586 | freevers_ts (&vers); |
587 | diff_mark_errors (err); |
588 | return (err); |
589 | } |
590 | } |
591 | } |
592 | |
593 | /* Output an "Index:" line for patch to use */ |
594 | cvs_output ("Index: ", 0); |
595 | cvs_output (finfo->fullname, 0); |
596 | cvs_output ("\n", 1); |
597 | |
598 | tocvsPath = wrap_tocvs_process_file(finfo->file); |
599 | if (tocvsPath) |
600 | { |
601 | /* Backup the current version of the file to CVS/,,filename */ |
602 | fname = xmalloc (strlen (finfo->file) |
603 | + sizeof CVSADM"CVS" |
604 | + sizeof CVSPREFIX",," |
605 | + 10); |
606 | sprintf(fname,"%s/%s%s",CVSADM"CVS", CVSPREFIX",,", finfo->file); |
607 | if (unlink_file_dir (fname) < 0) |
608 | if (! existence_error (errno)(((*__errno())) == 2)) |
609 | error (1, errno(*__errno()), "cannot remove %s", fname); |
610 | rename_file (finfo->file, fname); |
611 | /* Copy the wrapped file to the current directory then go to work */ |
612 | copy_file (tocvsPath, finfo->file); |
613 | } |
614 | |
615 | /* Set up file labels appropriate for compatibility with the Larry Wall |
616 | * implementation of patch if the user didn't specify. This is irrelevant |
617 | * according to the POSIX.2 specification. |
618 | */ |
619 | label1 = NULL((void*)0); |
620 | label2 = NULL((void*)0); |
621 | if (!have_rev1_label) |
622 | { |
623 | if (empty_file == DIFF_ADDED) |
624 | label1 = |
625 | make_file_label (DEVNULL"/dev/null", NULL((void*)0), NULL((void*)0)); |
626 | else |
627 | label1 = |
628 | make_file_label (finfo->fullname, use_rev1, vers ? vers->srcfile : NULL((void*)0)); |
629 | } |
630 | |
631 | if (!have_rev2_label) |
632 | { |
633 | if (empty_file == DIFF_REMOVED) |
634 | label2 = |
635 | make_file_label (DEVNULL"/dev/null", NULL((void*)0), NULL((void*)0)); |
636 | else |
637 | label2 = |
638 | make_file_label (finfo->fullname, use_rev2, vers ? vers->srcfile : NULL((void*)0)); |
639 | } |
640 | |
641 | if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) |
642 | { |
643 | /* This is fullname, not file, possibly despite the POSIX.2 |
644 | * specification, because that's the way all the Larry Wall |
645 | * implementations of patch (are there other implementations?) want |
646 | * things and the POSIX.2 spec appears to leave room for this. |
647 | */ |
648 | cvs_output ("\ |
649 | ===================================================================\n\ |
650 | RCS file: ", 0); |
651 | cvs_output (finfo->fullname, 0); |
652 | cvs_output ("\n", 1); |
653 | |
654 | cvs_output ("diff -N ", 0); |
655 | cvs_output (finfo->fullname, 0); |
656 | cvs_output ("\n", 1); |
657 | |
658 | if (empty_file == DIFF_ADDED) |
659 | { |
660 | if (use_rev2 == NULL((void*)0)) |
661 | status = diff_exec (DEVNULL"/dev/null", finfo->file, label1, label2, opts, RUN_TTY(char *)0); |
662 | else |
663 | { |
664 | int retcode; |
665 | |
666 | tmp = cvs_temp_name (); |
667 | retcode = RCS_checkout (vers->srcfile, (char *) NULL((void*)0), |
668 | use_rev2, (char *) NULL((void*)0), |
669 | (*options |
670 | ? options |
671 | : vers->options), |
672 | tmp, (RCSCHECKOUTPROC) NULL((void*)0), |
673 | (void *) NULL((void*)0)); |
674 | if (retcode != 0) |
675 | { |
676 | diff_mark_errors (err); |
677 | return err; |
678 | } |
679 | |
680 | status = diff_exec (DEVNULL"/dev/null", tmp, label1, label2, opts, RUN_TTY(char *)0); |
681 | } |
682 | } |
683 | else |
684 | { |
685 | int retcode; |
686 | |
687 | tmp = cvs_temp_name (); |
688 | retcode = RCS_checkout (vers->srcfile, (char *) NULL((void*)0), |
689 | use_rev1, (char *) NULL((void*)0), |
690 | *options ? options : vers->options, |
691 | tmp, (RCSCHECKOUTPROC) NULL((void*)0), |
692 | (void *) NULL((void*)0)); |
693 | if (retcode != 0) |
694 | { |
695 | diff_mark_errors (err); |
696 | return err; |
697 | } |
698 | |
699 | status = diff_exec (tmp, DEVNULL"/dev/null", label1, label2, opts, RUN_TTY(char *)0); |
700 | } |
701 | } |
702 | else |
703 | { |
704 | status = RCS_exec_rcsdiff (vers->srcfile, opts, |
705 | *options ? options : vers->options, |
706 | use_rev1, use_rev2, |
707 | label1, label2, |
708 | finfo->file); |
709 | |
710 | if (label1) free (label1); |
711 | if (label2) free (label2); |
712 | } |
713 | |
714 | switch (status) |
715 | { |
716 | case -1: /* fork failed */ |
717 | error (1, errno(*__errno()), "fork failed while diffing %s", |
718 | vers->srcfile->path); |
719 | case 0: /* everything ok */ |
720 | err = 0; |
721 | break; |
722 | default: /* other error */ |
723 | err = status; |
724 | break; |
725 | } |
726 | |
727 | if (tocvsPath) |
728 | { |
729 | if (unlink_file_dir (finfo->file) < 0) |
730 | if (! existence_error (errno)(((*__errno())) == 2)) |
731 | error (1, errno(*__errno()), "cannot remove %s", finfo->file); |
732 | |
733 | rename_file (fname, finfo->file); |
734 | if (unlink_file (tocvsPath) < 0) |
735 | error (1, errno(*__errno()), "cannot remove %s", tocvsPath); |
736 | free (fname); |
737 | } |
738 | |
739 | if (empty_file == DIFF_REMOVED |
740 | || (empty_file == DIFF_ADDED && use_rev2 != NULL((void*)0))) |
741 | { |
742 | if (CVS_UNLINKunlink (tmp) < 0) |
743 | error (0, errno(*__errno()), "cannot remove %s", tmp); |
744 | free (tmp); |
745 | } |
746 | |
747 | freevers_ts (&vers); |
748 | diff_mark_errors (err); |
749 | return (err); |
750 | } |
751 | |
752 | /* |
753 | * Remember the exit status for each file. |
754 | */ |
755 | static void |
756 | diff_mark_errors (err) |
757 | int err; |
758 | { |
759 | if (err > diff_errors) |
760 | diff_errors = err; |
761 | } |
762 | |
763 | /* |
764 | * Print a warm fuzzy message when we enter a dir |
765 | * |
766 | * Don't try to diff directories that don't exist! -- DW |
767 | */ |
768 | /* ARGSUSED */ |
769 | static Dtype |
770 | diff_dirproc (callerdat, dir, pos_repos, update_dir, entries) |
771 | void *callerdat; |
772 | char *dir; |
773 | char *pos_repos; |
774 | char *update_dir; |
775 | List *entries; |
776 | { |
777 | /* XXX - check for dirs we don't want to process??? */ |
778 | |
779 | /* YES ... for instance dirs that don't exist!!! -- DW */ |
780 | if (!isdir (dir)) |
781 | return (R_SKIP_ALL); |
782 | |
783 | if (!quiet) |
784 | error (0, 0, "Diffing %s", update_dir); |
785 | return (R_PROCESS); |
786 | } |
787 | |
788 | /* |
789 | * Concoct the proper exit status - done with files |
790 | */ |
791 | /* ARGSUSED */ |
792 | static int |
793 | diff_filesdoneproc (callerdat, err, repos, update_dir, entries) |
794 | void *callerdat; |
795 | int err; |
796 | char *repos; |
797 | char *update_dir; |
798 | List *entries; |
799 | { |
800 | return (diff_errors); |
801 | } |
802 | |
803 | /* |
804 | * Concoct the proper exit status - leaving directories |
805 | */ |
806 | /* ARGSUSED */ |
807 | static int |
808 | diff_dirleaveproc (callerdat, dir, err, update_dir, entries) |
809 | void *callerdat; |
810 | char *dir; |
811 | int err; |
812 | char *update_dir; |
813 | List *entries; |
814 | { |
815 | return (diff_errors); |
816 | } |
817 | |
818 | /* |
819 | * verify that a file is different |
820 | */ |
821 | static enum diff_file |
822 | diff_file_nodiff (finfo, vers, empty_file) |
823 | struct file_info *finfo; |
824 | Vers_TS *vers; |
825 | enum diff_file empty_file; |
826 | { |
827 | Vers_TS *xvers; |
828 | int retcode; |
829 | |
830 | /* free up any old use_rev* variables and reset 'em */ |
831 | if (use_rev1) |
832 | free (use_rev1); |
833 | if (use_rev2) |
834 | free (use_rev2); |
835 | use_rev1 = use_rev2 = (char *) NULL((void*)0); |
836 | |
837 | if (diff_rev1 || diff_date1) |
838 | { |
839 | /* special handling for TAG_HEAD */ |
840 | if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD"HEAD") == 0) |
841 | use_rev1 = ((vers->vn_rcs == NULL((void*)0) || vers->srcfile == NULL((void*)0)) |
842 | ? NULL((void*)0) |
843 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); |
844 | else |
845 | { |
846 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev1, diff_date1, 1, 0); |
847 | if (xvers->vn_rcs != NULL((void*)0)) |
848 | use_rev1 = xstrdup (xvers->vn_rcs); |
849 | freevers_ts (&xvers); |
850 | } |
851 | } |
852 | if (diff_rev2 || diff_date2) |
853 | { |
854 | /* special handling for TAG_HEAD */ |
855 | if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD"HEAD") == 0) |
856 | use_rev2 = ((vers->vn_rcs == NULL((void*)0) || vers->srcfile == NULL((void*)0)) |
857 | ? NULL((void*)0) |
858 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); |
859 | else |
860 | { |
861 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev2, diff_date2, 1, 0); |
862 | if (xvers->vn_rcs != NULL((void*)0)) |
863 | use_rev2 = xstrdup (xvers->vn_rcs); |
864 | freevers_ts (&xvers); |
865 | } |
866 | |
867 | if (use_rev1 == NULL((void*)0)) |
868 | { |
869 | /* The first revision does not exist. If EMPTY_FILES is |
870 | true, treat this as an added file. Otherwise, warn |
871 | about the missing tag. */ |
872 | if (use_rev2 == NULL((void*)0) || RCS_isdead( vers->srcfile, use_rev2 ) ) |
873 | /* At least in the case where DIFF_REV1 and DIFF_REV2 |
874 | are both numeric, we should be returning some kind |
875 | of error (see basicb-8a0 in testsuite). The symbolic |
876 | case may be more complicated. */ |
877 | return DIFF_SAME; |
878 | else if (empty_files) |
879 | return DIFF_ADDED; |
880 | else if (diff_rev1) |
881 | error (0, 0, "tag %s is not in file %s", diff_rev1, |
882 | finfo->fullname); |
883 | else |
884 | error (0, 0, "no revision for date %s in file %s", |
885 | diff_date1, finfo->fullname); |
886 | return DIFF_ERROR; |
887 | } |
888 | |
889 | if (use_rev2 == NULL((void*)0)) |
890 | { |
891 | /* The second revision does not exist. If EMPTY_FILES is |
892 | true, treat this as a removed file. Otherwise warn |
893 | about the missing tag. */ |
894 | if (empty_files) |
895 | return DIFF_REMOVED; |
896 | else if (diff_rev2) |
897 | error (0, 0, "tag %s is not in file %s", diff_rev2, |
898 | finfo->fullname); |
899 | else |
900 | error (0, 0, "no revision for date %s in file %s", |
901 | diff_date2, finfo->fullname); |
902 | return DIFF_ERROR; |
903 | } |
904 | |
905 | /* now, see if we really need to do the diff */ |
906 | if (strcmp (use_rev1, use_rev2) == 0) |
907 | return DIFF_SAME; |
908 | else |
909 | return DIFF_DIFFERENT; |
910 | } |
911 | |
912 | if ((diff_rev1 || diff_date1) && use_rev1 == NULL((void*)0)) |
913 | { |
914 | /* The first revision does not exist, and no second revision |
915 | was given. */ |
916 | if (empty_files) |
917 | { |
918 | if (empty_file == DIFF_REMOVED) |
919 | return DIFF_SAME; |
920 | else |
921 | { |
922 | if (user_file_rev && use_rev2 == NULL((void*)0)) |
923 | use_rev2 = xstrdup (user_file_rev); |
924 | return DIFF_ADDED; |
925 | } |
926 | } |
927 | else |
928 | { |
929 | if (diff_rev1) |
930 | error (0, 0, "tag %s is not in file %s", diff_rev1, |
931 | finfo->fullname); |
932 | else |
933 | error (0, 0, "no revision for date %s in file %s", |
934 | diff_date1, finfo->fullname); |
935 | return DIFF_ERROR; |
936 | } |
937 | } |
938 | |
939 | if (user_file_rev) |
940 | { |
941 | /* drop user_file_rev into first unused use_rev */ |
942 | if (!use_rev1) |
943 | use_rev1 = xstrdup (user_file_rev); |
944 | else if (!use_rev2) |
945 | use_rev2 = xstrdup (user_file_rev); |
946 | /* and if not, it wasn't needed anyhow */ |
947 | user_file_rev = 0; |
948 | } |
949 | |
950 | /* now, see if we really need to do the diff */ |
951 | if (use_rev1 && use_rev2) |
952 | { |
953 | if (strcmp (use_rev1, use_rev2) == 0) |
954 | return DIFF_SAME; |
955 | else |
956 | return DIFF_DIFFERENT; |
957 | } |
958 | |
959 | if (use_rev1 == NULL((void*)0) |
960 | || (vers->vn_user != NULL((void*)0) && strcmp (use_rev1, vers->vn_user) == 0)) |
961 | { |
962 | if (empty_file == DIFF_DIFFERENT |
963 | && vers->ts_user != NULL((void*)0) |
964 | && strcmp (vers->ts_rcs, vers->ts_user) == 0 |
965 | && (!(*options) || strcmp (options, vers->options) == 0)) |
966 | { |
967 | return DIFF_SAME; |
968 | } |
969 | if (use_rev1 == NULL((void*)0) |
970 | && (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0')) |
971 | { |
972 | if (vers->vn_user[0] == '-') |
973 | use_rev1 = xstrdup (vers->vn_user + 1); |
974 | else |
975 | use_rev1 = xstrdup (vers->vn_user); |
976 | } |
977 | } |
978 | |
979 | /* If we already know that the file is being added or removed, |
980 | then we don't want to do an actual file comparison here. */ |
981 | if (empty_file != DIFF_DIFFERENT) |
982 | return empty_file; |
983 | |
984 | /* |
985 | * with 0 or 1 -r option specified, run a quick diff to see if we |
986 | * should bother with it at all. |
987 | */ |
988 | |
989 | retcode = RCS_cmp_file (vers->srcfile, use_rev1, |
990 | *options ? options : vers->options, |
991 | finfo->file); |
992 | |
993 | return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT; |
994 | } |