var fs = require('fs'); var path = require('path'); var common = require('./common'); var os = require('os'); // Buffered file copy, synchronous // (Using readFileSync() + writeFileSync() could easily cause a memory overflow // with large files) function copyFileSync(srcFile, destFile) { if (!fs.existsSync(srcFile)) common.error('copyFileSync: no such file or directory: ' + srcFile); var BUF_LENGTH = 64*1024, buf = new Buffer(BUF_LENGTH), bytesRead = BUF_LENGTH, pos = 0, fdr = null, fdw = null; try { fdr = fs.openSync(srcFile, 'r'); } catch(e) { common.error('copyFileSync: could not read src file ('+srcFile+')'); } try { fdw = fs.openSync(destFile, 'w'); } catch(e) { 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, opts) { if (!opts) opts = {}; /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */ var checkDir = fs.statSync(sourceDir); try { 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); if (srcFileStat.isDirectory()) { /* recursion this thing right on back. */ cpdirSyncRecursive(srcFile, destFile, opts); } else if (srcFileStat.isSymbolicLink()) { var symlinkFull = fs.readlinkSync(srcFile); fs.symlinkSync(symlinkFull, destFile, os.platform() === "win32" ? "junction" : null); } 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); } } } // for files } // cpdirSyncRecursive //@ //@ ### cp([options,] source [, source ...], dest) //@ ### cp([options,] source_array, dest) //@ Available options: //@ //@ + `-f`: force (default behavior) //@ + `-n`: no-clobber //@ + `-r, -R`: recursive //@ //@ Examples: //@ //@ ```javascript //@ cp('file1', 'dir1'); //@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); //@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above //@ ``` //@ //@ Copies files. The wildcard `*` is accepted. function _cp(options, sources, dest) { options = common.parseOptions(options, { 'f': '!no_force', 'n': 'no_force', 'R': 'recursive', 'r': 'recursive' }); // Get sources, dest if (arguments.length < 3) { common.error('missing and/or '); } else if (arguments.length > 3) { sources = [].slice.call(arguments, 1, arguments.length - 1); dest = arguments[arguments.length - 1]; } else if (typeof sources === 'string') { sources = [sources]; } else if ('length' in sources) { sources = sources; // no-op for array } else { common.error('invalid arguments'); } var exists = fs.existsSync(dest), stats = exists && fs.statSync(dest); // Dest is not existing dir, but multiple sources given if ((!exists || !stats.isDirectory()) && sources.length > 1) common.error('dest is not a directory (too many sources)'); // Dest is an existing file, but no -f given if (exists && stats.isFile() && options.no_force) common.error('dest file already exists: ' + dest); if (options.recursive) { // Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*" // (see Github issue #15) sources.forEach(function(src, i) { if (src[src.length - 1] === '/') { sources[i] += '*'; // If src is a directory and dest doesn't exist, 'cp -r src dest' should copy src/* into dest } else if (fs.statSync(src).isDirectory() && !exists) { sources[i] += '/*'; } }); // Create dest try { fs.mkdirSync(dest, parseInt('0777', 8)); } catch (e) { // like Unix's cp, keep going even if we can't create dest dir } } sources = common.expand(sources); sources.forEach(function(src) { if (!fs.existsSync(src)) { common.error('no such file or directory: '+src, true); return; // skip file } // If here, src exists if (fs.statSync(src).isDirectory()) { if (!options.recursive) { // Non-Recursive common.log(src + ' is a directory (not copied)'); } else { // Recursive // 'cp /a/source dest' should create 'source' in 'dest' var newDest = path.join(dest, path.basename(src)), checkDir = fs.statSync(src); try { fs.mkdirSync(newDest, checkDir.mode); } catch (e) { //if the directory already exists, that's okay if (e.code !== 'EEXIST') { common.error('dest file no such file or directory: ' + newDest, true); throw e; } } cpdirSyncRecursive(src, newDest, {no_force: options.no_force}); } return; // done with dir } // If here, src is a file // When copying to '/path/dir': // thisDest = '/path/dir/file1' var thisDest = dest; if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) thisDest = path.normalize(dest + '/' + path.basename(src)); if (fs.existsSync(thisDest) && options.no_force) { common.error('dest file already exists: ' + thisDest, true); return; // skip file } copyFileSync(src, thisDest); }); // forEach(src) } module.exports = _cp;