From 4c536b41bccb2ca4627ffdeaf263f149ccc4b87f Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Mon, 8 Jun 2020 12:45:49 -0400 Subject: [PATCH 01/10] checkpoint --- lib/dtutils/file.lua | 61 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 9fa2c26..aaf7f01 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -57,35 +57,78 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = { Copyright = [[]], } -function dtutils_file.check_if_bin_exists(bin) +local function _check_if_bin_exists_windows(bin) local result = false local path = nil - if string.match(bin, "/") or string.match(bin, "\\") then + if string.match(bin, "\\") then path = bin else path = dtutils_file.get_executable_path_preference(bin) end - if string.len(path) > 0 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 if dtutils_file.check_if_file_exists(path) then - if (string.match(path, ".exe$") or string.match(path, ".EXE$")) and dt.configuration.running_os ~= "windows" then - result = dtutils_file.sanitize_filename("wine " .. path) - else + result = dtutils_file.sanitize_filename(path) + end + end + return result +end + +-- check_if_bin_exists for unix like systems (linux, macos) +local function _check_if_bin_exists_nix(bin) + local result = false + local path = nil + + if string.match(bin, "/") then + path = bin + else + path = dtutils_file.get_executable_path_preference(bin) + end + + if path then dt.print_log("path is " .. path) end + + if string.len(path) > 0 then + -- check for windows executable to run under wine + if string.match(path, ".exe$") or string.match(path, ".EXE$") then + if dtutils_file.check_if_file_exists(path) then result = dtutils_file.sanitize_filename(path) end + else + if dtutils_file.check_if_file_exists(path) then + local spath = dtutils_file.sanitize_filename(path) + -- check that it's an executable file + if os.execute("test -f " .. spath .. " && test -x " .. spath) then + result = spath + end + end end - elseif dt.configuration.running_os == "linux" then + end + if not result then local p = io.popen("which " .. bin) local output = p:read("*a") p:close() if string.len(output) > 0 then - result = dtutils_file.sanitize_filename(output:sub(1,-2)) + local spath = dtutils_file.sanitize_filename(output:sub(1,-2)) + if os.execute("test -f " .. spath .. " && test -x " .. spath) then + result = spath + end end end return result end +function dtutils_file.check_if_bin_exists(bin) + if dt.configuration.running_os == "windows" then + return _check_if_bin_exists_windows(bin) + else + return _check_if_bin_exists_nix(bin) + end +end + dtutils_file.libdoc.functions["split_filepath"] = { Name = [[split_filepath]], Synopsis = [[split a filepath into parts]], @@ -542,7 +585,7 @@ function dtutils_file.executable_path_widget(executables) is_directory = false, changed_callback = function(self) if dtutils_file.check_if_bin_exists(self.value) then - dtutils_file.set_executable_path_preference(executable, self.value) + dtutils_file.set_executable_path_preference(executable, dtutils_file.check_if_bin_exists(self.value)) end end} ) From 04525e12585c7e9bc904657d3627cdc5c9f3f4e1 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Tue, 21 Jul 2020 16:39:34 -0400 Subject: [PATCH 02/10] Added functions is_file(path), is_dir(path, and is_executable(path) to ensure a path is a file, directory, or executable respectively. Replaced code in check_if_bin_exists with these functions to reduce the lines of code and make it more understandable. --- lib/dtutils/file.lua | 180 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 169 insertions(+), 11 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index aaf7f01..a6e2404 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -35,6 +35,112 @@ 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]], + 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]], + Return_Value = [[result - boolean - true if path is a directory, nil if not]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[]], + Copyright = [[]], +} + +function dtutils_file.is_dir(path) + local cmd = nil + + if dt.configuration.running_os == "windows" then + cmd = "if exist " .. ds.sanitize(path .. "\\*") + else + cmd = "test -d " .. ds.sanitize(path) + 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 + + if dt.configuration.running_os == "windows" then + if not dtutils_file.is_dir(path) then + cmd = "if exist " .. ds.sanitize(path) + else + return nil + end + else + cmd = "test -f " .. ds.sanitize(path) + end + + return os.execute(cmd) +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 + + 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 + + +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]], @@ -67,11 +173,9 @@ local function _check_if_bin_exists_windows(bin) path = dtutils_file.get_executable_path_preference(bin) end - 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 - if dtutils_file.check_if_file_exists(path) then + + if dtutils_file.check_if_file_exists(path) then + if dtutils_file.is-executable(path) then result = dtutils_file.sanitize_filename(path) end end @@ -93,16 +197,14 @@ local function _check_if_bin_exists_nix(bin) if string.len(path) > 0 then -- check for windows executable to run under wine - if string.match(path, ".exe$") or string.match(path, ".EXE$") then + if _is-windows_executable(path) then if dtutils_file.check_if_file_exists(path) then result = dtutils_file.sanitize_filename(path) end else if dtutils_file.check_if_file_exists(path) then - local spath = dtutils_file.sanitize_filename(path) - -- check that it's an executable file - if os.execute("test -f " .. spath .. " && test -x " .. spath) then - result = spath + if dtutils_file.is_file(path) and dtutils_file.is_executable(path) then + result = ds.sanitize(path) end end end @@ -112,8 +214,9 @@ local function _check_if_bin_exists_nix(bin) local output = p:read("*a") p:close() if string.len(output) > 0 then + local spath = dtutils_file.sanitize_filename(output:sub(1,-2)) - if os.execute("test -f " .. spath .. " && test -x " .. spath) then + if dtutils_file.is_file(spath) and dtutils_file.is_executable(spath) then result = spath end end @@ -670,6 +773,61 @@ function dtutils_file.rmdir(path) return dsys.external_command(rm_cmd.." "..path) end +function dtutils_file.is_dir(path) + local cmd = nil + + if dt.configuration.running_os == "windows" then + cmd = "if exist " .. ds.sanitize(path .. "\\*") + else + cmd = "test -d " .. ds.sanitize(path) + end + + return os.execute(cmd) +end + +function dtutils_file.is_file(path) + local cmd = nil + + if dt.configuration.running_os == "windows" then + if not dtutils_file.is_dir(path) then + cmd = "if exist " .. ds.sanitize(path) + else + return nil + end + else + cmd = "test -f " .. ds.sanitize(path) + end + + return os.execute(cmd) +end + +local function is_windows_executable(path) + local result = nil + + 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 + + +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 + return dtutils_file From 1d5192a39cf57cbf3074094de7f300fe8e0cade6 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Tue, 21 Jul 2020 20:40:22 -0400 Subject: [PATCH 03/10] removed duplpicate copies of the is_ functions --- lib/dtutils/file.lua | 56 -------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index a6e2404..9f02bfd 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -773,61 +773,5 @@ function dtutils_file.rmdir(path) return dsys.external_command(rm_cmd.." "..path) end -function dtutils_file.is_dir(path) - local cmd = nil - - if dt.configuration.running_os == "windows" then - cmd = "if exist " .. ds.sanitize(path .. "\\*") - else - cmd = "test -d " .. ds.sanitize(path) - end - - return os.execute(cmd) -end - -function dtutils_file.is_file(path) - local cmd = nil - - if dt.configuration.running_os == "windows" then - if not dtutils_file.is_dir(path) then - cmd = "if exist " .. ds.sanitize(path) - else - return nil - end - else - cmd = "test -f " .. ds.sanitize(path) - end - - return os.execute(cmd) -end - -local function is_windows_executable(path) - local result = nil - - 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 - - -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 - - return dtutils_file From 7790c9700ba5bca77478f0e230012518cc1a6b29 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 2 Aug 2020 18:33:49 -0400 Subject: [PATCH 04/10] Separated check_if_bin_exists() into 2 functions, one to check the returned executable_path variable and another to search for the binary if it wasn't detected. All results are checked to ensure they exist, are a file, and are executable. This fixes the situation where a directory gets stored as an executable path. Search functions are operating system specific. Executable paths are saved for MacOS and Windows to make for shorter searches in the future. --- lib/dtutils/file.lua | 205 ++++++++++++++++++++++++++++++------------- 1 file changed, 144 insertions(+), 61 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 9f02bfd..05f229f 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -83,18 +83,27 @@ dtutils_file.libdoc.functions["is_file"] = { 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) + 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 + result = false + end else - return nil + return false end else - cmd = "test -f " .. ds.sanitize(path) + result = os.execute("test -f " .. ds.sanitize(path)) end - return os.execute(cmd) + return result end dtutils_file.libdoc.functions["is_executable"] = { @@ -116,17 +125,17 @@ dtutils_file.libdoc.functions["is_executable"] = { 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 + 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 - function dtutils_file.is_executable(path) local result = nil @@ -163,73 +172,146 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = { Copyright = [[]], } -local function _check_if_bin_exists_windows(bin) +local function _search_for_bin_windows(bin) local result = false - local path = nil + -- use where on path + -- use where on program files + -- use where on program files (x86) + local args = {"", '/R "C:\\Program Files"', '/R "C:\\Program Files (x86)"'} - if string.match(bin, "\\") then - path = bin - else - path = dtutils_file.get_executable_path_preference(bin) - end - - - if dtutils_file.check_if_file_exists(path) then - if dtutils_file.is-executable(path) then - result = dtutils_file.sanitize_filename(path) - end - end - return result -end - --- check_if_bin_exists for unix like systems (linux, macos) -local function _check_if_bin_exists_nix(bin) - local result = false - local path = nil - - if string.match(bin, "/") then - path = bin - else - path = dtutils_file.get_executable_path_preference(bin) - end - - if path then dt.print_log("path is " .. path) end - - if string.len(path) > 0 then - -- check for windows executable to run under wine - if _is-windows_executable(path) then - if dtutils_file.check_if_file_exists(path) then - result = dtutils_file.sanitize_filename(path) - end - else - if dtutils_file.check_if_file_exists(path) then - if dtutils_file.is_file(path) and dtutils_file.is_executable(path) then - result = ds.sanitize(path) + for _,arg in ipairs(args) do + local cmd = "where " .. arg .. " " .. ds.sanitize(bin) + local p = io.popen(cmd) + local output = p:read("*a") + p:close() + local lines = du.split(output, "\n") + 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 + dtutils_file.set_executable_path_preference(bin, line) -- save it so we don't have to search again + return line end end end end + return result +end + +local function _search_for_bin_nix(bin) + local result = false + local p = io.popen("which " .. bin) + local output = p:read("*a") + 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 + result = spath + end + end + return result +end + +local function _search_for_bin_macos(bin) + local result = false + + result = _search_for_bin_nix(bin) + if not result then - local p = io.popen("which " .. bin) + local search_start = "/Applications" + + if dtutils_file.check_if_file_exists("/Applications/" .. bin .. ".app") then + search_start = "/Applications/" .. bin .. ".app" + end + + local p = io.popen("find " .. search_start .. " -type f -name " .. bin .. " -print") local output = p:read("*a") p:close() - if string.len(output) > 0 then + local lines = du.split(output, "\n") - local spath = dtutils_file.sanitize_filename(output:sub(1,-2)) - if dtutils_file.is_file(spath) and dtutils_file.is_executable(spath) then - result = spath + for _,line in ipairs(lines) do + local spath = dtutils_file.sanitize_filename(line:sub(1, -2)) + if dtutils_file.is_executable(spath) then + dtutils_file.set_executable_path_preference(bin, spath) + result = spath + end + end + end + + return result +end + +local function _search_for_bin(bin) + local result = false + + if dt.configuration.running_os == "windows" then + result = _search_for_bin_windows(bin) + elseif dt.configuration.running_os == "macos" then + result = _search_for_bin_macos(bin) + else + result = _search_for_bin_nix(bin) + end + + return result +end + +local function _check_path_for_wine_bin(path) + local result = false + + if string.len(path) > 0 then + -- check for windows executable to run under wine + if _is_windows_executable(path) then + if dtutils_file.check_if_file_exists(path) then + result = "wine " .. dtutils_file.sanitize_filename(path) end end end return result end -function dtutils_file.check_if_bin_exists(bin) - if dt.configuration.running_os == "windows" then - return _check_if_bin_exists_windows(bin) +local function _check_path_for_bin(bin) + local result = false + local path = nil + + local PS = dt.configuration.running_os == "windows" and "\\" or "/" + + if string.match(bin, PS) then + path = bin else - return _check_if_bin_exists_nix(bin) + 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 + dtutils_file.set_executable_path_preference(bin, "") + path = nil + end end + + if path and dtutils_file.is_dir(path) then + path = nil + end + + if path and dt.configuration.running_os ~= "windows" then + result = _check_path_for_wine_bin(path) + end + + if path and not result then + if dtutils_file.is_executable(path) then + result = dtutils_file.sanitize_filename(path) + end + end + + return result +end + +function dtutils_file.check_if_bin_exists(bin) + local result = false + + result = _check_path_for_bin(bin) + + if not result then + result = _search_for_bin(bin) + end + + return result end dtutils_file.libdoc.functions["split_filepath"] = { @@ -688,10 +770,11 @@ function dtutils_file.executable_path_widget(executables) is_directory = false, changed_callback = function(self) if dtutils_file.check_if_bin_exists(self.value) then - dtutils_file.set_executable_path_preference(executable, dtutils_file.check_if_bin_exists(self.value)) + dtutils_file.set_executable_path_preference(executable, self.value) end - end} - ) + end + } + ) end local box = dt.new_widget("box"){ orientation = "vertical", From da0574839373e6c57e879f41d2b74cd19b54380c Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 29 Nov 2020 18:00:47 -0500 Subject: [PATCH 05/10] [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. --- lib/dtutils/file.lua | 295 +++++++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 122 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 05f229f..3fb4fa5 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -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 From ddcf3ed3f4dd8cf35c54ef3d6af8360d858682a0 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Sun, 29 Nov 2020 18:41:32 -0500 Subject: [PATCH 06/10] [file.lua] fixed line trimming in macos search for bin --- lib/dtutils/file.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 3fb4fa5..5f78574 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -207,9 +207,9 @@ local function _search_for_bin_macos(bin) local lines = du.split(output, "\n") 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.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 end end From b89338335337072591d792a0879fb306719b64b4 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Mon, 30 Nov 2020 21:06:57 -0500 Subject: [PATCH 07/10] [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. --- lib/dtutils/file.lua | 52 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 5f78574..77d310c 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -59,6 +59,7 @@ end local function _is_windows_executable(path) local result = false +dt.print_log("in -is_windows_executable") 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 @@ -142,12 +143,22 @@ function dtutils_file.test_file(path, test) return engine(cmdstring) end +--[[ + local function to return a case insensitive pattern for matching + i.e. gimp becomes [Gg][Ii][Mm][Pp] which should match any capitalization + of gimp. +]] + local function _case_insensitive_pattern(pattern) return pattern:gsub("(.)", function(letter) return string.format("[%s$s]", letter:lower(), letter:upper()) end) end +--[[ + local function to search windows for an executable +]] + local function _search_for_bin_windows(bin) local result = false -- use where on path @@ -175,24 +186,33 @@ local function _search_for_bin_windows(bin) return result end +--[[ + local function to search *nix systems for an executable +]] + local function _search_for_bin_nix(bin) local result = false local p = io.popen("which " .. bin) local output = p:read("*a") p:close() 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.test_file(spath, "f") and dtutils_file.test_file(spath, "x") then + dtutils_file.set_executable_path_preference(bin, spath) result = spath end end return result end +--[[ + local function to search macos systems for an executable +]] + local function _search_for_bin_macos(bin) 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 local search_start = "/Applications" @@ -218,6 +238,11 @@ local function _search_for_bin_macos(bin) return result 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 result = false @@ -235,6 +260,11 @@ local function _search_for_bin(bin) return result 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 result = false @@ -249,6 +279,12 @@ local function _check_path_for_wine_bin(path) return result 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 result = false local path = nil @@ -283,6 +319,12 @@ local function _check_path_for_bin(bin) return result 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 @@ -325,7 +367,10 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = { 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.]], + 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 = [[]], @@ -334,6 +379,7 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = { } function dtutils_file.check_if_bin_exists(bin) +dt.print_log("in check_if_bin_exists") local result = false if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then From b41b2a1566dd54d933f16cb87fb452e3ce2fa90b Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Mon, 30 Nov 2020 21:51:55 -0500 Subject: [PATCH 08/10] [file.lua] Added create_tmp_file() back in. --- lib/dtutils/file.lua | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index 77d310c..f9f1455 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -937,6 +937,38 @@ function dtutils_file.rmdir(path) return dsys.external_command(rm_cmd.." "..path) 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 From e86a3a323605afc32255a613184db5769a5cd73b Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Fri, 18 Dec 2020 11:08:12 -0500 Subject: [PATCH 09/10] [file.lua] removed debugging statement --- lib/dtutils/file.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index f9f1455..aa15e8c 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -379,7 +379,6 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = { } function dtutils_file.check_if_bin_exists(bin) -dt.print_log("in check_if_bin_exists") local result = false if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then From 96ba075e868284be920e3d5a3c73192ee10da042 Mon Sep 17 00:00:00 2001 From: wpferguson Date: Tue, 22 Dec 2020 22:00:41 -0500 Subject: [PATCH 10/10] Removed debugging statement --- lib/dtutils/file.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index aa15e8c..2bb0a7a 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -59,7 +59,6 @@ end local function _is_windows_executable(path) local result = false -dt.print_log("in -is_windows_executable") 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