Subject: bin/7231: /bin/sh quoting / trimming problem
To: None <gnats-bugs@gnats.netbsd.org>
From: None <Havard.Eidnes@runit.sintef.no>
List: netbsd-bugs
Date: 03/25/1999 11:42:57
>Number:         7231
>Category:       bin
>Synopsis:       /bin/sh quoting / trimming problem
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Mar 25 02:50:01 1999
>Last-Modified:
>Originator:     Havard Eidnes
>Organization:
	RUNIT AS
>Release:        NetBSD-current 19990315
>Environment:
	
System: NetBSD vader.runit.sintef.no 1.3I NetBSD 1.3I (VADER) #0: Sat Jan 9 18:53:46 MET 1999 he@hugin.runit.sintef.no:/usr/src/sys/arch/i386/compile/VADER i386


>Description:
	The sequence

	a=b:c
	echo "${a%:*}"

	will send garbage to stdout, while

	a=b:c
	echo ${a%:*}

	does not.

>How-To-Repeat:
	See above.

>Fix:
	It turns out that the : character is "quoted" in the internal
	representation when the expression itself is enclosed in
	double quotes.  The code needs to take care of this when doing
	the pattern matching in conjunction with trimming.

	This fix produced by Tor Egge (tegge@fast.no) for FreeBSD,
	ported over to NetBSD by yours truly.

	Since we don't have a Bourne-shell regression test, I did a
	"make build" with this shell, and that succeeded.

diff -ru5 ./expand.c /usr/src/bin/sh/expand.c
--- ./expand.c	Tue Feb 16 14:55:14 1999
+++ /usr/src/bin/sh/expand.c	Wed Mar 24 21:46:05 1999
@@ -110,11 +110,11 @@
 STATIC void expandmeta __P((struct strlist *, int));
 STATIC void expmeta __P((char *, char *));
 STATIC void addfname __P((char *));
 STATIC struct strlist *expsort __P((struct strlist *));
 STATIC struct strlist *msort __P((struct strlist *, int));
-STATIC int pmatch __P((char *, char *));
+STATIC int pmatch __P((char *, char *, int));
 STATIC char *cvtnum __P((int, char *));
 
 /*
  * Expand shell variables and backquotes inside a here document.
  */
@@ -494,10 +494,11 @@
 	int startloc;
 	int varflags;
 {
 	char *startp;
 	char *loc = NULL;
+	char *q;
 	int c = 0;
 	int saveherefd = herefd;
 	struct nodelist *saveargbackq = argbackq;
 	int amount;
 
@@ -532,36 +533,60 @@
 
 	case VSTRIMLEFT:
 		for (loc = startp; loc < str; loc++) {
 			c = *loc;
 			*loc = '\0';
-			if (patmatch(str, startp))
+			if (patmatch(str, startp, varflags & VSQUOTE))
 				goto recordleft;
 			*loc = c;
+			if ((varflags && VSQUOTE) && *loc == CTLESC)
+			        loc++;
 		}
 		return 0;
 
 	case VSTRIMLEFTMAX:
-		for (loc = str - 1; loc >= startp; loc--) {
+		for (loc = str - 1; loc >= startp;) {
 			c = *loc;
 			*loc = '\0';
-			if (patmatch(str, startp))
+			if (patmatch(str, startp, varflags & VSQUOTE))
 				goto recordleft;
 			*loc = c;
+			loc--;
+			if ((varflags & VSQUOTE) && loc > startp &&
+			    *(loc - 1) == CTLESC) {
+				for (q = startp; q < loc; q++)
+					if (*q == CTLESC)
+						q++;
+				if (q > loc)
+					loc--;
+			}
 		}
 		return 0;
 
 	case VSTRIMRIGHT:
-		for (loc = str - 1; loc >= startp; loc--)
-			if (patmatch(str, loc))
+	        for (loc = str - 1; loc >= startp;) {
+			if (patmatch(str, loc, varflags & VSQUOTE))
 				goto recordright;
+			loc--;
+			if ((varflags & VSQUOTE) && loc > startp &&
+			    *(loc - 1) == CTLESC) { 
+				for (q = startp; q < loc; q++)
+					if (*q == CTLESC)
+						q++;
+				if (q > loc)
+					loc--;
+			}
+		}
 		return 0;
 
 	case VSTRIMRIGHTMAX:
-		for (loc = startp; loc < str - 1; loc++)
-			if (patmatch(str, loc))
+		for (loc = startp; loc < str - 1; loc++) {
+			if (patmatch(str, loc, varflags & VSQUOTE))
 				goto recordright;
+			if ((varflags & VSQUOTE) && *loc == CTLESC)
+			        loc++;
+		}
 		return 0;
 
 	default:
 		abort();
 	}
@@ -1193,11 +1218,11 @@
 	if (*p == '.')
 		matchdot++;
 	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
 		if (dp->d_name[0] == '.' && ! matchdot)
 			continue;
-		if (patmatch(start, dp->d_name)) {
+		if (patmatch(start, dp->d_name, 0)) {
 			if (atend) {
 				scopy(dp->d_name, enddir);
 				addfname(expdir);
 			} else {
 				char *q;
@@ -1302,27 +1327,29 @@
 /*
  * Returns true if the pattern matches the string.
  */
 
 int
-patmatch(pattern, string)
+patmatch(pattern, string, squoted)
 	char *pattern;
 	char *string;
+	int squoted;	/* string might have quote chars */
 	{
 #ifdef notdef
 	if (pattern[0] == '!' && pattern[1] == '!')
 		return 1 - pmatch(pattern + 2, string);
 	else
 #endif
-		return pmatch(pattern, string);
+		return pmatch(pattern, string, squoted);
 }
 
 
 STATIC int
-pmatch(pattern, string)
+pmatch(pattern, string, squoted)
 	char *pattern;
 	char *string;
+	int squoted;
 	{
 	char *p, *q;
 	char c;
 
 	p = pattern;
@@ -1330,34 +1357,45 @@
 	for (;;) {
 		switch (c = *p++) {
 		case '\0':
 			goto breakloop;
 		case CTLESC:
+			if (squoted && *q == CTLESC)
+				q++;
 			if (*q++ != *p++)
 				return 0;
 			break;
 		case CTLQUOTEMARK:
 			continue;
 		case '?':
+			if (squoted && *q == CTLESC)
+				q++;
 			if (*q++ == '\0')
 				return 0;
 			break;
 		case '*':
 			c = *p;
 			while (c == CTLQUOTEMARK || c == '*')
 				c = *++p;
 			if (c != CTLESC &&  c != CTLQUOTEMARK &&
 			    c != '?' && c != '*' && c != '[') {
 				while (*q != c) {
+					if (squoted && *q == CTLESC &&
+					    q[1] == c)
+						break;
 					if (*q == '\0')
 						return 0;
+					if (squoted && *q == CTLESC)
+						q++;
 					q++;
 				}
 			}
 			do {
-				if (pmatch(p, q))
+				if (pmatch(p, q, squoted))
 					return 1;
+				if (squoted && *q == CTLESC)
+					q++;
 			} while (*q++ != '\0');
 			return 0;
 		case '[': {
 			char *endp;
 			int invert, found;
@@ -1381,10 +1419,12 @@
 				invert++;
 				p++;
 			}
 			found = 0;
 			chr = *q++;
+			if (squoted && chr == CTLESC)
+				chr = *q++;
 			if (chr == '\0')
 				return 0;
 			c = *p++;
 			do {
 				if (c == CTLQUOTEMARK)
@@ -1408,10 +1448,12 @@
 			if (found == invert)
 				return 0;
 			break;
 		}
 dft:	        default:
+			if (squoted && *q == CTLESC)
+				q++;
 			if (*q++ != c)
 				return 0;
 			break;
 		}
 	}
@@ -1471,11 +1513,11 @@
 	STARTSTACKSTR(expdest);
 	ifslastp = NULL;
 	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
 	STPUTC('\0', expdest);
 	p = grabstackstr(expdest);
-	result = patmatch(p, val);
+	result = patmatch(p, val, 0);
 	popstackmark(&smark);
 	return result;
 }
 
 /*
diff -ru5 ./expand.h /usr/src/bin/sh/expand.h
--- ./expand.h	Tue Feb 16 14:55:14 1999
+++ /usr/src/bin/sh/expand.h	Wed Mar 24 21:20:41 1999
@@ -62,11 +62,11 @@
 
 union node;
 void expandhere __P((union node *, int));
 void expandarg __P((union node *, struct arglist *, int));
 void expari __P((int));
-int patmatch __P((char *, char *));
+int patmatch __P((char *, char *, int));
 void rmescapes __P((char *));
 int casematch __P((union node *, char *));
 
 /* From arith.y */
 int arith __P((char *));
>Audit-Trail:
>Unformatted: