[file.lua] combined the is_ functions into file_test() which allows

testing to see if a path exists, is a dir, is a file, or
	   is executable.  Added case insensitive search to the
           windows search for bin.
This commit is contained in:
Bill Ferguson
2020-11-29 18:00:47 -05:00
parent 7790c9700b
commit da05748393

View File

@@ -35,14 +35,54 @@ local function _(msgid)
return gettext.dgettext("dtutils.file", msgid)
end
dtutils_file.libdoc.functions["is_dir"] = {
Name = [[is_dir]],
Synopsis = [[check if a path is a directory]],
--[[
local function to run a test command on windows and return a true if it succeeds
instead of returning true if it runs
]]
local function _win_os_execute(cmd)
local result = nil
local p = io.popen(cmd)
local output = p:read("*a")
p:close()
if string.match(output, "true") then
result = true
else
result = false
end
return result
end
--[[
local function to determine if a path name is a windows executable
]]
local function _is_windows_executable(path)
local result = false
if dtutils_file.test_file(path, "f") then
if string.match(path, ".exe$") or string.match(path, ".EXE$") or
string.match(path, ".com$") or string.match(path, ".COM$") or
string.match(path, ".bat$") or string.match(path, ".BAT$") or
string.match(path, ".cmd$") or string.match(path, ".CMD$") then
result = true
end
end
return result
end
dtutils_file.libdoc.functions["test_file"] = {
Name = [[test_file]],
Synopsis = [[test a file to see what it is]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.is_dir(path)
path - string - the path to check]],
Description = [[is_dir checks a path to see if it is a directory]],
local result = df.test_file(path, test)
path - string - the path to check
test - one of d, e, f, x where
d - directory
e - exists
f - file
x - executable]],
Description = [[test_file checks a path to see if it is a directory]],
Return_Value = [[result - boolean - true if path is a directory, nil if not]],
Limitations = [[]],
Example = [[]],
@@ -52,126 +92,62 @@ dtutils_file.libdoc.functions["is_dir"] = {
Copyright = [[]],
}
function dtutils_file.is_dir(path)
local cmd = nil
function dtutils_file.test_file(path, test)
local cmd = "test -"
local engine = os.execute
local cmdstring = ""
if dt.configuration.running_os == "windows" then
cmd = "if exist " .. ds.sanitize(path .. "\\*")
else
cmd = "test -d " .. ds.sanitize(path)
cmd = "if exist "
engine = _win_os_execute
end
return os.execute(cmd)
end
dtutils_file.libdoc.functions["is_file"] = {
Name = [[is_file]],
Synopsis = [[check if a path is a file]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.is_file(path)
path - string - the path to check]],
Description = [[is_file checks a path to see if it is a file]],
Return_Value = [[result - boolean - true if path is a file, nil if not]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.is_file(path)
local cmd = nil
local result = false
if dt.configuration.running_os == "windows" then
if not dtutils_file.is_dir(path) then
cmd = "if exist " .. ds.sanitize(path) .. " echo true"
local p = io.popen(cmd)
output = p:read("*a")
p:close()
if string.match(output, "true") then
result = true
if test == "d" then
-- test if directory
if dt.configuration.running_os == "windows" then
cmdstring = cmd .. dtutils_file.sanitize_filename(path .. "\\*") .. " echo true"
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "e" then
-- test exists
if dt.configuration.running_os == "windows" then
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "f" then
-- test if file
if dt.configuration.running_os == "windows" then
if not dtutils_file.test_file(path, "d") then -- make sure it's not a directory
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
else
result = false
return false
end
else
return false
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
elseif test == "x" then
-- test executable
if dt.configuration.running_os == "windows" then
return _is_windows_executable(path)
else
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
end
else
result = os.execute("test -f " .. ds.sanitize(path))
dt.print_error("[test_file] unknown test " .. test)
return false
end
return result
return engine(cmdstring)
end
dtutils_file.libdoc.functions["is_executable"] = {
Name = [[is_executable]],
Synopsis = [[check if a path is a executable]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.is_executable(path)
path - string - the path to check]],
Description = [[is_executable checks a path to see if it is an executable]],
Return_Value = [[result - boolean - true if path is an executable, nil if not]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
local function _is_windows_executable(path)
local result = nil
dt.print_log("checking " .. path)
if string.match(path, ".exe$") or string.match(path, ".EXE$") or
string.match(path, ".com$") or string.match(path, ".COM$") or
string.match(path, ".bat$") or string.match(path, ".BAT$") or
string.match(path, ".cmd$") or string.match(path, ".CMD$") then
result = true
end
return result
local function _case_insensitive_pattern(pattern)
return pattern:gsub("(.)", function(letter)
return string.format("[%s$s]", letter:lower(), letter:upper())
end)
end
function dtutils_file.is_executable(path)
local result = nil
if dt.configuration.running_os == "windows" then
if _is_windows_executable(path) then
result = true
end
else
result = os.execute("test -x " .. ds.sanitize(path))
end
return result
end
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
Name = [[check_if_bin_exists]],
Synopsis = [[check if an executable exists]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.check_if_bin_exists(bin)
bin - string - the binary to check for]],
Description = [[check_if_bin_exists checks to see if the specified binary exists.
check_if_bin_exists first checks to see if a preference for the binary has been
registered and uses that if found. The presence of the file is verified, then
quoted and returned. If no preference is specified and the operating system is
linux then the which command is used to check for a binary in the path. If found
that path is returned. If no binary is found, false is returned.]],
Return_Value = [[result - string - the sanitized path of the binary, false if not found]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
local function _search_for_bin_windows(bin)
local result = false
-- use where on path
@@ -185,9 +161,11 @@ local function _search_for_bin_windows(bin)
local output = p:read("*a")
p:close()
local lines = du.split(output, "\n")
local cibin = _case_insensitive_pattern(bin)
for _,line in ipairs(lines) do
if string.match(line, bin) then
if dtutils_file.is_file(line) and dtutils_file.is_executable(line) then
if string.match(line, cibin) then
dt.print_log("found win search match " .. line)
if dtutils_file.test_file(line, "f") and dtutils_file.test_file(line, "x") then
dtutils_file.set_executable_path_preference(bin, line) -- save it so we don't have to search again
return line
end
@@ -204,7 +182,7 @@ local function _search_for_bin_nix(bin)
p:close()
if string.len(output) > 0 then
local spath = dtutils_file.sanitize_filename(output:sub(1,-2))
if dtutils_file.is_file(spath) and dtutils_file.is_executable(spath) then
if dtutils_file.test_file(spath, "f") and dtutils_file.test_file(spath, "x") then
result = spath
end
end
@@ -230,7 +208,7 @@ local function _search_for_bin_macos(bin)
for _,line in ipairs(lines) do
local spath = dtutils_file.sanitize_filename(line:sub(1, -2))
if dtutils_file.is_executable(spath) then
if dtutils_file.test_file(spath, "x") then
dtutils_file.set_executable_path_preference(bin, spath)
result = spath
end
@@ -245,6 +223,9 @@ local function _search_for_bin(bin)
if dt.configuration.running_os == "windows" then
result = _search_for_bin_windows(bin)
if result then
result = dtutils_file.sanitize_filename(result)
end
elseif dt.configuration.running_os == "macos" then
result = _search_for_bin_macos(bin)
else
@@ -279,13 +260,13 @@ local function _check_path_for_bin(bin)
else
path = dtutils_file.get_executable_path_preference(bin)
-- reset path preference is the returned preference is a directory
if dtutils_file.is_dir(path) then
if dtutils_file.test_file(path, "d") then
dtutils_file.set_executable_path_preference(bin, "")
path = nil
end
end
if path and dtutils_file.is_dir(path) then
if path and dtutils_file.test_file(path, "d") then
path = nil
end
@@ -294,7 +275,7 @@ local function _check_path_for_bin(bin)
end
if path and not result then
if dtutils_file.is_executable(path) then
if dtutils_file.test_file(path, "x") then
result = dtutils_file.sanitize_filename(path)
end
end
@@ -302,15 +283,69 @@ local function _check_path_for_bin(bin)
return result
end
local function _old_check_if_bin_exists(bin) -- only run on windows if preference checked
local result = false
local path = nil
if string.match(bin, "\\") then
path = bin
else
path = dtutils_file.get_executable_path_preference(bin)
end
if string.len(path) > 0 then
if dtutils_file.check_if_file_exists(path) then
if (string.match(path, ".exe$") or string.match(path, ".EXE$")) then
result = dtutils_file.sanitize_filename(path)
end
end
end
return result
end
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
Name = [[check_if_bin_exists]],
Synopsis = [[check if an executable exists]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.check_if_bin_exists(bin)
bin - string - the binary to check for]],
Description = [[check_if_bin_exists checks to see if the specified binary exists.
check_if_bin_exists first checks to see if a preference for the binary has been
registered and uses that if found, after it's verified to be an executable and
exist. If no preference exissts, the user's path is checked for the executable.
If the executable is not found in the users path, then a search of the operating
system is conducted to see if the executable can be found.
If an executalble is found, it's verified to exist and be an executable. Once
the executable is verified, the path is saved as a preference to speed up
subsequent checks. The executable path is sanitized and returned.
If no executable is found, false is returned.]],
Return_Value = [[result - string - the sanitized path of the binary, false if not found]],
Limitations = [[If more than one executable that satisfies the search results is found, the
wrong one may be returned. If the wrong value is returned, the user can still specify the
correct execuable using tools/executable_manager.]],
Example = [[]],
See_Also = [[executable_manager]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.check_if_bin_exists(bin)
local result = false
result = _check_path_for_bin(bin)
if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then
result = _old_check_if_bin_exists(bin)
else
if not result then
result = _search_for_bin(bin)
result = _check_path_for_bin(bin)
if not result then
result = _search_for_bin(bin)
end
end
return result
end
@@ -856,5 +891,21 @@ function dtutils_file.rmdir(path)
return dsys.external_command(rm_cmd.." "..path)
end
--[[
The new check_if_bin_exists() does multiple calls to the operating system to check
if the file exists and is an executable. On windows, each call to the operating system
causes a window to open in order to run the command, then the window closes when the
command exits. If the user gets annoyed by the "flickering windows", then they can
enable this preference to use the old check_if_bin_exists() that relys on the
executable path preferences and doesn't do as many checks.
]]
if dt.configuration.running_os == "windows" then
dt.preferences.register("dtutils.file", "use_old_check_if_bin_exists", "bool",
"lua scripts use old check_if_bin_exists()",
"lessen flickering windows effect when scripts run",
false)
end
return dtutils_file