gdbasics/scripts/libs/fsutil.gd

166 lines
5.1 KiB
GDScript

###
### file system utility
###
extends Object
class_name GDBFsUtil
const __META_FILENAME_REGEX = "__gdb_fsutil_filename_regex__"
const __META_ENVVAR_REGEX = "__gdb_fsutil_envvar_regex__"
class SpecialFolder:
var path := ""
var name := ""
func _init(path_ : String, name_ : String):
self.path = path_
self.name = name_
################
# public stuff #
################
func _init():
assert(0, "This class should not be instantiated.")
static func find_all_by_name(path : String, name : String, files_only = true) -> Array:
var result = []
__find_all_by_name(path, name, result, files_only)
return result
static func escape_filename(filename : String) -> String:
return __get_filename_regex().sub(filename, "_", true)
static func is_filename_valid(filename : String) -> bool:
return __get_filename_regex().search(filename) == null
static func load_image_resource(filename : String):
var res = load(filename)
if res:
return res
var img = Image.new()
if img.load(filename) == OK:
var tex = ImageTexture.new()
tex.create_from_image(img)
return tex
return null
#! Retrieve information about the current git branch.
#!
#! Used to display this information in development builds.
#! Returns a dictionary with the following keys:
#! - "branch" - the name of the current branch
#! - "commit" - the full hash of the latest commit
#! - "commit_short" - the shortened hash of the last commit
static func get_git_info() -> Dictionary:
var file = File.new()
if file.open("res://.git/HEAD", File.READ) != OK:
return {"branch": "<unknown>", "commit": "???", "commit_short": "???"}
var text = file.get_line()
if !text.begins_with("ref:"):
return {"branch": "<detached>", "commit": text, "commit_short": text.left(7)}
var ref = text.right(5).get_file()
file.close()
if file.open("res://.git/refs/heads/%s" % ref, File.READ) != OK:
return {"branch": ref, "commit": "<unknown>", "commit_short": "<unknown>"}
var commitid = file.get_line()
return {"branch": ref, "commit": commitid, "commit_short": commitid.left(7)}
static func get_home_folder() -> String:
if OS.has_feature("X11"):
if OS.has_environment("HOME"):
return OS.get_environment("HOME")
elif OS.has_feature("Windows"):
if OS.has_environment("USERPROFILE"):
return OS.get_environment("USERPROFILE")
return "/"
static func get_special_folders() -> Array:
if OS.has_feature("X11"):
return __get_xdg_folders()
return []
static func replace_environment_variables(string : String) -> String:
var matches := __get_envvar_regex().search_all(string)
var parts := PackedStringArray()
var pos := 0
for the_match in matches:
var var_name : String = the_match.strings[-2] if !the_match.strings[-1] else the_match.strings[-1]
parts.append(string.substr(pos, the_match.get_start()))
parts.append(OS.get_environment(var_name))
pos = the_match.get_end()
parts.append(string.substr(pos))
return "".join(parts)
#################
# private stuff #
#################
static func __get_filename_regex() -> RegEx:
var tree := GDBUtility.get_scene_tree()
if tree.has_meta(__META_FILENAME_REGEX):
return tree.get_meta(__META_FILENAME_REGEX) as RegEx
var filename_regex := RegEx.new()
filename_regex.compile("[^a-zA-Z0-9_\\-\\. ]")
tree.set_meta(__META_FILENAME_REGEX, filename_regex)
return filename_regex
static func __get_envvar_regex() -> RegEx:
var tree := GDBUtility.get_scene_tree()
if tree.has_meta(__META_ENVVAR_REGEX):
return tree.get_meta(__META_ENVVAR_REGEX) as RegEx
var envvar_regex := RegEx.new()
if OS.has_feature("Windows"):
envvar_regex.compile("%(\\w+)%")
else:
envvar_regex.compile("\\$({(\\w+)}|(\\w+))")
tree.set_meta(__META_ENVVAR_REGEX, envvar_regex)
return envvar_regex
static func __find_all_by_name(path, name, result, files_only):
var dir = Directory.new()
if dir.open(path) != OK:
# print("cannot open dir %s" % path)
return
dir.list_dir_begin(true)
while true:
var fname = dir.get_next()
if fname == "":
break
var full_name = path + "/" + fname
if fname == name && (!files_only || dir.file_exists(full_name)):
result.append(full_name)
# dir_exists doesnt work for res:// paths, just attempt to add, will silently fail for files
if true: # dir.dir_exists(full_name):
__find_all_by_name(full_name, name, result, files_only)
dir.list_dir_end()
static func __get_xdg_folders() -> Array:
var user_dirs_filename := get_home_folder().plus_file(".config/user-dirs.dirs")
var user_dirs_file := File.new()
if !user_dirs_file.file_exists(user_dirs_filename):
return []
var status := user_dirs_file.open(user_dirs_filename, File.READ)
if status != OK:
printerr("Error fetching XDG user dirs.")
return []
var dir_regex := RegEx.new()
status = dir_regex.compile("^XDG_([A-Z]+)_DIR=\"([^\"]+)\"$")
assert(status == OK)
var folders := []
while !user_dirs_file.eof_reached():
var line := user_dirs_file.get_line().strip_edges()
var result := dir_regex.search(line)
if result == null:
continue
var folder_name : String = result.strings[1].capitalize()
var folder_path : String = replace_environment_variables(result.strings[2])
folders.append(SpecialFolder.new(folder_path, folder_name))
return folders