I send new version according to your comments On Fri, May 01, 2009 at 02:51:48PM +0200, Bruno Haible wrote: > > Hmm, well, users are expecting a speedup through your module always. If it > some (not too rare) cases the glibc fnmatch is faster, it's difficult to > convince people to use your function. In most cases we are slower because I fallback to fnmatch. I added this for convience that you dont need check if pattern can be compiled. Now I added fnmatch_fallbacked to check this and if performance is concern you could call fnmatch manualy.
> > The general and hard case is the multibyte encoding case, where the encoding > is something like GB18030, BIG5, or similar. Single-byte encodings may be > worth optimizing for, because the C locale on glibc systems uses a single-byte > encoding. Whether UTF-8 is worth special-casing, depends on the complexity. > I think I can process these encodings when encoding is stateless. I try my code, If I fail I know string doesnt contain pattern. If I succeed I call fnmatch to check it isn't false positive. Also - in bracket is solved by replacing bracket by ? and checking false positives. > > +struct _fnmatch_state; > > +typedef struct _fnmatch_state fnmatch_state; > > Is it really a "state", i.e. a data structure that is modified over and > over again? I would have expected a compiled pattern data structure, that > is created by fnmatch_compile and then left unchanged until fnmatch_free. I named it state because it captures state of underlying NFA. Do you have better name? > > > +#include <stdint.h> > > + > > +#define _GNU_SOURCE > > "#define GNU_SOURCE" has no effect after a system include file such as > <stdint.h> > was already included. Also, "#define _GNU_SOURCE" has no effect on Solaris. > For this reason, gnulib has a module 'extensions' which does the right thing > (and which you're already using). I copied this from fnmatch source. If I dont define FNM_EXTMATCH and so is undefined. How do it correctly? > > + uint32_t hash[8]; > > For better portability, write (UCHAR_MAX / 32) + 1 instead of 8. Ok but 16b char(if this exist) could make table too big. diff --git a/MODULES.html.sh b/MODULES.html.sh index 06afa2d..03d308e 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -61,6 +61,7 @@ fenv float fmtmsg fnmatch +fnmatchcomp ftw glob grp @@ -418,6 +419,7 @@ fmodf fmodl fmtmsg fnmatch +fnmatchcomp fopen fork fpathconf diff --git a/lib/fnmatchcomp.c b/lib/fnmatchcomp.c new file mode 100644 index 0000000..2709e02 --- /dev/null +++ b/lib/fnmatchcomp.c @@ -0,0 +1,526 @@ + /* Copyright (C) 2009 + Ondrej Bilka < nel...@seznam.cz > + + This program is free software: you can redistribute it and/or modif y + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see < http:// www.gnu.org/licenses/ > . */ + +#include <stdint.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <nl_types.h> +#include <langinfo.h> +#include <locale.h> +#include <ctype.h> + +#define _GNU_SOURCE +#include <fnmatch.h> +#include "fnmatchcomp.h" + +#define UINT4 uint32_t +#define UINT2 uint16_t +#define CHARSPERINT (sizeof (UINT4)/sizeof(char)) +#define STREQ(a, b) (strcmp(a, b) == 0) +#define MAX_PATTERN_SIZE (2*sizeof (bracket_pattern)) +#define NULTEST(...) if (__builtin_expect(!(__VA_ARGS__),0)) return NULL; +#define NULTEST2(...) if (__builtin_expect(!(__VA_ARGS__),0)) return -1; +typedef struct +{ + char tp; + char chr; +} chr_pattern; +typedef struct +{ + char tp; + char chars[CHARSPERINT]; + short sizerest; + short charno; +} star_pattern; +typedef struct +{ + char tp; +} end_pattern; +typedef struct +{ + char tp; +} endstar_pattern; +typedef struct +{ + char tp; + char flags; + char *chars; + int sizerest; + uint32_t hash[(UCHAR_MAX / 32) + 1]; +} bracket_pattern; +typedef struct +{ + char tp; +} begindot_pattern; +typedef struct +{ + char tp; +} slashend_pattern; +typedef struct +{ + char tp; + char *ptr; + char *prefix; + int flags; +} fallback_pattern; +typedef struct +{ + char tp; + void *states; +} fold_pattern; +typedef struct +{ + char tp; + void *states; + fallback_pattern *fallback; +} checkneeded_pattern; + +typedef star_pattern starslash_pattern; +enum PATTERNS +{ + PATTERN_chr, + PATTERN_star, + PATTERN_starslash, + PATTERN_end, + PATTERN_endstar, + PATTERN_bracket, + PATTERN_begindot, + PATTERN_slashend, + PATTERN_fallback, + PATTERN_fold, + PATTERN_checkneeded +}; +union _patterns +{ + chr_pattern chr; + star_pattern star; + starslash_pattern starslash; + end_pattern end; + endstar_pattern endstar; + bracket_pattern bracket; + begindot_pattern begindot; + fallback_pattern fallback; + fold_pattern fold; + checkneeded_pattern checkneeded; +}; +typedef union _patterns patterns; +static void freepatterns (patterns * p); + +struct states +{ + patterns *p; + struct states *next; +}; +struct _fnmatch_state +{ + struct states *states; + int flags; + int *refcount; + patterns *start; +}; + +int fnmatch_fallbacked(fnmatch_state * s){ + return (s->states->p.tp == PATTERN_fallback); +} +static int +ascii_compatible_encoding (const char *c) +{ + if (MB_CUR_MAX == 1 || STREQ (c, "UTF-8")) + return 0; + if (STREQ (c, "BIG5") || STREQ (c, "GB18030")) + return 1; + return 2; +} + +static void +strfold (const char *str, char *buf) +{ + while (*str) + *buf++ = tolower (*str++); + *buf = 0; +} + +static fnmatch_state * +initfnmatchstate () +{ + fnmatch_state *st; + NULTEST (st = (fnmatch_state *) malloc (sizeof (fnmatch_state))); + NULTEST (st->states = malloc (sizeof (struct states))); + st->states->next = NULL; + NULTEST (st->refcount = malloc (sizeof (int))); + *st->refcount = 1; + return st; +} + +static fnmatch_state * +fnmatch_fallback (const char *str, int flags) +{ + fnmatch_state *st; + NULTEST (st = initfnmatchstate ()); + NULTEST (st->start = st->states->p = malloc (sizeof (fallback_pattern))); + st->states->p->fallback.tp = PATTERN_fallback; + NULTEST (st->states->p->fallback.ptr = strdup (str)); + st->states->p->fallback.flags = flags; + NULTEST (st->states->p->fallback.prefix = strdup ("")); + return st; +} + +static fnmatch_state * +fold_fallback (fnmatch_state * s) +{ + fnmatch_state *st; + NULTEST (s); + NULTEST (st = initfnmatchstate ()); + NULTEST (st->start = st->states->p = malloc (sizeof (fold_pattern))); + st->states->p->fold.tp = PATTERN_fold; + st->states->p->fold.states = (void *) s; + return st; +} + +static fnmatch_state * +checkneeded_fallback (fnmatch_state * s, fnmatch_state * fb) +{ + NULTEST (s); + NULTEST (fb); + fnmatch_state *st; + NULTEST (st = initfnmatchstate ()); + NULTEST (st->start = st->states->p = malloc (sizeof (checkneeded_pattern))); + st->states->p->checkneeded.tp = PATTERN_checkneeded; + st->states->p->checkneeded.states = (void *) s; + st->states->p->checkneeded.fallback = (void *) fb; + return st; +} + +static int +parse_bracket (const char *str, patterns * pat, int flags) +{ + char *chr, lastbyte; + int mlen; + const char *s = str; + int i; + pat->bracket.tp = PATTERN_bracket; + if (*s == '!' || (getenv ("POSIXLY_CORRECT") != NULL && *s == '^')) + { + pat->bracket.flags = 1; + s++; + } + + if (!pat->bracket.chars) + { + if (!(pat->bracket.chars = malloc (2 * strlen (s) + 2))) + return -3; + } + chr = pat->bracket.chars; + do + { + if (*s == 0 || *s == '-') + pat->bracket.flags = 5; + if (*s == '[' + && (*(s + 1) == ':' || *(s + 1) == '=' || *(s + 1) == '.')) + return -1; + mlen = mblen (s, MB_CUR_MAX); + lastbyte = s[mlen - 1]; + pat->bracket.hash[lastbyte / 32] |= 1 << (lastbyte % 32); + memcpy (chr, s, mlen); + s += mlen; + chr += mlen + 1; + } + while (*s && *s != ']'); + *chr = 0; + if (pat->bracket.flags == 5) + { /* bracket is too complicated to process here + we replace it with ? and when we match entire pattern + we call fnmatch it wasn't false positive */ + *pat->bracket.chars = 0; + for (i = 0; i < 8; i++) + pat->bracket.hash[i] = 0; + } + return s - str; +} + +#define NEXTPATTERN(type) pat->chr.tp = PATTERN_##type; pat = (patterns *)(((void *) pat)+sizeof (type##_pattern)); +#define FALLBACK freepatterns(ret); free(ret); +#define BRACTEST(...) if ((__VA_ARGS__) == -3) {FALLBACK; return NULL; } +fnmatch_state * +fnmatch_compile (const char *str, int flags) +{ + const char *s; + patterns *pat, *ret; + int i, pass; + int size = 0, patsize; + int checkneeded = 0; + char *codeset = nl_langinfo (CODESET); + char *buf; + if (ascii_compatible_encoding (codeset) == 1) + checkneeded = 1; + if (ascii_compatible_encoding (codeset) == 2) + { + return fnmatch_fallback (str, flags); + } + if (flags & FNM_CASEFOLD) + { + if (MB_CUR_MAX != 1) + return fnmatch_fallback (str, flags); + NULTEST (buf = malloc (2 * strlen (str))); + strfold (str, buf); + fnmatch_state *st = + fold_fallback (fnmatch_compile (buf, flags & (!FNM_CASEFOLD))); + NULTEST (st); + free (buf); + if (((fnmatch_state *) st->start->fold.states)->start->chr.tp + == PATTERN_fallback) + { + fnmatch_free (st); + return fnmatch_fallback (str, flags); + } + return st; + } + + ret = (patterns *) calloc (1, strlen (str) * MAX_PATTERN_SIZE); + + for (pass = 0; pass < 2; pass++) + { + /* two passes in first we compute values we use in second to optimize + */ + patsize = size; + size = 0; + pat = ret; + if (flags & FNM_PERIOD && *str != '.') + { + NEXTPATTERN (begindot)} + for (s = str; *s; s++) + { + if (flags & FNM_EXTMATCH && *(s + 1) == '(' && + (*s == '*' || *s == '+' || *s == '@' || *s == '!')) + { + FALLBACK; + return fnmatch_fallback (str, flags); + } + switch (*s) + { + case '*': + while (*(s + 1) == '?') + { + BRACTEST (parse_bracket ("!", pat, flags)); + size++; + pat->bracket.sizerest = patsize - size; + NEXTPATTERN (bracket); + s++; + } + if (*(s + 1)) + { + if (pass) + { + patterns *tmppat = pat; + pat->star.sizerest = patsize - size; + NEXTPATTERN (star); + for (i = 0; pat->chr.tp == PATTERN_chr && pat->chr.chr + && i < CHARSPERINT; i++) + { + tmppat->star.chars[i] = pat->chr.chr; + NEXTPATTERN (chr); + } + if (i == 3) + i = 2; + tmppat->star.charno = i; + pat = tmppat; + } + if (flags & FNM_PATHNAME) + { + NEXTPATTERN (starslash); + } + else + { + NEXTPATTERN (star); + } + } + else + { + NEXTPATTERN (endstar); + } + break; + case '?': + BRACTEST (parse_bracket ("!", pat, flags)); + size++; + pat->bracket.sizerest = patsize - size; + NEXTPATTERN (bracket); + break; + case '[': + { + int siz = parse_bracket (s + 1, pat, flags); + BRACTEST (siz); + if (siz < 0) + { + FALLBACK; + return fnmatch_fallback (str, flags); + } + size++; + s += siz + 1; + pat->bracket.sizerest = patsize - size; + if (pat->bracket.flags == 5) + checkneeded = 1; + NEXTPATTERN (bracket); + } + break; + default: + if (*s == '\\' && (!(flags & FNM_NOESCAPE))) + s++; + pat->chr.chr = *s; + size++; + NEXTPATTERN (chr); + if (*s == '/' && (flags & FNM_PERIOD) && (flags & FNM_PATHNAME) + && *(s + 1) != '.') + { + NEXTPATTERN (begindot)} + + break; + } + } + } + if (flags & FNM_LEADING_DIR) + { + NEXTPATTERN (slashend) NEXTPATTERN (endstar)} + else + { + NEXTPATTERN (end)} + + fnmatch_state *st; + NULTEST (st = initfnmatchstate ()); + st->states->p = ret; + st->start = ret; + if (checkneeded) + st = checkneeded_fallback (st, fnmatch_fallback (str, flags)); + return st; +} + +#undef NEXTPATTERN +#define NEXTPATTERN(type) p = (patterns *)(((void *) p)+sizeof (type##_pattern)); + +static inline int +fnmatch2_internal (const patterns * p, const char *str, int len) +{ +#include "fnmatchcomp_loop.h" +} + +int +fnmatch_exec (const fnmatch_state * p, const char *str) +{ + const struct states *s; + int len = strlen (str); + for (s = p->states; s; s = s->next) + { + int ret = fnmatch2_internal (s->p, str, len); + if (ret <= 0) + return ret; + } + return FNM_NOMATCH; +} + +#define FNMATCH_PREFIX +static int +fnmatch2_prefix_internal (patterns * p, const char *str, int len, + fnmatch_state * ret) +{ +#include "fnmatchcomp_loop.h" +} + +fnmatch_state * +fnmatch_prefix (const fnmatch_state * p, const char *str) +{ + struct states *s; + int len = strlen (str); + fnmatch_state *ret; + NULTEST (ret = (fnmatch_state *) malloc (sizeof (fnmatch_state))); + memcpy ((void *) ret, (void *) p, sizeof (fnmatch_state)); + ret->states = NULL; + (*ret->refcount)++; + for (s = p->states; s; s = s->next) + { + fnmatch2_prefix_internal (s->p, str, len, ret); + } + return ret; +} + +static void +freepatterns (patterns * p) +{ +#define SKIPPATTERN(type) case PATTERN_##type: NEXTPATTERN(type); break; + while (p->chr.tp != PATTERN_end && p->chr.tp != PATTERN_endstar) + { + switch (p->chr.tp) + { + case PATTERN_chr: + if (!p->chr.chr) + return; + NEXTPATTERN (chr); + break; + SKIPPATTERN (star); + SKIPPATTERN (starslash); + SKIPPATTERN (end); + SKIPPATTERN (endstar); + case PATTERN_bracket: + free (p->bracket.chars); + NEXTPATTERN (bracket); + break; + SKIPPATTERN (begindot); + SKIPPATTERN (slashend); + case PATTERN_fallback: + free (p->fallback.ptr); + return; + case PATTERN_fold: + return; + case PATTERN_checkneeded: + fnmatch_free ((fnmatch_state *) p->checkneeded.fallback); + return; + } + } + +} + +void +fnmatch_free (fnmatch_state * s) +{ + struct states *st, *st2; + for (st = s->states; st;) + { + if (st->p->chr.tp == PATTERN_fallback) + { + free (st->p->fallback.prefix); + } + if (st->p->chr.tp == PATTERN_fold) + { + fnmatch_free ((fnmatch_state *) st->p->fold.states); + } + if (st->p->chr.tp == PATTERN_checkneeded) + { + fnmatch_free ((fnmatch_state *) st->p->checkneeded.states); + } + + st2 = st->next; + free (st); + st = st2; + } + (*s->refcount)--; + if (!*s->refcount) + { + free (s->refcount); + freepatterns (s->start); + free (s->start); + } + free (s); +} diff --git a/lib/fnmatchcomp.h b/lib/fnmatchcomp.h new file mode 100644 index 0000000..1cfce8c --- /dev/null +++ b/lib/fnmatchcomp.h @@ -0,0 +1,41 @@ + /* Copyright (C) 2009 + Ondrej Bilka < nel...@seznam.cz > + + This program is free software: you can redistribute it and/or modif y + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see < http:// www.gnu.org/licenses/ > . */ + +struct _fnmatch_state; +typedef struct _fnmatch_state fnmatch_state; + + /* compile pattern */ +fnmatch_state *fnmatch_compile (const char *pattern, int flags); + + /* add prefix to matched strings. + suppose you want match foo againist /home/foo, /home/bar/foo, + /home/bar/bar + fnmatch_state* s = fnmatch_compile("foo", 0); + fnmatch_state* home = fnmatch_prefix(s, "/home"); + fnmatch_state* bar = fnmatch_prefix(home, "/bar"); + fnmatch_exec(home, "/foo"); + fnmatch_exec(bar, "/foo"); + fnmatch_exec(bar, "/bar"); + */ +fnmatch_state *fnmatch_prefix (const fnmatch_state * pattern, + const char *prefix); + +int fnmatch_exec (const fnmatch_state * pattern, const char *string); + +void fnmatch_free (fnmatch_state * pattern); + +/*For some encodings we decide just call fnmatch. In that cases you can gain some performance by checking fnmatch_fallbacked and if returns 1 call fnmatch yourself*/ +int fnmatch_fallbacked(fnmatch_state *pattern); diff --git a/lib/fnmatchcomp_loop.h b/lib/fnmatchcomp_loop.h new file mode 100644 index 0000000..c189047 --- /dev/null +++ b/lib/fnmatchcomp_loop.h @@ -0,0 +1,196 @@ + /* Copyright (C) 2009 + Ondrej Bilka < nel...@seznam.cz > + + This program is free software: you can redistribute it and/or modif y + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see < http:// www.gnu.org/licenses/ > . */ + +const char *s = str, *chars; +int i, len2; +int sizerest, charno; +char *buf; +patterns *pat; +while (1) + { +#ifndef FNMATCH_PREFIX +#undef ADDSTATE +#define ADDSTATE +#else + struct states *state; +#undef ADDSTATE +#define ADDSTATE state = calloc(1, sizeof (struct states)); \ + state->next = ret->states; \ + ret->states = state; \ + state->p = p; + if (!*s) + { + ADDSTATE; + return 0; + } +#endif + switch (p->chr.tp) + { + case PATTERN_chr: + if (*s != p->chr.chr) + return FNM_NOMATCH; + NEXTPATTERN (chr); + s++; + break; + case PATTERN_starslash: +#undef SLASHTEST +#define SLASHTEST if (str[i] == '/') return FNM_NOMATCH; +#include "fnmatchcomp_star.h" + break; + case PATTERN_star: +#undef SLASHTEST +#define SLASHTEST +#include "fnmatchcomp_star.h" + break; + + case PATTERN_end: + return (*s) ? FNM_NOMATCH : 0; + break; + case PATTERN_slashend: + if (*s == '/' || *s == 0) + { + NEXTPATTERN (slashend); + } + break; + case PATTERN_endstar: + ADDSTATE; + return 0; + break; + case PATTERN_bracket: + { + int mat; + int mlen, mlen2; + unsigned char lastbyte; + chars = p->bracket.chars; + if (MB_CUR_MAX == 1) + { + mlen = 1; + lastbyte = s[mlen - 1]; + mat = (p->bracket.hash[lastbyte / 32] & + (1 << (lastbyte % 32))) ? 1 : 0; + } + else + { + mlen = mblen (s, MB_CUR_MAX); + mat = 1; + if (mlen < 0) + return FNM_NOMATCH; + lastbyte = s[mlen - 1]; + if (!(p->bracket.hash[lastbyte / 32] & (1 << (lastbyte % 32)))) + { + mat = 0; + goto match; + } + while (*chars) + { + mlen2 = strlen (chars); + if (!strncmp (chars, s, mlen) && mlen == mlen2) + goto match; + chars += mlen2 + 1; + } + mat = 0; + } + match: + if (mat ^ (p->bracket.flags & 1)) + { + if (p->bracket.flags & 2 && *s == '/') + return FNM_NOMATCH; + s += mlen; + if (len - (str - s) < p->bracket.sizerest) + return FNM_NOMATCH; + } + else + { + return FNM_NOMATCH; + } + NEXTPATTERN (bracket); + } + break; + case PATTERN_begindot: + if (*s == '.') + return FNM_NOMATCH; + NEXTPATTERN (begindot); + break; + case PATTERN_fallback: + + len2 = strlen (p->fallback.prefix) + len; + NULTEST2 (buf = + (len2 <= 1000) ? alloca (len2 + 1) : malloc (len2 + 1)); + strcpy (buf, p->fallback.prefix); + strcat (buf, s); +#ifndef FNMATCH_PREFIX + { + int ret = fnmatch (p->fallback.ptr, buf, p->fallback.flags); + if (len > 1000) + free (buf); + return ret; + } +#else + patterns *pat2 = malloc (sizeof (fallback_pattern)); + NULTEST2 (pat2); + memcpy (pat2, p, sizeof (fallback_pattern)); + NULTEST2 (pat2->fallback.prefix = strdup (buf)); + p = (patterns *) pat2; + ADDSTATE; + return FNM_NOMATCH; +#endif + if (len > 1000) + free (buf); + break; + + case PATTERN_fold: + NULTEST2 (buf = (len <= 1000) ? alloca (len + 1) : malloc (len + 1)); + strfold (str, buf); +#ifndef FNMATCH_PREFIX + return fnmatch_exec ((fnmatch_state *) p->fold.states, buf); +#else + NULTEST2 (pat2 = malloc (sizeof (fallback_pattern))); + pat2->fold.tp = PATTERN_fold; + NULTEST2 (pat2->fold.states = + (void *) + fold_fallback (fnmatch_prefix (p->fold.states, buf))); + p = (patterns *) pat2; + ADDSTATE; + if (len > 1000) + free (buf); + return FNM_NOMATCH; +#endif + break; + case PATTERN_checkneeded: +#ifndef FNMATCH_PREFIX + { + int ret = fnmatch_exec ((fnmatch_state *) p->checkneeded.states, s); + if (ret > 0) + return FNM_NOMATCH; + if (ret < 0) + return ret; + return fnmatch_exec ((fnmatch_state *) p->checkneeded.fallback, s); + } +#else + NULTEST2 (pat2 = malloc (sizeof (checkneeded_pattern))); + pat2->checkneeded.tp = PATTERN_checkneeded; + NULTEST2 (pat2->checkneeded.states = (void *) + fnmatch_prefix ((fnmatch_state *) p->checkneeded.states, + s)); + NULTEST2 (pat2->checkneeded.fallback = + (void *) fnmatch_prefix ((fnmatch_state *) p-> + checkneeded.fallback, s)); + ADDSTATE; + return FNM_NOMATCH; +#endif + break; + } + } diff --git a/lib/fnmatchcomp_star.h b/lib/fnmatchcomp_star.h new file mode 100644 index 0000000..33dc5f7 --- /dev/null +++ b/lib/fnmatchcomp_star.h @@ -0,0 +1,65 @@ + /* Copyright (C) 2009 + Ondrej Bilka < nel...@seznam.cz > + + This program is free software: you can redistribute it and/or modif y + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see < http:// www.gnu.org/licenses/ > . */ + +#ifndef FNMATCH_PREFIX +sizerest = p->star.sizerest; +chars = p->star.chars; +charno = p->star.charno; +NEXTPATTERN (star); +for (i = 0; i < charno; i++) + { + NEXTPATTERN (chr); + } + +switch (charno) + { + case 0: + for (i = s - str; i < len - sizerest; i++) + { + if (!fnmatch2_internal (p, str + i, len - i)) + return 0; + SLASHTEST; + } + break; +#define CASEN(type, no) \ + case no:\ + for (i = s-str; i <= len-sizerest; i++) {\ + if ((*((type*)(str+i)) == *(type *) chars)\ + && !fnmatch2_internal(p, str+i+no, len-i-no)) return 0; \ + SLASHTEST \ + } \ + break; + CASEN (char, 1); + CASEN (UINT2, 2); + case 3: + CASEN (UINT4, 4); + } + +return FNM_NOMATCH; +#else +pat = p; +NEXTPATTERN (star); +for (i = s - str; i < len; i++) + { + fnmatch2_prefix_internal (p, str + i, len - i, ret); + SLASHTEST; + } + +p = pat; +ADDSTATE; +return FNM_NOMATCH; +#endif +break; diff --git a/modules/fnmatchcomp b/modules/fnmatchcomp new file mode 100644 index 0000000..c89b1e7 --- /dev/null +++ b/modules/fnmatchcomp @@ -0,0 +1,34 @@ +Description: + Allows you to compile fnmatch pattern. You can add to this pattern prefix + which will be added as prefix of any string you will match with this pattern + +Files: +lib/fnmatchcomp.c +lib/fnmatchcomp.h +lib/fnmatchcomp_loop.h +lib/fnmatchcomp_star.h + +Depends-on: +fnmatch +extensions +alloca +stdbool +wchar +wctype +memchr +memcmp +mbsrtowcs +mbsinit + +configure.ac: + +Makefile.am: + +Include: +<fnmatchcomp.h> + +License: +LGPLv2+ + +Maintainer: +Ondrej Bilka