Skip to content

Commit 3b5ef0e

Browse files
committed
xutils: Fix xdl_recmatch() on incomplete lines
Thell Fowler noticed that various "ignore whitespace" options to git diff do not work well on an incomplete line. The loop control of the function responsible for these bugs was extremely difficult to follow. This patch restructures the loops for three variants of "ignore whitespace" logic. The basic idea of the re-written logic is: - A loop runs while the characters from both strings we are looking at match. We declare unmatch immediately when we find something that does not match and return false from the function. We break out of the loop if we ran out of either side of the string. The way we skip spaces inside this loop varies depending on the style of ignoring whitespaces. - After the above loop breaks, we know that the parts of the strings we inspected so far match, ignoring the whitespaces. The lines can match only if the remainder consists of nothing but whitespaces. This part of the logic is shared across all three styles. The new code is more obvious and should be much easier to follow. Tested-by: Thell Fowler <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 78ed710 commit 3b5ef0e

File tree

1 file changed

+49
-31
lines changed

1 file changed

+49
-31
lines changed

xdiff/xutils.c

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -190,48 +190,66 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
190190
{
191191
int i1, i2;
192192

193+
if (!(flags & XDF_WHITESPACE_FLAGS))
194+
return s1 == s2 && !memcmp(l1, l2, s1);
195+
196+
i1 = 0;
197+
i2 = 0;
198+
199+
/*
200+
* -w matches everything that matches with -b, and -b in turn
201+
* matches everything that matches with --ignore-space-at-eol.
202+
*
203+
* Each flavor of ignoring needs different logic to skip whitespaces
204+
* while we have both sides to compare.
205+
*/
193206
if (flags & XDF_IGNORE_WHITESPACE) {
194-
for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
195-
if (isspace(l1[i1]))
196-
while (isspace(l1[i1]) && i1 < s1)
197-
i1++;
198-
if (isspace(l2[i2]))
199-
while (isspace(l2[i2]) && i2 < s2)
200-
i2++;
201-
if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
207+
goto skip_ws;
208+
while (i1 < s1 && i2 < s2) {
209+
if (l1[i1++] != l2[i2++])
202210
return 0;
211+
skip_ws:
212+
while (i1 < s1 && isspace(l1[i1]))
213+
i1++;
214+
while (i2 < s2 && isspace(l2[i2]))
215+
i2++;
203216
}
204-
return (i1 >= s1 && i2 >= s2);
205217
} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
206-
for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
207-
if (isspace(l1[i1])) {
208-
if (!isspace(l2[i2]))
209-
return 0;
210-
while (isspace(l1[i1]) && i1 < s1)
211-
i1++;
212-
while (isspace(l2[i2]) && i2 < s2)
213-
i2++;
214-
} else if (l1[i1++] != l2[i2++])
215-
return 0;
216-
}
217-
return (i1 >= s1 && i2 >= s2);
218-
} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
219-
for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
220-
if (l1[i1] != l2[i2]) {
218+
while (i1 < s1 && i2 < s2) {
219+
if (isspace(l1[i1]) && isspace(l2[i2])) {
220+
/* Skip matching spaces and try again */
221221
while (i1 < s1 && isspace(l1[i1]))
222222
i1++;
223223
while (i2 < s2 && isspace(l2[i2]))
224224
i2++;
225-
if (i1 < s1 || i2 < s2)
226-
return 0;
227-
return 1;
225+
continue;
228226
}
227+
if (l1[i1++] != l2[i2++])
228+
return 0;
229+
}
230+
} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
231+
while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
232+
; /* keep going */
233+
}
234+
235+
/*
236+
* After running out of one side, the remaining side must have
237+
* nothing but whitespace for the lines to match. Note that
238+
* ignore-whitespace-at-eol case may break out of the loop
239+
* while there still are characters remaining on both lines.
240+
*/
241+
if (i1 < s1) {
242+
while (i1 < s1 && isspace(l1[i1]))
229243
i1++;
244+
if (s1 != i1)
245+
return 0;
246+
}
247+
if (i2 < s2) {
248+
while (i2 < s2 && isspace(l2[i2]))
230249
i2++;
231-
}
232-
return i1 >= s1 && i2 >= s2;
233-
} else
234-
return s1 == s2 && !memcmp(l1, l2, s1);
250+
return (s2 == i2);
251+
}
252+
return 1;
235253
}
236254

237255
static unsigned long xdl_hash_record_with_whitespace(char const **data,

0 commit comments

Comments
 (0)