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