6 Commits

Author SHA1 Message Date
wpferguson
96ba075e86 Removed debugging statement 2020-12-22 22:00:41 -05:00
Bill Ferguson
e86a3a3236 [file.lua] removed debugging statement 2020-12-18 11:08:12 -05:00
Bill Ferguson
b41b2a1566 [file.lua] Added create_tmp_file() back in. 2020-11-30 21:51:55 -05:00
Bill Ferguson
b893383353 [file.lua] Added comments for local functions. Added more API
documentation for check_if_bin_exists.  Added save
           executable path preference to _search_for_nix_bin()
           so that we can save a little time on subsequent runs
           once an executable is found.
2020-11-30 21:06:57 -05:00
Bill Ferguson
ddcf3ed3f4 [file.lua] fixed line trimming in macos search for bin 2020-11-29 18:41:32 -05:00
Bill Ferguson
da05748393 [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.
2020-11-29 18:00:47 -05:00

View File

@@ -35,14 +35,54 @@ local function _(msgid)
return gettext.dgettext("dtutils.file", msgid) return gettext.dgettext("dtutils.file", msgid)
end end
dtutils_file.libdoc.functions["is_dir"] = { --[[
Name = [[is_dir]], local function to run a test command on windows and return a true if it succeeds
Synopsis = [[check if a path is a directory]], 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" Usage = [[local df = require "lib/dtutils.file"
local result = df.is_dir(path) local result = df.test_file(path, test)
path - string - the path to check]], path - string - the path to check
Description = [[is_dir checks a path to see if it is a directory]], 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]], Return_Value = [[result - boolean - true if path is a directory, nil if not]],
Limitations = [[]], Limitations = [[]],
Example = [[]], Example = [[]],
@@ -52,125 +92,71 @@ dtutils_file.libdoc.functions["is_dir"] = {
Copyright = [[]], Copyright = [[]],
} }
function dtutils_file.is_dir(path) function dtutils_file.test_file(path, test)
local cmd = nil local cmd = "test -"
local engine = os.execute
local cmdstring = ""
if dt.configuration.running_os == "windows" then if dt.configuration.running_os == "windows" then
cmd = "if exist " .. ds.sanitize(path .. "\\*") cmd = "if exist "
else engine = _win_os_execute
cmd = "test -d " .. ds.sanitize(path)
end end
return os.execute(cmd) if test == "d" then
end -- test if directory
if dt.configuration.running_os == "windows" then
dtutils_file.libdoc.functions["is_file"] = { cmdstring = cmd .. dtutils_file.sanitize_filename(path .. "\\*") .. " echo true"
Name = [[is_file]], else
Synopsis = [[check if a path is a file]], cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
Usage = [[local df = require "lib/dtutils.file" end
elseif test == "e" then
local result = df.is_file(path) -- test exists
path - string - the path to check]], if dt.configuration.running_os == "windows" then
Description = [[is_file checks a path to see if it is a file]], cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
Return_Value = [[result - boolean - true if path is a file, nil if not]], else
Limitations = [[]], cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
Example = [[]], end
See_Also = [[]], elseif test == "f" then
Reference = [[]], -- test if file
License = [[]], if dt.configuration.running_os == "windows" then
Copyright = [[]], 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"
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
else else
result = false return false
end end
else 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 end
else else
result = os.execute("test -f " .. ds.sanitize(path)) dt.print_error("[test_file] unknown test " .. test)
return false
end end
return result return engine(cmdstring)
end end
dtutils_file.libdoc.functions["is_executable"] = { --[[
Name = [[is_executable]], local function to return a case insensitive pattern for matching
Synopsis = [[check if a path is a executable]], i.e. gimp becomes [Gg][Ii][Mm][Pp] which should match any capitalization
Usage = [[local df = require "lib/dtutils.file" of gimp.
]]
local result = df.is_executable(path) local function _case_insensitive_pattern(pattern)
path - string - the path to check]], return pattern:gsub("(.)", function(letter)
Description = [[is_executable checks a path to see if it is an executable]], return string.format("[%s$s]", letter:lower(), letter:upper())
Return_Value = [[result - boolean - true if path is an executable, nil if not]], end)
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
end end
function dtutils_file.is_executable(path) --[[
local function to search windows for an executable
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 function _search_for_bin_windows(bin)
local result = false local result = false
@@ -185,9 +171,11 @@ local function _search_for_bin_windows(bin)
local output = p:read("*a") local output = p:read("*a")
p:close() p:close()
local lines = du.split(output, "\n") local lines = du.split(output, "\n")
local cibin = _case_insensitive_pattern(bin)
for _,line in ipairs(lines) do for _,line in ipairs(lines) do
if string.match(line, bin) then if string.match(line, cibin) then
if dtutils_file.is_file(line) and dtutils_file.is_executable(line) 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 dtutils_file.set_executable_path_preference(bin, line) -- save it so we don't have to search again
return line return line
end end
@@ -197,24 +185,33 @@ local function _search_for_bin_windows(bin)
return result return result
end end
--[[
local function to search *nix systems for an executable
]]
local function _search_for_bin_nix(bin) local function _search_for_bin_nix(bin)
local result = false local result = false
local p = io.popen("which " .. bin) local p = io.popen("which " .. bin)
local output = p:read("*a") local output = p:read("*a")
p:close() p:close()
if string.len(output) > 0 then if string.len(output) > 0 then
local spath = dtutils_file.sanitize_filename(output:sub(1,-2)) 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
dtutils_file.set_executable_path_preference(bin, spath)
result = spath result = spath
end end
end end
return result return result
end end
--[[
local function to search macos systems for an executable
]]
local function _search_for_bin_macos(bin) local function _search_for_bin_macos(bin)
local result = false local result = false
result = _search_for_bin_nix(bin) result = _search_for_bin_nix(bin) -- see if it's in the path
if not result then if not result then
local search_start = "/Applications" local search_start = "/Applications"
@@ -229,9 +226,9 @@ local function _search_for_bin_macos(bin)
local lines = du.split(output, "\n") local lines = du.split(output, "\n")
for _,line in ipairs(lines) do for _,line in ipairs(lines) do
local spath = dtutils_file.sanitize_filename(line:sub(1, -2)) local spath = dtutils_file.sanitize_filename(line:sub(1, -1))
if dtutils_file.is_executable(spath) then if dtutils_file.test_file(spath, "x") then
dtutils_file.set_executable_path_preference(bin, spath) dtutils_file.set_executable_path_preference(bin, spath) -- save it so we don't have to search again
result = spath result = spath
end end
end end
@@ -240,11 +237,19 @@ local function _search_for_bin_macos(bin)
return result return result
end end
--[[
local function to provide a generic search call that can be
split into operating system specific calls
]]
local function _search_for_bin(bin) local function _search_for_bin(bin)
local result = false local result = false
if dt.configuration.running_os == "windows" then if dt.configuration.running_os == "windows" then
result = _search_for_bin_windows(bin) result = _search_for_bin_windows(bin)
if result then
result = dtutils_file.sanitize_filename(result)
end
elseif dt.configuration.running_os == "macos" then elseif dt.configuration.running_os == "macos" then
result = _search_for_bin_macos(bin) result = _search_for_bin_macos(bin)
else else
@@ -254,6 +259,11 @@ local function _search_for_bin(bin)
return result return result
end end
--[[
local function to check if an executable path is
a windows executable on linux or macos, thus requiring wine to run
]]
local function _check_path_for_wine_bin(path) local function _check_path_for_wine_bin(path)
local result = false local result = false
@@ -268,6 +278,12 @@ local function _check_path_for_wine_bin(path)
return result return result
end end
--[[
local function to check if an executable path is
a valid executable. Some generic checks are done before
system specific checks are done.
]]
local function _check_path_for_bin(bin) local function _check_path_for_bin(bin)
local result = false local result = false
local path = nil local path = nil
@@ -279,13 +295,13 @@ local function _check_path_for_bin(bin)
else else
path = dtutils_file.get_executable_path_preference(bin) path = dtutils_file.get_executable_path_preference(bin)
-- reset path preference is the returned preference is a directory -- 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, "") dtutils_file.set_executable_path_preference(bin, "")
path = nil path = nil
end end
end end
if path and dtutils_file.is_dir(path) then if path and dtutils_file.test_file(path, "d") then
path = nil path = nil
end end
@@ -294,7 +310,7 @@ local function _check_path_for_bin(bin)
end end
if path and not result then 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) result = dtutils_file.sanitize_filename(path)
end end
end end
@@ -302,15 +318,78 @@ local function _check_path_for_bin(bin)
return result return result
end end
--[[
local function to the old check_if_bin_exists functionality
on windows in order to decrease the amount of windows being
created and destroyed by system calls.
]]
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. Most packages are well behaved with the
notiable exception being GIMP on windows. Depending on the packager there are multiple
gimp executables, often with version numbers. In this case, the user needs to specify
the location of the correct executable using executable_manager.]],
Example = [[]],
See_Also = [[executable_manager]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.check_if_bin_exists(bin) function dtutils_file.check_if_bin_exists(bin)
local result = false 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 = _check_path_for_bin(bin)
result = _search_for_bin(bin)
if not result then
result = _search_for_bin(bin)
end
end end
return result return result
end end
@@ -856,5 +935,53 @@ function dtutils_file.rmdir(path)
return dsys.external_command(rm_cmd.." "..path) return dsys.external_command(rm_cmd.." "..path)
end end
dtutils_file.libdoc.functions["create_tmp_file"] = {
Name = [[create_tmp_file]],
Synopsis = [[creates a temporary file]],
Usage = [[local df = require "lib/dtutils.file
local result = df.create_tmp_file()]],
Description = [[create_tmp_file can be used to create temporary files]],
Return_Value = [[result - string - path to the created temporary file.]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.create_tmp_file()
local tmp_file = os.tmpname()
if dt.configuration.running_os == "windows" then
tmp_file = dt.configuration.tmp_dir .. tmp_file -- windows os.tmpname() defaults to root directory
end
local f = io.open(tmp_file, "w")
if not f then
log.msg(log.error, string.format("Error writing to `%s`", tmp_file))
os.remove(tmp_file)
return nil
end
return tmp_file
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 return dtutils_file