diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index c09506f6464642f6048f9a925fd299f5b9f6ca90..f3119ba912260d9230d786fa75254fe960238358 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -267,6 +267,8 @@ public class BasePackage extends RBuiltinPackage {
         add(ConnectionFunctions.SocketConnection.class, ConnectionFunctionsFactory.SocketConnectionNodeGen::create);
         add(ConnectionFunctions.RawConnection.class, ConnectionFunctionsFactory.RawConnectionNodeGen::create);
         add(ConnectionFunctions.RawConnectionValue.class, ConnectionFunctionsFactory.RawConnectionValueNodeGen::create);
+        add(ConnectionFunctions.Fifo.class, ConnectionFunctionsFactory.FifoNodeGen::create);
+        add(ConnectionFunctions.Pipe.class, ConnectionFunctionsFactory.PipeNodeGen::create);
         add(ConnectionFunctions.Stderr.class, ConnectionFunctionsFactory.StderrNodeGen::create);
         add(ConnectionFunctions.Stdin.class, ConnectionFunctionsFactory.StdinNodeGen::create);
         add(ConnectionFunctions.Stdout.class, ConnectionFunctionsFactory.StdoutNodeGen::create);
@@ -277,6 +279,7 @@ public class BasePackage extends RBuiltinPackage {
         add(ConnectionFunctions.WriteBin.class, ConnectionFunctionsFactory.WriteBinNodeGen::create);
         add(ConnectionFunctions.WriteChar.class, ConnectionFunctionsFactory.WriteCharNodeGen::create);
         add(ConnectionFunctions.WriteLines.class, ConnectionFunctionsFactory.WriteLinesNodeGen::create);
+        add(ConnectionFunctions.IsIncomplete.class, ConnectionFunctionsFactory.IsIncompleteNodeGen::create);
         add(Contributors.class, ContributorsNodeGen::create);
         add(CopyDFAttr.class, CopyDFAttrNodeGen::create);
         add(Crossprod.class, CrossprodNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Capabilities.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Capabilities.java
index 109ddc233bd053d03bc66ec90a3f0fc9d9e0e74b..b63bb9fb4ee8b1ca853fe580049c8310600be019 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Capabilities.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Capabilities.java
@@ -46,7 +46,7 @@ public abstract class Capabilities extends RBuiltinNode {
         http_fttp(true, "http/ftp"),
         sockets(true, null),
         libxml(false, null),
-        fifo(false, null),
+        fifo(true, null),
         cledit(false, null),
         iconv(false, null),
         nls(false, "NLS"),
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index c5c88dd46deb72138752c13de9f9f74a7dbb5f01..30aa8c4efb3bddaa7fb3ba973fa57c835891e778 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -49,10 +49,12 @@ import static com.oracle.truffle.r.runtime.conn.StdConnections.getStdout;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
+import java.net.URL;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.DoubleBuffer;
 import java.nio.IntBuffer;
+import java.nio.charset.IllegalCharsetNameException;
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -63,15 +65,18 @@ import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.ConnectionFunctionsFactory.WriteDataNodeGen;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.HeadPhaseBuilder;
+import com.oracle.truffle.r.nodes.builtin.casts.fluent.InitialPhaseBuilder;
 import com.oracle.truffle.r.runtime.RCompression;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.conn.CompressedConnections.CompressedRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.conn.FifoConnections.FifoRConnection;
+import com.oracle.truffle.r.runtime.conn.FileConnections.CompressedRConnection;
 import com.oracle.truffle.r.runtime.conn.FileConnections.FileRConnection;
+import com.oracle.truffle.r.runtime.conn.PipeConnections.PipeRConnection;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.RawConnections.RawRConnection;
 import com.oracle.truffle.r.runtime.conn.SocketConnections.RSocketConnection;
@@ -139,8 +144,16 @@ public abstract class ConnectionFunctions {
     }
 
     public static final class CastsHelper {
-        private static void description(Casts casts) {
-            casts.arg("description").mustBe(stringValue()).asStringVector().shouldBe(singleElement(), RError.Message.ARGUMENT_ONLY_FIRST_1, "description").findFirst().mustNotBeNA();
+        private static HeadPhaseBuilder<String> description(Casts casts) {
+            return descriptionInternal(casts.arg("description"));
+        }
+
+        private static HeadPhaseBuilder<String> descriptionNull(Casts casts) {
+            return descriptionInternal(casts.arg("description").allowNull());
+        }
+
+        private static HeadPhaseBuilder<String> descriptionInternal(InitialPhaseBuilder<Object> casts) {
+            return casts.mustBe(stringValue()).asStringVector().shouldBe(singleElement(), RError.Message.ARGUMENT_ONLY_FIRST_1, "description").findFirst().mustNotBeNA();
         }
 
         private static HeadPhaseBuilder<String> open(Casts casts) {
@@ -189,7 +202,11 @@ public abstract class ConnectionFunctions {
         }
 
         private static void method(Casts casts) {
-            casts.arg("method").asStringVector().findFirst();
+            casts.arg("method").asStringVector().findFirst().mustBe(equalTo("default").or(equalTo("internal")), RError.Message.UNSUPPORTED_URL_METHOD);
+        }
+
+        static void blockingNotSupported(Casts casts) {
+            casts.arg("blocking").asLogicalVector().findFirst().mustBe(logicalTrue(), RError.Message.NYI, "non-blocking mode not supported").map(toBoolean());
         }
     }
 
@@ -200,7 +217,7 @@ public abstract class ConnectionFunctions {
             Casts casts = new Casts(File.class);
             CastsHelper.description(casts);
             CastsHelper.open(casts);
-            casts.arg("blocking").asLogicalVector().findFirst().mustBe(logicalTrue(), RError.Message.NYI, "non-blocking mode not supported").map(toBoolean());
+            CastsHelper.blocking(casts);
             CastsHelper.encoding(casts);
             CastsHelper.method(casts);
             CastsHelper.raw(casts);
@@ -208,11 +225,25 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        @SuppressWarnings("unused")
-        protected RAbstractIntVector file(String description, String openArg, boolean blocking, String encoding, String method, boolean raw) {
+        protected RAbstractIntVector file(String description, String openArg, boolean blocking, String encoding, @SuppressWarnings("unused") String method, boolean raw) {
             String open = openArg;
-            // TODO handle http/ftp prefixes and redirect and method
-            String path = removeFileURLPrefix(description);
+
+            // check if the description is an URL and dispatch if necessary
+            String path = description;
+            try {
+                URL url = new URL(description);
+                if (!"file".equals(url.getProtocol())) {
+                    return new URLRConnection(description, open, encoding).asVector();
+                } else {
+                    path = removeFileURLPrefix(description);
+                }
+            } catch (MalformedURLException e) {
+                // ignore and try to open file
+            } catch (IOException e) {
+                RError.warning(RError.SHOW_CALLER, RError.Message.UNABLE_TO_RESOLVE, e.getMessage());
+                throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_OPEN_CONNECTION);
+            }
+
             if (path.length() == 0) {
                 // special case, temp file opened in "w+" or "w+b" only
                 if (open.length() == 0) {
@@ -225,10 +256,12 @@ public abstract class ConnectionFunctions {
                 }
             }
             try {
-                return new FileRConnection(path, open).asVector();
+                return new FileRConnection(description, path, open, blocking, encoding, raw).asVector();
             } catch (IOException ex) {
                 warning(RError.Message.CANNOT_OPEN_FILE, description, ex.getMessage());
                 throw error(RError.Message.CANNOT_OPEN_CONNECTION);
+            } catch (IllegalCharsetNameException ex) {
+                throw error(RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
             }
         }
     }
@@ -256,11 +289,13 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RAbstractIntVector zzFile(RAbstractStringVector description, String open, String encoding, int compression) {
+        protected RAbstractIntVector zzFile(String description, String open, String encoding, int compression) {
             try {
-                return new CompressedRConnection(description.getDataAt(0), open, cType, encoding, compression).asVector();
+                return new CompressedRConnection(description, open, cType, encoding, compression).asVector();
             } catch (IOException ex) {
-                throw reportError(description.getDataAt(0), ex);
+                throw reportError(description, ex);
+            } catch (IllegalCharsetNameException ex) {
+                throw RError.error(RError.SHOW_CALLER, RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
             }
         }
 
@@ -308,10 +343,7 @@ public abstract class ConnectionFunctions {
 
         static {
             Casts casts = new Casts(TextConnection.class);
-            CastsHelper.description(casts);
-            // TODO how to have either a RNull or a String/RStringVector and have the latter coerced
-            // to a
-            // RAbstractStringVector to avoid the explicit handling in the specialization
+            CastsHelper.descriptionNull(casts);
             casts.arg("text").allowNull().mustBe(stringValue());
             CastsHelper.open(casts).mustBe(equalTo("").or(equalTo("r").or(equalTo("w").or(equalTo("a")))), RError.Message.UNSUPPORTED_MODE);
             casts.arg("env").mustNotBeNull(RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class));
@@ -328,15 +360,9 @@ public abstract class ConnectionFunctions {
             }
         }
 
-        @SuppressWarnings("unused")
         @Specialization
-        @TruffleBoundary
-        protected RAbstractIntVector textConnection(String description, RNull text, String open, REnvironment env, int encoding) {
-            if (open.length() == 0 || open.equals("r")) {
-                throw error(RError.Message.INVALID_ARGUMENT, "text");
-            } else {
-                throw RError.nyi(RError.SHOW_CALLER, "textConnection: NULL");
-            }
+        protected RAbstractIntVector textConnection(String description, @SuppressWarnings("unused") RNull text, String open, REnvironment env, int encoding) {
+            return textConnection(description, (RAbstractStringVector) null, open, env, encoding);
         }
     }
 
@@ -350,10 +376,10 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object textConnection(int con) {
+        protected RAbstractStringVector textConnection(int con) {
             RConnection connection = RConnection.fromIndex(con);
             if (connection instanceof TextRConnection) {
-                return RDataFactory.createStringVector(((TextRConnection) connection).getValue(), RDataFactory.COMPLETE_VECTOR);
+                return ((TextRConnection) connection).getValue();
             } else {
                 throw error(Message.NOT_A_TEXT_CONNECTION);
             }
@@ -375,11 +401,14 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RAbstractIntVector socketConnection(String host, int port, boolean server, boolean blocking, String open, @SuppressWarnings("unused") RAbstractStringVector encoding, int timeout) {
+        protected RAbstractIntVector socketConnection(String host, int port, boolean server, boolean blocking, String open,
+                        String encoding, int timeout) {
             try {
-                return new RSocketConnection(open, server, host, port, blocking, timeout).asVector();
+                return new RSocketConnection(open, server, host, port, blocking, timeout, encoding).asVector();
             } catch (IOException ex) {
                 throw error(RError.Message.CANNOT_OPEN_CONNECTION);
+            } catch (IllegalCharsetNameException ex) {
+                throw error(RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
             }
         }
     }
@@ -398,14 +427,16 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RAbstractIntVector urlConnection(String url, String open, @SuppressWarnings("unused") boolean blocking, @SuppressWarnings("unused") String encoding,
+        protected RAbstractIntVector urlConnection(String url, String open, @SuppressWarnings("unused") boolean blocking, String encoding,
                         @SuppressWarnings("unused") String method) {
             try {
-                return new URLRConnection(url, open).asVector();
+                return new URLRConnection(url, open, encoding).asVector();
             } catch (MalformedURLException ex) {
                 throw error(RError.Message.UNSUPPORTED_URL_SCHEME);
             } catch (IOException ex) {
                 throw error(RError.Message.CANNOT_OPEN_CONNECTION);
+            } catch (IllegalCharsetNameException ex) {
+                throw error(RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
             }
         }
     }
@@ -452,7 +483,7 @@ public abstract class ConnectionFunctions {
         @Specialization
         @TruffleBoundary
         protected Object textConnection(int con) {
-            RConnection connection = RConnection.fromIndex(con);
+            BaseRConnection connection = RConnection.fromIndex(con);
             if (connection instanceof RawRConnection) {
                 return RDataFactory.createRawVector(((RawRConnection) connection).getValue());
             } else {
@@ -585,7 +616,7 @@ public abstract class ConnectionFunctions {
         @Specialization
         @TruffleBoundary
         protected Object readLines(int con, int n, boolean ok, boolean warn, @SuppressWarnings("unused") String encoding, boolean skipNul) {
-            // TODO implement all the arguments
+            // TODO Implement argument 'encoding'.
             try (RConnection openConn = RConnection.fromIndex(con).forceOpen("rt")) {
                 String[] lines = openConn.readLines(n, warn, skipNul);
                 if (n > 0 && lines.length < n && !ok) {
@@ -708,7 +739,7 @@ public abstract class ConnectionFunctions {
         @Specialization(guards = "!ncharsEmpty(nchars)")
         @TruffleBoundary
         protected RStringVector readChar(int con, RAbstractIntVector nchars, boolean useBytes) {
-            try (RConnection openConn = RConnection.fromIndex(con).forceOpen("rb")) {
+            try (BaseRConnection openConn = RConnection.fromIndex(con).forceOpen("rb")) {
                 String[] data = new String[nchars.getLength()];
                 for (int i = 0; i < data.length; i++) {
                     data[i] = openConn.readChar(nchars.getDataAt(i), useBytes);
@@ -748,10 +779,10 @@ public abstract class ConnectionFunctions {
         @TruffleBoundary
         private RNull writeCharGeneric(RAbstractStringVector object, int con, RAbstractIntVector nchars, RAbstractStringVector sep, boolean useBytes) {
             try (RConnection openConn = RConnection.fromIndex(con).forceOpen("wb")) {
-                int length = object.getLength();
+                final int length = object.getLength();
+                final int ncharsLen = nchars.getLength();
                 for (int i = 0; i < length; i++) {
-                    // FIXME: 'i % length' is probably wrong
-                    int nc = nchars.getDataAt(i % length);
+                    int nc = nchars.getDataAt(i % ncharsLen);
                     String s = object.getDataAt(i);
                     final int writeLen = Math.min(s.length(), nc);
                     int pad = nc - s.length();
@@ -1187,13 +1218,15 @@ public abstract class ConnectionFunctions {
              * for the NA (enquiry) case.
              */
             long offset = 0;
+            final int actualOrigin;
             if (RRuntime.isNAorNaN(where)) {
-                origin = 0;
+                actualOrigin = 0;
             } else {
                 offset = (long) where;
+                actualOrigin = origin;
             }
             try {
-                long newOffset = RConnection.fromIndex(con).seek(offset, RConnection.SeekMode.values()[origin], RConnection.SeekRWMode.values()[rw]);
+                long newOffset = RConnection.fromIndex(con).seek(offset, RConnection.SeekMode.values()[actualOrigin], RConnection.SeekRWMode.values()[rw]);
                 if (newOffset > Integer.MAX_VALUE) {
                     throw RError.nyi(RError.SHOW_CALLER, "seek > Integer.MAX_VALUE");
                 }
@@ -1203,4 +1236,76 @@ public abstract class ConnectionFunctions {
             }
         }
     }
+
+    @RBuiltin(name = "fifo", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding"}, behavior = IO)
+    public abstract static class Fifo extends RBuiltinNode {
+
+        static {
+            Casts casts = new Casts(Fifo.class);
+            CastsHelper.description(casts);
+            CastsHelper.open(casts);
+            // We cannot support non-blocking because Java does simply not allow to open a file or
+            // named pipe in non-blocking mode.
+            CastsHelper.blockingNotSupported(casts);
+            CastsHelper.encoding(casts);
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected RAbstractIntVector fifo(String path, String openArg, boolean blocking, String encoding) {
+
+            String open = openArg;
+            try {
+                return new FifoRConnection(path, open, blocking, encoding).asVector();
+            } catch (IOException ex) {
+                RError.warning(this, RError.Message.CANNOT_OPEN_FIFO, path);
+                throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_OPEN_CONNECTION);
+            } catch (IllegalCharsetNameException ex) {
+                throw RError.error(RError.SHOW_CALLER, RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
+            }
+        }
+    }
+
+    @RBuiltin(name = "pipe", kind = INTERNAL, parameterNames = {"description", "open", "encoding"}, behavior = IO)
+    public abstract static class Pipe extends RBuiltinNode {
+
+        static {
+            Casts casts = new Casts(Pipe.class);
+            CastsHelper.description(casts);
+            CastsHelper.open(casts);
+            CastsHelper.encoding(casts);
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected RAbstractIntVector pipe(String path, String openArg, String encoding) {
+
+            String open = openArg;
+            try {
+                return new PipeRConnection(path, open, encoding).asVector();
+            } catch (IOException ex) {
+                RError.warning(this, RError.Message.CANNOT_OPEN_FIFO, path);
+                throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_OPEN_CONNECTION);
+            } catch (IllegalCharsetNameException ex) {
+                throw RError.error(RError.SHOW_CALLER, RError.Message.UNSUPPORTED_ENCODING_CONVERSION, encoding, "");
+            }
+        }
+    }
+
+    @RBuiltin(name = "isIncomplete", kind = INTERNAL, parameterNames = {"con"}, behavior = IO)
+    public abstract static class IsIncomplete extends RBuiltinNode {
+
+        static {
+            Casts casts = new Casts(IsIncomplete.class);
+            CastsHelper.connection(casts);
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected RLogicalVector isIncomplete(int con) {
+
+            final boolean res = RConnection.fromIndex(con).isIncomplete();
+            return RDataFactory.createLogicalVectorFromScalar(res);
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
index a1d9f81682824f9db676bb090289431879662522..b80c2f0d91394557a75151b63c341b280c201d4b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
@@ -212,6 +212,8 @@ public class RCompression {
             if (rc == 0) {
                 readThread.join();
                 return Arrays.copyOf(readThread.getData(), readThread.getTotalRead());
+            } else {
+                throw new IOException("bzip2 error code: " + rc);
             }
         } catch (InterruptedException ex) {
             // fall through
@@ -239,6 +241,8 @@ public class RCompression {
                 OpenOption[] openOptions = append ? new OpenOption[]{StandardOpenOption.APPEND} : new OpenOption[0];
                 Files.write(Paths.get(path), cData, openOptions);
                 return;
+            } else {
+                throw new IOException("bzip2 error code: " + rc);
             }
         } catch (InterruptedException ex) {
             // fall through
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index ad5120700f477af7b7e2fdb65e2feed6b8c62db3..09e7edf54a8abe767c81b58b44c309bd17696021 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -373,6 +373,7 @@ public final class RError extends RuntimeException {
         ONLY_WRITE_BINARY_CONNECTION("can only write to a binary connection"),
         ONLY_WRITE_CHAR_OBJECTS("can only write character objects"),
         NOT_A_TEXT_CONNECTION("'con' is not a textConnection"),
+        NOT_AN_OUTPUT_TEXT_CONNECTION("'con' is not an output textConnection"),
         UNSEEKABLE_CONNECTION("'con' is not seekable"),
         MUST_BE_STRING_OR_CONNECTION("'%s' must be a character string or a connection"),
         MORE_CHARACTERS("writeChar: more characters requested than are in the string - will zero-pad"),
@@ -831,7 +832,14 @@ public final class RError extends RuntimeException {
         NOT_AN_OUTPUT_RAW_CONNECTION("'con' is not an output rawConnection"),
         NOT_A_RAW_CONNECTION("'con' is not a rawConnection"),
         SEEK_OUTSITE_RAW_CONNECTION("attempt to seek outside the range of the raw connection"),
-        VECTOR_IS_TOO_LARGE("vector is too large");
+        VECTOR_IS_TOO_LARGE("vector is too large"),
+        SEEK_NOT_RELEVANT_FOR_TEXT_CON("seek is not relevant for text connection"),
+        NOT_ENABLED_FOR_THIS_CONN("'%s' not enabled for this connection"),
+        CANNOT_OPEN_FIFO("cannot open fifo '%s'"),
+        UNSUPPORTED_ENCODING_CONVERSION("unsupported conversion from '%s' to '%s'"),
+        UNABLE_TO_RESOLVE("unable to resolve '%s'"),
+        LINE_CONTAINS_EMBEDDED_NULLS("line %d appears to contain an embedded nul"),
+        UNSUPPORTED_URL_METHOD("method = \"%s\" is not supported");
 
         public final String message;
         final boolean hasArgs;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/CompressedConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/CompressedConnections.java
deleted file mode 100644
index 7bf6d1af13b9fb97d677e675c67c74b63b24618f..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/CompressedConnections.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.runtime.conn;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
-import org.tukaani.xz.LZMA2Options;
-import org.tukaani.xz.XZ;
-import org.tukaani.xz.XZInputStream;
-import org.tukaani.xz.XZOutputStream;
-
-import com.oracle.truffle.r.runtime.RCompression;
-import com.oracle.truffle.r.runtime.RCompression.Type;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BasePathRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateWriteRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ReadWriteHelper;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-
-public class CompressedConnections {
-    public static final int GZIP_BUFFER_SIZE = (2 << 20);
-
-    /**
-     * Base class for all modes of gzfile/bzfile/xzfile connections. N.B. In GNU R these can read
-     * gzip, bzip, lzma and uncompressed files, and this has to be implemented by reading the first
-     * few bytes of the file and detecting the type of the file.
-     */
-    public static class CompressedRConnection extends BasePathRConnection {
-        private final RCompression.Type cType;
-        @SuppressWarnings("unused") private final String encoding; // TODO
-        @SuppressWarnings("unused") private final int compression; // TODO
-
-        public CompressedRConnection(String path, String modeString, Type cType, String encoding, int compression) throws IOException {
-            super(path, mapConnectionClass(cType), modeString, AbstractOpenMode.ReadBinary);
-            this.cType = cType;
-            this.encoding = encoding;
-            this.compression = compression;
-            openNonLazyConnection();
-        }
-
-        private static ConnectionClass mapConnectionClass(RCompression.Type cType) {
-            switch (cType) {
-                case NONE:
-                    return ConnectionClass.File;
-                case GZIP:
-                    return ConnectionClass.GZFile;
-                case BZIP2:
-                    return ConnectionClass.BZFile;
-                case XZ:
-                    return ConnectionClass.XZFile;
-                default:
-                    throw RInternalError.shouldNotReachHere();
-            }
-        }
-
-        @Override
-        protected void createDelegateConnection() throws IOException {
-            DelegateRConnection delegate = null;
-            AbstractOpenMode openMode = getOpenMode().abstractOpenMode;
-            switch (openMode) {
-                case Read:
-                case ReadBinary:
-                    /*
-                     * For input, we check the actual compression type as GNU R is permissive about
-                     * the claimed type.
-                     */
-                    RCompression.Type cTypeActual = RCompression.getCompressionType(path);
-                    if (cTypeActual != cType) {
-                        updateConnectionClass(mapConnectionClass(cTypeActual));
-                    }
-                    switch (cTypeActual) {
-                        case NONE:
-                            if (openMode == AbstractOpenMode.ReadBinary) {
-                                delegate = new FileConnections.FileReadBinaryRConnection(this);
-                            } else {
-                                delegate = new FileConnections.FileReadTextRConnection(this);
-                            }
-                            break;
-                        case GZIP:
-                            delegate = new CompressedInputRConnection(this, new GZIPInputStream(new FileInputStream(path), GZIP_BUFFER_SIZE));
-                            break;
-                        case XZ:
-                            delegate = new CompressedInputRConnection(this, new XZInputStream(new FileInputStream(path)));
-                            break;
-                        case BZIP2:
-                            // no in Java support, so go via byte array
-                            byte[] bzipUdata = RCompression.bzipUncompressFromFile(path);
-                            delegate = new ByteStreamCompressedInputRConnection(this, new ByteArrayInputStream(bzipUdata));
-                    }
-                    break;
-
-                case Append:
-                case AppendBinary:
-                case Write:
-                case WriteBinary: {
-                    boolean append = openMode == AbstractOpenMode.Append || openMode == AbstractOpenMode.AppendBinary;
-                    switch (cType) {
-                        case GZIP:
-                            delegate = new CompressedOutputRConnection(this, new GZIPOutputStream(new FileOutputStream(path, append), GZIP_BUFFER_SIZE));
-                            break;
-                        case BZIP2:
-                            delegate = new BZip2OutputRConnection(this, new ByteArrayOutputStream(), append);
-                            break;
-                        case XZ:
-                            delegate = new CompressedOutputRConnection(this, new XZOutputStream(new FileOutputStream(path, append), new LZMA2Options(), XZ.CHECK_CRC32));
-                            break;
-                    }
-                    break;
-                }
-                default:
-                    throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode());
-            }
-            setDelegate(delegate);
-        }
-
-        // @Override
-        /**
-         * GnuR behavior for lazy connections is odd, e.g. gzfile returns "text", even though the
-         * default mode is "rb".
-         */
-        // public boolean isTextMode() {
-        // }
-    }
-
-    private static class CompressedInputRConnection extends DelegateReadRConnection implements ReadWriteHelper {
-        private final InputStream inputStream;
-
-        protected CompressedInputRConnection(CompressedRConnection base, InputStream is) {
-            super(base);
-            this.inputStream = is;
-        }
-
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, inputStream, useBytes);
-        }
-
-        @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            return readBinHelper(buffer, inputStream);
-        }
-
-        @Override
-        public byte[] readBinChars() throws IOException {
-            return readBinCharsHelper(inputStream);
-        }
-
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            inputStream.close();
-        }
-    }
-
-    private static class ByteStreamCompressedInputRConnection extends CompressedInputRConnection {
-        ByteStreamCompressedInputRConnection(CompressedRConnection base, ByteArrayInputStream is) {
-            super(base, is);
-        }
-    }
-
-    private static class CompressedOutputRConnection extends DelegateWriteRConnection implements ReadWriteHelper {
-        protected OutputStream outputStream;
-
-        protected CompressedOutputRConnection(CompressedRConnection base, OutputStream os) {
-            super(base);
-            this.outputStream = os;
-        }
-
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return outputStream;
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            flush();
-            outputStream.close();
-        }
-
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            writeLinesHelper(outputStream, lines, sep);
-        }
-
-        @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(outputStream, s, pad, eos);
-        }
-
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            writeBinHelper(buffer, outputStream);
-        }
-
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            writeStringHelper(outputStream, s, nl);
-        }
-
-        @Override
-        public void flush() throws IOException {
-            outputStream.flush();
-        }
-    }
-
-    private static class BZip2OutputRConnection extends CompressedOutputRConnection {
-        private final ByteArrayOutputStream bos;
-        private final boolean append;
-
-        BZip2OutputRConnection(CompressedRConnection base, ByteArrayOutputStream os, boolean append) {
-            super(base, os);
-            this.bos = os;
-            this.append = append;
-        }
-
-        @Override
-        public void close() throws IOException {
-            flush();
-            outputStream.close();
-            // Now actually do the compression using sub-process
-            byte[] data = bos.toByteArray();
-            RCompression.bzipCompressToFile(data, ((BasePathRConnection) base).path, append);
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
index 3eb19bd3cc339cc4ae780f1c27a3fa92c6dab278..3e0941dce29b4a1325dbe39163aad99d1a9b039a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.runtime.conn;
 
-import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -30,15 +29,29 @@ import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
@@ -107,7 +120,7 @@ public class ConnectionSupport {
             for (int i = 0; i <= hwm; i++) {
                 WeakReference<BaseRConnection> ref = allConnections.get(i);
                 if (ref != null) {
-                    BaseRConnection con = ref.get();
+                    RConnection con = ref.get();
                     if (con != null) {
                         list.add(i);
                     }
@@ -319,7 +332,9 @@ public class ConnectionSupport {
         Text("textConnection"),
         URL("url"),
         RAW("rawConnection"),
-        Internal("internal");
+        Internal("internal"),
+        PIPE("pipe"),
+        FIFO("fifo");
 
         private final String printName;
 
@@ -342,19 +357,12 @@ public class ConnectionSupport {
         }
     }
 
-    // TODO implement all open modes
-
-    public static final class InvalidConnection extends RConnection {
+    public static final class InvalidConnection implements RConnection {
 
         public static final InvalidConnection instance = new InvalidConnection();
 
         private static final int INVALID_DESCRIPTOR = -1;
 
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
-        }
-
         @Override
         public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
             throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
@@ -400,11 +408,6 @@ public class ConnectionSupport {
             throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
         }
 
-        @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
-        }
-
         @Override
         public int getc() throws IOException {
             throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
@@ -464,6 +467,21 @@ public class ConnectionSupport {
         public boolean isOpen() {
             throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
         }
+
+        @Override
+        public void pushBack(RAbstractStringVector lines, boolean addNewLine) {
+            throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
+        }
+
+        @Override
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
+        }
+
+        @Override
+        public ByteChannel getChannel() throws IOException {
+            throw RInternalError.shouldNotReachHere("INVALID CONNECTION");
+        }
     }
 
     /**
@@ -485,7 +503,7 @@ public class ConnectionSupport {
      * it subsequently will throw an error. The latter will open/close the connection (internally)
      * and this can be repeated indefinitely.
      */
-    public abstract static class BaseRConnection extends RConnection {
+    public abstract static class BaseRConnection implements RConnection {
 
         /**
          * {@code true} is the connection has been opened successfully. N.B. This supports lazy
@@ -519,21 +537,84 @@ public class ConnectionSupport {
          */
         private int descriptor;
 
+        private boolean blocking = true;
+
+        /**
+         * The encoding to use to read or to write to the connection.
+         */
+        private Charset encoding;
+
         private ConnectionClass conClass;
 
+        private LinkedList<String> pushBack;
+
+        /**
+         * Indicates that the last line read operation was incomplete.<br>
+         * This is only relevant for connections in text and non-blocking mode.
+         */
+        private boolean incomplete = false;
+
         /**
          * The constructor for every connection class except {@link StdConnections}.
          *
          * @param conClass the specific class of the connection, e.g, {@link ConnectionClass#File}
          * @param modeString the mode in which the connection should be opened, "" for lazy opening
          * @param defaultModeForLazy the mode to use when this connection is opened implicitly
+         * @param blocking Indicates if this connection has been openend in blocking mode.
+         * @param encoding The name of the encoding used to read from or to write to the connection
+         *            ({@code null} is allowed and sets the character set to
+         *            {@code Charset#defaultCharset()}).
          *
          */
-        protected BaseRConnection(ConnectionClass conClass, String modeString, AbstractOpenMode defaultModeForLazy) throws IOException {
+        protected BaseRConnection(ConnectionClass conClass, String modeString, AbstractOpenMode defaultModeForLazy, boolean blocking, String encoding) throws IOException, IllegalCharsetNameException {
             this(conClass, new OpenMode(modeString, defaultModeForLazy));
             if (conClass != ConnectionClass.Internal) {
                 this.descriptor = getContextStateImpl().setConnection(this);
             }
+            this.blocking = blocking;
+            if (encoding != null) {
+                this.encoding = Charset.forName(ConnectionSupport.convertEncodingName(encoding));
+            } else {
+                this.encoding = Charset.defaultCharset();
+            }
+        }
+
+        /**
+         * The constructor for every connection class except {@link StdConnections}.
+         *
+         * @param conClass the specific class of the connection, e.g, {@link ConnectionClass#File}
+         * @param modeString the mode in which the connection should be opened, "" for lazy opening
+         * @param defaultModeForLazy the mode to use when this connection is opened implicitly
+         * @param blocking Indicates if this connection has been openend in blocking mode.
+         *
+         */
+        protected BaseRConnection(ConnectionClass conClass, String modeString, AbstractOpenMode defaultModeForLazy, boolean blocking) throws IOException {
+            this(conClass, modeString, defaultModeForLazy, blocking, null);
+        }
+
+        /**
+         * The constructor for every connection class except {@link StdConnections}.
+         *
+         * @param conClass the specific class of the connection, e.g, {@link ConnectionClass#File}
+         * @param modeString the mode in which the connection should be opened, "" for lazy opening
+         * @param defaultModeForLazy the mode to use when this connection is opened implicitly
+         * @param encoding The name of the encoding used to read from or to write to the connection.
+         *
+         */
+        protected BaseRConnection(ConnectionClass conClass, String modeString, AbstractOpenMode defaultModeForLazy, String encoding) throws IOException {
+            this(conClass, modeString, defaultModeForLazy, true, encoding);
+        }
+
+        /**
+         * The constructor for every connection class except {@link StdConnections}.
+         *
+         * @param conClass the specific class of the connection, e.g, {@link ConnectionClass#File}
+         * @param modeString the mode in which the connection should be opened, "" for lazy opening
+         * @param defaultModeForLazy the mode to use when this connection is opened implicitly
+         *
+         */
+        protected BaseRConnection(ConnectionClass conClass, String modeString, AbstractOpenMode defaultModeForLazy) throws IOException {
+            this(conClass, modeString, defaultModeForLazy, true, null);
         }
 
         /**
@@ -611,8 +692,19 @@ public class ConnectionSupport {
             return getOpenMode().isText();
         }
 
+        public boolean isBlocking() {
+            return blocking;
+        }
+
+        /**
+         * Returns the original encoding string.
+         */
+        public Charset getEncoding() {
+            return encoding;
+        }
+
         @Override
-        public RConnection forceOpen(String modeString) throws IOException {
+        public BaseRConnection forceOpen(String modeString) throws IOException {
             if (closed) {
                 throw new IOException(RError.Message.INVALID_CONNECTION.message);
             }
@@ -648,10 +740,9 @@ public class ConnectionSupport {
             opened = true;
         }
 
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
+        protected String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
             checkOpen();
-            return theConnection.readLinesInternal(n, warn, skipNul);
+            return theConnection.readLines(n, warn, skipNul);
         }
 
         @Override
@@ -666,6 +757,12 @@ public class ConnectionSupport {
             return theConnection.getOutputStream();
         }
 
+        @Override
+        public ByteChannel getChannel() throws IOException {
+            checkOpen();
+            return theConnection.getChannel();
+        }
+
         @Override
         public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
             checkOpen();
@@ -742,8 +839,7 @@ public class ConnectionSupport {
             return theConnection.getc();
         }
 
-        @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+        public long seekInternal(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
             checkOpen();
             return theConnection.seek(offset, seekMode, seekRWMode);
         }
@@ -786,349 +882,290 @@ public class ConnectionSupport {
         public boolean isClosed() {
             return closed;
         }
-    }
-
-    public static BaseRConnection getBaseConnection(RConnection conn) {
-        if (conn instanceof BaseRConnection) {
-            return (BaseRConnection) conn;
-        } else if (conn instanceof DelegateReadRConnection) {
-            return ((DelegateReadRConnection) conn).base;
-        } else {
-            throw RInternalError.shouldNotReachHere();
-        }
-    }
-
-    interface ReadWriteHelper {
 
-        /**
-         * {@code readLines} from an {@link InputStream}. It would be convenient to use a
-         * {@link BufferedReader} but mixing binary and text operations, which is a requirement,
-         * would then be difficult.
-         *
-         * @param warn TODO
-         * @param skipNul TODO
-         */
-        default String[] readLinesHelper(InputStream in, int n, boolean warn, boolean skipNul) throws IOException {
-            ArrayList<String> lines = new ArrayList<>();
-            int totalRead = 0;
-            byte[] buffer = new byte[64];
-            int pushBack = 0;
-            while (true) {
-                int ch;
-                if (pushBack != 0) {
-                    ch = pushBack;
-                    pushBack = 0;
-                } else {
-                    ch = in.read();
-                }
-                boolean lineEnd = false;
-                if (ch < 0) {
-                    if (totalRead > 0) {
-                        /*
-                         * TODO GnuR says keep data and output a warning if blocking, otherwise
-                         * silently push back. FastR doesn't support non-blocking yet, so we keep
-                         * the data. Some refactoring is needed to be able to reliably access the
-                         * "name" for the warning.
-                         */
-                        lines.add(new String(buffer, 0, totalRead));
-                        if (warn) {
-                            RError.warning(RError.SHOW_CALLER2, RError.Message.INCOMPLETE_FINAL_LINE, "TODO: connection path");
-                        }
-                    }
-                    break;
-                }
-                if (ch == '\n') {
-                    lineEnd = true;
-                } else if (ch == '\r') {
-                    lineEnd = true;
-                    ch = in.read();
-                    if (ch == '\n') {
-                        // swallow the trailing lf
-                    } else {
-                        pushBack = ch;
-                    }
-                }
-                if (lineEnd) {
-                    lines.add(new String(buffer, 0, totalRead));
-                    if (n > 0 && lines.size() == n) {
-                        break;
+        private String readOneLineWithPushBack(List<String> res, int ind) {
+            String s = pushBack.pollLast();
+            if (s == null) {
+                return null;
+            } else {
+                String[] lines = s.split("\n", 2);
+                if (lines.length == 2) {
+                    // we hit end of the line
+                    if (lines[1].length() != 0) {
+                        // suffix is not empty and needs to be processed later
+                        pushBack.push(lines[1]);
                     }
-                    totalRead = 0;
+                    assert res.size() == ind;
+                    res.add(ind, lines[0]);
+                    return null;
                 } else {
-                    buffer = checkBuffer(buffer, totalRead);
-                    buffer[totalRead++] = (byte) (ch & 0xFF);
-                }
-            }
-            String[] result = new String[lines.size()];
-            lines.toArray(result);
-            return result;
-        }
-
-        default void writeLinesHelper(OutputStream out, RAbstractStringVector lines, String sep) throws IOException {
-            for (int i = 0; i < lines.getLength(); i++) {
-                out.write(lines.getDataAt(i).getBytes());
-                out.write(sep.getBytes());
-            }
-        }
-
-        default void writeStringHelper(OutputStream out, String s, boolean nl) throws IOException {
-            out.write(s.getBytes());
-            if (nl) {
-                out.write('\n');
-            }
-        }
+                    // no end of the line found yet
+                    StringBuilder sb = new StringBuilder();
+                    do {
+                        assert lines.length == 1;
+                        sb.append(lines[0]);
+                        s = pushBack.pollLast();
+                        if (s == null) {
+                            break;
+                        }
 
-        default void writeCharHelper(OutputStream out, String s, int pad, String eos) throws IOException {
-            out.write(s.getBytes());
-            if (pad > 0) {
-                for (int i = 0; i < pad; i++) {
-                    out.write(0);
-                }
-            }
-            if (eos != null) {
-                if (eos.length() > 0) {
-                    out.write(eos.getBytes());
+                        lines = s.split("\n", 2);
+                        if (lines.length == 2) {
+                            // we hit end of the line
+                            if (lines[1].length() != 0) {
+                                // suffix is not empty and needs to be processed later
+                                pushBack.push(lines[1]);
+                            }
+                            assert res.size() == ind;
+                            res.add(ind, sb.append(lines[0]).toString());
+                            return null;
+                        } // else continue
+                    } while (true);
+                    return sb.toString();
                 }
-                // function writeChar is defined to append the null character if eos != null
-                out.write(0);
             }
         }
 
-        default void writeBinHelper(ByteBuffer buffer, OutputStream outputStream) throws IOException {
-            int n = buffer.remaining();
-            byte[] b = new byte[n];
-            buffer.get(b);
-            outputStream.write(b);
-        }
-
         /**
-         * Reads null-terminated character strings from an {@link InputStream}.
+         * TODO(fa) This method is subject for refactoring. I modified the original implementation
+         * to be capable to handle a negative 'n' parameter. It indicates to read as much lines as
+         * available.
          */
-        default byte[] readBinCharsHelper(InputStream in) throws IOException {
-            int ch = in.read();
-            if (ch < 0) {
-                return null;
-            }
-            int totalRead = 0;
-            byte[] buffer = new byte[64];
-            while (true) {
-                buffer = checkBuffer(buffer, totalRead);
-                buffer[totalRead++] = (byte) (ch & 0xFF);
-                if (ch == 0) {
-                    break;
-                }
-                ch = in.read();
+        @TruffleBoundary
+        private String[] readLinesWithPushBack(int n, boolean warn, boolean skipNul) throws IOException {
+            // NOTE: 'n' may be negative indicating to read as much lines as available
+            final List<String> res;
+            if (n >= 0) {
+                res = new ArrayList<>(n);
+            } else {
+                res = new ArrayList<>();
             }
-            return buffer;
-        }
 
-        default int readBinHelper(ByteBuffer buffer, InputStream inputStream) throws IOException {
-            int bytesToRead = buffer.remaining();
-            byte[] b = new byte[bytesToRead];
-            int totalRead = 0;
-            int thisRead = 0;
-            while ((totalRead < bytesToRead) && ((thisRead = inputStream.read(b, totalRead, bytesToRead - totalRead)) > 0)) {
-                totalRead += thisRead;
-            }
-            buffer.put(b, 0, totalRead);
-            return totalRead;
-        }
+            for (int i = 0; i < n || n < 0; i++) {
+                String s = readOneLineWithPushBack(res, i);
+                final int remainingLineCount = n >= 0 ? n - i : n;
+                if (s == null) {
+                    if (i >= res.size() || res.get(i) == null) {
+                        // no more push back value
 
-        default String readCharHelper(int nchars, InputStream in, @SuppressWarnings("unused") boolean useBytes) throws IOException {
-            byte[] bytes = new byte[nchars];
-            in.read(bytes);
-            int j = 0;
-            for (; j < bytes.length; j++) {
-                // strings end at 0
-                if (bytes[j] == 0) {
+                        String[] resInternal = readLinesInternal(remainingLineCount, warn, skipNul);
+                        res.addAll(Arrays.asList(resInternal));
+                        pushBack = null;
+                        break;
+                    }
+                    // else res[i] has been filled - move to trying to fill the next one
+                } else {
+                    // reached the last push back value without reaching and of line
+                    assert pushBack.size() == 0;
+                    String[] resInternal = readLinesInternal(remainingLineCount, warn, skipNul);
+                    res.addAll(i, Arrays.asList(resInternal));
+                    if (res.get(i) != null) {
+                        res.set(i, s + res.get(i));
+                    } else {
+                        res.set(i, s);
+                    }
+                    pushBack = null;
                     break;
                 }
             }
-            return new String(bytes, 0, j);
-        }
-    }
-
-    private static byte[] checkBuffer(byte[] buffer, int n) {
-        if (n > buffer.length - 1) {
-            byte[] newBuffer = new byte[buffer.length + buffer.length / 2];
-            System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
-            return newBuffer;
-        } else {
-            return buffer;
-        }
-    }
-
-    abstract static class DelegateRConnection extends RConnection {
-        protected BaseRConnection base;
-
-        DelegateRConnection(BaseRConnection base) {
-            this.base = base;
-        }
-
-        @Override
-        public int getDescriptor() {
-            return base.getDescriptor();
+            return res.toArray(new String[res.size()]);
         }
 
         @Override
-        public boolean isTextMode() {
-            return base.isTextMode();
+        public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
+            if (pushBack == null) {
+                return readLinesInternal(n, warn, skipNul);
+            } else if (pushBack.size() == 0) {
+                pushBack = null;
+                return readLinesInternal(n, warn, skipNul);
+            } else {
+                return readLinesWithPushBack(n, warn, skipNul);
+            }
         }
 
+        /**
+         * Pushes lines back to the connection.
+         */
         @Override
-        public boolean isOpen() {
-            return base.isOpen();
+        @TruffleBoundary
+        public final void pushBack(RAbstractStringVector lines, boolean addNewLine) {
+            if (pushBack == null) {
+                pushBack = new LinkedList<>();
+            }
+            for (int i = 0; i < lines.getLength(); i++) {
+                String newLine = lines.getDataAt(i);
+                if (addNewLine) {
+                    newLine = newLine + '\n';
+                }
+                pushBack.addFirst(newLine);
+            }
         }
 
-        @Override
-        public RConnection forceOpen(String modeString) throws IOException {
-            return base.forceOpen(modeString);
+        /**
+         * Return the length of the push back.
+         */
+        @TruffleBoundary
+        public final int pushBackLength() {
+            return pushBack == null ? 0 : pushBack.size();
         }
 
-        @Override
-        public boolean isSeekable() {
-            return false;
+        /**
+         * Clears the pushback.
+         */
+        @TruffleBoundary
+        public final void pushBackClear() {
+            pushBack = null;
         }
 
+        /**
+         * Support for {@code seek} Internal. Also clears push back lines.
+         */
         @Override
         public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.UNSEEKABLE_CONNECTION);
+            if (isSeekable()) {
+                // discard any push back strings
+                pushBackClear();
+            }
+            // Do not throw error at this position, since the error messages varies depending on the
+            // connection.
+            return seekInternal(offset, seekMode, seekRWMode);
         }
-    }
 
-    abstract static class DelegateReadRConnection extends DelegateRConnection {
-        protected DelegateReadRConnection(BaseRConnection base) {
-            super(base);
+        /**
+         * Returns {@code true} iff the last read operation was blocked or there is unflushed
+         * output.
+         */
+        public boolean isIncomplete() {
+            return incomplete;
         }
 
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+        protected void setIncomplete(boolean b) {
+            this.incomplete = b;
         }
 
-        @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
-        }
+        public final RAbstractIntVector asVector() {
+            String[] classes = new String[]{ConnectionSupport.getBaseConnection(this).getConnectionClass().getPrintName(), "connection"};
 
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
-        }
+            RAbstractIntVector result = RDataFactory.createIntVector(new int[]{getDescriptor()}, true);
 
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+            RStringVector classVector = RDataFactory.createStringVector(classes, RDataFactory.COMPLETE_VECTOR);
+            // it's important to put "this" into the externalptr, so that it doesn't get collected
+            RExternalPtr connectionId = RDataFactory.createExternalPtr(null, this, RDataFactory.createSymbol("connection"), RNull.instance);
+            DynamicObject attrs = RAttributesLayout.createClassWithConnId(classVector, connectionId);
+            result.initAttributes(attrs);
+            return result;
         }
+    }
 
-        @Override
-        public int getc() throws IOException {
-            return getInputStream().read();
+    public static BaseRConnection getBaseConnection(RConnection conn) {
+        if (conn instanceof BaseRConnection) {
+            return (BaseRConnection) conn;
+        } else if (conn instanceof DelegateReadRConnection) {
+            return ((DelegateRConnection) conn).base;
+        } else {
+            throw RInternalError.shouldNotReachHere();
         }
+    }
 
-        @Override
-        public void flush() {
-            // nothing to do
-        }
+    abstract static class BasePathRConnection extends BaseRConnection {
+        /** The path of the actual file to open. */
+        protected final String path;
 
-        @Override
-        public OutputStream getOutputStream() {
-            throw RInternalError.shouldNotReachHere();
-        }
+        /** The description used in the call (required summary output). */
+        protected final String description;
 
-        @Override
-        public boolean canRead() {
-            return true;
+        protected BasePathRConnection(String description, String path, ConnectionClass connectionClass, String modeString, String encoding) throws IOException {
+            this(description, path, connectionClass, modeString, AbstractOpenMode.Read, encoding);
         }
 
-        @Override
-        public boolean canWrite() {
-            return false;
+        protected BasePathRConnection(String description, String path, ConnectionClass connectionClass, String modeString, boolean blocking, String encoding) throws IOException {
+            this(description, path, connectionClass, modeString, AbstractOpenMode.Read, blocking, encoding);
         }
-    }
 
-    abstract static class DelegateWriteRConnection extends DelegateRConnection {
-        protected DelegateWriteRConnection(BaseRConnection base) {
-            super(base);
+        protected BasePathRConnection(String description, String path, ConnectionClass connectionClass, String modeString, AbstractOpenMode defaultLazyOpenMode, String encoding) throws IOException {
+            super(connectionClass, modeString, defaultLazyOpenMode, encoding);
+            this.path = Utils.tildeExpand(path);
+            this.description = description;
         }
 
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+        protected BasePathRConnection(String description, String path, ConnectionClass connectionClass, String modeString, AbstractOpenMode defaultLazyOpenMode, boolean blocking, String encoding)
+                        throws IOException {
+            super(connectionClass, modeString, defaultLazyOpenMode, blocking, encoding);
+            this.path = Utils.tildeExpand(path);
+            this.description = description;
         }
 
         @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+        public String getSummaryDescription() {
+            // Use 'description' and not 'path' since this may be different, e.g., on temp files.
+            return description;
         }
+    }
 
-        @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
-        }
+    public static ByteChannel newChannel(InputStream in) {
+        final ReadableByteChannel newChannel = Channels.newChannel(in);
+        return new ByteChannel() {
 
-        @Override
-        public byte[] readBinChars() throws IOException {
-            throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
-        }
+            @Override
+            public int read(ByteBuffer dst) throws IOException {
+                return newChannel.read(dst);
+            }
 
-        @Override
-        public int getc() throws IOException {
-            throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
-        }
+            @Override
+            public boolean isOpen() {
+                return newChannel.isOpen();
+            }
 
-        @Override
-        public InputStream getInputStream() {
-            throw RInternalError.shouldNotReachHere();
-        }
+            @Override
+            public void close() throws IOException {
+                newChannel.close();
 
-        @Override
-        public boolean canRead() {
-            return false;
-        }
+            }
 
-        @Override
-        public boolean canWrite() {
-            return true;
-        }
+            @Override
+            public int write(ByteBuffer src) throws IOException {
+                throw new IOException("This channel is read-only.");
+            }
+        };
     }
 
-    abstract static class DelegateReadWriteRConnection extends DelegateRConnection {
-        protected DelegateReadWriteRConnection(BaseRConnection base) {
-            super(base);
-        }
+    public static ByteChannel newChannel(OutputStream out) {
+        final WritableByteChannel newChannel = Channels.newChannel(out);
+        return new ByteChannel() {
 
-        @Override
-        public boolean canRead() {
-            return true;
-        }
-
-        @Override
-        public boolean canWrite() {
-            return true;
-        }
+            @Override
+            public int read(ByteBuffer dst) throws IOException {
+                throw new IOException("This channel is write-only.");
+            }
 
-        @Override
-        public int getc() throws IOException {
-            return getInputStream().read();
-        }
-    }
+            @Override
+            public boolean isOpen() {
+                return newChannel.isOpen();
+            }
 
-    abstract static class BasePathRConnection extends BaseRConnection {
-        protected final String path;
+            @Override
+            public void close() throws IOException {
+                newChannel.close();
 
-        protected BasePathRConnection(String path, ConnectionClass connectionClass, String modeString) throws IOException {
-            this(path, connectionClass, modeString, AbstractOpenMode.Read);
-        }
+            }
 
-        protected BasePathRConnection(String path, ConnectionClass connectionClass, String modeString, AbstractOpenMode defaultLazyOpenMode) throws IOException {
-            super(connectionClass, modeString, defaultLazyOpenMode);
-            this.path = Utils.tildeExpand(path);
-        }
+            @Override
+            public int write(ByteBuffer src) throws IOException {
+                return newChannel.write(src);
+            }
+        };
+    }
 
-        @Override
-        public String getSummaryDescription() {
-            return path;
+    /**
+     * Converts between character set names of iconv and Java.
+     *
+     * @param encoding The iconv name of the character set to use.
+     * @return The Java name of the character set to use.
+     */
+    public static String convertEncodingName(String encoding) {
+        if (encoding == null || encoding.isEmpty() || "native.enc".equals(encoding)) {
+            return Charset.defaultCharset().name();
         }
+        return encoding;
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..0139c2e9a71b7867f954a047e6b0f8acc87d21a4
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateRConnection.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.ArrayList;
+import java.util.Objects;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.sun.istack.internal.NotNull;
+
+import sun.nio.cs.StreamDecoder;
+
+/**
+ * Actually performs the I/O operations for a connections.<br>
+ * <p>
+ * A delegate connection is called from its base connection and implements the actual I/O
+ * operations.
+ * </p>
+ */
+abstract class DelegateRConnection implements RConnection {
+    protected final BaseRConnection base;
+    private StreamDecoder decoder;
+
+    DelegateRConnection(BaseRConnection base) {
+        this.base = Objects.requireNonNull(base);
+    }
+
+    @Override
+    public int getDescriptor() {
+        return base.getDescriptor();
+    }
+
+    @Override
+    public boolean isTextMode() {
+        return base.isTextMode();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return base.isOpen();
+    }
+
+    @Override
+    public RConnection forceOpen(String modeString) throws IOException {
+        return base.forceOpen(modeString);
+    }
+
+    @Override
+    public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+        if (!isSeekable()) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.NOT_ENABLED_FOR_THIS_CONN, "seek");
+        }
+        throw RInternalError.shouldNotReachHere("seek has not been implemented for this connection");
+    }
+
+    /**
+     * {@code readLines} from the connection. It would be convenient to use a {@link BufferedReader}
+     * but mixing binary and text operations, which is a requirement, would then be difficult.
+     *
+     * @param warn Specifies if warnings should be output.
+     * @param skipNul Specifies if the null character should be ignored.
+     */
+    @Override
+    @TruffleBoundary
+    public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
+        base.setIncomplete(false);
+        ArrayList<String> lines = new ArrayList<>();
+        int totalRead = 0;
+        byte[] buffer = new byte[64];
+        int pushBack = 0;
+        boolean nullRead = false;
+        while (true) {
+            int ch;
+            if (pushBack != 0) {
+                ch = pushBack;
+                pushBack = 0;
+            } else {
+                ch = getc();
+            }
+            boolean lineEnd = false;
+            if (ch < 0) {
+                if (totalRead > 0) {
+                    /*
+                     * GnuR says if non-blocking and in text mode, silently push back incomplete
+                     * lines, otherwise keep data and output warning.
+                     */
+                    final String incompleteFinalLine = new String(buffer, 0, totalRead, base.getEncoding());
+                    if (!base.isBlocking() && base.isTextMode()) {
+                        base.pushBack(RDataFactory.createStringVector(incompleteFinalLine), false);
+                        base.setIncomplete(true);
+                    } else {
+                        lines.add(incompleteFinalLine);
+                        if (warn) {
+                            RError.warning(RError.SHOW_CALLER, RError.Message.INCOMPLETE_FINAL_LINE, base.getSummaryDescription());
+                        }
+                    }
+                }
+                break;
+            }
+            if (ch == '\n') {
+                lineEnd = true;
+            } else if (ch == '\r') {
+                lineEnd = true;
+                ch = getc();
+                if (ch == '\n') {
+                    // swallow the trailing lf
+                } else {
+                    pushBack = ch;
+                }
+            } else if (ch == 0) {
+                nullRead = true;
+                if (warn && !skipNul) {
+                    RError.warning(RError.SHOW_CALLER, RError.Message.LINE_CONTAINS_EMBEDDED_NULLS, lines.size() + 1);
+                }
+            }
+            if (lineEnd) {
+                lines.add(new String(buffer, 0, totalRead, base.getEncoding()));
+                if (n > 0 && lines.size() == n) {
+                    break;
+                }
+                totalRead = 0;
+                nullRead = false;
+            } else {
+                if (!nullRead) {
+                    buffer = DelegateRConnection.checkBuffer(buffer, totalRead);
+                    buffer[totalRead++] = (byte) (ch & 0xFF);
+                }
+                if (skipNul) {
+                    nullRead = false;
+                }
+            }
+        }
+        String[] result = new String[lines.size()];
+        lines.toArray(result);
+        return result;
+    }
+
+    /**
+     * Writes a string to a channel.
+     *
+     * @param out the channel
+     * @param s The actual string to write.
+     * @param nl Indicates if a line separator should be appended.
+     * @param encoding The encoding to use for writing.
+     * @return {@code true} if an incomplete line was written; {@code false} otherwise
+     * @throws IOException
+     */
+    public static boolean writeStringHelper(WritableByteChannel out, String s, boolean nl, Charset encoding) throws IOException {
+        boolean incomplete;
+        final byte[] bytes = s.getBytes(encoding);
+        final byte[] lineSepBytes = nl ? System.lineSeparator().getBytes(encoding) : null;
+
+        ByteBuffer buf = ByteBuffer.allocate(bytes.length + (nl ? lineSepBytes.length : 0));
+        buf.put(bytes);
+        if (nl) {
+            buf.put(lineSepBytes);
+            incomplete = false;
+        } else {
+            incomplete = !s.contains("\n");
+        }
+
+        buf.rewind();
+        out.write(buf);
+        return incomplete;
+    }
+
+    /**
+     * Writes characters in binary mode (without any re-encoding) to the provided channel.
+     *
+     * @param channel The writable byte channel to write to (must not be {@code null}).
+     * @param s The character string to write (must not be {@code null}).
+     * @param pad The number of null characters to append to the characters.
+     * @param eos The end-of-string terminator (may be {@code null}).
+     * @throws IOException
+     */
+    public static void writeCharHelper(@NotNull WritableByteChannel channel, @NotNull String s, int pad, String eos) throws IOException {
+
+        final byte[] bytes = s.getBytes();
+        final byte[] eosBytes = eos != null ? eos.getBytes() : null;
+
+        final int bufLen = bytes.length + (pad > 0 ? pad : 0) + (eos != null ? eosBytes.length + 1 : 0);
+        assert bufLen >= s.length();
+        ByteBuffer buf = ByteBuffer.allocate(bufLen);
+        buf.put(bytes);
+        if (pad > 0) {
+            for (int i = 0; i < pad; i++) {
+                buf.put((byte) 0);
+            }
+        }
+        if (eos != null) {
+            if (eos.length() > 0) {
+                buf.put(eos.getBytes());
+            }
+            // function writeChar is defined to append the null character if eos != null
+            buf.put((byte) 0);
+        }
+        buf.rewind();
+        channel.write(buf);
+    }
+
+    /**
+     * Reads null-terminated character strings from a {@link ReadableByteChannel}.
+     */
+    public static byte[] readBinCharsHelper(ReadableByteChannel channel) throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(1);
+        int numRead = channel.read(buf);
+        if (numRead <= 0) {
+            return null;
+        }
+        int totalRead = 0;
+        byte[] buffer = new byte[64];
+        while (true) {
+            buffer = DelegateRConnection.checkBuffer(buffer, totalRead);
+            buffer[totalRead++] = (byte) (buf.get() & 0xFF);
+            if (numRead == 0) {
+                break;
+            }
+            buf.clear();
+            numRead = channel.read(buf);
+        }
+        return buffer;
+    }
+
+    /**
+     * Reads a specified amount of characters.
+     *
+     * @param nchars Number of characters to read.
+     * @param in The encoded byte stream.
+     * @return The read string.
+     * @throws IOException
+     */
+    public static String readCharHelper(int nchars, Reader in) throws IOException {
+        char[] chars = new char[nchars];
+        in.read(chars);
+        int j = 0;
+        for (; j < chars.length; j++) {
+            // strings end at 0
+            if (chars[j] == 0) {
+                break;
+            }
+        }
+
+        return new String(chars, 0, j);
+    }
+
+    /**
+     * Reads a specified number of single-byte characters.<br>
+     * <p>
+     * This method is meant to be used if R's function {@code readChar} is called with parameter
+     * {@code useBytes=TRUE}.
+     * </p>
+     *
+     * @param nchars The number of single-byte characters to read.
+     * @param channel The channel to read from (must not be {@code null}).
+     * @throws IOException
+     */
+    public static String readCharHelper(int nchars, ReadableByteChannel channel) throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(nchars);
+        channel.read(buf);
+        int j = 0;
+        for (; j < buf.position(); j++) {
+            // strings end at 0
+            if (buf.get(j) == 0) {
+                break;
+            }
+        }
+
+        return new String(buf.array(), 0, j);
+    }
+
+    /**
+     * Reads a specified number of characters (not bytes).<br>
+     * <p>
+     * This method is meant to be used if R's function {@code readChar} is called with parameter
+     * {@code useBytes=FALSE}.
+     * </p>
+     *
+     * @param nchars The number of characters to read.
+     * @param decoder The stream decoder to use (must not be {@code null}).
+     * @throws IOException
+     */
+    public static String readCharHelper(int nchars, StreamDecoder decoder) throws IOException {
+        // we need a decoder
+        char[] chars = new char[nchars];
+        decoder.read(chars);
+        int j = 0;
+        for (; j < chars.length; j++) {
+            // strings end at 0
+            if (chars[j] == 0) {
+                break;
+            }
+        }
+        return new String(chars, 0, j);
+    }
+
+    /**
+     * Implements standard seeking behavior.
+     */
+    public static long seek(SeekableByteChannel channel, long offset, SeekMode seekMode, @SuppressWarnings("unused") SeekRWMode seekRWMode) throws IOException {
+        long position = channel.position();
+        switch (seekMode) {
+            case ENQUIRE:
+                break;
+            case CURRENT:
+                if (offset != 0) {
+                    channel.position(position + offset);
+                }
+                break;
+            case START:
+                channel.position(offset);
+                break;
+            case END:
+                throw RInternalError.unimplemented();
+
+        }
+        return position;
+    }
+
+    /**
+     * Enlarges the buffer if necessary.
+     */
+    private static byte[] checkBuffer(byte[] buffer, int n) {
+        if (n > buffer.length - 1) {
+            byte[] newBuffer = new byte[buffer.length + buffer.length / 2];
+            System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
+            return newBuffer;
+        } else {
+            return buffer;
+        }
+    }
+
+    public static boolean writeLinesHelper(WritableByteChannel out, RAbstractStringVector lines, String sep, Charset encoding) throws IOException {
+        boolean incomplete = false;
+        for (int i = 0; i < lines.getLength(); i++) {
+            final String line = lines.getDataAt(i);
+            incomplete = DelegateRConnection.writeStringHelper(out, line, false, encoding);
+            incomplete = DelegateRConnection.writeStringHelper(out, sep, false, encoding) || incomplete;
+        }
+        return incomplete;
+    }
+
+    @Override
+    public void pushBack(RAbstractStringVector lines, boolean addNewLine) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    /**
+     * Creates the stream decoder on demand and returns it.
+     */
+    protected StreamDecoder getDecoder() throws IOException {
+        if (decoder == null) {
+            CharsetDecoder charsetEncoder = base.getEncoding().newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
+            decoder = StreamDecoder.forDecoder(getChannel(), charsetEncoder, -1);
+        }
+        return decoder;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..70a2bac8df5e6cc84b12f1b876f627b4d56a0b26
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadRConnection.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+
+public abstract class DelegateReadRConnection extends DelegateRConnection {
+
+    private final ByteBuffer tmp = ByteBuffer.allocate(1);
+
+    protected DelegateReadRConnection(BaseRConnection base) {
+        super(base);
+    }
+
+    @Override
+    public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
+        throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+    }
+
+    @Override
+    public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
+        throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+    }
+
+    @Override
+    public void writeString(String s, boolean nl) throws IOException {
+        throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+    }
+
+    @Override
+    public void writeBin(ByteBuffer buffer) throws IOException {
+        throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
+    }
+
+    @Override
+    public int getc() throws IOException {
+        if (isTextMode()) {
+            return getDecoder().read();
+        } else {
+            tmp.clear();
+            int nread = getChannel().read(tmp);
+            tmp.rewind();
+            return nread > 0 ? tmp.get() : -1;
+        }
+    }
+
+    @Override
+    public String readChar(int nchars, boolean useBytes) throws IOException {
+        if (useBytes) {
+            return DelegateRConnection.readCharHelper(nchars, getChannel());
+        } else {
+            return DelegateRConnection.readCharHelper(nchars, getDecoder());
+        }
+    }
+
+    @Override
+    public int readBin(ByteBuffer buffer) throws IOException {
+        return getChannel().read(buffer);
+    }
+
+    @Override
+    public byte[] readBinChars() throws IOException {
+        return DelegateRConnection.readBinCharsHelper(getChannel());
+    }
+
+    @Override
+    public void flush() {
+        // nothing to do when reading
+    }
+
+    @Override
+    public OutputStream getOutputStream() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public boolean canRead() {
+        return true;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return false;
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return Channels.newInputStream(getChannel());
+    }
+
+    @Override
+    public void close() throws IOException {
+        getChannel().close();
+    }
+
+    @Override
+    public void closeAndDestroy() throws IOException {
+        base.closed = true;
+        close();
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..011cf49d1b9256813e2f4151b61cf8565dd1f695
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateReadWriteRConnection.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+
+abstract class DelegateReadWriteRConnection extends DelegateRConnection {
+
+    private final ByteBuffer tmp = ByteBuffer.allocate(1);
+
+    protected DelegateReadWriteRConnection(BaseRConnection base) {
+        super(base);
+    }
+
+    @Override
+    public boolean canRead() {
+        return true;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return true;
+    }
+
+    @Override
+    public int getc() throws IOException {
+        tmp.clear();
+        int nread = getChannel().read(tmp);
+        tmp.rewind();
+        return nread > 0 ? tmp.get() : -1;
+    }
+
+    @Override
+    public String readChar(int nchars, boolean useBytes) throws IOException {
+        if (useBytes) {
+            return DelegateRConnection.readCharHelper(nchars, getChannel());
+        } else {
+            return DelegateRConnection.readCharHelper(nchars, getDecoder());
+        }
+    }
+
+    @Override
+    public int readBin(ByteBuffer buffer) throws IOException {
+        return getChannel().read(buffer);
+    }
+
+    @Override
+    public byte[] readBinChars() throws IOException {
+        return DelegateRConnection.readBinCharsHelper(getChannel());
+    }
+
+    @Override
+    public void flush() {
+        // nothing to do for channels
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return Channels.newOutputStream(getChannel());
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return Channels.newInputStream(getChannel());
+    }
+
+    @Override
+    public void close() throws IOException {
+        getChannel().close();
+    }
+
+    @Override
+    public void closeAndDestroy() throws IOException {
+        base.closed = true;
+        close();
+    }
+
+    @Override
+    public void writeBin(ByteBuffer buffer) throws IOException {
+        getChannel().write(buffer);
+    }
+
+    @Override
+    public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
+        DelegateRConnection.writeCharHelper(getChannel(), s, pad, eos);
+    }
+
+    @Override
+    public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
+        boolean incomplete = DelegateRConnection.writeLinesHelper(getChannel(), lines, sep, base.getEncoding());
+        base.setIncomplete(incomplete);
+    }
+
+    @Override
+    public void writeString(String s, boolean nl) throws IOException {
+        DelegateRConnection.writeStringHelper(getChannel(), s, nl, base.getEncoding());
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateWriteRConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateWriteRConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d4520d88cfea5b2ce808e579e45c8f4991fe4b5
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/DelegateWriteRConnection.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+
+abstract class DelegateWriteRConnection extends DelegateRConnection {
+
+    protected DelegateWriteRConnection(BaseRConnection base) {
+        super(base);
+    }
+
+    @Override
+    public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
+        throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+    }
+
+    @Override
+    public String readChar(int nchars, boolean useBytes) throws IOException {
+        throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+    }
+
+    @Override
+    public int readBin(ByteBuffer buffer) throws IOException {
+        throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+    }
+
+    @Override
+    public byte[] readBinChars() throws IOException {
+        throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+    }
+
+    @Override
+    public int getc() throws IOException {
+        throw new IOException(RError.Message.CANNOT_READ_CONNECTION.message);
+    }
+
+    @Override
+    public InputStream getInputStream() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public boolean canRead() {
+        return false;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return true;
+    }
+
+    @Override
+    public void closeAndDestroy() throws IOException {
+        base.closed = true;
+        close();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        // channels don't need any flushing
+    }
+
+    @Override
+    public void close() throws IOException {
+        flush();
+        getChannel().close();
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return Channels.newOutputStream(getChannel());
+    }
+
+    @Override
+    public void writeBin(ByteBuffer buffer) throws IOException {
+        getChannel().write(buffer);
+    }
+
+    @Override
+    public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
+        DelegateRConnection.writeCharHelper(getChannel(), s, pad, eos);
+    }
+
+    @Override
+    public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
+        boolean incomplete = DelegateRConnection.writeLinesHelper(getChannel(), lines, sep, base.getEncoding());
+        base.setIncomplete(incomplete);
+    }
+
+    @Override
+    public void writeString(String s, boolean nl) throws IOException {
+        DelegateRConnection.writeStringHelper(getChannel(), s, nl, base.getEncoding());
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FifoConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FifoConnections.java
new file mode 100644
index 0000000000000000000000000000000000000000..df63d1fc42ab27e999a08ff4488191dfb5be44f9
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FifoConnections.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.lang.ProcessBuilder.Redirect;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+
+import com.oracle.truffle.r.runtime.ProcessOutputManager;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
+
+public class FifoConnections {
+
+    public static class FifoRConnection extends BaseRConnection {
+
+        private final String path;
+
+        public FifoRConnection(String path, String open, boolean blocking, String encoding) throws IOException {
+            super(ConnectionClass.FIFO, open, AbstractOpenMode.Read, blocking, encoding);
+            this.path = path;
+            openNonLazyConnection();
+        }
+
+        @Override
+        protected void createDelegateConnection() throws IOException {
+            final DelegateRConnection delegate;
+            if (isBlocking()) {
+                delegate = createBlockingDelegate();
+            } else {
+
+                delegate = createNonBlockingDelegate();
+            }
+            setDelegate(delegate);
+        }
+
+        private DelegateRConnection createBlockingDelegate() throws IOException {
+            DelegateRConnection delegate = null;
+            switch (getOpenMode().abstractOpenMode) {
+                case Read:
+                case ReadBinary:
+                    delegate = new FifoReadRConnection(this, path);
+                    break;
+                case Write:
+                case WriteBinary:
+                    delegate = new FifoWriteConnection(this, path);
+                    break;
+                case ReadAppend:
+                case ReadWrite:
+                case ReadWriteBinary:
+                case ReadWriteTrunc:
+                case ReadWriteTruncBinary:
+                    delegate = new FifoReadWriteConnection(this, path);
+                    break;
+                default:
+                    throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode());
+            }
+            return delegate;
+        }
+
+        private DelegateRConnection createNonBlockingDelegate() throws IOException {
+            DelegateRConnection delegate = null;
+            switch (getOpenMode().abstractOpenMode) {
+                case Read:
+                case ReadBinary:
+                    delegate = new FifoReadNonBlockingRConnection(this, path);
+                    break;
+                case Write:
+                case WriteBinary:
+                    delegate = new FifoWriteNonBlockingRConnection(this, path);
+                    break;
+                case ReadAppend:
+                case ReadWrite:
+                case ReadWriteBinary:
+                case ReadWriteTrunc:
+                case ReadWriteTruncBinary:
+                    delegate = new FifoReadWriteNonBlockingRConnection(this, path);
+                    break;
+                default:
+                    throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode());
+            }
+            return delegate;
+        }
+
+        @Override
+        public String getSummaryDescription() {
+            return path;
+        }
+    }
+
+    static class FifoReadRConnection extends DelegateReadRConnection {
+        private final FileChannel channel;
+
+        protected FifoReadRConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            channel = FileChannel.open(Paths.get(path), StandardOpenOption.READ);
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class FifoWriteConnection extends DelegateWriteRConnection {
+        private final RandomAccessFile raf;
+
+        FifoWriteConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            this.raf = createAndOpenFifo(path, "rw");
+        }
+
+        @Override
+        public SeekableByteChannel getChannel() {
+            return raf.getChannel();
+        }
+
+        @Override
+        public void close() throws IOException {
+            raf.close();
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class FifoReadWriteConnection extends DelegateReadWriteRConnection {
+
+        private final RandomAccessFile raf;
+
+        protected FifoReadWriteConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            this.raf = createAndOpenFifo(path, "rw");
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return raf.getChannel();
+        }
+
+    }
+
+    static class FifoReadNonBlockingRConnection extends DelegateReadRConnection {
+        private final FileChannel channel;
+
+        protected FifoReadNonBlockingRConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            channel = FileChannel.open(Paths.get(path), StandardOpenOption.READ);
+        }
+
+        @Override
+        public SeekableByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class FifoWriteNonBlockingRConnection extends DelegateWriteRConnection {
+        private final FileChannel channel;
+
+        FifoWriteNonBlockingRConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            channel = createAndOpenNonBlockingFifo(path, StandardOpenOption.READ, StandardOpenOption.WRITE);
+        }
+
+        @Override
+        public SeekableByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class FifoReadWriteNonBlockingRConnection extends DelegateReadWriteRConnection {
+        private final FileChannel channel;
+
+        FifoReadWriteNonBlockingRConnection(BaseRConnection base, String path) throws IOException {
+            super(base);
+            channel = createAndOpenNonBlockingFifo(path, StandardOpenOption.WRITE, StandardOpenOption.READ);
+        }
+
+        @Override
+        public SeekableByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+
+    }
+
+    private static final String MKFIFO_ERROR_FILE_EXISTS = "File exists";
+
+    private static RandomAccessFile createAndOpenFifo(String path, String mode) throws IOException {
+        if (!Files.exists(Paths.get(path))) {
+            // try to create fifo on demand
+            createNamedPipe(path);
+        }
+        return new RandomAccessFile(path, mode);
+    }
+
+    private static FileChannel createAndOpenNonBlockingFifo(String path, OpenOption... openOptions) throws IOException {
+        if (!Files.exists(Paths.get(path))) {
+            // try to create fifo on demand
+            createNamedPipe(path);
+        }
+        return FileChannel.open(Paths.get(path), openOptions);
+    }
+
+    /**
+     * Creates a named pipe (FIFO) by invoking {@code mkfifo} in a shell.
+     *
+     * <b>NOTE:</b> This is a Linux-specific operation and won't work on other OSes.
+     *
+     * @param path The path to the named pipe.
+     * @throws IOException
+     */
+    private static void createNamedPipe(String path) throws IOException {
+
+        String[] command = new String[]{"mkfifo", path};
+        ProcessBuilder pb = new ProcessBuilder(command);
+        pb.redirectError(Redirect.INHERIT);
+        Process p = pb.start();
+        InputStream is = p.getInputStream();
+        ProcessOutputManager.OutputThreadVariable readThread = new ProcessOutputManager.OutputThreadVariable(command[0], is);
+        readThread.start();
+        try {
+            int rc = p.waitFor();
+            if (rc == 0) {
+                readThread.join();
+                String msg = new String(Arrays.copyOf(readThread.getData(), readThread.getTotalRead()));
+
+                // if the message is not empty and it is not the "file exists" error, then throw
+                // error
+                if (!msg.isEmpty() && !msg.endsWith(MKFIFO_ERROR_FILE_EXISTS)) {
+                    throw new IOException(msg);
+                }
+            }
+        } catch (InterruptedException ex) {
+            // fall through
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FileConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FileConnections.java
index 77c0e2875edf4e45f279d1eea5742f279efdd91d..a72e817ec18f761e35c1fc294f02669dc40240b2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FileConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/FileConnections.java
@@ -22,9 +22,8 @@
  */
 package com.oracle.truffle.r.runtime.conn;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -32,34 +31,44 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.FileChannel;
+import java.nio.file.OpenOption;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
 
+import org.tukaani.xz.LZMA2Options;
+import org.tukaani.xz.XZ;
 import org.tukaani.xz.XZInputStream;
+import org.tukaani.xz.XZOutputStream;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RCompression;
+import com.oracle.truffle.r.runtime.RCompression.Type;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.TempPathName;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BasePathRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadWriteRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateWriteRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.OpenMode;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ReadWriteHelper;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public class FileConnections {
+    public static final int GZIP_BUFFER_SIZE = (2 << 20);
+
     /**
      * Base class for all modes of file connections.
-     *
      */
     public static class FileRConnection extends BasePathRConnection {
+        private final boolean raw;
 
-        public FileRConnection(String path, String modeString) throws IOException {
-            super(checkTemp(path), ConnectionClass.File, modeString);
+        public FileRConnection(String description, String path, String modeString, boolean blocking, String encoding, boolean raw) throws IOException {
+            super(description, checkTemp(path), ConnectionClass.File, modeString, blocking, encoding);
+            this.raw = raw;
             openNonLazyConnection();
         }
 
@@ -73,278 +82,251 @@ public class FileConnections {
 
         @Override
         protected void createDelegateConnection() throws IOException {
-            DelegateRConnection delegate = null;
-            switch (getOpenMode().abstractOpenMode) {
-                case Read:
-                    delegate = new FileReadTextRConnection(this);
-                    break;
-                case Write:
-                    delegate = new FileWriteTextRConnection(this, false);
-                    break;
-                case Append:
-                    delegate = new FileWriteTextRConnection(this, true);
-                    break;
-                case ReadBinary:
-                    delegate = new FileReadBinaryRConnection(this);
-                    break;
-                case WriteBinary:
-                    delegate = new FileWriteBinaryConnection(this, false);
-                    break;
-                case ReadWriteTrunc:
-                case ReadWriteTruncBinary:
-                    delegate = new FileReadWriteConnection(this);
-                    break;
-                default:
-                    throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode());
-            }
+
+            DelegateRConnection delegate = FileConnections.createDelegateConnection(this, RCompression.Type.NONE, raw);
             setDelegate(delegate);
         }
     }
 
-    static class FileReadTextRConnection extends DelegateReadRConnection implements ReadWriteHelper {
-        private InputStream inputStream;
-
-        FileReadTextRConnection(BasePathRConnection base) throws IOException {
-            super(base);
-            // can be compressed - check for it
-            RCompression.Type cType = RCompression.getCompressionType(base.path);
-            switch (cType) {
-                case NONE:
-                    inputStream = new BufferedInputStream(new FileInputStream(base.path));
-                    break;
-                case GZIP:
-                    inputStream = new GZIPInputStream(new FileInputStream(base.path), CompressedConnections.GZIP_BUFFER_SIZE);
-                    break;
-                case BZIP2:
-                    // no in Java support, so go via byte array
-                    byte[] bzipUdata = RCompression.bzipUncompressFromFile(base.path);
-                    inputStream = new ByteArrayInputStream(bzipUdata);
-                    break;
-                case XZ:
-                    inputStream = new XZInputStream(new FileInputStream(base.path));
-                    break;
-                default:
-                    throw RError.nyi(RError.SHOW_CALLER2, "compression type: " + cType.name());
-            }
+    /**
+     * Base class for all modes of gzfile/bzfile/xzfile connections. N.B. In GNU R these can read
+     * gzip, bzip, lzma and uncompressed files, and this has to be implemented by reading the first
+     * few bytes of the file and detecting the type of the file.
+     */
+    public static class CompressedRConnection extends BasePathRConnection {
+        private final RCompression.Type cType;
+        @SuppressWarnings("unused") private final int compression; // TODO
+
+        public CompressedRConnection(String path, String modeString, Type cType, String encoding, int compression) throws IOException {
+            super(path, path, mapConnectionClass(cType), modeString, AbstractOpenMode.ReadBinary, encoding);
+            this.cType = cType;
+            this.compression = compression;
+            openNonLazyConnection();
         }
 
         @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_READ_BINARY_CONNECTION);
-        }
+        protected void createDelegateConnection() throws IOException {
+            setDelegate(FileConnections.createDelegateConnection(this, cType, false));
 
-        @Override
-        public byte[] readBinChars() throws IOException {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_READ_BINARY_CONNECTION);
         }
 
-        @TruffleBoundary
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
+        // @Override
+        /**
+         * GnuR behavior for lazy connections is odd, e.g. gzfile returns "text", even though the
+         * default mode is "rb".
+         */
+        // public boolean isTextMode() {
+        // }
+    }
 
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, inputStream, useBytes);
-        }
+    private static DelegateRConnection createUncompressedDelegateConnection(BasePathRConnection base)
+                    throws IOException {
+
+        DelegateRConnection delegate = null;
+        switch (base.getOpenMode().abstractOpenMode) {
+            case Read:
+                delegate = new FileReadTextRConnection(base);
+                break;
+            case ReadBinary:
+                delegate = new FileReadBinaryRConnection(base);
+                break;
+            case Write:
+                delegate = new FileWriteTextRConnection(base, false);
+                break;
+            case Append:
+                delegate = new FileWriteTextRConnection(base, true);
+                break;
+            case WriteBinary:
+                delegate = new FileWriteBinaryConnection(base, false);
+                break;
+            case ReadWriteTrunc:
+            case ReadWriteTruncBinary:
+                delegate = new FileReadWriteConnection(base);
+                break;
+            default:
+                throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + base.getOpenMode());
+        }
+        return delegate;
+    }
 
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
+    private static DelegateRConnection createGZIPDelegateConnection(BasePathRConnection base) throws IOException {
+
+        switch (base.getOpenMode().abstractOpenMode) {
+            case Read:
+            case ReadBinary:
+                return new CompressedInputRConnection(base, new GZIPInputStream(new FileInputStream(base.path), GZIP_BUFFER_SIZE));
+            case Append:
+            case AppendBinary:
+                return new CompressedOutputRConnection(base, new GZIPOutputStream(new FileOutputStream(base.path, true), GZIP_BUFFER_SIZE), true);
+            case Write:
+            case WriteBinary:
+                return new CompressedOutputRConnection(base, new GZIPOutputStream(new FileOutputStream(base.path, false), GZIP_BUFFER_SIZE), true);
+            default:
+                throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + base.getOpenMode());
         }
+    }
 
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
+    private static DelegateRConnection createXZDelegateConnection(BasePathRConnection base) throws IOException {
+
+        switch (base.getOpenMode().abstractOpenMode) {
+            case Read:
+            case ReadBinary:
+                return new CompressedInputRConnection(base, new XZInputStream(new FileInputStream(base.path)));
+            case Append:
+            case AppendBinary:
+                return new CompressedOutputRConnection(base, new XZOutputStream(new FileOutputStream(base.path, true), new LZMA2Options(), XZ.CHECK_CRC32), false);
+            case Write:
+            case WriteBinary:
+                return new CompressedOutputRConnection(base, new XZOutputStream(new FileOutputStream(base.path, false), new LZMA2Options(), XZ.CHECK_CRC32), false);
+            default:
+                throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + base.getOpenMode());
         }
+    }
 
-        @Override
-        public void close() throws IOException {
-            inputStream.close();
+    private static DelegateRConnection createBZIP2DelegateConnection(BasePathRConnection base) throws IOException {
+
+        switch (base.getOpenMode().abstractOpenMode) {
+            case Read:
+            case ReadBinary:
+                byte[] bzipUdata = RCompression.bzipUncompressFromFile(base.path);
+                return new ByteStreamCompressedInputRConnection(base, new ByteArrayInputStream(bzipUdata));
+            case Append:
+            case AppendBinary:
+                return new BZip2OutputRConnection(base, new ByteArrayOutputStream(), true);
+            case Write:
+            case WriteBinary:
+                return new BZip2OutputRConnection(base, new ByteArrayOutputStream(), false);
+            default:
+                throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + base.getOpenMode());
         }
     }
 
-    private static class FileWriteTextRConnection extends DelegateWriteRConnection implements ReadWriteHelper {
-        private final BufferedOutputStream outputStream;
+    private static DelegateRConnection createDelegateConnection(BasePathRConnection base, RCompression.Type cType, boolean raw) throws IOException {
+        AbstractOpenMode openMode = base.getOpenMode().abstractOpenMode;
 
-        FileWriteTextRConnection(FileRConnection base, boolean append) throws IOException {
-            super(base);
-            outputStream = new BufferedOutputStream(new FileOutputStream(base.path, append));
+        /*
+         * For input, we check the actual compression type as GNU R is permissive about the claimed
+         * type except 'raw' is true.
+         */
+        final RCompression.Type cTypeActual;
+        if (!raw && (openMode == AbstractOpenMode.Read || openMode == AbstractOpenMode.ReadBinary)) {
+            cTypeActual = RCompression.getCompressionType(base.path);
+            if (cTypeActual != cType) {
+                base.updateConnectionClass(mapConnectionClass(cTypeActual));
+            }
+        } else {
+            cTypeActual = cType;
         }
 
-        @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(outputStream, s, pad, eos);
+        switch (cTypeActual) {
+            case NONE:
+                return createUncompressedDelegateConnection(base);
+            case GZIP:
+                return createGZIPDelegateConnection(base);
+            case XZ:
+                return createXZDelegateConnection(base);
+            case BZIP2:
+                return createBZIP2DelegateConnection(base);
         }
+        throw RInternalError.shouldNotReachHere("unsupported compression type");
+    }
 
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_WRITE_BINARY_CONNECTION);
+    private static ConnectionClass mapConnectionClass(RCompression.Type cType) {
+        switch (cType) {
+            case NONE:
+                return ConnectionClass.File;
+            case GZIP:
+                return ConnectionClass.GZFile;
+            case BZIP2:
+                return ConnectionClass.BZFile;
+            case XZ:
+                return ConnectionClass.XZFile;
+            default:
+                throw RInternalError.shouldNotReachHere();
         }
+    }
 
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            writeLinesHelper(outputStream, lines, sep);
-            flush();
-        }
+    static class FileReadBinaryRConnection extends DelegateReadRConnection {
 
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            writeStringHelper(outputStream, s, nl);
-        }
+        private final FileChannel channel;
 
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return outputStream;
+        FileReadBinaryRConnection(BasePathRConnection base) throws IOException {
+            super(base);
+            channel = FileChannel.open(Paths.get(base.path), StandardOpenOption.READ);
         }
 
         @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
+        public boolean isSeekable() {
+            return true;
         }
 
         @Override
-        public void close() throws IOException {
-            outputStream.close();
+        public ByteChannel getChannel() {
+            return channel;
         }
 
-        @Override
-        public void flush() throws IOException {
-            outputStream.flush();
-        }
     }
 
-    static class FileReadBinaryRConnection extends DelegateReadRConnection implements ReadWriteHelper {
-        private final FileInputStream inputStream;
+    static class FileReadTextRConnection extends FileReadBinaryRConnection {
 
-        FileReadBinaryRConnection(BasePathRConnection base) throws IOException {
+        FileReadTextRConnection(BasePathRConnection base) throws IOException {
             super(base);
-            inputStream = new FileInputStream(base.path);
-        }
-
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, inputStream, useBytes);
         }
 
         @Override
         public int readBin(ByteBuffer buffer) throws IOException {
-            return inputStream.getChannel().read(buffer);
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_READ_BINARY_CONNECTION);
         }
 
         @Override
         public byte[] readBinChars() throws IOException {
-            return readBinCharsHelper(inputStream);
-        }
-
-        @TruffleBoundary
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            inputStream.close();
-        }
-
-        @Override
-        public boolean isSeekable() {
-            return true;
-        }
-
-        @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            long position = inputStream.getChannel().position();
-            switch (seekMode) {
-                case ENQUIRE:
-                    break;
-                case CURRENT:
-                    if (offset != 0) {
-                        inputStream.getChannel().position(position + offset);
-                    }
-                    break;
-                case START:
-                    inputStream.getChannel().position(offset);
-                    break;
-                case END:
-                    throw RInternalError.unimplemented();
-
-            }
-            return position;
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_READ_BINARY_CONNECTION);
         }
     }
 
-    private static class FileWriteBinaryConnection extends DelegateWriteRConnection implements ReadWriteHelper {
-        private final FileOutputStream outputStream;
+    private static class FileWriteTextRConnection extends FileWriteBinaryConnection {
 
-        FileWriteBinaryConnection(FileRConnection base, boolean append) throws IOException {
-            super(base);
-            outputStream = new FileOutputStream(base.path, append);
-        }
-
-        @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(outputStream, s, pad, eos);
+        FileWriteTextRConnection(BasePathRConnection base, boolean append) throws IOException {
+            super(base, append);
         }
 
         @Override
         public void writeBin(ByteBuffer buffer) throws IOException {
-            outputStream.getChannel().write(buffer);
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_WRITE_BINARY_CONNECTION);
         }
+    }
 
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return outputStream;
-        }
+    private static class FileWriteBinaryConnection extends DelegateWriteRConnection {
 
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
+        private final FileChannel channel;
 
-        @Override
-        public void close() throws IOException {
-            flush();
-            outputStream.close();
-        }
-
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            for (int i = 0; i < lines.getLength(); i++) {
-                String line = lines.getDataAt(i);
-                outputStream.write(line.getBytes());
-                outputStream.write(sep.getBytes());
+        FileWriteBinaryConnection(BasePathRConnection base, boolean append) throws IOException {
+            super(base);
+            List<OpenOption> opts = new ArrayList<>();
+            opts.add(StandardOpenOption.WRITE);
+            opts.add(StandardOpenOption.CREATE);
+            if (append) {
+                opts.add(StandardOpenOption.APPEND);
+            } else {
+                opts.add(StandardOpenOption.TRUNCATE_EXISTING);
             }
+            channel = FileChannel.open(Paths.get(base.path), opts.toArray(new OpenOption[opts.size()]));
+
         }
 
         @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            writeStringHelper(outputStream, s, nl);
+        public boolean isSeekable() {
+            return true;
         }
 
         @Override
-        public void flush() throws IOException {
-            outputStream.flush();
+        public ByteChannel getChannel() {
+            return channel;
         }
+
     }
 
-    private static class FileReadWriteConnection extends DelegateReadWriteRConnection implements ReadWriteHelper {
+    private static class FileReadWriteConnection extends DelegateReadWriteRConnection {
         /*
          * This is a minimal implementation to support one specific use in package installation.
          *
@@ -356,20 +338,8 @@ public class FileConnections {
         private long readOffset;
         private long writeOffset;
         private SeekRWMode lastMode = SeekRWMode.READ;
-        private final RAFInputStream inputStream;
-
-        /**
-         * Allows an {@link RandomAccessFile} to appear to be an {@link InputStream}.
-         *
-         */
-        private class RAFInputStream extends InputStream {
-            @Override
-            public int read() throws IOException {
-                return FileReadWriteConnection.this.getc();
-            }
-        }
 
-        FileReadWriteConnection(FileRConnection base) throws IOException {
+        FileReadWriteConnection(BasePathRConnection base) throws IOException {
             super(base);
             OpenMode openMode = base.getOpenMode();
             String rafMode = null;
@@ -382,7 +352,6 @@ public class FileConnections {
                     throw RInternalError.shouldNotReachHere();
             }
             raf = new RandomAccessFile(base.path, rafMode);
-            inputStream = new RAFInputStream();
         }
 
         @Override
@@ -430,25 +399,9 @@ public class FileConnections {
         }
 
         @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
+        public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
             raf.seek(readOffset);
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
-        }
-
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            throw RInternalError.unimplemented();
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
+            return super.readLines(n, warn, skipNul);
         }
 
         @Override
@@ -458,9 +411,8 @@ public class FileConnections {
 
         @Override
         public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            // TODO encodings
             raf.seek(writeOffset);
-            byte[] sepData = sep.getBytes();
+            byte[] sepData = sep.getBytes(base.getEncoding());
             for (int i = 0; i < lines.getLength(); i++) {
                 writeString(lines.getDataAt(i), false);
                 raf.write(sepData);
@@ -469,41 +421,102 @@ public class FileConnections {
             lastMode = SeekRWMode.WRITE;
         }
 
-        @Override
-        public void flush() throws IOException {
-        }
-
         @Override
         public void writeString(String s, boolean nl) throws IOException {
-            raf.write(s.getBytes());
+            raf.write(s.getBytes(base.getEncoding()));
             if (nl) {
-                raf.writeBytes(System.lineSeparator());
+                raf.write(System.lineSeparator().getBytes(base.getEncoding()));
             }
         }
 
         @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            throw RInternalError.unimplemented();
+        public ByteChannel getChannel() {
+            return raf.getChannel();
+        }
+
+    }
+
+    private static class CompressedInputRConnection extends DelegateReadRConnection {
+        private final ByteChannel channel;
+
+        protected CompressedInputRConnection(BasePathRConnection base, InputStream is) {
+            super(base);
+            channel = ConnectionSupport.newChannel(is);
         }
 
         @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            throw RInternalError.unimplemented();
+        public ByteChannel getChannel() {
+            return channel;
         }
 
         @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            throw RInternalError.unimplemented();
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class ByteStreamCompressedInputRConnection extends CompressedInputRConnection {
+        ByteStreamCompressedInputRConnection(BasePathRConnection base, ByteArrayInputStream is) {
+            super(base, is);
+        }
+    }
+
+    private static class CompressedOutputRConnection extends DelegateWriteRConnection {
+        protected ByteChannel channel;
+        private final boolean seekable;
+        private long seekPosition = 0L;
+
+        protected CompressedOutputRConnection(BasePathRConnection base, OutputStream os, boolean seekable) {
+            super(base);
+            this.seekable = seekable;
+            this.channel = ConnectionSupport.newChannel(os);
         }
 
         @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            throw RInternalError.unimplemented();
+        public void closeAndDestroy() throws IOException {
+            base.closed = true;
+            close();
         }
 
         @Override
-        public byte[] readBinChars() throws IOException {
-            throw RInternalError.unimplemented();
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            if (seekable) {
+                // TODO GZIP is basically seekable; however, the output stream does not allow any
+                // seeking
+                long oldPos = seekPosition;
+                seekPosition = offset;
+                return oldPos;
+            }
+            return super.seek(offset, seekMode, seekRWMode);
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return seekable;
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return channel;
+        }
+    }
+
+    private static class BZip2OutputRConnection extends CompressedOutputRConnection {
+        private final ByteArrayOutputStream bos;
+        private final boolean append;
+
+        BZip2OutputRConnection(BasePathRConnection base, ByteArrayOutputStream os, boolean append) {
+            super(base, os, false);
+            this.bos = os;
+            this.append = append;
+        }
+
+        @Override
+        public void close() throws IOException {
+            flush();
+            // Now actually do the compression using sub-process
+            byte[] data = bos.toByteArray();
+            RCompression.bzipCompressToFile(data, ((BasePathRConnection) base).path, append);
         }
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/PipeConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/PipeConnections.java
new file mode 100644
index 0000000000000000000000000000000000000000..8aa871b1b29f22629d12022f1a37493da5a01970
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/PipeConnections.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ProcessBuilder.Redirect;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
+
+public class PipeConnections {
+
+    private static Process executeAndJoin(String command) throws IOException {
+        ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", command);
+        pb.redirectError(Redirect.INHERIT);
+        Process p = pb.start();
+        try {
+            p.waitFor();
+        } catch (InterruptedException e) {
+            // TODO not sure how to handle an interrupted exception at this point
+        }
+        return p;
+    }
+
+    public static class PipeRConnection extends BaseRConnection {
+
+        private final String command;
+
+        public PipeRConnection(String command, String open, String encoding) throws IOException {
+            super(ConnectionClass.FIFO, open, AbstractOpenMode.Read, encoding);
+            this.command = command;
+            openNonLazyConnection();
+        }
+
+        @Override
+        protected void createDelegateConnection() throws IOException {
+            final DelegateRConnection delegate;
+            switch (getOpenMode().abstractOpenMode) {
+                case Read:
+                case ReadBinary:
+                    delegate = new PipeReadRConnection(this, command);
+                    break;
+                case Write:
+                case WriteBinary:
+                    delegate = new PipeWriteConnection(this, command);
+                    break;
+                case ReadAppend:
+                case ReadWrite:
+                case ReadWriteBinary:
+                case ReadWriteTrunc:
+                case ReadWriteTruncBinary:
+                    delegate = new PipeReadWriteConnection(this, command);
+                    break;
+                default:
+                    throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode());
+            }
+            setDelegate(delegate);
+        }
+
+        @Override
+        public String getSummaryDescription() {
+            return command;
+        }
+    }
+
+    static class PipeReadRConnection extends DelegateReadRConnection {
+        private final ByteChannel channel;
+
+        protected PipeReadRConnection(BaseRConnection base, String command) throws IOException {
+            super(base);
+            Process p = PipeConnections.executeAndJoin(command);
+            channel = ConnectionSupport.newChannel(p.getInputStream());
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class PipeWriteConnection extends DelegateWriteRConnection {
+        private final ByteChannel channel;
+
+        PipeWriteConnection(BaseRConnection base, String command) throws IOException {
+            super(base);
+            Process p = PipeConnections.executeAndJoin(command);
+            channel = ConnectionSupport.newChannel(p.getOutputStream());
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+    }
+
+    private static class PipeReadWriteConnection extends DelegateReadWriteRConnection {
+
+        private final RWChannel channel;
+
+        protected PipeReadWriteConnection(BaseRConnection base, String command) throws IOException {
+            super(base);
+            Process p = PipeConnections.executeAndJoin(command);
+            channel = new RWChannel(p.getInputStream(), p.getOutputStream());
+        }
+
+        @Override
+        public ByteChannel getChannel() {
+            return channel;
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
+
+        private static final class RWChannel implements ByteChannel {
+            private final ReadableByteChannel rchannel;
+            private final WritableByteChannel wchannel;
+
+            RWChannel(InputStream in, OutputStream out) {
+                rchannel = Channels.newChannel(in);
+                wchannel = Channels.newChannel(out);
+            }
+
+            @Override
+            public int read(ByteBuffer dst) throws IOException {
+                return rchannel.read(dst);
+            }
+
+            @Override
+            public boolean isOpen() {
+                return rchannel.isOpen() && wchannel.isOpen();
+            }
+
+            @Override
+            public void close() throws IOException {
+                rchannel.close();
+                wchannel.close();
+            }
+
+            @Override
+            public int write(ByteBuffer src) throws IOException {
+                return wchannel.write(src);
+            }
+
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
index b699372ccaae032d19a0777b141c5d47ba662ce7..341d12e3ba8fbdd49f07a197ab28d790f74acbb1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,156 +26,64 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
-import java.util.LinkedList;
+import java.nio.channels.ByteChannel;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 /**
  * Denotes an R {@code connection} instance used in the {@code base} I/O library.
- *
- * TODO Refactor the pushBack code into ConnectionsSupport
  */
-public abstract class RConnection implements AutoCloseable {
-
-    public final RAbstractIntVector asVector() {
-        String[] classes = new String[]{ConnectionSupport.getBaseConnection(this).getConnectionClass().getPrintName(), "connection"};
-
-        RAbstractIntVector result = RDataFactory.createIntVector(new int[]{getDescriptor()}, true);
-
-        RStringVector classVector = RDataFactory.createStringVector(classes, RDataFactory.COMPLETE_VECTOR);
-        // it's important to put "this" into the externalptr, so that it doesn't get collected
-        RExternalPtr connectionId = RDataFactory.createExternalPtr(null, this, RDataFactory.createSymbol("connection"), RNull.instance);
-        DynamicObject attrs = RAttributesLayout.createClassWithConnId(classVector, connectionId);
-        result.initAttributes(attrs);
-        return result;
-    }
+public interface RConnection extends AutoCloseable {
 
-    public static BaseRConnection fromIndex(int con) {
+    static BaseRConnection fromIndex(int con) {
         return RContext.getInstance().stateRConnection.getConnection(con, true);
     }
 
-    private LinkedList<String> pushBack;
-
-    public abstract String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException;
-
-    private String readOneLineWithPushBack(String[] res, int ind, @SuppressWarnings("unused") boolean warn, @SuppressWarnings("unused") boolean skipNul) {
-        String s = pushBack.pollLast();
-        if (s == null) {
-            return null;
-        } else {
-            String[] lines = s.split("\n", 2);
-            if (lines.length == 2) {
-                // we hit end of the line
-                if (lines[1].length() != 0) {
-                    // suffix is not empty and needs to be processed later
-                    pushBack.push(lines[1]);
-                }
-                res[ind] = lines[0];
-                return null;
-            } else {
-                // no end of the line found yet
-                StringBuilder sb = new StringBuilder();
-                do {
-                    assert lines.length == 1;
-                    sb.append(lines[0]);
-                    s = pushBack.pollLast();
-                    if (s == null) {
-                        break;
-                    }
-
-                    lines = s.split("\n", 2);
-                    if (lines.length == 2) {
-                        // we hit end of the line
-                        if (lines[1].length() != 0) {
-                            // suffix is not empty and needs to be processed later
-                            pushBack.push(lines[1]);
-                        }
-                        res[ind] = sb.append(lines[0]).toString();
-                        return null;
-                    } // else continue
-                } while (true);
-                return sb.toString();
-            }
-        }
-    }
-
-    @TruffleBoundary
-    private String[] readLinesWithPushBack(int n, boolean warn, boolean skipNul) throws IOException {
-        String[] res = new String[n];
-        for (int i = 0; i < n; i++) {
-            String s = readOneLineWithPushBack(res, i, warn, skipNul);
-            if (s == null) {
-                if (res[i] == null) {
-                    // no more push back value
-                    System.arraycopy(readLinesInternal(n - i, warn, skipNul), 0, res, i, n - i);
-                    pushBack = null;
-                    break;
-                }
-                // else res[i] has been filled - move to trying to fill the next one
-            } else {
-                // reached the last push back value without reaching and of line
-                assert pushBack.size() == 0;
-                System.arraycopy(readLinesInternal(n - i, warn, skipNul), 0, res, i, n - i);
-                res[i] = s + res[i];
-                pushBack = null;
-                break;
-            }
-        }
-        return res;
-    }
-
     /**
-     * Read (n > 0 up to n else unlimited) lines on the connection.
+     * Return the underlying input stream (for internal use).<br>
+     * <p>
+     * <b>NOTE:</b> The connection may do some caching internally! Therefore, the behavior is
+     * undefined if you mix using the input stream directly and using the read methods of the
+     * connection.
+     * </p>
      *
      */
-    @TruffleBoundary
-    public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
-        if (pushBack == null) {
-            return readLinesInternal(n, warn, skipNul);
-        } else if (pushBack.size() == 0) {
-            pushBack = null;
-            return readLinesInternal(n, warn, skipNul);
-        } else {
-            return readLinesWithPushBack(n, warn, skipNul);
-        }
-    }
+    InputStream getInputStream() throws IOException;
 
     /**
-     * Return the underlying input stream (for internal use). TODO Replace with a more principled
-     * solution.
+     * Return the underlying output stream (for internal use).<br>
+     * <p>
+     * <b>NOTE:</b> The connection may do some caching internally! Therefore, the behavior is
+     * undefined if you mix using the output stream directly and using the write methods of the
+     * connection.
+     * </p>
      */
-    public abstract InputStream getInputStream() throws IOException;
+    OutputStream getOutputStream() throws IOException;
 
     /**
-     * Return the underlying output stream (for internal use). TODO Replace with a more principled
-     * solution.
+     * Return the underlying byte channel (for internal use).
+     *
+     * @throws IOException
      */
-    public abstract OutputStream getOutputStream() throws IOException;
+    ByteChannel getChannel() throws IOException;
 
     /**
      * Close the connection. The corresponds to the {@code R close} function.
      */
-    public abstract void closeAndDestroy() throws IOException;
+    void closeAndDestroy() throws IOException;
 
     /**
      * Returns {@ode true} iff we can read on this connection.
      */
-    public abstract boolean canRead();
+    boolean canRead();
 
     /**
      * Returns {@ode true} iff we can write on this connection.
      */
-    public abstract boolean canWrite();
+    boolean canWrite();
 
     /**
      * Forces the connection open. If the connection was already open does nothing. Otherwise, tries
@@ -198,56 +106,23 @@ public abstract class RConnection implements AutoCloseable {
      * rely on it but should use the result in the body of the {@code try} block. If the connection
      * cannot be opened {@link IOException} is thrown.
      */
-    public abstract RConnection forceOpen(String modeString) throws IOException;
+    RConnection forceOpen(String modeString) throws IOException;
 
     /**
      * Closes the internal state of the stream, but does not set the connection state to "closed",
      * i.e., allowing it to be re-opened.
      */
     @Override
-    public abstract void close() throws IOException;
+    void close() throws IOException;
 
-    /**
-     * Pushes lines back to the connection.
-     */
-    @TruffleBoundary
-    public final void pushBack(RAbstractStringVector lines, boolean addNewLine) {
-        if (pushBack == null) {
-            pushBack = new LinkedList<>();
-        }
-        for (int i = 0; i < lines.getLength(); i++) {
-            String newLine = lines.getDataAt(i);
-            if (addNewLine) {
-                newLine = newLine + '\n';
-            }
-            pushBack.addFirst(newLine);
-        }
-    }
-
-    /**
-     * Return the length of the push back.
-     */
-    @TruffleBoundary
-    public final int pushBackLength() {
-        return pushBack == null ? 0 : pushBack.size();
-    }
-
-    /**
-     * Clears the pushback.
-     */
-    @TruffleBoundary
-    public final void pushBackClear() {
-        pushBack = null;
-    }
-
-    public enum SeekMode {
+    enum SeekMode {
         ENQUIRE,
         START,
         CURRENT,
         END
     }
 
-    public enum SeekRWMode {
+    enum SeekRWMode {
         LAST,
         READ,
         WRITE
@@ -256,27 +131,27 @@ public abstract class RConnection implements AutoCloseable {
     /**
      * Support for {@code isSeekable} Internal.
      */
-    public abstract boolean isSeekable();
+    boolean isSeekable();
 
     /**
-     * Support for {@code seek} Internal.
+     * Allows to seek in the connection.
      */
-    public abstract long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException;
+    long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException;
 
     /**
      * Internal support for reading one character at a time.
      */
-    public abstract int getc() throws IOException;
+    int getc() throws IOException;
 
     /**
      * Write the {@code lines} to the connection, with {@code sep} appended after each "line". N.B.
      * The output will only appear as a sequence of lines if {@code sep == "\n"}.
      */
-    public abstract void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException;
+    void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException;
 
-    public abstract void flush() throws IOException;
+    void flush() throws IOException;
 
-    public abstract int getDescriptor();
+    int getDescriptor();
 
     /**
      * Writes {@code s} optionally followed by a newline to the connection. This does not correspond
@@ -284,7 +159,7 @@ public abstract class RConnection implements AutoCloseable {
      * Since these can be diverted by the {@code sink} builtin, every output connection class must
      * support this.
      */
-    public abstract void writeString(String s, boolean nl) throws IOException;
+    void writeString(String s, boolean nl) throws IOException;
 
     /**
      * Internal connection-specific support for the {@code writeChar} builtin.
@@ -294,20 +169,20 @@ public abstract class RConnection implements AutoCloseable {
      * @param eos string to append to s
      * @param useBytes TODO
      */
-    public abstract void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException;
+    void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException;
 
     /**
      * Internal connection-specific support for the {@code readChar} builtin.
      *
      * @param nchars number of characters to read
      */
-    public abstract String readChar(int nchars, boolean useBytes) throws IOException;
+    String readChar(int nchars, boolean useBytes) throws IOException;
 
     /**
      * Internal connection-specific support for the {@code writeBin} builtin. The implementation
      * should attempt to write all the data, denoted by {@code buffer.remaining()}.
      */
-    public abstract void writeBin(ByteBuffer buffer) throws IOException;
+    void writeBin(ByteBuffer buffer) throws IOException;
 
     /**
      * Internal connection-specific support for the {@code readBin} builtin. The buffer is allocated
@@ -315,7 +190,7 @@ public abstract class RConnection implements AutoCloseable {
      * should attempt to read that much data, returning the actual number read as the result. EOS is
      * denoted by a return value of zero.
      */
-    public abstract int readBin(ByteBuffer buffer) throws IOException;
+    int readBin(ByteBuffer buffer) throws IOException;
 
     /**
      * Internal connection-specific support for the {@code readBin} builtin on character data.
@@ -324,16 +199,23 @@ public abstract class RConnection implements AutoCloseable {
      * implies that no data was read. The caller must locate the null terminator to determine the
      * length of the string.
      */
-    public abstract byte[] readBinChars() throws IOException;
+    byte[] readBinChars() throws IOException;
+
+    /**
+     * Read (n > 0 up to n else unlimited) lines on the connection.
+     */
+    @TruffleBoundary
+    String[] readLines(int n, boolean warn, boolean skipNul) throws IOException;
 
     /**
      * Returns {@code true} iff this is a text mode connection.
      */
-    public abstract boolean isTextMode();
+    boolean isTextMode();
 
     /**
      * Returns {@code true} iff this connection is open.
      */
-    public abstract boolean isOpen();
+    boolean isOpen();
 
+    void pushBack(RAbstractStringVector lines, boolean addNewLine);
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RawConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RawConnections.java
index 02d6e0dd6c3809506191ce9fb7076ad85ab4a366..3213cfc97fe70ac6131796149f234e9f0b46c4ad 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RawConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RawConnections.java
@@ -23,23 +23,15 @@
 package com.oracle.truffle.r.runtime.conn;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
 import java.util.Objects;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadWriteRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateWriteRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ReadWriteHelper;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public class RawConnections {
 
@@ -99,7 +91,7 @@ public class RawConnections {
             return channel.getBuffer();
         }
 
-        public static long seek(SeekableMemoryByteChannel channel, long offset, SeekMode seekMode, @SuppressWarnings("unused") SeekRWMode seekRWMode) throws IOException {
+        private static long seek(SeekableByteChannel channel, long offset, SeekMode seekMode, @SuppressWarnings("unused") SeekRWMode seekRWMode) throws IOException {
             final long origPos = channel.position();
             final long newPos;
             switch (seekMode) {
@@ -122,7 +114,7 @@ public class RawConnections {
 
     }
 
-    static class RawReadRConnection extends DelegateReadRConnection implements ReadWriteHelper {
+    static class RawReadRConnection extends DelegateReadRConnection {
         private SeekableMemoryByteChannel channel;
 
         RawReadRConnection(BaseRConnection base, SeekableMemoryByteChannel channel) {
@@ -139,14 +131,9 @@ public class RawConnections {
             super(base);
         }
 
-        @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            return channel.read(buffer);
-        }
-
         @Override
         public byte[] readBinChars() throws IOException {
-            // acquire copy from buffer without advancing the cursor
+            // acquire copy from buffer without advancing the cursorskipNul
             final byte[] buffer = channel.getBufferFromCursor();
             int i = 0;
             while (i < buffer.length && buffer[i] != (byte) 0) {
@@ -160,45 +147,23 @@ public class RawConnections {
             return new byte[0];
         }
 
-        @TruffleBoundary
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(channel.getInputStream(), n, warn, skipNul);
-        }
-
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, channel.getInputStream(), useBytes);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return channel.getInputStream();
-        }
-
         @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
         }
 
         @Override
-        public void close() throws IOException {
-            channel.close();
+        public SeekableByteChannel getChannel() {
+            return channel;
         }
 
         @Override
-        public OutputStream getOutputStream() {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.NOT_AN_OUTPUT_RAW_CONNECTION);
-        }
-
-        @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
+        public boolean isSeekable() {
+            return true;
         }
     }
 
-    private static class RawWriteBinaryConnection extends DelegateWriteRConnection implements ReadWriteHelper {
+    private static class RawWriteBinaryConnection extends DelegateWriteRConnection {
         private final SeekableMemoryByteChannel channel;
 
         RawWriteBinaryConnection(BaseRConnection base, SeekableMemoryByteChannel channel, boolean append) {
@@ -214,67 +179,31 @@ public class RawConnections {
         }
 
         @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(channel.getOutputStream(), s, pad, eos);
-        }
-
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            channel.write(buffer);
-        }
-
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return channel.getOutputStream();
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            flush();
-            channel.close();
-        }
-
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            for (int i = 0; i < lines.getLength(); i++) {
-                String line = lines.getDataAt(i);
-                channel.write(line.getBytes());
-                channel.write(sep.getBytes());
-            }
-        }
-
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            writeStringHelper(channel.getOutputStream(), s, nl);
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
         }
 
         @Override
-        public void flush() throws IOException {
-            // nothing to do
+        public SeekableByteChannel getChannel() {
+            return channel;
         }
 
         @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
+        public boolean isSeekable() {
+            return true;
         }
     }
 
-    private static class RawReadWriteConnection extends DelegateReadWriteRConnection implements ReadWriteHelper {
+    private static class RawReadWriteConnection extends DelegateReadWriteRConnection {
 
         private final SeekableMemoryByteChannel channel;
 
         protected RawReadWriteConnection(BaseRConnection base, SeekableMemoryByteChannel channel, boolean append) {
             super(base);
-            this.channel = Objects.requireNonNull(channel);
-            if (!append) {
+            this.channel = channel;
+            if (append) {
                 try {
-                    channel.position(0);
+                    channel.position(channel.size());
                 } catch (IOException e) {
                     RInternalError.shouldNotReachHere();
                 }
@@ -282,79 +211,18 @@ public class RawConnections {
         }
 
         @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            throw RInternalError.unimplemented();
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return channel.getInputStream();
-        }
-
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return channel.getOutputStream();
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            channel.close();
-        }
-
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            for (int i = 0; i < lines.getLength(); i++) {
-                channel.write(lines.getDataAt(i).getBytes());
-                channel.write(sep.getBytes());
-            }
-        }
-
-        @Override
-        public void flush() throws IOException {
-            // nothing to do
-        }
-
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            channel.write(s.getBytes());
-            if (nl) {
-                channel.write(System.lineSeparator().getBytes());
-            }
-        }
-
-        @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(channel.getOutputStream(), s, pad, eos);
-        }
-
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, channel.getInputStream(), useBytes);
-        }
-
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            channel.write(buffer);
-        }
-
-        @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            return channel.read(buffer);
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
         }
 
         @Override
-        public byte[] readBinChars() throws IOException {
-            throw RInternalError.unimplemented();
+        public SeekableByteChannel getChannel() {
+            return channel;
         }
 
         @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
-            return RawRConnection.seek(channel, offset, seekMode, seekRWMode);
+        public boolean isSeekable() {
+            return true;
         }
 
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java
index 79dd7dfe9ef7fec4d1905242344c39aaa01cb6c7..c625c855a30a84a941f19ceed6f05d377e14fe6f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java
@@ -89,7 +89,7 @@ public class SeekableMemoryByteChannel implements SeekableByteChannel {
         this(clp2(Objects.requireNonNull(initialBuffer).length));
         assert buf.length >= initialBuffer.length;
         endPos = initialBuffer.length;
-        position = initialBuffer.length;
+        position = 0L;
         System.arraycopy(initialBuffer, 0, buf, 0, initialBuffer.length);
     }
 
@@ -105,9 +105,10 @@ public class SeekableMemoryByteChannel implements SeekableByteChannel {
         }
         Objects.requireNonNull(dst);
         RInternalError.guarantee(dst.remaining() <= Integer.MAX_VALUE);
-        final int nCanRead = (int) Math.min(dst.remaining(), size());
+        final int nCanRead = (int) Math.min(dst.remaining(), remaining());
         dst.put(buf, (int) (position - offset), nCanRead);
         position += nCanRead;
+        assert position <= endPos;
         return nCanRead;
     }
 
@@ -181,11 +182,18 @@ public class SeekableMemoryByteChannel implements SeekableByteChannel {
         return endPos - offset;
     }
 
+    private long remaining() {
+        return endPos - position;
+    }
+
     @Override
     public SeekableByteChannel position(long newPosition) throws IOException {
         if (newPosition < 0) {
             throw new IllegalArgumentException("newPosition must not be negative");
         }
+        if (newPosition + offset > endPos) {
+            throw new IllegalArgumentException("setting position outside data range is currently not supported");
+        }
         position = newPosition + offset;
         return this;
     }
@@ -224,10 +232,13 @@ public class SeekableMemoryByteChannel implements SeekableByteChannel {
     }
 
     public int read() throws IOException {
-        final ByteBuffer bf = ByteBuffer.allocate(1);
-        read(bf);
-        bf.rewind();
-        return bf.get();
+        if (position < endPos) {
+            final ByteBuffer bf = ByteBuffer.allocate(1);
+            read(bf);
+            bf.rewind();
+            return bf.get();
+        }
+        return -1;
     }
 
     private class SyncOS extends OutputStream {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SocketConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SocketConnections.java
index e7da9ceca6c5ac57c0273d57c0c631a8dc9898e9..2f978e6fa943aabad0c82ecf873bfd52a925a407 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SocketConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SocketConnections.java
@@ -23,21 +23,15 @@
 package com.oracle.truffle.r.runtime.conn;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.InetSocketAddress;
-import java.net.ServerSocket;
 import java.net.Socket;
-import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadWriteRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ReadWriteHelper;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public class SocketConnections {
     /**
@@ -50,22 +44,29 @@ public class SocketConnections {
         protected final boolean server;
         protected final String host;
         protected final int port;
-        protected final boolean blocking;
         protected final int timeout;
 
-        public RSocketConnection(String modeString, boolean server, String host, int port, boolean blocking, int timeout) throws IOException {
-            super(ConnectionClass.Socket, modeString, AbstractOpenMode.Read);
+        public RSocketConnection(String modeString, boolean server, String host, int port, boolean blocking, int timeout, String encoding) throws IOException {
+            super(ConnectionClass.Socket, modeString, AbstractOpenMode.Read, blocking, encoding);
             this.server = server;
             this.host = host;
             this.port = port;
-            this.blocking = blocking;
             this.timeout = timeout;
             openNonLazyConnection();
         }
 
         @Override
         protected void createDelegateConnection() throws IOException {
-            DelegateRConnection delegate = server ? new RServerSocketConnection(this) : new RClientSocketConnection(this);
+            DelegateRConnection delegate;
+            if (server) {
+                delegate = new RServerSocketConnection(this);
+            } else {
+                if (isBlocking()) {
+                    delegate = new RClientSocketConnection(this);
+                } else {
+                    delegate = new RClientSocketNonBlockConnection(this);
+                }
+            }
             setDelegate(delegate);
         }
 
@@ -75,11 +76,9 @@ public class SocketConnections {
         }
     }
 
-    private abstract static class RSocketReadWriteConnection extends DelegateReadWriteRConnection implements ReadWriteHelper {
+    private abstract static class RSocketReadWriteConnection extends DelegateReadWriteRConnection {
         private Socket socket;
-        private SocketChannel socketChannel;
-        protected InputStream inputStream;
-        protected OutputStream outputStream;
+        private SocketChannel channel;
         protected final RSocketConnection thisBase;
 
         protected RSocketReadWriteConnection(RSocketConnection base) {
@@ -87,10 +86,11 @@ public class SocketConnections {
             this.thisBase = base;
         }
 
-        protected void openStreams(Socket socketArg) throws IOException {
-            this.socket = socketArg;
-            this.socketChannel = socket.getChannel();
-            if (thisBase.blocking) {
+        protected void openStreams(SocketChannel socketArg) throws IOException {
+            channel = socketArg;
+            socket = socketArg.socket();
+            if (thisBase.isBlocking()) {
+                channel.configureBlocking(true);
                 // Java (int) timeouts do not meet the POSIX standard of 31 days
                 long millisTimeout = ((long) thisBase.timeout) * 1000;
                 if (millisTimeout > Integer.MAX_VALUE) {
@@ -98,95 +98,69 @@ public class SocketConnections {
                 }
                 socket.setSoTimeout((int) millisTimeout);
             } else {
-                socketChannel.configureBlocking(false);
+                channel.configureBlocking(false);
             }
-            inputStream = socket.getInputStream();
-            outputStream = socket.getOutputStream();
-        }
-
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
-        }
-
-        @Override
-        public OutputStream getOutputStream() throws IOException {
-            return outputStream;
-        }
-
-        @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            writeLinesHelper(outputStream, lines, sep);
-        }
-
-        @Override
-        public void writeBin(ByteBuffer buffer) throws IOException {
-            writeBinHelper(buffer, outputStream);
         }
 
         @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            return readBinHelper(buffer, inputStream);
+        public ByteChannel getChannel() {
+            return channel;
         }
 
         @Override
-        public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            writeCharHelper(outputStream, s, pad, eos);
+        public boolean isSeekable() {
+            return false;
         }
+    }
 
-        @Override
-        public void writeString(String s, boolean nl) throws IOException {
-            writeStringHelper(outputStream, s, nl);
-        }
+    private abstract static class RSocketReadWriteNonBlockConnection extends DelegateReadWriteRConnection {
+        private Socket socket;
+        private SocketChannel socketChannel;
 
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, inputStream, useBytes);
+        protected RSocketReadWriteNonBlockConnection(RSocketConnection base) {
+            super(base);
         }
 
-        @Override
-        public byte[] readBinChars() throws IOException {
-            return readBinCharsHelper(inputStream);
+        protected void openStreams(Socket socketArg) throws IOException {
+            this.socket = socketArg;
+            this.socketChannel = socket.getChannel();
+            socketChannel.configureBlocking(false);
         }
 
         @Override
-        public void flush() throws IOException {
-            outputStream.flush();
+        public void close() throws IOException {
+            socketChannel.close();
+            socket.close();
         }
 
         @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
+        public ByteChannel getChannel() {
+            return socketChannel;
         }
 
         @Override
-        public void close() throws IOException {
-            socket.close();
+        public boolean isSeekable() {
+            return false;
         }
     }
 
     private static class RServerSocketConnection extends RSocketReadWriteConnection {
-        private final Socket connectionSocket;
+        private final SocketChannel connectionSocket;
 
         RServerSocketConnection(RSocketConnection base) throws IOException {
             super(base);
             InetSocketAddress addr = new InetSocketAddress(base.port);
-            ServerSocket serverSocket = new ServerSocket();
+            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
+
             // we expect only one connection per-server socket; furthermore, we need to accommodate
             // for multiple connections being established locally on the same server port;
             // consequently, we close the server socket at the end of the constructor and allow
             // address reuse to be able to open the next connection after the current one closes
-            serverSocket.setReuseAddress(true);
-            serverSocket.bind(addr);
-            connectionSocket = serverSocket.accept();
+            serverSocketChannel.socket().setReuseAddress(true);
+            serverSocketChannel.socket().bind(addr);
+            connectionSocket = serverSocketChannel.accept();
             openStreams(connectionSocket);
-            serverSocket.close();
+            serverSocketChannel.close();
         }
 
         @Override
@@ -199,6 +173,15 @@ public class SocketConnections {
     private static class RClientSocketConnection extends RSocketReadWriteConnection {
 
         RClientSocketConnection(RSocketConnection base) throws IOException {
+            super(base);
+            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(base.host, base.port));
+            openStreams(socketChannel);
+        }
+    }
+
+    private static class RClientSocketNonBlockConnection extends RSocketReadWriteNonBlockConnection {
+
+        RClientSocketNonBlockConnection(RSocketConnection base) throws IOException {
             super(base);
             SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(base.host, base.port));
             openStreams(socketChannel.socket());
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
index 22f8ca5d238f1834edc99797045c60ffdbac47c8..6150dc902b2cf11456b38457c64e489a62bf8f12 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -79,15 +79,15 @@ public class StdConnections {
         return RContext.getInstance().stateStdConnections;
     }
 
-    public static RConnection getStdin() {
+    public static BaseRConnection getStdin() {
         return getContextState().stdin;
     }
 
-    public static RConnection getStdout() {
+    public static BaseRConnection getStdout() {
         return getContextState().stdout;
     }
 
-    public static RConnection getStderr() {
+    public static BaseRConnection getStderr() {
         return getContextState().stderr;
     }
 
@@ -161,7 +161,7 @@ public class StdConnections {
         }
 
         @Override
-        public RConnection forceOpen(String modeString) {
+        public BaseRConnection forceOpen(String modeString) {
             return this;
         }
 
@@ -171,7 +171,7 @@ public class StdConnections {
         }
 
         @Override
-        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+        public long seekInternal(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
             throw RError.error(RError.SHOW_CALLER2, RError.Message.UNSEEKABLE_CONNECTION);
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
index 9434f758c41dffdab375149fbc3da7d08555a24d..e789830946419241102ac19c33223ddb19c7cb50 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
@@ -23,9 +23,9 @@
 package com.oracle.truffle.r.runtime.conn;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
 import java.util.ArrayList;
 
 import com.oracle.truffle.r.runtime.RError;
@@ -33,9 +33,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateWriteRConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -45,7 +42,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 public class TextConnections {
     public static class TextRConnection extends BaseRConnection {
         protected String description;
-        protected RAbstractStringVector object;
+        private final RAbstractStringVector object;
         protected REnvironment env;
 
         public TextRConnection(String description, RAbstractStringVector object, REnvironment env, String modeString) throws IOException {
@@ -66,10 +63,14 @@ public class TextConnections {
             DelegateRConnection delegate = null;
             switch (getOpenMode().abstractOpenMode) {
                 case Read:
-                    delegate = new TextReadRConnection(this);
+                    if (object != null) {
+                        delegate = new TextReadRConnection(this, object);
+                    } else {
+                        throw RError.error(RError.SHOW_CALLER2, RError.Message.INVALID_ARGUMENT, "text");
+                    }
                     break;
                 case Write:
-                    delegate = new TextWriteRConnection(this);
+                    delegate = new TextWriteRConnection(this, object);
                     break;
                 default:
                     throw RError.nyi(RError.SHOW_CALLER2, "open mode: " + getOpenMode().modeString);
@@ -77,24 +78,25 @@ public class TextConnections {
             setDelegate(delegate);
         }
 
-        public String[] getValue() {
+        public RAbstractStringVector getValue() {
             return ((GetConnectionValue) theConnection).getValue();
         }
     }
 
     private interface GetConnectionValue {
-        String[] getValue();
+        RAbstractStringVector getValue();
     }
 
     private static class TextReadRConnection extends DelegateReadRConnection implements GetConnectionValue {
         private final String[] lines;
         private int index;
 
-        TextReadRConnection(TextRConnection base) {
+        TextReadRConnection(TextRConnection base, RAbstractStringVector object) {
             super(base);
+            assert object != null;
             StringBuffer sb = new StringBuffer();
-            for (int i = 0; i < base.object.getLength(); i++) {
-                sb.append(base.object.getDataAt(i));
+            for (int i = 0; i < object.getLength(); i++) {
+                sb.append(object.getDataAt(i));
                 // vector elements are implicitly terminated with a newline
                 sb.append('\n');
             }
@@ -102,7 +104,7 @@ public class TextConnections {
         }
 
         @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
+        public String[] readLines(int n, boolean warn, boolean skipNul) throws IOException {
             int nleft = lines.length - index;
             int nlines = nleft;
             if (n > 0) {
@@ -115,44 +117,24 @@ public class TextConnections {
             return result;
         }
 
-        @SuppressWarnings("hiding")
         @Override
-        public void writeLines(RAbstractStringVector lines, String sep, boolean useBytes) throws IOException {
-            throw new IOException(RError.Message.CANNOT_WRITE_CONNECTION.message);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            throw RInternalError.shouldNotReachHere();
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-        }
-
-        @Override
-        public void close() {
-        }
-
-        @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            throw RError.nyi(null, "readBin on text connection");
+        public void close() throws IOException {
+            // nothing to do
         }
 
         @Override
-        public byte[] readBinChars() throws IOException {
-            throw RError.nyi(null, "readBinChars on text connection");
+        public boolean isSeekable() {
+            return false;
         }
 
         @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            throw RError.nyi(null, "readChar on text connection");
+        public ByteChannel getChannel() {
+            throw RInternalError.shouldNotReachHere();
         }
 
         @Override
-        public String[] getValue() {
-            throw RError.nyi(null, "textConnectionValue");
+        public RAbstractStringVector getValue() {
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.NOT_AN_OUTPUT_TEXT_CONNECTION);
         }
     }
 
@@ -160,31 +142,39 @@ public class TextConnections {
         private String incompleteLine;
         private RStringVector textVec;
         private String idName;
+        private RAbstractStringVector object;
+
+        /** Indicates if the connection is anonymous, i.e., not input object has been provided. */
+        private final boolean anonymous;
 
         private void initTextVec(RStringVector v, TextRConnection textBase) {
-            if (textBase.description.equals("NULL")) {
-                throw RError.nyi(null, "anonymous text output connection");
-            }
-            idName = textBase.object.getDataAt(0);
-            try {
-                textVec = v;
-                textBase.env.put(idName, textVec);
-            } catch (PutException ex) {
-                throw RError.error(RError.SHOW_CALLER2, ex);
+
+            if (anonymous) {
+                object = v;
+            } else {
+                idName = object.getDataAt(0);
+                try {
+                    textVec = v;
+                    textBase.env.put(idName, textVec);
+                } catch (PutException ex) {
+                    throw RError.error(RError.SHOW_CALLER2, ex);
+                }
+                // lock the binding
+                textBase.env.lockBinding(idName);
             }
-            // lock the binding
-            textBase.env.lockBinding(idName);
         }
 
-        protected TextWriteRConnection(BaseRConnection base) {
+        protected TextWriteRConnection(BaseRConnection base, RAbstractStringVector object) {
             super(base);
+            this.object = object;
+            this.anonymous = object == null;
             TextRConnection textBase = (TextRConnection) base;
             initTextVec(RDataFactory.createStringVector(0), textBase);
         }
 
         @Override
-        public OutputStream getOutputStream() throws IOException {
-            return new ConnectionOutputStream();
+        public ByteChannel getChannel() {
+            return ConnectionSupport.newChannel(new ConnectionOutputStream());
         }
 
         @Override
@@ -193,10 +183,17 @@ public class TextConnections {
             if (incompleteLine != null) {
                 appendData(new String[]{incompleteLine});
                 incompleteLine = null;
+                base.setIncomplete(false);
             }
             base.closed = true;
             TextRConnection textBase = (TextRConnection) base;
-            textBase.env.unlockBinding(idName);
+            unlockBinding(textBase);
+        }
+
+        private void unlockBinding(TextRConnection textBase) {
+            if (idName != null) {
+                textBase.env.unlockBinding(idName);
+            }
         }
 
         @Override
@@ -210,8 +207,9 @@ public class TextConnections {
             ArrayList<String> appendedLines = new ArrayList<>();
             while ((nlIndex = result.indexOf('\n', px)) >= 0) {
                 if (incompleteLine != null) {
-                    appendedLines.add(new StringBuffer(incompleteLine).append(result.substring(px, nlIndex)).toString());
+                    appendedLines.add(new StringBuilder(incompleteLine).append(result.substring(px, nlIndex)).toString());
                     incompleteLine = null;
+                    base.setIncomplete(false);
                 } else {
                     appendedLines.add(result.substring(px, nlIndex));
                 }
@@ -222,9 +220,11 @@ public class TextConnections {
             if (incompleteLine != null && !endOfLine) {
                 // end of line not found - accumulate incomplete line
                 incompleteLine = new StringBuffer(incompleteLine).append(result).toString();
+                base.setIncomplete(true);
             } else if (px < result.length()) {
                 // only reset incompleteLine if
                 incompleteLine = result.substring(px);
+                base.setIncomplete(true);
             }
             if (appendedLines.size() > 0) {
                 // update the vector data
@@ -235,7 +235,7 @@ public class TextConnections {
         }
 
         void appendData(String[] appendedData) {
-            String[] existingData = textVec.getDataWithoutCopying();
+            String[] existingData = textVec != null ? textVec.getDataWithoutCopying() : new String[0];
             String[] updateData = appendedData;
             if (existingData.length > 0) {
                 updateData = new String[existingData.length + appendedData.length];
@@ -243,11 +243,7 @@ public class TextConnections {
                 System.arraycopy(appendedData, 0, updateData, existingData.length, appendedData.length);
             }
             TextRConnection textBase = (TextRConnection) base;
-            /*
-             * N.B. This assumes one thread per RContext else another thread could be calling
-             * lockBinding
-             */
-            textBase.env.unlockBinding(idName);
+            unlockBinding(textBase);
             // TODO: is vector really complete?
             initTextVec(RDataFactory.createStringVector(updateData, RDataFactory.COMPLETE_VECTOR), textBase);
         }
@@ -258,6 +254,7 @@ public class TextConnections {
             if (incompleteLine != null) {
                 sb.append(incompleteLine);
                 incompleteLine = null;
+                base.setIncomplete(false);
             }
             for (int i = 0; i < lines.getLength(); i++) {
                 sb.append(lines.getDataAt(i));
@@ -277,17 +274,17 @@ public class TextConnections {
 
         @Override
         public void writeChar(String s, int pad, String eos, boolean useBytes) throws IOException {
-            throw RError.nyi(null, "writeChar on text connection");
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.NOT_ENABLED_FOR_THIS_CONN, "write");
         }
 
         @Override
         public void writeBin(ByteBuffer buffer) throws IOException {
-            throw RError.nyi(null, "writeBin on text connection");
+            throw RError.error(RError.SHOW_CALLER2, RError.Message.ONLY_WRITE_BINARY_CONNECTION);
         }
 
         @Override
-        public String[] getValue() {
-            throw RError.nyi(null, "textConnectionValue");
+        public RAbstractStringVector getValue() {
+            return object;
         }
 
         private class ConnectionOutputStream extends OutputStream {
@@ -315,6 +312,16 @@ public class TextConnections {
                 throw RInternalError.unimplemented();
             }
         }
+
+        @Override
+        public long seek(long offset, SeekMode seekMode, SeekRWMode seekRWMode) throws IOException {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.SEEK_NOT_RELEVANT_FOR_TEXT_CON);
+        }
+
+        @Override
+        public boolean isSeekable() {
+            return false;
+        }
     }
 
     /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/URLConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/URLConnections.java
index 7aad2be8e1da673e7e06da3e01258bc900eccacf..9bd5460030ab1a4fe14e3008922472f7c7ff978d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/URLConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/URLConnections.java
@@ -24,25 +24,21 @@ package com.oracle.truffle.r.runtime.conn;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
 
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ConnectionClass;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.DelegateReadRConnection;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.ReadWriteHelper;
 
 public class URLConnections {
     public static class URLRConnection extends BaseRConnection {
         protected final String urlString;
 
-        public URLRConnection(String url, String modeString) throws IOException {
-            super(ConnectionClass.URL, modeString, AbstractOpenMode.Read);
+        public URLRConnection(String url, String modeString, String encoding) throws IOException {
+            super(ConnectionClass.URL, modeString, AbstractOpenMode.Read, encoding);
             this.urlString = url;
         }
 
@@ -56,6 +52,7 @@ public class URLConnections {
             DelegateRConnection delegate = null;
             switch (getOpenMode().abstractOpenMode) {
                 case Read:
+                case ReadBinary:
                     delegate = new URLReadRConnection(this);
                     break;
                 default:
@@ -65,50 +62,24 @@ public class URLConnections {
         }
     }
 
-    private static class URLReadRConnection extends DelegateReadRConnection implements ReadWriteHelper {
+    private static class URLReadRConnection extends DelegateReadRConnection {
 
-        private final BufferedInputStream inputStream;
+        private final ByteChannel rchannel;
 
         protected URLReadRConnection(URLRConnection base) throws MalformedURLException, IOException {
             super(base);
             URL url = new URL(base.urlString);
-            inputStream = new BufferedInputStream(url.openStream());
+            rchannel = ConnectionSupport.newChannel(new BufferedInputStream(url.openStream()));
         }
 
         @Override
-        public int readBin(ByteBuffer buffer) throws IOException {
-            throw RError.nyi(null, "readBin on URL");
+        public ByteChannel getChannel() {
+            return rchannel;
         }
 
         @Override
-        public byte[] readBinChars() throws IOException {
-            throw RError.nyi(null, "readBinChars on URL");
-        }
-
-        @Override
-        public String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException {
-            return readLinesHelper(inputStream, n, warn, skipNul);
-        }
-
-        @Override
-        public InputStream getInputStream() throws IOException {
-            return inputStream;
-        }
-
-        @Override
-        public void closeAndDestroy() throws IOException {
-            base.closed = true;
-            close();
-        }
-
-        @Override
-        public void close() throws IOException {
-            inputStream.close();
-        }
-
-        @Override
-        public String readChar(int nchars, boolean useBytes) throws IOException {
-            return readCharHelper(nchars, inputStream, useBytes);
+        public boolean isSeekable() {
+            return false;
         }
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 4e3506d9a43fb5af9a7bd5618430986cf82c8824..88bd852e6cc7cb1b6056daaa27433829351ce472 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -21071,6 +21071,19 @@ Levels: c f h k m n p x
 [1000] 1
 Levels: 0 1
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_fifoConnection.testFifoOpenInexisting#
+#capabilities("fifo")
+fifo
+TRUE
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_fifoConnection.testFifoOpenInexisting#Output.IgnoreErrorContext#Output.IgnoreWarningContext#
+#{ zz <- fifo("/tmp/pipe3408688236", "r", blocking = TRUE); close(zz); }
+Error in fifo("/tmp/pipe3408688236", "r", blocking = TRUE) :
+  cannot open the connection
+In addition: Warning message:
+In fifo("/tmp/pipe3408688236", "r", blocking = TRUE) :
+  cannot open fifo '/tmp/pipe3408688236'
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_fileaccess.testfileaccess1#
 #argv <- list(character(0), 0); .Internal(file.access(argv[[1]], argv[[2]]))
 integer(0)
@@ -27878,7 +27891,7 @@ Error in lengths(quote(a)) : 'x' must be a list or atomic vector
 #{ x <- 1 ; levels(x)<-4.5; levels(x);}
 [1] 4.5
 
-##com.oracle.truffle.r.test.builtins.TestBuiltin_levels.testLevels#
+##com.oracle.truffle.r.test.builtins.TestBuiltin_levels.testLevels#Output.MayIgnoreErrorContext#
 #{ x <- 1 ; levels(x)<-NULL; levels(notx)}
 Error in levels(notx) : object 'notx' not found
 
@@ -42177,82 +42190,6 @@ numeric(0)
 #argv <- structure(list(length = 0), .Names = 'length');do.call('raw', argv)
 raw(0)
 
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.runRSourceTests#
-#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_readBin.R") }
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.runRSourceTests#
-#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_readWriteBin.R") }
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.runRSourceTests#
-#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_seek.R") }
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.runRSourceTests#
-#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_writeBin.R") }
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadAppendText#
-#{ rc <- rawConnection(raw(0), "a+"); close(rc); write(charToRaw("A"), rc) }
-Error in cat(x, file = file, sep = c(rep.int(sep, ncolumns - 1), "\n"),  :
-  invalid connection
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadAppendText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); write(charToRaw(", World"), rc); res <- rawConnectionValue(rc); close(rc); res }
- [1] 48 65 6c 6c 6f 32 63 20 32 30 20 35 37 20 36 66 20 37 32 0a 36 63 20 36 34
-[26] 0a
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadAppendText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); write(charToRaw(", World"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
-[1] "Hello2c 20 57 6f 72\n6c 64\n"
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadAppendText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
- [1] 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 00
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadAppendText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); writeChar(", World", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
-[1] "Hello, World"
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadWriteText#
-#{ rc <- rawConnection(raw(0), "r+"); close(rc); write(charToRaw("A"), rc) }
-Error in cat(x, file = file, sep = c(rep.int(sep, ncolumns - 1), "\n"),  :
-  invalid connection
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadWriteText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); write(charToRaw(", World"), rc); res <- rawConnectionValue(rc); close(rc); res }
- [1] 32 63 20 32 30 20 35 37 20 36 66 20 37 32 0a 36 63 20 36 34 0a
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadWriteText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); write(charToRaw(", World"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
-[1] "2c 20 57 6f 72\n6c 64\n"
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadWriteText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
-[1] 2c 20 57 6f 72 6c 64 00
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testReadWriteText#
-#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); writeChar(", World", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
-[1] ", World"
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testWriteBinary#Ignored.Unknown#
-#{ s <- "äöüß"; rc <- rawConnection(raw(0), "wb"); write(charToRaw(s), rc); res <- rawConnectionValue(rc); close(rc); res }
- [1] 63 33 20 61 34 20 63 33 20 62 36 20 63 33 0a 62 63 20 63 33 20 39 66 0a
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testWriteBinary#
-#{ zz <- rawConnection(raw(0), "wb"); x <- c("a", "this will be truncated", "abc"); nc <- c(3, 10, 3); writeChar(x, zz, nc, eos = NULL); writeChar(x, zz, eos = "\r\n"); res <- rawConnectionValue(zz); close(zz); res }
- [1] 61 00 00 74 68 69 73 20 77 69 6c 6c 20 61 62 63 61 0d 0a 00 74 68 69 73 20
-[26] 77 69 6c 6c 20 62 65 20 74 72 75 6e 63 61 74 65 64 0d 0a 00 61 62 63 0d 0a
-[51] 00
-Warning message:
-In writeChar(x, zz, nc, eos = NULL) :
-  writeChar: more characters requested than are in the string - will zero-pad
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testWriteText#
-#{ rc <- rawConnection(raw(0), "w"); writeChar("Hello", rc); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
- [1] 48 65 6c 6c 6f 00 2c 20 57 6f 72 6c 64 00
-
-##com.oracle.truffle.r.test.builtins.TestBuiltin_rawConnection.testWriteText#
-#{ s <- "äöüß"; rc <- rawConnection(raw(0), "w"); writeChar(s, rc); rawConnectionValue(rc) }
-[1] c3 a4 c3 b6 c3 bc c3 9f 00
-
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_rawShift.testrawShift1#
 #argv <- structure(list(x = as.raw(c(0, 1, 32, 127, 128, 255, 123)), n = -1.1), .Names = c('x', 'n'));do.call('rawShift', argv)
 [1] 00 00 10 3f 40 7f 3d
@@ -73462,6 +73399,98 @@ NULL
 #withRestarts({cat("<start>");invokeRestart("foo", 123L, 456L);789L},<<<NEWLINE>>> foo=list(description="my handler", handler=function(a,b) c(a,b)))
 <start>[1] 123 456
 
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#Ignored.Unknown#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/fifo_GnuR_example.R") }
+[1] "abc"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_readBin.R") }
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_readWriteBin.R") }
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_seek.R") }
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/rawConnection_writeBin.R") }
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/readLines_GnuR_example.R") }
+[1] "TITLE extra line" "2 3 5 7"          ""                 "11 13 17"
+[5] "123"              "abc"              "123"              "abc def"
+Warning message:
+In readLines("test1") : incomplete final line found on 'test1'
+
+##com.oracle.truffle.r.test.library.base.TestConnections.runRSourceTests#
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/builtins/connection/R/textConnection.R") }
+Read 4 items
+Read 4 items
+
+Summary of Residuals:
+
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testEncoding#
+#fin <- file('', "w+", encoding = "___inexistingCharSet___")
+Error in file("", "w+", encoding = "___inexistingCharSet___") :
+  unsupported conversion from '___inexistingCharSet___' to ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testEncoding#
+#{ wline <- 'Hellö'; fin <- file('', 'w+', encoding = 'UTF-8'); writeLines(wline, fin); seek(fin, 0); rline <- readLines(fin, 1); close(fin); c(wline, rline, wline == rline) }
+[1] "Hellö" "Hellö" "TRUE"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testFileOpenRaw#
+#{ zz <- file("gzipped_____5137528280012599068___.gz", "r", raw=T); res <- readBin(zz, raw(), 4); close(zz); res }
+Error in readBin(zz, raw(), 4) : can only read from a binary connection
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testFileSummary#
+#zz <- file('', 'w+'); summary(zz); close(zz)
+$description
+[1] ""
+
+$class
+[1] "file"
+
+$mode
+[1] "w+"
+
+$text
+[1] "text"
+
+$opened
+[1] "opened"
+
+$`can read`
+[1] "yes"
+
+$`can write`
+[1] "yes"
+
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testFileSummary#
+#{ zz <- file("gzipped_____5137528280012599068___.gz", "r"); res <- summary(zz); close(zz); res }
+$description
+[1] "gzipped_____5137528280012599068___.gz"
+
+$class
+[1] "gzfile"
+
+$mode
+[1] "r"
+
+$text
+[1] "text"
+
+$opened
+[1] "opened"
+
+$`can read`
+[1] "yes"
+
+$`can write`
+[1] "no"
+
+
 ##com.oracle.truffle.r.test.library.base.TestConnections.testFileWriteReadBin#
 #{ readBin(file("tmptest/com.oracle.truffle.r.test.library.base.conn/wb1", "rb"), 3) }
 numeric(0)
@@ -73566,6 +73595,255 @@ numeric(0)
 #{ con<-textConnection(c("a","b","c","d")); pushBackLength(con) }
 [1] 0
 
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadAppendText#
+#{ rc <- rawConnection(raw(0), "a+"); close(rc); write(charToRaw("A"), rc) }
+Error in cat(x, file = file, sep = c(rep.int(sep, ncolumns - 1), "\n"),  :
+  invalid connection
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadAppendText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); write(charToRaw(", World"), rc); res <- rawConnectionValue(rc); close(rc); res }
+ [1] 48 65 6c 6c 6f 32 63 20 32 30 20 35 37 20 36 66 20 37 32 0a 36 63 20 36 34
+[26] 0a
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadAppendText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); write(charToRaw(", World"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
+[1] "Hello2c 20 57 6f 72\n6c 64\n"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadAppendText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
+ [1] 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 00
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadAppendText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "a+"); writeChar(", World", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
+[1] "Hello, World"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadWriteText#
+#{ rc <- rawConnection(raw(0), "r+"); close(rc); write(charToRaw("A"), rc) }
+Error in cat(x, file = file, sep = c(rep.int(sep, ncolumns - 1), "\n"),  :
+  invalid connection
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadWriteText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); write(charToRaw(", World"), rc); res <- rawConnectionValue(rc); close(rc); res }
+ [1] 32 63 20 32 30 20 35 37 20 36 66 20 37 32 0a 36 63 20 36 34 0a
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadWriteText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); write(charToRaw(", World"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
+[1] "2c 20 57 6f 72\n6c 64\n"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadWriteText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
+[1] 2c 20 57 6f 72 6c 64 00
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawReadWriteText#
+#{ rv <- charToRaw("Hello"); rc <- rawConnection(rv, "r+"); writeChar(", World", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }
+[1] ", World"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawWriteBinary#Ignored.Unknown#
+#{ s <- "äöüß"; rc <- rawConnection(raw(0), "wb"); write(charToRaw(s), rc); res <- rawConnectionValue(rc); close(rc); res }
+ [1] 63 33 20 61 34 20 63 33 20 62 36 20 63 33 0a 62 63 20 63 33 20 39 66 0a
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawWriteBinary#
+#{ zz <- rawConnection(raw(0), "wb"); x <- c("a", "this will be truncated", "abc"); nc <- c(3, 10, 3); writeChar(x, zz, nc, eos = NULL); writeChar(x, zz, eos = "\r\n"); res <- rawConnectionValue(zz); close(zz); res }
+ [1] 61 00 00 74 68 69 73 20 77 69 6c 6c 20 61 62 63 61 0d 0a 00 74 68 69 73 20
+[26] 77 69 6c 6c 20 62 65 20 74 72 75 6e 63 61 74 65 64 0d 0a 00 61 62 63 0d 0a
+[51] 00
+Warning message:
+In writeChar(x, zz, nc, eos = NULL) :
+  writeChar: more characters requested than are in the string - will zero-pad
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawWriteText#
+#{ rc <- rawConnection(raw(0), "w"); writeChar("Hello", rc); writeChar(", World", rc); res <- rawConnectionValue(rc); close(rc); res }
+ [1] 48 65 6c 6c 6f 00 2c 20 57 6f 72 6c 64 00
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testRawWriteText#
+#{ s <- "äöüß"; rc <- rawConnection(raw(0), "w"); writeChar(s, rc); rawConnectionValue(rc) }
+[1] c3 a4 c3 b6 c3 bc c3 9f 00
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd"
+Warning messages:
+1: In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+2: In readLines(zz, 2, warn = T, skipNul = F) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = T) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+Warning messages:
+1: In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+2: In readLines(zz, 2, warn = T, skipNul = F) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = T) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=F); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd"
+Warning messages:
+1: In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+2: In readLines(zz, 2, warn = T, skipNul = F) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = T) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+Warning messages:
+1: In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+2: In readLines(zz, 2, warn = T, skipNul = F) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = T) :
+  incomplete final line found on ''
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=F, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=F); close(zz); res }
+[1] "abcd" "ABC"
+Warning message:
+In readLines(zz, 2, warn = T, skipNul = F) :
+  line 1 appears to contain an embedded nul
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testReadLines#Output.MayIgnoreWarningContext#
+#{ zz <- file('',"w+b", blocking=T); writeBin(as.raw(c(97,98,99,100,0,101,10,65,66,67,10)), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=T, skipNul=T); close(zz); res }
+[1] "abcde" "ABC"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testSeekTextConnection#
+#{ zz <- textConnection("Hello, World!"); res <- isSeekable(zz); close(zz); res }
+[1] FALSE
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testSeekTextConnection#Output.IgnoreErrorMessage#
+#{ zz <- textConnection("Hello, World!"); res <- seek(zz, 5); close(zz); res }
+Error in seek.connection(zz, 5) :
+  seek is not relevant for text connection
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testTextReadConnection#Output.IgnoreErrorContext#
+#textConnection(NULL, 'r')
+Error in textConnection(NULL, "r") : invalid 'text' argument
+
 ##com.oracle.truffle.r.test.library.base.TestConnections.testTextReadConnection#
 #{ con <- textConnection(c("1", "2", "3","4")); readLines(con) }
 [1] "1" "2" "3" "4"
@@ -73582,25 +73860,29 @@ numeric(0)
 #{ con <- textConnection(c("1", "2", "3","4")); readLines(con, 2); readLines(con, 2); readLines(con, 2) }
 character(0)
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#Ignored.Unimplemented#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #c <- textConnection('out', 'w'); cat('testtext', file=c); isIncomplete(c); cat('testtext2\n', file=c); isIncomplete(c); close(c); out
 [1] TRUE
 [1] FALSE
 [1] "testtexttesttext2"
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
+#{ c <- textConnection(NULL, 'w'); cat('testtext\n', file=c); textConnectionValue(c) }
+[1] "testtext"
+
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #{ con <- textConnection("tcval", open="w"); writeLines("a", con); tcval; close(con) }
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #{ con <- textConnection("tcval", open="w"); writeLines("a", con); writeLines(c("a", "b"), con, sep="."); tcval; close(con) }
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #{ con <- textConnection("tcval", open="w"); writeLines("a", con); writeLines(c("a", "b"), con, sep="."); writeLines("", con); tcval; close(con) }
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #{ con <- textConnection("tcval", open="w"); writeLines("a\nb", con); tcval; close(con) }
 
-##com.oracle.truffle.r.test.library.base.TestConnections.testWriteConnection#
+##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection#
 #{ d<-data.frame(c(1,2), c(10, 20)); buf<-character(); c<-textConnection("buf", open="w", local=T); write.table(d, c); buf }
 [1] "\"c.1..2.\" \"c.10..20.\"" "\"1\" 1 10"
 [3] "\"2\" 2 20"
@@ -79641,7 +79923,7 @@ $class
 #{ x<-as.raw(c(7L,42L)); y<-as.data.frame(x, row.names=NULL, nm="x"); is.data.frame(y); }
 [1] TRUE
 
-##com.oracle.truffle.r.test.library.base.TestSimpleDataFrames.testAsDataFrame#
+##com.oracle.truffle.r.test.library.base.TestSimpleDataFrames.testAsDataFrame#Output.MayIgnoreWarningContext#
 #{ x<-c(7L,42L); y<-as.data.frame(x, row.names="r1", nm="x"); attributes(y); }
 $names
 [1] "x"
@@ -79684,7 +79966,7 @@ $class
 [1] "data.frame"
 
 
-##com.oracle.truffle.r.test.library.base.TestSimpleDataFrames.testAsDataFrame#
+##com.oracle.truffle.r.test.library.base.TestSimpleDataFrames.testAsDataFrame#Output.MayIgnoreWarningContext#
 #{ x<-c(7L,42L); y<-as.data.frame(x, row.names=c("r1", "r2", "r3"), nm="x"); attributes(y); }
 $names
 [1] "x"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_fifoConnection.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_fifoConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..106799d228cb04e6b3e3268de5ce42d7ff5470fd
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_fifoConnection.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.builtins;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+// Checkstyle: stop line length check
+public class TestBuiltin_fifoConnection extends TestBase {
+
+    private static List<Path> TEMP_FIFOS = new ArrayList<>();
+
+    @BeforeClass
+    public static void setup() {
+        Path path = Paths.get(System.getProperty("java.io.tmpdir"));
+        TEMP_FIFOS.add(path.resolve("pipe3408688236"));
+        TEMP_FIFOS.add(path.resolve("pipe4039819292"));
+    }
+
+    @Test
+    public void testFifoOpenInexisting() {
+        assertEval("capabilities(\"fifo\")");
+
+        Assert.assertFalse(Files.exists(TEMP_FIFOS.get(0)));
+        assertEval(Output.IgnoreErrorContext, Output.IgnoreWarningContext, "{ zz <- fifo(\"" + TEMP_FIFOS.get(0) + "\", \"r\", blocking = TRUE); close(zz); }");
+    }
+
+    @Test(timeout = 100)
+    @Ignore
+    public void testFifoOpenNonBlocking() {
+        Assert.assertFalse(Files.exists(TEMP_FIFOS.get(0)));
+        assertEval(Ignored.ImplementationError, "{ zz <- fifo(\"" + TEMP_FIFOS.get(0) + "\", \"r\"); close(zz); }");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        for (Path p : TEMP_FIFOS) {
+            try {
+                Files.delete(p);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rawConnection.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rawConnection.java
deleted file mode 100644
index 33ae519955fced53b0f171c9716690dbf56c6d30..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rawConnection.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.test.builtins;
-
-import org.junit.Test;
-
-import com.oracle.truffle.r.test.TestRBase;
-
-// Checkstyle: stop line length check
-public class TestBuiltin_rawConnection extends TestRBase {
-
-    @Override
-    protected String getTestDir() {
-        return "builtins/connection";
-    }
-
-    @Test
-    public void testReadAppendText() {
-
-        assertEval("{ rc <- rawConnection(raw(0), \"a+\"); close(rc); write(charToRaw(\"A\"), rc) }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); writeChar(\", World\", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); write(charToRaw(\", World\"), rc); res <- rawConnectionValue(rc); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); write(charToRaw(\", World\"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
-    }
-
-    @Test
-    public void testReadWriteText() {
-
-        assertEval("{ rc <- rawConnection(raw(0), \"r+\"); close(rc); write(charToRaw(\"A\"), rc) }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); writeChar(\", World\", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); write(charToRaw(\", World\"), rc); res <- rawConnectionValue(rc); close(rc); res }");
-        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); write(charToRaw(\", World\"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
-    }
-
-    @Test
-    public void testWriteText() {
-
-        assertEval("{ s <- \"äöüß\"; rc <- rawConnection(raw(0), \"w\"); writeChar(s, rc); rawConnectionValue(rc) }");
-        assertEval("{ rc <- rawConnection(raw(0), \"w\"); writeChar(\"Hello\", rc); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
-    }
-
-    @Test
-    public void testWriteBinary() {
-        // this test is currently ignored, since 'charToRaw' is not compliant
-        assertEval(Ignored.Unknown, "{ s <- \"äöüß\"; rc <- rawConnection(raw(0), \"wb\"); write(charToRaw(s), rc); res <- rawConnectionValue(rc); close(rc); res }");
-        assertEval("{ zz <- rawConnection(raw(0), \"wb\"); x <- c(\"a\", \"this will be truncated\", \"abc\"); nc <- c(3, 10, 3); writeChar(x, zz, nc, eos = NULL); writeChar(x, zz, eos = \"\\r\\n\"); res <- rawConnectionValue(zz); close(zz); res }");
-    }
-
-}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/fifo_GnuR_example.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/fifo_GnuR_example.R
new file mode 100644
index 0000000000000000000000000000000000000000..9ffda2392b2cf9143bc149bb054bcbdf1e8a3878
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/fifo_GnuR_example.R
@@ -0,0 +1,9 @@
+# Ignored
+# This test does currently not work on Java because there is simply no way in Java for opening a UNIX named pipe non-blocking.
+if(capabilities("fifo")) {
+  zz <- fifo("foo-fifo", "w+")
+  writeLines("abc", zz)
+  print(readLines(zz))
+  close(zz)
+  unlink("foo-fifo")
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/readLines_GnuR_example.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/readLines_GnuR_example.R
new file mode 100644
index 0000000000000000000000000000000000000000..4c6ed7c53290a25b6b54c7ed191e8b416e5c466d
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/readLines_GnuR_example.R
@@ -0,0 +1,12 @@
+cat("TITLE extra line", "2 3 5 7", "", "11 13 17", file = "ex.data", sep = "\n")
+r0 <- readLines("ex.data", n = -1)
+unlink("ex.data")
+cat("123\nabc", file = "test1")
+r1 <- readLines("test1")
+con <- file("test1", "r", blocking = FALSE)
+r2 <- readLines(con)
+cat(" def\n", file = "test1", append = TRUE)
+r3 <- readLines(con)
+close(con)
+unlink("test1")
+print(c(r0, r1, r2, r3))
\ No newline at end of file
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/textConnection.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/textConnection.R
new file mode 100644
index 0000000000000000000000000000000000000000..7785051d50e512c1053436f50003e228a7d7fb66
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/connection/R/textConnection.R
@@ -0,0 +1,30 @@
+zz <- textConnection(LETTERS)
+readLines(zz, 2)
+scan(zz, "", 4)
+pushBack(c("aa", "bb"), zz)
+scan(zz, "", 4)
+close(zz)
+
+zz <- textConnection("foo", "w")
+writeLines(c("testit1", "testit2"), zz)
+cat("testit3 ", file = zz)
+isIncomplete(zz)
+cat("testit4\n", file = zz)
+# removed this call since we haven't implemented it yet
+# isIncomplete(zz)
+close(zz)
+foo
+
+# capture R output: use part of example from help(lm)
+zz <- textConnection("foo", "w")
+ctl <- c(4.17, 5.58, 5.18, 6.11, 4.5, 4.61, 5.17, 4.53, 5.33, 5.14)
+trt <- c(4.81, 4.17, 4.41, 3.59, 5.87, 3.83, 6.03, 4.89, 4.32, 4.69)
+group <- gl(2, 10, 20, labels = c("Ctl", "Trt"))
+weight <- c(ctl, trt)
+sink(zz)
+anova(lm.D9 <- lm(weight ~ group))
+cat("\nSummary of Residuals:\n\n")
+summary(resid(lm.D9))
+sink()
+close(zz)
+cat(foo, sep = "\n")
\ No newline at end of file
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java
index b67b54e5a1ce70a0ad76d64d77ea2448218bac18..c4cfaa4a5700465d619e287b2d57966903c8d037 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java
@@ -22,15 +22,24 @@
  */
 package com.oracle.truffle.r.test.library.base;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.zip.GZIPOutputStream;
 
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.oracle.truffle.r.test.TestBase;
+import com.oracle.truffle.r.test.TestRBase;
 
-public class TestConnections extends TestBase {
+// Checkstyle: stop line length check
+public class TestConnections extends TestRBase {
     private static final class TestDir {
         private final Path testDirPath;
 
@@ -44,17 +53,30 @@ public class TestConnections extends TestBase {
     }
 
     private static TestDir testDir;
+    private static Path tempFileGzip;
+
+    @Override
+    protected String getTestDir() {
+        return "builtins/connection";
+    }
 
     @BeforeClass
-    public static void setupTestDir() {
+    public static void setup() throws IOException {
         testDir = new TestDir();
+
+        // create a gzipped file
+        tempFileGzip = Paths.get("gzipped_____5137528280012599068___.gz");
+        OutputStream gzos = new GZIPOutputStream(Files.newOutputStream(tempFileGzip, StandardOpenOption.WRITE, StandardOpenOption.CREATE));
+        gzos.write("Hello, World!".getBytes());
+        gzos.close();
     }
 
     @AfterClass
-    public static void teardownTestDir() {
+    public static void teardown() {
         if (!deleteDir(testDir.testDirPath)) {
             System.err.println("WARNING: error deleting : " + testDir.testDirPath);
         }
+        deleteDir(tempFileGzip);
     }
 
     @Test
@@ -84,6 +106,8 @@ public class TestConnections extends TestBase {
 
     @Test
     public void testTextReadConnection() {
+        assertEval(Output.IgnoreErrorContext, "textConnection(NULL, 'r')");
+
         assertEval("{ con <- textConnection(c(\"1\", \"2\", \"3\",\"4\")); readLines(con) }");
         assertEval("{ con <- textConnection(c(\"1\", \"2\", \"3\",\"4\")); readLines(con, 2) }");
         assertEval("{ con <- textConnection(c(\"1\", \"2\", \"3\",\"4\")); readLines(con, 2); readLines(con, 2) }");
@@ -114,13 +138,105 @@ public class TestConnections extends TestBase {
     }
 
     @Test
-    public void testWriteConnection() {
+    public void testWriteTextConnection() {
         assertEval("{ con <- textConnection(\"tcval\", open=\"w\"); writeLines(\"a\", con); tcval; close(con) }");
         assertEval("{ con <- textConnection(\"tcval\", open=\"w\"); writeLines(\"a\", con); writeLines(c(\"a\", \"b\"), con, sep=\".\"); tcval; close(con) }");
         assertEval("{ con <- textConnection(\"tcval\", open=\"w\"); writeLines(\"a\", con); writeLines(c(\"a\", \"b\"), con, sep=\".\"); writeLines(\"\", con); tcval; close(con) }");
         assertEval("{ con <- textConnection(\"tcval\", open=\"w\"); writeLines(\"a\\nb\", con); tcval; close(con) }");
-        assertEval(Ignored.Unimplemented, "c <- textConnection('out', 'w'); cat('testtext', file=c); isIncomplete(c); cat('testtext2\\n', file=c); isIncomplete(c); close(c); out");
+        assertEval("c <- textConnection('out', 'w'); cat('testtext', file=c); isIncomplete(c); cat('testtext2\\n', file=c); isIncomplete(c); close(c); out");
+
+        // anonymous connection
+        assertEval("{ c <- textConnection(NULL, 'w'); cat('testtext\\n', file=c); textConnectionValue(c) }");
 
         assertEval("{ d<-data.frame(c(1,2), c(10, 20)); buf<-character(); c<-textConnection(\"buf\", open=\"w\", local=T); write.table(d, c); buf }");
     }
+
+    @Test
+    public void testSeekTextConnection() {
+        assertEval("{ zz <- textConnection(\"Hello, World!\"); res <- isSeekable(zz); close(zz); res }");
+        assertEval(Output.IgnoreErrorMessage, "{ zz <- textConnection(\"Hello, World!\"); res <- seek(zz, 5); close(zz); res }");
+    }
+
+    @Test
+    public void testFileSummary() {
+        Assert.assertTrue("Could not create required temp file for test.", Files.exists(tempFileGzip));
+        assertEval("{ zz <- file(\"" + tempFileGzip + "\", \"r\"); res <- summary(zz); close(zz); res }");
+
+        assertEval("zz <- file('', 'w+'); summary(zz); close(zz)");
+    }
+
+    @Test
+    public void testFileOpenRaw() {
+        Assert.assertTrue("Could not create required temp file for test.", Files.exists(tempFileGzip));
+        assertEval("{ zz <- file(\"" + tempFileGzip + "\", \"r\", raw=T); res <- readBin(zz, raw(), 4); close(zz); res }");
+    }
+
+    @Test
+    public void testEncoding() {
+        // use inexisting charset
+        assertEval("fin <- file('', \"w+\", encoding = \"___inexistingCharSet___\")");
+
+        // write UTF-8 file
+        assertEval("{ wline <- 'Hellö'; fin <- file('', 'w+', encoding = 'UTF-8'); writeLines(wline, fin); seek(fin, 0); rline <- readLines(fin, 1); close(fin); c(wline, rline, wline == rline) }");
+    }
+
+    @Test
+    public void testReadLines() {
+        // one line containing '\0'
+        final String lineWithNul = "c(97,98,99,100,0,101,10)";
+
+        // two lines, first containing '\0'
+        final String twoLinesOneNul = "c(97,98,99,100,0,101,10,65,66,67,10)";
+
+        // one line containing '\0' and imcomplete
+        final String lineWithNulIncomp = "c(97,98,99,100,0,101)";
+
+        // two lines, first containing '\0', second line incomplete
+        final String twoLinesOneNulIncomp = "c(97,98,99,100,0,101,10,65,66,67)";
+
+        assertEval(Output.MayIgnoreWarningContext, TestBase.template(
+                        "{ zz <- file('',\"w+b\", blocking=%0); writeBin(as.raw(%1), zz, useBytes=T); seek(zz, 0); res <- readLines(zz, 2, warn=%2, skipNul=%3); close(zz); res }",
+                        LVAL, arr(lineWithNul, twoLinesOneNul, lineWithNulIncomp, twoLinesOneNulIncomp), LVAL, LVAL));
+    }
+
+    @Test
+    public void testRawReadAppendText() {
+
+        assertEval("{ rc <- rawConnection(raw(0), \"a+\"); close(rc); write(charToRaw(\"A\"), rc) }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); writeChar(\", World\", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); write(charToRaw(\", World\"), rc); res <- rawConnectionValue(rc); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"a+\"); write(charToRaw(\", World\"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
+    }
+
+    @Test
+    public void testRawReadWriteText() {
+
+        assertEval("{ rc <- rawConnection(raw(0), \"r+\"); close(rc); write(charToRaw(\"A\"), rc) }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); writeChar(\", World\", rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); write(charToRaw(\", World\"), rc); res <- rawConnectionValue(rc); close(rc); res }");
+        assertEval("{ rv <- charToRaw(\"Hello\"); rc <- rawConnection(rv, \"r+\"); write(charToRaw(\", World\"), rc); res <- rawToChar(rawConnectionValue(rc)); close(rc); res }");
+    }
+
+    @Test
+    public void testRawWriteText() {
+
+        assertEval("{ s <- \"äöüß\"; rc <- rawConnection(raw(0), \"w\"); writeChar(s, rc); rawConnectionValue(rc) }");
+        assertEval("{ rc <- rawConnection(raw(0), \"w\"); writeChar(\"Hello\", rc); writeChar(\", World\", rc); res <- rawConnectionValue(rc); close(rc); res }");
+    }
+
+    @Test
+    public void testRawWriteBinary() {
+
+        // this test is currently ignored, since 'charToRaw' is not compliant
+        assertEval(Ignored.Unknown, "{ s <- \"äöüß\"; rc <- rawConnection(raw(0), \"wb\"); write(charToRaw(s), rc); res <- rawConnectionValue(rc); close(rc); res }");
+        assertEval("{ zz <- rawConnection(raw(0), \"wb\"); x <- c(\"a\", \"this will be truncated\", \"abc\"); nc <- c(3, 10, 3); writeChar(x, zz, nc, eos = NULL); writeChar(x, zz, eos = \"\\r\\n\"); res <- rawConnectionValue(zz); close(zz); res }");
+    }
+
+    private static final String[] LVAL = arr("T", "F");
+
+    private static String[] arr(String... args) {
+        return args;
+    }
 }