1 | var fs = require('fs');
|
2 | var path = require('path');
|
3 | var common = require('./common');
|
4 |
|
5 | common.register('cp', _cp, {
|
6 | cmdOptions: {
|
7 | 'f': '!no_force',
|
8 | 'n': 'no_force',
|
9 | 'u': 'update',
|
10 | 'R': 'recursive',
|
11 | 'r': 'recursive',
|
12 | 'L': 'followsymlink',
|
13 | 'P': 'noFollowsymlink',
|
14 | },
|
15 | wrapOutput: false,
|
16 | });
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | function copyFileSync(srcFile, destFile, options) {
|
22 | if (!fs.existsSync(srcFile)) {
|
23 | common.error('copyFileSync: no such file or directory: ' + srcFile);
|
24 | }
|
25 |
|
26 | var isWindows = process.platform === 'win32';
|
27 |
|
28 |
|
29 | try {
|
30 | if (options.update && common.statFollowLinks(srcFile).mtime < fs.statSync(destFile).mtime) {
|
31 | return;
|
32 | }
|
33 | } catch (e) {
|
34 |
|
35 | }
|
36 |
|
37 | if (common.statNoFollowLinks(srcFile).isSymbolicLink() && !options.followsymlink) {
|
38 | try {
|
39 | common.statNoFollowLinks(destFile);
|
40 | common.unlinkSync(destFile);
|
41 | } catch (e) {
|
42 |
|
43 | }
|
44 |
|
45 | var symlinkFull = fs.readlinkSync(srcFile);
|
46 | fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null);
|
47 | } else {
|
48 | var buf = common.buffer();
|
49 | var bufLength = buf.length;
|
50 | var bytesRead = bufLength;
|
51 | var pos = 0;
|
52 | var fdr = null;
|
53 | var fdw = null;
|
54 |
|
55 | try {
|
56 | fdr = fs.openSync(srcFile, 'r');
|
57 | } catch (e) {
|
58 |
|
59 | common.error('copyFileSync: could not read src file (' + srcFile + ')');
|
60 | }
|
61 |
|
62 | try {
|
63 | fdw = fs.openSync(destFile, 'w');
|
64 | } catch (e) {
|
65 |
|
66 | common.error('copyFileSync: could not write to dest file (code=' + e.code + '):' + destFile);
|
67 | }
|
68 |
|
69 | while (bytesRead === bufLength) {
|
70 | bytesRead = fs.readSync(fdr, buf, 0, bufLength, pos);
|
71 | fs.writeSync(fdw, buf, 0, bytesRead);
|
72 | pos += bytesRead;
|
73 | }
|
74 |
|
75 | fs.closeSync(fdr);
|
76 | fs.closeSync(fdw);
|
77 |
|
78 | fs.chmodSync(destFile, common.statFollowLinks(srcFile).mode);
|
79 | }
|
80 | }
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function cpdirSyncRecursive(sourceDir, destDir, currentDepth, opts) {
|
91 | if (!opts) opts = {};
|
92 |
|
93 |
|
94 | if (currentDepth >= common.config.maxdepth) return;
|
95 | currentDepth++;
|
96 |
|
97 | var isWindows = process.platform === 'win32';
|
98 |
|
99 |
|
100 |
|
101 | try {
|
102 | fs.mkdirSync(destDir);
|
103 | } catch (e) {
|
104 |
|
105 | if (e.code !== 'EEXIST') throw e;
|
106 | }
|
107 |
|
108 | var files = fs.readdirSync(sourceDir);
|
109 |
|
110 | for (var i = 0; i < files.length; i++) {
|
111 | var srcFile = sourceDir + '/' + files[i];
|
112 | var destFile = destDir + '/' + files[i];
|
113 | var srcFileStat = common.statNoFollowLinks(srcFile);
|
114 |
|
115 | var symlinkFull;
|
116 | if (opts.followsymlink) {
|
117 | if (cpcheckcycle(sourceDir, srcFile)) {
|
118 |
|
119 | console.error('Cycle link found.');
|
120 | symlinkFull = fs.readlinkSync(srcFile);
|
121 | fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null);
|
122 | continue;
|
123 | }
|
124 | }
|
125 | if (srcFileStat.isDirectory()) {
|
126 |
|
127 | cpdirSyncRecursive(srcFile, destFile, currentDepth, opts);
|
128 | } else if (srcFileStat.isSymbolicLink() && !opts.followsymlink) {
|
129 | symlinkFull = fs.readlinkSync(srcFile);
|
130 | try {
|
131 | common.statNoFollowLinks(destFile);
|
132 | common.unlinkSync(destFile);
|
133 | } catch (e) {
|
134 |
|
135 | }
|
136 | fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null);
|
137 | } else if (srcFileStat.isSymbolicLink() && opts.followsymlink) {
|
138 | srcFileStat = common.statFollowLinks(srcFile);
|
139 | if (srcFileStat.isDirectory()) {
|
140 | cpdirSyncRecursive(srcFile, destFile, currentDepth, opts);
|
141 | } else {
|
142 | copyFileSync(srcFile, destFile, opts);
|
143 | }
|
144 | } else {
|
145 |
|
146 | if (fs.existsSync(destFile) && opts.no_force) {
|
147 | common.log('skipping existing file: ' + files[i]);
|
148 | } else {
|
149 | copyFileSync(srcFile, destFile, opts);
|
150 | }
|
151 | }
|
152 | }
|
153 |
|
154 |
|
155 |
|
156 | var checkDir = common.statFollowLinks(sourceDir);
|
157 | fs.chmodSync(destDir, checkDir.mode);
|
158 | }
|
159 |
|
160 |
|
161 | function checkRecentCreated(sources, index) {
|
162 | var lookedSource = sources[index];
|
163 | return sources.slice(0, index).some(function (src) {
|
164 | return path.basename(src) === path.basename(lookedSource);
|
165 | });
|
166 | }
|
167 |
|
168 | function cpcheckcycle(sourceDir, srcFile) {
|
169 | var srcFileStat = common.statNoFollowLinks(srcFile);
|
170 | if (srcFileStat.isSymbolicLink()) {
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 | var cyclecheck = common.statFollowLinks(srcFile);
|
178 | if (cyclecheck.isDirectory()) {
|
179 | var sourcerealpath = fs.realpathSync(sourceDir);
|
180 | var symlinkrealpath = fs.realpathSync(srcFile);
|
181 | var re = new RegExp(symlinkrealpath);
|
182 | if (re.test(sourcerealpath)) {
|
183 | return true;
|
184 | }
|
185 | }
|
186 | }
|
187 | return false;
|
188 | }
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | function _cp(options, sources, dest) {
|
214 |
|
215 | if (options.followsymlink) {
|
216 | options.noFollowsymlink = false;
|
217 | }
|
218 | if (!options.recursive && !options.noFollowsymlink) {
|
219 | options.followsymlink = true;
|
220 | }
|
221 |
|
222 |
|
223 | if (arguments.length < 3) {
|
224 | common.error('missing <source> and/or <dest>');
|
225 | } else {
|
226 | sources = [].slice.call(arguments, 1, arguments.length - 1);
|
227 | dest = arguments[arguments.length - 1];
|
228 | }
|
229 |
|
230 | var destExists = fs.existsSync(dest);
|
231 | var destStat = destExists && common.statFollowLinks(dest);
|
232 |
|
233 |
|
234 | if ((!destExists || !destStat.isDirectory()) && sources.length > 1) {
|
235 | common.error('dest is not a directory (too many sources)');
|
236 | }
|
237 |
|
238 |
|
239 | if (destExists && destStat.isFile() && options.no_force) {
|
240 | return new common.ShellString('', '', 0);
|
241 | }
|
242 |
|
243 | sources.forEach(function (src, srcIndex) {
|
244 | if (!fs.existsSync(src)) {
|
245 | if (src === '') src = "''";
|
246 | common.error('no such file or directory: ' + src, { continue: true });
|
247 | return;
|
248 | }
|
249 | var srcStat = common.statFollowLinks(src);
|
250 | if (!options.noFollowsymlink && srcStat.isDirectory()) {
|
251 | if (!options.recursive) {
|
252 |
|
253 | common.error("omitting directory '" + src + "'", { continue: true });
|
254 | } else {
|
255 |
|
256 |
|
257 | var newDest = (destStat && destStat.isDirectory()) ?
|
258 | path.join(dest, path.basename(src)) :
|
259 | dest;
|
260 |
|
261 | try {
|
262 | common.statFollowLinks(path.dirname(dest));
|
263 | cpdirSyncRecursive(src, newDest, 0, { no_force: options.no_force, followsymlink: options.followsymlink });
|
264 | } catch (e) {
|
265 |
|
266 | common.error("cannot create directory '" + dest + "': No such file or directory");
|
267 | }
|
268 | }
|
269 | } else {
|
270 |
|
271 |
|
272 |
|
273 |
|
274 | var thisDest = dest;
|
275 | if (destStat && destStat.isDirectory()) {
|
276 | thisDest = path.normalize(dest + '/' + path.basename(src));
|
277 | }
|
278 |
|
279 | var thisDestExists = fs.existsSync(thisDest);
|
280 | if (thisDestExists && checkRecentCreated(sources, srcIndex)) {
|
281 |
|
282 | if (!options.no_force) {
|
283 | common.error("will not overwrite just-created '" + thisDest + "' with '" + src + "'", { continue: true });
|
284 | }
|
285 | return;
|
286 | }
|
287 |
|
288 | if (thisDestExists && options.no_force) {
|
289 | return;
|
290 | }
|
291 |
|
292 | if (path.relative(src, thisDest) === '') {
|
293 |
|
294 | common.error("'" + thisDest + "' and '" + src + "' are the same file", { continue: true });
|
295 | return;
|
296 | }
|
297 |
|
298 | copyFileSync(src, thisDest, options);
|
299 | }
|
300 | });
|
301 |
|
302 | return new common.ShellString('', common.state.error, common.state.errorCode);
|
303 | }
|
304 | module.exports = _cp;
|