Skip to content

Commit da05748

Browse files
committed
[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.
1 parent 7790c97 commit da05748

File tree

1 file changed

+167
-116
lines changed

1 file changed

+167
-116
lines changed

lib/dtutils/file.lua

Lines changed: 167 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -35,86 +35,55 @@ local function _(msgid)
3535
return gettext.dgettext("dtutils.file", msgid)
3636
end
3737

38-
dtutils_file.libdoc.functions["is_dir"] = {
39-
Name = [[is_dir]],
40-
Synopsis = [[check if a path is a directory]],
41-
Usage = [[local df = require "lib/dtutils.file"
38+
--[[
39+
local function to run a test command on windows and return a true if it succeeds
40+
instead of returning true if it runs
41+
]]
4242

43-
local result = df.is_dir(path)
44-
path - string - the path to check]],
45-
Description = [[is_dir checks a path to see if it is a directory]],
46-
Return_Value = [[result - boolean - true if path is a directory, nil if not]],
47-
Limitations = [[]],
48-
Example = [[]],
49-
See_Also = [[]],
50-
Reference = [[]],
51-
License = [[]],
52-
Copyright = [[]],
53-
}
54-
55-
function dtutils_file.is_dir(path)
56-
local cmd = nil
57-
58-
if dt.configuration.running_os == "windows" then
59-
cmd = "if exist " .. ds.sanitize(path .. "\\*")
43+
local function _win_os_execute(cmd)
44+
local result = nil
45+
local p = io.popen(cmd)
46+
local output = p:read("*a")
47+
p:close()
48+
if string.match(output, "true") then
49+
result = true
6050
else
61-
cmd = "test -d " .. ds.sanitize(path)
51+
result = false
6252
end
63-
64-
return os.execute(cmd)
53+
return result
6554
end
6655

67-
dtutils_file.libdoc.functions["is_file"] = {
68-
Name = [[is_file]],
69-
Synopsis = [[check if a path is a file]],
70-
Usage = [[local df = require "lib/dtutils.file"
56+
--[[
57+
local function to determine if a path name is a windows executable
58+
]]
7159

72-
local result = df.is_file(path)
73-
path - string - the path to check]],
74-
Description = [[is_file checks a path to see if it is a file]],
75-
Return_Value = [[result - boolean - true if path is a file, nil if not]],
76-
Limitations = [[]],
77-
Example = [[]],
78-
See_Also = [[]],
79-
Reference = [[]],
80-
License = [[]],
81-
Copyright = [[]],
82-
}
83-
84-
function dtutils_file.is_file(path)
85-
local cmd = nil
60+
local function _is_windows_executable(path)
8661
local result = false
87-
88-
if dt.configuration.running_os == "windows" then
89-
if not dtutils_file.is_dir(path) then
90-
cmd = "if exist " .. ds.sanitize(path) .. " echo true"
91-
local p = io.popen(cmd)
92-
output = p:read("*a")
93-
p:close()
94-
if string.match(output, "true") then
62+
if dtutils_file.test_file(path, "f") then
63+
if string.match(path, ".exe$") or string.match(path, ".EXE$") or
64+
string.match(path, ".com$") or string.match(path, ".COM$") or
65+
string.match(path, ".bat$") or string.match(path, ".BAT$") or
66+
string.match(path, ".cmd$") or string.match(path, ".CMD$") then
9567
result = true
96-
else
97-
result = false
98-
end
99-
else
100-
return false
10168
end
102-
else
103-
result = os.execute("test -f " .. ds.sanitize(path))
10469
end
105-
10670
return result
10771
end
10872

109-
dtutils_file.libdoc.functions["is_executable"] = {
110-
Name = [[is_executable]],
111-
Synopsis = [[check if a path is a executable]],
73+
dtutils_file.libdoc.functions["test_file"] = {
74+
Name = [[test_file]],
75+
Synopsis = [[test a file to see what it is]],
11276
Usage = [[local df = require "lib/dtutils.file"
11377
114-
local result = df.is_executable(path)
115-
path - string - the path to check]],
116-
Description = [[is_executable checks a path to see if it is an executable]],
117-
Return_Value = [[result - boolean - true if path is an executable, nil if not]],
78+
local result = df.test_file(path, test)
79+
path - string - the path to check
80+
test - one of d, e, f, x where
81+
d - directory
82+
e - exists
83+
f - file
84+
x - executable]],
85+
Description = [[test_file checks a path to see if it is a directory]],
86+
Return_Value = [[result - boolean - true if path is a directory, nil if not]],
11887
Limitations = [[]],
11988
Example = [[]],
12089
See_Also = [[]],
@@ -123,54 +92,61 @@ dtutils_file.libdoc.functions["is_executable"] = {
12392
Copyright = [[]],
12493
}
12594

126-
local function _is_windows_executable(path)
127-
local result = nil
128-
dt.print_log("checking " .. path)
95+
function dtutils_file.test_file(path, test)
96+
local cmd = "test -"
97+
local engine = os.execute
98+
local cmdstring = ""
12999

130-
if string.match(path, ".exe$") or string.match(path, ".EXE$") or
131-
string.match(path, ".com$") or string.match(path, ".COM$") or
132-
string.match(path, ".bat$") or string.match(path, ".BAT$") or
133-
string.match(path, ".cmd$") or string.match(path, ".CMD$") then
134-
result = true
100+
if dt.configuration.running_os == "windows" then
101+
cmd = "if exist "
102+
engine = _win_os_execute
135103
end
136-
return result
137-
end
138-
139-
function dtutils_file.is_executable(path)
140104

141-
local result = nil
142-
143-
if dt.configuration.running_os == "windows" then
144-
if _is_windows_executable(path) then
145-
result = true
105+
if test == "d" then
106+
-- test if directory
107+
if dt.configuration.running_os == "windows" then
108+
cmdstring = cmd .. dtutils_file.sanitize_filename(path .. "\\*") .. " echo true"
109+
else
110+
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
111+
end
112+
elseif test == "e" then
113+
-- test exists
114+
if dt.configuration.running_os == "windows" then
115+
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
116+
else
117+
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
118+
end
119+
elseif test == "f" then
120+
-- test if file
121+
if dt.configuration.running_os == "windows" then
122+
if not dtutils_file.test_file(path, "d") then -- make sure it's not a directory
123+
cmdstring = cmd .. dtutils_file.sanitize_filename(path) .. " echo true"
124+
else
125+
return false
126+
end
127+
else
128+
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
129+
end
130+
elseif test == "x" then
131+
-- test executable
132+
if dt.configuration.running_os == "windows" then
133+
return _is_windows_executable(path)
134+
else
135+
cmdstring = cmd .. test .. " " .. dtutils_file.sanitize_filename(path)
146136
end
147137
else
148-
result = os.execute("test -x " .. ds.sanitize(path))
138+
dt.print_error("[test_file] unknown test " .. test)
139+
return false
149140
end
150-
return result
151-
end
152141

153-
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
154-
Name = [[check_if_bin_exists]],
155-
Synopsis = [[check if an executable exists]],
156-
Usage = [[local df = require "lib/dtutils.file"
142+
return engine(cmdstring)
143+
end
157144

158-
local result = df.check_if_bin_exists(bin)
159-
bin - string - the binary to check for]],
160-
Description = [[check_if_bin_exists checks to see if the specified binary exists.
161-
check_if_bin_exists first checks to see if a preference for the binary has been
162-
registered and uses that if found. The presence of the file is verified, then
163-
quoted and returned. If no preference is specified and the operating system is
164-
linux then the which command is used to check for a binary in the path. If found
165-
that path is returned. If no binary is found, false is returned.]],
166-
Return_Value = [[result - string - the sanitized path of the binary, false if not found]],
167-
Limitations = [[]],
168-
Example = [[]],
169-
See_Also = [[]],
170-
Reference = [[]],
171-
License = [[]],
172-
Copyright = [[]],
173-
}
145+
local function _case_insensitive_pattern(pattern)
146+
return pattern:gsub("(.)", function(letter)
147+
return string.format("[%s$s]", letter:lower(), letter:upper())
148+
end)
149+
end
174150

175151
local function _search_for_bin_windows(bin)
176152
local result = false
@@ -185,9 +161,11 @@ local function _search_for_bin_windows(bin)
185161
local output = p:read("*a")
186162
p:close()
187163
local lines = du.split(output, "\n")
164+
local cibin = _case_insensitive_pattern(bin)
188165
for _,line in ipairs(lines) do
189-
if string.match(line, bin) then
190-
if dtutils_file.is_file(line) and dtutils_file.is_executable(line) then
166+
if string.match(line, cibin) then
167+
dt.print_log("found win search match " .. line)
168+
if dtutils_file.test_file(line, "f") and dtutils_file.test_file(line, "x") then
191169
dtutils_file.set_executable_path_preference(bin, line) -- save it so we don't have to search again
192170
return line
193171
end
@@ -204,7 +182,7 @@ local function _search_for_bin_nix(bin)
204182
p:close()
205183
if string.len(output) > 0 then
206184
local spath = dtutils_file.sanitize_filename(output:sub(1,-2))
207-
if dtutils_file.is_file(spath) and dtutils_file.is_executable(spath) then
185+
if dtutils_file.test_file(spath, "f") and dtutils_file.test_file(spath, "x") then
208186
result = spath
209187
end
210188
end
@@ -230,7 +208,7 @@ local function _search_for_bin_macos(bin)
230208

231209
for _,line in ipairs(lines) do
232210
local spath = dtutils_file.sanitize_filename(line:sub(1, -2))
233-
if dtutils_file.is_executable(spath) then
211+
if dtutils_file.test_file(spath, "x") then
234212
dtutils_file.set_executable_path_preference(bin, spath)
235213
result = spath
236214
end
@@ -245,6 +223,9 @@ local function _search_for_bin(bin)
245223

246224
if dt.configuration.running_os == "windows" then
247225
result = _search_for_bin_windows(bin)
226+
if result then
227+
result = dtutils_file.sanitize_filename(result)
228+
end
248229
elseif dt.configuration.running_os == "macos" then
249230
result = _search_for_bin_macos(bin)
250231
else
@@ -279,13 +260,13 @@ local function _check_path_for_bin(bin)
279260
else
280261
path = dtutils_file.get_executable_path_preference(bin)
281262
-- reset path preference is the returned preference is a directory
282-
if dtutils_file.is_dir(path) then
263+
if dtutils_file.test_file(path, "d") then
283264
dtutils_file.set_executable_path_preference(bin, "")
284265
path = nil
285266
end
286267
end
287268

288-
if path and dtutils_file.is_dir(path) then
269+
if path and dtutils_file.test_file(path, "d") then
289270
path = nil
290271
end
291272

@@ -294,23 +275,77 @@ local function _check_path_for_bin(bin)
294275
end
295276

296277
if path and not result then
297-
if dtutils_file.is_executable(path) then
278+
if dtutils_file.test_file(path, "x") then
298279
result = dtutils_file.sanitize_filename(path)
299280
end
300281
end
301282

302283
return result
303284
end
304285

286+
local function _old_check_if_bin_exists(bin) -- only run on windows if preference checked
287+
local result = false
288+
local path = nil
289+
290+
if string.match(bin, "\\") then
291+
path = bin
292+
else
293+
path = dtutils_file.get_executable_path_preference(bin)
294+
end
295+
296+
if string.len(path) > 0 then
297+
if dtutils_file.check_if_file_exists(path) then
298+
if (string.match(path, ".exe$") or string.match(path, ".EXE$")) then
299+
result = dtutils_file.sanitize_filename(path)
300+
end
301+
end
302+
end
303+
return result
304+
end
305+
306+
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
307+
Name = [[check_if_bin_exists]],
308+
Synopsis = [[check if an executable exists]],
309+
Usage = [[local df = require "lib/dtutils.file"
310+
311+
local result = df.check_if_bin_exists(bin)
312+
bin - string - the binary to check for]],
313+
Description = [[check_if_bin_exists checks to see if the specified binary exists.
314+
check_if_bin_exists first checks to see if a preference for the binary has been
315+
registered and uses that if found, after it's verified to be an executable and
316+
exist. If no preference exissts, the user's path is checked for the executable.
317+
If the executable is not found in the users path, then a search of the operating
318+
system is conducted to see if the executable can be found.
319+
320+
If an executalble is found, it's verified to exist and be an executable. Once
321+
the executable is verified, the path is saved as a preference to speed up
322+
subsequent checks. The executable path is sanitized and returned.
323+
324+
If no executable is found, false is returned.]],
325+
Return_Value = [[result - string - the sanitized path of the binary, false if not found]],
326+
Limitations = [[If more than one executable that satisfies the search results is found, the
327+
wrong one may be returned. If the wrong value is returned, the user can still specify the
328+
correct execuable using tools/executable_manager.]],
329+
Example = [[]],
330+
See_Also = [[executable_manager]],
331+
Reference = [[]],
332+
License = [[]],
333+
Copyright = [[]],
334+
}
335+
305336
function dtutils_file.check_if_bin_exists(bin)
306337
local result = false
307338

308-
result = _check_path_for_bin(bin)
339+
if dt.configuration.running_os == "windows" and dt.preferences.read("dtutils.file", "use_old_check_if_bin_exists", "bool") then
340+
result = _old_check_if_bin_exists(bin)
341+
else
342+
343+
result = _check_path_for_bin(bin)
309344

310-
if not result then
311-
result = _search_for_bin(bin)
345+
if not result then
346+
result = _search_for_bin(bin)
347+
end
312348
end
313-
314349
return result
315350
end
316351

@@ -856,5 +891,21 @@ function dtutils_file.rmdir(path)
856891
return dsys.external_command(rm_cmd.." "..path)
857892
end
858893

894+
--[[
895+
The new check_if_bin_exists() does multiple calls to the operating system to check
896+
if the file exists and is an executable. On windows, each call to the operating system
897+
causes a window to open in order to run the command, then the window closes when the
898+
command exits. If the user gets annoyed by the "flickering windows", then they can
899+
enable this preference to use the old check_if_bin_exists() that relys on the
900+
executable path preferences and doesn't do as many checks.
901+
]]
902+
903+
if dt.configuration.running_os == "windows" then
904+
dt.preferences.register("dtutils.file", "use_old_check_if_bin_exists", "bool",
905+
"lua scripts use old check_if_bin_exists()",
906+
"lessen flickering windows effect when scripts run",
907+
false)
908+
end
909+
859910
return dtutils_file
860911

0 commit comments

Comments
 (0)