Ejemplo n.º 1
0
/* ARGSUSED */
static int diff_fileproc (void *callerdat, struct file_info *finfo)
{
    int status, err = 2;		/* 2 == trouble, like rcsdiff */
    Vers_TS *vers;
    enum diff_file empty_file = DIFF_DIFFERENT;
    char *tmp;
    char *fname;
    char *label1;
    char *label2;
    char *default_branch;

    /* Initialize these solely to avoid warnings from gcc -Wall about
       variables that might be used uninitialized.  */
    tmp = NULL;
    fname = NULL;

    user_file_rev = 0;
    vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0, 0);
    if(vers->tag && RCS_isbranch(finfo->rcs, vers->tag))
        default_branch = vers->tag;
    else
        default_branch = NULL;

    if (diff_rev2 != NULL || diff_date2 != NULL)
    {
        /* Skip all the following checks regarding the user file; we're
           not using it.  */
    }
    else if (vers->vn_user == NULL)
    {
        /* The file does not exist in the working directory.  */
        if ((diff_rev1 != NULL || diff_date1 != NULL)
                && vers->srcfile != NULL)
        {
            /* The file does exist in the repository.  */
            if (empty_files)
                empty_file = DIFF_REMOVED;
            else
            {
                int exists;

                exists = 0;
                /* special handling for TAG_HEAD */
                if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
                {
                    char *head =
                        (vers->vn_rcs == NULL
                         ? NULL
                         : RCS_branch_head (vers->srcfile, vers->vn_rcs));
                    exists = head != NULL;
                    if (head != NULL)
                        xfree (head);
                }
                else
                {
                    Vers_TS *xvers;

                    xvers = Version_TS (finfo, NULL, diff_rev1?diff_rev1:default_branch, diff_date1,
                                        1, 0, 0);
                    exists = xvers->vn_rcs != NULL;
                    freevers_ts (&xvers);
                }
                if (exists)
                    error (0, 0,
                           "%s no longer exists, no comparison available",
                           fn_root(finfo->fullname));
                freevers_ts (&vers);
                diff_mark_errors (err);
                return (err);
            }
        }
        else
        {
            error (0, 0, "I know nothing about %s", fn_root(finfo->fullname));
            freevers_ts (&vers);
            diff_mark_errors (err);
            return (err);
        }
    }
    else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
    {
        if (empty_files)
            empty_file = DIFF_ADDED;
        else
        {
            error (0, 0, "%s is a new entry, no comparison available",
                   fn_root(finfo->fullname));
            freevers_ts (&vers);
            diff_mark_errors (err);
            return (err);
        }
    }
    else if (vers->vn_user[0] == '-')
    {
        if (empty_files)
            empty_file = DIFF_REMOVED;
        else
        {
            error (0, 0, "%s was removed, no comparison available",
                   fn_root(finfo->fullname));
            freevers_ts (&vers);
            diff_mark_errors (err);
            return (err);
        }
    }
    else
    {
        if (vers->vn_rcs == NULL && vers->srcfile == NULL)
        {
            error (0, 0, "cannot find revision control file for %s",
                   fn_root(finfo->fullname));
            freevers_ts (&vers);
            diff_mark_errors (err);
            return (err);
        }
        else
        {
            if (vers->ts_user == NULL)
            {
                error (0, 0, "cannot find %s", fn_root(finfo->fullname));
                freevers_ts (&vers);
                diff_mark_errors (err);
                return (err);
            }
            else if (!strcmp (vers->ts_user, vers->ts_rcs))
            {
                /* The user file matches some revision in the repository
                   Diff against the repository (for remote CVS, we might not
                   have a copy of the user file around).  */
                user_file_rev = vers->vn_user;
            }
        }
    }
    empty_file = diff_file_nodiff (finfo, vers, empty_file, default_branch);
    if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR)
    {
        freevers_ts (&vers);
        if (empty_file == DIFF_SAME)
        {
            /* In the server case, would be nice to send a "Checked-in"
               response, so that the client can rewrite its timestamp.
               server_checked_in by itself isn't the right thing (it
               needs a server_register), but I'm not sure what is.
               It isn't clear to me how "cvs status" handles this (that
               is, for a client which sends Modified not Is-modified to
               "cvs status"), but it does.  */
            return (0);
        }
        else
        {
            diff_mark_errors (err);
            return (err);
        }
    }

    if (empty_file == DIFF_DIFFERENT)
    {
        int dead1, dead2;

        if (use_rev1 == NULL)
            dead1 = 0;
        else
            dead1 = RCS_isdead (vers->srcfile, use_rev1);
        if (use_rev2 == NULL)
            dead2 = 0;
        else
            dead2 = RCS_isdead (vers->srcfile, use_rev2);

        if (dead1 && dead2)
        {
            freevers_ts (&vers);
            return (0);
        }
        else if (dead1)
        {
            if (empty_files)
                empty_file = DIFF_ADDED;
            else
            {
                error (0, 0, "%s is a new entry, no comparison available",
                       fn_root(finfo->fullname));
                freevers_ts (&vers);
                diff_mark_errors (err);
                return (err);
            }
        }
        else if (dead2)
        {
            if (empty_files)
                empty_file = DIFF_REMOVED;
            else
            {
                error (0, 0, "%s was removed, no comparison available",
                       fn_root(finfo->fullname));
                freevers_ts (&vers);
                diff_mark_errors (err);
                return (err);
            }
        }
    }

    /* Output an "Index:" line for patch to use */
    if(!is_rcs)
    {
        cvs_output ("Index: ", 0);
        cvs_output (fn_root(finfo->fullname), 0);
        cvs_output ("\n", 1);
    }

    /* Set up file labels appropriate for compatibility with the Larry Wall
     * implementation of patch if the user didn't specify.  This is irrelevant
     * according to the POSIX.2 specification.
     */
    label1 = NULL;
    label2 = NULL;
    if (!have_rev1_label)
    {
        if (empty_file == DIFF_ADDED)
        {
            TRACE(3,"diff_fileproc(); !have_rev1_label; empty_file == DIFF_ADDED make_file_label(is_rcs=%d)",is_rcs);

            label1 =
                make_file_label (DEVNULL, NULL, NULL, is_rcs);
        }
        else
        {
            TRACE(3,"diff_fileproc(); !have_rev1_label; empty_file != DIFF_ADDED make_file_label(is_rcs=%d)",is_rcs);

            label1 =
                make_file_label (is_rcs?fn_root(finfo->file):fn_root(finfo->fullname), use_rev1, vers ? vers->srcfile : NULL, is_rcs);
        }
    }

    if (!have_rev2_label)
    {
        if (empty_file == DIFF_REMOVED)
        {
            TRACE(3,"diff_fileproc(); !have_rev2_label; empty_file == DIFF_REMOVED make_file_label(is_rcs=%d)",is_rcs);

            label2 =
                make_file_label (DEVNULL, NULL, NULL, is_rcs);
        }
        else
        {
            TRACE(3,"diff_fileproc(); !have_rev2_label; empty_file != DIFF_REMOVED make_file_label(is_rcs=%d)",is_rcs);

            label2 =
                make_file_label (is_rcs?fn_root(finfo->file):fn_root(finfo->fullname), use_rev2, vers ? vers->srcfile : NULL, is_rcs);
        }
    }

    if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
    {
        /* This is fullname, not file, possibly despite the POSIX.2
         * specification, because that's the way all the Larry Wall
         * implementations of patch (are there other implementations?) want
         * things and the POSIX.2 spec appears to leave room for this.
         */
        cvs_output ("\
===================================================================\n\
RCS file: ", 0);
        cvs_output (fn_root(finfo->fullname), 0);
        cvs_output ("\n", 1);

        cvs_output ("diff -N ", 0);
        cvs_output (fn_root(finfo->fullname), 0);
        cvs_output ("\n", 1);

        if (empty_file == DIFF_ADDED)
        {
            if (use_rev2 == NULL)
                status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, RUN_TTY);
            else
            {
                int retcode;

                tmp = cvs_temp_name ();
                retcode = RCS_checkout (vers->srcfile, (char *) NULL,
                                        use_rev2, (char *) NULL,
                                        vers->options,
                                        tmp, (RCSCHECKOUTPROC) NULL,
                                        (void *) NULL, NULL);
                if (retcode != 0)
                {
                    diff_mark_errors (err);
                    return err;
                }

                status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY);
            }
        }
        else
        {
            int retcode;

            tmp = cvs_temp_name ();
            retcode = RCS_checkout (vers->srcfile, (char *) NULL,
                                    use_rev1, (char *) NULL,
                                    *options ? options : vers->options,
                                    tmp, (RCSCHECKOUTPROC) NULL,
                                    (void *) NULL, NULL);
            if (retcode != 0)
            {
                diff_mark_errors (err);
                return err;
            }

            status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY);
        }
    }
Ejemplo n.º 2
0
/* Diff revisions and/or files.  OPTS controls the format of the diff
   (it contains options such as "-w -c", &c), or "" for the default.
   OPTIONS controls keyword expansion, as a string starting with "-k",
   or "" to use the default.  REV1 is the first revision to compare
   against; it must be non-NULL.  If REV2 is non-NULL, compare REV1
   and REV2; if REV2 is NULL compare REV1 with the file in the working
   directory, whose name is WORKFILE.  LABEL1 and LABEL2 are default
   file labels, and (if non-NULL) should be added as -L options
   to diff.  Output goes to stdout.

   Return value is 0 for success, -1 for a failure which set errno,
   or positive for a failure which printed a message on stderr.

   This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.

   An issue is what timezone is used for the dates which appear in the
   diff output.  rcsdiff uses the -z flag, which is not presently
   processed by CVS diff, but I'm not sure exactly how hard to worry
   about this--any such features are undocumented in the context of
   CVS, and I'm not sure how important to users.  */
int
RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc,
		  char * const *diff_argv, const char *options,
                  const char *rev1, const char *rev1_cache, const char *rev2,
                  const char *label1, const char *label2, const char *workfile)
{
    char *tmpfile1 = NULL;
    char *tmpfile2 = NULL;
    const char *use_file1, *use_file2;
    int status, retval;


    cvs_output ("\
===================================================================\n\
RCS file: ", 0);
    cvs_output (rcsfile->print_path, 0);
    cvs_output ("\n", 1);

    /* Historically, `cvs diff' has expanded the $Name keyword to the
       empty string when checking out revisions.  This is an accident,
       but no one has considered the issue thoroughly enough to determine
       what the best behavior is.  Passing NULL for the `nametag' argument
       preserves the existing behavior. */

    cvs_output ("retrieving revision ", 0);
    cvs_output (rev1, 0);
    cvs_output ("\n", 1);

    if (rev1_cache != NULL)
	use_file1 = rev1_cache;
    else
    {
	tmpfile1 = cvs_temp_name();
	status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
	                       NULL, NULL);
	if (status > 0)
	{
	    retval = status;
	    goto error_return;
	}
	else if (status < 0)
	{
	    error( 0, errno,
	           "cannot check out revision %s of %s", rev1, rcsfile->path );
	    retval = 1;
	    goto error_return;
	}
	use_file1 = tmpfile1;
    }

    if (rev2 == NULL)
    {
	assert (workfile != NULL);
	use_file2 = workfile;
    }
    else
    {
	tmpfile2 = cvs_temp_name ();
	cvs_output ("retrieving revision ", 0);
	cvs_output (rev2, 0);
	cvs_output ("\n", 1);
	status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
			       tmpfile2, NULL, NULL);
	if (status > 0)
	{
	    retval = status;
	    goto error_return;
	}
	else if (status < 0)
	{
	    error (0, errno,
		   "cannot check out revision %s of %s", rev2, rcsfile->path);
	    return 1;
	}
	use_file2 = tmpfile2;
    }

    RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile);
    status = diff_exec (use_file1, use_file2, label1, label2,
			diff_argc, diff_argv, RUN_TTY);
    if (status >= 0)
    {
	retval = status;
	goto error_return;
    }
    else if (status < 0)
    {
	error (0, errno,
	       "cannot diff %s and %s", use_file1, use_file2);
	retval = 1;
	goto error_return;
    }

 error_return:
    {
	/* Call CVS_UNLINK() below rather than unlink_file to avoid the check
	 * for noexec.
	 */
	if( tmpfile1 != NULL )
	{
	    if( CVS_UNLINK( tmpfile1 ) < 0 )
	    {
		if( !existence_error( errno ) )
		    error( 0, errno, "cannot remove temp file %s", tmpfile1 );
	    }
	    free( tmpfile1 );
	}
	if( tmpfile2 != NULL )
	{
	    if( CVS_UNLINK( tmpfile2 ) < 0 )
	    {
		if( !existence_error( errno ) )
		    error( 0, errno, "cannot remove temp file %s", tmpfile2 );
	    }
	    free (tmpfile2);
	}
    }

    return retval;
}