source: trip-planner-front/node_modules/tmp/lib/tmp.js@ b738035

Last change on this file since b738035 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 14.5 KB
Line 
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 */
12const fs = require('fs');
13const path = require('path');
14const crypto = require('crypto');
15const osTmpDir = require('os-tmpdir');
16const _c = process.binding('constants');
17
18/*
19 * The working inner variables.
20 */
21const
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
46var
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 */
58function _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 */
84function _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 */
98function _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 */
117function _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 */
144function 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 */
180function 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 */
210function 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 */
260function 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 */
288function _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 */
325function 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 */
351function 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 */
374function _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 */
416function _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 */
435function _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 */
458function _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 */
477function 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 */
484function 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 */
508function 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 */
517function setGracefulCleanup() {
518 _gracefulCleanup = true;
519}
520
521const version = process.versions.node.split('.').map(function (value) {
522 return parseInt(value, 10);
523});
524
525if (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
534process.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
600module.exports.tmpdir = tmpDir;
601
602module.exports.dir = dir;
603module.exports.dirSync = dirSync;
604
605module.exports.file = file;
606module.exports.fileSync = fileSync;
607
608module.exports.tmpName = tmpName;
609module.exports.tmpNameSync = tmpNameSync;
610
611module.exports.setGracefulCleanup = setGracefulCleanup;
Note: See TracBrowser for help on using the repository browser.