Exemple #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);
        }
    }
Exemple #2
0
int
RCS_merge (RCSNode *rcs, const char *path, const char *workfile,
           const char *options, const char *rev1, const char *rev2)
{
    char *xrev1, *xrev2;
    char *tmp1, *tmp2;
    char *diffout = NULL;
    int retval;

    if (options != NULL && options[0] != '\0')
      assert (options[0] == '-' && options[1] == 'k');

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

    /* Calculate numeric revision numbers from rev1 and rev2 (may be
       symbolic).
       FIXME - No they can't.  Both calls to RCS_merge are passing in
       numeric revisions.  */
    xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
    xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
    assert (xrev1 && xrev2);

    /* Check out chosen revisions.  The error message when RCS_checkout
       fails is not very informative -- it is taken verbatim from RCS 5.7,
       and relies on RCS_checkout saying something intelligent upon failure. */
    cvs_output ("retrieving revision ", 0);
    cvs_output (xrev1, 0);
    cvs_output ("\n", 1);

    tmp1 = cvs_temp_name();
    if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, NULL, NULL))
    {
	cvs_outerr ("rcsmerge: co failed\n", 0);
	exit (EXIT_FAILURE);
    }

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

    tmp2 = cvs_temp_name();
    if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, NULL, NULL))
    {
	cvs_outerr ("rcsmerge: co failed\n", 0);
	exit (EXIT_FAILURE);
    }

    /* Merge changes. */
    cvs_output ("Merging differences between ", 0);
    cvs_output (xrev1, 0);
    cvs_output (" and ", 0);
    cvs_output (xrev2, 0);
    cvs_output (" into ", 0);
    cvs_output (workfile, 0);
    cvs_output ("\n", 1);

    /* Remember that the first word in the `call_diff_setup' string is used now
       only for diagnostic messages -- CVS no longer forks to run diff3. */
    diffout = cvs_temp_name();
    call_diff_setup ("diff3", 0, NULL);
    call_diff_add_arg ("-E");
    call_diff_add_arg ("-am");

    call_diff_add_arg ("-L");
    call_diff_add_arg (workfile);
    call_diff_add_arg ("-L");
    call_diff_add_arg (xrev1);
    call_diff_add_arg ("-L");
    call_diff_add_arg (xrev2);

    call_diff_add_arg ("--");
    call_diff_add_arg (workfile);
    call_diff_add_arg (tmp1);
    call_diff_add_arg (tmp2);

    retval = call_diff3 (diffout);

    if (retval == 1)
	cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
    else if (retval == 2)
	exit (EXIT_FAILURE);

    if (diffout)
	copy_file (diffout, workfile);

    /* Clean up. */
    {
	int save_noexec = noexec;
	noexec = 0;
	if (unlink_file (tmp1) < 0)
	{
	    if (!existence_error (errno))
		error (0, errno, "cannot remove temp file %s", tmp1);
	}
	free (tmp1);
	if (unlink_file (tmp2) < 0)
	{
	    if (!existence_error (errno))
		error (0, errno, "cannot remove temp file %s", tmp2);
	}
	free (tmp2);
	if (diffout)
	{
	    if (unlink_file (diffout) < 0)
	    {
		if (!existence_error (errno))
		    error (0, errno, "cannot remove temp file %s", diffout);
	    }
	    free (diffout);
	}
	free (xrev1);
	free (xrev2);
	noexec = save_noexec;
    }

    return retval;
}
Exemple #3
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;
}
Exemple #4
0
/*
 * Builds a temporary file using setup_tmpfile() and invokes the user's
 * editor on the file.  The header garbage in the resultant file is then
 * stripped and the log message is stored in the "message" argument.
 * 
 * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
 * is NULL, use the CVSADM_TEMPLATE file instead.
 */
void do_editor (const char *dir, char **messagep, const char *repository, List *changes)
{
    static int reuse_log_message = 0;
    char *line;
    int line_length;
    size_t line_chars_allocated;
    char *fname;
    struct stat pre_stbuf, post_stbuf;
    int retcode = 0;

    if (noexec || reuse_log_message)
		return;

    /* Abort creation of temp file if no editor is defined */
    if (strcmp (Editor, "") == 0)
		error(1, 0, "no editor defined, must use -e or -m");

	/* Create a temporary file */
	/* FIXME - It's possible we should be relying on cvs_temp_file to open
	* the file here - we get race conditions otherwise.
	*/
	fname = cvs_temp_name ();
again:
	if ((fp = CVS_FOPEN (fname, "w+")) == NULL)
		error (1, 0, "cannot create temporary file %s", fname);

	if (*messagep)
	{
		fprintf (fp, "%s", *messagep);

		if ((*messagep)[0] == '\0' ||
			(*messagep)[strlen (*messagep) - 1] != '\n')
			fprintf (fp, "\n");
	}

    if (repository != NULL)
	{
		rcsinfo_param_t args;
		args.directory = Short_Repository(repository);
		args.message=NULL;
		/* tack templates on if necessary */
		TRACE(3,"run rcsinfo trigger");
		if(!run_trigger(&args,rcsinfo_proc) && args.message)
		{
			fprintf(fp,"%s",args.message);
			if (args.message[0] == '\0' || args.message[strlen(args.message) - 1] != '\n')
				fprintf (fp, "\n");
		}
	}
    else
    {
		FILE *tfp;
		char buf[1024];
		size_t n;
		size_t nwrite;

		/* Why "b"?  */
		tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
		if (tfp == NULL)
		{
			if (!existence_error (errno))
			error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
		}
		else
		{
			while (!feof (tfp))
			{
				char *p = buf;
				n = fread (buf, 1, sizeof buf, tfp);
				nwrite = n;
				while (nwrite > 0)
				{
					n = fwrite (p, 1, nwrite, fp);
					nwrite -= n;
					p += n;
				}
				if (ferror (tfp))
					error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
			}
			if (fclose (tfp) < 0)
				error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
		}
	}

	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
#ifdef _WIN32
#if (CVSNT_SPECIAL_BUILD_FLAG != 0)
	if (strcasecmp(CVSNT_SPECIAL_BUILD,"Suite")!=0)
#endif
	{
	fprintf (fp,"%s     Committed on the Free edition of March Hare Software CVSNT Server\n",CVSEDITPREFIX);
	fprintf (fp,"%s     Upgrade to CVS Suite for more features and support:\n",CVSEDITPREFIX);
	fprintf (fp,"%s           http://march-hare.com/cvsnt/\n",CVSEDITPREFIX);
	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
    }
#endif
	fprintf (fp,"%sEnter Log.  Lines beginning with `%.*s' are removed automatically\n%s\n",
				CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,CVSEDITPREFIX);
	if (dir != NULL && *dir)
		fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,	dir, CVSEDITPREFIX);
	if (changes != NULL)
		setup_tmpfile (fp, CVSEDITPREFIX, changes);
	fprintf (fp,"%s----------------------------------------------------------------------\n",
				CVSEDITPREFIX);

	/* finish off the temp file */
	if (fclose (fp) == EOF)
		error (1, errno, "%s", fname);
	if ( CVS_STAT (fname, &pre_stbuf) == -1)
		pre_stbuf.st_mtime = 0;

	/* run the editor */
	run_setup (Editor);
	run_arg (fname);
	if ((retcode = run_exec (true)) != 0)
		error (0, retcode == -1 ? errno : 0, "warning: editor session failed");

	/* put the entire message back into the *messagep variable */
	fp = open_file (fname, "r");

	if (*messagep)
		xfree (*messagep);

	if ( CVS_STAT (fname, &post_stbuf) != 0)
		error (1, errno, "cannot find size of temp file %s", fname);

	if (post_stbuf.st_size == 0)
		*messagep = NULL;
	else
	{
		/* On NT, we might read less than st_size bytes, but we won't
		read more.  So this works.  */
		*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
 		*messagep[0] = '\0';
	}

	line = NULL;
	line_chars_allocated = 0;

	if (*messagep)
	{
		size_t message_len = post_stbuf.st_size + 1;
		size_t offset = 0;
		while (1)
		{
			line_length = getline (&line, &line_chars_allocated, fp);
			if (line_length == -1)
			{
				if (ferror (fp))
					error (0, errno, "warning: cannot read %s", fname);
				break;
			}
			if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
				continue;
			if (offset + line_length >= message_len)
			expand_string (messagep, &message_len, offset + line_length + 1);
			strcpy (*messagep + offset, line);
			offset += line_length;
		}
	}
	
	if (fclose (fp) < 0)
		error (0, errno, "warning: cannot close %s", fname);

	if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL || strcmp (*messagep, "\n") == 0)
	{
		for (;;)
		{
			printf ("\nLog message unchanged or not specified\n");
			printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
			printf ("Action: (continue) ");
			fflush (stderr);
			fflush (stdout);
			line_length = getline (&line, &line_chars_allocated, stdin);
			if (line_length < 0)
			{
				error (0, errno, "cannot read from stdin");
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborting");
			}
			else if (line_length == 0 || *line == '\n' || *line == 'c' || *line == 'C')
				break;
			if (*line == 'a' || *line == 'A')
			{
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborted by user");
			}
			if (*line == 'e' || *line == 'E')
				goto again;
			if (*line == '!')
			{
				reuse_log_message = 1;
				break;
			}
			printf ("Unknown input\n");
		}
    }
    if (line)
		xfree (line);
    if (unlink_file (fname) < 0)
		error (0, errno, "warning: cannot remove temp file %s", fname);
    xfree (fname);
}