Home | History | Annotate | Line # | Download | only in src
      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 
     61 static 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 
     79 static char **call_diff_argv;
     80 static int call_diff_argc;
     81 static size_t call_diff_arg_allocated;
     82 
     83 static int call_diff (const char *out);
     84 static int call_diff3 (char *out);
     85 
     86 static void call_diff_write_output (const char *, size_t);
     87 static void call_diff_flush_output (void);
     88 static void call_diff_write_stdout (const char *);
     89 static void call_diff_error (const char *, const char *, const char *);
     90 
     91 
     92 
     93 /* VARARGS */
     94 static void
     95 call_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 
    104 static void
    105 call_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 
    124 static void
    125 call_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 
    134 static void
    135 call_diff_flush_output (void)
    136 {
    137     cvs_flushout ();
    138 }
    139 
    140 /* Call back function for the diff library to write to stdout.  */
    141 
    142 static void
    143 call_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 
    150 static void
    151 call_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 
    161 static 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 
    172 static 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 
    182 static int
    183 call_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 
    197 static int
    198 call_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 
    212 int
    213 RCS_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.  */
    352 int
    353 RCS_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\
    366 RCS 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 
    509 int
    510 diff_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 
    569 static void
    570 RCS_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