OpenHue MCP Server
by lsemenenko
var common = require('./common');
var fs = require('fs');
common.register('rm', _rm, {
cmdOptions: {
'f': 'force',
'r': 'recursive',
'R': 'recursive',
},
});
// Recursively removes 'dir'
// Adapted from https://github.com/ryanmcgrath/wrench-js
//
// Copyright (c) 2010 Ryan McGrath
// Copyright (c) 2012 Artur Adib
//
// Licensed under the MIT License
// http://www.opensource.org/licenses/mit-license.php
function rmdirSyncRecursive(dir, force, fromSymlink) {
var files;
files = fs.readdirSync(dir);
// Loop through and delete everything in the sub-tree after checking it
for (var i = 0; i < files.length; i++) {
var file = dir + '/' + files[i];
var currFile = common.statNoFollowLinks(file);
if (currFile.isDirectory()) { // Recursive function back to the beginning
rmdirSyncRecursive(file, force);
} else { // Assume it's a file - perhaps a try/catch belongs here?
if (force || isWriteable(file)) {
try {
common.unlinkSync(file);
} catch (e) {
/* istanbul ignore next */
common.error('could not remove file (code ' + e.code + '): ' + file, {
continue: true,
});
}
}
}
}
// if was directory was referenced through a symbolic link,
// the contents should be removed, but not the directory itself
if (fromSymlink) return;
// Now that we know everything in the sub-tree has been deleted, we can delete the main directory.
// Huzzah for the shopkeep.
var result;
try {
// Retry on windows, sometimes it takes a little time before all the files in the directory are gone
var start = Date.now();
// TODO: replace this with a finite loop
for (;;) {
try {
result = fs.rmdirSync(dir);
if (fs.existsSync(dir)) throw { code: 'EAGAIN' };
break;
} catch (er) {
/* istanbul ignore next */
// In addition to error codes, also check if the directory still exists and loop again if true
if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM' || er.code === 'EAGAIN')) {
if (Date.now() - start > 1000) throw er;
} else if (er.code === 'ENOENT') {
// Directory did not exist, deletion was successful
break;
} else {
throw er;
}
}
}
} catch (e) {
common.error('could not remove directory (code ' + e.code + '): ' + dir, { continue: true });
}
return result;
} // rmdirSyncRecursive
// Hack to determine if file has write permissions for current user
// Avoids having to check user, group, etc, but it's probably slow
function isWriteable(file) {
var writePermission = true;
try {
var __fd = fs.openSync(file, 'a');
fs.closeSync(__fd);
} catch (e) {
writePermission = false;
}
return writePermission;
}
function handleFile(file, options) {
if (options.force || isWriteable(file)) {
// -f was passed, or file is writable, so it can be removed
common.unlinkSync(file);
} else {
common.error('permission denied: ' + file, { continue: true });
}
}
function handleDirectory(file, options) {
if (options.recursive) {
// -r was passed, so directory can be removed
rmdirSyncRecursive(file, options.force);
} else {
common.error('path is a directory', { continue: true });
}
}
function handleSymbolicLink(file, options) {
var stats;
try {
stats = common.statFollowLinks(file);
} catch (e) {
// symlink is broken, so remove the symlink itself
common.unlinkSync(file);
return;
}
if (stats.isFile()) {
common.unlinkSync(file);
} else if (stats.isDirectory()) {
if (file[file.length - 1] === '/') {
// trailing separator, so remove the contents, not the link
if (options.recursive) {
// -r was passed, so directory can be removed
var fromSymlink = true;
rmdirSyncRecursive(file, options.force, fromSymlink);
} else {
common.error('path is a directory', { continue: true });
}
} else {
// no trailing separator, so remove the link
common.unlinkSync(file);
}
}
}
function handleFIFO(file) {
common.unlinkSync(file);
}
//@
//@ ### rm([options,] file [, file ...])
//@ ### rm([options,] file_array)
//@
//@ Available options:
//@
//@ + `-f`: force
//@ + `-r, -R`: recursive
//@
//@ Examples:
//@
//@ ```javascript
//@ rm('-rf', '/tmp/*');
//@ rm('some_file.txt', 'another_file.txt');
//@ rm(['some_file.txt', 'another_file.txt']); // same as above
//@ ```
//@
//@ Removes files.
function _rm(options, files) {
if (!files) common.error('no paths given');
// Convert to array
files = [].slice.call(arguments, 1);
files.forEach(function (file) {
var lstats;
try {
var filepath = (file[file.length - 1] === '/')
? file.slice(0, -1) // remove the '/' so lstatSync can detect symlinks
: file;
lstats = common.statNoFollowLinks(filepath); // test for existence
} catch (e) {
// Path does not exist, no force flag given
if (!options.force) {
common.error('no such file or directory: ' + file, { continue: true });
}
return; // skip file
}
// If here, path exists
if (lstats.isFile()) {
handleFile(file, options);
} else if (lstats.isDirectory()) {
handleDirectory(file, options);
} else if (lstats.isSymbolicLink()) {
handleSymbolicLink(file, options);
} else if (lstats.isFIFO()) {
handleFIFO(file);
}
}); // forEach(file)
return '';
} // rm
module.exports = _rm;