busybox/shell/match.c
Denys Vlasenko 701e127f7d hush: optimize #[#] and %[%] for speed. size -2 bytes.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2010-09-04 21:21:07 +02:00

151 lines
3.2 KiB
C

/*
* ##/%% variable matching code ripped out of ash shell for code sharing
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized.
*/
#ifdef STANDALONE
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# define FAST_FUNC /* nothing */
# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
#else
# include "libbb.h"
#endif
#include <fnmatch.h>
#include "match.h"
#define pmatch(a, b) !fnmatch((a), (b), 0)
char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
{
char *loc;
char *end;
unsigned len = strlen(string);
int early_exit;
/* We can stop the scan early only if the string part
* we are matching against is shrinking, and the pattern has
* an unquoted "star" at the corresponding end. There are two cases.
* Case 1:
* "qwerty" does not match against pattern "*zy",
* no point in trying to match "werty", "erty" etc:
*/
early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
if (flags & SCAN_MOVE_FROM_LEFT) {
loc = string;
end = string + len + 1;
} else {
loc = string + len;
end = string - 1;
if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
/* Case 2:
* "qwerty" does not match against pattern "qz*",
* no point in trying to match "qwert", "qwer" etc:
*/
const char *p = pattern + strlen(pattern);
if (--p >= pattern && *p == '*') {
early_exit = 1;
while (--p >= pattern && *p == '\\')
early_exit ^= 1;
}
}
}
while (loc != end) {
char c;
int match;
c = *loc;
if (flags & SCAN_MATCH_LEFT_HALF) {
*loc = '\0';
match = pmatch(pattern, string);
*loc = c;
} else {
match = pmatch(pattern, loc);
}
if (match)
return loc;
if (early_exit) {
#ifdef STANDALONE
printf("(early exit) ");
#endif
break;
}
if (flags & SCAN_MOVE_FROM_LEFT) {
loc++;
} else {
loc--;
}
}
return NULL;
}
#ifdef STANDALONE
int main(int argc, char *argv[])
{
char *string;
char *op;
char *pattern;
char *loc;
setvbuf(stdout, NULL, _IONBF, 0);
if (!argv[1]) {
puts(
"Usage: match <test> [test...]\n\n"
"Where a <test> is the form: <string><op><match>\n"
"This is to test the shell ${var#val} expression type.\n\n"
"e.g. `match 'abc#a*'` -> bc"
);
return 1;
}
while (*++argv) {
size_t off;
unsigned scan_flags;
string = *argv;
off = strcspn(string, "#%");
if (!off) {
printf("invalid format\n");
continue;
}
op = string + off;
scan_flags = pick_scan(op[0], op[1]);
printf("'%s': flags:%x, ", string, scan_flags);
pattern = op + 1;
if (op[0] == op[1])
pattern++;
op[0] = '\0';
loc = scan_and_match(string, pattern, scan_flags);
if (scan_flags & SCAN_MATCH_LEFT_HALF) {
printf("'%s'\n", loc);
} else {
if (loc)
*loc = '\0';
printf("'%s'\n", string);
}
}
return 0;
}
#endif