From: Peter Avalos Date: Sun, 21 Aug 2011 21:33:45 +0000 (-0700) Subject: sh: Add support for named character classes in bracket expressions. X-Git-Tag: v2.12.0~148 X-Git-Url: https://gitweb.dragonflybsd.org/~nant/dragonfly.git/commitdiff_plain/215809b779970af1a7de5914487f7e474ee48c4b sh: Add support for named character classes in bracket expressions. Example: case x in [[:alpha:]]) echo yes ;; esac Obtained-from: FreeBSD 223120 --- diff --git a/bin/sh/expand.c b/bin/sh/expand.c index 44c94c4830..12ac06f481 100644 --- a/bin/sh/expand.c +++ b/bin/sh/expand.c @@ -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 @@ -52,6 +52,7 @@ #include #include #include +#include /* * Routines to expand arguments to commands. We have to deal with @@ -1399,6 +1400,36 @@ 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. */ @@ -1406,7 +1437,7 @@ get_wc(const char **p) 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) { diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index eecb026833..d2cf51693f 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -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 index 0000000000..0d9aefb39b --- /dev/null +++ b/tools/regression/bin/sh/builtins/case8.0 @@ -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