Index: gtksourceview/gtksourceiter.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceiter.c,v
retrieving revision 1.11
diff -u -p -r1.11 gtksourceiter.c
--- gtksourceview/gtksourceiter.c	1 Mar 2005 10:44:01 -0000	1.11
+++ gtksourceview/gtksourceiter.c	1 Mar 2005 21:22:44 -0000
@@ -32,185 +32,194 @@
 
 #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC
 
-/* this function acts like g_utf8_offset_to_pointer() except that if it finds a
- * decomposable character it consumes the decomposition length from the given
- * offset.  So it's useful when the offset was calculated for the normalized
- * version of str, but we need a pointer to str itself. */
-static const gchar *
-pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
+static gboolean
+g_utf8_caselessmatch (const gchar *haystack, const gchar *needle, const gchar **end)
 {
-	gchar *casefold, *normal;
-	const gchar *p, *q;
+	gchar *casefold;
+	gchar *normalized_haystack;
+	gchar *normalized_needle;
+	const gchar *p;
+	glong len_nh, len_nn, len;
+	gboolean ret = FALSE;
+
+	casefold = g_utf8_casefold (haystack, -1);
+	normalized_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+	g_free (casefold);
+
+	casefold = g_utf8_casefold (needle, -1);
+	normalized_needle = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+	g_free (casefold);
+
+	len_nh = strlen (normalized_haystack);
+	len_nn = strlen (normalized_needle);
+
+	if (len_nh < len_nn)
+		goto out;
+
+	ret = (strncmp (normalized_haystack, normalized_needle, len_nn) == 0);
+	if (!ret)
+		goto out;
+
+	/* we have a match. Now we must figure out were it ends */
 
-	p = str;
-	while (offset > 0)
+	/* most common case: move after the needle */
+	len = strlen (haystack);
+	p = haystack + MIN (len, len_nn);
+
+	while (TRUE)
 	{
-		q = g_utf8_next_char (p);
-		casefold = g_utf8_casefold (p, q - p);
-		normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-		offset -= g_utf8_strlen (normal, -1);
+		int i;
+
+		/* sanity check */
+		if (p < haystack || p > haystack + len)
+		{
+			g_print ("EEEEeeewwwww!\n");
+			ret = FALSE;
+			goto out;
+		}
+
+		casefold = g_utf8_casefold (haystack, p - haystack);
+		normalized_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
 		g_free (casefold);
-		g_free (normal);
-		p = q;
+
+		i = strncmp (normalized_haystack, normalized_needle, len_nn);
+
+		/* FIXME: are there any weirdo cases where p starts to oscillate between two values? */
+
+		if (i == 0)
+		{
+			*end = p;
+			goto out;
+		}
+		else if (i > 0)
+		{
+			p = g_utf8_prev_char (p);
+		}
+		else
+		{
+			p = g_utf8_next_char (p);
+		}
 	}
-	return p;
+
+out:
+	g_free (normalized_haystack);
+	g_free (normalized_needle);	
+
+	return ret;
 }
 
-static const gchar *
-g_utf8_strcasestr (const gchar *haystack, const gchar *needle)
+static void
+g_utf8_strcasestr (const gchar *haystack,
+		   const gchar *needle,
+		   const gchar **start,
+		   const gchar **end)
 {
-	gsize needle_len;
-	gsize haystack_len;
-	const gchar *ret = NULL;
-	gchar *p;
-	gchar *casefold;
-	gchar *caseless_haystack;
-	gint i;
-
-	g_return_val_if_fail (haystack != NULL, NULL);
-	g_return_val_if_fail (needle != NULL, NULL);
+	const gchar *p;
+	int needle_len;
 
-	casefold = g_utf8_casefold (haystack, -1);
-	caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-	g_free (casefold);
+	g_return_if_fail (haystack != NULL);
+	g_return_if_fail (needle != NULL);
 
 	needle_len = g_utf8_strlen (needle, -1);
-	haystack_len = g_utf8_strlen (caseless_haystack, -1);
 
 	if (needle_len == 0)
 	{
-		ret = (gchar *)haystack;
-		goto finally_1;
+		*start = haystack;
+		*end = haystack + strlen (haystack);
+		return;
 	}
 
-	if (haystack_len < needle_len)
-	{
-		ret = NULL;
-		goto finally_1;
-	}
-
-	p = (gchar*)caseless_haystack;
+	p = haystack;
 	needle_len = strlen (needle);
-	i = 0;
 
 	while (*p)
 	{
-		if ((strncmp (p, needle, needle_len) == 0))
+		const gchar *p_end;
+
+		if (g_utf8_caselessmatch (p, needle, &p_end))
 		{
-			ret = pointer_from_offset_skipping_decomp (haystack, i);
-			goto finally_1;
+			if (start) *start = p;
+			if (end) *end = p_end;
+			return;
 		}
 
 		p = g_utf8_next_char (p);
-		i++;
 	}
 
-finally_1:
-	g_free (caseless_haystack);
-
-	return ret;
+	if (start) *start = NULL;
+	if (end) *end = NULL;
 }
 
-static const gchar *
-g_utf8_strrcasestr (const gchar *haystack, const gchar *needle)
+static const void
+g_utf8_strrcasestr (const gchar *haystack,
+		    const gchar *needle,
+		    const gchar **start,
+		    const gchar **end)
 {
 	gsize needle_len;
 	gsize haystack_len;
-	const gchar *ret = NULL;
-	gchar *p;
 	gchar *casefold;
-	gchar *caseless_haystack;
+	gchar *normalized_haystack;
+	gchar *p;
 	gint i;
 
-	g_return_val_if_fail (haystack != NULL, NULL);
-	g_return_val_if_fail (needle != NULL, NULL);
-
-	casefold = g_utf8_casefold (haystack, -1);
-	caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-	g_free (casefold);
+	g_return_if_fail (haystack != NULL);
+	g_return_if_fail (needle != NULL);
 
 	needle_len = g_utf8_strlen (needle, -1);
-	haystack_len = g_utf8_strlen (caseless_haystack, -1);
 
 	if (needle_len == 0)
 	{
-		ret = (gchar *)haystack;
-		goto finally_1;
+		*start = haystack;
+		*end = haystack + strlen (haystack);
+		return;
 	}
 
-	if (haystack_len < needle_len)
-	{
-		ret = NULL;
-		goto finally_1;
-	}
+	casefold = g_utf8_casefold (haystack, -1);
+	normalized_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+	g_free (casefold);
+	haystack_len = g_utf8_strlen (normalized_haystack, -1);
+	g_free (normalized_haystack);
 
-	i = haystack_len - needle_len;
-	p = g_utf8_offset_to_pointer (caseless_haystack, i);
+	/* If we don't normalize the haystack to initially determine
+	   the search starting position, it's possible that the needle
+	   is decomposed and the haystack is not, which produces an
+	   incorrect initial value for p.
+	   Eg. haystack = "1234ß"; needle = "ss";
+	   While the last character in haystack should match, the
+	   search will begin at the "4" because the length of needle
+	   is 2. To avoid that, we normalize haystack and use the
+	   normalized length to calculate p.
+	   OTOH, i can't be larger than g_utf8_strlen(haystack), since
+	   g_utf8_offset_to_pointer() doesn't check for limits. */
+	
+	i = MIN (MAX (0, haystack_len - needle_len), g_utf8_strlen (haystack, -1));
+	p = g_utf8_offset_to_pointer (haystack, i);
 	needle_len = strlen (needle);
 
-	while (p >= caseless_haystack)
+	while (p >= haystack)
 	{
-		if (strncmp (p, needle, needle_len) == 0)
+		const gchar *p_end;
+
+		if (g_utf8_caselessmatch (p, needle, &p_end))
 		{
-			ret = pointer_from_offset_skipping_decomp (haystack, i);
-			goto finally_1;
+			if (start) *start = p;
+			if (end) *end = p_end;
+			return;
 		}
 
 		p = g_utf8_prev_char (p);
-		i--;
 	}
 
-finally_1:
-	g_free (caseless_haystack);
-
-	return ret;
-}
-
-static gboolean
-g_utf8_caselessnmatch (const char *s1, const char *s2,
-		       gssize n1, gssize n2)
-{
-	gchar *casefold;
-	gchar *normalized_s1;
-	gchar *normalized_s2;
-	gint len_s1;
-	gint len_s2;
-	gboolean ret = FALSE;
-
-	g_return_val_if_fail (s1 != NULL, FALSE);
-	g_return_val_if_fail (s2 != NULL, FALSE);
-	g_return_val_if_fail (n1 > 0, FALSE);
-	g_return_val_if_fail (n2 > 0, FALSE);
-
-	casefold = g_utf8_casefold (s1, n1);
-	normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-	g_free (casefold);
-
-	casefold = g_utf8_casefold (s2, n2);
-	normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-	g_free (casefold);
-
-	len_s1 = strlen (normalized_s1);
-	len_s2 = strlen (normalized_s2);
-
-	if (len_s1 < len_s2)
-		goto finally_2;
-
-	ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
-
-finally_2:
-	g_free (normalized_s1);
-	g_free (normalized_s2);	
-
-	return ret;
+	if (start) *start = NULL;
+	if (end) *end = NULL;
 }
 
 static void
 forward_chars_with_skipping (GtkTextIter *iter,
 			     gint         count,
 			     gboolean     skip_invisible,
-			     gboolean     skip_nontext,
-			     gboolean     skip_decomp)
+			     gboolean     skip_nontext)
 {
 	gint i;
 
@@ -235,22 +244,6 @@ forward_chars_with_skipping (GtkTextIter
 		    /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE)
 			ignored = TRUE;
 
-		if (!ignored && skip_decomp)
-		{
-			/* being UTF8 correct sucks; this accounts for extra
-			   offsets coming from canonical decompositions of
-			   UTF8 characters (e.g. accented characters) which 
-			   g_utf8_normalize() performs */
-			gchar *normal;
-			gchar buffer[6];
-			gint buffer_len;
-
-			buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer);
-			normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_ALL);
-			i -= (g_utf8_strlen (normal, -1) - 1);
-			g_free (normal);
-		}
-
 		gtk_text_iter_forward_char (iter);
 
 		if (!ignored)
@@ -268,7 +261,8 @@ lines_match (const GtkTextIter *start,
 {
 	GtkTextIter next;
 	gchar *line_text;
-	const gchar *found;
+	const gchar *found_start;
+	const gchar *found_end;
 	gint offset;
 
 	if (*lines == NULL || **lines == '\0')
@@ -304,42 +298,50 @@ lines_match (const GtkTextIter *start,
 
 	if (match_start) /* if this is the first line we're matching */
 	{
-		found = g_utf8_strcasestr (line_text, *lines);
+		g_utf8_strcasestr (line_text, *lines, &found_start, &found_end);
 	}
 	else
 	{
 		/* If it's not the first line, we have to match from the
 		 * start of the line.
 		 */
-		if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
-					   strlen (*lines)))
-			found = line_text;
+		const gchar *line_text_end;
+		
+		if (g_utf8_caselessmatch (line_text, *lines, &line_text_end))
+		{
+			found_start = line_text;
+			found_end = line_text_end;
+		}
 		else
-			found = NULL;
+		{
+			found_start = NULL;
+			found_end = NULL;
+		}
 	}
 
-	if (found == NULL)
+	if (found_start == NULL)
 	{
 		g_free (line_text);
 		return FALSE;
 	}
 
 	/* Get offset to start of search string */
-	offset = g_utf8_strlen (line_text, found - line_text);
+	offset = g_utf8_strlen (line_text, found_start - line_text);
 
 	next = *start;
 
 	/* If match start needs to be returned, set it to the
 	 * start of the search string.
 	 */
-	forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
+	forward_chars_with_skipping (&next, offset, visible_only, !slice);
 	if (match_start)
 	{
 		*match_start = next;
 	}
 
 	/* Go to end of search string */
-	forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
+	offset = g_utf8_strlen (found_start, found_end - found_start);
+	forward_chars_with_skipping (&next, offset, visible_only, !slice);
 
 	g_free (line_text);
 
@@ -364,7 +366,8 @@ backward_lines_match (const GtkTextIter 
 {
 	GtkTextIter line, next;
 	gchar *line_text;
-	const gchar *found;
+	const gchar *found_start;
+	const gchar *found_end;
 	gint offset;
 
 	if (*lines == NULL || **lines == '\0')
@@ -402,30 +405,38 @@ backward_lines_match (const GtkTextIter 
 
 	if (match_start) /* if this is the first line we're matching */
 	{
-		found = g_utf8_strrcasestr (line_text, *lines);
+		g_utf8_strrcasestr (line_text, *lines, &found_start, &found_end);
 	}
 	else
 	{
 		/* If it's not the first line, we have to match from the
 		 * start of the line.
 		 */
-		if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
-					   strlen (*lines)))
-			found = line_text;
+
+		const gchar *line_text_end;
+
+		if (g_utf8_caselessmatch (line_text, *lines, &line_text_end))
+		{
+			found_start = line_text;
+			found_end = line_text_end;
+		}
 		else
-			found = NULL;
+		{
+			found_start = NULL;
+			found_end = NULL;
+		}
 	}
 
-	if (found == NULL)
+	if (found_start == NULL)
 	{
 		g_free (line_text);
 		return FALSE;
 	}
 
 	/* Get offset to start of search string */
-	offset = g_utf8_strlen (line_text, found - line_text);
+	offset = g_utf8_strlen (line_text, found_start - line_text);
 
-	forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
+	forward_chars_with_skipping (&next, offset, visible_only, !slice);
 
 	/* If match start needs to be returned, set it to the
 	 * start of the search string.
@@ -436,7 +447,8 @@ backward_lines_match (const GtkTextIter 
 	}
 
 	/* Go to end of search string */
-	forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
+	offset = g_utf8_strlen (found_start, found_end - found_start);
+	forward_chars_with_skipping (&next, offset, visible_only, !slice);
 
 	g_free (line_text);
 
@@ -459,7 +471,7 @@ strbreakup (const char *string,
 	    gint        max_tokens)
 {
 	GSList *string_list = NULL, *slist;
-	gchar **str_array, *s, *casefold, *new_string;
+	gchar **str_array, *s, *new_string;
 	guint i, n = 1;
 
 	g_return_val_if_fail (string != NULL, NULL);
@@ -481,24 +493,18 @@ strbreakup (const char *string,
 			new_string = g_new (gchar, len + 1);
 			strncpy (new_string, string, len);
 			new_string[len] = 0;
-			casefold = g_utf8_casefold (new_string, -1);
-			g_free (new_string);
-			new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-			g_free (casefold);
 			string_list = g_slist_prepend (string_list, new_string);
 			n++;
 			string = s + delimiter_len;
 			s = strstr (string, delimiter);
-		} while (--max_tokens && s);
+		}
+		while (--max_tokens && s);
 	}
 
 	if (*string)
 	{
 		n++;
-		casefold = g_utf8_casefold (string, -1);
-		new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
-		g_free (casefold);
-		string_list = g_slist_prepend (string_list, new_string);
+		string_list = g_slist_prepend (string_list, g_strdup (string));
 	}
 
 	str_array = g_new (gchar*, n);
