| var fs = require('fs'); |
| var path = require('path'); |
| var common = require('./common'); |
| |
| // 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); |
| } else { |
| /* At this point, we've hit a file actually worth copying... so copy it on over. */ |
| if (fs.existsSync(destFile) && !opts.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 |
| //@ + `-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': 'force', |
| 'R': 'recursive', |
| 'r': 'recursive' |
| }); |
| |
| // Get sources, dest |
| if (arguments.length < 3) { |
| common.error('missing <source> and/or <dest>'); |
| } 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.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] += '*'; |
| }); |
| |
| // 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') throw e; |
| } |
| |
| cpdirSyncRecursive(src, newDest, {force: options.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.force) { |
| common.error('dest file already exists: ' + thisDest, true); |
| return; // skip file |
| } |
| |
| copyFileSync(src, thisDest); |
| }); // forEach(src) |
| } |
| module.exports = _cp; |