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 * The functions in this file provide an interface for performing
9 * operations directly on RCS files.
10 */
11
12 #include "cvs.h"
13 #include <assert.h>
14 #include <stdio.h>
15 #include "diffrun.h"
16
17 /* This file, rcs.h, and rcs.c, together sometimes known as the "RCS
18 library", are intended to define our interface to RCS files.
19
20 Whether there will also be a version of RCS which uses this
21 library, or whether the library will be packaged for uses beyond
22 CVS or RCS (many people would like such a thing) is an open
23 question. Some considerations:
24
25 1. An RCS library for CVS must have the capabilities of the
26 existing CVS code which accesses RCS files. In particular, simple
27 approaches will often be slow.
28
29 2. An RCS library should not use code from the current RCS
30 (5.7 and its ancestors). The code has many problems. Too few
31 comments, too many layers of abstraction, too many global variables
32 (the correct number for a library is zero), too much intricately
33 interwoven functionality, and too many clever hacks. Paul Eggert,
34 the current RCS maintainer, agrees.
35
36 3. More work needs to be done in terms of separating out the RCS
37 library from the rest of CVS (for example, cvs_output should be
38 replaced by a callback, and the declarations should be centralized
39 into rcs.h, and probably other such cleanups).
40
41 4. To be useful for RCS and perhaps for other uses, the library
42 may need features beyond those needed by CVS.
43
44 5. Any changes to the RCS file format *must* be compatible. Many,
45 many tools (not just CVS and RCS) can at least import this format.
46 RCS and CVS must preserve the current ability to import/export it
47 (preferably improved--magic branches are currently a roadblock).
48 See doc/RCSFILES in the CVS distribution for documentation of this
49 file format.
50
51 On a related note, see the comments at diff_exec, later in this file,
52 for more on the diff library. */
53
54 static void RCS_output_diff_options PROTO ((char *, char *, char *, char *));
55
56
57 /* Stuff to deal with passing arguments the way libdiff.a wants to deal
58 with them. This is a crufty interface; there is no good reason for it
59 to resemble a command line rather than something closer to "struct
60 log_data" in log.c. */
61
62 /* First call call_diff_setup to setup any initial arguments. The
63 argument will be parsed into whitespace separated words and added
64 to the global call_diff_argv list.
65
66 Then, optionally, call call_diff_arg for each additional argument
67 that you'd like to pass to the diff library.
68
69 Finally, call call_diff or call_diff3 to produce the diffs. */
70
71 static char **call_diff_argv;
72 static int call_diff_argc;
73 static int call_diff_argc_allocated;
74
75 static void call_diff_add_arg PROTO ((const char *));
76 static void call_diff_setup PROTO ((const char *prog));
77 static int call_diff PROTO ((char *out));
78 static int call_diff3 PROTO ((char *out));
79
80 static void call_diff_write_output PROTO((const char *, size_t));
81 static void call_diff_flush_output PROTO((void));
82 static void call_diff_write_stdout PROTO((const char *));
83 static void call_diff_error PROTO((const char *, const char *, const char *));
84
85 /* VARARGS */
86 static void
call_diff_setup(prog)87 call_diff_setup (prog)
88 const char *prog;
89 {
90 char *cp;
91 int i;
92 char *call_diff_prog;
93
94 /* clean out any malloc'ed values from call_diff_argv */
95 for (i = 0; i < call_diff_argc; i++)
96 {
97 if (call_diff_argv[i])
98 {
99 free (call_diff_argv[i]);
100 call_diff_argv[i] = (char *) 0;
101 }
102 }
103 call_diff_argc = 0;
104
105 call_diff_prog = xstrdup (prog);
106
107 /* put each word into call_diff_argv, allocating it as we go */
108 for (cp = strtok (call_diff_prog, " \t");
109 cp != NULL;
110 cp = strtok ((char *) NULL, " \t"))
111 call_diff_add_arg (cp);
112 free (call_diff_prog);
113 }
114
115 static void
call_diff_arg(s)116 call_diff_arg (s)
117 const char *s;
118 {
119 call_diff_add_arg (s);
120 }
121
122 static void
call_diff_add_arg(s)123 call_diff_add_arg (s)
124 const char *s;
125 {
126 /* allocate more argv entries if we've run out */
127 if (call_diff_argc >= call_diff_argc_allocated)
128 {
129 call_diff_argc_allocated += 50;
130 call_diff_argv = (char **)
131 xrealloc ((char *) call_diff_argv,
132 call_diff_argc_allocated * sizeof (char **));
133 }
134
135 if (s)
136 call_diff_argv[call_diff_argc++] = xstrdup (s);
137 else
138 /* Not post-incremented on purpose! */
139 call_diff_argv[call_diff_argc] = (char *) 0;
140 }
141
142 /* Callback function for the diff library to write data to the output
143 file. This is used when we are producing output to stdout. */
144
145 static void
call_diff_write_output(text,len)146 call_diff_write_output (text, len)
147 const char *text;
148 size_t len;
149 {
150 if (len > 0)
151 cvs_output (text, len);
152 }
153
154 /* Call back function for the diff library to flush the output file.
155 This is used when we are producing output to stdout. */
156
157 static void
call_diff_flush_output()158 call_diff_flush_output ()
159 {
160 cvs_flushout ();
161 }
162
163 /* Call back function for the diff library to write to stdout. */
164
165 static void
call_diff_write_stdout(text)166 call_diff_write_stdout (text)
167 const char *text;
168 {
169 cvs_output (text, 0);
170 }
171
172 /* Call back function for the diff library to write to stderr. */
173
174 static void
call_diff_error(format,a1,a2)175 call_diff_error (format, a1, a2)
176 const char *format;
177 const char *a1;
178 const char *a2;
179 {
180 /* FIXME: Should we somehow indicate that this error is coming from
181 the diff library? */
182 error (0, 0, format, a1, a2);
183 }
184
185 /* This set of callback functions is used if we are sending the diff
186 to stdout. */
187
188 static struct diff_callbacks call_diff_stdout_callbacks =
189 {
190 call_diff_write_output,
191 call_diff_flush_output,
192 call_diff_write_stdout,
193 call_diff_error
194 };
195
196 /* This set of callback functions is used if we are sending the diff
197 to a file. */
198
199 static struct diff_callbacks call_diff_file_callbacks =
200 {
201 (void (*) PROTO((const char *, size_t))) NULL,
202 (void (*) PROTO((void))) NULL,
203 call_diff_write_stdout,
204 call_diff_error
205 };
206
207 static int
call_diff(out)208 call_diff (out)
209 char *out;
210 {
211 if (out == RUN_TTY)
212 return diff_run (call_diff_argc, call_diff_argv, NULL,
213 &call_diff_stdout_callbacks);
214 else
215 return diff_run (call_diff_argc, call_diff_argv, out,
216 &call_diff_file_callbacks);
217 }
218
219 static int
call_diff3(out)220 call_diff3 (out)
221 char *out;
222 {
223 if (out == RUN_TTY)
224 return diff3_run (call_diff_argc, call_diff_argv, NULL,
225 &call_diff_stdout_callbacks);
226 else
227 return diff3_run (call_diff_argc, call_diff_argv, out,
228 &call_diff_file_callbacks);
229 }
230
231
232
233 /* Merge revisions REV1 and REV2. */
234
235 int
RCS_merge(rcs,path,workfile,options,rev1,rev2)236 RCS_merge(rcs, path, workfile, options, rev1, rev2)
237 RCSNode *rcs;
238 char *path;
239 char *workfile;
240 char *options;
241 char *rev1;
242 char *rev2;
243 {
244 char *xrev1, *xrev2;
245 char *tmp1, *tmp2;
246 char *diffout = NULL;
247 int retval;
248
249 if (options != NULL && options[0] != '\0')
250 assert (options[0] == '-' && options[1] == 'k');
251
252 cvs_output ("RCS file: ", 0);
253 cvs_output (rcs->path, 0);
254 cvs_output ("\n", 1);
255
256 /* Calculate numeric revision numbers from rev1 and rev2 (may be
257 symbolic). */
258 xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
259 xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
260
261 /* Check out chosen revisions. The error message when RCS_checkout
262 fails is not very informative -- it is taken verbatim from RCS 5.7,
263 and relies on RCS_checkout saying something intelligent upon failure. */
264 cvs_output ("retrieving revision ", 0);
265 cvs_output (xrev1, 0);
266 cvs_output ("\n", 1);
267
268 tmp1 = cvs_temp_name();
269 if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1,
270 (RCSCHECKOUTPROC)0, NULL))
271 {
272 cvs_outerr ("rcsmerge: co failed\n", 0);
273 error_exit();
274 }
275
276 cvs_output ("retrieving revision ", 0);
277 cvs_output (xrev2, 0);
278 cvs_output ("\n", 1);
279
280 tmp2 = cvs_temp_name();
281 if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2,
282 (RCSCHECKOUTPROC)0, NULL))
283 {
284 cvs_outerr ("rcsmerge: co failed\n", 0);
285 error_exit();
286 }
287
288 /* Merge changes. */
289 cvs_output ("Merging differences between ", 0);
290 cvs_output (xrev1, 0);
291 cvs_output (" and ", 0);
292 cvs_output (xrev2, 0);
293 cvs_output (" into ", 0);
294 cvs_output (workfile, 0);
295 cvs_output ("\n", 1);
296
297 /* Remember that the first word in the `call_diff_setup' string is used now
298 only for diagnostic messages -- CVS no longer forks to run diff3. */
299 diffout = cvs_temp_name();
300 call_diff_setup ("diff3");
301 call_diff_arg ("-E");
302 call_diff_arg ("-am");
303
304 call_diff_arg ("-L");
305 call_diff_arg (workfile);
306 call_diff_arg ("-L");
307 call_diff_arg (xrev1);
308 call_diff_arg ("-L");
309 call_diff_arg (xrev2);
310
311 call_diff_arg (workfile);
312 call_diff_arg (tmp1);
313 call_diff_arg (tmp2);
314
315 retval = call_diff3 (diffout);
316
317 if (retval == 1)
318 cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
319 else if (retval == 2)
320 error_exit();
321
322 if (diffout)
323 copy_file (diffout, workfile);
324
325 /* Clean up. */
326 {
327 int save_noexec = noexec;
328 noexec = 0;
329 if (unlink_file (tmp1) < 0)
330 {
331 if (!existence_error (errno))
332 error (0, errno, "cannot remove temp file %s", tmp1);
333 }
334 free (tmp1);
335 if (unlink_file (tmp2) < 0)
336 {
337 if (!existence_error (errno))
338 error (0, errno, "cannot remove temp file %s", tmp2);
339 }
340 free (tmp2);
341 if (diffout)
342 {
343 if (unlink_file (diffout) < 0)
344 {
345 if (!existence_error (errno))
346 error (0, errno, "cannot remove temp file %s", diffout);
347 }
348 free (diffout);
349 }
350 free (xrev1);
351 free (xrev2);
352 noexec = save_noexec;
353 }
354
355 return retval;
356 }
357
358 /* Diff revisions and/or files. OPTS controls the format of the diff
359 (it contains options such as "-w -c", &c), or "" for the default.
360 OPTIONS controls keyword expansion, as a string starting with "-k",
361 or "" to use the default. REV1 is the first revision to compare
362 against; it must be non-NULL. If REV2 is non-NULL, compare REV1
363 and REV2; if REV2 is NULL compare REV1 with the file in the working
364 directory, whose name is WORKFILE. LABEL1 and LABEL2 are default
365 file labels, and (if non-NULL) should be added as -L options
366 to diff. Output goes to stdout.
367
368 Return value is 0 for success, -1 for a failure which set errno,
369 or positive for a failure which printed a message on stderr.
370
371 This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.
372
373 An issue is what timezone is used for the dates which appear in the
374 diff output. rcsdiff uses the -z flag, which is not presently
375 processed by CVS diff, but I'm not sure exactly how hard to worry
376 about this--any such features are undocumented in the context of
377 CVS, and I'm not sure how important to users. */
378 int
RCS_exec_rcsdiff(rcsfile,opts,options,rev1,rev2,label1,label2,workfile)379 RCS_exec_rcsdiff (rcsfile, opts, options, rev1, rev2, label1, label2, workfile)
380 RCSNode *rcsfile;
381 char *opts;
382 char *options;
383 char *rev1;
384 char *rev2;
385 char *label1;
386 char *label2;
387 char *workfile;
388 {
389 char *tmpfile1;
390 char *tmpfile2;
391 char *use_file2;
392 int status, retval;
393
394 tmpfile1 = cvs_temp_name ();
395 tmpfile2 = NULL;
396
397 cvs_output ("\
398 ===================================================================\n\
399 RCS file: ", 0);
400 cvs_output (rcsfile->path, 0);
401 cvs_output ("\n", 1);
402
403 /* Historically, `cvs diff' has expanded the $Name keyword to the
404 empty string when checking out revisions. This is an accident,
405 but no one has considered the issue thoroughly enough to determine
406 what the best behavior is. Passing NULL for the `nametag' argument
407 preserves the existing behavior. */
408
409 status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
410 (RCSCHECKOUTPROC)0, NULL);
411 if (status > 0)
412 {
413 retval = status;
414 goto error_return;
415 }
416 else if (status < 0)
417 {
418 error (0, errno,
419 "cannot check out revision %s of %s", rev1, rcsfile->path);
420 retval = 1;
421 goto error_return;
422 }
423
424 if (rev2 == NULL)
425 {
426 assert (workfile != NULL);
427 use_file2 = workfile;
428 }
429 else
430 {
431 tmpfile2 = cvs_temp_name ();
432 status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
433 tmpfile2, (RCSCHECKOUTPROC)0, NULL);
434 if (status > 0)
435 {
436 retval = status;
437 goto error_return;
438 }
439 else if (status < 0)
440 {
441 error (0, errno,
442 "cannot check out revision %s of %s", rev2, rcsfile->path);
443 return 1;
444 }
445 use_file2 = tmpfile2;
446 }
447
448 RCS_output_diff_options (opts, rev1, rev2, workfile);
449 status = diff_execv (tmpfile1, use_file2, label1, label2, opts, RUN_TTY);
450 if (status >= 0)
451 {
452 retval = status;
453 goto error_return;
454 }
455 else if (status < 0)
456 {
457 error (0, errno,
458 "cannot diff %s and %s", tmpfile1, use_file2);
459 retval = 1;
460 goto error_return;
461 }
462
463 error_return:
464 {
465 int save_noexec = noexec;
466 noexec = 0;
467 if (unlink_file (tmpfile1) < 0)
468 {
469 if (!existence_error (errno))
470 error (0, errno, "cannot remove temp file %s", tmpfile1);
471 }
472 noexec = save_noexec;
473 }
474 free (tmpfile1);
475 if (tmpfile2 != NULL)
476 {
477 int save_noexec = noexec;
478 noexec = 0;
479 if (unlink_file (tmpfile2) < 0)
480 {
481 if (!existence_error (errno))
482 error (0, errno, "cannot remove temp file %s", tmpfile2);
483 }
484 noexec = save_noexec;
485 free (tmpfile2);
486 }
487
488 return retval;
489 }
490
491
492 /* Show differences between two files. This is the start of a diff library.
493
494 Some issues:
495
496 * Should option parsing be part of the library or the caller? The
497 former allows the library to add options without changing the callers,
498 but it causes various problems. One is that something like --brief really
499 wants special handling in CVS, and probably the caller should retain
500 some flexibility in this area. Another is online help (the library could
501 have some feature for providing help, but how does that interact with
502 the help provided by the caller directly?). Another is that as things
503 stand currently, there is no separate namespace for diff options versus
504 "cvs diff" options like -l (that is, if the library adds an option which
505 conflicts with a CVS option, it is trouble).
506
507 * This isn't required for a first-cut diff library, but if there
508 would be a way for the caller to specify the timestamps that appear
509 in the diffs (rather than the library getting them from the files),
510 that would clean up the kludgy utime() calls in patch.c.
511
512 Show differences between FILE1 and FILE2. Either one can be
513 DEVNULL to indicate a nonexistent file (same as an empty file
514 currently, I suspect, but that may be an issue in and of itself).
515 OPTIONS is a list of diff options, or "" if none. At a minimum,
516 CVS expects that -c (update.c, patch.c) and -n (update.c) will be
517 supported. Other options, like -u, --speed-large-files, &c, will
518 be specified if the user specified them.
519
520 OUT is a filename to send the diffs to, or RUN_TTY to send them to
521 stdout. Error messages go to stderr. Return value is 0 for
522 success, -1 for a failure which set errno, 1 for success (and some
523 differences were found), or >1 for a failure which printed a
524 message on stderr. */
525
526 int
diff_exec(file1,file2,label1,label2,options,out)527 diff_exec (file1, file2, label1, label2, options, out)
528 char *file1;
529 char *file2;
530 char *label1;
531 char *label2;
532 char *options;
533 char *out;
534 {
535 char *args;
536
537 #ifdef PRESERVE_PERMISSIONS_SUPPORT
538 /* If either file1 or file2 are special files, pretend they are
539 /dev/null. Reason: suppose a file that represents a block
540 special device in one revision becomes a regular file. CVS
541 must find the `difference' between these files, but a special
542 file contains no data useful for calculating this metric. The
543 safe thing to do is to treat the special file as an empty file,
544 thus recording the regular file's full contents. Doing so will
545 create extremely large deltas at the point of transition
546 between device files and regular files, but this is probably
547 very rare anyway.
548
549 There may be ways around this, but I think they are fraught
550 with danger. -twp */
551
552 if (preserve_perms &&
553 strcmp (file1, DEVNULL) != 0 &&
554 strcmp (file2, DEVNULL) != 0)
555 {
556 struct stat sb1, sb2;
557
558 if (CVS_LSTAT (file1, &sb1) < 0)
559 error (1, errno, "cannot get file information for %s", file1);
560 if (CVS_LSTAT (file2, &sb2) < 0)
561 error (1, errno, "cannot get file information for %s", file2);
562
563 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
564 file1 = DEVNULL;
565 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
566 file2 = DEVNULL;
567 }
568 #endif
569
570 args = xmalloc (strlen (options) + 10);
571 /* The first word in this string is used only for error reporting. */
572 sprintf (args, "diff %s", options);
573 call_diff_setup (args);
574 if (label1)
575 call_diff_arg (label1);
576 if (label2)
577 call_diff_arg (label2);
578 call_diff_arg (file1);
579 call_diff_arg (file2);
580 free (args);
581
582 return call_diff (out);
583 }
584
585 int
diff_execv(file1,file2,label1,label2,options,out)586 diff_execv (file1, file2, label1, label2, options, out)
587 char *file1;
588 char *file2;
589 char *label1;
590 char *label2;
591 char *options;
592 char *out;
593 {
594 char *args;
595
596 #ifdef PRESERVE_PERMISSIONS_SUPPORT
597 /* Pretend that special files are /dev/null for purposes of making
598 diffs. See comments in diff_exec. */
599
600 if (preserve_perms &&
601 strcmp (file1, DEVNULL) != 0 &&
602 strcmp (file2, DEVNULL) != 0)
603 {
604 struct stat sb1, sb2;
605
606 if (CVS_LSTAT (file1, &sb1) < 0)
607 error (1, errno, "cannot get file information for %s", file1);
608 if (CVS_LSTAT (file2, &sb2) < 0)
609 error (1, errno, "cannot get file information for %s", file2);
610
611 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
612 file1 = DEVNULL;
613 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
614 file2 = DEVNULL;
615 }
616 #endif
617
618 args = xmalloc (strlen (options) + 10);
619 /* The first word in this string is used only for error reporting. */
620 /* I guess we are pretty confident that options starts with a space. */
621 sprintf (args, "diff%s", options);
622 call_diff_setup (args);
623 if (label1)
624 call_diff_arg (label1);
625 if (label2)
626 call_diff_arg (label2);
627 call_diff_arg (file1);
628 call_diff_arg (file2);
629 free (args);
630
631 return call_diff (out);
632 }
633
634 /* Print the options passed to DIFF, in the format used by rcsdiff.
635 The rcsdiff code that produces this output is extremely hairy, and
636 it is not clear how rcsdiff decides which options to print and
637 which not to print. The code below reproduces every rcsdiff run
638 that I have seen. */
639
640 static void
RCS_output_diff_options(opts,rev1,rev2,workfile)641 RCS_output_diff_options (opts, rev1, rev2, workfile)
642 char *opts;
643 char *rev1;
644 char *rev2;
645 char *workfile;
646 {
647 char *tmp;
648
649 tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10);
650
651 sprintf (tmp, "diff%s -r%s", opts, rev1);
652 cvs_output (tmp, 0);
653 free (tmp);
654
655 if (rev2)
656 {
657 cvs_output (" -r", 3);
658 cvs_output (rev2, 0);
659 }
660 else
661 {
662 assert (workfile != NULL);
663 cvs_output (" ", 1);
664 cvs_output (workfile, 0);
665 }
666 cvs_output ("\n", 1);
667 }
668