diff_find_change(
    win_T	*wp,
    linenr_T	lnum,
    int		*startp,	// first char of the change
    int		*endp)		// last char of the change
{
    char_u	*line_org;
    char_u	*line_new;
    int		i;
    int		si_org, si_new;
    int		ei_org, ei_new;
    diff_T	*dp;
    int		idx;
    int		off;
    int		added = TRUE;
    char_u	*p1, *p2;
    int		l;

    // Make a copy of the line, the next ml_get() will invalidate it.
    line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE));
    if (line_org == NULL)
	return FALSE;

    idx = diff_buf_idx(wp->w_buffer);
    if (idx == DB_COUNT)	// cannot happen
    {
	vim_free(line_org);
	return FALSE;
    }

    // search for a change that includes "lnum" in the list of diffblocks.
    FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp)
	if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
	    break;
    if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL)
    {
	vim_free(line_org);
	return FALSE;
    }

    off = lnum - dp->df_lnum[idx];

    for (i = 0; i < DB_COUNT; ++i)
	if (curtab->tp_diffbuf[i] != NULL && i != idx)
	{
	    // Skip lines that are not in the other change (filler lines).
	    if (off >= dp->df_count[i])
		continue;
	    added = FALSE;
	    line_new = ml_get_buf(curtab->tp_diffbuf[i],
						 dp->df_lnum[i] + off, FALSE);

	    // Search for start of difference
	    si_org = si_new = 0;
	    while (line_org[si_org] != NUL)
	    {
		if (((diff_flags & DIFF_IWHITE)
			    && VIM_ISWHITE(line_org[si_org])
					      && VIM_ISWHITE(line_new[si_new]))
			|| ((diff_flags & DIFF_IWHITEALL)
			    && (VIM_ISWHITE(line_org[si_org])
					    || VIM_ISWHITE(line_new[si_new]))))
		{
		    si_org = (int)(skipwhite(line_org + si_org) - line_org);
		    si_new = (int)(skipwhite(line_new + si_new) - line_new);
		}
		else
		{
		    if (!diff_equal_char(line_org + si_org, line_new + si_new,
									   &l))
			break;
		    si_org += l;
		    si_new += l;
		}
	    }
	    if (has_mbyte)
	    {
		// Move back to first byte of character in both lines (may
		// have "nn^" in line_org and "n^ in line_new).
		si_org -= (*mb_head_off)(line_org, line_org + si_org);
		si_new -= (*mb_head_off)(line_new, line_new + si_new);
	    }
	    if (*startp > si_org)
		*startp = si_org;

	    // Search for end of difference, if any.
	    if (line_org[si_org] != NUL || line_new[si_new] != NUL)
	    {
		ei_org = (int)STRLEN(line_org);
		ei_new = (int)STRLEN(line_new);
		while (ei_org >= *startp && ei_new >= si_new
						&& ei_org >= 0 && ei_new >= 0)
		{
		    if (((diff_flags & DIFF_IWHITE)
				&& VIM_ISWHITE(line_org[ei_org])
					      && VIM_ISWHITE(line_new[ei_new]))
			    || ((diff_flags & DIFF_IWHITEALL)
				&& (VIM_ISWHITE(line_org[ei_org])
					    || VIM_ISWHITE(line_new[ei_new]))))
		    {
			while (ei_org >= *startp
					     && VIM_ISWHITE(line_org[ei_org]))
			    --ei_org;
			while (ei_new >= si_new
					     && VIM_ISWHITE(line_new[ei_new]))
			    --ei_new;
		    }
		    else
		    {
			p1 = line_org + ei_org;
			p2 = line_new + ei_new;
			p1 -= (*mb_head_off)(line_org, p1);
			p2 -= (*mb_head_off)(line_new, p2);
			if (!diff_equal_char(p1, p2, &l))
			    break;
			ei_org -= l;
			ei_new -= l;
		    }
		}
		if (*endp < ei_org)
		    *endp = ei_org;
	    }
	}

    vim_free(line_org);
    return added;
}