almost hack ${foo//bar/baz} support for real, now

still one corner case left ☹
→ 11:09⎜«Han:#UnixNL» Ik _haat_ bash
⇒ kann ich mich nur anschließen…
This commit is contained in:
tg 2008-02-27 11:24:12 +00:00
parent 01b54f1fd5
commit 85b0cb20eb
4 changed files with 169 additions and 44 deletions

69
check.t
View File

@ -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"

102
eval.c
View File

@ -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;
}

38
mksh.1
View File

@ -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 }

4
sh.h
View File

@ -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>