about summary refs log tree commit diff
path: root/posix/fnmatch_loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'posix/fnmatch_loop.c')
-rw-r--r--posix/fnmatch_loop.c135
1 files changed, 118 insertions, 17 deletions
diff --git a/posix/fnmatch_loop.c b/posix/fnmatch_loop.c
index 5f6c05710e..831bd0631a 100644
--- a/posix/fnmatch_loop.c
+++ b/posix/fnmatch_loop.c
@@ -31,6 +31,16 @@ FCT (pattern, string, no_leading_period, flags)
 {
   register const CHAR *p = pattern, *n = string;
   register UCHAR c;
+#ifdef _LIBC
+  const UCHAR *collseq = (const UCHAR *)
+    _NL_CURRENT(LC_COLLATE, CONCAT(_NL_COLLATE_COLLSEQ,SUFFIX));
+# ifdef WIDE_CHAR_VERSION
+  const wint_t *names = (const wint_t *)
+    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_NAMES);
+  size_t size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_HASH_SIZE);
+  size_t layers = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_HASH_LAYERS);
+# endif
+#endif
 
   while ((c = *p++) != L('\0'))
     {
@@ -210,9 +220,9 @@ FCT (pattern, string, no_leading_period, flags)
 		    /* Leave room for the null.  */
 		    CHAR str[CHAR_CLASS_MAX_LENGTH + 1];
 		    size_t c1 = 0;
-# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
+#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
 		    wctype_t wt;
-# endif
+#endif
 		    const CHAR *startp = p;
 
 		    for (;;)
@@ -240,7 +250,7 @@ FCT (pattern, string, no_leading_period, flags)
 		      }
 		    str[c1] = L('\0');
 
-# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
+#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
 		    wt = IS_CHAR_CLASS (str);
 		    if (wt == 0)
 		      /* Invalid character class name.  */
@@ -248,7 +258,7 @@ FCT (pattern, string, no_leading_period, flags)
 
 		    if (ISWCTYPE (BTOWC ((UCHAR) *n), wt))
 		      goto matched;
-# else
+#else
 		    if ((STREQ (str, L("alnum")) && ISALNUM ((UCHAR) *n))
 			|| (STREQ (str, L("alpha")) && ISALPHA ((UCHAR) *n))
 			|| (STREQ (str, L("blank")) && ISBLANK ((UCHAR) *n))
@@ -262,7 +272,7 @@ FCT (pattern, string, no_leading_period, flags)
 			|| (STREQ (str, L("upper")) && ISUPPER ((UCHAR) *n))
 			|| (STREQ (str, L("xdigit")) && ISXDIGIT ((UCHAR) *n)))
 		      goto matched;
-# endif
+#endif
 		  }
 		else if (c == L('\0'))
 		  /* [ (unterminated) loses.  */
@@ -279,27 +289,117 @@ FCT (pattern, string, no_leading_period, flags)
 
 		    if (c == L('-') && *p != L(']'))
 		      {
-			/* It is a range.  */
-			CHAR lo[2];
-			CHAR fc[2];
+#if _LIBC
+			/* We have to find the collation sequence
+			   value for C.  Collation sequence is nothing
+			   we can regularly access.  The sequence
+			   value is defined by the order in which the
+			   definitions of the collation values for the
+			   various characters appear in the source
+			   file.  A strange concept, nowhere
+			   documented.  */
+			int32_t fseqidx;
+			int32_t lseqidx;
 			UCHAR cend = *p++;
+# ifdef WIDE_CHAR_VERSION
+			size_t cnt;
+# endif
+
 			if (!(flags & FNM_NOESCAPE) && cend == L('\\'))
 			  cend = *p++;
 			if (cend == L('\0'))
 			  return FNM_NOMATCH;
 
-			lo[0] = cold;
-			lo[1] = L('\0');
-			fc[0] = fn;
-			fc[1] = L('\0');
-			if (STRCOLL (lo, fc) <= 0)
+# ifdef WIDE_CHAR_VERSION
+			/* Search in the `names' array for the characters.  */
+			fseqidx = fn % size;
+			cnt = 0;
+			while (names[fseqidx] != fn)
 			  {
-			    CHAR hi[2];
-			    hi[0] = FOLD (cend);
-			    hi[1] = L('\0');
-			    if (STRCOLL (fc, hi) <= 0)
+			    if (++cnt == layers)
+			      /* XXX We don't know anything about
+				 the character we are supposed to
+				 match.  This means we are failing.  */
+			      goto range_not_matched;
+
+			    fseqidx += size;
+			  }
+			lseqidx = cold % size;
+			cnt = 0;
+			while (names[lseqidx] != cold)
+			  {
+			    if (++cnt == layers)
+			      {
+				lseqidx = -1;
+				break;
+			      }
+			    lseqidx += size;
+			  }
+# else
+			fseqidx = fn;
+			lseqidx = cold;
+# endif
+
+			/* XXX It is not entirely clear to me how to handle
+			   characters which are not mentioned in the
+			   collation specification.  */
+			if (
+# ifdef WIDE_CHAR_VERSION
+			    lseqidx == -1 ||
+# endif
+			    collseq[lseqidx] <= collseq[fseqidx])
+			  {
+			    /* We have to look at the upper bound.  */
+			    int32_t hseqidx;
+
+			    cend = FOLD (cend);
+# ifdef WIDE_CHAR_VERSION
+			    hseqidx = cend % size;
+			    cnt = 0;
+			    while (names[hseqidx] != cend)
+			      {
+				if (++cnt == layers)
+				  {
+				    /* Hum, no information about the upper
+				       bound.  The matching succeeds if the
+				       lower bound is matched exactly.  */
+				    if (lseqidx == -1 || cold != fn)
+				      goto range_not_matched;
+
+				    goto matched;
+				  }
+			      }
+# else
+			    hseqidx = cend;
+# endif
+
+			    if (
+# ifdef WIDE_CHAR_VERSION
+				(lseqidx == -1
+				 && collseq[fseqidx] == collseq[hseqidx]) ||
+# endif
+				collseq[fseqidx] <= collseq[hseqidx])
 			      goto matched;
 			  }
+# ifdef WIDE_CHAR_VERSION
+		      range_not_matched:
+# endif
+#else
+			/* We use a boring value comparison of the character
+			   values.  This is better than comparing using
+			   `strcoll' since the latter would have surprising
+			   and sometimes fatal consequences.  */
+			UCHAR cend = *p++;
+
+			if (!(flags & FNM_NOESCAPE) && cend == L('\\'))
+			  cend = *p++;
+			if (cend == L('\0'))
+			  return FNM_NOMATCH;
+
+			/* It is a range.  */
+			if (cold <= fc && fc <= c)
+			  goto matched;
+#endif
 
 			c = *p++;
 		      }
@@ -371,3 +471,4 @@ FCT (pattern, string, no_leading_period, flags)
 #undef STRCOLL
 #undef L
 #undef BTOWC
+#undef SUFFIX