diff options
Diffstat (limited to 'node_modules/shelljs/src/cp.js')
-rw-r--r-- | node_modules/shelljs/src/cp.js | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/node_modules/shelljs/src/cp.js b/node_modules/shelljs/src/cp.js new file mode 100644 index 000000000..04c4e57ef --- /dev/null +++ b/node_modules/shelljs/src/cp.js @@ -0,0 +1,278 @@ +var fs = require('fs'); +var path = require('path'); +var common = require('./common'); +var os = require('os'); + +common.register('cp', _cp, { + cmdOptions: { + 'f': '!no_force', + 'n': 'no_force', + 'u': 'update', + 'R': 'recursive', + 'r': 'recursive', + 'L': 'followsymlink', + 'P': 'noFollowsymlink', + }, + wrapOutput: false, +}); + +// Buffered file copy, synchronous +// (Using readFileSync() + writeFileSync() could easily cause a memory overflow +// with large files) +function copyFileSync(srcFile, destFile, options) { + if (!fs.existsSync(srcFile)) { + common.error('copyFileSync: no such file or directory: ' + srcFile); + } + + // Check the mtimes of the files if the '-u' flag is provided + try { + if (options.update && fs.statSync(srcFile).mtime < fs.statSync(destFile).mtime) { + return; + } + } catch (e) { + // If we're here, destFile probably doesn't exist, so just do a normal copy + } + + if (fs.lstatSync(srcFile).isSymbolicLink() && !options.followsymlink) { + try { + fs.lstatSync(destFile); + common.unlinkSync(destFile); // re-link it + } catch (e) { + // it doesn't exist, so no work needs to be done + } + + var symlinkFull = fs.readlinkSync(srcFile); + fs.symlinkSync(symlinkFull, destFile, os.platform() === 'win32' ? 'junction' : null); + } else { + var BUF_LENGTH = 64 * 1024; + var buf = new Buffer(BUF_LENGTH); + var bytesRead = BUF_LENGTH; + var pos = 0; + var fdr = null; + var fdw = null; + + try { + fdr = fs.openSync(srcFile, 'r'); + } catch (e) { + /* istanbul ignore next */ + common.error('copyFileSync: could not read src file (' + srcFile + ')'); + } + + try { + fdw = fs.openSync(destFile, 'w'); + } catch (e) { + /* istanbul ignore next */ + common.error('copyFileSync: could not write to dest file (code=' + e.code + '):' + destFile); + } + + while (bytesRead === BUF_LENGTH) { + bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); + fs.writeSync(fdw, buf, 0, bytesRead); + pos += bytesRead; + } + + fs.closeSync(fdr); + fs.closeSync(fdw); + + fs.chmodSync(destFile, fs.statSync(srcFile).mode); + } +} + +// Recursively copies 'sourceDir' into 'destDir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function cpdirSyncRecursive(sourceDir, destDir, currentDepth, opts) { + if (!opts) opts = {}; + + // Ensure there is not a run away recursive copy + if (currentDepth >= common.config.maxdepth) return; + currentDepth++; + + // Create the directory where all our junk is moving to; read the mode of the + // source directory and mirror it + try { + var checkDir = fs.statSync(sourceDir); + fs.mkdirSync(destDir, checkDir.mode); + } catch (e) { + // if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + var files = fs.readdirSync(sourceDir); + + for (var i = 0; i < files.length; i++) { + var srcFile = sourceDir + '/' + files[i]; + var destFile = destDir + '/' + files[i]; + var srcFileStat = fs.lstatSync(srcFile); + + var symlinkFull; + if (opts.followsymlink) { + if (cpcheckcycle(sourceDir, srcFile)) { + // Cycle link found. + console.error('Cycle link found.'); + symlinkFull = fs.readlinkSync(srcFile); + fs.symlinkSync(symlinkFull, destFile, os.platform() === 'win32' ? 'junction' : null); + continue; + } + } + if (srcFileStat.isDirectory()) { + /* recursion this thing right on back. */ + cpdirSyncRecursive(srcFile, destFile, currentDepth, opts); + } else if (srcFileStat.isSymbolicLink() && !opts.followsymlink) { + symlinkFull = fs.readlinkSync(srcFile); + try { + fs.lstatSync(destFile); + common.unlinkSync(destFile); // re-link it + } catch (e) { + // it doesn't exist, so no work needs to be done + } + fs.symlinkSync(symlinkFull, destFile, os.platform() === 'win32' ? 'junction' : null); + } else if (srcFileStat.isSymbolicLink() && opts.followsymlink) { + srcFileStat = fs.statSync(srcFile); + if (srcFileStat.isDirectory()) { + cpdirSyncRecursive(srcFile, destFile, currentDepth, opts); + } else { + copyFileSync(srcFile, destFile, opts); + } + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + if (fs.existsSync(destFile) && opts.no_force) { + common.log('skipping existing file: ' + files[i]); + } else { + copyFileSync(srcFile, destFile, opts); + } + } + } // for files +} // cpdirSyncRecursive + +function cpcheckcycle(sourceDir, srcFile) { + var srcFileStat = fs.lstatSync(srcFile); + if (srcFileStat.isSymbolicLink()) { + // Do cycle check. For example: + // $ mkdir -p 1/2/3/4 + // $ cd 1/2/3/4 + // $ ln -s ../../3 link + // $ cd ../../../.. + // $ cp -RL 1 copy + var cyclecheck = fs.statSync(srcFile); + if (cyclecheck.isDirectory()) { + var sourcerealpath = fs.realpathSync(sourceDir); + var symlinkrealpath = fs.realpathSync(srcFile); + var re = new RegExp(symlinkrealpath); + if (re.test(sourcerealpath)) { + return true; + } + } + } + return false; +} + +//@ +//@ ### cp([options,] source [, source ...], dest) +//@ ### cp([options,] source_array, dest) +//@ Available options: +//@ +//@ + `-f`: force (default behavior) +//@ + `-n`: no-clobber +//@ + `-u`: only copy if source is newer than dest +//@ + `-r`, `-R`: recursive +//@ + `-L`: follow symlinks +//@ + `-P`: don't follow symlinks +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cp('file1', 'dir1'); +//@ cp('-R', 'path/to/dir/', '~/newCopy/'); +//@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +//@ ``` +//@ +//@ Copies files. +function _cp(options, sources, dest) { + // If we're missing -R, it actually implies -L (unless -P is explicit) + if (options.followsymlink) { + options.noFollowsymlink = false; + } + if (!options.recursive && !options.noFollowsymlink) { + options.followsymlink = true; + } + + // Get sources, dest + if (arguments.length < 3) { + common.error('missing <source> and/or <dest>'); + } else { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } + + var destExists = fs.existsSync(dest); + var destStat = destExists && fs.statSync(dest); + + // Dest is not existing dir, but multiple sources given + if ((!destExists || !destStat.isDirectory()) && sources.length > 1) { + common.error('dest is not a directory (too many sources)'); + } + + // Dest is an existing file, but -n is given + if (destExists && destStat.isFile() && options.no_force) { + return new common.ShellString('', '', 0); + } + + sources.forEach(function (src) { + if (!fs.existsSync(src)) { + common.error('no such file or directory: ' + src, { continue: true }); + return; // skip file + } + var srcStat = fs.statSync(src); + if (!options.noFollowsymlink && srcStat.isDirectory()) { + if (!options.recursive) { + // Non-Recursive + common.error("omitting directory '" + src + "'", { continue: true }); + } else { + // Recursive + // 'cp /a/source dest' should create 'source' in 'dest' + var newDest = (destStat && destStat.isDirectory()) ? + path.join(dest, path.basename(src)) : + dest; + + try { + fs.statSync(path.dirname(dest)); + cpdirSyncRecursive(src, newDest, 0, { no_force: options.no_force, followsymlink: options.followsymlink }); + } catch (e) { + /* istanbul ignore next */ + common.error("cannot create directory '" + dest + "': No such file or directory"); + } + } + } else { + // If here, src is a file + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (destStat && destStat.isDirectory()) { + thisDest = path.normalize(dest + '/' + path.basename(src)); + } + + if (fs.existsSync(thisDest) && options.no_force) { + return; // skip file + } + + if (path.relative(src, thisDest) === '') { + // a file cannot be copied to itself, but we want to continue copying other files + common.error("'" + thisDest + "' and '" + src + "' are the same file", { continue: true }); + return; + } + + copyFileSync(src, thisDest, options); + } + }); // forEach(src) + + return new common.ShellString('', common.state.error, common.state.errorCode); +} +module.exports = _cp; |