diff --git a/src/cp.js b/src/cp.js index 1f39f1a..630b5f5 100644 --- a/src/cp.js +++ b/src/cp.js @@ -53,8 +53,8 @@ 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 { + var checkDir = fs.statSync(sourceDir); fs.mkdirSync(destDir, checkDir.mode); } catch (e) { //if the directory already exists, that's okay @@ -117,95 +117,63 @@ function _cp(options, sources, dest) { // Get sources, dest if (arguments.length < 3) { common.error('missing and/or '); - } else if (arguments.length > 3) { + } else { 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); + var destExists = fs.existsSync(dest), + destStat = destExists && fs.statSync(dest); // Dest is not existing dir, but multiple sources given - if ((!exists || !stats.isDirectory()) && sources.length > 1) + if ((!destExists || !destStat.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, {dot: true}); + // Dest is an existing file, but -n is given + if (destExists && destStat.isFile() && options.no_force) + common.error('dest file already destExists: ' + dest); 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()) { + var srcStat = fs.statSync(src); + if (srcStat.isDirectory()) { if (!options.recursive) { // Non-Recursive - common.log(src + ' is a directory (not copied)'); + common.error("omitting directory '" + src + "'", true); } 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; - } - } + var newDest = (destStat && destStat.isDirectory()) ? + path.join(dest, path.basename(src)) : + dest; - cpdirSyncRecursive(src, newDest, {no_force: options.no_force}); + try { + fs.statSync(path.dirname(dest)); + cpdirSyncRecursive(src, newDest, {no_force: options.no_force}); + } catch(e) { + common.error("cannot create directory '" + dest + "': No such file or directory"); + } } return; // done with dir + } 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) { + common.error('dest file already destExists: ' + thisDest, true); + return; // skip file + } + + copyFileSync(src, thisDest); } - - // 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; diff --git a/test/cp.js b/test/cp.js index 7732f06..f10a25e 100644 --- a/test/cp.js +++ b/test/cp.js @@ -142,7 +142,7 @@ assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + '') shell.rm('-rf', 'tmp/*'); shell.cp('-R', 'resources/cp/', 'tmp/'); assert.equal(shell.error(), null); -assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp') + ''); +assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + ''); // recursive, globbing regular files with extension (see Github issue #376) shell.rm('-rf', 'tmp/*'); @@ -151,6 +151,13 @@ assert.equal(shell.error(), null); assert.ok(fs.existsSync('tmp/file1.txt')); assert.ok(fs.existsSync('tmp/file2.txt')); +// recursive, copying one regular file (also related to Github issue #376) +shell.rm('-rf', 'tmp/*'); +shell.cp('-R', 'resources/file1.txt', 'tmp'); +assert.equal(shell.error(), null); +assert.ok(fs.existsSync('tmp/file1.txt')); +assert.ok(!fs.statSync('tmp/file1.txt').isDirectory()); // don't let it be a dir + //recursive, everything exists, no force flag shell.rm('-rf', 'tmp/*'); shell.cp('-R', 'resources/cp', 'tmp'); @@ -179,12 +186,18 @@ shell.cp('-r', 'resources/issue44', 'tmp/dir2/dir3'); assert.ok(shell.error()); assert.equal(fs.existsSync('tmp/dir2'), false); -//recursive, creates dest dir, implicitly copies contents of source dir +//recursive, copies entire directory shell.rm('-rf', 'tmp/*'); shell.cp('-r', 'resources/cp/dir_a', 'tmp/dest'); assert.equal(shell.error(), null); assert.equal(fs.existsSync('tmp/dest/z'), true); +//recursive, with trailing slash, does the exact same +shell.rm('-rf', 'tmp/*'); +shell.cp('-r', 'resources/cp/dir_a/', 'tmp/dest'); +assert.equal(shell.error(), null); +assert.equal(fs.existsSync('tmp/dest/z'), true); + // On Windows, permission bits are quite different so skip those tests for now if (common.platform !== 'win') { //preserve mode bits @@ -201,4 +214,26 @@ shell.cp('-r', 'resources/ls/', 'tmp/'); assert.ok(!shell.error()); assert.ok(fs.existsSync('tmp/.hidden_file')); +// no-recursive will copy regular files only +shell.rm('-rf', 'tmp/'); +shell.mkdir('tmp/'); +shell.cp('resources/file1.txt', 'resources/ls/', 'tmp/'); +assert.ok(shell.error()); +assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents +assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself +assert.ok(fs.existsSync('tmp/file1.txt')); + +// no-recursive will copy regular files only +shell.rm('-rf', 'tmp/'); +shell.mkdir('tmp/'); +shell.cp('resources/file1.txt', 'resources/file2.txt', 'resources/cp', + 'resources/ls/', 'tmp/'); +assert.ok(shell.error()); +assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents +assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself +assert.ok(!fs.existsSync('tmp/a')); // doesn't copy dir contents +assert.ok(!fs.existsSync('tmp/cp')); // doesn't copy dir itself +assert.ok(fs.existsSync('tmp/file1.txt')); +assert.ok(fs.existsSync('tmp/file2.txt')); + shell.exit(123); diff --git a/test/set.js b/test/set.js index a27fa58..8c52a95 100644 --- a/test/set.js +++ b/test/set.js @@ -6,7 +6,6 @@ var oldConfigSilent = shell.config.silent; shell.config.silent = true; shell.rm('-rf', 'tmp'); -shell.mkdir('tmp'); // // Valids