sh: Make patmatch() non-recursive.

This commit is contained in:
Jilles Tjoelker 2012-01-01 20:50:19 +00:00
parent 8f8d30274a
commit 820491f824
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=229201

View File

@ -1445,57 +1445,63 @@ int
patmatch(const char *pattern, const char *string, int squoted) patmatch(const char *pattern, const char *string, int squoted)
{ {
const char *p, *q, *end; const char *p, *q, *end;
const char *bt_p, *bt_q;
char c; char c;
wchar_t wc, wc2; wchar_t wc, wc2;
p = pattern; p = pattern;
q = string; q = string;
bt_p = NULL;
bt_q = NULL;
for (;;) { for (;;) {
switch (c = *p++) { switch (c = *p++) {
case '\0': case '\0':
goto breakloop; if (*q != '\0')
goto backtrack;
return 1;
case CTLESC: case CTLESC:
if (squoted && *q == CTLESC) if (squoted && *q == CTLESC)
q++; q++;
if (*q++ != *p++) if (*q++ != *p++)
return 0; goto backtrack;
break; break;
case CTLQUOTEMARK: case CTLQUOTEMARK:
continue; continue;
case '?': case '?':
if (squoted && *q == CTLESC) if (squoted && *q == CTLESC)
q++; q++;
if (localeisutf8) if (*q == '\0')
wc = get_wc(&q);
else
wc = (unsigned char)*q++;
if (wc == '\0')
return 0; return 0;
if (localeisutf8) {
wc = get_wc(&q);
/*
* A '?' does not match invalid UTF-8 but a
* '*' does, so backtrack.
*/
if (wc == 0)
goto backtrack;
} else
wc = (unsigned char)*q++;
break; break;
case '*': case '*':
c = *p; c = *p;
while (c == CTLQUOTEMARK || c == '*') while (c == CTLQUOTEMARK || c == '*')
c = *++p; c = *++p;
if (c != CTLESC && c != CTLQUOTEMARK && /*
c != '?' && c != '*' && c != '[') { * If the pattern ends here, we know the string
while (*q != c) { * matches without needing to look at the rest of it.
if (squoted && *q == CTLESC && */
q[1] == c) if (c == '\0')
break; return 1;
if (*q == '\0') /*
return 0; * First try the shortest match for the '*' that
if (squoted && *q == CTLESC) * could work. We can forget any earlier '*' since
q++; * there is no way having it match more characters
q++; * can help us, given that we are already here.
} */
} bt_p = p;
do { bt_q = q;
if (patmatch(p, q, squoted)) break;
return 1;
if (squoted && *q == CTLESC)
q++;
} while (*q++ != '\0');
return 0;
case '[': { case '[': {
const char *endp; const char *endp;
int invert, found; int invert, found;
@ -1507,7 +1513,7 @@ patmatch(const char *pattern, const char *string, int squoted)
for (;;) { for (;;) {
while (*endp == CTLQUOTEMARK) while (*endp == CTLQUOTEMARK)
endp++; endp++;
if (*endp == '\0') if (*endp == 0)
goto dft; /* no matching ] */ goto dft; /* no matching ] */
if (*endp == CTLESC) if (*endp == CTLESC)
endp++; endp++;
@ -1522,12 +1528,14 @@ patmatch(const char *pattern, const char *string, int squoted)
found = 0; found = 0;
if (squoted && *q == CTLESC) if (squoted && *q == CTLESC)
q++; q++;
if (localeisutf8) if (*q == '\0')
chr = get_wc(&q);
else
chr = (unsigned char)*q++;
if (chr == '\0')
return 0; return 0;
if (localeisutf8) {
chr = get_wc(&q);
if (chr == 0)
goto backtrack;
} else
chr = (unsigned char)*q++;
c = *p++; c = *p++;
do { do {
if (c == CTLQUOTEMARK) if (c == CTLQUOTEMARK)
@ -1568,21 +1576,34 @@ patmatch(const char *pattern, const char *string, int squoted)
} }
} while ((c = *p++) != ']'); } while ((c = *p++) != ']');
if (found == invert) if (found == invert)
return 0; goto backtrack;
break; break;
} }
dft: default: dft: default:
if (squoted && *q == CTLESC) if (squoted && *q == CTLESC)
q++; q++;
if (*q++ != c) if (*q == '\0')
return 0; return 0;
if (*q++ == c)
break;
backtrack:
/*
* If we have a mismatch (other than hitting the end
* of the string), go back to the last '*' seen and
* have it match one additional character.
*/
if (bt_p == NULL)
return 0;
if (squoted && *bt_q == CTLESC)
bt_q++;
if (*bt_q == '\0')
return 0;
bt_q++;
p = bt_p;
q = bt_q;
break; break;
} }
} }
breakloop:
if (*q != '\0')
return 0;
return 1;
} }