1 // most of this code was written by Andrew Kelley
2 // licensed under the BSD license: see
3 // https://github.com/andrewrk/node-mv/blob/master/package.json
5 // this needs a cleanup
7 var fs = require('graceful-fs')
8 var ncp = require('../copy/ncp')
9 var path = require('path')
10 var rimraf = require('rimraf')
11 var mkdirp = require('../mkdirs').mkdirs
13 function mv (source, dest, options, callback) {
14 if (typeof options === 'function') {
19 var shouldMkdirp = ('mkdirp' in options) ? options.mkdirp : true
20 var clobber = ('clobber' in options) ? options.clobber : false
22 var limit = options.limit || 16
31 mkdirp(path.dirname(dest), function (err) {
32 if (err) return callback(err)
37 function doRename () {
39 fs.rename(source, dest, function (err) {
40 if (!err) return callback()
42 if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
43 rimraf(dest, function (err) {
44 if (err) return callback(err)
45 options.clobber = false // just clobbered it, no need to do it again
46 mv(source, dest, options, callback)
52 if (err.code === 'EPERM') {
53 setTimeout(function () {
54 rimraf(dest, function (err) {
55 if (err) return callback(err)
56 options.clobber = false
57 mv(source, dest, options, callback)
63 if (err.code !== 'EXDEV') return callback(err)
64 moveAcrossDevice(source, dest, clobber, limit, callback)
67 fs.link(source, dest, function (err) {
69 if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM') {
70 moveAcrossDevice(source, dest, clobber, limit, callback)
76 fs.unlink(source, callback)
82 function moveAcrossDevice (source, dest, clobber, limit, callback) {
83 fs.stat(source, function (err, stat) {
89 if (stat.isDirectory()) {
90 moveDirAcrossDevice(source, dest, clobber, limit, callback)
92 moveFileAcrossDevice(source, dest, clobber, limit, callback)
97 function moveFileAcrossDevice (source, dest, clobber, limit, callback) {
98 var outFlags = clobber ? 'w' : 'wx'
99 var ins = fs.createReadStream(source)
100 var outs = fs.createWriteStream(dest, {flags: outFlags})
102 ins.on('error', function (err) {
105 outs.removeListener('close', onClose)
107 // may want to create a directory but `out` line above
108 // creates an empty file for us: See #108
109 // don't care about error here
110 fs.unlink(dest, function () {
111 // note: `err` here is from the input stream errror
112 if (err.code === 'EISDIR' || err.code === 'EPERM') {
113 moveDirAcrossDevice(source, dest, clobber, limit, callback)
120 outs.on('error', function (err) {
123 outs.removeListener('close', onClose)
127 outs.once('close', onClose)
130 function onClose () {
131 fs.unlink(source, callback)
135 function moveDirAcrossDevice (source, dest, clobber, limit, callback) {
142 function startNcp () {
143 ncp(source, dest, options, function (errList) {
144 if (errList) return callback(errList[0])
145 rimraf(source, callback)
150 rimraf(dest, function (err) {
151 if (err) return callback(err)