2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
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.
8 * The functions in this file provide an interface for performing
9 * operations directly on RCS files.
19 /* This file, rcs.h, and rcs.c, together sometimes known as the "RCS
20 library", are intended to define our interface to RCS files.
22 Whether there will also be a version of RCS which uses this
23 library, or whether the library will be packaged for uses beyond
24 CVS or RCS (many people would like such a thing) is an open
25 question. Some considerations:
27 1. An RCS library for CVS must have the capabilities of the
28 existing CVS code which accesses RCS files. In particular, simple
29 approaches will often be slow.
31 2. An RCS library should not use code from the current RCS
32 (5.7 and its ancestors). The code has many problems. Too few
33 comments, too many layers of abstraction, too many global variables
34 (the correct number for a library is zero), too much intricately
35 interwoven functionality, and too many clever hacks. Paul Eggert,
36 the current RCS maintainer, agrees.
38 3. More work needs to be done in terms of separating out the RCS
39 library from the rest of CVS (for example, cvs_output should be
40 replaced by a callback, and the declarations should be centralized
41 into rcs.h, and probably other such cleanups).
43 4. To be useful for RCS and perhaps for other uses, the library
44 may need features beyond those needed by CVS.
46 5. Any changes to the RCS file format *must* be compatible. Many,
47 many tools (not just CVS and RCS) can at least import this format.
48 RCS and CVS must preserve the current ability to import/export it
49 (preferably improved--magic branches are currently a roadblock).
50 See doc/RCSFILES in the CVS distribution for documentation of this
53 On a related note, see the comments at diff_exec, later in this file,
54 for more on the diff library. */
56 static void RCS_output_diff_options PROTO ((const char *, const char *,
57 const char *, const char *));
60 /* Stuff to deal with passing arguments the way libdiff.a wants to deal
61 with them. This is a crufty interface; there is no good reason for it
62 to resemble a command line rather than something closer to "struct
63 log_data" in log.c. */
65 /* First call call_diff_setup to setup any initial arguments. The
66 argument will be parsed into whitespace separated words and added
67 to the global call_diff_argv list.
69 Then, optionally, call call_diff_arg for each additional argument
70 that you'd like to pass to the diff library.
72 Finally, call call_diff or call_diff3 to produce the diffs. */
74 static char **call_diff_argv;
75 static int call_diff_argc;
76 static int call_diff_argc_allocated;
78 static void call_diff_add_arg PROTO ((const char *));
79 static void call_diff_setup PROTO ((const char *prog));
80 static int call_diff PROTO ((const char *out));
81 static int call_diff3 PROTO ((char *out));
83 static void call_diff_write_output PROTO((const char *, size_t));
84 static void call_diff_flush_output PROTO((void));
85 static void call_diff_write_stdout PROTO((const char *));
86 static void call_diff_error PROTO((const char *, const char *, const char *));
90 call_diff_setup (prog)
97 /* clean out any malloc'ed values from call_diff_argv */
98 for (i = 0; i < call_diff_argc; i++)
100 if (call_diff_argv[i])
102 free (call_diff_argv[i]);
103 call_diff_argv[i] = (char *) 0;
108 call_diff_prog = xstrdup (prog);
110 /* put each word into call_diff_argv, allocating it as we go */
111 for (cp = strtok (call_diff_prog, " \t");
113 cp = strtok ((char *) NULL, " \t"))
114 call_diff_add_arg (cp);
115 free (call_diff_prog);
122 call_diff_add_arg (s);
126 call_diff_add_arg (s)
129 /* allocate more argv entries if we've run out */
130 if (call_diff_argc >= call_diff_argc_allocated)
132 call_diff_argc_allocated += 50;
133 call_diff_argv = (char **)
134 xrealloc ((char *) call_diff_argv,
135 call_diff_argc_allocated * sizeof (char **));
139 call_diff_argv[call_diff_argc++] = xstrdup (s);
141 /* Not post-incremented on purpose! */
142 call_diff_argv[call_diff_argc] = (char *) 0;
145 /* Callback function for the diff library to write data to the output
146 file. This is used when we are producing output to stdout. */
149 call_diff_write_output (text, len)
154 cvs_output (text, len);
157 /* Call back function for the diff library to flush the output file.
158 This is used when we are producing output to stdout. */
161 call_diff_flush_output ()
166 /* Call back function for the diff library to write to stdout. */
169 call_diff_write_stdout (text)
172 cvs_output (text, 0);
175 /* Call back function for the diff library to write to stderr. */
178 call_diff_error (format, a1, a2)
183 /* FIXME: Should we somehow indicate that this error is coming from
185 error (0, 0, format, a1, a2);
188 /* This set of callback functions is used if we are sending the diff
191 static struct diff_callbacks call_diff_stdout_callbacks =
193 call_diff_write_output,
194 call_diff_flush_output,
195 call_diff_write_stdout,
199 /* This set of callback functions is used if we are sending the diff
202 static struct diff_callbacks call_diff_file_callbacks =
204 (void (*) PROTO((const char *, size_t))) NULL,
205 (void (*) PROTO((void))) NULL,
206 call_diff_write_stdout,
217 return diff_run (call_diff_argc, call_diff_argv, NULL,
218 &call_diff_stdout_callbacks);
220 return diff_run (call_diff_argc, call_diff_argv, out,
221 &call_diff_file_callbacks);
231 return diff3_run (call_diff_argc, call_diff_argv, NULL,
232 &call_diff_stdout_callbacks);
234 return diff3_run (call_diff_argc, call_diff_argv, out,
235 &call_diff_file_callbacks);
240 /* Merge revisions REV1 and REV2. */
243 RCS_merge(rcs, path, workfile, options, rev1, rev2)
246 const char *workfile;
253 char *diffout = NULL;
256 if (options != NULL && options[0] != '\0')
257 assert (options[0] == '-' && options[1] == 'k');
259 cvs_output ("RCS file: ", 0);
260 cvs_output (rcs->path, 0);
261 cvs_output ("\n", 1);
263 /* Calculate numeric revision numbers from rev1 and rev2 (may be
265 xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
266 xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
268 /* Check out chosen revisions. The error message when RCS_checkout
269 fails is not very informative -- it is taken verbatim from RCS 5.7,
270 and relies on RCS_checkout saying something intelligent upon failure. */
271 cvs_output ("retrieving revision ", 0);
272 cvs_output (xrev1, 0);
273 cvs_output ("\n", 1);
275 tmp1 = cvs_temp_name();
276 if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1,
277 (RCSCHECKOUTPROC)0, NULL))
279 cvs_outerr ("rcsmerge: co failed\n", 0);
283 cvs_output ("retrieving revision ", 0);
284 cvs_output (xrev2, 0);
285 cvs_output ("\n", 1);
287 tmp2 = cvs_temp_name();
288 if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2,
289 (RCSCHECKOUTPROC)0, NULL))
291 cvs_outerr ("rcsmerge: co failed\n", 0);
296 cvs_output ("Merging differences between ", 0);
297 cvs_output (xrev1, 0);
298 cvs_output (" and ", 0);
299 cvs_output (xrev2, 0);
300 cvs_output (" into ", 0);
301 cvs_output (workfile, 0);
302 cvs_output ("\n", 1);
304 /* Remember that the first word in the `call_diff_setup' string is used now
305 only for diagnostic messages -- CVS no longer forks to run diff3. */
306 diffout = cvs_temp_name();
307 call_diff_setup ("diff3");
308 call_diff_arg ("-E");
309 call_diff_arg ("-am");
311 call_diff_arg ("-L");
312 call_diff_arg (workfile);
313 call_diff_arg ("-L");
314 call_diff_arg (xrev1);
315 call_diff_arg ("-L");
316 call_diff_arg (xrev2);
318 call_diff_arg ("--");
319 call_diff_arg (workfile);
320 call_diff_arg (tmp1);
321 call_diff_arg (tmp2);
323 retval = call_diff3 (diffout);
326 cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
327 else if (retval == 2)
331 copy_file (diffout, workfile);
335 int save_noexec = noexec;
337 if (unlink_file (tmp1) < 0)
339 if (!existence_error (errno))
340 error (0, errno, "cannot remove temp file %s", tmp1);
343 if (unlink_file (tmp2) < 0)
345 if (!existence_error (errno))
346 error (0, errno, "cannot remove temp file %s", tmp2);
351 if (unlink_file (diffout) < 0)
353 if (!existence_error (errno))
354 error (0, errno, "cannot remove temp file %s", diffout);
360 noexec = save_noexec;
366 /* Diff revisions and/or files. OPTS controls the format of the diff
367 (it contains options such as "-w -c", &c), or "" for the default.
368 OPTIONS controls keyword expansion, as a string starting with "-k",
369 or "" to use the default. REV1 is the first revision to compare
370 against; it must be non-NULL. If REV2 is non-NULL, compare REV1
371 and REV2; if REV2 is NULL compare REV1 with the file in the working
372 directory, whose name is WORKFILE. LABEL1 and LABEL2 are default
373 file labels, and (if non-NULL) should be added as -L options
374 to diff. Output goes to stdout.
376 Return value is 0 for success, -1 for a failure which set errno,
377 or positive for a failure which printed a message on stderr.
379 This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.
381 An issue is what timezone is used for the dates which appear in the
382 diff output. rcsdiff uses the -z flag, which is not presently
383 processed by CVS diff, but I'm not sure exactly how hard to worry
384 about this--any such features are undocumented in the context of
385 CVS, and I'm not sure how important to users. */
387 RCS_exec_rcsdiff(rcsfile, opts, options, rev1, rev1_cache, rev2,
388 label1, label2, workfile )
393 const char *rev1_cache;
397 const char *workfile;
399 char *tmpfile1 = NULL;
400 char *tmpfile2 = NULL;
401 const char *use_file1, *use_file2;
406 ===================================================================\n\
408 cvs_output (rcsfile->path, 0);
409 cvs_output ("\n", 1);
411 /* Historically, `cvs diff' has expanded the $Name keyword to the
412 empty string when checking out revisions. This is an accident,
413 but no one has considered the issue thoroughly enough to determine
414 what the best behavior is. Passing NULL for the `nametag' argument
415 preserves the existing behavior. */
417 cvs_output ("retrieving revision ", 0);
418 cvs_output (rev1, 0);
419 cvs_output ("\n", 1);
421 if (rev1_cache != NULL)
422 use_file1 = rev1_cache;
425 tmpfile1 = cvs_temp_name();
426 status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
427 (RCSCHECKOUTPROC)0, NULL);
436 "cannot check out revision %s of %s", rev1, rcsfile->path );
440 use_file1 = tmpfile1;
445 assert (workfile != NULL);
446 use_file2 = workfile;
450 tmpfile2 = cvs_temp_name ();
451 cvs_output ("retrieving revision ", 0);
452 cvs_output (rev2, 0);
453 cvs_output ("\n", 1);
454 status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
455 tmpfile2, (RCSCHECKOUTPROC)0, NULL);
464 "cannot check out revision %s of %s", rev2, rcsfile->path);
467 use_file2 = tmpfile2;
470 RCS_output_diff_options (opts, rev1, rev2, workfile);
471 status = diff_exec( use_file1, use_file2, label1, label2, opts, RUN_TTY );
480 "cannot diff %s and %s", use_file1, use_file2);
487 /* Call CVS_UNLINK() below rather than unlink_file to avoid the check
490 if( tmpfile1 != NULL )
492 if( CVS_UNLINK( tmpfile1 ) < 0 )
494 if( !existence_error( errno ) )
495 error( 0, errno, "cannot remove temp file %s", tmpfile1 );
499 if( tmpfile2 != NULL )
501 if( CVS_UNLINK( tmpfile2 ) < 0 )
503 if( !existence_error( errno ) )
504 error( 0, errno, "cannot remove temp file %s", tmpfile2 );
515 /* Show differences between two files. This is the start of a diff library.
519 * Should option parsing be part of the library or the caller? The
520 former allows the library to add options without changing the callers,
521 but it causes various problems. One is that something like --brief really
522 wants special handling in CVS, and probably the caller should retain
523 some flexibility in this area. Another is online help (the library could
524 have some feature for providing help, but how does that interact with
525 the help provided by the caller directly?). Another is that as things
526 stand currently, there is no separate namespace for diff options versus
527 "cvs diff" options like -l (that is, if the library adds an option which
528 conflicts with a CVS option, it is trouble).
530 * This isn't required for a first-cut diff library, but if there
531 would be a way for the caller to specify the timestamps that appear
532 in the diffs (rather than the library getting them from the files),
533 that would clean up the kludgy utime() calls in patch.c.
535 Show differences between FILE1 and FILE2. Either one can be
536 DEVNULL to indicate a nonexistent file (same as an empty file
537 currently, I suspect, but that may be an issue in and of itself).
538 OPTIONS is a list of diff options, or "" if none. At a minimum,
539 CVS expects that -c (update.c, patch.c) and -n (update.c) will be
540 supported. Other options, like -u, --speed-large-files, &c, will
541 be specified if the user specified them.
543 OUT is a filename to send the diffs to, or RUN_TTY to send them to
544 stdout. Error messages go to stderr. Return value is 0 for
545 success, -1 for a failure which set errno, 1 for success (and some
546 differences were found), or >1 for a failure which printed a
547 message on stderr. */
550 diff_exec (file1, file2, label1, label2, options, out)
560 #ifdef PRESERVE_PERMISSIONS_SUPPORT
561 /* If either file1 or file2 are special files, pretend they are
562 /dev/null. Reason: suppose a file that represents a block
563 special device in one revision becomes a regular file. CVS
564 must find the `difference' between these files, but a special
565 file contains no data useful for calculating this metric. The
566 safe thing to do is to treat the special file as an empty file,
567 thus recording the regular file's full contents. Doing so will
568 create extremely large deltas at the point of transition
569 between device files and regular files, but this is probably
572 There may be ways around this, but I think they are fraught
575 if (preserve_perms &&
576 strcmp (file1, DEVNULL) != 0 &&
577 strcmp (file2, DEVNULL) != 0)
579 struct stat sb1, sb2;
581 if (CVS_LSTAT (file1, &sb1) < 0)
582 error (1, errno, "cannot get file information for %s", file1);
583 if (CVS_LSTAT (file2, &sb2) < 0)
584 error (1, errno, "cannot get file information for %s", file2);
586 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
588 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
593 args = xmalloc (strlen (options) + 10);
594 /* The first word in this string is used only for error reporting. */
595 sprintf (args, "diff %s", options);
596 call_diff_setup (args);
598 call_diff_arg (label1);
600 call_diff_arg (label2);
601 call_diff_arg ("--");
602 call_diff_arg (file1);
603 call_diff_arg (file2);
606 return call_diff (out);
609 /* Print the options passed to DIFF, in the format used by rcsdiff.
610 The rcsdiff code that produces this output is extremely hairy, and
611 it is not clear how rcsdiff decides which options to print and
612 which not to print. The code below reproduces every rcsdiff run
616 RCS_output_diff_options (opts, rev1, rev2, workfile)
620 const char *workfile;
624 tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10);
626 sprintf (tmp, "diff%s -r%s", opts, rev1);
632 cvs_output (" -r", 3);
633 cvs_output (rev2, 0);
637 assert (workfile != NULL);
639 cvs_output (workfile, 0);
641 cvs_output ("\n", 1);