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