From 85b0cb20eb33a43f7020afd3f936fc8d215b5ca8 Mon Sep 17 00:00:00 2001
From: tg <tg@mirbsd.org>
Date: Wed, 27 Feb 2008 11:24:12 +0000
Subject: [PATCH] =?UTF-8?q?almost=20hack=20${foo//bar/baz}=20support=20for?=
 =?UTF-8?q?=20real,=20now=20still=20one=20corner=20case=20left=20=E2=98=B9?=
 =?UTF-8?q?=20=E2=86=92=2011:09=E2=8E=9C=C2=ABHan:#UnixNL=C2=BB=20Ik=20=5F?=
 =?UTF-8?q?haat=5F=20bash=20=E2=87=92=20kann=20ich=20mich=20nur=20anschlie?=
 =?UTF-8?q?=C3=9Fen=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 check.t |  69 +++++++++++++++++++++++++++++++++++---
 eval.c  | 102 +++++++++++++++++++++++++++++++++++++-------------------
 mksh.1  |  38 +++++++++++++++++++--
 sh.h    |   4 +--
 4 files changed, 169 insertions(+), 44 deletions(-)

diff --git a/check.t b/check.t
index e5e324f..5099ad3 100644
--- a/check.t
+++ b/check.t
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.147 2008/02/26 20:43:10 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.148 2008/02/27 11:24:11 tg Exp $
 # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@@ -7,7 +7,7 @@
 # http://www.research.att.com/~gsf/public/ifs.sh
 
 expected-stdout:
-	@(#)MIRBSD KSH R33 2008/02/26
+	@(#)MIRBSD KSH R33 2008/02/27
 description:
 	Check version of shell.
 category: pdksh
@@ -964,7 +964,7 @@ expected-stdout:
 ---
 name: eglob-trim-1
 description:
-	Eglobing in trim expressions...
+	Eglobbing in trim expressions...
 	(at&t ksh fails this - docs say # matches shortest string, ## matches
 	longest...)
 stdin:
@@ -981,7 +981,7 @@ expected-stdout:
 ---
 name: eglob-trim-2
 description:
-	Check eglobing works in trims...
+	Check eglobbing works in trims...
 stdin:
 	x=abcdef
 	echo 1: ${x#*(a|b)cd}
@@ -994,6 +994,65 @@ expected-stdout:
 	3: abcdef
 	4: cdef
 ---
+name: eglob-substrpl-1
+description:
+	Check eglobbing works in substs... and they work at all
+stdin:
+	x=1222321_ab/cde_b/c_1221
+	echo 1: ${x/2}
+	echo 2: ${x//2}
+	echo 3: ${x/+(2)}
+	echo 4: ${x//+(2)}
+	echo 5: ${x/2/4}
+	echo 6: ${x//2/4}
+	echo 7: ${x/+(2)/4}
+	echo 8: ${x//+(2)/4}
+	echo 9: ${x/b/c/e/f}
+	echo 10: ${x/b\/c/e/f}
+	echo 11: ${x/b\/c/e\/f}
+	echo 12: ${x/b\/c/e\\/f}
+	echo 13: ${x/b\\/c/e\\/f}
+	echo 14: ${x//b/c/e/f}
+	echo 15: ${x//b\/c/e/f}
+	echo 16: ${x//b\/c/e\/f}
+	echo 17: ${x//b\/c/e\\/f}
+	echo 18: ${x//b\\/c/e\\/f}
+	echo 19: ${x/b\/*\/c/x}
+	echo 20: ${x/\//.}
+	echo 21: ${x//\//.}
+	echo 22: ${x///.}
+	echo 23: ${x//#1/9}
+	echo 24: ${x//%1/9}
+	echo 25: ${x//\%1/9}
+#	echo 26: ${x//\%1/9}
+expected-stdout:
+	1: 122321_ab/cde_b/c_1221
+	2: 131_ab/cde_b/c_11
+	3: 1321_ab/cde_b/c_1221
+	4: 131_ab/cde_b/c_11
+	5: 1422321_ab/cde_b/c_1221
+	6: 1444341_ab/cde_b/c_1441
+	7: 14321_ab/cde_b/c_1221
+	8: 14341_ab/cde_b/c_141
+	9: 1222321_ac/e/f/cde_b/c_1221
+	10: 1222321_ae/fde_b/c_1221
+	11: 1222321_ae/fde_b/c_1221
+	12: 1222321_ae\/fde_b/c_1221
+	13: 1222321_ab/cde_b/c_1221
+	14: 1222321_ac/e/f/cde_c/e/f/c_1221
+	15: 1222321_ae/fde_e/f_1221
+	16: 1222321_ae/fde_e/f_1221
+	17: 1222321_ae\/fde_e\/f_1221
+	18: 1222321_ab/cde_b/c_1221
+	19: 1222321_ax_1221
+	20: 1222321_ab.cde_b/c_1221
+	21: 1222321_ab.cde_b.c_1221
+	22: 1222321_ab/cde_b/c_1221
+	23: 9222321_ab/cde_b/c_1221
+	24: 1222321_ab/cde_b/c_1229
+	25: 1222321_ab/cde_b/c_1229
+#	26: 1222321_ab/cde_b/c_1221
+---
 name: glob-bad-1
 description:
 	Check that globbing isn't done when glob has syntax error
@@ -3231,7 +3290,7 @@ expected-stdout:
 ---
 name: regression-52
 description:
-	Check that globing works in pipelined commands
+	Check that globbing works in pipelined commands
 file-setup: file 644 "env"
 	PS1=P
 file-setup: file 644 "abc"
diff --git a/eval.c b/eval.c
index 23f736b..a3efb37 100644
--- a/eval.c
+++ b/eval.c
@@ -2,7 +2,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.37 2008/02/27 01:00:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.38 2008/02/27 11:24:11 tg Exp $");
 
 #ifdef MKSH_SMALL
 #define MKSH_NOPWNAM
@@ -379,7 +379,7 @@ expand(const char *cp,	/* input word */
 						f = DOPAT | (f&DONTRUNCOMMAND) |
 						    DOTEMP_;
 						quote = 0;
-						sqchar = '/';
+						sqchar = 0x100 | '/';
 						break;
 					case '=':
 						/* Enabling tilde expansion
@@ -600,8 +600,16 @@ expand(const char *cp,	/* input word */
 			break;
 		}
 
-		if (quote && sqchar == c)
-			*dp++ = '\\';
+		if (sqchar) {
+			/* keep backslash before backslash or sqchar */
+			if (quote || c == '\\')
+				*dp++ = '\\';
+			if (sqchar & 0x100 && (quote || (sqchar & 0xFF) != c)) {
+				/* beginning of string, ign. leading sqchars */
+				sqchar &= 0xFF;
+			} else if ((sqchar & 0xFF) == c && !quote)
+				sqchar = 0;
+		}
 
 		/* check for end of word or IFS separation */
 		if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
@@ -1004,51 +1012,76 @@ trimsub(char *str, char *pat, int how)
 	case '/':		/* replace once - SLOW! */
 	case '/'|0x80:		/* replace all - SLOWER! */
 	    {
-		char *rpat, *rrep, *tpat1, *tpat2, *sbeg, *s, *d;
+		char *rpat, *rrep, *tpat1, *tpat2, *tpat0, *sbeg, *s, *d;
 		bool gotmatch = false;
 
-		sbeg = s = str;
 		/* separate search pattern and replacement string */
-		p = d = rpat = str_save(pat, ATEMP);
- 		while (*p)
-			if (*p == '\\') {
-				p++;
-				if (*p)
-					p++;
-			} else if (*p == '/') {
-				*p++ = '\0';
-				d = p;
+		s = d = rpat = str_save(pat, ATEMP);
+ 		while ((c = *s++))
+			if (c == '\\') {
+				if (!(*d++ = *s++))
+					break;
+			} else if (c == '/') {
+				*d++ = '\0';
+				p = s;
 				gotmatch = true;
 				break;
 			} else
-				p++;
-		rrep = gotmatch ? d : null;
+				*d++ = c;
+		rrep = gotmatch ? p : null;
+		/* do not accept empty pattern */
+		if (!*rpat) {
+			afree(rpat, ATEMP);
+			return (str);
+		}
+
+		/* prepare string on which to work */
+		sbeg = s = str;
 
 		/* first see if we have any match at all */
-		tpat1 = shf_smprintf("%c%c%c*%s%c*%c)", MAGIC, '@' | 0x80,
-		    MAGIC, rpat, MAGIC, MAGIC);
-		tpat2 = shf_smprintf("%c%c%s%c*%c)", MAGIC, '@' | 0x80,
-		    rpat, MAGIC, MAGIC);
+		tpat0 = rpat;
+		if (*rpat == '\\' && (rpat[1] == '#' || rpat[1] == '%'))
+			tpat0++;
+		if (*tpat0 == '#') {
+			/* anchor at the beginning */
+			tpat0++;
+			tpat1 = shf_smprintf("%s%c*", tpat0, MAGIC);
+			tpat2 = tpat1;
+		} else if (*tpat0 == '%') {
+			/* anchor at the end */
+			tpat0++;
+			tpat1 = shf_smprintf("%c*%s", MAGIC, tpat0);
+			tpat2 = tpat0;
+		} else {
+			/* float */
+			tpat1 = shf_smprintf("%c*%s%c*", MAGIC, rpat, MAGIC);
+			tpat2 = tpat1 + 2;
+		}
  again_repl:
 		/* this would not be necessary if gmatchx would return
 		 * the start and end values of a match found, like re*
 		 */
-		if (!gmatchx(s, tpat1, false))
+		if (!gmatchx(sbeg, tpat1, false))
 			goto end_repl;
 		/* now anchor the beginning of the match */
-		while (sbeg <= end)
-			if (gmatchx(sbeg, tpat2, false))
-				break;
-			else
-				sbeg++;
+		if (*pat != '#')
+			while (sbeg <= end) {
+				if (gmatchx(sbeg, tpat2, false))
+					break;
+				else
+					sbeg++;
+			}
 		/* now anchor the end of the match */
-		for (p = end; p >= sbeg; p--) {
-			c = *p; *p = '\0';
-			gotmatch = gmatchx(sbeg, rpat, false);
-			*p = c;
-			if (gotmatch)
-				break;
-		}
+		p = end;
+		if (*pat != '%')
+			while (p >= sbeg) {
+				c = *p; *p = '\0';
+				gotmatch = gmatchx(sbeg, tpat0, false);
+				*p = c;
+				if (gotmatch)
+					break;
+				p--;
+			}
 		end = str_nsave(s, sbeg - s, ATEMP);
 		d = shf_smprintf("%s%s%s", end, rrep, p);
 		afree(end, ATEMP);
@@ -1062,7 +1095,6 @@ trimsub(char *str, char *pat, int how)
  end_repl:
 		afree(rpat, ATEMP);
 		afree(tpat1, ATEMP);
-		afree(tpat2, ATEMP);
 		return (s);
 		break;
 	    }
diff --git a/mksh.1 b/mksh.1
index 2aa9f69..a3b8e82 100644
--- a/mksh.1
+++ b/mksh.1
@@ -1,7 +1,7 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.107 2008/02/26 20:43:11 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.108 2008/02/27 11:24:12 tg Exp $
 .\" $OpenBSD: ksh.1,v 1.120 2007/05/31 20:47:44 otto Exp $
 .\"
-.Dd February 26, 2008
+.Dd February 27, 2008
 .Dt MKSH 1
 .Os MirBSD
 .Sh NAME
@@ -1235,6 +1235,40 @@ of them result in the longest match.
 .Xc
 .Sm on
 Like ${..#..} substitution, but it deletes from the end of the value.
+.Pp
+.Sm off
+.It Xo
+.Pf ${ Ar name
+.Pf / Ar pattern / Ar string No }
+.Xc
+.It Xo
+.Pf ${ Ar name
+.Pf // Ar pattern / Ar string No }
+.Xc
+.Sm on
+Like ${..#..} substitution, but it replaces the longest match of
+.Ar pattern ,
+anchored anywhere in the value, with
+.Ar string .
+If
+.Ar pattern
+begins with
+.Ql # ,
+it is anchored at the beginning of the value; if it begins with
+.Ql % ,
+it is anchored at the end.
+A single
+.Ql /
+replaces the first occurence of the search
+.Ar pattern ,
+and two of them replace all occurences.
+If
+.Pf / Ar string
+is omitted, the
+.Ar pattern
+is replaced by the empty string, i.e. deleted.
+.Pp
+.Sm off
 .It Xo
 .Pf ${ Ar name : Ns Ar pos
 .Pf : Ns Ar len Ns }
diff --git a/sh.h b/sh.h
index 4c1dd3a..f2b1dc9 100644
--- a/sh.h
+++ b/sh.h
@@ -8,8 +8,8 @@
 /*	$OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $	*/
 /*	$OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $	*/
 
-#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.188 2008/02/27 01:00:10 tg Exp $"
-#define MKSH_VERSION "R33 2008/02/26"
+#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.189 2008/02/27 11:24:12 tg Exp $"
+#define MKSH_VERSION "R33 2008/02/27"
 
 #if HAVE_SYS_PARAM_H
 #include <sys/param.h>