sh: Add support for named character classes in bracket expressions.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 21 Aug 2011 21:33:45 +0000 (14:33 -0700)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 21 Aug 2011 21:33:45 +0000 (14:33 -0700)
Example:
  case x in [[:alpha:]]) echo yes ;; esac

Obtained-from:   FreeBSD 223120

bin/sh/expand.c
bin/sh/sh.1
tools/regression/bin/sh/builtins/case8.0 [new file with mode: 0644]

index 44c94c4..12ac06f 100644 (file)
@@ -36,7 +36,7 @@
  * SUCH DAMAGE.
  *
  * @(#)expand.c        8.5 (Berkeley) 5/15/95
- * $FreeBSD: src/bin/sh/expand.c,v 1.90 2011/06/13 21:03:27 jilles Exp $
+ * $FreeBSD: src/bin/sh/expand.c,v 1.91 2011/06/15 21:48:10 jilles Exp $
  */
 
 #include <sys/types.h>
@@ -52,6 +52,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <wchar.h>
+#include <wctype.h>
 
 /*
  * Routines to expand arguments to commands.  We have to deal with
@@ -1400,13 +1401,43 @@ get_wc(const char **p)
 
 
 /*
+ * See if a character matches a character class, starting at the first colon
+ * of "[:class:]".
+ * If a valid character class is recognized, a pointer to the next character
+ * after the final closing bracket is stored into *end, otherwise a null
+ * pointer is stored into *end.
+ */
+static int
+match_charclass(const char *p, wchar_t chr, const char **end)
+{
+       char name[20];
+       const char *nameend;
+       wctype_t cclass;
+
+       *end = NULL;
+       p++;
+       nameend = strstr(p, ":]");
+       if (nameend == NULL || nameend - p >= (int)sizeof(name) || nameend == p)
+               return 0;
+       memcpy(name, p, nameend - p);
+       name[nameend - p] = '\0';
+       *end = nameend + 2;
+       cclass = wctype(name);
+       /* An unknown class matches nothing but is valid nevertheless. */
+       if (cclass == 0)
+               return 0;
+       return iswctype(chr, cclass);
+}
+
+
+/*
  * Returns true if the pattern matches the string.
  */
 
 int
 patmatch(const char *pattern, const char *string, int squoted)
 {
-       const char *p, *q;
+       const char *p, *q, *end;
        char c;
        wchar_t wc, wc2;
 
@@ -1494,6 +1525,11 @@ patmatch(const char *pattern, const char *string, int squoted)
                        do {
                                if (c == CTLQUOTEMARK)
                                        continue;
+                               if (c == '[' && *p == ':') {
+                                       found |= match_charclass(p, chr, &end);
+                                       if (end != NULL)
+                                               p = end;
+                               }
                                if (c == CTLESC)
                                        c = *p++;
                                if (localeisutf8 && c & 0x80) {
index eecb026..d2cf516 100644 (file)
@@ -34,7 +34,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"    from: @(#)sh.1  8.6 (Berkeley) 5/4/95
-.\" $FreeBSD: src/bin/sh/sh.1,v 1.168 2011/06/12 23:06:04 jilles Exp $
+.\" $FreeBSD: src/bin/sh/sh.1,v 1.169 2011/06/15 21:48:10 jilles Exp $
 .\"
 .Dd August 21, 2011
 .Dt SH 1
@@ -1659,6 +1659,15 @@ matches a
 rather than introducing a character class.
 A character class matches any of the characters between the square brackets.
 A range of characters may be specified using a minus sign.
+A named class of characters (see
+.Xr wctype 3 )
+may be specified by surrounding the name with
+.Ql \&[:
+and
+.Ql :\&] .
+For example,
+.Ql \&[\&[:alpha:\&]\&]
+is a shell pattern that matches a single letter.
 The character class may be complemented by making an exclamation point
 .Pq Ql !\&
 or the caret
@@ -2582,6 +2591,7 @@ will return the argument.
 .Xr execve 2 ,
 .Xr getrlimit 2 ,
 .Xr umask 2 ,
+.Xr wctype 3 ,
 .Xr editrc 5 ,
 .Xr script 7
 .Sh HISTORY
diff --git a/tools/regression/bin/sh/builtins/case8.0 b/tools/regression/bin/sh/builtins/case8.0
new file mode 100644 (file)
index 0000000..0d9aefb
--- /dev/null
@@ -0,0 +1,32 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/case8.0,v 1.1 2011/06/15 21:48:10 jilles Exp $
+
+case aZ_ in
+[[:alpha:]_][[:upper:]_][[:alpha:]_]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ' ' in
+[[:alpha:][:digit:]]) echo Failed at $LINENO ;;
+[![:alpha:][:digit:]]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case '.X.' in
+*[[:lower:]]*) echo Failed at $LINENO ;;
+*[[:upper:]]*) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case ' ' in
+[![:print:]]) echo Failed at $LINENO ;;
+[![:alnum:][:punct:]]) ;;
+*) echo Failed at $LINENO ;;
+esac
+
+case '
+' in
+[[:print:]]) echo Failed at $LINENO ;;
+['
+'[:digit:]]) ;;
+*) echo Failed at $LINENO ;;
+esac