mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
fix require (#908)
* fix require * Refactor smlua_find_mod_file * formatting * initialize arrays for consistency * normalize relative path in mod_allocate_file instead (fix mod dev mode) * do cleanup * Remove useless branch + add comments * fix mod_file_exists * WIP: cache mod files at root (LIVE RELOAD IS BROKEN) * Fix live reload * remove useless declaration (??) * fix incorrect top handling * cleanup x2 * stupid * loading sentinel + mark top level files as loading * address dj's comment
This commit is contained in:
parent
9a934f8443
commit
25838f13bc
9 changed files with 171 additions and 109 deletions
|
|
@ -362,7 +362,19 @@ void smlua_init(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gLuaActiveModFile = file;
|
gLuaActiveModFile = file;
|
||||||
smlua_load_script(mod, file, i, true);
|
|
||||||
|
// file has been required by some module before this
|
||||||
|
if (!smlua_get_cached_module_result(L, mod, file)) {
|
||||||
|
smlua_mark_module_as_loading(L, mod, file);
|
||||||
|
|
||||||
|
s32 prevTop = lua_gettop(L);
|
||||||
|
int rc = smlua_load_script(mod, file, i, true);
|
||||||
|
|
||||||
|
if (rc == LUA_OK) {
|
||||||
|
smlua_cache_module_result(L, mod, file, prevTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lua_settop(L, 0);
|
lua_settop(L, 0);
|
||||||
}
|
}
|
||||||
gLuaActiveMod = NULL;
|
gLuaActiveMod = NULL;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ int smlua_hook_event(lua_State* L) {
|
||||||
|
|
||||||
hook->reference[hook->count] = ref;
|
hook->reference[hook->count] = ref;
|
||||||
hook->mod[hook->count] = gLuaActiveMod;
|
hook->mod[hook->count] = gLuaActiveMod;
|
||||||
|
hook->modFile[hook->count] = gLuaActiveModFile;
|
||||||
hook->count++;
|
hook->count++;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "smlua.h"
|
#include "smlua.h"
|
||||||
|
#include "smlua_require.h"
|
||||||
#include "pc/mods/mods.h"
|
#include "pc/mods/mods.h"
|
||||||
#include "pc/mods/mods_utils.h"
|
#include "pc/mods/mods_utils.h"
|
||||||
|
|
||||||
|
|
@ -343,16 +344,8 @@ static void smlua_reload_module(lua_State *L, struct Mod* mod, struct ModFile *f
|
||||||
// only handle loaded Lua modules
|
// only handle loaded Lua modules
|
||||||
if (!file->isLoadedLuaModule) { return; }
|
if (!file->isLoadedLuaModule) { return; }
|
||||||
|
|
||||||
// build registry key for this mod's loaded table
|
|
||||||
char registryKey[SYS_MAX_PATH + 16];
|
|
||||||
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", mod->relativePath);
|
|
||||||
|
|
||||||
// get per-mod "loaded" table
|
// get per-mod "loaded" table
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, registryKey); // ..., loadedTable
|
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||||
if (!lua_istable(L, -1)) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the old module table: loadedTable[file->relativePath]
|
// get the old module table: loadedTable[file->relativePath]
|
||||||
lua_getfield(L, -1, file->relativePath); // ..., loadedTable, oldMod
|
lua_getfield(L, -1, file->relativePath); // ..., loadedTable, oldMod
|
||||||
|
|
|
||||||
|
|
@ -4,47 +4,89 @@
|
||||||
#include "pc/mods/mods_utils.h"
|
#include "pc/mods/mods_utils.h"
|
||||||
#include "pc/fs/fmem.h"
|
#include "pc/fs/fmem.h"
|
||||||
|
|
||||||
|
#define LOADING_SENTINEL ((void*)-1)
|
||||||
|
|
||||||
// table to track loaded modules per mod
|
// table to track loaded modules per mod
|
||||||
static void smlua_init_mod_loaded_table(lua_State* L, const char* modPath) {
|
void smlua_get_or_create_mod_loaded_table(lua_State* L, struct Mod* mod) {
|
||||||
// Create a unique registry key for this mod's loaded table
|
char registryKey[SYS_MAX_PATH + 16] = "";
|
||||||
char registryKey[SYS_MAX_PATH + 16];
|
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", mod->relativePath);
|
||||||
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", modPath);
|
|
||||||
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, registryKey);
|
lua_getfield(L, LUA_REGISTRYINDEX, registryKey);
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
lua_setfield(L, LUA_REGISTRYINDEX, registryKey);
|
lua_setfield(L, LUA_REGISTRYINDEX, registryKey);
|
||||||
} else {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool smlua_get_cached_module_result(lua_State* L, struct Mod* mod, struct ModFile* file) {
|
||||||
|
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||||
|
lua_getfield(L, -1, file->relativePath);
|
||||||
|
|
||||||
|
if (lua_touserdata(L, -1) == LOADING_SENTINEL) {
|
||||||
|
LOG_LUA_LINE("loop or previous error loading module '%s'", file->relativePath);
|
||||||
|
lua_pop(L, 1); // pop sentinel
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
// not cached
|
||||||
|
lua_pop(L, 2); // pop nil and loaded table
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cached, remove loaded table and leave value on top
|
||||||
|
lua_remove(L, -2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlua_mark_module_as_loading(lua_State* L, struct Mod* mod, struct ModFile* file) {
|
||||||
|
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||||
|
lua_pushlightuserdata(L, LOADING_SENTINEL);
|
||||||
|
lua_setfield(L, -2, file->relativePath);
|
||||||
|
lua_pop(L, 1); // pop loaded table
|
||||||
|
}
|
||||||
|
|
||||||
|
void smlua_cache_module_result(lua_State* L, struct Mod* mod, struct ModFile* file, s32 prevTop) {
|
||||||
|
if (lua_gettop(L) == prevTop) {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
} else if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||||
|
|
||||||
|
lua_pushvalue(L, -2); // duplicate result
|
||||||
|
lua_setfield(L, -2, file->relativePath); // loaded[file->relativePath] = result
|
||||||
|
lua_pop(L, 1); // pop loaded table
|
||||||
|
}
|
||||||
|
|
||||||
static struct ModFile* smlua_find_mod_file(const char* moduleName) {
|
static struct ModFile* smlua_find_mod_file(const char* moduleName) {
|
||||||
char relativeDir[SYS_MAX_PATH] = "";
|
char basePath[SYS_MAX_PATH] = "";
|
||||||
|
char absolutePath[SYS_MAX_PATH] = "";
|
||||||
|
|
||||||
if (!gLuaActiveMod) {
|
if (!gLuaActiveMod) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the directory of the current file
|
||||||
if (gLuaActiveModFile) {
|
if (gLuaActiveModFile) {
|
||||||
path_get_folder(gLuaActiveModFile->relativePath, relativeDir);
|
path_get_folder(gLuaActiveModFile->relativePath, basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasRelativeDir = strlen(relativeDir) > 0;
|
// resolve moduleName to a path relative to mod root
|
||||||
|
resolve_relative_path(basePath, moduleName, absolutePath);
|
||||||
|
|
||||||
struct ModFile* bestPick = NULL;
|
|
||||||
int bestRelativeDepth = INT_MAX;
|
|
||||||
int bestTotalDepth = INT_MAX;
|
|
||||||
bool foundRelativeFile = false;
|
|
||||||
|
|
||||||
char rawName[SYS_MAX_PATH] = "";
|
|
||||||
char luaName[SYS_MAX_PATH] = "";
|
char luaName[SYS_MAX_PATH] = "";
|
||||||
char luacName[SYS_MAX_PATH] = "";
|
char luacName[SYS_MAX_PATH] = "";
|
||||||
snprintf(rawName, SYS_MAX_PATH, "/%s", moduleName);
|
snprintf(luaName, SYS_MAX_PATH, "%s.lua", absolutePath);
|
||||||
snprintf(luaName, SYS_MAX_PATH, "/%s.lua", moduleName);
|
snprintf(luacName, SYS_MAX_PATH, "%s.luac", absolutePath);
|
||||||
snprintf(luacName, SYS_MAX_PATH, "/%s.luac", moduleName);
|
|
||||||
|
|
||||||
|
// since mods' relativePaths are relative to the mod's root, we can do a direct comparison
|
||||||
for (int i = 0; i < gLuaActiveMod->fileCount; i++) {
|
for (int i = 0; i < gLuaActiveMod->fileCount; i++) {
|
||||||
struct ModFile* file = &gLuaActiveMod->files[i];
|
struct ModFile* file = &gLuaActiveMod->files[i];
|
||||||
|
|
||||||
|
|
@ -59,36 +101,12 @@ static struct ModFile* smlua_find_mod_file(const char* moduleName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for match
|
// check for match
|
||||||
if (!str_ends_with(file->relativePath, rawName) && !str_ends_with(file->relativePath, luaName) && !str_ends_with(file->relativePath, luacName)) {
|
if (!strcmp(file->relativePath, luaName) || !strcmp(file->relativePath, luacName)) {
|
||||||
continue;
|
return file;
|
||||||
}
|
|
||||||
|
|
||||||
// get total path depth
|
|
||||||
int totalDepth = path_depth(file->relativePath);
|
|
||||||
|
|
||||||
// make sure we never load the old-style lua files with require()
|
|
||||||
if (totalDepth < 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get relative path depth
|
|
||||||
int relativeDepth = INT_MAX;
|
|
||||||
if (hasRelativeDir && path_is_relative_to(file->relativePath, relativeDir)) {
|
|
||||||
relativeDepth = path_depth(file->relativePath + strlen(relativeDir));
|
|
||||||
foundRelativeFile = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pick new best
|
|
||||||
// relative files will always win against absolute files
|
|
||||||
// other than that, shallower files will win
|
|
||||||
if (relativeDepth < bestRelativeDepth || (!foundRelativeFile && totalDepth < bestTotalDepth)) {
|
|
||||||
bestPick = file;
|
|
||||||
bestRelativeDepth = relativeDepth;
|
|
||||||
bestTotalDepth = totalDepth;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestPick;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smlua_custom_require(lua_State* L) {
|
static int smlua_custom_require(lua_State* L) {
|
||||||
|
|
@ -96,69 +114,47 @@ static int smlua_custom_require(lua_State* L) {
|
||||||
|
|
||||||
struct Mod* activeMod = gLuaActiveMod;
|
struct Mod* activeMod = gLuaActiveMod;
|
||||||
if (!activeMod) {
|
if (!activeMod) {
|
||||||
LOG_LUA("require() called outside of mod context");
|
LOG_LUA_LINE("require() called outside of mod context");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create registry key for this mod's loaded table
|
if (str_ends_with(moduleName, "/") || str_ends_with(moduleName, "\\")) {
|
||||||
char registryKey[SYS_MAX_PATH + 16] = "";
|
LOG_LUA_LINE("cannot require a directory");
|
||||||
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", activeMod->relativePath);
|
return 0;
|
||||||
|
|
||||||
// get or create the mod's loaded table
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, registryKey);
|
|
||||||
if (lua_isnil(L, -1)) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, LUA_REGISTRYINDEX, registryKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the file in mod files
|
// find the file in mod files
|
||||||
struct ModFile* file = smlua_find_mod_file(moduleName);
|
struct ModFile* file = smlua_find_mod_file(moduleName);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
LOG_LUA("module '%s' not found in mod files", moduleName);
|
LOG_LUA_LINE("module '%s' not found in mod files", moduleName);
|
||||||
lua_pop(L, 1); // pop table
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if module is already loaded
|
|
||||||
lua_getfield(L, -1, file->relativePath);
|
|
||||||
if (!lua_isnil(L, -1)) {
|
|
||||||
// module already loaded, return it
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
lua_pop(L, 1); // pop nil value
|
|
||||||
|
|
||||||
// mark module as "loading" to prevent recursion
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
lua_setfield(L, -2, file->relativePath);
|
|
||||||
|
|
||||||
// cache the previous mod file
|
|
||||||
struct ModFile* prevModFile = gLuaActiveModFile;
|
|
||||||
s32 prevTop = lua_gettop(L);
|
|
||||||
|
|
||||||
// tag it as a loaded lua module
|
// tag it as a loaded lua module
|
||||||
file->isLoadedLuaModule = true;
|
file->isLoadedLuaModule = true;
|
||||||
|
|
||||||
// load and execute
|
// check cache first
|
||||||
gLuaActiveModFile = file;
|
if (smlua_get_cached_module_result(L, activeMod, file)) {
|
||||||
smlua_load_script(activeMod, file, activeMod->index, false);
|
return 1;
|
||||||
gLuaActiveModFile = prevModFile;
|
|
||||||
|
|
||||||
// if the module didn't return anything, use true
|
|
||||||
if (prevTop == lua_gettop(L)) {
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
} else if (lua_isnil(L, -1)) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// store in loaded table
|
// mark module as "loading" to prevent recursion
|
||||||
lua_pushvalue(L, -1); // duplicate return value
|
smlua_mark_module_as_loading(L, activeMod, file);
|
||||||
lua_setfield(L, -3, file->relativePath); // loaded[file->relativePath] = return_value
|
|
||||||
|
|
||||||
// clean up stack
|
// cache the previous mod file
|
||||||
lua_remove(L, -2);
|
struct ModFile* prevModFile = gLuaActiveModFile;
|
||||||
|
|
||||||
|
// load and execute
|
||||||
|
gLuaActiveModFile = file;
|
||||||
|
s32 prevTop = lua_gettop(L);
|
||||||
|
|
||||||
|
int rc = smlua_load_script(activeMod, file, activeMod->index, false);
|
||||||
|
|
||||||
|
if (rc == LUA_OK) {
|
||||||
|
smlua_cache_module_result(L, activeMod, file, prevTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
gLuaActiveModFile = prevModFile;
|
||||||
|
|
||||||
return 1; // return the module value
|
return 1; // return the module value
|
||||||
}
|
}
|
||||||
|
|
@ -179,6 +175,7 @@ void smlua_init_require_system(void) {
|
||||||
// initialize loaded tables for each mod
|
// initialize loaded tables for each mod
|
||||||
for (int i = 0; i < gActiveMods.entryCount; i++) {
|
for (int i = 0; i < gActiveMods.entryCount; i++) {
|
||||||
struct Mod* mod = gActiveMods.entries[i];
|
struct Mod* mod = gActiveMods.entries[i];
|
||||||
smlua_init_mod_loaded_table(L, mod->relativePath);
|
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||||
|
lua_pop(L, 1); // pop loaded table
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
#include "smlua.h"
|
#include "smlua.h"
|
||||||
|
|
||||||
void smlua_require_update(lua_State* L);
|
|
||||||
void smlua_bind_custom_require(lua_State* L);
|
void smlua_bind_custom_require(lua_State* L);
|
||||||
void smlua_reload_module(lua_State *L, struct Mod* mod, struct ModFile *file);
|
void smlua_get_or_create_mod_loaded_table(lua_State* L, struct Mod* mod);
|
||||||
|
bool smlua_get_cached_module_result(lua_State* L, struct Mod* mod, struct ModFile* file);
|
||||||
|
void smlua_mark_module_as_loading(lua_State* L, struct Mod* mod, struct ModFile* file);
|
||||||
|
void smlua_cache_module_result(lua_State* L, struct Mod* mod, struct ModFile* file, s32 prevTop);
|
||||||
void smlua_init_require_system(void);
|
void smlua_init_require_system(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -539,9 +539,17 @@ void set_environment_region(u8 index, s16 value) {
|
||||||
bool mod_file_exists(const char* filename) {
|
bool mod_file_exists(const char* filename) {
|
||||||
if (gLuaActiveMod == NULL) { return false; }
|
if (gLuaActiveMod == NULL) { return false; }
|
||||||
|
|
||||||
|
char normPath[SYS_MAX_PATH] = { 0 };
|
||||||
|
|
||||||
|
if (snprintf(normPath, sizeof(normPath), "%s", filename) < 0) {
|
||||||
|
LOG_ERROR("Failed to copy filename for normalization: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_path(normPath);
|
||||||
|
|
||||||
for (s32 i = 0; i < gLuaActiveMod->fileCount; i++) {
|
for (s32 i = 0; i < gLuaActiveMod->fileCount; i++) {
|
||||||
struct ModFile* file = &gLuaActiveMod->files[i];
|
struct ModFile* file = &gLuaActiveMod->files[i];
|
||||||
if (!strcmp(file->relativePath, filename)) {
|
if (!strcmp(file->relativePath, normPath)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,15 +245,22 @@ static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
|
||||||
memset(file, 0, sizeof(struct ModFile));
|
memset(file, 0, sizeof(struct ModFile));
|
||||||
|
|
||||||
// set relative path
|
// set relative path
|
||||||
if (snprintf(file->relativePath, SYS_MAX_PATH - 1, "%s", relativePath) < 0) {
|
char normPath[SYS_MAX_PATH] = { 0 };
|
||||||
LOG_ERROR("Failed to remember relative path '%s'", relativePath);
|
if (snprintf(normPath, sizeof(normPath), "%s", relativePath) < 0) {
|
||||||
|
LOG_ERROR("Failed to copy relative path for normalization: %s", relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_path(normPath);
|
||||||
|
|
||||||
|
if (snprintf(file->relativePath, SYS_MAX_PATH - 1, "%s", normPath) < 0) {
|
||||||
|
LOG_ERROR("Failed to remember relative path '%s'", normPath);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure out full path
|
// figure out full path
|
||||||
char fullPath[SYS_MAX_PATH] = { 0 };
|
char fullPath[SYS_MAX_PATH] = { 0 };
|
||||||
if (!mod_file_full_path(fullPath, mod, file)) {
|
if (!mod_file_full_path(fullPath, mod, file)) {
|
||||||
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, relativePath);
|
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, normPath);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,47 @@ int path_depth(const char* path) {
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resolve_relative_path(const char* base, const char* path, char* output) {
|
||||||
|
char combined[SYS_MAX_PATH] = "";
|
||||||
|
|
||||||
|
// If path is absolute, copy as is. Otherwise, combine base and relative path
|
||||||
|
if (path[0] == '/' || path[0] == '\\') {
|
||||||
|
snprintf(combined, sizeof(combined), "%s", path);
|
||||||
|
} else {
|
||||||
|
snprintf(combined, sizeof(combined), "%s/%s", base, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* tokens[64];
|
||||||
|
int tokenCount = 0;
|
||||||
|
|
||||||
|
// Tokenize path by separators
|
||||||
|
char* token = strtok(combined, "/\\");
|
||||||
|
while (token && tokenCount < 64) {
|
||||||
|
if (strcmp(token, "..") == 0) {
|
||||||
|
// Pop last token to go up a directory
|
||||||
|
if (tokenCount > 0) { tokenCount--; }
|
||||||
|
|
||||||
|
// Ignore "." (current directory) or empty tokens
|
||||||
|
} else if (strcmp(token, ".") != 0 && token[0] != '\0') {
|
||||||
|
tokens[tokenCount++] = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, "/\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = '\0';
|
||||||
|
|
||||||
|
// Build output path from tokens
|
||||||
|
for (int i = 0; i < tokenCount; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
strncat(output, "/", SYS_MAX_PATH - strlen(output) - 1);
|
||||||
|
}
|
||||||
|
strncat(output, tokens[i], SYS_MAX_PATH - strlen(output) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_path(output);
|
||||||
|
}
|
||||||
|
|
||||||
bool path_is_relative_to(const char* fullPath, const char* baseDir) {
|
bool path_is_relative_to(const char* fullPath, const char* baseDir) {
|
||||||
return strncmp(fullPath, baseDir, strlen(baseDir)) == 0;
|
return strncmp(fullPath, baseDir, strlen(baseDir)) == 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ bool concat_path(char* destination, char* path, char* fname);
|
||||||
char* path_basename(char* path);
|
char* path_basename(char* path);
|
||||||
void path_get_folder(char* path, char* outpath);
|
void path_get_folder(char* path, char* outpath);
|
||||||
int path_depth(const char* path);
|
int path_depth(const char* path);
|
||||||
|
void resolve_relative_path(const char* base, const char* path, char* output);
|
||||||
bool path_is_relative_to(const char* fullPath, const char* baseDir);
|
bool path_is_relative_to(const char* fullPath, const char* baseDir);
|
||||||
bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
|
bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue