summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2005-09-23 17:03:16 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2005-09-23 17:03:16 +0000
commitad2bd42c858aa7236e7b7404806d16b5b0efb6ac (patch)
tree3c7fd50ab1014769c6d59d15145aefca37ed02be
parentced5aab522df2754c2fa4581c3538c2cb6d4c1e1 (diff)
downloadzsh-ad2bd42c858aa7236e7b7404806d16b5b0efb6ac.tar.gz
zsh-ad2bd42c858aa7236e7b7404806d16b5b0efb6ac.zip
21758: optimise =(<<<...) to run within the shell.
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/expn.yo9
-rw-r--r--Src/exec.c67
-rw-r--r--Test/A04redirect.ztst8
4 files changed, 77 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index c2894399f..d4ce2f9d2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-09-23 Peter Stephenson <pws@csr.com>
+
+ * 21758: Doc/Zsh/expn.yo, Src/exec.c: optimise =(<<<...) to
+ replace an argument by a filename containing it within the
+ shell.
+
2005-09-22 Peter Stephenson <pws@pwstephenson.fsnet.co.uk>
* unposted, c.f. 21752: Doc/Zsh/contrib.yo,
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index a75513b25..60c74cab1 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -340,6 +340,15 @@ process. This may be used instead of the tt(<)
form for a program that expects to lseek (see manref(lseek)(2))
on the input file.
+There is an optimisation for substitutions of the form
+tt(=LPAR()<<<)var(arg)tt(RPAR()), where var(arg) is a single-word argument
+to the here-string redirection tt(<<<). This form produces a file name
+containing the value of var(arg) after any substitutions have been
+performed. This is handled entirely within the current shell. This is
+effectively the reverse of the special form tt($LPAR()<)var(arg)tt(RPAR())
+which treats var(arg) as a file name and replaces it with the file's
+contents.
+
The tt(=) form is useful as both the tt(/dev/fd) and the named pipe
implementation of tt(<LPAR())var(...)tt(RPAR()) have drawbacks. In
the former case, some programmes may automatically close the file
diff --git a/Src/exec.c b/Src/exec.c
index 95583d4e7..e77a04a53 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2941,33 +2941,50 @@ getherestr(struct redir *fn)
return fd;
}
-/* $(...) */
+/*
+ * Test if some wordcode starts with a simple redirection of type
+ * redir_type. If it does, return the name of the file, copied onto
+ * the heap. If it doesn't, return NULL.
+ */
-/**/
-LinkList
-getoutput(char *cmd, int qt)
+static char *
+simple_redir_name(Eprog prog, int redir_type)
{
- Eprog prog;
- int pipes[2];
- pid_t pid;
Wordcode pc;
- if (!(prog = parse_string(cmd)))
- return NULL;
-
pc = prog->prog;
if (prog != &dummy_eprog &&
wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) &&
wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) &&
WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END &&
wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END &&
- wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == REDIR_READ &&
+ wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == redir_type &&
!WC_REDIR_VARID(pc[3]) &&
!pc[4] &&
wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) {
+ return dupstring(ecrawstr(prog, pc + 5, NULL));
+ }
+
+ return NULL;
+}
+
+/* $(...) */
+
+/**/
+LinkList
+getoutput(char *cmd, int qt)
+{
+ Eprog prog;
+ int pipes[2];
+ pid_t pid;
+ char *s;
+
+ if (!(prog = parse_string(cmd)))
+ return NULL;
+
+ if ((s = simple_redir_name(prog, REDIR_READ))) {
/* $(< word) */
int stream;
- char *s = dupstring(ecrawstr(prog, pc + 5, NULL));
singsub(&s);
if (errflag)
@@ -3101,6 +3118,7 @@ getoutputfile(char *cmd)
char *nam;
Eprog prog;
int fd;
+ char *s;
if (thisjob == -1)
return NULL;
@@ -3109,13 +3127,36 @@ getoutputfile(char *cmd)
if (!(nam = gettempname(NULL, 0)))
return NULL;
+ if ((s = simple_redir_name(prog, REDIR_HERESTR))) {
+ /*
+ * =(<<<stuff). Optimise a la $(<file). It's
+ * effectively the reverse, converting a string into a file name
+ * rather than vice versa.
+ */
+ singsub(&s);
+ if (errflag)
+ s = NULL;
+ else
+ untokenize(s);
+ }
+
if (!jobtab[thisjob].filelist)
jobtab[thisjob].filelist = znewlinklist();
zaddlinknode(jobtab[thisjob].filelist, nam);
- child_block();
+ if (!s)
+ child_block();
fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
+ if (s) {
+ /* optimised here-string */
+ int len;
+ unmetafy(s, &len);
+ write(fd, s, len);
+ close(fd);
+ return nam;
+ }
+
if (fd < 0 || (cmdoutpid = pid = zfork(NULL)) == -1) {
/* fork or open error */
child_unblock();
diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst
index b80556797..06c380bdb 100644
--- a/Test/A04redirect.ztst
+++ b/Test/A04redirect.ztst
@@ -269,3 +269,11 @@
exec {myfd}>&-
1:Error closing file descriptor using readonly variable
?(eval):4: can't close file descriptor from readonly parameter myfd
+
+# This tests the here-string to filename optimisation; we can't
+# test that it's actually being optimised, but we can test that it
+# still works.
+ cat =(<<<$'This string has been replaced\nby a file containing it.\n')
+0:Optimised here-string to filename
+>This string has been replaced
+>by a file containing it.