1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-01 05:16:05 +01:00

26042 with some fixes from 26043 (Mikael):

allow <(...) and >(...) to occur in the middle of strings
and =(...) to have extra text following
This commit is contained in:
Peter Stephenson 2008-11-13 21:18:14 +00:00
parent dcd26714bb
commit 35a8612f21
7 changed files with 143 additions and 61 deletions

View file

@ -1,3 +1,11 @@
2008-11-13 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 26042 with some fixes from 26043 (Mikael): README,
Doc/Zsh/expn.yo, Src/exec.c, Src/lex.c, Src/subst.c,
Test/D03procsubst.zst: allow <(...) and >(...) to occur
in the middle of command arguments and =(...) to have
other strings following.
2008-11-12 Oliver Kiddle <opk@zsh.org>
* 26030: Src/Zle/zle_main.c: fix memory leak in vared

View file

@ -353,12 +353,18 @@ texinode(Process Substitution)(Parameter Expansion)(History Expansion)(Expansion
sect(Process Substitution)
cindex(process substitution)
cindex(substitution, process)
Each command argument of the form
Each part of a command argument that takes the form
`tt(<LPAR())var(list)tt(RPAR())',
`tt(>LPAR())var(list)tt(RPAR())' or
`tt(=LPAR())var(list)tt(RPAR())'
is subject to process substitution.
In the case of the tt(<) or tt(>) forms, the shell runs process
is subject to process substitution. The expression may be preceeded
or followed by other strings except that, to prevent clashes with
commonly occurring strings and patterns, the last
form must occur at the start of a command argument, and none of
the forms may occur inside parentheses used for grouping of patterns or
inside parameter substitutions.
In the case of the tt(<) or tt(>) forms, the shell runs the commands in
var(list) asynchronously. If the system supports the tt(/dev/fd)
mechanism, the command argument is the name of the device file
corresponding to a file descriptor; otherwise, if the system supports named

11
README
View file

@ -69,6 +69,17 @@ always the right behaviour for the intended purpose of debugging and is
consistent with recent versions of other shells. The option
DEBUG_BEFORE_CMD can be unset to revert to the previous behaviour.
Previously, process substitutions of the form =(...), <(...) and >(...)
were only handled if they appeared as separate command arguments.
(However, the latter two forms caused the current argument to be
terminated and a new one started even if they occurred in the middle of
a string.) Now all three may be followed by other strings, and the
latter two may also be preceeded by other strings. None may occur inside
parameter substitutions, or inside parentheses used for grouping of
patterns, in order to avoid clashes with cases where
tt(<) or tt(>) where not treated specially in previous versions of the
shell.
In previous versions of the shell it was possible to use index 0 in an
array or string subscript to refer to the same element as index 1 if the
option KSH_ARRAYS was not in effect. This was a limited approximation to

View file

@ -3560,7 +3560,7 @@ readoutput(int in, int qt)
/**/
static Eprog
parsecmd(char *cmd)
parsecmd(char *cmd, char **eptr)
{
char *str;
Eprog prog;
@ -3571,7 +3571,9 @@ parsecmd(char *cmd)
return NULL;
}
*str = '\0';
if (str[1] || !(prog = parse_string(cmd + 2, 0))) {
if (eptr)
*eptr = str+1;
if (!(prog = parse_string(cmd + 2, 0))) {
zerr("parse error in process substitution");
return NULL;
}
@ -3582,7 +3584,7 @@ parsecmd(char *cmd)
/**/
char *
getoutputfile(char *cmd)
getoutputfile(char *cmd, char **eptr)
{
pid_t pid;
char *nam;
@ -3592,7 +3594,7 @@ getoutputfile(char *cmd)
if (thisjob == -1)
return NULL;
if (!(prog = parsecmd(cmd)))
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
if (!(nam = gettempname(NULL, 0)))
return NULL;
@ -3677,7 +3679,7 @@ namedpipe(void)
/**/
char *
getproc(char *cmd)
getproc(char *cmd, char **eptr)
{
#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD)
zerr("doesn't look like your system supports FIFOs.");
@ -3696,7 +3698,7 @@ getproc(char *cmd)
return NULL;
if (!(pnam = namedpipe()))
return NULL;
if (!(prog = parsecmd(cmd)))
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
if (!jobtab[thisjob].filelist)
jobtab[thisjob].filelist = znewlinklist();
@ -3723,7 +3725,7 @@ getproc(char *cmd)
if (thisjob == -1)
return NULL;
pnam = hcalloc(strlen(PATH_DEV_FD) + 6);
if (!(prog = parsecmd(cmd)))
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
mpipe(pipes);
if ((pid = zfork(&bgtime))) {
@ -3772,7 +3774,7 @@ getpipe(char *cmd, int nullexec)
pid_t pid;
struct timeval bgtime;
if (!(prog = parsecmd(cmd)))
if (!(prog = parsecmd(cmd, NULL)))
return -1;
mpipe(pipes);
if ((pid = zfork(&bgtime))) {

View file

@ -835,7 +835,7 @@ gettok(void)
return OUTPAR;
case LX1_INANG:
d = hgetc();
if (!incmdpos && d == '(') {
if (d == '(') {
hungetc(d);
lexstop = 0;
unpeekfd:
@ -1152,20 +1152,13 @@ gettokstr(int c, int sub)
c = Comma;
break;
case LX2_OUTANG:
if (!intpos) {
if (in_brace_param || sub)
break;
else
goto brk;
}
if (in_brace_param || sub)
break;
e = hgetc();
if (e != '(') {
hungetc(e);
lexstop = 0;
if (in_brace_param || sub)
break;
else
goto brk;
goto brk;
}
add(Outang);
if (skipcomm()) {
@ -1178,7 +1171,7 @@ gettokstr(int c, int sub)
if (isset(SHGLOB) && sub)
break;
e = hgetc();
if(e == '(' && intpos) {
if (!(in_brace_param || sub) && e == '(') {
add(Inang);
if (skipcomm()) {
peek = LEXERR;

View file

@ -56,43 +56,27 @@ prefork(LinkList list, int flags)
queue_signals();
for (node = firstnode(list); node; incnode(node)) {
char *str, c;
str = (char *)getdata(node);
if (((c = *str) == Inang || c == Outang || c == Equals) &&
str[1] == Inpar) {
if (c == Inang || c == Outang)
setdata(node, (void *) getproc(str)); /* <(...) or >(...) */
else
setdata(node, (void *) getoutputfile(str)); /* =(...) */
if (!getdata(node)) {
setdata(node, dupstring(""));
unqueue_signals();
return;
}
} else {
if (isset(SHFILEEXPANSION)) {
/*
* Here and below we avoid taking the address
* of a void * and then pretending it's a char **
* instead of a void ** by a little inefficiency.
* This could be avoided with some extra linked list
* machinery, but that would need quite a lot of work
* to ensure consistency. What we really need is
* templates...
*/
char *cptr = (char *)getdata(node);
filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
/*
* The assignment is so simple it's not worth
* testing if cptr changed...
*/
setdata(node, cptr);
}
if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
unqueue_signals();
return;
}
if (isset(SHFILEEXPANSION)) {
/*
* Here and below we avoid taking the address
* of a void * and then pretending it's a char **
* instead of a void ** by a little inefficiency.
* This could be avoided with some extra linked list
* machinery, but that would need quite a lot of work
* to ensure consistency. What we really need is
* templates...
*/
char *cptr = (char *)getdata(node);
filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
/*
* The assignment is so simple it's not worth
* testing if cptr changed...
*/
setdata(node, cptr);
}
if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
unqueue_signals();
return;
}
}
for (node = firstnode(list); node; incnode(node)) {
@ -168,7 +152,37 @@ stringsubst(LinkList list, LinkNode node, int ssub, int asssub)
char *str = str3, c;
while (!errflag && (c = *str)) {
if ((qt = c == Qstring) || c == String) {
if ((c == Inang || c == Outang || (str == str3 && c == Equals)) &&
str[1] == Inpar) {
char *subst, *rest, *snew, *sptr;
int str3len = str - str3, sublen, restlen;
if (c == Inang || c == Outang)
subst = getproc(str, &rest); /* <(...) or >(...) */
else
subst = getoutputfile(str, &rest); /* =(...) */
if (!subst)
subst = "";
sublen = strlen(subst);
restlen = strlen(rest);
sptr = snew = hcalloc(str3len + sublen + restlen + 1);
if (str3len) {
memcpy(sptr, str3, str3len);
sptr += str3len;
}
if (sublen) {
memcpy(sptr, subst, sublen);
sptr += sublen;
}
if (restlen)
memcpy(sptr, rest, restlen);
sptr[restlen] = '\0';
str3 = snew;
str = snew + str3len + sublen;
setdata(node, str3);
continue;
} else if ((qt = c == Qstring) || c == String) {
if ((c = str[1]) == Inpar) {
if (!qt)
list->list.flags |= LF_ARRAY;

View file

@ -36,3 +36,51 @@
0:FDs remain open for external commands called from functions
>First
>Zweite
catfield2() {
local -a args
args=(${(s.,.)1})
print $args[1]
cat $args[2]
print $args[3]
}
catfield2 up,<(print $'\x64'own),sideways
0:<(...) when embedded within an argument
>up
>down
>sideways
outputfield2() {
local -a args
args=(${(s.,.)1})
print $args[1]
echo 'How sweet the moonlight sits upon the bank' >$args[2]
print $args[3]
}
outputfield2 muddy,>(sed -e s/s/th/g >outputfield2.txt),vesture
# yuk
while [[ ! -e outputfield2.txt || ! -s outputfield2.txt ]]; do :; done
cat outputfield2.txt
0:>(...) when embedded within an argument
>muddy
>vesture
>How thweet the moonlight thitth upon the bank
catfield1() {
local -a args
args=(${(s.,.)1})
cat $args[1]
print $args[2]
}
catfield1 =(echo s$'\x69't),jessica
0:=(...) followed by something else without a break
>sit
>jessica
(
setopt nonomatch
# er... why is this treated as a glob?
print everything,=(here is left),alone
)
0:=(...) preceded by other stuff has no special effect
>everything,=(here is left),alone