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