Bug#243885: Processed: t
On Fri, May 28, 2004 at 01:42:48PM -0400, Jeff Bailey wrote:
> On Fri, 2004-05-28 at 08:10, Herbert Xu wrote:
> > This bug will go away as soon as glibc fixes their fnmatch(3)
> > implementation re #243885.
>
> > To the glibc maintainers, have you made any progress on that issue?
>
> Upstream's regex implementation is now completely different from ours -
> Can this issue wait until after Sarge releases? We'll be updating to a
> new snapshot then. Fixing it in the meantime is likely to be more work
> than it's worth (and is quite likely to introduce other subtle bugs)
The fnmatch() implementation of the dietlibc also doesn't support
backslash-quoting in character classes, and it seems uclibc fails also.
I patched the diet libc fnmatch() function to support \-quoting, and
dash to use this implementation. This fixes #250499 and #243885 in my
tests. The patches against dietlibc:fnmatch() and the dash package are
attached.
I'm not sure what bugs the fnmatch() function from the diet libc can
introduce, and would rather like to see glibc fixed if possible. It
also would be nice if anyone again can confirm that backslash-quoting in
character classes is what the standard requires; there're three libc's
that don't do it.
Thanks, Gerrit.
Index: libshell/fnmatch.c
===================================================================
RCS file: /var/lib/cvs/dietlibc/libshell/fnmatch.c,v
retrieving revision 1.4
diff -u -r1.4 fnmatch.c
--- libshell/fnmatch.c 28 Mar 2004 08:13:22 -0000 1.4
+++ libshell/fnmatch.c 31 May 2004 17:04:29 -0000
@@ -87,6 +87,10 @@
} else {
res = ((*(cc->istype))(*string));
}
+ } else if (*pattern == '\\') {
+ ++pattern;
+ res=match(*pattern,*string,flags);
+ if (*pattern) ++pattern;
} else {
invalidclass:
if (pattern[1]=='-' && pattern[2]!=']') {
diff -urNbxCVS -xchangelog ../dash.old/Makefile ./Makefile
--- ../dash.old/Makefile 2004-05-31 17:04:00.000000000 +0000
+++ ./Makefile 2004-05-31 16:40:54.000000000 +0000
@@ -5,7 +5,7 @@
YHEADER=1
PROG= sh
-SHSRCS= alias.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \
+SHSRCS= alias.c arith_yylex.c cd.c error.c eval.c exec.c expand.c fnmatch.c \
histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
mystring.c options.c parser.c redir.c show.c trap.c output.c var.c \
test.c printf.c times.c
diff -urNbxCVS -xchangelog ../dash.old/debian/rules ./debian/rules
--- ../dash.old/debian/rules 2004-05-31 17:04:00.000000000 +0000
+++ ./debian/rules 2004-05-31 13:53:56.000000000 +0000
@@ -13,7 +13,7 @@
CDEF := \
-Wall -DBSD=1 -DSMALL -D_GNU_SOURCE -DHAVE_VASPRINTF=1 \
- -DGLOB_BROKEN -DFNMATCH_BROKEN -DIFS_BROKEN \
+ -DGLOB_BROKEN -DIFS_BROKEN \
-D__COPYRIGHT\(x\)= -D__RCSID\(x\)= -D_DIAGASSERT\(x\)=
OPT := -g -O2 -fstrict-aliasing
diff -urNbxCVS -xchangelog ../dash.old/fnmatch.c ./fnmatch.c
--- ../dash.old/fnmatch.c 1970-01-01 00:00:00.000000000 +0000
+++ ./fnmatch.c 2004-05-31 16:36:52.000000000 +0000
@@ -0,0 +1,143 @@
+#include <ctype.h>
+#include <fnmatch.h>
+#include <string.h>
+
+#define NOTFIRST 128
+
+#define STRUCT_CHARCLASS(c) { #c , is##c }
+
+static struct charclass {
+ char * class;
+ int (*istype)(int);
+} allclasses[] = {
+ STRUCT_CHARCLASS(alnum),
+ STRUCT_CHARCLASS(alpha),
+ STRUCT_CHARCLASS(blank),
+ STRUCT_CHARCLASS(cntrl),
+ STRUCT_CHARCLASS(digit),
+ STRUCT_CHARCLASS(graph),
+ STRUCT_CHARCLASS(lower),
+ STRUCT_CHARCLASS(print),
+ STRUCT_CHARCLASS(punct),
+ STRUCT_CHARCLASS(space),
+ STRUCT_CHARCLASS(upper),
+ STRUCT_CHARCLASS(xdigit),
+};
+
+/* look for "class:]" in pattern */
+static struct charclass *charclass_lookup(const char *pattern) {
+ unsigned int i;
+
+ for (i = 0; i< sizeof(allclasses)/sizeof(*allclasses); i++) {
+ int len = strlen(allclasses[i].class);
+ if (!strncmp(pattern, allclasses[i].class, len)) {
+ pattern += len;
+ if (strncmp(pattern, ":]", 2)) goto noclass;
+ return &allclasses[i];
+ }
+ }
+noclass:
+ return NULL;
+}
+
+static int match(char c,char d,int flags) {
+ if (flags&FNM_CASEFOLD)
+ return (tolower(c)==tolower(d));
+ else
+ return (c==d);
+}
+
+int fnmatch(const char *pattern, const char *string, int flags) {
+ if (*string==0) {
+ while (*pattern=='*') ++pattern;
+ return (!!*pattern);
+ }
+ if (*string=='.' && *pattern!='.' && (flags&FNM_PERIOD)) {
+ /* don't match if FNM_PERIOD and this is the first char */
+ if (!(flags&NOTFIRST))
+ return FNM_NOMATCH;
+ /* don't match if FNM_PERIOD and FNM_PATHNAME and previous was '/' */
+ if ((flags&(FNM_PATHNAME)) && string[-1]=='/')
+ return FNM_NOMATCH;
+ }
+ flags|=NOTFIRST;
+ switch (*pattern) {
+ case '[':
+ {
+ int neg=0;
+ const char* start; /* first member of character class */
+
+ ++pattern;
+ if (*string=='/' && flags&FNM_PATHNAME) return FNM_NOMATCH;
+ if (*pattern=='!') { neg=1; ++pattern; }
+ start=pattern;
+ while (*pattern) {
+ int res=0;
+
+ if (*pattern==']' && pattern!=start) break;
+ if (*pattern=='[' && pattern[1]==':') {
+ /* MEMBER - stupid POSIX char classes */
+ const struct charclass *cc;
+
+ if (!(cc = charclass_lookup(pattern+2))) goto invalidclass;
+ pattern += strlen(cc->class) + 4;
+ if (flags&FNM_CASEFOLD
+ && (cc->istype == isupper || cc->istype == islower)) {
+ res = islower(tolower(*string));
+ } else {
+ res = ((*(cc->istype))(*string));
+ }
+ } else if (*pattern == '\\') {
+ ++pattern;
+ res=match(*pattern,*string,flags);
+ if (*pattern) ++pattern;
+ } else {
+invalidclass:
+ if (pattern[1]=='-' && pattern[2]!=']') {
+ /* MEMBER - character range */
+ if (*string>=*pattern && *string<=pattern[2]) res=1;
+ if (flags&FNM_CASEFOLD) {
+ if (tolower(*string)>=tolower(*pattern) && tolower(*string)<=tolower(pattern[2])) res=1;
+ }
+ pattern+=3;
+ } else {
+ /* MEMBER - literal character match */
+ res=match(*pattern,*string,flags);
+ ++pattern;
+ }
+ }
+ if ((res&&!neg) || ((neg&&!res) && *pattern==']')) {
+ while (*pattern && *pattern!=']') ++pattern;
+ return fnmatch(pattern+!!*pattern,string+1,flags);
+ } else if (res && neg)
+ return FNM_NOMATCH;
+ }
+ }
+ break;
+ case '\\':
+ if (flags&FNM_NOESCAPE) {
+ if (*string=='\\')
+ return fnmatch(pattern+1,string+1,flags);
+ } else {
+ if (*string==pattern[1])
+ return fnmatch(pattern+2,string+1,flags);
+ }
+ break;
+ case '*':
+ if ((*string=='/' && flags&FNM_PATHNAME) || fnmatch(pattern,string+1,flags))
+ return fnmatch(pattern+1,string,flags);
+ return 0;
+ case 0:
+ if (*string==0 || (*string=='/' && (flags&FNM_LEADING_DIR)))
+ return 0;
+ break;
+ case '?':
+ if (*string=='/' && flags&FNM_PATHNAME) break;
+ return fnmatch(pattern+1,string+1,flags);
+ default:
+ if (match(*pattern,*string,flags))
+ return fnmatch(pattern+1,string+1,flags);
+ break;
+ }
+ return FNM_NOMATCH;
+}
diff -urNbxCVS -xchangelog ../dash.old/fnmatch.h ./fnmatch.h
--- ../dash.old/fnmatch.h 1970-01-01 00:00:00.000000000 +0000
+++ ./fnmatch.h 2004-05-31 14:20:38.000000000 +0000
@@ -0,0 +1,21 @@
+#ifndef _FNMATCH_H
+#define _FNMATCH_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+int fnmatch(const char *pattern, const char *string, int flags) __THROW;
+
+#define FNM_NOESCAPE 1
+#define FNM_PATHNAME 2
+#define FNM_FILE_NAME 2
+#define FNM_PERIOD 4
+#define FNM_LEADING_DIR 8
+#define FNM_CASEFOLD 16
+
+#define FNM_NOMATCH 1
+
+__END_DECLS
+
+#endif
Reply to: