Fix redirection with variables

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1736055 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
index adb78cd..143a0e7 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
@@ -18,10 +18,13 @@
  */
 package org.apache.felix.gogo.runtime;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.io.PrintStream;
+import java.net.URI;
+import java.net.URL;
 import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 import java.nio.channels.Channel;
@@ -31,8 +34,11 @@
 import java.nio.channels.WritableByteChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -255,22 +261,25 @@
                         fd = 1;
                     }
                     boolean append = m.group(3) != null;
-                    Token file = tokens.get(++i);
-                    Path outPath = closure.session().currentDir().resolve(file.toString());
                     Set<StandardOpenOption> options = new HashSet<>();
                     options.add(StandardOpenOption.WRITE);
                     options.add(StandardOpenOption.CREATE);
-                    if (append) {
-                        options.add(StandardOpenOption.APPEND);
-                    } else {
-                        options.add(StandardOpenOption.TRUNCATE_EXISTING);
-                    }
-                    Channel ch = Files.newByteChannel(outPath, options);
-                    if (fd >= 0) {
-                        setStream(ch, fd, WRITE);
-                    } else {
-                        setStream(ch, 1, WRITE);
-                        setStream(ch, 2, WRITE);
+                    options.add(append ? StandardOpenOption.APPEND : StandardOpenOption.TRUNCATE_EXISTING);
+                    Token tok = tokens.get(++i);
+                    Object val = Expander.expand(tok, closure);
+                    for (Path p : toPaths(val))
+                    {
+                        p = closure.session().currentDir().resolve(p);
+                        Channel ch = Files.newByteChannel(p, options);
+                        if (fd >= 0)
+                        {
+                            setStream(ch, fd, WRITE);
+                        }
+                        else
+                        {
+                            setStream(ch, 1, WRITE);
+                            setStream(ch, 2, WRITE);
+                        }
                     }
                 }
                 else if ((m = Pattern.compile("([0-9])?>&([0-9])").matcher(t)).matches()) {
@@ -301,16 +310,20 @@
                         fd = Integer.parseInt(m.group(1));
                     }
                     boolean output = m.group(2) != null;
-                    Token file = tokens.get(++i);
-                    Path inPath = closure.session().currentDir().resolve(file.toString());
                     Set<StandardOpenOption> options = new HashSet<>();
                     options.add(StandardOpenOption.READ);
                     if (output) {
                         options.add(StandardOpenOption.WRITE);
                         options.add(StandardOpenOption.CREATE);
                     }
-                    Channel ch = Files.newByteChannel(inPath, options);
-                    setStream(ch, fd, READ + (output ? WRITE : 0));
+                    Token tok = tokens.get(++i);
+                    Object val = Expander.expand(tok, closure);
+                    for (Path p : toPaths(val))
+                    {
+                        p = closure.session().currentDir().resolve(p);
+                        Channel ch = Files.newByteChannel(p, options);
+                        setStream(ch, fd, READ + (output ? WRITE : 0));
+                    }
                 }
             }
 
@@ -396,6 +409,60 @@
         }
     }
 
+    private List<Path> toPaths(Object val) throws IOException
+    {
+        List<Path> paths = new ArrayList<>();
+        if (val instanceof Collection)
+        {
+            for (Object o : (Collection) val)
+            {
+                Path p = toPath(o);
+                if (p != null)
+                {
+                    paths.add(p);
+                }
+            }
+        }
+        else if (val != null)
+        {
+            Path p = toPath(val);
+            if (p != null)
+            {
+                paths.add(p);
+            }
+        }
+        if (paths.isEmpty())
+        {
+            throw new IOException("no such file or directory");
+        }
+        return paths;
+    }
+
+    private Path toPath(Object o)
+    {
+        if (o instanceof Path)
+        {
+            return (Path) o;
+        }
+        else if (o instanceof File)
+        {
+            return ((File) o).toPath();
+        }
+        else if (o instanceof URI)
+        {
+            return Paths.get((URI) o);
+        }
+        else if (o != null)
+        {
+            String s = String.valueOf(o);
+            if (!s.isEmpty())
+            {
+                return Paths.get(String.valueOf(o));
+            }
+        }
+        return null;
+    }
+
     private Channel wrap(Channel channel) {
         if (channel == null) {
             return null;
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser4.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser4.java
index 3931452..07c82f9 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser4.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser4.java
@@ -30,6 +30,7 @@
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 /*
  * Test features of the new parser/tokenizer, many of which are not supported
@@ -99,6 +100,41 @@
         assertEquals("foo\na\nb\n", c.execute("echo foo | cat <fooa | cat<foob | tac"));
     }
 
+    @Test
+    public void testRedirectWithVar() throws Exception
+    {
+        Context c = new Context();
+        c.addCommand("echo", this);
+        c.addCommand("tac", this);
+        c.addCommand("cat", this);
+
+        Path path = Paths.get("target/tmp");
+        Files.createDirectories(path);
+        c.currentDir(path);
+
+        c.execute("a=foo");
+        c.execute("echo bar > $a");
+        assertEquals("bar\n", c.execute("cat <$a | tac"));
+
+        // Empty var
+        try {
+            c.execute("echo bar > $b");
+            fail("Expected IOException");
+        } catch (IOException e) {
+        }
+
+        try {
+            c.execute("cat < $b");
+            fail("Expected IOException");
+        } catch (IOException e) {
+        }
+
+        // Array var
+        c.execute("c = [ ar1 ar2 ]");
+        c.execute("echo bar > $c");
+        assertEquals("bar\nbar\n", c.execute("cat <$c | tac"));
+    }
+
     public void echo(String msg)
     {
         System.out.println(msg);