about summary refs log tree commit diff
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.tar.xz
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.