			     BASH PATCH REPORT
			     =================

Bash-Release:	5.0
Patch-ID:	bash50-003

Bug-Reported-by:	Andrew Church <achurch+bash@achurch.org>
Bug-Reference-ID:	<5c534aa2.04371@msgid.achurch.org>
Bug-Reference-URL:	http://lists.gnu.org/archive/html/bug-bash/2019-01/msg00276.html

Bug-Description:

There are several incompatibilities in how bash-5.0 processes pathname
expansion (globbing) of filename arguments that have backslashes in the
directory portion.

Patch (apply with `patch -p0'):

*** ../bash-5.0-patched/lib/glob/glob_loop.c	2019-01-16 16:13:21.000000000 -0500
--- lib/glob/glob_loop.c	2019-02-01 09:45:11.000000000 -0500
***************
*** 27,34 ****
    register const GCHAR *p;
    register GCHAR c;
!   int bopen;
  
    p = pattern;
!   bopen = 0;
  
    while ((c = *p++) != L('\0'))
--- 27,34 ----
    register const GCHAR *p;
    register GCHAR c;
!   int bopen, bsquote;
  
    p = pattern;
!   bopen = bsquote = 0;
  
    while ((c = *p++) != L('\0'))
***************
*** 56,66 ****
        case L('\\'):
  	/* Don't let the pattern end in a backslash (GMATCH returns no match
! 	   if the pattern ends in a backslash anyway), but otherwise return 1,
! 	   since the matching engine uses backslash as an escape character
! 	   and it can be removed. */
! 	return (*p != L('\0'));
        }
  
!   return 0;
  }
  
--- 56,75 ----
        case L('\\'):
  	/* Don't let the pattern end in a backslash (GMATCH returns no match
! 	   if the pattern ends in a backslash anyway), but otherwise note that 
! 	   we have seen this, since the matching engine uses backslash as an
! 	   escape character and it can be removed. We return 2 later if we
! 	   have seen only backslash-escaped characters, so interested callers
! 	   know they can shortcut and just dequote the pathname. */
! 	if (*p != L('\0'))
! 	  {
! 	    p++;
! 	    bsquote = 1;
! 	    continue;
! 	  }
! 	else 	/* (*p == L('\0')) */
! 	  return 0;
        }
  
!   return bsquote ? 2 : 0;
  }
  
*** ../bash-5.0-patched/lib/glob/glob.h	2013-10-28 14:46:12.000000000 -0400
--- lib/glob/glob.h	2019-03-07 11:06:47.000000000 -0500
***************
*** 31,34 ****
--- 31,35 ----
  #define GX_ADDCURDIR	0x200	/* internal -- add passed directory name */
  #define GX_GLOBSTAR	0x400	/* turn on special handling of ** */
+ #define GX_RECURSE	0x800	/* internal -- glob_filename called recursively */
  
  extern int glob_pattern_p __P((const char *));
*** ../bash-5.0-patched/lib/glob/glob.c	2018-09-20 10:53:23.000000000 -0400
--- lib/glob/glob.c	2019-03-07 14:23:43.000000000 -0500
***************
*** 1062,1066 ****
    unsigned int directory_len;
    int free_dirname;			/* flag */
!   int dflags;
  
    result = (char **) malloc (sizeof (char *));
--- 1078,1082 ----
    unsigned int directory_len;
    int free_dirname;			/* flag */
!   int dflags, hasglob;
  
    result = (char **) malloc (sizeof (char *));
***************
*** 1111,1117 ****
      }
  
    /* If directory_name contains globbing characters, then we
!      have to expand the previous levels.  Just recurse. */
!   if (directory_len > 0 && glob_pattern_p (directory_name))
      {
        char **directories, *d, *p;
--- 1127,1136 ----
      }
  
+   hasglob = 0;
    /* If directory_name contains globbing characters, then we
!      have to expand the previous levels.  Just recurse.
!      If glob_pattern_p returns != [0,1] we have a pattern that has backslash
!      quotes but no unquoted glob pattern characters. We dequote it below. */
!   if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1)
      {
        char **directories, *d, *p;
***************
*** 1176,1180 ****
  	d[directory_len - 1] = '\0';
  
!       directories = glob_filename (d, dflags);
  
        if (free_dirname)
--- 1195,1199 ----
  	d[directory_len - 1] = '\0';
  
!       directories = glob_filename (d, dflags|GX_RECURSE);
  
        if (free_dirname)
***************
*** 1333,1336 ****
--- 1352,1369 ----
  	  return (NULL);
  	}
+       /* If we have a directory name with quoted characters, and we are
+ 	 being called recursively to glob the directory portion of a pathname,
+ 	 we need to dequote the directory name before returning it so the
+ 	 caller can read the directory */
+       if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0)
+ 	{
+ 	  dequote_pathname (directory_name);
+ 	  directory_len = strlen (directory_name);
+ 	}
+ 
+       /* We could check whether or not the dequoted directory_name is a
+ 	 directory and return it here, returning the original directory_name
+ 	 if not, but we don't do that yet. I'm not sure it matters. */
+ 
        /* Handle GX_MARKDIRS here. */
        result[0] = (char *) malloc (directory_len + 1);
*** ../bash-5.0-patched/pathexp.c	2018-04-29 17:44:48.000000000 -0400
--- pathexp.c	2019-01-31 20:19:41.000000000 -0500
***************
*** 66,74 ****
    register int c;
    char *send;
!   int open;
  
    DECLARE_MBSTATE;
  
!   open = 0;
    send = string + strlen (string);
  
--- 66,74 ----
    register int c;
    char *send;
!   int open, bsquote;
  
    DECLARE_MBSTATE;
  
!   open = bsquote = 0;
    send = string + strlen (string);
  
***************
*** 101,105 ****
  	   globbing. */
  	case '\\':
! 	  return (*string != 0);
  	 	  
  	case CTLESC:
--- 101,112 ----
  	   globbing. */
  	case '\\':
! 	  if (*string != '\0' && *string != '/')
! 	    {
! 	      bsquote = 1;
! 	      string++;
! 	      continue;
! 	    }
! 	  else if (*string == 0)
! 	    return (0);
  	 	  
  	case CTLESC:
***************
*** 118,122 ****
  #endif
      }
!   return (0);
  }
  
--- 125,130 ----
  #endif
      }
! 
!   return (bsquote ? 2 : 0);
  }
  
*** ../bash-5.0-patched/bashline.c	2019-01-16 16:13:21.000000000 -0500
--- bashline.c	2019-02-22 09:29:08.000000000 -0500
***************
*** 3753,3757 ****
  
  	case '\\':
! 	  if (*string == 0)
  	    return (0);	 	  
  	}
--- 3766,3770 ----
  
  	case '\\':
! 	  if (*string++ == 0)
  	    return (0);	 	  
  	}
*** ../bash-5.0/patchlevel.h	2016-06-22 14:51:03.000000000 -0400
--- patchlevel.h	2016-10-01 11:01:28.000000000 -0400
***************
*** 26,30 ****
     looks for to find the patch level (for the sccs version string). */
  
! #define PATCHLEVEL 2
  
  #endif /* _PATCHLEVEL_H_ */
--- 26,30 ----
     looks for to find the patch level (for the sccs version string). */
  
! #define PATCHLEVEL 3
  
  #endif /* _PATCHLEVEL_H_ */
