2024-11-05 07:53:24 +00:00
|
|
|
-- TO USE, PUT THE INTERIOR OF THIS FUNCTION IN YOUR FILE
|
|
|
|
-- this only works if that file is in the same directory as this one - but works no matter where it was called from
|
|
|
|
local function _example_load()
|
|
|
|
local success, utility = pcall(function()
|
2024-11-05 23:19:37 +00:00
|
|
|
return dofile((arg[0]:match("@?(.*/)") or arg[0]:match("@?(.*\\)")) .. "utility-functions.lua")
|
2024-11-05 07:53:24 +00:00
|
|
|
end)
|
|
|
|
if not success then
|
|
|
|
print("\n\n" .. tostring(utility))
|
|
|
|
error("\n\nThis script may be installed improperly. Follow instructions at:\n\thttps://github.com/TangentFoxy/.lua-files#installation\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-13 12:19:28 +00:00
|
|
|
math.randomseed(os.time())
|
|
|
|
|
2024-01-14 00:17:31 +00:00
|
|
|
local utility = {}
|
|
|
|
|
|
|
|
if package.config:sub(1, 1) == "\\" then
|
|
|
|
utility.OS = "Windows"
|
2024-11-07 02:56:44 +00:00
|
|
|
utility.path_separator = "\\"
|
2024-11-07 03:41:57 +00:00
|
|
|
utility.recursive_remove_command = "rmdir /s /q "
|
2024-01-14 00:17:31 +00:00
|
|
|
else
|
|
|
|
utility.OS = "UNIX-like"
|
2024-11-07 02:56:44 +00:00
|
|
|
utility.path_separator = "/"
|
2024-11-07 03:40:32 +00:00
|
|
|
utility.recursive_remove_command = "rm -r "
|
2024-01-14 00:17:31 +00:00
|
|
|
end
|
|
|
|
|
2024-11-05 07:53:24 +00:00
|
|
|
utility.path = arg[0]:match("@?(.*/)") or arg[0]:match("@?(.*\\)") -- inspired by discussion in https://stackoverflow.com/q/6380820
|
2024-01-14 11:34:45 +00:00
|
|
|
|
2024-01-14 00:17:31 +00:00
|
|
|
-- always uses outputting to a temporary file to guarantee safety
|
|
|
|
function os.capture_safe(command, tmp_file_name)
|
|
|
|
local file_name = tmp_file_name or utility.tmp_file_name()
|
|
|
|
os.execute(command .. " > " .. file_name)
|
|
|
|
|
|
|
|
local file = io.open(file_name, "r")
|
|
|
|
local output = file:read("*all")
|
|
|
|
file:close()
|
|
|
|
os.execute("rm " .. file_name) -- NOTE may not work on all systems, I have a version somewhere that always does
|
|
|
|
return output
|
|
|
|
end
|
|
|
|
|
2024-01-14 00:23:54 +00:00
|
|
|
function os.capture(command)
|
|
|
|
if io.popen then
|
|
|
|
local file = assert(io.popen(command, 'r'))
|
|
|
|
local output = assert(file:read('*all'))
|
|
|
|
file:close()
|
|
|
|
return output
|
|
|
|
else
|
|
|
|
print("WARNING: io.popen not available, using a temporary file to receive output from:\n", command)
|
|
|
|
return os.capture_safe(command)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-13 12:19:28 +00:00
|
|
|
-- trim6 from Lua users wiki (best all-round pure Lua performance)
|
|
|
|
function string.trim(s)
|
|
|
|
return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
|
|
|
|
end
|
|
|
|
|
2024-11-07 02:00:21 +00:00
|
|
|
function string.enquote(s)
|
|
|
|
return "\"" .. s .. "\""
|
|
|
|
end
|
|
|
|
|
2024-11-09 07:37:27 +00:00
|
|
|
local function escape_special_characters(s)
|
|
|
|
local special_characters = "[()%%.[^$%]*+%-?]"
|
|
|
|
if s == nil then return end
|
|
|
|
return (s:gsub(special_characters, "%%%1"))
|
|
|
|
end
|
|
|
|
|
|
|
|
function string.gsplit(s, delimiter)
|
|
|
|
delimiter = delimiter or ","
|
|
|
|
if s:sub(-#delimiter) ~= delimiter then s = s .. delimiter end
|
|
|
|
return s:gmatch("(.-)" .. escape_special_characters(delimiter))
|
|
|
|
end
|
|
|
|
|
|
|
|
function string.split(s, delimiter)
|
|
|
|
local result = {}
|
|
|
|
for item in s:gsplit(delimiter) do
|
|
|
|
result[#result + 1] = item
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2024-11-05 07:53:24 +00:00
|
|
|
utility.require = function(name)
|
|
|
|
local success, package_or_err = pcall(function()
|
2024-11-05 23:19:37 +00:00
|
|
|
return dofile((arg[0]:match("@?(.*/)") or arg[0]:match("@?(.*\\)")) .. name .. ".lua")
|
2024-11-05 07:53:24 +00:00
|
|
|
end)
|
|
|
|
if success then
|
|
|
|
return package_or_err
|
|
|
|
else
|
|
|
|
print("\n\n" .. tostring(package_or_err))
|
|
|
|
error("\n\nThis script may be installed improperly. Follow instructions at:\n\thttps://github.com/TangentFoxy/.lua-files#installation\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- errors if specified program isn't in the path
|
2024-01-13 12:19:28 +00:00
|
|
|
utility.required_program = function(name)
|
2024-01-14 00:23:54 +00:00
|
|
|
local command
|
|
|
|
if utility.OS == "Windows" then
|
2024-01-14 01:04:55 +00:00
|
|
|
command = "where " -- NOTE: This will print a path when it works. :\ Windows, am I right?
|
2024-01-14 00:23:54 +00:00
|
|
|
else
|
|
|
|
command = "which "
|
|
|
|
end
|
|
|
|
|
|
|
|
-- TODO verify this works on Linux / macOS
|
|
|
|
if os.execute(command .. tostring(name)) ~= 0 then
|
2024-01-14 00:58:25 +00:00
|
|
|
error("\n\n" .. tostring(name) .. " must be installed and in the path\n")
|
2024-01-13 12:19:28 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- modified from my fork of lume
|
|
|
|
utility.uuid = function()
|
|
|
|
local fn = function(x)
|
|
|
|
local r = math.random(16) - 1
|
|
|
|
r = (x == "x") and (r + 1) or (r % 4) + 9
|
|
|
|
return ("0123456789abcdef"):sub(r, r)
|
|
|
|
end
|
|
|
|
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
|
|
|
|
end
|
|
|
|
|
2024-01-13 22:43:38 +00:00
|
|
|
utility.tmp_file_name = function()
|
2024-01-14 23:42:06 +00:00
|
|
|
return "." .. utility.uuid() .. ".tmp"
|
2024-01-13 22:43:38 +00:00
|
|
|
end
|
|
|
|
|
2024-11-09 01:22:38 +00:00
|
|
|
utility.make_safe_file_name = function(file_name)
|
|
|
|
file_name = file_name:gsub("[%\"%:%\\%!%@%#%$%%%^%*%=%{%}%|%;%<%>%?%/]", "") -- everything except the &
|
|
|
|
file_name = file_name:gsub(" %&", ",") -- replacing & with a comma works for 99% of things
|
|
|
|
file_name = file_name:gsub("%&", ",") -- replacing & with a comma works for 99% of things
|
|
|
|
file_name = file_name:gsub("[%s+]", " ") -- more than one space in succession should be a single space
|
|
|
|
return file_name
|
|
|
|
end
|
|
|
|
|
2024-11-09 01:16:58 +00:00
|
|
|
-- io.open, but errors are immediately thrown, and the file is closed for you
|
|
|
|
utility.open = function(file_name, mode, custom_error_message)
|
|
|
|
local file, err = io.open(file_name, mode)
|
|
|
|
if not file then error(custom_error_message or err) end
|
|
|
|
return function(fn)
|
2024-11-09 07:48:44 +00:00
|
|
|
local success, result_or_error = pcall(function() return fn(file) end)
|
2024-11-09 01:16:58 +00:00
|
|
|
file:close()
|
2024-11-09 07:51:44 +00:00
|
|
|
if not success then
|
|
|
|
error(result_or_error) -- custom_error_message is only for when the file doesn't exist, this function should not hide *your* errors
|
2024-11-09 07:48:44 +00:00
|
|
|
end
|
2024-11-09 07:51:44 +00:00
|
|
|
return result_or_error
|
2024-11-09 01:16:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-13 12:19:28 +00:00
|
|
|
utility.escape_quotes = function(input)
|
2024-01-14 00:17:31 +00:00
|
|
|
-- the order of these commands is important and must be preserved
|
2024-01-13 12:19:28 +00:00
|
|
|
input = input:gsub("\\", "\\\\")
|
|
|
|
input = input:gsub("\"", "\\\"")
|
|
|
|
return input
|
|
|
|
end
|
|
|
|
|
2024-11-05 07:53:24 +00:00
|
|
|
-- Example, print all items in this directory: utility.ls(".")(print)
|
2024-01-14 00:17:31 +00:00
|
|
|
utility.ls = function(path)
|
|
|
|
local command
|
|
|
|
if utility.OS == "Windows" then
|
|
|
|
command = "dir /w /b"
|
|
|
|
else
|
|
|
|
command = "ls -1"
|
|
|
|
end
|
2024-01-14 00:54:25 +00:00
|
|
|
if path then
|
2024-01-14 11:34:45 +00:00
|
|
|
command = command .. " \"" .. path .. "\""
|
2024-01-14 00:54:25 +00:00
|
|
|
end
|
2024-01-14 00:17:31 +00:00
|
|
|
|
|
|
|
local tmp_file_name = utility.tmp_file_name()
|
|
|
|
local output = os.capture_safe(command, tmp_file_name)
|
|
|
|
|
|
|
|
return function(fn)
|
2024-01-14 02:40:46 +00:00
|
|
|
for line in output:gmatch("[^\r\n]+") do -- thanks to https://stackoverflow.com/a/32847589
|
2024-01-14 00:17:31 +00:00
|
|
|
if line ~= tmp_file_name then -- exclude temporary file name
|
|
|
|
fn(line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-13 12:19:28 +00:00
|
|
|
return utility
|