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:
parent
01b54f1fd5
commit
85b0cb20eb
69
check.t
69
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"
|
||||
|
102
eval.c
102
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;
|
||||
}
|
||||
|
38
mksh.1
38
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 }
|
||||
|
4
sh.h
4
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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user