7f684e4b59384215fae2d2eae5ce5b43ed749325
[platform/upstream/nodejs.git] / deps / npm / node_modules / rimraf / rimraf.js
1 module.exports = rimraf
2 rimraf.sync = rimrafSync
3
4 var assert = require("assert")
5 var path = require("path")
6 var fs = require("fs")
7 var glob = require("glob")
8
9 var globOpts = {
10   nosort: true,
11   nocomment: true,
12   nonegate: true,
13   silent: true
14 }
15
16 // for EMFILE handling
17 var timeout = 0
18
19 var isWindows = (process.platform === "win32")
20
21 function defaults (options) {
22   var methods = [
23     'unlink',
24     'chmod',
25     'stat',
26     'lstat',
27     'rmdir',
28     'readdir'
29   ]
30   methods.forEach(function(m) {
31     options[m] = options[m] || fs[m]
32     m = m + 'Sync'
33     options[m] = options[m] || fs[m]
34   })
35
36   options.maxBusyTries = options.maxBusyTries || 3
37   options.emfileWait = options.emfileWait || 1000
38 }
39
40 function rimraf (p, options, cb) {
41   if (typeof options === 'function') {
42     cb = options
43     options = {}
44   }
45
46   assert(p, 'rimraf: missing path')
47   assert.equal(typeof p, 'string', 'rimraf: path should be a string')
48   assert(options, 'rimraf: missing options')
49   assert.equal(typeof options, 'object', 'rimraf: options should be object')
50   assert.equal(typeof cb, 'function', 'rimraf: callback function required')
51
52   defaults(options)
53
54   var busyTries = 0
55   var errState = null
56   var n = 0
57
58   if (!glob.hasMagic(p))
59     return afterGlob(null, [p])
60
61   fs.lstat(p, function (er, stat) {
62     if (!er)
63       return afterGlob(null, [p])
64
65     glob(p, globOpts, afterGlob)
66   })
67
68   function next (er) {
69     errState = errState || er
70     if (--n === 0)
71       cb(errState)
72   }
73
74   function afterGlob (er, results) {
75     if (er)
76       return cb(er)
77
78     n = results.length
79     if (n === 0)
80       return cb()
81
82     results.forEach(function (p) {
83       rimraf_(p, options, function CB (er) {
84         if (er) {
85           if (isWindows && (er.code === "EBUSY" || er.code === "ENOTEMPTY") &&
86               busyTries < options.maxBusyTries) {
87             busyTries ++
88             var time = busyTries * 100
89             // try again, with the same exact callback as this one.
90             return setTimeout(function () {
91               rimraf_(p, options, CB)
92             }, time)
93           }
94
95           // this one won't happen if graceful-fs is used.
96           if (er.code === "EMFILE" && timeout < options.emfileWait) {
97             return setTimeout(function () {
98               rimraf_(p, options, CB)
99             }, timeout ++)
100           }
101
102           // already gone
103           if (er.code === "ENOENT") er = null
104         }
105
106         timeout = 0
107         next(er)
108       })
109     })
110   }
111 }
112
113 // Two possible strategies.
114 // 1. Assume it's a file.  unlink it, then do the dir stuff on EPERM or EISDIR
115 // 2. Assume it's a directory.  readdir, then do the file stuff on ENOTDIR
116 //
117 // Both result in an extra syscall when you guess wrong.  However, there
118 // are likely far more normal files in the world than directories.  This
119 // is based on the assumption that a the average number of files per
120 // directory is >= 1.
121 //
122 // If anyone ever complains about this, then I guess the strategy could
123 // be made configurable somehow.  But until then, YAGNI.
124 function rimraf_ (p, options, cb) {
125   assert(p)
126   assert(options)
127   assert(typeof cb === 'function')
128
129   // sunos lets the root user unlink directories, which is... weird.
130   // so we have to lstat here and make sure it's not a dir.
131   options.lstat(p, function (er, st) {
132     if (er && er.code === "ENOENT")
133       return cb(null)
134
135     if (st && st.isDirectory())
136       return rmdir(p, options, er, cb)
137
138     options.unlink(p, function (er) {
139       if (er) {
140         if (er.code === "ENOENT")
141           return cb(null)
142         if (er.code === "EPERM")
143           return (isWindows)
144             ? fixWinEPERM(p, options, er, cb)
145             : rmdir(p, options, er, cb)
146         if (er.code === "EISDIR")
147           return rmdir(p, options, er, cb)
148       }
149       return cb(er)
150     })
151   })
152 }
153
154 function fixWinEPERM (p, options, er, cb) {
155   assert(p)
156   assert(options)
157   assert(typeof cb === 'function')
158   if (er)
159     assert(er instanceof Error)
160
161   options.chmod(p, 666, function (er2) {
162     if (er2)
163       cb(er2.code === "ENOENT" ? null : er)
164     else
165       options.stat(p, function(er3, stats) {
166         if (er3)
167           cb(er3.code === "ENOENT" ? null : er)
168         else if (stats.isDirectory())
169           rmdir(p, options, er, cb)
170         else
171           options.unlink(p, cb)
172       })
173   })
174 }
175
176 function fixWinEPERMSync (p, options, er) {
177   assert(p)
178   assert(options)
179   if (er)
180     assert(er instanceof Error)
181
182   try {
183     options.chmodSync(p, 666)
184   } catch (er2) {
185     if (er2.code === "ENOENT")
186       return
187     else
188       throw er
189   }
190
191   try {
192     var stats = options.statSync(p)
193   } catch (er3) {
194     if (er3.code === "ENOENT")
195       return
196     else
197       throw er
198   }
199
200   if (stats.isDirectory())
201     rmdirSync(p, options, er)
202   else
203     options.unlinkSync(p)
204 }
205
206 function rmdir (p, options, originalEr, cb) {
207   assert(p)
208   assert(options)
209   if (originalEr)
210     assert(originalEr instanceof Error)
211   assert(typeof cb === 'function')
212
213   // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
214   // if we guessed wrong, and it's not a directory, then
215   // raise the original error.
216   options.rmdir(p, function (er) {
217     if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
218       rmkids(p, options, cb)
219     else if (er && er.code === "ENOTDIR")
220       cb(originalEr)
221     else
222       cb(er)
223   })
224 }
225
226 function rmkids(p, options, cb) {
227   assert(p)
228   assert(options)
229   assert(typeof cb === 'function')
230
231   options.readdir(p, function (er, files) {
232     if (er)
233       return cb(er)
234     var n = files.length
235     if (n === 0)
236       return options.rmdir(p, cb)
237     var errState
238     files.forEach(function (f) {
239       rimraf(path.join(p, f), options, function (er) {
240         if (errState)
241           return
242         if (er)
243           return cb(errState = er)
244         if (--n === 0)
245           options.rmdir(p, cb)
246       })
247     })
248   })
249 }
250
251 // this looks simpler, and is strictly *faster*, but will
252 // tie up the JavaScript thread and fail on excessively
253 // deep directory trees.
254 function rimrafSync (p, options) {
255   options = options || {}
256   defaults(options)
257
258   assert(p, 'rimraf: missing path')
259   assert.equal(typeof p, 'string', 'rimraf: path should be a string')
260   assert(options, 'rimraf: missing options')
261   assert.equal(typeof options, 'object', 'rimraf: options should be object')
262
263   var results
264
265   if (!glob.hasMagic(p)) {
266     results = [p]
267   } else {
268     try {
269       fs.lstatSync(p)
270       results = [p]
271     } catch (er) {
272       results = glob.sync(p, globOpts)
273     }
274   }
275
276   if (!results.length)
277     return
278
279   for (var i = 0; i < results.length; i++) {
280     var p = results[i]
281
282     try {
283       var st = options.lstatSync(p)
284     } catch (er) {
285       if (er.code === "ENOENT")
286         return
287     }
288
289     try {
290       // sunos lets the root user unlink directories, which is... weird.
291       if (st && st.isDirectory())
292         rmdirSync(p, options, null)
293       else
294         options.unlinkSync(p)
295     } catch (er) {
296       if (er.code === "ENOENT")
297         return
298       if (er.code === "EPERM")
299         return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
300       if (er.code !== "EISDIR")
301         throw er
302       rmdirSync(p, options, er)
303     }
304   }
305 }
306
307 function rmdirSync (p, options, originalEr) {
308   assert(p)
309   assert(options)
310   if (originalEr)
311     assert(originalEr instanceof Error)
312
313   try {
314     options.rmdirSync(p)
315   } catch (er) {
316     if (er.code === "ENOENT")
317       return
318     if (er.code === "ENOTDIR")
319       throw originalEr
320     if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
321       rmkidsSync(p, options)
322   }
323 }
324
325 function rmkidsSync (p, options) {
326   assert(p)
327   assert(options)
328   options.readdirSync(p).forEach(function (f) {
329     rimrafSync(path.join(p, f), options)
330   })
331   options.rmdirSync(p, options)
332 }