diff options
-rw-r--r-- | doc/case.html | 44 | ||||
-rw-r--r-- | 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. </p> <pre> - case [ -E | -e ] [ -i ] [ -n | -N ] <em>value</em> + case [ -S | -s ] [ -E | -e ] [ -i ] [ -n | -N ] <em>value</em> { [ regex { <em>prog...</em> } ] [ regex { <em>prog...</em> } ] @@ -57,6 +57,13 @@ is empty. </li> <h2> Options </h2> <ul> + <li> <tt>-s</tt> : Shell matching. The <em>regex</em> words will not be +interpreted as regular expressions, but as shell expressions to be interpreted +via <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html">fnmatch()</a>. +The other options also change meanings, see the <em>Shell matching</em> section below. </li> + <li> <tt>-S</tt> : Regular expression matching. This is the default. This +section, and all of the sections below except the <em>Shell matching</em> one, +assumes that it is the case. </li> <li> <tt>-e</tt> : Interpret the <em>regex</em> words as <a href="https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03">basic regular expressions</a>. </li> @@ -116,6 +123,41 @@ to the output of the <tt>/usr/bin/env</tt> command: 2=baz </pre> +<h2> Shell matching </h2> + +<p> + If the <tt>-s</tt> option has been given to <tt>case</tt>, then the <em>regex</em> +words are not interpreted as regular expressions, but as shell patterns, as is +performed by the shell's +<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05">case</a> +conditional construct. This has the following consequences: +</p> + +<ul> + <li> Subexpression matching is always disabled. </li> + <li> <em>prog...</em> is always executed with an unmodified environment. </li> + <li> The options to the <tt>case</tt> command change meanings: instead of +controlling how the <em>regex</em> regular expressions are interpreted by the +<a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/regcomp.html">regcomp()</a> +primitive, they now control how <em>value</em> is matched against the <em>regex</em> patterns +(which are not regular expressions!) via the +<a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html">fnmatch()</a> +primitive. Namely: + <ul> + <li> <tt>-e</tt> : Treat a backslash as an ordinary character; do not allow +character escaping in patterns. (This sets the <tt>FNM_NOESCAPE</tt> flag.) </li> + <li> <tt>-E</tt> : Allow backslash escaping in patterns. This is the default. +(This clears the <tt>FNM_NOESCAPE</tt> flag.) </li> + <li> <tt>-i</tt> : Treat a period (<tt>.</tt>) as a special character for +matching (set <tt>FNM_PERIOD</tt>). By default, the period is not a special character +(<tt>FNM_PERIOD</tt> is cleared). </li> + <li> <tt>-N</tt> : Treat patterns as pathnames: make slashes character special. +(This sets the <tt>FNM_PATHNAME</tt> flag.) </li> + <li> <tt>-n</tt> : Do not treat patterns as pathnames (clear the +<tt>FNM_PATHNAME</tt> flag). This is the default. </li> + </ul> </li> +</ul> + <h2> Notes </h2> <ul> 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 <string.h> #include <regex.h> +#include <fnmatch.h> #include <skalibs/gccattributes.h> #include <skalibs/types.h> @@ -11,7 +12,7 @@ #include <execline/execline.h> -#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) ; |