| local fs = require("filesystem") |
| local shell = require("shell") |
| |
| local function usage() |
| print("Usage: rm [options] <filename1> [<filename2> [...]]"..[[ |
| |
| -f ignore nonexistent files and arguments, never prompt |
| -r remove directories and their contents recursively |
| -v explain what is being done |
| --help display this help and exit |
| |
| For complete documentation and more options, run: man rm]]) |
| end |
| |
| local args, options = shell.parse(...) |
| if #args == 0 or options.help then |
| usage() |
| return 1 |
| end |
| |
| local bRec = options.r or options.R or options.recursive |
| local bForce = options.f or options.force |
| local bVerbose = options.v or options.verbose |
| local bEmptyDirs = options.d or options.dir |
| local promptLevel = (options.I and 3) or (options.i and 1) or 0 |
| |
| local metas = {} |
| |
| -- promptLevel 3 done before fs.exists |
| -- promptLevel 1 asks for each, displaying fs.exists on hit as it visits |
| |
| local function _path(m) return shell.resolve(m.rel) end |
| local function _link(m) return fs.isLink(_path(m)) end |
| local function _exists(m) return _link(m) or fs.exists(_path(m)) end |
| local function _dir(m) return not _link(m) and fs.isDirectory(_path(m)) end |
| local function _readonly(m) return not _exists(m) or fs.get(_path(m)).isReadOnly() end |
| local function _empty(m) return _exists(m) and _dir(m) and (fs.list(_path(m))==nil) end |
| |
| local function createMeta(origin, rel) |
| local m = {origin=origin,rel=rel:gsub("/+$", "")} |
| if _dir(m) then |
| m.rel = m.rel .. '/' |
| end |
| return m |
| end |
| |
| local function unlink(path) |
| os.remove(path) |
| return true |
| end |
| |
| local function confirm() |
| local r = io.read("*l") |
| return r == 'y' or r == 'yes' |
| end |
| |
| local function remove_all(parent) |
| if parent == nil or not _dir(parent) or _empty(parent) then |
| return true |
| end |
| |
| local all_ok = true |
| if bRec and promptLevel == 1 then |
| io.stdout:write(string.format("rm: descend into directory `%s'? ", parent.rel)) |
| if not confirm() then |
| return false |
| end |
| |
| for file in fs.list(_path(parent)) do |
| local child = createMeta(parent.origin, parent.rel .. file) |
| all_ok = remove(child) and all_ok |
| end |
| end |
| |
| return all_ok |
| end |
| |
| local function remove(meta) |
| if not remove_all(meta) then |
| return false |
| end |
| |
| if not _exists(meta) then |
| io.stderr:write( |
| string.format("rm: cannot remove `%s': No such file or directory\n", meta.rel)) |
| return false |
| elseif _dir(meta) and not bRec and not (_empty(meta) and bEmptyDirs) then |
| if not bEmptyDirs then |
| io.stderr:write( |
| string.format("rm: cannot remove `%s': Is a directory\n", meta.rel)) |
| else |
| io.stderr:write( |
| string.format("rm: cannot remove `%s': Directory not empty\n", meta.rel)) |
| end |
| return false |
| end |
| |
| local ok = true |
| if promptLevel == 1 then |
| if _dir(meta) then |
| io.stdout:write(string.format("rm: remove directory `%s'? ", meta.rel)) |
| elseif meta.link then |
| io.stdout:write(string.format("rm: remove symbolic link `%s'? ", meta.rel)) |
| else -- file |
| io.stdout:write(string.format("rm: remove regular file `%s'? ", meta.rel)) |
| end |
| |
| ok = confirm() |
| end |
| |
| if ok then |
| if _readonly(meta) then |
| io.stderr:write( |
| string.format("rm: cannot remove `%s': Is read only\n", meta.rel)) |
| return false |
| elseif not unlink(_path(meta)) then |
| io.stderr:write(meta.rel .. ": failed to be removed\n") |
| ok = false |
| elseif bVerbose then |
| io.write("removed '" .. meta.rel .. "'\n"); |
| end |
| end |
| |
| return ok |
| end |
| |
| for _,arg in ipairs(args) do |
| metas[#metas+1] = createMeta(arg, arg) |
| end |
| |
| if promptLevel == 3 and #metas > 3 then |
| io.stdout:write(string.format("rm: remove %i arguments? ", #metas)) |
| if not confirm() then |
| return |
| end |
| end |
| |
| local ok = true |
| for _,meta in ipairs(metas) do |
| local result = remove(meta) |
| ok = ok and result |
| end |