| var common = require('./common'); |
| var fs = require('fs'); |
| |
| // 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) { |
| 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], |
| currFile = fs.lstatSync(file); |
| |
| if(currFile.isDirectory()) { // Recursive function back to the beginning |
| rmdirSyncRecursive(file, force); |
| } |
| |
| else if(currFile.isSymbolicLink()) { // Unlink symlinks |
| if (force || isWriteable(file)) { |
| try { |
| common.unlinkSync(file); |
| } catch (e) { |
| common.error('could not remove file (code '+e.code+'): ' + file, true); |
| } |
| } |
| } |
| |
| else // Assume it's a file - perhaps a try/catch belongs here? |
| if (force || isWriteable(file)) { |
| try { |
| common.unlinkSync(file); |
| } catch (e) { |
| common.error('could not remove file (code '+e.code+'): ' + file, true); |
| } |
| } |
| } |
| |
| // 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 { |
| result = fs.rmdirSync(dir); |
| } catch(e) { |
| common.error('could not remove directory (code '+e.code+'): ' + dir, 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; |
| } |
| |
| //@ |
| //@ ### 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. The wildcard `*` is accepted. |
| function _rm(options, files) { |
| options = common.parseOptions(options, { |
| 'f': 'force', |
| 'r': 'recursive', |
| 'R': 'recursive' |
| }); |
| if (!files) |
| common.error('no paths given'); |
| |
| if (typeof files === 'string') |
| files = [].slice.call(arguments, 1); |
| // if it's array leave it as it is |
| |
| files = common.expand(files); |
| |
| files.forEach(function(file) { |
| if (!fs.existsSync(file)) { |
| // Path does not exist, no force flag given |
| if (!options.force) |
| common.error('no such file or directory: '+file, true); |
| |
| return; // skip file |
| } |
| |
| // If here, path exists |
| |
| var stats = fs.lstatSync(file); |
| if (stats.isFile() || stats.isSymbolicLink()) { |
| |
| // Do not check for file writing permissions |
| if (options.force) { |
| common.unlinkSync(file); |
| return; |
| } |
| |
| if (isWriteable(file)) |
| common.unlinkSync(file); |
| else |
| common.error('permission denied: '+file, true); |
| |
| return; |
| } // simple file |
| |
| // Path is an existing directory, but no -r flag given |
| if (stats.isDirectory() && !options.recursive) { |
| common.error('path is a directory', true); |
| return; // skip path |
| } |
| |
| // Recursively remove existing directory |
| if (stats.isDirectory() && options.recursive) { |
| rmdirSyncRecursive(file, options.force); |
| } |
| }); // forEach(file) |
| } // rm |
| module.exports = _rm; |