OSDN Git Service

Suppress glob-brace expansion of single item lists.
authorKeith Marshall <keith@users.osdn.me>
Mon, 16 Apr 2018 14:41:53 +0000 (15:41 +0100)
committerKeith Marshall <keith@users.osdn.me>
Mon, 16 Apr 2018 14:41:53 +0000 (15:41 +0100)
mingwrt/ChangeLog
mingwrt/mingwex/glob.c

index 5c80702..e108893 100644 (file)
@@ -1,3 +1,12 @@
+2018-04-16  Keith Marshall  <keith@users.osdn.me>
+
+       Suppress glob-brace expansion of single item lists.
+
+       * mingwex/glob.c (glob_match) [c == '{']: Look-ahead to matching '}';
+       ignore intervening nested '{' ... '}' expression groups, and suppress
+       special interpretation of '{' if '\0' encountered before '}' at outer
+       nesting level, or if no unescaped ',' found within outer expression.
+
 2018-02-26  Keith Marshall  <keith@users.osdn.me>
 
        Prepare and publish MinGW.org WSL-5.1 release.
index b51fdf0..2a6aea4 100644 (file)
@@ -7,7 +7,7 @@
  * $Id$
  *
  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2011-2014, 2017, MinGW.org Project.
+ * Copyright (C) 2011-2014, 2017, 2018, MinGW.org Project.
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -902,15 +902,96 @@ glob_match( const char *pattern, int flags, int (*errfn)(), glob_t *gl_buf )
            */
           if( *src != '\0') *dest++ = *src++;
         }
-        /* ...copying every character up to but excluding the
-         * opening brace of the first brace bounded expression
-         * (if any), or up to and including the NUL terminator
-         * otherwise...
+        if( (c = *src) == '{' )
+        {
+          /* We've found the opening brace of a possible brace
+           * expansion; perform a look-ahead, to confirm that it
+           * is actually expandible, (i.e. its matching closing
+           * brace is present, and there is at least one comma
+           * separator at its initial nesting level).
+           */
+          const char *scan = src; int level = 1, comma = ',';
+          do {
+               /* We already know that the first character in
+                * the look-ahead range is '{', so we start our
+                * look-ahead scan at the following character in
+                * level one, proceeding until we either find the
+                * corresponding '}', or we run out of scannable
+                * characters; in passing, note whether or not
+                * we find a comma separator at level one.
+                */
+               switch( *++scan )
+               {
+                 /* While scanning, any further opening brace
+                  * characters take us to one higher level of
+                  * expression nesting, whereas closing braces
+                  * bring us back to one lower level, until we
+                  * ultimately drop out of level one.
+                  */
+                 case '{': ++level; break;
+                 case '}': --level; break;
+
+                 /* If we find a comma...
+                  */
+                 case ',':
+                   /* ...and it appears at expression nesting
+                    * level one, we note that it is associated
+                    * with an opening brace, (this doesn't imply
+                    * character replacement; merely association,
+                    * and at any other nesting level, we assign
+                    * no significance to the comma).
+                    */
+                   if( level == 1 ) comma = '{';
+                   break;
+
+                 /* If we run out of scannable characters, (i.e.
+                  * we find a NUL terminator)...
+                  */
+                 case '\0':
+                   /* ...then the expression is malformed; drop
+                    * any previously noted association for comma,
+                    * and forcibly close all open nesting levels,
+                    * so aborting the expression scan.
+                    */
+                   comma = ','; level = 0;
+                   break;
+
+                 /* Other than the above...
+                  */
+                 default:
+                   /* ...we must handle escapes for any of these
+                    * special characters, while again taking care
+                    * to avoid overrunning the NUL terminator.
+                    */
+                   if( (*scan == glob_escape_char) && (scan[1] != '\0') )
+                     ++scan;
+               }
+               /* We continue this look-ahead until we either exit
+                * the level one scan naturally, (on detection of its
+                * closing '}'), or because we ran out of characters
+                * to scan, (i.e. we found a NUL terminator).
+                */
+             } while( level > 0 );
+
+          /* In the event that we have found an opening brace, but
+           * no associated comma separator, (or worse, no matching
+           * closing brace), then our "comma" variable will reflect
+           * an unsuccessful expression validation; since it will
+           * otherwise have mapped an association to the opening
+           * brace, we can simply update "c" to match.
+           */
+          c = comma;
+        }
+        /* In the event that we didn't find a well-formed brace
+         * expression, (including at least one comma separator at
+         * nesting level one), then we must simply store whatever
+         * character we did find at the current pattern offset...
          */
-        if( (c = *src) != '{' ) *dest++ = *src++;
+        if( c != '{' ) *dest++ = *src++;
         /*
          * ...repeating until we either exhaust the original
-         * pattern, or we find an opening brace.
+         * pattern, or we do find an opening brace which does
+         * have an associated level one comma separator.
          */
        } while( (c != '\0') && (c != '{') );