/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */

#ifndef _XS_MATCH_H

#define _XS_MATCH_H

/* spec is very similar to shell file globbing:
   an * matches anything;
   a ? matches any character;
   | select alternative strings to match;
   a \\ escapes a special character;
   any other char matches itself. */

int xs_match(const char *str, const char *spec);

#ifdef XS_IMPLEMENTATION

int xs_match(const char *str, const char *spec)
{
    const char *b_str;
    const char *b_spec = NULL;
    const char *o_str  = str;

retry:

    for (;;) {
        char c = *str++;
        char p = *spec++;

        if (c == '\0') {
            /* end of string; also end of spec? */
            if (p == '\0' || p == '|')
                return 1;
            else
                break;
        }
        else
        if (p == '?') {
            /* match anything except the end */
            if (c == '\0')
                return 0;
        }
        else
        if (p == '*') {
            /* end of spec? match */
            if (*spec == '\0')
                return 1;

            /* store spec for later */
            b_spec = spec;

            /* back one char */
            b_str  = --str;
        }
        else {
            if (p == '\\')
                p = *spec++;

            if (c != p) {
                /* mismatch; do we have a backtrack? */
                if (b_spec) {
                    /* continue where we left, one char forward */
                    spec = b_spec;
                    str  = ++b_str;
                }
                else
                    break;
            }
        }
    }

    /* try to find an alternative mark */
    while (*spec) {
        char p = *spec++;

        if (p == '\\')
            p = *spec++;

        if (p == '|') {
            /* no backtrack spec, restart str from the beginning */
            b_spec = NULL;
            str    = o_str;

            goto retry;
        }
    }

    return 0;
}

#endif /* XS_IMPLEMENTATION */

#endif /* XS_MATCH_H */
