|
Packit |
0021fb |
/*
|
|
Packit |
0021fb |
* This file is part of ltrace.
|
|
Packit |
0021fb |
* Copyright (C) 2007,2008,2012,2013 Petr Machata, Red Hat Inc.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is free software; you can redistribute it and/or
|
|
Packit |
0021fb |
* modify it under the terms of the GNU General Public License as
|
|
Packit |
0021fb |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit |
0021fb |
* License, or (at your option) any later version.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
0021fb |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
0021fb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
0021fb |
* General Public License for more details.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* You should have received a copy of the GNU General Public License
|
|
Packit |
0021fb |
* along with this program; if not, write to the Free Software
|
|
Packit |
0021fb |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
Packit |
0021fb |
* 02110-1301 USA
|
|
Packit |
0021fb |
*/
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#include <sys/types.h>
|
|
Packit |
0021fb |
#include <regex.h>
|
|
Packit |
0021fb |
#include <string.h>
|
|
Packit |
0021fb |
#include <stdlib.h>
|
|
Packit |
0021fb |
#include <assert.h>
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static ssize_t
|
|
Packit |
0021fb |
match_character_class(const char *glob, size_t length, size_t from)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert(length > 0);
|
|
Packit |
0021fb |
const char *colon = memchr(glob + from + 2, ':', length - 1);
|
|
Packit |
0021fb |
if (colon == NULL || colon[1] != ']')
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
return colon - glob;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static ssize_t
|
|
Packit |
0021fb |
match_brack(const char *glob, size_t length, size_t from, int *exclmp)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
size_t i = from + 1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (i >= length)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* Complement operator. */
|
|
Packit |
0021fb |
*exclmp = 0;
|
|
Packit |
0021fb |
if (glob[i] == '^' || glob[i] == '!') {
|
|
Packit |
0021fb |
*exclmp = glob[i++] == '!';
|
|
Packit |
0021fb |
if (i >= length)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* On first character, both [ and ] are legal. But when [ is
|
|
Packit |
0021fb |
* followed with :, it's character class. */
|
|
Packit |
0021fb |
if (glob[i] == '[' && glob[i + 1] == ':') {
|
|
Packit |
0021fb |
ssize_t j = match_character_class(glob, length, i);
|
|
Packit |
0021fb |
if (j < 0)
|
|
Packit |
0021fb |
fail:
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
i = j;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
++i; /* skip any character, including [ or ] */
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
for (; i < length; ++i) {
|
|
Packit |
0021fb |
char c = glob[i];
|
|
Packit |
0021fb |
if (c == '[' && glob[i + 1] == ':') {
|
|
Packit |
0021fb |
ssize_t j = match_character_class(glob, length, i);
|
|
Packit |
0021fb |
if (j < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
i = j;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else if (c == ']') {
|
|
Packit |
0021fb |
return i;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static int
|
|
Packit |
0021fb |
append(char **bufp, const char *str, size_t str_size,
|
|
Packit |
0021fb |
size_t *sizep, size_t *allocp)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (str_size == 0)
|
|
Packit |
0021fb |
str_size = strlen(str);
|
|
Packit |
0021fb |
size_t nsize = *sizep + str_size;
|
|
Packit |
0021fb |
if (nsize > *allocp) {
|
|
Packit |
0021fb |
size_t nalloc = nsize * 2;
|
|
Packit |
0021fb |
char *nbuf = realloc(*bufp, nalloc);
|
|
Packit |
0021fb |
if (nbuf == NULL)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
*allocp = nalloc;
|
|
Packit |
0021fb |
*bufp = nbuf;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
memcpy(*bufp + *sizep, str, str_size);
|
|
Packit |
0021fb |
*sizep = nsize;
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static int
|
|
Packit |
0021fb |
glob_to_regex(const char *glob, char **retp)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
size_t allocd = 0;
|
|
Packit |
0021fb |
size_t size = 0;
|
|
Packit |
0021fb |
char *buf = NULL;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
size_t length = strlen(glob);
|
|
Packit |
0021fb |
int escape = 0;
|
|
Packit |
0021fb |
size_t i;
|
|
Packit |
0021fb |
for(i = 0; i < length; ++i) {
|
|
Packit |
0021fb |
char c = glob[i];
|
|
Packit |
0021fb |
if (escape) {
|
|
Packit |
0021fb |
if (c == '\\') {
|
|
Packit |
0021fb |
if (append(&buf, "\\\\", 0,
|
|
Packit |
0021fb |
&size, &allocd) < 0) {
|
|
Packit |
0021fb |
fail:
|
|
Packit |
0021fb |
free(buf);
|
|
Packit |
0021fb |
return REG_ESPACE;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else if (c == '*') {
|
|
Packit |
0021fb |
if (append(&buf, "\\*", 0, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
} else if (c == '?') {
|
|
Packit |
0021fb |
if (append(&buf, "?", 0, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
} else if (append(&buf, (char[]){ '\\', c }, 2,
|
|
Packit |
0021fb |
&size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
escape = 0;
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
if (c == '\\')
|
|
Packit |
0021fb |
escape = 1;
|
|
Packit |
0021fb |
else if (c == '[') {
|
|
Packit |
0021fb |
int exclm;
|
|
Packit |
0021fb |
ssize_t j = match_brack(glob, length, i, &exclm;;
|
|
Packit |
0021fb |
if (j < 0) {
|
|
Packit |
0021fb |
free(buf);
|
|
Packit |
0021fb |
return REG_EBRACK;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
if (exclm
|
|
Packit |
0021fb |
&& append(&buf, "[^", 2,
|
|
Packit |
0021fb |
&size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
if (append(&buf, glob + i + 2*exclm,
|
|
Packit |
0021fb |
j - i + 1 - 2*exclm,
|
|
Packit |
0021fb |
&size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
i = j;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else if (c == '*') {
|
|
Packit |
0021fb |
if (append(&buf, ".*", 0, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
} else if (c == '?') {
|
|
Packit |
0021fb |
if (append(&buf, ".", 0, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
} else if (c == '.') {
|
|
Packit |
0021fb |
if (append(&buf, "\\.", 0, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
} else if (append(&buf, &c, 1, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (escape) {
|
|
Packit |
0021fb |
free(buf);
|
|
Packit |
0021fb |
return REG_EESCAPE;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
char c = 0;
|
|
Packit |
0021fb |
if (append(&buf, &c, 1, &size, &allocd) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
*retp = buf;
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
globcomp(regex_t *preg, const char *glob, int cflags)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
char *regex = NULL;
|
|
Packit |
0021fb |
int status = glob_to_regex(glob, ®ex);
|
|
Packit |
0021fb |
if (status != 0)
|
|
Packit |
0021fb |
return status;
|
|
Packit |
0021fb |
assert(regex != NULL);
|
|
Packit |
0021fb |
status = regcomp(preg, regex, cflags);
|
|
Packit |
0021fb |
free(regex);
|
|
Packit |
0021fb |
return status;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#ifdef TEST
|
|
Packit |
0021fb |
#include <stdio.h>
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
translate(const char *glob, int exp_status, const char *expect)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
char *pattern = NULL;
|
|
Packit |
0021fb |
int status = glob_to_regex(glob, &pattern);
|
|
Packit |
0021fb |
if (status != exp_status) {
|
|
Packit |
0021fb |
fprintf(stderr, "translating %s, expected status %d, got %d\n",
|
|
Packit |
0021fb |
glob, exp_status, status);
|
|
Packit |
0021fb |
return;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (status == 0) {
|
|
Packit |
0021fb |
assert(pattern != NULL);
|
|
Packit |
0021fb |
if (strcmp(pattern, expect) != 0)
|
|
Packit |
0021fb |
fprintf(stderr, "translating %s, expected %s, got %s\n",
|
|
Packit |
0021fb |
glob, expect, pattern);
|
|
Packit |
0021fb |
free(pattern);
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
assert(pattern == NULL);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
try_match(const char *glob, const char *str, int expect)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
regex_t preg;
|
|
Packit |
0021fb |
int status = globcomp(&preg, glob, 0);
|
|
Packit |
0021fb |
assert(status == 0);
|
|
Packit |
0021fb |
status = regexec(&preg, str, 0, NULL, 0);
|
|
Packit |
0021fb |
assert(status == expect);
|
|
Packit |
0021fb |
regfree(&preg;;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
int
|
|
Packit |
0021fb |
main(void)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
translate("*", 0, ".*");
|
|
Packit |
0021fb |
translate("?", 0, ".");
|
|
Packit |
0021fb |
translate(".*", 0, "\\..*");
|
|
Packit |
0021fb |
translate("*.*", 0, ".*\\..*");
|
|
Packit |
0021fb |
translate("*a*", 0, ".*a.*");
|
|
Packit |
0021fb |
translate("[abc]", 0, "[abc]");
|
|
Packit |
0021fb |
translate("[^abc]", 0, "[^abc]");
|
|
Packit |
0021fb |
translate("[!abc]", 0, "[^abc]");
|
|
Packit |
0021fb |
translate("[]]", 0, "[]]");
|
|
Packit |
0021fb |
translate("[[]", 0, "[[]");
|
|
Packit |
0021fb |
translate("[^]]", 0, "[^]]");
|
|
Packit |
0021fb |
translate("[^a-z]", 0, "[^a-z]");
|
|
Packit |
0021fb |
translate("[abc\\]]", 0, "[abc\\]]");
|
|
Packit |
0021fb |
translate("[abc\\]def]", 0, "[abc\\]def]");
|
|
Packit |
0021fb |
translate("[[:space:]]", 0, "[[:space:]]");
|
|
Packit |
0021fb |
translate("[^[:space:]]", 0, "[^[:space:]]");
|
|
Packit |
0021fb |
translate("[![:space:]]", 0, "[^[:space:]]");
|
|
Packit |
0021fb |
translate("[^a-z]*", 0, "[^a-z].*");
|
|
Packit |
0021fb |
translate("[^a-z]bar*", 0, "[^a-z]bar.*");
|
|
Packit |
0021fb |
translate("*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.", 0,
|
|
Packit |
0021fb |
".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\."
|
|
Packit |
0021fb |
".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.");
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
translate("\\", REG_EESCAPE, NULL);
|
|
Packit |
0021fb |
translate("[^[:naotuh\\", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("[^[:", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("[^[", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("[^", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("[\\", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("[", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
translate("abc[", REG_EBRACK, NULL);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
try_match("abc*def", "abc012def", 0);
|
|
Packit |
0021fb |
try_match("abc*def", "ab012def", REG_NOMATCH);
|
|
Packit |
0021fb |
try_match("[abc]*def", "a1def", 0);
|
|
Packit |
0021fb |
try_match("[abc]*def", "b1def", 0);
|
|
Packit |
0021fb |
try_match("[abc]*def", "d1def", REG_NOMATCH);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#endif
|