From 45ca80e0e1509c613f05cdb5fe8ec1157a4a7a48 Mon Sep 17 00:00:00 2001
From: Laurent Bercot
Date: Fri, 26 Nov 2021 07:00:11 +0000
Subject: Add shell matching option to case
Signed-off-by: Laurent Bercot
---
doc/case.html | 44 +++++++++++++++++++++++++++++++++-
src/execline/case.c | 68 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/doc/case.html b/doc/case.html
index a82eec8..8ae817b 100644
--- a/doc/case.html
+++ b/doc/case.html
@@ -31,7 +31,7 @@ matches.
- case [ -E | -e ] [ -i ] [ -n | -N ] value
+ case [ -S | -s ] [ -E | -e ] [ -i ] [ -n | -N ] value
{
[ regex { prog... } ]
[ regex { prog... } ]
@@ -57,6 +57,13 @@ is empty.
Options
+ - -s : Shell matching. The regex words will not be
+interpreted as regular expressions, but as shell expressions to be interpreted
+via fnmatch().
+The other options also change meanings, see the Shell matching section below.
+ - -S : Regular expression matching. This is the default. This
+section, and all of the sections below except the Shell matching one,
+assumes that it is the case.
- -e : Interpret the regex words as
basic
regular expressions.
@@ -116,6 +123,41 @@ to the output of the /usr/bin/env command:
2=baz
+ Shell matching
+
+
+ If the -s option has been given to case, then the regex
+words are not interpreted as regular expressions, but as shell patterns, as is
+performed by the shell's
+case
+conditional construct. This has the following consequences:
+
+
+
+ - Subexpression matching is always disabled.
+ - prog... is always executed with an unmodified environment.
+ - The options to the case command change meanings: instead of
+controlling how the regex regular expressions are interpreted by the
+regcomp()
+primitive, they now control how value is matched against the regex patterns
+(which are not regular expressions!) via the
+fnmatch()
+primitive. Namely:
+
+ - -e : Treat a backslash as an ordinary character; do not allow
+character escaping in patterns. (This sets the FNM_NOESCAPE flag.)
+ - -E : Allow backslash escaping in patterns. This is the default.
+(This clears the FNM_NOESCAPE flag.)
+ - -i : Treat a period (.) as a special character for
+matching (set FNM_PERIOD). By default, the period is not a special character
+(FNM_PERIOD is cleared).
+ - -N : Treat patterns as pathnames: make slashes character special.
+(This sets the FNM_PATHNAME flag.)
+ - -n : Do not treat patterns as pathnames (clear the
+FNM_PATHNAME flag). This is the default.
+
+
+
Notes
diff --git a/src/execline/case.c b/src/execline/case.c
index 77f905e..b026256 100644
--- a/src/execline/case.c
+++ b/src/execline/case.c
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
#include
@@ -11,7 +12,7 @@
#include
-#define USAGE "case [ -e | -E ] [ -n | -N ] [ -i ] value { re1 { prog1... } re2 { prog2... } ... } progdefault... "
+#define USAGE "case [ -s | -S ] [ -e | -E ] [ -n | -N ] [ -i ] value { re1 { prog1... } re2 { prog2... } ... } progdefault... "
#define dieusage() strerr_dieusage(100, USAGE)
static void execit (char const *const *argv, char const *expr, char const *s, regmatch_t const *pmatch, size_t n) gccattr_noreturn ;
@@ -47,6 +48,7 @@ static void execit (char const *const *argv, char const *expr, char const *s, re
int main (int argc, char const **argv, char const *const *envp)
{
+ int flagshell = 0 ;
int flagextended = 1 ;
int flagnosub = 1 ;
int flagicase = 0 ;
@@ -58,10 +60,12 @@ int main (int argc, char const **argv, char const *const *envp)
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
- int opt = subgetopt_r(argc, argv, "eEnNi", &l) ;
+ int opt = subgetopt_r(argc, argv, "sSeEnNi", &l) ;
if (opt == -1) break ;
switch (opt)
{
+ case 's' : flagshell = 1 ; break ;
+ case 'S' : flagshell = 0 ; break ;
case 'e' : flagextended = 0 ; break ;
case 'E' : flagextended = 1 ; break ;
case 'N' : flagnosub = 0 ; break ;
@@ -82,42 +86,56 @@ int main (int argc, char const **argv, char const *const *envp)
{
char const *expr = argv[i++] ;
int argc2 ;
- regex_t re ;
if (i == argc1) strerr_dief1x(100, "malformed case block") ;
argc2 = el_semicolon(argv + i) ;
if (i + argc2 >= argc1) strerr_dief1x(100, "unterminated regex block") ;
+ if (flagshell)
{
- int r ;
- size_t len = strlen(expr) ;
- char tmp[len+3] ;
- tmp[0] = '^' ;
- memcpy(tmp + 1, expr, len) ;
- tmp[1+len] = '$' ;
- tmp[2+len] = 0 ;
- r = regcomp(&re, tmp, (flagextended ? REG_EXTENDED : 0) | (flagicase ? REG_ICASE : 0) | (flagnosub ? REG_NOSUB : 0) | REG_NEWLINE) ;
- if (r)
+ int r = fnmatch(expr, s, (flagextended ? 0 : FNM_NOESCAPE) | (flagicase ? FNM_PERIOD : 0) | (flagnosub ? 0 : FNM_PATHNAME)) ;
+ if (!r)
{
- char buf[256] ;
- regerror(r, &re, buf, 256) ;
- strerr_diefu4x(r == REG_ESPACE ? 111 : 100, "regcomp \"^", argv[i], "$\": ", buf) ;
+ argv[i + argc2] = 0 ;
+ xexec0(argv + i) ;
}
+ else if (r != FNM_NOMATCH)
+ strerr_warnw2x("invalid fnmatch pattern: ", expr) ;
}
+ else
{
- regmatch_t pmatch[re.re_nsub && !flagnosub ? re.re_nsub + 1 : 1] ;
- int r = regexec(&re, s, re.re_nsub + 1, pmatch, 0) ;
- if (!r)
+ regex_t re ;
{
- argv[i + argc2] = 0 ;
- execit(argv + i, expr, s, pmatch, flagnosub ? 0 : 1 + re.re_nsub) ;
+ int r ;
+ size_t len = strlen(expr) ;
+ char tmp[len+3] ;
+ tmp[0] = '^' ;
+ memcpy(tmp + 1, expr, len) ;
+ tmp[1+len] = '$' ;
+ tmp[2+len] = 0 ;
+ r = regcomp(&re, tmp, (flagextended ? REG_EXTENDED : 0) | (flagicase ? REG_ICASE : 0) | (flagnosub ? REG_NOSUB : 0) | REG_NEWLINE) ;
+ if (r)
+ {
+ char buf[256] ;
+ regerror(r, &re, buf, 256) ;
+ strerr_diefu4x(r == REG_ESPACE ? 111 : 100, "regcomp \"^", argv[i], "$\": ", buf) ;
+ }
}
- if (r != REG_NOMATCH)
{
- char buf[256] ;
- regerror(r, &re, buf, 256) ;
- strerr_diefu6x(111, "match string \"", s, "\" against regex \"", expr, "\": ", buf) ;
+ regmatch_t pmatch[re.re_nsub && !flagnosub ? re.re_nsub + 1 : 1] ;
+ int r = regexec(&re, s, re.re_nsub + 1, pmatch, 0) ;
+ if (!r)
+ {
+ argv[i + argc2] = 0 ;
+ execit(argv + i, expr, s, pmatch, flagnosub ? 0 : 1 + re.re_nsub) ;
+ }
+ if (r != REG_NOMATCH)
+ {
+ char buf[256] ;
+ regerror(r, &re, buf, 256) ;
+ strerr_diefu6x(111, "match string \"", s, "\" against regex \"", expr, "\": ", buf) ;
+ }
}
+ regfree(&re) ;
}
- regfree(&re) ;
i += argc2 + 1 ;
}
xexec0(argv + argc1 + 1) ;
--
cgit v1.2.3