diff options
Diffstat (limited to 'src/filesystem.c')
-rw-r--r-- | src/filesystem.c | 667 |
1 files changed, 337 insertions, 330 deletions
diff --git a/src/filesystem.c b/src/filesystem.c index dadd977..0d6989b 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -14,12 +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 */ +#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 */ +# define _DARWIN_C_SOURCE 1 /* for flock */ #endif #include "filesystem.h" @@ -27,19 +27,19 @@ #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) -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +# include <direct.h> +# include <io.h> +# include <windows.h> +# define F_OK 0 +# define mkdir(path, flags) _mkdir(path) +# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) #else -# include <dirent.h> -# include <unistd.h> +# include <dirent.h> +# include <unistd.h> #endif #if defined(HAVE_FLOCK) && defined(HAVE_FILENO) -# include <sys/file.h> +# include <sys/file.h> #endif #include <sys/stat.h> @@ -51,21 +51,21 @@ #include <string.h> #ifndef PAGE_SIZE -# define PAGE_SIZE 4096 +# define PAGE_SIZE 4096 #endif static bool lilv_is_dir_sep(const char c) { - return c == '/' || c == LILV_DIR_SEP[0]; + 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] == '\\')); + return (isalpha(path[0]) && (path[1] == ':' || path[1] == '|') && + (path[2] == '/' || path[2] == '\\')); } #endif @@ -73,217 +73,226 @@ char* lilv_temp_directory_path(void) { #ifdef _WIN32 - DWORD len = GetTempPath(0, NULL); - char* buf = (char*)calloc(len, 1); - if (GetTempPath(len, buf) == 0) { - free(buf); - return NULL; - } - - return buf; + DWORD len = GetTempPath(0, NULL); + char* buf = (char*)calloc(len, 1); + if (GetTempPath(len, buf) == 0) { + free(buf); + return NULL; + } + + return buf; #else - const char* const tmpdir = getenv("TMPDIR"); + const char* const tmpdir = getenv("TMPDIR"); - return tmpdir ? lilv_strdup(tmpdir) : lilv_strdup("/tmp"); + return tmpdir ? lilv_strdup(tmpdir) : lilv_strdup("/tmp"); #endif } bool lilv_path_is_absolute(const char* path) { - if (lilv_is_dir_sep(path[0])) { - return true; - } + if (lilv_is_dir_sep(path[0])) { + return true; + } #ifdef _WIN32 - if (is_windows_path(path)) { - return true; - } + if (is_windows_path(path)) { + return true; + } #endif - return false; + 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; + 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_current(void) { - return getcwd(NULL, 0); + return getcwd(NULL, 0); } char* lilv_path_absolute(const char* path) { - if (lilv_path_is_absolute(path)) { - return lilv_strdup(path); - } - - char* cwd = getcwd(NULL, 0); - char* abs_path = lilv_path_join(cwd, path); - free(cwd); - return abs_path; + if (lilv_path_is_absolute(path)) { + return lilv_strdup(path); + } + + char* cwd = getcwd(NULL, 0); + char* abs_path = lilv_path_join(cwd, path); + free(cwd); + return abs_path; } char* lilv_path_absolute_child(const char* path, const char* parent) { - if (lilv_path_is_absolute(path)) { - return lilv_strdup(path); - } + if (lilv_path_is_absolute(path)) { + return lilv_strdup(path); + } - return lilv_path_join(parent, path); + return lilv_path_join(parent, 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; - } - } + 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; + } + } #ifdef _WIN32 - const bool use_slash = strchr(path, '/'); + const bool use_slash = strchr(path, '/'); #else - static const bool use_slash = true; + static const bool use_slash = true; #endif - // 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) { - if (use_slash) { - memcpy(rel + (i * 3), "../", 3); - } else { - memcpy(rel + (i * 3), "..\\", 3); - } - } - - // Write suffix - memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); - return rel; + // 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) { + if (use_slash) { + memcpy(rel + (i * 3), "../", 3); + } else { + memcpy(rel + (i * 3), "..\\", 3); + } + } + + // Write suffix + memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); + return rel; } char* lilv_path_parent(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("."); - } - - // 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; + const char* s = path + strlen(path) - 1; // Last character + + // 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 + for (; s > path && lilv_is_dir_sep(*s); --s) { + } + + if (s == path) { // Hit beginning + return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); + } + + // 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_filename(const char* path) { - const size_t path_len = strlen(path); - size_t last_sep = path_len; - for (size_t i = 0; i < path_len; ++i) { - if (lilv_is_dir_sep(path[i])) { - last_sep = i; - } - } - - if (last_sep >= path_len) { - return lilv_strdup(path); - } - - const size_t ret_len = path_len - last_sep; - char* const ret = (char*)calloc(ret_len + 1, 1); - - strncpy(ret, path + last_sep + 1, ret_len); - return ret; + const size_t path_len = strlen(path); + size_t last_sep = path_len; + for (size_t i = 0; i < path_len; ++i) { + if (lilv_is_dir_sep(path[i])) { + last_sep = i; + } + } + + if (last_sep >= path_len) { + return lilv_strdup(path); + } + + const size_t ret_len = path_len - last_sep; + char* const ret = (char*)calloc(ret_len + 1, 1); + + strncpy(ret, path + last_sep + 1, ret_len); + return ret; } char* lilv_path_join(const char* a, const char* b) { - if (!a) { - return (b && b[0]) ? lilv_strdup(b) : NULL; - } + if (!a) { + return (b && b[0]) ? lilv_strdup(b) : NULL; + } - const size_t a_len = strlen(a); - const size_t b_len = b ? strlen(b) : 0; - const bool a_end_is_sep = a_len > 0 && lilv_is_dir_sep(a[a_len - 1]); - const size_t pre_len = a_len - (a_end_is_sep ? 1 : 0); - char* path = (char*)calloc(1, a_len + b_len + 2); - memcpy(path, a, pre_len); + const size_t a_len = strlen(a); + const size_t b_len = b ? strlen(b) : 0; + const bool a_end_is_sep = a_len > 0 && lilv_is_dir_sep(a[a_len - 1]); + const size_t pre_len = a_len - (a_end_is_sep ? 1 : 0); + char* path = (char*)calloc(1, a_len + b_len + 2); + memcpy(path, a, pre_len); #ifdef _WIN32 - // Use forward slash if it seems that the input paths do - const bool a_has_slash = strchr(a, '/'); - const bool b_has_slash = b && strchr(b, '/'); - if (a_has_slash || b_has_slash) { - path[pre_len] = '/'; - } else { - path[pre_len] = '\\'; - } + // Use forward slash if it seems that the input paths do + const bool a_has_slash = strchr(a, '/'); + const bool b_has_slash = b && strchr(b, '/'); + if (a_has_slash || b_has_slash) { + path[pre_len] = '/'; + } else { + path[pre_len] = '\\'; + } #else - path[pre_len] = '/'; + path[pre_len] = '/'; #endif - 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; + 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_path_canonical(const char* path) { - if (!path) { - return NULL; - } + if (!path) { + return NULL; + } #if defined(_WIN32) - char* out = (char*)malloc(MAX_PATH); - GetFullPathName(path, MAX_PATH, out, NULL); - return out; + 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); + char* real_path = realpath(path, NULL); + return real_path ? real_path : lilv_strdup(path); #endif } @@ -291,98 +300,97 @@ bool lilv_path_exists(const char* path) { #ifdef HAVE_LSTAT - struct stat st; - return !lstat(path, &st); + struct stat st; + return !lstat(path, &st); #else - return !access(path, F_OK); + return !access(path, F_OK); #endif } bool lilv_is_directory(const char* path) { - struct stat st; - return !stat(path, &st) && S_ISDIR(st.st_mode); + struct stat st; + return !stat(path, &st) && S_ISDIR(st.st_mode); } 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 && fflush(out)) { - st = errno; - } - - if (!st && (ferror(in) || ferror(out))) { - st = EBADF; - } - - free(page); - fclose(in); - fclose(out); - - return st; + 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 && fflush(out)) { + st = errno; + } + + 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)) { + int ret = 0; + if (strcmp(oldpath, newpath)) { #ifdef _WIN32 - ret = !CreateHardLink(newpath, oldpath, 0); + ret = !CreateHardLink(newpath, oldpath, 0); #else - char* target = lilv_path_relative_to(oldpath, newpath); + char* target = lilv_path_relative_to(oldpath, newpath); - ret = symlink(target, newpath); + ret = symlink(target, newpath); - free(target); + free(target); #endif - } - return ret; + } + return ret; } int lilv_flock(FILE* file, bool lock, bool block) { #ifdef _WIN32 - HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); - OVERLAPPED overlapped = {0}; - - if (lock) { - const DWORD flags = (LOCKFILE_EXCLUSIVE_LOCK | - (block ? 0 : LOCKFILE_FAIL_IMMEDIATELY)); - - return !LockFileEx( - handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped); - } else { - return !UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped); - } + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); + OVERLAPPED overlapped = {0}; + + if (lock) { + const DWORD flags = + (LOCKFILE_EXCLUSIVE_LOCK | (block ? 0 : LOCKFILE_FAIL_IMMEDIATELY)); + + return !LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped); + } else { + return !UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped); + } #elif defined(HAVE_FLOCK) && defined(HAVE_FILENO) - return flock(fileno(file), - (lock ? LOCK_EX : LOCK_UN) | (block ? 0 : LOCK_NB)); + return flock(fileno(file), + (lock ? LOCK_EX : LOCK_UN) | (block ? 0 : LOCK_NB)); #else - return 0; + return 0; #endif } @@ -392,28 +400,28 @@ lilv_dir_for_each(const char* path, 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 { - if (strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")) { - f(path, fd.cFileName, data); - } - } while (FindNextFile(fh, &fd)); - } - FindClose(fh); - free(pat); + char* pat = lilv_path_join(path, "*"); + WIN32_FIND_DATA fd; + HANDLE fh = FindFirstFile(pat, &fd); + if (fh != INVALID_HANDLE_VALUE) { + do { + if (strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")) { + f(path, fd.cFileName, data); + } + } while (FindNextFile(fh, &fd)); + } + FindClose(fh); + free(pat); #else - DIR* dir = opendir(path); - if (dir) { - for (struct dirent* entry = NULL; (entry = readdir(dir));) { - if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { - f(path, entry->d_name, data); - } - } - closedir(dir); - } + DIR* dir = opendir(path); + if (dir) { + for (struct dirent* entry = NULL; (entry = readdir(dir));) { + if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { + f(path, entry->d_name, data); + } + } + closedir(dir); + } #endif } @@ -421,131 +429,130 @@ char* lilv_create_temporary_directory(const char* pattern) { #ifdef _WIN32 - static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - static const int n_chars = sizeof(chars) - 1; + static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const int n_chars = sizeof(chars) - 1; - const size_t pattern_len = strlen(pattern); - if (pattern_len < 7 || strcmp(pattern + pattern_len - 6, "XXXXXX")) { - errno = EINVAL; - return NULL; - } + const size_t pattern_len = strlen(pattern); + if (pattern_len < 7 || strcmp(pattern + pattern_len - 6, "XXXXXX")) { + errno = EINVAL; + return NULL; + } - char* const tmpdir = lilv_temp_directory_path(); - char* const path_pattern = lilv_path_join(tmpdir, pattern); - const size_t path_pattern_len = strlen(path_pattern); - char* const suffix = path_pattern + path_pattern_len - 6; + char* const tmpdir = lilv_temp_directory_path(); + char* const path_pattern = lilv_path_join(tmpdir, pattern); + const size_t path_pattern_len = strlen(path_pattern); + char* const suffix = path_pattern + path_pattern_len - 6; - free(tmpdir); + free(tmpdir); - for (unsigned attempt = 0; attempt < 128; ++attempt) { - for (unsigned i = 0; i < 6; ++i) { - suffix[i] = chars[rand() % n_chars]; - } + for (unsigned attempt = 0; attempt < 128; ++attempt) { + for (unsigned i = 0; i < 6; ++i) { + suffix[i] = chars[rand() % n_chars]; + } - if (!mkdir(path_pattern, 0700)) { - return path_pattern; - } - } + if (!mkdir(path_pattern, 0700)) { + return path_pattern; + } + } - return NULL; + return NULL; #else - char* const tmpdir = lilv_temp_directory_path(); - char* const path_pattern = lilv_path_join(tmpdir, pattern); + char* const tmpdir = lilv_temp_directory_path(); + char* const path_pattern = lilv_path_join(tmpdir, pattern); - free(tmpdir); - return mkdtemp(path_pattern); // NOLINT (not a leak) + free(tmpdir); + return mkdtemp(path_pattern); // NOLINT (not a leak) #endif } int lilv_create_directories(const char* dir_path) { - char* path = lilv_strdup(dir_path); - const size_t path_len = strlen(path); - size_t i = 1; + 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; - } + 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 || !lilv_is_directory(path))) { - free(path); - return errno; - } - path[i] = c; - } - } - - free(path); - return 0; + 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 || !lilv_is_directory(path))) { + 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; + struct stat buf; + if (stat(path, &buf)) { + return 0; + } + return buf.st_size; } int lilv_remove(const char* path) { #ifdef _WIN32 - if (lilv_is_directory(path)) { - return !RemoveDirectory(path); - } + if (lilv_is_directory(path)) { + return !RemoveDirectory(path); + } #endif - return remove(path); + return remove(path); } 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_path_canonical(a_path); - char* const b_real = lilv_path_canonical(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; + 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_path_canonical(a_path); + char* const b_real = lilv_path_canonical(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; } |