From f7134d7f1a854771e0255a293d9d22bea8210b5b Mon Sep 17 00:00:00 2001 From: Mick Jordan <mick.jordan@oracle.com> Date: Fri, 14 Aug 2015 08:59:52 -0700 Subject: [PATCH] implement recursive option of file.copy --- .../r/nodes/builtin/base/FileFunctions.java | 66 +++++++++++++++++-- .../com/oracle/truffle/r/runtime/RError.java | 1 + 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java index bafde071e8..8f50865fc7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java @@ -83,7 +83,7 @@ public class FileFunctions { * There are two simple (non-trivial) cases and one tricky 1. 1. Append one or more * files to a single file (len1 == 1, len2 >= 1) 2. Append one file to one file for * several files (len1 == len2) - * + * * The tricky case is when len1 > 1 && len2 > len1. E.g. f1,f2 <- g1,g2,g3 In this case, * this is really f1,f2,f1 <- g1,g2,g3 */ @@ -221,7 +221,7 @@ public class FileFunctions { * the information. The R closure that called the .Internal turns the result into a * dataframe and sets the row.names attributes to the paths in vec. It also updates the * mtime, ctime, atime fields using .POSIXct. - * + * * We try to use the JDK classes, even though they provide a more abstract interface * than R. In particular there seems to be no way to get the uid/gid values. We might be * better off justing using a native call. @@ -818,9 +818,6 @@ public class FileFunctions { boolean copyMode = checkLogical(copyModeArg, "copy.mode"); boolean copyDate = checkLogical(copyDateArg, "copy.dates"); - if (recursive) { - throw RError.nyi(this, "'recursive' option"); - } // Java cannot distinguish copy.mode and copy.dates CopyOption[] copyOptions; if (copyMode || copyDate) { @@ -842,6 +839,12 @@ public class FileFunctions { toDir = vecTo0Path; } } + if (recursive) { + if (toDir == null) { + RError.warning(this, RError.Message.FILE_COPY_RECURSIVE_IGNORED); + recursive = false; + } + } for (int i = 0; i < lenFrom; i++) { String from = vecFrom.getDataAt(i % lenFrom); @@ -854,8 +857,16 @@ public class FileFunctions { Path toPath = fileSystem.getPath(Utils.tildeExpand(to)); status[i] = RRuntime.LOGICAL_TRUE; try { - if (!Files.exists(toPath) || overWrite) { - Files.copy(fromPath, toPath, copyOptions); + if (recursive && Files.isDirectory(fromPath)) { + // to is just one dir (checked above) + boolean copyError = copyDir(fromPath, toPath, copyOptions); + if (copyError) { + status[i] = RRuntime.LOGICAL_FALSE; + } + } else { + if (!Files.exists(toPath) || overWrite) { + Files.copy(fromPath, toPath, copyOptions); + } } } catch (UnsupportedOperationException | IOException ex) { status[i] = RRuntime.LOGICAL_FALSE; @@ -865,6 +876,47 @@ public class FileFunctions { } return RDataFactory.createLogicalVector(status, RDataFactory.COMPLETE_VECTOR); } + + private static final class DirCopy extends SimpleFileVisitor<Path> { + private final Path fromDir; + private final Path toDir; + private final CopyOption[] copyOptions; + private boolean error; + + private DirCopy(Path fromDir, Path toDir, CopyOption[] copyOptions) { + this.fromDir = fromDir; + this.toDir = toDir; + this.copyOptions = copyOptions; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + Path newDir = toDir.resolve(fromDir.relativize(fromDir)); + try { + Files.copy(dir, newDir, copyOptions); + } catch (FileAlreadyExistsException x) { + // ok + } catch (IOException ex) { + error = true; + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path newFile = toDir.resolve(fromDir.relativize(file)); + Files.copy(file, newFile, copyOptions); + return FileVisitResult.CONTINUE; + } + } + + @SuppressWarnings("static-method") + private boolean copyDir(Path fromDir, Path toDir, CopyOption[] copyOptions) throws IOException { + DirCopy dirCopy = new DirCopy(fromDir, toDir, copyOptions); + Files.walkFileTree(fromDir, dirCopy); + return dirCopy.error; + } } abstract static class XyzNameAdapter extends RBuiltinNode { 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 cc152a4447..849d957b7c 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 @@ -583,6 +583,7 @@ public final class RError extends RuntimeException { FILE_CANNOT_COPY(" cannot link '%s' to '%s', reason %s"), FILE_CANNOT_REMOVE(" cannot remove file '%s'"), FILE_CANNOT_RENAME(" cannot rename file '%s' to '%s'"), + FILE_COPY_RECURSIVE_IGNORED("'recursive' will be ignored as 'to' is not a single existing directory"), DIR_CANNOT_CREATE("cannot create dir '%s'"), IMPOSSIBLE_SUBSTITUTE("substitute result cannot be represented"), PACKAGE_AVAILABLE("'%s' may not be available when loading"), -- GitLab