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