Commit ba1f6ac9 authored by Bas Couwenberg's avatar Bas Couwenberg

Imported Upstream version 0.0.24

parents
node_modules/
.idea/
language: node_js
node_js:
- "0.6"
- "0.8"
- "0.10"
# Tmp
A simple temporary file and directory creator for [node.js.][1]
[![Build Status](https://secure.travis-ci.org/raszi/node-tmp.png?branch=master)](http://travis-ci.org/raszi/node-tmp)
## About
The main difference between bruce's [node-temp][2] is that mine more
aggressively checks for the existence of the newly created temporary file and
creates the new file with `O_EXCL` instead of simple `O_CREAT | O_RDRW`, so it
is safer.
The API is slightly different as well, Tmp does not yet provide synchronous
calls and all the parameters are optional.
You can set whether you want to remove the temporary file on process exit or
not, and the destination directory can also be set.
## How to install
```bash
npm install tmp
```
## Usage
### File creation
Simple temporary file creation, the file will be unlinked on process exit.
```javascript
var tmp = require('tmp');
tmp.file(function _tempFileCreated(err, path, fd) {
if (err) throw err;
console.log("File: ", path);
console.log("Filedescriptor: ", fd);
});
```
### Directory creation
Simple temporary directory creation, it will be removed on process exit.
If the directory still contains items on process exit, then it won't be removed.
```javascript
var tmp = require('tmp');
tmp.dir(function _tempDirCreated(err, path) {
if (err) throw err;
console.log("Dir: ", path);
});
```
If you want to cleanup the directory even when there are entries in it, then
you can pass the `unsafeCleanup` option when creating it.
### Filename generation
It is possible with this library to generate a unique filename in the specified
directory.
```javascript
var tmp = require('tmp');
tmp.tmpName(function _tempNameGenerated(err, path) {
if (err) throw err;
console.log("Created temporary filename: ", path);
});
```
## Advanced usage
### File creation
Creates a file with mode `0644`, prefix will be `prefix-` and postfix will be `.txt`.
```javascript
var tmp = require('tmp');
tmp.file({ mode: 0644, prefix: 'prefix-', postfix: '.txt' }, function _tempFileCreated(err, path, fd) {
if (err) throw err;
console.log("File: ", path);
console.log("Filedescriptor: ", fd);
});
```
### Directory creation
Creates a directory with mode `0755`, prefix will be `myTmpDir_`.
```javascript
var tmp = require('tmp');
tmp.dir({ mode: 0750, prefix: 'myTmpDir_' }, function _tempDirCreated(err, path) {
if (err) throw err;
console.log("Dir: ", path);
});
```
### mkstemps like
Creates a new temporary directory with mode `0700` and filename like `/tmp/tmp-nk2J1u`.
```javascript
var tmp = require('tmp');
tmp.dir({ template: '/tmp/tmp-XXXXXX' }, function _tempDirCreated(err, path) {
if (err) throw err;
console.log("Dir: ", path);
});
```
### Filename generation
The `tmpName()` function accepts the `prefix`, `postfix`, `dir`, etc. parameters also:
```javascript
var tmp = require('tmp');
tmp.tmpName({ template: '/tmp/tmp-XXXXXX' }, function _tempNameGenerated(err, path) {
if (err) throw err;
console.log("Created temporary filename: ", path);
});
```
## Graceful cleanup
One may want to cleanup the temporary files even when an uncaught exception
occurs. To enforce this, you can call the `setGracefulCleanup()` method:
```javascript
var tmp = require('tmp');
tmp.setGracefulCleanup();
```
## Options
All options are optional :)
* `mode`: the file mode to create with, it fallbacks to `0600` on file creation and `0700` on directory creation
* `prefix`: the optional prefix, fallbacks to `tmp-` if not provided
* `postfix`: the optional postfix, fallbacks to `.tmp` on file creation
* `template`: [`mkstemps`][3] like filename template, no default
* `dir`: the optional temporary directory, fallbacks to system default (guesses from environment)
* `tries`: how many times should the function try to get a unique filename before giving up, default `3`
* `keep`: signals that the temporary file or directory should not be deleted on exit, default is `false`, means delete
* `unsafeCleanup`: recursively removes the created temporary directory, even when it's not empty. default is `false`
[1]: http://nodejs.org/
[2]: https://github.com/bruce/node-temp
[3]: http://www.kernel.org/doc/man-pages/online/pages/man3/mkstemp.3.html
/*!
* Tmp
*
* Copyright (c) 2011-2013 KARASZI Istvan <github@spam.raszi.hu>
*
* MIT Licensed
*/
/**
* Module dependencies.
*/
var
fs = require('fs'),
path = require('path'),
os = require('os'),
exists = fs.exists || path.exists,
tmpDir = os.tmpDir || _getTMPDir,
_c = require('constants');
/**
* The working inner variables.
*/
var
// store the actual TMP directory
_TMP = tmpDir(),
// the random characters to choose from
randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz",
randomCharsLength = randomChars.length,
// this will hold the objects need to be removed on exit
_removeObjects = [],
_gracefulCleanup = false,
_uncaughtException = false;
/**
* Gets the temp directory.
*
* @return {String}
* @api private
*/
function _getTMPDir() {
var tmpNames = [ 'TMPDIR', 'TMP', 'TEMP' ];
for (var i = 0, length = tmpNames.length; i < length; i++) {
if (_isUndefined(process.env[tmpNames[i]])) continue;
return process.env[tmpNames[i]];
}
// fallback to the default
return '/tmp';
}
/**
* 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 (!callback || typeof callback != "function") {
callback = options;
options = {};
}
return [ options, callback ];
}
/**
* Gets a temporary file name.
*
* @param {Object} opts
* @param {Function} cb
* @api private
*/
function _getTmpName(options, callback) {
var
args = _parseArguments(options, callback),
opts = args[0],
cb = args[1],
template = opts.template,
templateDefined = !_isUndefined(template),
tries = opts.tries || 3;
if (isNaN(tries) || tries < 0)
return cb(new Error('Invalid tries'));
if (templateDefined && !template.match(/XXXXXX/))
return cb(new Error('Invalid template provided'));
function _getName() {
// prefix and postfix
if (!templateDefined) {
var name = [
(_isUndefined(opts.prefix)) ? 'tmp-' : opts.prefix,
process.pid,
(Math.random() * 0x1000000000).toString(36),
opts.postfix
].join('');
return path.join(opts.dir || _TMP, name);
}
// mkstemps like template
var chars = [];
for (var i = 0; i < 6; i++) {
chars.push(randomChars.substr(Math.floor(Math.random() * randomCharsLength), 1));
}
return template.replace(/XXXXXX/, chars.join(''));
}
(function _getUniqueName() {
var name = _getName();
// 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'));
}
cb(null, name);
});
}());
}
/**
* 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, _c.O_CREAT | _c.O_EXCL | _c.O_RDWR, opts.mode || 0600, function _fileCreated(err, fd) {
if (err) return cb(err);
var removeCallback = _prepareRemoveCallback(fs.unlinkSync.bind(fs), name);
if (!opts.keep) {
_removeObjects.unshift(removeCallback);
}
cb(null, name, fd, removeCallback);
});
});
}
/**
* Removes files and folders in a directory recursively.
*
* @param {String} dir
*/
function _rmdirRecursiveSync(dir) {
var files = fs.readdirSync(dir);
for (var i = 0, length = files.length; i < length; i++) {
var file = path.join(dir, files[i]);
// lstat so we don't recurse into symlinked directories.
var stat = fs.lstatSync(file);
if (stat.isDirectory()) {
_rmdirRecursiveSync(file);
} else {
fs.unlinkSync(file);
}
}
fs.rmdirSync(dir);
}
/**
*
* @param {Function} removeFunction
* @param {String} path
* @returns {Function}
* @private
*/
function _prepareRemoveCallback(removeFunction, path) {
var called = false;
return function() {
if (called) {
return;
}
removeFunction(path);
called = true;
};
}
/**
* 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 || 0700, function _dirCreated(err) {
if (err) return cb(err);
var removeCallback = _prepareRemoveCallback(
opts.unsafeCleanup
? _rmdirRecursiveSync
: fs.rmdirSync.bind(fs),
name
);
if (!opts.keep) {
_removeObjects.unshift(removeCallback);
}
cb(null, name, removeCallback);
});
});
}
/**
* 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.file = _createTmpFile;
module.exports.tmpName = _getTmpName;
module.exports.setGracefulCleanup = _setGracefulCleanup;
{
"name": "tmp",
"version": "0.0.24",
"description": "Temporary file and directory creator",
"author": "KARASZI István <github@spam.raszi.hu> (http://raszi.hu/)",
"homepage": "http://github.com/raszi/node-tmp",
"keywords": [ "temporary", "tmp", "temp", "tempdir", "tempfile", "tmpdir", "tmpfile" ],
"licenses": [
{
"type": "MIT",
"url": "http://opensource.org/licenses/MIT"
}
],
"repository": {
"type": "git",
"url": "git://github.com/raszi/node-tmp.git"
},
"bugs": {
"url": "http://github.com/raszi/node-tmp/issues"
},
"main": "lib/tmp.js",
"scripts": {
"test": "vows test/*-test.js"
},
"engines": {
"node": ">=0.4.0"
},
"dependencies": {},
"devDependencies": {
"vows": "~0.7.0"
}
}
var
assert = require('assert'),
path = require('path'),
exec = require('child_process').exec;
function _spawnTestWithError(testFile, params, cb) {
_spawnTest(true, testFile, params, cb);
}
function _spawnTestWithoutError(testFile, params, cb) {
_spawnTest(false, testFile, params, cb);
}
function _spawnTest(passError, testFile, params, cb) {
var
filename,
node_path = process.argv[0],
command = [ node_path, path.join(__dirname, testFile) ].concat(params).join(' ');
exec(command, function _execDone(err, stdout, stderr) {
if (passError) {
if (err) {
return cb(err);
} else if (stderr.length > 0) {
return cb(stderr.toString());
}
}
return cb(null, stdout.toString());
});
}
function _testStat(stat, mode) {
assert.equal(stat.uid, process.getuid(), 'should have the same UID');
assert.equal(stat.gid, process.getgid(), 'should have the same GUID');
assert.equal(stat.mode, mode);
}
function _testPrefix(prefix) {
return function _testPrefixGenerated(err, name, fd) {
assert.equal(path.basename(name).slice(0, prefix.length), prefix, 'should have the provided prefix');
};
}
function _testPostfix(postfix) {
return function _testPostfixGenerated(err, name, fd) {
assert.equal(name.slice(name.length - postfix.length, name.length), postfix, 'should have the provided postfix');
};
}
function _testKeep(type, keep, cb) {
_spawnTestWithError('keep.js', [ type, keep ], cb);
}
function _testGraceful(type, graceful, cb) {
_spawnTestWithoutError('graceful.js', [ type, graceful ], cb);
}
function _assertName(err, name) {
assert.isString(name);
assert.isNotZero(name.length);
}
function _testUnsafeCleanup(unsafe, cb) {
_spawnTestWithoutError('unsafe.js', [ 'dir', unsafe ], cb);
}
module.exports.testStat = _testStat;
module.exports.testPrefix = _testPrefix;
module.exports.testPostfix = _testPostfix;
module.exports.testKeep = _testKeep;
module.exports.testGraceful = _testGraceful;
module.exports.assertName = _assertName;
module.exports.testUnsafeCleanup = _testUnsafeCleanup;
var
vows = require('vows'),
assert = require('assert'),
path = require('path'),
fs = require('fs'),
existsSync = fs.existsSync || path.existsSync,
tmp = require('../lib/tmp.js'),
Test = require('./base.js');
function _testDir(mode) {
return function _testDirGenerated(err, name) {
assert.ok(existsSync(name), 'should exist');
var stat = fs.statSync(name);
assert.ok(stat.isDirectory(), 'should be a directory');
Test.testStat(stat, mode);
};
}
vows.describe('Directory creation').addBatch({
'when using without parameters': {
topic: function () {
tmp.dir(this.callback);
},
'should be a directory': _testDir(040700),
'should have the default prefix': Test.testPrefix('tmp-')
},
'when using with prefix': {
topic: function () {
tmp.dir({ prefix: 'something' }, this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should be a directory': _testDir(040700),
'should have the provided prefix': Test.testPrefix('something')
},
'when using with postfix': {
topic: function () {
tmp.dir({ postfix: '.txt' }, this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should be a directory': _testDir(040700),
'should have the provided postfix': Test.testPostfix('.txt')
},
'when using template': {
topic: function () {
tmp.dir({ template: path.join(tmp.tmpdir, 'clike-XXXXXX-postfix') }, this.callback);
},
'should not return with error': assert.isNull,
'should return with a name': Test.assertName,
'should be a file': _testDir(040700),
'should have the provided prefix': Test.testPrefix('clike-'),
'should have the provided postfix': Test.testPostfix('-postfix')
},
'when using multiple options': {
topic: function () {
tmp.dir({ prefix: 'foo', postfix: 'bar', mode: 0750 }, this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should be a directory': _testDir(040750),
'should have the provided prefix': Test.testPrefix('foo'),
'should have the provided postfix': Test.testPostfix('bar')
},
'when using multiple options and mode': {
topic: function () {
tmp.dir({ prefix: 'complicated', postfix: 'options', mode: 0755 }, this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should be a directory': _testDir(040755),
'should have the provided prefix': Test.testPrefix('complicated'),
'should have the provided postfix': Test.testPostfix('options')
},
'no tries': {
topic: function () {
tmp.dir({ tries: -1 }, this.callback);
},
'should return with an error': assert.isObject
},
'keep testing': {
topic: function () {
Test.testKeep('dir', '1', this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should be a dir': function (err, name) {
_testDir(040700)(err, name);
fs.rmdirSync(name);
}
},
'unlink testing': {
topic: function () {
Test.testKeep('dir', '0', this.callback);
},
'should not return with error': assert.isNull,
'should return with a name': Test.assertName,
'should not exist': function (err, name) {
assert.ok(!existsSync(name), "Directory should be removed");
}
},
'non graceful testing': {
topic: function () {
Test.testGraceful('dir', '0', this.callback);
},
'should not return with error': assert.isNull,
'should return with a name': Test.assertName,
'should be a dir': function (err, name) {
_testDir(040700)(err, name);
fs.rmdirSync(name);
}
},
'graceful testing': {
topic: function () {
Test.testGraceful('dir', '1', this.callback);
},
'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should not exist': function (err, name) {
assert.ok(!existsSync(name), "Directory should be removed");
}
},