testing version of LuaBIOS and OpenOS
people were having issues getting them to work so now we promote consistency
This commit is contained in:
31
data/OpenOS/lib/tools/programLocations.lua
Normal file
31
data/OpenOS/lib/tools/programLocations.lua
Normal file
@@ -0,0 +1,31 @@
|
||||
local computer = require("computer")
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local lib = {}
|
||||
|
||||
function lib.locate(path)
|
||||
for _,lookup in ipairs(computer.getProgramLocations()) do
|
||||
if lookup[1] == path then
|
||||
return lookup[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib.reportNotFound(path, reason)
|
||||
checkArg(1, path, "string")
|
||||
if fs.isDirectory(shell.resolve(path)) then
|
||||
io.stderr:write(path .. ": is a directory\n")
|
||||
return 126
|
||||
end
|
||||
local loot = lib.locate(path)
|
||||
if loot then
|
||||
io.stderr:write("The program '" .. path .. "' is currently not installed. To install it:\n" ..
|
||||
"1. Craft the '" .. loot .. "' floppy disk and insert it into this computer.\n" ..
|
||||
"2. Run `install " .. loot .. "`")
|
||||
elseif type(reason) == "string" then
|
||||
io.stderr:write(path .. ": " .. reason .. "\n")
|
||||
end
|
||||
return 127
|
||||
end
|
||||
|
||||
return lib
|
||||
272
data/OpenOS/lib/tools/transfer.lua
Normal file
272
data/OpenOS/lib/tools/transfer.lua
Normal file
@@ -0,0 +1,272 @@
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local lib = {}
|
||||
|
||||
local function perr(ops, format, ...)
|
||||
if format then
|
||||
io.stderr:write(ops.cmd .. string.format(": " .. format, ...) .. "\n")
|
||||
ops.exit_code = 1
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
local function contents_check(arg, options, bMustExist)
|
||||
if arg == "" then
|
||||
return perr(options, "cannot create regular file '' No such file or directory")
|
||||
end
|
||||
local path = shell.resolve(arg)
|
||||
local content_pattern = "^(%.*)(.?)"
|
||||
local contents_of, of_dir = arg:reverse():match(content_pattern)
|
||||
of_dir = of_dir:match("^/?$")
|
||||
local dots = contents_of and contents_of:len() or 0
|
||||
contents_of = of_dir and ({true,true})[dots]
|
||||
|
||||
if (not bMustExist or fs.exists(path)) and of_dir and not fs.isDirectory(path) then
|
||||
perr(options, "'%s' is not a directory", arg)
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
return contents_of, path
|
||||
end
|
||||
|
||||
local function areEqual(path1, path2)
|
||||
local f1, f2 = fs.open(path1, "rb")
|
||||
local result = true
|
||||
if f1 then
|
||||
f2 = fs.open(path2, "rb")
|
||||
if f2 then
|
||||
local chunkSize = 4 * 1024
|
||||
repeat
|
||||
local s1, s2 = f1:read(chunkSize), f2:read(chunkSize)
|
||||
if s1 ~= s2 then
|
||||
result = false
|
||||
break
|
||||
end
|
||||
until not s1 or not s2
|
||||
f2:close()
|
||||
end
|
||||
f1:close()
|
||||
end
|
||||
assert(f1 and f2, "could not open files for reading: " .. path1 .. ", " .. path2)
|
||||
return result
|
||||
end
|
||||
|
||||
local function status(verbose, from, to)
|
||||
if verbose then
|
||||
to = to and (" -> " .. to) or ""
|
||||
io.write(from .. to .. "\n")
|
||||
end
|
||||
os.sleep(0) -- allow interrupting
|
||||
end
|
||||
|
||||
local function prompt(message)
|
||||
io.write(message .. " [Y/n] ")
|
||||
local result = io.read()
|
||||
if not result then -- closed pipe
|
||||
os.exit(1)
|
||||
end
|
||||
return result and (result == "" or result:sub(1, 1):lower() == "y")
|
||||
end
|
||||
|
||||
local function stat(path, ops, P)
|
||||
local real, reason = fs.realPath(path)
|
||||
if not real and not P then
|
||||
perr(ops, "cannot read '%s': '%s'", path, reason)
|
||||
return false
|
||||
end
|
||||
local isLink, linkTarget = fs.isLink(path)
|
||||
return true,
|
||||
real,
|
||||
reason,
|
||||
isLink,
|
||||
linkTarget,
|
||||
fs.exists(path),
|
||||
fs.get(path),
|
||||
real and fs.isDirectory(real)
|
||||
end
|
||||
|
||||
function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
fromPath = fromPath:gsub("/+", "/")
|
||||
toPath = toPath:gsub("/+", "/")
|
||||
local fromPathFull = shell.resolve(fromPath)
|
||||
local toPathFull = shell.resolve(toPath)
|
||||
local mv = options.cmd == "mv"
|
||||
local verbose = options.v and (not mv or top)
|
||||
if options.skip[fromPathFull] then
|
||||
status(verbose, string.format("skipping %s", fromPath))
|
||||
return true
|
||||
end
|
||||
local function release(result, reason)
|
||||
if result and mv and top then
|
||||
local rm_result = not fs.get(fromPathFull).isReadOnly() and fs.remove(fromPathFull)
|
||||
if not rm_result then
|
||||
perr(options, "cannot remove '%s': filesystem is readonly", fromPath)
|
||||
result = false
|
||||
end
|
||||
end
|
||||
return result, reason
|
||||
end
|
||||
|
||||
local
|
||||
ok,
|
||||
fromReal,
|
||||
_, --fromError,
|
||||
fromIsLink,
|
||||
fromLinkTarget,
|
||||
fromExists,
|
||||
fromFs,
|
||||
fromIsDir = stat(fromPathFull, options, options.P)
|
||||
if not ok then return nil end
|
||||
local
|
||||
ok,
|
||||
toReal,
|
||||
_,--toError,
|
||||
toIsLink,
|
||||
_,--toLinkTarget,
|
||||
toExists,
|
||||
toFs,
|
||||
toIsDir = stat(toPathFull, options)
|
||||
if not ok then os.exit(1) end
|
||||
if toFs.isReadOnly() then
|
||||
perr(options, "cannot create target '%s': filesystem is readonly", toPath)
|
||||
return
|
||||
end
|
||||
|
||||
local same_path = fromReal == toReal
|
||||
|
||||
local same_fs = fromFs == toFs
|
||||
local is_mount = origin[fromReal]
|
||||
|
||||
if mv and is_mount then
|
||||
return false, string.format("cannot move '%s', it is a mount point", fromPath)
|
||||
end
|
||||
|
||||
if fromIsLink and options.P and not (toExists and same_path and not toIsLink) then
|
||||
if toExists and options.n then
|
||||
return true
|
||||
end
|
||||
fs.remove(toPathFull)
|
||||
if toExists then
|
||||
status(verbose, string.format("removed '%s'", toPath))
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
return release(fs.link(fromLinkTarget, toPathFull))
|
||||
elseif fromIsDir then
|
||||
if not options.r then
|
||||
status(true, string.format("omitting directory '%s'", fromPath))
|
||||
options.exit_code = 1
|
||||
return true
|
||||
end
|
||||
if toExists and not toIsDir then
|
||||
-- my real cp always does this, even with -f, -n or -i.
|
||||
return nil, "cannot overwrite non-directory '" .. toPath .. "' with directory '" .. fromPath .. "'"
|
||||
end
|
||||
if options.x and not top and is_mount then
|
||||
return true
|
||||
end
|
||||
if same_fs then
|
||||
if (toReal.."/"):find(fromReal.."/",1,true) then
|
||||
return nil, "cannot write a directory, '" .. fromPath .. "', into itself, '" .. toPath .. "'"
|
||||
end
|
||||
end
|
||||
if mv then
|
||||
if fs.list(toReal)() then -- to is NOT empty
|
||||
return nil, "cannot move '" .. fromPath .. "' to '" .. toPath .. "': Directory not empty"
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
end
|
||||
if not toExists then
|
||||
status(verbose, fromPath, toPath)
|
||||
fs.makeDirectory(toPathFull)
|
||||
end
|
||||
for file in fs.list(fromPathFull) do
|
||||
local result, reason = lib.recurse(fromPath .."/".. file, toPath.."/"..file, options, origin, false) -- false, no longer top
|
||||
if not result then
|
||||
return false, reason
|
||||
end
|
||||
end
|
||||
return release(true)
|
||||
elseif fromExists then
|
||||
if toExists then
|
||||
if same_path then
|
||||
return nil, "'" .. fromPath .. "' and '" .. toPath .. "' are the same file"
|
||||
end
|
||||
if options.n then
|
||||
return true
|
||||
end
|
||||
if options.u and not toIsDir and areEqual(fromReal, toReal) then
|
||||
return true
|
||||
end
|
||||
if options.i then
|
||||
if not prompt("overwrite '" .. toPath .. "'?") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if toIsDir then
|
||||
return nil, "cannot overwrite directory '" .. toPath .. "' with non-directory"
|
||||
end
|
||||
fs.remove(toReal)
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
return release(fs.copy(fromPathFull, toPathFull))
|
||||
else
|
||||
return nil, "'" .. fromPath .. "': No such file or directory"
|
||||
end
|
||||
end
|
||||
|
||||
function lib.batch(args, options)
|
||||
options.exit_code = 0
|
||||
|
||||
-- standardized options
|
||||
options.i = options.i and not options.f
|
||||
options.P = options.P or options.r
|
||||
|
||||
local skips = options.skip or {}
|
||||
options.skip = {}
|
||||
for _, skip_item in ipairs(skips) do
|
||||
options.skip[shell.resolve(skip_item)] = true
|
||||
end
|
||||
|
||||
local origin = {}
|
||||
for dev,path in fs.mounts() do
|
||||
origin[path] = dev
|
||||
end
|
||||
|
||||
local toArg = table.remove(args)
|
||||
local _, ok = contents_check(toArg, options)
|
||||
if not ok then
|
||||
return 1
|
||||
end
|
||||
local originalToIsDir = fs.isDirectory(ok)
|
||||
|
||||
for _, fromArg in ipairs(args) do
|
||||
-- a "contents of" copy is where src path ends in . or ..
|
||||
-- a source path ending with . is not sufficient - could be the source filename
|
||||
local contents_of
|
||||
contents_of, ok = contents_check(fromArg, options, true)
|
||||
if ok then
|
||||
-- we do not append fromPath name to toPath in case of contents_of copy
|
||||
local toPath = toArg
|
||||
if contents_of and options.cmd == "mv" then
|
||||
perr(options, "invalid move path '%s'", fromArg)
|
||||
else
|
||||
if not contents_of and originalToIsDir then
|
||||
local fromName = fs.name(fromArg)
|
||||
if fromName then
|
||||
toPath = toPath .. "/" .. fromName
|
||||
end
|
||||
end
|
||||
|
||||
local result, reason = lib.recurse(fromArg, toPath, options, origin, true)
|
||||
|
||||
if not result then
|
||||
perr(options, reason)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return options.exit_code
|
||||
end
|
||||
|
||||
return lib
|
||||
Reference in New Issue
Block a user