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