implement $KSH_MATCH and, to make it usable, ${foo@/bar/baz};
add a real-life example (for slagtc’s programmable tab completion) to the manpage
This commit is contained in:
parent
8f135b3904
commit
757e25fb21
41
check.t
41
check.t
@ -1,4 +1,4 @@
|
|||||||
# $MirOS: src/bin/mksh/check.t,v 1.747 2016/08/01 21:29:05 tg Exp $
|
# $MirOS: src/bin/mksh/check.t,v 1.748 2016/08/01 21:37:59 tg Exp $
|
||||||
# -*- mode: sh -*-
|
# -*- mode: sh -*-
|
||||||
#-
|
#-
|
||||||
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||||
@ -30,7 +30,7 @@
|
|||||||
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
|
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
|
||||||
|
|
||||||
expected-stdout:
|
expected-stdout:
|
||||||
@(#)MIRBSD KSH R53 2016/07/28
|
@(#)MIRBSD KSH R53 2016/08/01
|
||||||
description:
|
description:
|
||||||
Check version of shell.
|
Check version of shell.
|
||||||
stdin:
|
stdin:
|
||||||
@ -39,7 +39,7 @@ name: KSH_VERSION
|
|||||||
category: shell:legacy-no
|
category: shell:legacy-no
|
||||||
---
|
---
|
||||||
expected-stdout:
|
expected-stdout:
|
||||||
@(#)LEGACY KSH R53 2016/07/28
|
@(#)LEGACY KSH R53 2016/08/01
|
||||||
description:
|
description:
|
||||||
Check version of legacy shell.
|
Check version of legacy shell.
|
||||||
stdin:
|
stdin:
|
||||||
@ -1791,6 +1791,41 @@ stdin:
|
|||||||
expected-stdout:
|
expected-stdout:
|
||||||
1 .
|
1 .
|
||||||
---
|
---
|
||||||
|
name: expand-slashes-1
|
||||||
|
description:
|
||||||
|
Check that side effects in substring replacement are handled correctly
|
||||||
|
stdin:
|
||||||
|
foo=n1n1n1n2n3
|
||||||
|
i=2
|
||||||
|
n=1
|
||||||
|
echo 1 ${foo//n$((n++))/[$((++i))]} .
|
||||||
|
echo 2 $n , $i .
|
||||||
|
expected-stdout:
|
||||||
|
1 [3][3][3]n2n3 .
|
||||||
|
2 2 , 3 .
|
||||||
|
---
|
||||||
|
name: expand-slashes-2
|
||||||
|
description:
|
||||||
|
Check that side effects in substring replacement are handled correctly
|
||||||
|
stdin:
|
||||||
|
foo=n1n1n1n2n3
|
||||||
|
i=2
|
||||||
|
n=1
|
||||||
|
echo 1 ${foo@/n$((n++))/[$((++i))]} .
|
||||||
|
echo 2 $n , $i .
|
||||||
|
expected-stdout:
|
||||||
|
1 [3]n1n1[4][5] .
|
||||||
|
2 5 , 5 .
|
||||||
|
---
|
||||||
|
name: expand-slashes-3
|
||||||
|
description:
|
||||||
|
Check that we can access the replaced string
|
||||||
|
stdin:
|
||||||
|
foo=n1n1n1n2n3
|
||||||
|
echo 1 ${foo@/n[12]/[$KSH_MATCH]} .
|
||||||
|
expected-stdout:
|
||||||
|
1 [n1][n1][n1][n2]n3 .
|
||||||
|
---
|
||||||
name: eglob-bad-1
|
name: eglob-bad-1
|
||||||
description:
|
description:
|
||||||
Check that globbing isn't done when glob has syntax error
|
Check that globbing isn't done when glob has syntax error
|
||||||
|
65
eval.c
65
eval.c
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "sh.h"
|
#include "sh.h"
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.191 2016/07/25 00:04:41 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.192 2016/08/01 21:38:01 tg Exp $");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* string expansion
|
* string expansion
|
||||||
@ -186,7 +186,7 @@ evalonestr(const char *cp, int f)
|
|||||||
rv = (char *) *XPptrv(w);
|
rv = (char *) *XPptrv(w);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rv = evalstr(cp, f&~DOGLOB);
|
rv = evalstr(cp, f & ~DOGLOB);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
XPfree(w);
|
XPfree(w);
|
||||||
@ -478,12 +478,14 @@ expand(
|
|||||||
strndupx(x.str, beg, num, ATEMP);
|
strndupx(x.str, beg, num, ATEMP);
|
||||||
goto do_CSUBST;
|
goto do_CSUBST;
|
||||||
}
|
}
|
||||||
|
case 0x100 | '/':
|
||||||
case '/': {
|
case '/': {
|
||||||
char *s, *p, *d, *sbeg, *end;
|
char *s, *p, *d, *sbeg, *end;
|
||||||
char *pat, *rrep;
|
char *pat = NULL, *rrep = null;
|
||||||
char fpat = 0, *tpat1, *tpat2;
|
char fpat = 0, *tpat1, *tpat2;
|
||||||
|
char *ws, *wpat, *wrep;
|
||||||
|
|
||||||
s = wdcopy(sp, ATEMP);
|
s = ws = wdcopy(sp, ATEMP);
|
||||||
p = s + (wdscan(sp, ADELIM) - sp);
|
p = s + (wdscan(sp, ADELIM) - sp);
|
||||||
d = s + (wdscan(sp, CSUBST) - sp);
|
d = s + (wdscan(sp, CSUBST) - sp);
|
||||||
p[-2] = EOS;
|
p[-2] = EOS;
|
||||||
@ -492,16 +494,24 @@ expand(
|
|||||||
else
|
else
|
||||||
d[-2] = EOS;
|
d[-2] = EOS;
|
||||||
sp += (d ? d : p) - s - 1;
|
sp += (d ? d : p) - s - 1;
|
||||||
if (!(stype & 0x80) &&
|
if (!(stype & 0x180) &&
|
||||||
s[0] == CHAR &&
|
s[0] == CHAR &&
|
||||||
(s[1] == '#' || s[1] == '%'))
|
(s[1] == '#' || s[1] == '%'))
|
||||||
fpat = s[1];
|
fpat = s[1];
|
||||||
pat = evalstr(s + (fpat ? 2 : 0),
|
wpat = s + (fpat ? 2 : 0);
|
||||||
DOTILDE | DOSCALAR | DOPAT);
|
wrep = d ? p : NULL;
|
||||||
rrep = d ? evalstr(p,
|
if (!(stype & 0x100)) {
|
||||||
DOTILDE | DOSCALAR) : null;
|
rrep = wrep ? evalstr(wrep,
|
||||||
afree(s, ATEMP);
|
DOTILDE | DOSCALAR) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare string on which to work */
|
||||||
|
strdupx(s, str_val(st->var), ATEMP);
|
||||||
|
sbeg = s;
|
||||||
|
again_search:
|
||||||
|
pat = evalstr(wpat,
|
||||||
|
DOTILDE | DOSCALAR | DOPAT);
|
||||||
/* check for special cases */
|
/* check for special cases */
|
||||||
if (!*pat && !fpat) {
|
if (!*pat && !fpat) {
|
||||||
/*
|
/*
|
||||||
@ -510,19 +520,15 @@ expand(
|
|||||||
*/
|
*/
|
||||||
goto no_repl;
|
goto no_repl;
|
||||||
}
|
}
|
||||||
if ((stype & 0x80) &&
|
if ((stype & 0x180) &&
|
||||||
gmatchx(null, pat, false)) {
|
gmatchx(null, pat, false)) {
|
||||||
/*
|
/*
|
||||||
* pattern matches empty
|
* pattern matches empty
|
||||||
* string => don't loop
|
* string => don't loop
|
||||||
*/
|
*/
|
||||||
stype &= ~0x80;
|
stype &= ~0x180;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* prepare string on which to work */
|
|
||||||
strdupx(s, str_val(st->var), ATEMP);
|
|
||||||
sbeg = s;
|
|
||||||
|
|
||||||
/* first see if we have any match at all */
|
/* first see if we have any match at all */
|
||||||
if (fpat == '#') {
|
if (fpat == '#') {
|
||||||
/* anchor at the beginning */
|
/* anchor at the beginning */
|
||||||
@ -567,13 +573,27 @@ expand(
|
|||||||
break;
|
break;
|
||||||
p--;
|
p--;
|
||||||
}
|
}
|
||||||
|
strndupx(end, sbeg, p - sbeg, ATEMP);
|
||||||
|
record_match(end);
|
||||||
|
afree(end, ATEMP);
|
||||||
|
if (stype & 0x100) {
|
||||||
|
if (rrep != null)
|
||||||
|
afree(rrep, ATEMP);
|
||||||
|
rrep = wrep ? evalstr(wrep,
|
||||||
|
DOTILDE | DOSCALAR) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
strndupx(end, s, sbeg - s, ATEMP);
|
strndupx(end, s, sbeg - s, ATEMP);
|
||||||
d = shf_smprintf(Tf_sss, end, rrep, p);
|
d = shf_smprintf(Tf_sss, end, rrep, p);
|
||||||
afree(end, ATEMP);
|
afree(end, ATEMP);
|
||||||
sbeg = d + (sbeg - s) + strlen(rrep);
|
sbeg = d + (sbeg - s) + strlen(rrep);
|
||||||
afree(s, ATEMP);
|
afree(s, ATEMP);
|
||||||
s = d;
|
s = d;
|
||||||
if (stype & 0x80)
|
if (stype & 0x100) {
|
||||||
|
afree(tpat1, ATEMP);
|
||||||
|
afree(pat, ATEMP);
|
||||||
|
goto again_search;
|
||||||
|
} else if (stype & 0x80)
|
||||||
goto again_repl;
|
goto again_repl;
|
||||||
end_repl:
|
end_repl:
|
||||||
afree(tpat1, ATEMP);
|
afree(tpat1, ATEMP);
|
||||||
@ -582,6 +602,7 @@ expand(
|
|||||||
afree(pat, ATEMP);
|
afree(pat, ATEMP);
|
||||||
if (rrep != null)
|
if (rrep != null)
|
||||||
afree(rrep, ATEMP);
|
afree(rrep, ATEMP);
|
||||||
|
afree(ws, ATEMP);
|
||||||
goto do_CSUBST;
|
goto do_CSUBST;
|
||||||
}
|
}
|
||||||
case '#':
|
case '#':
|
||||||
@ -733,6 +754,7 @@ expand(
|
|||||||
debunk(dp, dp, strlen(dp) + 1));
|
debunk(dp, dp, strlen(dp) + 1));
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
|
case 0x100 | '/':
|
||||||
case '/':
|
case '/':
|
||||||
case 0x100 | '#':
|
case 0x100 | '#':
|
||||||
case 0x100 | 'Q':
|
case 0x100 | 'Q':
|
||||||
@ -1207,6 +1229,7 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||||||
case '#':
|
case '#':
|
||||||
case '?':
|
case '?':
|
||||||
case '0':
|
case '0':
|
||||||
|
case 0x100 | '/':
|
||||||
case '/':
|
case '/':
|
||||||
case 0x100 | '#':
|
case 0x100 | '#':
|
||||||
case 0x100 | 'Q':
|
case 0x100 | 'Q':
|
||||||
@ -1237,6 +1260,7 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||||||
case '#':
|
case '#':
|
||||||
case '?':
|
case '?':
|
||||||
case '0':
|
case '0':
|
||||||
|
case 0x100 | '/':
|
||||||
case '/':
|
case '/':
|
||||||
case 0x100 | '#':
|
case 0x100 | '#':
|
||||||
case 0x100 | 'Q':
|
case 0x100 | 'Q':
|
||||||
@ -1277,13 +1301,13 @@ varsub(Expand *xp, const char *sp, const char *word,
|
|||||||
|
|
||||||
c = stype & 0x7F;
|
c = stype & 0x7F;
|
||||||
/* test the compiler's code generator */
|
/* test the compiler's code generator */
|
||||||
if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
|
if (((stype < 0x100) && (ctype(c, C_SUBOP2) ||
|
||||||
(((stype & 0x80) ? *xp->str == '\0' : xp->str == null) &&
|
(((stype & 0x80) ? *xp->str == '\0' : xp->str == null) &&
|
||||||
(state != XARG || (ifs0 || xp->split ?
|
(state != XARG || (ifs0 || xp->split ?
|
||||||
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
|
(xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
|
||||||
c == '=' || c == '-' || c == '?' : c == '+'))) ||
|
c == '=' || c == '-' || c == '?' : c == '+'))) ||
|
||||||
stype == (0x80 | '0') || stype == (0x100 | '#') ||
|
stype == (0x80 | '0') || stype == (0x100 | '#') ||
|
||||||
stype == (0x100 | 'Q'))
|
stype == (0x100 | 'Q') || (stype & 0x7F) == '/')
|
||||||
/* expand word instead of variable value */
|
/* expand word instead of variable value */
|
||||||
state = XBASE;
|
state = XBASE;
|
||||||
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
|
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
|
||||||
@ -1420,6 +1444,7 @@ trimsub(char *str, char *pat, int how)
|
|||||||
for (p = str; p <= end; p += utf_ptradj(p)) {
|
for (p = str; p <= end; p += utf_ptradj(p)) {
|
||||||
c = *p; *p = '\0';
|
c = *p; *p = '\0';
|
||||||
if (gmatchx(str, pat, false)) {
|
if (gmatchx(str, pat, false)) {
|
||||||
|
record_match(str);
|
||||||
*p = c;
|
*p = c;
|
||||||
return (p);
|
return (p);
|
||||||
}
|
}
|
||||||
@ -1431,6 +1456,7 @@ trimsub(char *str, char *pat, int how)
|
|||||||
for (p = end; p >= str; p--) {
|
for (p = end; p >= str; p--) {
|
||||||
c = *p; *p = '\0';
|
c = *p; *p = '\0';
|
||||||
if (gmatchx(str, pat, false)) {
|
if (gmatchx(str, pat, false)) {
|
||||||
|
record_match(str);
|
||||||
*p = c;
|
*p = c;
|
||||||
return (p);
|
return (p);
|
||||||
}
|
}
|
||||||
@ -1458,6 +1484,7 @@ trimsub(char *str, char *pat, int how)
|
|||||||
for (p = str; p <= end; p++)
|
for (p = str; p <= end; p++)
|
||||||
if (gmatchx(p, pat, false)) {
|
if (gmatchx(p, pat, false)) {
|
||||||
trimsub_match:
|
trimsub_match:
|
||||||
|
record_match(p);
|
||||||
strndupx(end, str, p - str, ATEMP);
|
strndupx(end, str, p - str, ATEMP);
|
||||||
return (end);
|
return (end);
|
||||||
}
|
}
|
||||||
|
3
exec.c
3
exec.c
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "sh.h"
|
#include "sh.h"
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.178 2016/07/25 00:04:41 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.179 2016/08/01 21:38:02 tg Exp $");
|
||||||
|
|
||||||
#ifndef MKSH_DEFAULT_EXECSHELL
|
#ifndef MKSH_DEFAULT_EXECSHELL
|
||||||
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
|
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
|
||||||
@ -390,6 +390,7 @@ execute(struct op * volatile t,
|
|||||||
for (ap = (const char **)t->vars; *ap; ap++) {
|
for (ap = (const char **)t->vars; *ap; ap++) {
|
||||||
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
|
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
|
||||||
gmatchx(ccp, s, false))) {
|
gmatchx(ccp, s, false))) {
|
||||||
|
record_match(ccp);
|
||||||
rv = execute(t->left, flags & XERROK,
|
rv = execute(t->left, flags & XERROK,
|
||||||
xerrok);
|
xerrok);
|
||||||
i = 0;
|
i = 0;
|
||||||
|
16
funcs.c
16
funcs.c
@ -38,7 +38,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.304 2016/08/01 14:23:24 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.305 2016/08/01 21:38:02 tg Exp $");
|
||||||
|
|
||||||
#if HAVE_KILLPG
|
#if HAVE_KILLPG
|
||||||
/*
|
/*
|
||||||
@ -3202,14 +3202,20 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
|||||||
|
|
||||||
/* = */
|
/* = */
|
||||||
case TO_STEQL:
|
case TO_STEQL:
|
||||||
if (te->flags & TEF_DBRACKET)
|
if (te->flags & TEF_DBRACKET) {
|
||||||
return (gmatchx(opnd1, opnd2, false));
|
if ((i = gmatchx(opnd1, opnd2, false)))
|
||||||
|
record_match(opnd1);
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
return (strcmp(opnd1, opnd2) == 0);
|
return (strcmp(opnd1, opnd2) == 0);
|
||||||
|
|
||||||
/* != */
|
/* != */
|
||||||
case TO_STNEQ:
|
case TO_STNEQ:
|
||||||
if (te->flags & TEF_DBRACKET)
|
if (te->flags & TEF_DBRACKET) {
|
||||||
return (!gmatchx(opnd1, opnd2, false));
|
if ((i = gmatchx(opnd1, opnd2, false)))
|
||||||
|
record_match(opnd1);
|
||||||
|
return (!i);
|
||||||
|
}
|
||||||
return (strcmp(opnd1, opnd2) != 0);
|
return (strcmp(opnd1, opnd2) != 0);
|
||||||
|
|
||||||
/* < */
|
/* < */
|
||||||
|
13
lex.c
13
lex.c
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "sh.h"
|
#include "sh.h"
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.227 2016/07/25 21:05:21 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.228 2016/08/01 21:38:03 tg Exp $");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* states while lexing word
|
* states while lexing word
|
||||||
@ -462,10 +462,12 @@ yylex(int cf)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (c == '/') {
|
} else if (c == '/') {
|
||||||
|
c2 = ADELIM;
|
||||||
|
parse_adelim_slash:
|
||||||
*wp++ = CHAR;
|
*wp++ = CHAR;
|
||||||
*wp++ = c;
|
*wp++ = c;
|
||||||
if ((c = getsc()) == '/') {
|
if ((c = getsc()) == '/') {
|
||||||
*wp++ = ADELIM;
|
*wp++ = c2;
|
||||||
*wp++ = c;
|
*wp++ = c;
|
||||||
} else
|
} else
|
||||||
ungetsc(c);
|
ungetsc(c);
|
||||||
@ -475,6 +477,13 @@ yylex(int cf)
|
|||||||
statep->ls_adelim.num = 1;
|
statep->ls_adelim.num = 1;
|
||||||
statep->nparen = 0;
|
statep->nparen = 0;
|
||||||
break;
|
break;
|
||||||
|
} else if (c == '@') {
|
||||||
|
c2 = getsc();
|
||||||
|
ungetsc(c2);
|
||||||
|
if (c2 == '/') {
|
||||||
|
c2 = CHAR;
|
||||||
|
goto parse_adelim_slash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If this is a trim operation,
|
* If this is a trim operation,
|
||||||
|
74
mksh.1
74
mksh.1
@ -1,4 +1,4 @@
|
|||||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.408 2016/08/01 19:40:00 tg Exp $
|
.\" $MirOS: src/bin/mksh/mksh.1,v 1.409 2016/08/01 21:38:04 tg Exp $
|
||||||
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
|
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
|
||||||
.\"-
|
.\"-
|
||||||
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||||
@ -1682,6 +1682,25 @@ Inefficiently implemented, may be slow.
|
|||||||
.Pp
|
.Pp
|
||||||
.Sm off
|
.Sm off
|
||||||
.It Xo
|
.It Xo
|
||||||
|
.Pf ${ Ar name
|
||||||
|
.Pf @/ Ar pattern / Ar string No }
|
||||||
|
.Xc
|
||||||
|
.Sm on
|
||||||
|
The same as
|
||||||
|
.Sm off
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar name
|
||||||
|
.Pf // Ar pattern / Ar string No } ,
|
||||||
|
.Xc
|
||||||
|
.Sm on
|
||||||
|
except that both
|
||||||
|
.Ar pattern
|
||||||
|
and
|
||||||
|
.Ar string
|
||||||
|
are expanded anew for each iteration.
|
||||||
|
.Pp
|
||||||
|
.Sm off
|
||||||
|
.It Xo
|
||||||
.Pf ${ Ar name : Ns Ar pos
|
.Pf ${ Ar name : Ns Ar pos
|
||||||
.Pf : Ns Ar len Ns }
|
.Pf : Ns Ar len Ns }
|
||||||
.Xc
|
.Xc
|
||||||
@ -1933,6 +1952,54 @@ The effective group id of the shell.
|
|||||||
The real group id of the shell.
|
The real group id of the shell.
|
||||||
.It Ev KSHUID
|
.It Ev KSHUID
|
||||||
The real user id of the shell.
|
The real user id of the shell.
|
||||||
|
.It Ev KSH_MATCH
|
||||||
|
The last matched string.
|
||||||
|
In a future version, this will be an indexed array,
|
||||||
|
with indexes 1 and up capturing matching groups.
|
||||||
|
Set by string comparisons (== and !=) in double-bracket test
|
||||||
|
expressions when a match is found (when != returns false), by
|
||||||
|
.Ic case
|
||||||
|
when a match is encountered, and by the substitution operations
|
||||||
|
.Sm off
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf # Ar pat No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf ## Ar pat No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf % Ar pat No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf %% Ar pat No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf / Ar pat / Ar rpl No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf /# Ar pat / Ar rpl No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf /% Ar pat / Ar rpl No } ,
|
||||||
|
.Xc
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf // Ar pat / Ar rpl No } ,
|
||||||
|
.Xc
|
||||||
|
and
|
||||||
|
.Xo
|
||||||
|
.Pf ${ Ar x
|
||||||
|
.Pf @/ Ar pat / Ar rpl No } .
|
||||||
|
.Xc
|
||||||
|
.Sm on
|
||||||
|
See the end of the Emacs editing mode documentation for an example.
|
||||||
.It Ev KSH_VERSION
|
.It Ev KSH_VERSION
|
||||||
The name and version of the shell (read-only).
|
The name and version of the shell (read-only).
|
||||||
See also the version commands in
|
See also the version commands in
|
||||||
@ -5794,6 +5861,11 @@ Immediately after a
|
|||||||
.Ic yank ,
|
.Ic yank ,
|
||||||
replaces the inserted text string with the next previously killed text string.
|
replaces the inserted text string with the next previously killed text string.
|
||||||
.El
|
.El
|
||||||
|
.Pp
|
||||||
|
The tab completion escapes characters the same way as the following code:
|
||||||
|
.Bd -literal
|
||||||
|
print \-nr \-\- "${x@/[\e"\-\e$&\-*:\-?[\e\e\e\`{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
|
||||||
|
.Ed
|
||||||
.Ss Vi editing mode
|
.Ss Vi editing mode
|
||||||
.Em Note:
|
.Em Note:
|
||||||
The vi command-line editing mode is orphaned, yet still functional.
|
The vi command-line editing mode is orphaned, yet still functional.
|
||||||
|
5
sh.h
5
sh.h
@ -175,9 +175,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef EXTERN
|
#ifdef EXTERN
|
||||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.782 2016/08/01 20:23:15 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.783 2016/08/01 21:38:05 tg Exp $");
|
||||||
#endif
|
#endif
|
||||||
#define MKSH_VERSION "R53 2016/07/28"
|
#define MKSH_VERSION "R53 2016/08/01"
|
||||||
|
|
||||||
/* arithmetic types: C implementation */
|
/* arithmetic types: C implementation */
|
||||||
#if !HAVE_CAN_INTTYPES
|
#if !HAVE_CAN_INTTYPES
|
||||||
@ -2319,6 +2319,7 @@ uint32_t chvt_rndsetup(const void *, size_t) MKSH_A_PURE;
|
|||||||
mksh_ari_t rndget(void);
|
mksh_ari_t rndget(void);
|
||||||
void rndset(unsigned long);
|
void rndset(unsigned long);
|
||||||
void rndpush(const void *);
|
void rndpush(const void *);
|
||||||
|
void record_match(const char *);
|
||||||
|
|
||||||
enum Test_op {
|
enum Test_op {
|
||||||
/* non-operator */
|
/* non-operator */
|
||||||
|
14
var.c
14
var.c
@ -28,7 +28,7 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.206 2016/07/25 21:02:13 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.207 2016/08/01 21:38:07 tg Exp $");
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* Variables
|
* Variables
|
||||||
@ -1739,3 +1739,15 @@ rndpush(const void *s)
|
|||||||
BAFHUpdateOctet_reg(h, 0);
|
BAFHUpdateOctet_reg(h, 0);
|
||||||
qh_state = h;
|
qh_state = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* record last glob match */
|
||||||
|
void
|
||||||
|
record_match(const char *istr)
|
||||||
|
{
|
||||||
|
struct tbl *vp;
|
||||||
|
|
||||||
|
vp = local("KSH_MATCH", false);
|
||||||
|
unset(vp, 1);
|
||||||
|
vp->flag = DEFINED | RDONLY;
|
||||||
|
setstr(vp, istr, 0x4);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user