Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / tmp / lib / tmp.js
1 /*!
2  * Tmp
3  *
4  * Copyright (c) 2011-2017 KARASZI Istvan <github@spam.raszi.hu>
5  *
6  * MIT Licensed
7  */
8
9 /*
10  * Module dependencies.
11  */
12 const fs = require('fs');
13 const path = require('path');
14 const crypto = require('crypto');
15 const osTmpDir = require('os-tmpdir');
16 const _c = process.binding('constants');
17
18 /*
19  * The working inner variables.
20  */
21 const
22   /**
23    * The temporary directory.
24    * @type {string}
25    */
26   tmpDir = osTmpDir(),
27
28   // the random characters to choose from
29   RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
30
31   TEMPLATE_PATTERN = /XXXXXX/,
32
33   DEFAULT_TRIES = 3,
34
35   CREATE_FLAGS = (_c.O_CREAT || _c.fs.O_CREAT) | (_c.O_EXCL || _c.fs.O_EXCL) | (_c.O_RDWR || _c.fs.O_RDWR),
36
37   EBADF = _c.EBADF || _c.os.errno.EBADF,
38   ENOENT = _c.ENOENT || _c.os.errno.ENOENT,
39
40   DIR_MODE = 448 /* 0o700 */,
41   FILE_MODE = 384 /* 0o600 */,
42
43   // this will hold the objects need to be removed on exit
44   _removeObjects = [];
45
46 var
47   _gracefulCleanup = false,
48   _uncaughtException = false;
49
50 /**
51  * Random name generator based on crypto.
52  * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
53  *
54  * @param {number} howMany
55  * @returns {string} the generated random name
56  * @private
57  */
58 function _randomChars(howMany) {
59   var
60     value = [],
61     rnd = null;
62
63   // make sure that we do not fail because we ran out of entropy
64   try {
65     rnd = crypto.randomBytes(howMany);
66   } catch (e) {
67     rnd = crypto.pseudoRandomBytes(howMany);
68   }
69
70   for (var i = 0; i < howMany; i++) {
71     value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
72   }
73
74   return value.join('');
75 }
76
77 /**
78  * Checks whether the `obj` parameter is defined or not.
79  *
80  * @param {Object} obj
81  * @returns {boolean} true if the object is undefined
82  * @private
83  */
84 function _isUndefined(obj) {
85   return typeof obj === 'undefined';
86 }
87
88 /**
89  * Parses the function arguments.
90  *
91  * This function helps to have optional arguments.
92  *
93  * @param {(Options|Function)} options
94  * @param {Function} callback
95  * @returns {Array} parsed arguments
96  * @private
97  */
98 function _parseArguments(options, callback) {
99   if (typeof options == 'function') {
100     return [callback || {}, options];
101   }
102
103   if (_isUndefined(options)) {
104     return [{}, callback];
105   }
106
107   return [options, callback];
108 }
109
110 /**
111  * Generates a new temporary name.
112  *
113  * @param {Object} opts
114  * @returns {string} the new random name according to opts
115  * @private
116  */
117 function _generateTmpName(opts) {
118   if (opts.name) {
119     return path.join(opts.dir || tmpDir, opts.name);
120   }
121
122   // mkstemps like template
123   if (opts.template) {
124     return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
125   }
126
127   // prefix and postfix
128   const name = [
129     opts.prefix || 'tmp-',
130     process.pid,
131     _randomChars(12),
132     opts.postfix || ''
133   ].join('');
134
135   return path.join(opts.dir || tmpDir, name);
136 }
137
138 /**
139  * Gets a temporary file name.
140  *
141  * @param {(Options|tmpNameCallback)} options options or callback
142  * @param {?tmpNameCallback} callback the callback function
143  */
144 function tmpName(options, callback) {
145   var
146     args = _parseArguments(options, callback),
147     opts = args[0],
148     cb = args[1],
149     tries = opts.name ? 1 : opts.tries || DEFAULT_TRIES;
150
151   if (isNaN(tries) || tries < 0)
152     return cb(new Error('Invalid tries'));
153
154   if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
155     return cb(new Error('Invalid template provided'));
156
157   (function _getUniqueName() {
158     const name = _generateTmpName(opts);
159
160     // check whether the path exists then retry if needed
161     fs.stat(name, function (err) {
162       if (!err) {
163         if (tries-- > 0) return _getUniqueName();
164
165         return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
166       }
167
168       cb(null, name);
169     });
170   }());
171 }
172
173 /**
174  * Synchronous version of tmpName.
175  *
176  * @param {Object} options
177  * @returns {string} the generated random name
178  * @throws {Error} if the options are invalid or could not generate a filename
179  */
180 function tmpNameSync(options) {
181   var
182     args = _parseArguments(options),
183     opts = args[0],
184     tries = opts.name ? 1 : opts.tries || DEFAULT_TRIES;
185
186   if (isNaN(tries) || tries < 0)
187     throw new Error('Invalid tries');
188
189   if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
190     throw new Error('Invalid template provided');
191
192   do {
193     const name = _generateTmpName(opts);
194     try {
195       fs.statSync(name);
196     } catch (e) {
197       return name;
198     }
199   } while (tries-- > 0);
200
201   throw new Error('Could not get a unique tmp filename, max tries reached');
202 }
203
204 /**
205  * Creates and opens a temporary file.
206  *
207  * @param {(Options|fileCallback)} options the config options or the callback function
208  * @param {?fileCallback} callback
209  */
210 function file(options, callback) {
211   var
212     args = _parseArguments(options, callback),
213     opts = args[0],
214     cb = args[1];
215
216   opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;
217
218   // gets a temporary filename
219   tmpName(opts, function _tmpNameCreated(err, name) {
220     if (err) return cb(err);
221
222     // create and open the file
223     fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
224       if (err) return cb(err);
225
226       if (opts.discardDescriptor) {
227         return fs.close(fd, function _discardCallback(err) {
228           if (err) {
229             // Low probability, and the file exists, so this could be
230             // ignored.  If it isn't we certainly need to unlink the
231             // file, and if that fails too its error is more
232             // important.
233             try {
234               fs.unlinkSync(name);
235             } catch (e) {
236               if (!isENOENT(e)) {
237                 err = e;
238               }
239             }
240             return cb(err);
241           }
242           cb(null, name, undefined, _prepareTmpFileRemoveCallback(name, -1, opts));
243         });
244       }
245       if (opts.detachDescriptor) {
246         return cb(null, name, fd, _prepareTmpFileRemoveCallback(name, -1, opts));
247       }
248       cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
249     });
250   });
251 }
252
253 /**
254  * Synchronous version of file.
255  *
256  * @param {Options} options
257  * @returns {FileSyncObject} object consists of name, fd and removeCallback
258  * @throws {Error} if cannot create a file
259  */
260 function fileSync(options) {
261   var
262     args = _parseArguments(options),
263     opts = args[0];
264
265   opts.postfix = opts.postfix || '.tmp';
266
267   const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
268   const name = tmpNameSync(opts);
269   var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
270   if (opts.discardDescriptor) {
271     fs.closeSync(fd); 
272     fd = undefined;
273   }
274
275   return {
276     name: name,
277     fd: fd,
278     removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts)
279   };
280 }
281
282 /**
283  * Removes files and folders in a directory recursively.
284  *
285  * @param {string} root
286  * @private
287  */
288 function _rmdirRecursiveSync(root) {
289   const dirs = [root];
290
291   do {
292     var
293       dir = dirs.pop(),
294       deferred = false,
295       files = fs.readdirSync(dir);
296
297     for (var i = 0, length = files.length; i < length; i++) {
298       var
299         file = path.join(dir, files[i]),
300         stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories
301
302       if (stat.isDirectory()) {
303         if (!deferred) {
304           deferred = true;
305           dirs.push(dir);
306         }
307         dirs.push(file);
308       } else {
309         fs.unlinkSync(file);
310       }
311     }
312
313     if (!deferred) {
314       fs.rmdirSync(dir);
315     }
316   } while (dirs.length !== 0);
317 }
318
319 /**
320  * Creates a temporary directory.
321  *
322  * @param {(Options|dirCallback)} options the options or the callback function
323  * @param {?dirCallback} callback
324  */
325 function dir(options, callback) {
326   var
327     args = _parseArguments(options, callback),
328     opts = args[0],
329     cb = args[1];
330
331   // gets a temporary filename
332   tmpName(opts, function _tmpNameCreated(err, name) {
333     if (err) return cb(err);
334
335     // create the directory
336     fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
337       if (err) return cb(err);
338
339       cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
340     });
341   });
342 }
343
344 /**
345  * Synchronous version of dir.
346  *
347  * @param {Options} options
348  * @returns {DirSyncObject} object consists of name and removeCallback
349  * @throws {Error} if it cannot create a directory
350  */
351 function dirSync(options) {
352   var
353     args = _parseArguments(options),
354     opts = args[0];
355
356   const name = tmpNameSync(opts);
357   fs.mkdirSync(name, opts.mode || DIR_MODE);
358
359   return {
360     name: name,
361     removeCallback: _prepareTmpDirRemoveCallback(name, opts)
362   };
363 }
364
365 /**
366  * Prepares the callback for removal of the temporary file.
367  *
368  * @param {string} name the path of the file
369  * @param {number} fd file descriptor
370  * @param {Object} opts
371  * @returns {fileCallback}
372  * @private
373  */
374 function _prepareTmpFileRemoveCallback(name, fd, opts) {
375   const removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
376     try {
377       if (0 <= fdPath[0]) {
378         fs.closeSync(fdPath[0]);
379       }
380     }
381     catch (e) {
382       // under some node/windows related circumstances, a temporary file
383       // may have not be created as expected or the file was already closed
384       // by the user, in which case we will simply ignore the error
385       if (!isEBADF(e) && !isENOENT(e)) {
386         // reraise any unanticipated error
387         throw e;
388       }
389     }
390     try {
391       fs.unlinkSync(fdPath[1]);
392     }
393     catch (e) {
394       if (!isENOENT(e)) {
395         // reraise any unanticipated error
396         throw e;
397       }
398     }
399   }, [fd, name]);
400
401   if (!opts.keep) {
402     _removeObjects.unshift(removeCallback);
403   }
404
405   return removeCallback;
406 }
407
408 /**
409  * Prepares the callback for removal of the temporary directory.
410  *
411  * @param {string} name
412  * @param {Object} opts
413  * @returns {Function} the callback
414  * @private
415  */
416 function _prepareTmpDirRemoveCallback(name, opts) {
417   const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
418   const removeCallback = _prepareRemoveCallback(removeFunction, name);
419
420   if (!opts.keep) {
421     _removeObjects.unshift(removeCallback);
422   }
423
424   return removeCallback;
425 }
426
427 /**
428  * Creates a guarded function wrapping the removeFunction call.
429  *
430  * @param {Function} removeFunction
431  * @param {Object} arg
432  * @returns {Function}
433  * @private
434  */
435 function _prepareRemoveCallback(removeFunction, arg) {
436   var called = false;
437
438   return function _cleanupCallback(next) {
439     if (!called) {
440       const index = _removeObjects.indexOf(_cleanupCallback);
441       if (index >= 0) {
442         _removeObjects.splice(index, 1);
443       }
444
445       called = true;
446       removeFunction(arg);
447     }
448
449     if (next) next(null);
450   };
451 }
452
453 /**
454  * The garbage collector.
455  *
456  * @private
457  */
458 function _garbageCollector() {
459   if (_uncaughtException && !_gracefulCleanup) {
460     return;
461   }
462
463   // the function being called removes itself from _removeObjects,
464   // loop until _removeObjects is empty
465   while (_removeObjects.length) {
466     try {
467       _removeObjects[0].call(null);
468     } catch (e) {
469       // already removed?
470     }
471   }
472 }
473
474 /**
475  * Helper for testing against EBADF to compensate changes made to Node 7.x under Windows.
476  */
477 function isEBADF(error) {
478   return isExpectedError(error, -EBADF, 'EBADF');
479 }
480
481 /**
482  * Helper for testing against ENOENT to compensate changes made to Node 7.x under Windows.
483  */
484 function isENOENT(error) {
485   return isExpectedError(error, -ENOENT, 'ENOENT');
486 }
487
488 /**
489  * Helper to determine whether the expected error code matches the actual code and errno,
490  * which will differ between the supported node versions.
491  *
492  * - Node >= 7.0:
493  *   error.code {String}
494  *   error.errno {String|Number} any numerical value will be negated
495  *
496  * - Node >= 6.0 < 7.0:
497  *   error.code {String}
498  *   error.errno {Number} negated
499  *
500  * - Node >= 4.0 < 6.0: introduces SystemError
501  *   error.code {String}
502  *   error.errno {Number} negated
503  *
504  * - Node >= 0.10 < 4.0:
505  *   error.code {Number} negated
506  *   error.errno n/a
507  */
508 function isExpectedError(error, code, errno) {
509   return error.code == code || error.code == errno;
510 }
511
512 /**
513  * Sets the graceful cleanup.
514  *
515  * Also removes the created files and directories when an uncaught exception occurs.
516  */
517 function setGracefulCleanup() {
518   _gracefulCleanup = true;
519 }
520
521 const version = process.versions.node.split('.').map(function (value) {
522   return parseInt(value, 10);
523 });
524
525 if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
526   process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
527     _uncaughtException = true;
528     _garbageCollector();
529
530     throw err;
531   });
532 }
533
534 process.addListener('exit', function _exit(code) {
535   if (code) _uncaughtException = true;
536   _garbageCollector();
537 });
538
539 /**
540  * Configuration options.
541  *
542  * @typedef {Object} Options
543  * @property {?number} tries the number of tries before give up the name generation
544  * @property {?string} template the "mkstemp" like filename template
545  * @property {?string} name fix name
546  * @property {?string} dir the tmp directory to use
547  * @property {?string} prefix prefix for the generated name
548  * @property {?string} postfix postfix for the generated name
549  */
550
551 /**
552  * @typedef {Object} FileSyncObject
553  * @property {string} name the name of the file
554  * @property {string} fd the file descriptor
555  * @property {fileCallback} removeCallback the callback function to remove the file
556  */
557
558 /**
559  * @typedef {Object} DirSyncObject
560  * @property {string} name the name of the directory
561  * @property {fileCallback} removeCallback the callback function to remove the directory
562  */
563
564 /**
565  * @callback tmpNameCallback
566  * @param {?Error} err the error object if anything goes wrong
567  * @param {string} name the temporary file name
568  */
569
570 /**
571  * @callback fileCallback
572  * @param {?Error} err the error object if anything goes wrong
573  * @param {string} name the temporary file name
574  * @param {number} fd the file descriptor
575  * @param {cleanupCallback} fn the cleanup callback function
576  */
577
578 /**
579  * @callback dirCallback
580  * @param {?Error} err the error object if anything goes wrong
581  * @param {string} name the temporary file name
582  * @param {cleanupCallback} fn the cleanup callback function
583  */
584
585 /**
586  * Removes the temporary created file or directory.
587  *
588  * @callback cleanupCallback
589  * @param {simpleCallback} [next] function to call after entry was removed
590  */
591
592 /**
593  * Callback function for function composition.
594  * @see {@link https://github.com/raszi/node-tmp/issues/57|raszi/node-tmp#57}
595  *
596  * @callback simpleCallback
597  */
598
599 // exporting all the needed methods
600 module.exports.tmpdir = tmpDir;
601
602 module.exports.dir = dir;
603 module.exports.dirSync = dirSync;
604
605 module.exports.file = file;
606 module.exports.fileSync = fileSync;
607
608 module.exports.tmpName = tmpName;
609 module.exports.tmpNameSync = tmpNameSync;
610
611 module.exports.setGracefulCleanup = setGracefulCleanup;