701e127f7d
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
151 lines
3.2 KiB
C
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
|