• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   - Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  *
11  *   - Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  *   - Neither the name of Oracle nor the names of its
16  *     contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
41 import java.nio.file.*;
42 import static java.nio.file.StandardCopyOption.*;
43 import java.nio.file.attribute.*;
44 import static java.nio.file.FileVisitResult.*;
45 import java.io.IOException;
46 import java.util.*;
47 
48 /**
49  * Sample code that copies files in a similar manner to the cp(1) program.
50  */
51 
52 public class Copy {
53 
54     /**
55      * Returns {@code true} if okay to overwrite a  file ("cp -i")
56      */
okayToOverwrite(Path file)57     static boolean okayToOverwrite(Path file) {
58         String answer = System.console().readLine("overwrite %s (yes/no)? ", file);
59         return (answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes"));
60     }
61 
62     /**
63      * Copy source file to target location. If {@code prompt} is true then
64      * prompt user to overwrite target if it exists. The {@code preserve}
65      * parameter determines if file attributes should be copied/preserved.
66      */
copyFile(Path source, Path target, boolean prompt, boolean preserve)67     static void copyFile(Path source, Path target, boolean prompt, boolean preserve) {
68         CopyOption[] options = (preserve) ?
69             new CopyOption[] { COPY_ATTRIBUTES, REPLACE_EXISTING } :
70             new CopyOption[] { REPLACE_EXISTING };
71         if (!prompt || Files.notExists(target) || okayToOverwrite(target)) {
72             try {
73                 Files.copy(source, target, options);
74             } catch (IOException x) {
75                 System.err.format("Unable to copy: %s: %s%n", source, x);
76             }
77         }
78     }
79 
80     /**
81      * A {@code FileVisitor} that copies a file-tree ("cp -r")
82      */
83     static class TreeCopier implements FileVisitor<Path> {
84         private final Path source;
85         private final Path target;
86         private final boolean prompt;
87         private final boolean preserve;
88 
TreeCopier(Path source, Path target, boolean prompt, boolean preserve)89         TreeCopier(Path source, Path target, boolean prompt, boolean preserve) {
90             this.source = source;
91             this.target = target;
92             this.prompt = prompt;
93             this.preserve = preserve;
94         }
95 
96         @Override
preVisitDirectory(Path dir, BasicFileAttributes attrs)97         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
98             // before visiting entries in a directory we copy the directory
99             // (okay if directory already exists).
100             CopyOption[] options = (preserve) ?
101                 new CopyOption[] { COPY_ATTRIBUTES } : new CopyOption[0];
102 
103             Path newdir = target.resolve(source.relativize(dir));
104             try {
105                 Files.copy(dir, newdir, options);
106             } catch (FileAlreadyExistsException x) {
107                 // ignore
108             } catch (IOException x) {
109                 System.err.format("Unable to create: %s: %s%n", newdir, x);
110                 return SKIP_SUBTREE;
111             }
112             return CONTINUE;
113         }
114 
115         @Override
visitFile(Path file, BasicFileAttributes attrs)116         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
117             copyFile(file, target.resolve(source.relativize(file)),
118                      prompt, preserve);
119             return CONTINUE;
120         }
121 
122         @Override
postVisitDirectory(Path dir, IOException exc)123         public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
124             // fix up modification time of directory when done
125             if (exc == null && preserve) {
126                 Path newdir = target.resolve(source.relativize(dir));
127                 try {
128                     FileTime time = Files.getLastModifiedTime(dir);
129                     Files.setLastModifiedTime(newdir, time);
130                 } catch (IOException x) {
131                     System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
132                 }
133             }
134             return CONTINUE;
135         }
136 
137         @Override
visitFileFailed(Path file, IOException exc)138         public FileVisitResult visitFileFailed(Path file, IOException exc) {
139             if (exc instanceof FileSystemLoopException) {
140                 System.err.println("cycle detected: " + file);
141             } else {
142                 System.err.format("Unable to copy: %s: %s%n", file, exc);
143             }
144             return CONTINUE;
145         }
146     }
147 
usage()148     static void usage() {
149         System.err.println("java Copy [-ip] source... target");
150         System.err.println("java Copy -r [-ip] source-dir... target");
151         System.exit(-1);
152     }
153 
main(String[] args)154     public static void main(String[] args) throws IOException {
155         boolean recursive = false;
156         boolean prompt = false;
157         boolean preserve = false;
158 
159         // process options
160         int argi = 0;
161         while (argi < args.length) {
162             String arg = args[argi];
163             if (!arg.startsWith("-"))
164                 break;
165             if (arg.length() < 2)
166                 usage();
167             for (int i=1; i<arg.length(); i++) {
168                 char c = arg.charAt(i);
169                 switch (c) {
170                     case 'r' : recursive = true; break;
171                     case 'i' : prompt = true; break;
172                     case 'p' : preserve = true; break;
173                     default : usage();
174                 }
175             }
176             argi++;
177         }
178 
179         // remaining arguments are the source files(s) and the target location
180         int remaining = args.length - argi;
181         if (remaining < 2)
182             usage();
183         Path[] source = new Path[remaining-1];
184         int i=0;
185         while (remaining > 1) {
186             source[i++] = Paths.get(args[argi++]);
187             remaining--;
188         }
189         Path target = Paths.get(args[argi]);
190 
191         // check if target is a directory
192         boolean isDir = Files.isDirectory(target);
193 
194         // copy each source file/directory to target
195         for (i=0; i<source.length; i++) {
196             Path dest = (isDir) ? target.resolve(source[i].getFileName()) : target;
197 
198             if (recursive) {
199                 // follow links when copying files
200                 EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
201                 TreeCopier tc = new TreeCopier(source[i], dest, prompt, preserve);
202                 Files.walkFileTree(source[i], opts, Integer.MAX_VALUE, tc);
203             } else {
204                 // not recursive so source must not be a directory
205                 if (Files.isDirectory(source[i])) {
206                     System.err.format("%s: is a directory%n", source[i]);
207                     continue;
208                 }
209                 copyFile(source[i], dest, prompt, preserve);
210             }
211         }
212     }
213 }
214