Blame glob.c

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, &regex);
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