Innovenergy_trunk/frontend/node_modules/tmp/lib/tmp.js

463 lines
10 KiB
JavaScript

/*!
* Tmp
*
* Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
*
* MIT Licensed
*/
/**
* Module dependencies.
*/
var
fs = require('fs'),
path = require('path'),
os = require('os'),
crypto = require('crypto'),
exists = fs.exists || path.exists,
existsSync = fs.existsSync || path.existsSync,
tmpDir = require('os-tmpdir'),
_c = require('constants');
/**
* The working inner variables.
*/
var
// store the actual TMP directory
_TMP = tmpDir(),
// the random characters to choose from
RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
TEMPLATE_PATTERN = /XXXXXX/,
DEFAULT_TRIES = 3,
CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,
DIR_MODE = 448 /* 0700 */,
FILE_MODE = 384 /* 0600 */,
// this will hold the objects need to be removed on exit
_removeObjects = [],
_gracefulCleanup = false,
_uncaughtException = false;
/**
* Random name generator based on crypto.
* Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
*
* @param {Number} howMany
* @return {String}
* @api private
*/
function _randomChars(howMany) {
var
value = [],
rnd = null;
// make sure that we do not fail because we ran out of entropy
try {
rnd = crypto.randomBytes(howMany);
} catch (e) {
rnd = crypto.pseudoRandomBytes(howMany);
}
for (var i = 0; i < howMany; i++) {
value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
}
return value.join('');
}
/**
* Checks whether the `obj` parameter is defined or not.
*
* @param {Object} obj
* @return {Boolean}
* @api private
*/
function _isUndefined(obj) {
return typeof obj === 'undefined';
}
/**
* Parses the function arguments.
*
* This function helps to have optional arguments.
*
* @param {Object} options
* @param {Function} callback
* @api private
*/
function _parseArguments(options, callback) {
if (typeof options == 'function') {
var
tmp = options;
options = callback || {};
callback = tmp;
} else if (typeof options == 'undefined') {
options = {};
}
return [options, callback];
}
/**
* Generates a new temporary name.
*
* @param {Object} opts
* @returns {String}
* @api private
*/
function _generateTmpName(opts) {
if (opts.name) {
return path.join(opts.dir || _TMP, opts.name);
}
// mkstemps like template
if (opts.template) {
return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
}
// prefix and postfix
var name = [
opts.prefix || 'tmp-',
process.pid,
_randomChars(12),
opts.postfix || ''
].join('');
return path.join(opts.dir || _TMP, name);
}
/**
* Gets a temporary file name.
*
* @param {Object} options
* @param {Function} callback
* @api private
*/
function _getTmpName(options, callback) {
var
args = _parseArguments(options, callback),
opts = args[0],
cb = args[1],
tries = opts.tries || DEFAULT_TRIES;
if (isNaN(tries) || tries < 0)
return cb(new Error('Invalid tries'));
if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
return cb(new Error('Invalid template provided'));
(function _getUniqueName() {
var name = _generateTmpName(opts);
// check whether the path exists then retry if needed
exists(name, function _pathExists(pathExists) {
if (pathExists) {
if (tries-- > 0) return _getUniqueName();
return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
}
cb(null, name);
});
}());
}
/**
* Synchronous version of _getTmpName.
*
* @param {Object} options
* @returns {String}
* @api private
*/
function _getTmpNameSync(options) {
var
args = _parseArguments(options),
opts = args[0],
tries = opts.tries || DEFAULT_TRIES;
if (isNaN(tries) || tries < 0)
throw new Error('Invalid tries');
if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
throw new Error('Invalid template provided');
do {
var name = _generateTmpName(opts);
if (!existsSync(name)) {
return name;
}
} while (tries-- > 0);
throw new Error('Could not get a unique tmp filename, max tries reached');
}
/**
* Creates and opens a temporary file.
*
* @param {Object} options
* @param {Function} callback
* @api public
*/
function _createTmpFile(options, callback) {
var
args = _parseArguments(options, callback),
opts = args[0],
cb = args[1];
opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;
// gets a temporary filename
_getTmpName(opts, function _tmpNameCreated(err, name) {
if (err) return cb(err);
// create and open the file
fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
if (err) return cb(err);
cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
});
});
}
/**
* Synchronous version of _createTmpFile.
*
* @param {Object} options
* @returns {Object} object consists of name, fd and removeCallback
* @api private
*/
function _createTmpFileSync(options) {
var
args = _parseArguments(options),
opts = args[0];
opts.postfix = opts.postfix || '.tmp';
var name = _getTmpNameSync(opts);
var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
return {
name : name,
fd : fd,
removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
};
}
/**
* Removes files and folders in a directory recursively.
*
* @param {String} root
* @api private
*/
function _rmdirRecursiveSync(root) {
var dirs = [root];
do {
var
dir = dirs.pop(),
deferred = false,
files = fs.readdirSync(dir);
for (var i = 0, length = files.length; i < length; i++) {
var
file = path.join(dir, files[i]),
stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories
if (stat.isDirectory()) {
if (!deferred) {
deferred = true;
dirs.push(dir);
}
dirs.push(file);
} else {
fs.unlinkSync(file);
}
}
if (!deferred) {
fs.rmdirSync(dir);
}
} while (dirs.length !== 0);
}
/**
* Creates a temporary directory.
*
* @param {Object} options
* @param {Function} callback
* @api public
*/
function _createTmpDir(options, callback) {
var
args = _parseArguments(options, callback),
opts = args[0],
cb = args[1];
// gets a temporary filename
_getTmpName(opts, function _tmpNameCreated(err, name) {
if (err) return cb(err);
// create the directory
fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
if (err) return cb(err);
cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
});
});
}
/**
* Synchronous version of _createTmpDir.
*
* @param {Object} options
* @returns {Object} object consists of name and removeCallback
* @api private
*/
function _createTmpDirSync(options) {
var
args = _parseArguments(options),
opts = args[0];
var name = _getTmpNameSync(opts);
fs.mkdirSync(name, opts.mode || DIR_MODE);
return {
name : name,
removeCallback : _prepareTmpDirRemoveCallback(name, opts)
};
}
/**
* Prepares the callback for removal of the temporary file.
*
* @param {String} name
* @param {int} fd
* @param {Object} opts
* @api private
* @returns {Function} the callback
*/
function _prepareTmpFileRemoveCallback(name, fd, opts) {
var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
try {
fs.closeSync(fdPath[0]);
}
catch (e) {
// under some node/windows related circumstances, a temporary file
// may have not be created as expected or the file was already closed
// by the user, in which case we will simply ignore the error
if (e.errno != -_c.EBADF && e.errno != -c.ENOENT) {
// reraise any unanticipated error
throw e;
}
}
fs.unlinkSync(fdPath[1]);
}, [fd, name]);
if (!opts.keep) {
_removeObjects.unshift(removeCallback);
}
return removeCallback;
}
/**
* Prepares the callback for removal of the temporary directory.
*
* @param {String} name
* @param {Object} opts
* @returns {Function} the callback
* @api private
*/
function _prepareTmpDirRemoveCallback(name, opts) {
var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
var removeCallback = _prepareRemoveCallback(removeFunction, name);
if (!opts.keep) {
_removeObjects.unshift(removeCallback);
}
return removeCallback;
}
/**
* Creates a guarded function wrapping the removeFunction call.
*
* @param {Function} removeFunction
* @param {Object} arg
* @returns {Function}
* @api private
*/
function _prepareRemoveCallback(removeFunction, arg) {
var called = false;
return function _cleanupCallback() {
if (called) return;
var index = _removeObjects.indexOf(removeFunction);
if (index >= 0) {
_removeObjects.splice(index, 1);
}
called = true;
removeFunction(arg);
};
}
/**
* The garbage collector.
*
* @api private
*/
function _garbageCollector() {
if (_uncaughtException && !_gracefulCleanup) {
return;
}
for (var i = 0, length = _removeObjects.length; i < length; i++) {
try {
_removeObjects[i].call(null);
} catch (e) {
// already removed?
}
}
}
function _setGracefulCleanup() {
_gracefulCleanup = true;
}
var version = process.versions.node.split('.').map(function (value) {
return parseInt(value, 10);
});
if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
_uncaughtException = true;
_garbageCollector();
throw err;
});
}
process.addListener('exit', function _exit(code) {
if (code) _uncaughtException = true;
_garbageCollector();
});
// exporting all the needed methods
module.exports.tmpdir = _TMP;
module.exports.dir = _createTmpDir;
module.exports.dirSync = _createTmpDirSync;
module.exports.file = _createTmpFile;
module.exports.fileSync = _createTmpFileSync;
module.exports.tmpName = _getTmpName;
module.exports.tmpNameSync = _getTmpNameSync;
module.exports.setGracefulCleanup = _setGracefulCleanup;