/*
 * ##/%% variable matching code ripped out of ash shell for code sharing
 *
 * This code is derived from software contributed to Berkeley by
 * Kenneth Almquist.
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 *
 * Copyright (c) 1989, 1991, 1993, 1994
 *      The Regents of the University of California.  All rights reserved.
 *
 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
 * was re-ported from NetBSD and debianized.
 */
#ifdef STANDALONE
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# define FAST_FUNC /* nothing */
# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
#else
# include "libbb.h"
#endif
#include <fnmatch.h>
#include "match.h"

char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
{
	char *loc;
	char *end;
	unsigned len = strlen(string);
	int early_exit;

	/* We can stop the scan early only if the string part
	 * we are matching against is shrinking, and the pattern has
	 * an unquoted "star" at the corresponding end. There are two cases.
	 * Case 1:
	 * "qwerty" does not match against pattern "*zy",
	 * no point in trying to match "werty", "erty" etc:
	 */
	early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');

	if (flags & SCAN_MOVE_FROM_LEFT) {
		loc = string;
		end = string + len + 1;
	} else {
		loc = string + len;
		end = string - 1;
		if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
			/* Case 2:
			 * "qwerty" does not match against pattern "qz*",
			 * no point in trying to match "qwert", "qwer" etc:
			 */
			const char *p = pattern + strlen(pattern);
			if (--p >= pattern && *p == '*') {
				early_exit = 1;
				while (--p >= pattern && *p == '\\')
					early_exit ^= 1;
			}
		}
	}

	while (loc != end) {
		char c;
		int r;

		c = *loc;
		if (flags & SCAN_MATCH_LEFT_HALF) {
			*loc = '\0';
			r = fnmatch(pattern, string, 0);
			*loc = c;
		} else {
			r = fnmatch(pattern, loc, 0);
		}
		if (r == 0) /* match found */
			return loc;
		if (early_exit) {
#ifdef STANDALONE
			printf("(early exit) ");
#endif
			break;
		}

		if (flags & SCAN_MOVE_FROM_LEFT) {
			loc++;
		} else {
			loc--;
		}
	}
	return NULL;
}

#ifdef STANDALONE
int main(int argc, char *argv[])
{
	char *string;
	char *op;
	char *pattern;
	char *loc;

	setvbuf(stdout, NULL, _IONBF, 0);

	if (!argv[1]) {
		puts(
			"Usage: match <test> [test...]\n\n"
			"Where a <test> is the form: <string><op><match>\n"
			"This is to test the shell ${var#val} expression type.\n\n"
			"e.g. `match 'abc#a*'` -> bc"
		);
		return 1;
	}

	while (*++argv) {
		size_t off;
		unsigned scan_flags;

		string = *argv;
		off = strcspn(string, "#%");
		if (!off) {
			printf("invalid format\n");
			continue;
		}
		op = string + off;
		scan_flags = pick_scan(op[0], op[1]);

		printf("'%s': flags:%x, ", string, scan_flags);
		pattern = op + 1;
		if (op[0] == op[1])
			pattern++;
		op[0] = '\0';

		loc = scan_and_match(string, pattern, scan_flags);

		if (scan_flags & SCAN_MATCH_LEFT_HALF) {
			printf("'%s'\n", loc);
		} else {
			if (loc)
				*loc = '\0';
			printf("'%s'\n", string);
		}
	}

	return 0;
}
#endif
