sokol-luajit/tools/vfs/dirtools.lua

383 lines
12 KiB
Lua
Raw Normal View History

local ffi = require("ffi")
local dirtools = {}
local tinsert = table.insert
local tconcat = table.concat
---------------------------------------------------------------------------------------
-- Dirtools is about to become a little more complex and become a Virtual File System.
--
-- Scope: The VFS will handle all file management, thus io calls should be replaced
-- with the equivalent vfs calls (same name, different lib).
-- There are unique examples where this might not be needed, this is why the io
-- lib is not being overridden.
--
-- File Support: VFS will handle unique file types and resource conversion, which may
-- end up a separate module. Initially file types supported by the builder will be:
-- - image files (png, jpg)
-- - lua scripts (.lua and .lcc bytecode)
-- - shaders (only .glsl - these will be converted to bytecode at build)
-- - data files (extension must be registered with vfs, and custom handler can be added)
--
-- Resrouces: VFS is most important for building and packaging resource for release
-- creation. The build tool (rbuilder) shall have options for how resources are
-- packaged. For example: Various custom data file types may need to exist
-- standalone, and not be packaged into the resource files.
--
-- Require: VFS will manage loading lua and modules via search_packages system in lua.
-- If an example or project runs from a sokol-luajit repo then the build system
-- shall replicate the same loading within the bytecode generation.
-- Build script combine, build and this file dirtools will include functions to handle
-- the specific pathing and file types as part of the require system.
---------------------------------------------------------------------------------------
local sep = "\\"
if(ffi.os ~= "Windows") then sep = "/" end
---------------------------------------------------------------------------------------
dirtools.add_default_paths = function(path)
local folders = {
["Linux"] = "linux",
["Windows"] = "win64",
["MacOSX"] = "macos",
}
local extensions = {
["Linux"] = "so",
["Windows"] = "dll",
["MacOSX"] = "so",
}
package.cpath = package.cpath..";"..path.."bin/"..folders[ffi.os].."/?."..extensions[ffi.os]
package.path = package.path..";"..path.."/ffi/sokol/?.lua"
package.path = package.path..";"..path.."lua/?.lua"
package.path = package.path..";"..path.."/?.lua"
end
---------------------------------------------------------------------------------------
local function log_info( str )
print(string.format("[Info] %s", str))
end
local function log_error( str )
print(string.format("[Error] %s", str))
end
---------------------------------------------------------------------------------------
dirtools.compare_paths = function(p1, p2)
-- Remove minus, they seem to cause the most problems
p1 = string.gsub(p1, "%-", "_")
p2 = string.gsub(p2, "%-", "_")
return string.match(p1, p2)
end
---------------------------------------------------------------------------------------
dirtools.combine_path = function( base, addition )
return base..sep..addition
end
---------------------------------------------------------------------------------------
dirtools.get_drives = function()
local drives = {}
if(ffi.os == "Windows") then
local cmd = "fsutil fsinfo drives"
local fh = io.popen(cmd, "r")
if(fh) then
local data = fh:read("*a")
fh:close()
if(string.match(data, "Drives: ")) then
data = string.sub(data, 10, -1)
for f in string.gmatch(data, "([^%s]+)") do
f = string.sub(f, 1, -2)
tinsert(drives, f)
end
end
end
else
-- Note: Add more -t <filetype> to support different file system mounts
local cmd = "df -h -t ext4 --output=target"
local fh = io.popen(cmd, "r")
if(fh) then
local data = fh:read("*a")
local count = 0
for f in string.gmatch(data, "(.-)\n") do
f = string.gsub(f, "%s+", "")
if(count > 0) then
if(#f > 0) then
tinsert(drives, f)
end
end
count = count + 1
end
fh:close()
end
end
return drives
end
---------------------------------------------------------------------------------------
-- Much safer way to build folder than pattern match (very unstable)
dirtools.get_folder = function(path)
local parts = {}
local patt = "(.-)[\\]"
if(ffi.os ~= "Windows") then patt = "(.-)[/]" end
for pseg in string.gmatch(path, patt) do
tinsert(parts, pseg)
end
if(#parts < 1) then return path end
return tconcat(parts, sep)
end
---------------------------------------------------------------------------------------
-- Check if the path is folder or file
if(ffi.os == "Windows") then
dirtools.is_folder = function(path)
local fh = io.popen("attrib "..path, "r")
if(fh) then
local res = fh:read("*a")
local fileattr = string.sub(res, 1, 20)
fileattr = string.gsub(fileattr, " ", "")
fh:close()
return (#fileattr == 0)
end
return false
end
else
dirtools.is_folder = function(path)
local fh = io.popen("file "..path, "r")
if(fh) then
local res = fh:read("*a")
local folder = string.match(res, ".-: directory$")
fh:close()
return (folder ~= nil)
end
return false
end
end
---------------------------------------------------------------------------------------
dirtools.make_folder = function(folderpath)
if(dirtools.is_folder(folderpath) == false) then
local cmd = "mkdir -f "..folderpath
if(ffi.os == "Windows") then cmd = "mkdir "..folderpath end
local fh = io.popen(cmd, "r")
if(fh) then
local data = fh:read("*a")
fh:close()
log_info(data)
else
log_error("Cannot make folder: "..folderpath)
end
end
end
---------------------------------------------------------------------------------------
dirtools.get_relative_path = function( fullpath, parent_path )
local rpath = nil
local str_st, str_end = string.find( fullpath, parent_path, 1, true )
if(str_st and str_end) then
rpath = string.sub(fullpath, str_end+2, -1)
end
return rpath
end
---------------------------------------------------------------------------------------
dirtools.get_app_path = function( expected_root_folder )
local base_dir = "."
if(ffi.os == "Windows") then
local cmdh = io.popen("cd", "r")
if(cmdh) then base_dir = cmdh:read("*a"); cmdh:close() end
else
local cmdh = io.popen("pwd", "r")
if(cmdh) then base_dir = cmdh:read("*a"); cmdh:close() end
end
if(expected_root_folder) then
local folder_name = expected_root_folder
local last_folder, remain = string.match(base_dir, "(.-"..folder_name..")(.-)")
last_folder = last_folder or ""
remain = remain or ""
remain = remain:gsub("%s+", "")
if(string.len(last_folder) > 0) then
base_dir = last_folder..sep
else
base_dir = ""
end
end
-- print("Base Directory: "..base_dir)
return base_dir
end
-- --------------------------------------------------------------------------------------
local allfolders_cmd = "dir /ON /AD /B %s"
if(ffi.os ~= "Windows") then allfolders_cmd = "ls -p %s | grep /" end
local allfiles_cmd = "dir /ON /A-D /B %s"
if(ffi.os ~= "Windows") then allfiles_cmd = "ls -p %s | grep -v /" end
-- --------------------------------------------------------------------------------------
local list_folders_cache = {}
local list_cache = {}
dirtools.get_folderslist = function(path, cache_update)
-- Check path first. If its a drive on windows then no caching
if(ffi.os == "Windows") then
local colon = string.sub(path, 2,-1)
if(colon == ":") then cache_update = true; path = path..sep end
end
if(list_folders_cache[path] and cache_update == nil) then
return list_folders_cache[path]
end
local files = {}
table.insert(files, 1, { name = ".." })
local res = ""
-- Fill with temp file list of dir /b
local fh = io.popen(string.format(allfolders_cmd, path), "r")
if(fh) then
res = fh:read("*a")
fh:close()
else
log_error("dirtools.get_folderslist bad path: "..tostring(path))
return files
end
for f in string.gmatch(res, "(.-)\n") do
local newfile = { name = ffi.string(f), folder = true }
newfile.select = ffi.new("int[1]")
newfile.select[0] = 0
table.insert(files, newfile)
end
list_folders_cache[path] = files
return files
end
---------------------------------------------------------------------------------------
dirtools.get_dirlist = function(path, cache_update)
-- Check path first. If its a drive on windows then no caching
if(ffi.os == "Windows") then
local colon = string.match(path, "(.)$")
if(colon == ":") then path = path..sep end
end
if(list_cache[path] and cache_update == nil) then
return list_cache[path]
end
-- Get all the folders first
local files = dirtools.get_folderslist(path)
-- Add the files to the list.
local res = ""
-- Fill with temp file list of dir /b
local fh = io.popen(string.format(allfiles_cmd, path), "r")
if(fh) then
res = fh:read("*a")
fh:close()
if(#res == 0) then
list_cache[path] = files
return files
end
else
log_error("dirtools.get_dirlist bad path: "..tostring(path))
return {}
end
for f in string.gmatch(res, "(.-)\n") do
local newfile = { name = ffi.string(f), folder = nil }
newfile.select = ffi.new("int[1]")
newfile.select[0] = 0
table.insert(files, newfile)
end
list_cache[path] = files
return files
end
---------------------------------------------------------------------------------------
dirtools.path_match = function(list, path)
for i,v in ipairs(list) do
if(dirtools.compare_paths(v.name, path)) then
return true
end
end
return false
end
---------------------------------------------------------------------------------------
dirtools.get_parent = function( path )
if(path == nil or path == "") then path = "." end
local parentpath = dirtools.get_folder(path)
return parentpath
end
---------------------------------------------------------------------------------------
dirtools.find_folder = function( start_path, parent_up, folder_name )
-- If starting in current application folder, convert it to a full path
if(start_path == ".") then
start_path = dirtools.get_app_path()
end
local list = dirtools.get_folderslist(start_path)
if(dirtools.path_match(list, "sokol-luajit") == true) then
return start_path
end
local current = start_path
for i=1, parent_up do
current = dirtools.get_parent(current)
list = dirtools.get_folderslist(current)
if(dirtools.path_match(list, "sokol-luajit") == true) then
return current
end
end
return nil
end
---------------------------------------------------------------------------------------
dirtools.change_folder = function( path, child )
local newpath = path..sep..child
return newpath
end
---------------------------------------------------------------------------------------
-- By default the paths are added
dirtools.init = function( base_path )
local path = dirtools.get_app_path(base_path)
dirtools.add_default_paths(path)
end
---------------------------------------------------------------------------------------
return dirtools
2024-10-27 16:11:43 +08:00
---------------------------------------------------------------------------------------