diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/filesystem.c | 405 | ||||
-rw-r--r-- | src/filesystem.h | 129 | ||||
-rw-r--r-- | src/lilv_internal.h | 19 | ||||
-rw-r--r-- | src/node.c | 1 | ||||
-rw-r--r-- | src/plugin.c | 1 | ||||
-rw-r--r-- | src/state.c | 38 | ||||
-rw-r--r-- | src/util.c | 384 | ||||
-rw-r--r-- | src/world.c | 1 |
8 files changed, 568 insertions, 410 deletions
diff --git a/src/filesystem.c b/src/filesystem.c new file mode 100644 index 0000000..dbb528f --- /dev/null +++ b/src/filesystem.c @@ -0,0 +1,405 @@ +/* + Copyright 2007-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#define _POSIX_C_SOURCE 200809L /* for fileno */ +#define _BSD_SOURCE 1 /* for realpath, symlink */ +#define _DEFAULT_SOURCE 1 /* for realpath, symlink */ + +#ifdef __APPLE__ +# define _DARWIN_C_SOURCE 1 /* for flock */ +#endif + +#include "filesystem.h" +#include "lilv_config.h" +#include "lilv_internal.h" + +#ifdef _WIN32 +# include <windows.h> +# include <direct.h> +# include <io.h> +# define F_OK 0 +# define mkdir(path, flags) _mkdir(path) +#else +# include <dirent.h> +# include <unistd.h> +#endif + +#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) +# include <sys/file.h> +#endif + +#include <sys/stat.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +#endif + +static bool +lilv_is_dir_sep(const char c) +{ + return c == '/' || c == LILV_DIR_SEP[0]; +} + +#ifdef _WIN32 +static inline bool +is_windows_path(const char* path) +{ + return (isalpha(path[0]) && (path[1] == ':' || path[1] == '|') && + (path[2] == '/' || path[2] == '\\')); +} +#endif + +bool +lilv_path_is_absolute(const char* path) +{ + if (lilv_is_dir_sep(path[0])) { + return true; + } + +#ifdef _WIN32 + if (is_windows_path(path)) { + return true; + } +#endif + + return false; +} + +bool +lilv_path_is_child(const char* path, const char* dir) +{ + if (path && dir) { + const size_t path_len = strlen(path); + const size_t dir_len = strlen(dir); + return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); + } + return false; +} + +char* +lilv_path_absolute(const char* path) +{ + if (lilv_path_is_absolute(path)) { + return lilv_strdup(path); + } else { + char* cwd = getcwd(NULL, 0); + char* abs_path = lilv_path_join(cwd, path); + free(cwd); + return abs_path; + } +} + +char* +lilv_path_relative_to(const char* path, const char* base) +{ + const size_t path_len = strlen(path); + const size_t base_len = strlen(base); + const size_t min_len = (path_len < base_len) ? path_len : base_len; + + // Find the last separator common to both paths + size_t last_shared_sep = 0; + for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { + if (lilv_is_dir_sep(path[i])) { + last_shared_sep = i; + } + } + + if (last_shared_sep == 0) { + // No common components, return path + return lilv_strdup(path); + } + + // Find the number of up references ("..") required + size_t up = 0; + for (size_t i = last_shared_sep + 1; i < base_len; ++i) { + if (lilv_is_dir_sep(base[i])) { + ++up; + } + } + + // Write up references + const size_t suffix_len = path_len - last_shared_sep; + char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); + for (size_t i = 0; i < up; ++i) { + memcpy(rel + (i * 3), "../", 3); + } + + // Write suffix + memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); + return rel; +} + +char* +lilv_dirname(const char* path) +{ + const char* s = path + strlen(path) - 1; // Last character + for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash + for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash + for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates + + if (s == path) { // Hit beginning + return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); + } else { // Pointing to the last character of the result (inclusive) + char* dirname = (char*)malloc(s - path + 2); + memcpy(dirname, path, s - path + 1); + dirname[s - path + 1] = '\0'; + return dirname; + } +} + +char* +lilv_path_join(const char* a, const char* b) +{ + if (!a) { + return lilv_strdup(b); + } + + const size_t a_len = strlen(a); + const size_t b_len = b ? strlen(b) : 0; + const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); + char* path = (char*)calloc(1, a_len + b_len + 2); + memcpy(path, a, pre_len); + path[pre_len] = '/'; + if (b) { + memcpy(path + pre_len + 1, + b + (lilv_is_dir_sep(b[0]) ? 1 : 0), + lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); + } + return path; +} + +char* +lilv_dir_path(const char* path) +{ + if (!path) { + return NULL; + } + + const size_t len = strlen(path); + + if (lilv_is_dir_sep(path[len - 1])) { + return lilv_strdup(path); + } + + char* dir_path = (char*)calloc(len + 2, 1); + memcpy(dir_path, path, len); + dir_path[len] = LILV_DIR_SEP[0]; + return dir_path; +} + +char* +lilv_realpath(const char* path) +{ + if (!path) { + return NULL; + } + +#if defined(_WIN32) + char* out = (char*)malloc(MAX_PATH); + GetFullPathName(path, MAX_PATH, out, NULL); + return out; +#else + char* real_path = realpath(path, NULL); + return real_path ? real_path : lilv_strdup(path); +#endif +} + +bool +lilv_path_exists(const char* path) +{ +#ifdef HAVE_LSTAT + struct stat st; + return !lstat(path, &st); +#else + return !access(path, F_OK); +#endif +} + +int +lilv_copy_file(const char* src, const char* dst) +{ + FILE* in = fopen(src, "r"); + if (!in) { + return errno; + } + + FILE* out = fopen(dst, "w"); + if (!out) { + fclose(in); + return errno; + } + + char* page = (char*)malloc(PAGE_SIZE); + size_t n_read = 0; + int st = 0; + while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { + if (fwrite(page, 1, n_read, out) != n_read) { + st = errno; + break; + } + } + + if (!st && (ferror(in) || ferror(out))) { + st = EBADF; + } + + free(page); + fclose(in); + fclose(out); + + return st; +} + +int +lilv_symlink(const char* oldpath, const char* newpath) +{ + int ret = 0; + if (strcmp(oldpath, newpath)) { +#ifdef _WIN32 +#ifdef HAVE_CREATESYMBOLICLINK + ret = !CreateSymbolicLink(newpath, oldpath, 0); +#endif + if (ret) { + ret = !CreateHardLink(newpath, oldpath, 0); + } +#else + ret = symlink(oldpath, newpath); +#endif + } + return ret; +} + +int +lilv_flock(FILE* file, bool lock) +{ +#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) + return flock(fileno(file), lock ? LOCK_EX : LOCK_UN); +#else + return 0; +#endif +} + +void +lilv_dir_for_each(const char* path, + void* data, + void (*f)(const char* path, const char* name, void* data)) +{ +#ifdef _WIN32 + char* pat = lilv_path_join(path, "*"); + WIN32_FIND_DATA fd; + HANDLE fh = FindFirstFile(pat, &fd); + if (fh != INVALID_HANDLE_VALUE) { + do { + f(path, fd.cFileName, data); + } while (FindNextFile(fh, &fd)); + } + free(pat); +#else + DIR* dir = opendir(path); + if (dir) { + for (struct dirent* entry = NULL; (entry = readdir(dir));) { + f(path, entry->d_name, data); + } + closedir(dir); + } +#endif +} + +int +lilv_mkdir_p(const char* dir_path) +{ + char* path = lilv_strdup(dir_path); + const size_t path_len = strlen(path); + size_t i = 1; + +#ifdef _WIN32 + if (is_windows_path(dir_path)) { + i = 3; + } +#endif + + for (; i <= path_len; ++i) { + const char c = path[i]; + if (c == LILV_DIR_SEP[0] || c == '/' || c == '\0') { + path[i] = '\0'; + if (mkdir(path, 0755) && errno != EEXIST) { + free(path); + return errno; + } + path[i] = c; + } + } + + free(path); + return 0; +} + +static off_t +lilv_file_size(const char* path) +{ + struct stat buf; + if (stat(path, &buf)) { + return 0; + } + return buf.st_size; +} + +bool +lilv_file_equals(const char* a_path, const char* b_path) +{ + if (!strcmp(a_path, b_path)) { + return true; // Paths match + } + + bool match = false; + FILE* a_file = NULL; + FILE* b_file = NULL; + char* const a_real = lilv_realpath(a_path); + char* const b_real = lilv_realpath(b_path); + if (!strcmp(a_real, b_real)) { + match = true; // Real paths match + } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { + match = false; // Sizes differ + } else if (!(a_file = fopen(a_real, "rb")) || + !(b_file = fopen(b_real, "rb"))) { + match = false; // Missing file matches nothing + } else { + // TODO: Improve performance by reading chunks + match = true; + while (!feof(a_file) && !feof(b_file)) { + if (fgetc(a_file) != fgetc(b_file)) { + match = false; + break; + } + } + } + + if (a_file) { + fclose(a_file); + } + if (b_file) { + fclose(b_file); + } + free(a_real); + free(b_real); + return match; +} diff --git a/src/filesystem.h b/src/filesystem.h new file mode 100644 index 0000000..de06214 --- /dev/null +++ b/src/filesystem.h @@ -0,0 +1,129 @@ +/* + Copyright 2007-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include <stdbool.h> +#include <stdio.h> + +/// Return true iff `path` is an absolute path +bool +lilv_path_is_absolute(const char* path); + +/// Return true iff `path` is a child of `dir` +bool +lilv_path_is_child(const char* path, const char* dir); + +/** + Return `path` as an absolute path. + + If `path` is absolute, an identical copy of it is returned. Otherwise, the + returned path is relative to the current working directory. +*/ +char* +lilv_path_absolute(const char* path); + +/** + Return `path` relative to `base` if possible. + + If `path` is not within `base`, a copy is returned. Otherwise, an + equivalent path relative to `base` is returned (which may contain + up-references). +*/ +char* +lilv_path_relative_to(const char* path, const char* base); + +/** + Return the path to the directory that contains `path`. + + Returns the root path if `path` is the root path. +*/ +char* +lilv_dirname(const char* path); + +/// Join path `a` and path `b` with a single directory separator between them +char* +lilv_path_join(const char* a, const char* b); + +/// Return `path` normalized to have a trailing directory separator +char* +lilv_dir_path(const char* path); + +/** + Return `path` as a canonicalized absolute path. + + This expands all symbolic links, relative references, and removes extra + directory separators. +*/ +char* +lilv_realpath(const char* path); + +/// Return true iff `path` points to an existing file system entry +bool +lilv_path_exists(const char* path); + +/** + Copy the file at path `src` to path `dst`. + + @return Zero on success, or a standard `errno` error code. +*/ +int +lilv_copy_file(const char* src, const char* dst); + +/** + Create a symlink at `newpath` that points to `oldpath`. + + @return Zero on success, otherwise non-zero and `errno` is set. +*/ +int +lilv_symlink(const char* oldpath, const char* newpath); + +/** + Set or remove an advisory exclusive lock on `file`. + + If the `lock` is true and the file is already locked, then this will not + succeed and non-zero will be returned. + + @return Zero on success. +*/ +int +lilv_flock(FILE* file, bool lock); + +/** + Visit every file in the directory at `path`. + + @param path A path to a directory. + + @param data Opaque user data that is passed to `f`. + + @param f A function called on every entry in the directory. The `path` + parameter is always the directory path passed to this function, the `name` + parameter is the name of the directory entry (not its full path). +*/ +void +lilv_dir_for_each(const char* path, + void* data, + void (*f)(const char* path, const char* name, void* data)); + +/** + Create the directory `dir_path` and any parent directories if necessary. + + @return Zero on success, or an `errno` error code. +*/ +int +lilv_mkdir_p(const char* dir_path); + +/// Return true iff the given paths point to files with identical contents +bool +lilv_file_equals(const char* a_path, const char* b_path); diff --git a/src/lilv_internal.h b/src/lilv_internal.h index 3a36837..212e41a 100644 --- a/src/lilv_internal.h +++ b/src/lilv_internal.h @@ -381,32 +381,13 @@ char* lilv_strjoin(const char* first, ...); char* lilv_strdup(const char* str); char* lilv_get_lang(void); char* lilv_expand(const char* path); -char* lilv_dirname(const char* path); -char* lilv_dir_path(const char* path); -int lilv_copy_file(const char* src, const char* dst); -bool lilv_path_exists(const char* path, const void* ignored); -char* lilv_path_absolute(const char* path); -bool lilv_path_is_absolute(const char* path); char* lilv_get_latest_copy(const char* path, const char* copy_path); -char* lilv_path_relative_to(const char* path, const char* base); -bool lilv_path_is_child(const char* path, const char* dir); -int lilv_flock(FILE* file, bool lock); -char* lilv_realpath(const char* path); -int lilv_symlink(const char* oldpath, const char* newpath); -int lilv_mkdir_p(const char* dir_path); -char* lilv_path_join(const char* a, const char* b); -bool lilv_file_equals(const char* a_path, const char* b_path); char* lilv_find_free_path(const char* in_path, bool (*exists)(const char*, const void*), const void* user_data); -void -lilv_dir_for_each(const char* path, - void* data, - void (*f)(const char* path, const char* name, void* data)); - typedef void (*LilvVoidFunc)(void); /** dlsym wrapper to return a function pointer (without annoying warning) */ @@ -14,6 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "filesystem.h" #include "lilv_internal.h" #include "lilv/lilv.h" diff --git a/src/plugin.c b/src/plugin.c index a6919d5..9e9eba1 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -14,7 +14,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "lilv_config.h" #include "lilv_internal.h" #include "lilv/lilv.h" diff --git a/src/state.c b/src/state.c index e4e381a..749316c 100644 --- a/src/state.c +++ b/src/state.c @@ -14,6 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "filesystem.h" #include "lilv_internal.h" #include "lilv/lilv.h" @@ -228,6 +229,12 @@ retrieve_callback(LV2_State_Handle handle, } static bool +path_exists(const char* path, const void* ignored) +{ + return lilv_path_exists(path); +} + +static bool lilv_state_has_path(const char* path, const void* state) { return lilv_state_rel2abs((const LilvState*)state, path) != path; @@ -277,7 +284,7 @@ abstract_path(LV2_State_Map_Path_Handle handle, if (!copy || !lilv_file_equals(real_path, copy)) { // No recent enough copy, make a new one free(copy); - copy = lilv_find_free_path(cpath, lilv_path_exists, NULL); + copy = lilv_find_free_path(cpath, path_exists, NULL); if ((st = lilv_copy_file(real_path, copy))) { LILV_ERRORF("Error copying state file %s (%s)\n", copy, strerror(st)); @@ -956,7 +963,7 @@ static bool link_exists(const char* path, const void* data) { const char* target = (const char*)data; - if (!lilv_path_exists(path, NULL)) { + if (!lilv_path_exists(path)) { return false; } char* real_path = lilv_realpath(path); @@ -968,7 +975,19 @@ link_exists(const char* path, const void* data) static int maybe_symlink(const char* oldpath, const char* newpath) { - return link_exists(newpath, oldpath) ? 0 : lilv_symlink(oldpath, newpath); + if (link_exists(newpath, oldpath)) { + return 0; + } + + const int st = lilv_symlink(oldpath, newpath); + if (st) { + LILV_ERRORF("Failed to link %s => %s (%s)\n", + newpath, + oldpath, + strerror(errno)); + } + + return st; } static void @@ -1125,8 +1144,13 @@ lilv_state_make_links(const LilvState* state, const char* dir) } else { // Make a link in the link directory to external file char* lpath = lilv_find_free_path(pat, link_exists, pm->abs); - if (!lilv_path_exists(lpath, NULL)) { - lilv_symlink(pm->abs, lpath); + if (!lilv_path_exists(lpath)) { + if (lilv_symlink(pm->abs, lpath)) { + LILV_ERRORF("Failed to link %s => %s (%s)\n", + pm->abs, + lpath, + strerror(errno)); + } } // Make a link in the save directory to the external link @@ -1228,7 +1252,7 @@ static void try_unlink(const char* state_dir, const char* path) { if (!strncmp(state_dir, path, strlen(state_dir))) { - if (lilv_path_exists(path, NULL) && unlink(path)) { + if (lilv_path_exists(path) && unlink(path)) { LILV_ERRORF("Failed to remove %s (%s)\n", path, strerror(errno)); } } @@ -1246,7 +1270,7 @@ lilv_state_delete(LilvWorld* world, LilvNode* bundle = lilv_new_file_uri(world, NULL, state->dir); LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle); char* manifest_path = lilv_node_get_path(manifest, NULL); - const bool has_manifest = lilv_path_exists(manifest_path, NULL); + const bool has_manifest = lilv_path_exists(manifest_path); SordModel* model = sord_new(world->world, SORD_SPO, false); if (has_manifest) { @@ -14,35 +14,12 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_C_SOURCE 200809L /* for fileno */ -#define _BSD_SOURCE 1 /* for realpath, symlink */ -#define _DEFAULT_SOURCE 1 /* for realpath, symlink */ - -#ifdef __APPLE__ -# define _DARWIN_C_SOURCE 1 /* for flock */ -#endif - -#include "lilv_config.h" +#include "filesystem.h" #include "lilv_internal.h" #include "lilv/lilv.h" #include "serd/serd.h" -#ifdef _WIN32 -# include <windows.h> -# include <direct.h> -# include <io.h> -# define F_OK 0 -# define mkdir(path, flags) _mkdir(path) -#else -# include <dirent.h> -# include <unistd.h> -#endif - -#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) -# include <sys/file.h> -#endif - #include <sys/stat.h> #include <sys/types.h> @@ -56,10 +33,6 @@ #include <stdlib.h> #include <string.h> -#ifndef PAGE_SIZE -# define PAGE_SIZE 4096 -#endif - void lilv_free(void* ptr) { @@ -240,60 +213,6 @@ lilv_expand(const char* path) return out; } -static bool -lilv_is_dir_sep(const char c) -{ - return c == '/' || c == LILV_DIR_SEP[0]; -} - -char* -lilv_dirname(const char* path) -{ - const char* s = path + strlen(path) - 1; // Last character - for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash - for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash - for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates - - if (s == path) { // Hit beginning - return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); - } else { // Pointing to the last character of the result (inclusive) - char* dirname = (char*)malloc(s - path + 2); - memcpy(dirname, path, s - path + 1); - dirname[s - path + 1] = '\0'; - return dirname; - } -} - -char* -lilv_dir_path(const char* path) -{ - if (!path) { - return NULL; - } - - const size_t len = strlen(path); - - if (lilv_is_dir_sep(path[len - 1])) { - return lilv_strdup(path); - } - - char* dir_path = (char*)calloc(len + 2, 1); - memcpy(dir_path, path, len); - dir_path[len] = LILV_DIR_SEP[0]; - return dir_path; -} - -bool -lilv_path_exists(const char* path, const void* ignored) -{ -#ifdef HAVE_LSTAT - struct stat st; - return !lstat(path, &st); -#else - return !access(path, F_OK); -#endif -} - char* lilv_find_free_path(const char* in_path, bool (*exists)(const char*, const void*), @@ -313,100 +232,6 @@ lilv_find_free_path(const char* in_path, return NULL; } -int -lilv_copy_file(const char* src, const char* dst) -{ - FILE* in = fopen(src, "r"); - if (!in) { - return errno; - } - - FILE* out = fopen(dst, "w"); - if (!out) { - fclose(in); - return errno; - } - - char* page = (char*)malloc(PAGE_SIZE); - size_t n_read = 0; - int st = 0; - while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { - if (fwrite(page, 1, n_read, out) != n_read) { - st = errno; - break; - } - } - - if (!st && (ferror(in) || ferror(out))) { - st = EBADF; - } - - free(page); - fclose(in); - fclose(out); - - return st; -} - -#ifdef _WIN32 -static inline bool -is_windows_path(const char* path) -{ - return (isalpha(path[0]) && (path[1] == ':' || path[1] == '|') && - (path[2] == '/' || path[2] == '\\')); -} -#endif - -bool -lilv_path_is_absolute(const char* path) -{ - if (lilv_is_dir_sep(path[0])) { - return true; - } - -#ifdef _WIN32 - if (is_windows_path(path)) { - return true; - } -#endif - - return false; -} - -char* -lilv_path_absolute(const char* path) -{ - if (lilv_path_is_absolute(path)) { - return lilv_strdup(path); - } else { - char* cwd = getcwd(NULL, 0); - char* abs_path = lilv_path_join(cwd, path); - free(cwd); - return abs_path; - } -} - -char* -lilv_path_join(const char* a, const char* b) -{ - if (!a) { - return lilv_strdup(b); - } - - const size_t a_len = strlen(a); - const size_t b_len = b ? strlen(b) : 0; - const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); - char* path = (char*)calloc(1, a_len + b_len + 2); - memcpy(path, a, pre_len); - path[pre_len] = '/'; - if (b) { - memcpy(path + pre_len + 1, - b + (lilv_is_dir_sep(b[0]) ? 1 : 0), - lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); - } - return path; -} - typedef struct { char* pattern; time_t time; @@ -456,210 +281,3 @@ lilv_get_latest_copy(const char* path, const char* copy_path) return latest.latest; } -char* -lilv_realpath(const char* path) -{ - if (!path) { - return NULL; - } - -#if defined(_WIN32) - char* out = (char*)malloc(MAX_PATH); - GetFullPathName(path, MAX_PATH, out, NULL); - return out; -#else - char* real_path = realpath(path, NULL); - return real_path ? real_path : lilv_strdup(path); -#endif -} - -int -lilv_symlink(const char* oldpath, const char* newpath) -{ - int ret = 0; - if (strcmp(oldpath, newpath)) { -#ifdef _WIN32 -#ifdef HAVE_CREATESYMBOLICLINK - ret = !CreateSymbolicLink(newpath, oldpath, 0); -#endif - if (ret) { - ret = !CreateHardLink(newpath, oldpath, 0); - } -#else - ret = symlink(oldpath, newpath); -#endif - } - if (ret) { - LILV_ERRORF("Failed to link %s => %s (%s)\n", - newpath, oldpath, strerror(errno)); - } - return ret; -} - -char* -lilv_path_relative_to(const char* path, const char* base) -{ - const size_t path_len = strlen(path); - const size_t base_len = strlen(base); - const size_t min_len = (path_len < base_len) ? path_len : base_len; - - // Find the last separator common to both paths - size_t last_shared_sep = 0; - for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { - if (lilv_is_dir_sep(path[i])) { - last_shared_sep = i; - } - } - - if (last_shared_sep == 0) { - // No common components, return path - return lilv_strdup(path); - } - - // Find the number of up references ("..") required - size_t up = 0; - for (size_t i = last_shared_sep + 1; i < base_len; ++i) { - if (lilv_is_dir_sep(base[i])) { - ++up; - } - } - - // Write up references - const size_t suffix_len = path_len - last_shared_sep; - char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); - for (size_t i = 0; i < up; ++i) { - memcpy(rel + (i * 3), "../", 3); - } - - // Write suffix - memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); - return rel; -} - -bool -lilv_path_is_child(const char* path, const char* dir) -{ - if (path && dir) { - const size_t path_len = strlen(path); - const size_t dir_len = strlen(dir); - return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); - } - return false; -} - -int -lilv_flock(FILE* file, bool lock) -{ -#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) - return flock(fileno(file), lock ? LOCK_EX : LOCK_UN); -#else - return 0; -#endif -} - -void -lilv_dir_for_each(const char* path, - void* data, - void (*f)(const char* path, const char* name, void* data)) -{ -#ifdef _WIN32 - char* pat = lilv_path_join(path, "*"); - WIN32_FIND_DATA fd; - HANDLE fh = FindFirstFile(pat, &fd); - if (fh != INVALID_HANDLE_VALUE) { - do { - f(path, fd.cFileName, data); - } while (FindNextFile(fh, &fd)); - } - free(pat); -#else - DIR* dir = opendir(path); - if (dir) { - for (struct dirent* entry = NULL; (entry = readdir(dir));) { - f(path, entry->d_name, data); - } - closedir(dir); - } -#endif -} - -int -lilv_mkdir_p(const char* dir_path) -{ - char* path = lilv_strdup(dir_path); - const size_t path_len = strlen(path); - size_t i = 1; - -#ifdef _WIN32 - if (is_windows_path(dir_path)) { - i = 3; - } -#endif - - for (; i <= path_len; ++i) { - const char c = path[i]; - if (c == LILV_DIR_SEP[0] || c == '/' || c == '\0') { - path[i] = '\0'; - if (mkdir(path, 0755) && errno != EEXIST) { - free(path); - return errno; - } - path[i] = c; - } - } - - free(path); - return 0; -} - -static off_t -lilv_file_size(const char* path) -{ - struct stat buf; - if (stat(path, &buf)) { - LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); - return 0; - } - return buf.st_size; -} - -bool -lilv_file_equals(const char* a_path, const char* b_path) -{ - if (!strcmp(a_path, b_path)) { - return true; // Paths match - } - - bool match = false; - FILE* a_file = NULL; - FILE* b_file = NULL; - char* const a_real = lilv_realpath(a_path); - char* const b_real = lilv_realpath(b_path); - if (!strcmp(a_real, b_real)) { - match = true; // Real paths match - } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { - match = false; // Sizes differ - } else if (!(a_file = fopen(a_real, "rb")) || - !(b_file = fopen(b_real, "rb"))) { - match = false; // Missing file matches nothing - } else { - // TODO: Improve performance by reading chunks - match = true; - while (!feof(a_file) && !feof(b_file)) { - if (fgetc(a_file) != fgetc(b_file)) { - match = false; - break; - } - } - } - - if (a_file) { - fclose(a_file); - } - if (b_file) { - fclose(b_file); - } - free(a_real); - free(b_real); - return match; -} diff --git a/src/world.c b/src/world.c index 2650490..17896ee 100644 --- a/src/world.c +++ b/src/world.c @@ -14,6 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "filesystem.h" #include "lilv_config.h" #include "lilv_internal.h" |