/* This file is part of GNU Rush.                  
   Copyright (C) 2008-2024 Sergey Poznyakoff

   GNU Rush 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 3, or (at your option)
   any later version.

   GNU Rush 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 GNU Rush.  If not, see <http://www.gnu.org/licenses/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "wordsplit.h"

enum {
	WILD_FALSE = 0,
	WILD_TRUE,
	WILD_ABORT
};

static int
match_char_class(char const **pexpr, char c)
{
	int res;
	int rc;
	char const *expr = *pexpr;

	expr++;
	if (*expr == '^') {
		res = 0;
		expr++;
	} else
		res = 1;

	if (*expr == '-' || *expr == ']')
		rc = c == *expr++;
	else
		rc = !res;
	
	for (; *expr && *expr != ']'; expr++) {
		if (rc == res) {
			if (*expr == '\\' && expr[1] == ']')
				expr++;
		} else if (expr[1] == '-') {
			if (*expr == '\\')
				rc = *++expr == c;
			else {
				rc = *expr <= c && c <= expr[2];
				expr += 2;
			}
		} else if (*expr == '\\' && expr[1] == ']')
			rc = *++expr == c;
		else
			rc = *expr == c;
	}
	*pexpr = *expr ? expr + 1 : expr;
	return rc == res;
}

#define END_OF_NAME(s,l) ((l) == 0 || *(s) == 0)
#define NEXT_CHAR(s,l) (s++, l--)

int
wilder_match(char const *expr, char const *name, size_t len)
{
        int c;

        while (expr && *expr) {
		if (END_OF_NAME(name, len) && *expr != '*')
			return WILD_ABORT;
                switch (*expr) {
                case '*':
			while (*++expr == '*')
				;
			if (*expr == 0)
				return WILD_TRUE;
			while (!END_OF_NAME(name, len)) {
				int res;
				res = wilder_match(expr, name, len);
				if (res != WILD_FALSE)
					return res;
				NEXT_CHAR(name, len);
			}
                        return WILD_ABORT;
                        
                case '?':
                        expr++;
			NEXT_CHAR(name, len);
                        break;
                        
		case '[':
			if (!match_char_class(&expr, *name))
				return WILD_FALSE;
			NEXT_CHAR(name, len);
			break;
			
                case '\\':
                        if (expr[1]) {
				c = *++expr; expr++;
				if (*name != wordsplit_c_unquote_char(c))
					return WILD_FALSE;
				NEXT_CHAR(name, len);
				break;
			}
			/* fall through */
                default:
			if (*expr != *name)
                                return WILD_FALSE;
                        expr++;
			NEXT_CHAR(name, len);
                }
        }
        return END_OF_NAME(name, len) ? WILD_TRUE : WILD_FALSE;
}

/* Return 0 if first LEN bytes of NAME match globbing pattern EXPR. */
int
wildmatch(char const *expr, char const *name, size_t len)
{
	return wilder_match(expr, name, len) != WILD_TRUE;
}
