linux/tools/perf/util/string.c
Masami Hiramatsu 6964cd2c8e perf tools: Enhance glob string matching
Enhance strglobmatch() for supporting character classes([CHARS],
complementation and ranges are also supported) and escaped
special characters (\*, \? etc).

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100105224724.19431.56271.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-01-13 10:09:14 +01:00

311 lines
5.4 KiB
C

#include "string.h"
#include "util.h"
static int hex(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
if ((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;
return -1;
}
/*
* While we find nice hex chars, build a long_val.
* Return number of chars processed.
*/
int hex2u64(const char *ptr, u64 *long_val)
{
const char *p = ptr;
*long_val = 0;
while (*p) {
const int hex_val = hex(*p);
if (hex_val < 0)
break;
*long_val = (*long_val << 4) | hex_val;
p++;
}
return p - ptr;
}
char *strxfrchar(char *s, char from, char to)
{
char *p = s;
while ((p = strchr(p, from)) != NULL)
*p++ = to;
return s;
}
#define K 1024LL
/*
* perf_atoll()
* Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
* and return its numeric value
*/
s64 perf_atoll(const char *str)
{
unsigned int i;
s64 length = -1, unit = 1;
if (!isdigit(str[0]))
goto out_err;
for (i = 1; i < strlen(str); i++) {
switch (str[i]) {
case 'B':
case 'b':
break;
case 'K':
if (str[i + 1] != 'B')
goto out_err;
else
goto kilo;
case 'k':
if (str[i + 1] != 'b')
goto out_err;
kilo:
unit = K;
break;
case 'M':
if (str[i + 1] != 'B')
goto out_err;
else
goto mega;
case 'm':
if (str[i + 1] != 'b')
goto out_err;
mega:
unit = K * K;
break;
case 'G':
if (str[i + 1] != 'B')
goto out_err;
else
goto giga;
case 'g':
if (str[i + 1] != 'b')
goto out_err;
giga:
unit = K * K * K;
break;
case 'T':
if (str[i + 1] != 'B')
goto out_err;
else
goto tera;
case 't':
if (str[i + 1] != 'b')
goto out_err;
tera:
unit = K * K * K * K;
break;
case '\0': /* only specified figures */
unit = 1;
break;
default:
if (!isdigit(str[i]))
goto out_err;
break;
}
}
length = atoll(str) * unit;
goto out;
out_err:
length = -1;
out:
return length;
}
/*
* Helper function for splitting a string into an argv-like array.
* originaly copied from lib/argv_split.c
*/
static const char *skip_sep(const char *cp)
{
while (*cp && isspace(*cp))
cp++;
return cp;
}
static const char *skip_arg(const char *cp)
{
while (*cp && !isspace(*cp))
cp++;
return cp;
}
static int count_argc(const char *str)
{
int count = 0;
while (*str) {
str = skip_sep(str);
if (*str) {
count++;
str = skip_arg(str);
}
}
return count;
}
/**
* argv_free - free an argv
* @argv - the argument vector to be freed
*
* Frees an argv and the strings it points to.
*/
void argv_free(char **argv)
{
char **p;
for (p = argv; *p; p++)
free(*p);
free(argv);
}
/**
* argv_split - split a string at whitespace, returning an argv
* @str: the string to be split
* @argcp: returned argument count
*
* Returns an array of pointers to strings which are split out from
* @str. This is performed by strictly splitting on white-space; no
* quote processing is performed. Multiple whitespace characters are
* considered to be a single argument separator. The returned array
* is always NULL-terminated. Returns NULL on memory allocation
* failure.
*/
char **argv_split(const char *str, int *argcp)
{
int argc = count_argc(str);
char **argv = zalloc(sizeof(*argv) * (argc+1));
char **argvp;
if (argv == NULL)
goto out;
if (argcp)
*argcp = argc;
argvp = argv;
while (*str) {
str = skip_sep(str);
if (*str) {
const char *p = str;
char *t;
str = skip_arg(str);
t = strndup(p, str-p);
if (t == NULL)
goto fail;
*argvp++ = t;
}
}
*argvp = NULL;
out:
return argv;
fail:
argv_free(argv);
return NULL;
}
/* Character class matching */
static bool __match_charclass(const char *pat, char c, const char **npat)
{
bool complement = false, ret = true;
if (*pat == '!') {
complement = true;
pat++;
}
if (*pat++ == c) /* First character is special */
goto end;
while (*pat && *pat != ']') { /* Matching */
if (*pat == '-' && *(pat + 1) != ']') { /* Range */
if (*(pat - 1) <= c && c <= *(pat + 1))
goto end;
if (*(pat - 1) > *(pat + 1))
goto error;
pat += 2;
} else if (*pat++ == c)
goto end;
}
if (!*pat)
goto error;
ret = false;
end:
while (*pat && *pat != ']') /* Searching closing */
pat++;
if (!*pat)
goto error;
*npat = pat + 1;
return complement ? !ret : ret;
error:
return false;
}
/**
* strglobmatch - glob expression pattern matching
* @str: the target string to match
* @pat: the pattern string to match
*
* This returns true if the @str matches @pat. @pat can includes wildcards
* ('*','?') and character classes ([CHARS], complementation and ranges are
* also supported). Also, this supports escape character ('\') to use special
* characters as normal character.
*
* Note: if @pat syntax is broken, this always returns false.
*/
bool strglobmatch(const char *str, const char *pat)
{
while (*str && *pat && *pat != '*') {
if (*pat == '?') { /* Matches any single character */
str++;
pat++;
continue;
} else if (*pat == '[') /* Character classes/Ranges */
if (__match_charclass(pat + 1, *str, &pat)) {
str++;
continue;
} else
return false;
else if (*pat == '\\') /* Escaped char match as normal char */
pat++;
if (*str++ != *pat++)
return false;
}
/* Check wild card */
if (*pat == '*') {
while (*pat == '*')
pat++;
if (!*pat) /* Tail wild card matches all */
return true;
while (*str)
if (strglobmatch(str++, pat))
return true;
}
return !*str && !*pat;
}