summaryrefslogtreecommitdiff
path: root/src/fileops.c
diff options
context:
space:
mode:
authorChris Young <chris@unsatisfactorysoftware.co.uk>2012-06-07 20:29:22 +0100
committerChris Young <chris@unsatisfactorysoftware.co.uk>2012-06-07 20:29:22 +0100
commitc3f35902f3f951de5ce5193409f336ee45c682b6 (patch)
treee7329cc1496e676a65fb108bb9e830437e5ced7f /src/fileops.c
parentcada414a8044307b28f7a4c75986e5473bb4bc1c (diff)
parentcddb8efe564738873a4cf9ac63b7976d74035ae9 (diff)
downloadlibgit2-c3f35902f3f951de5ce5193409f336ee45c682b6.tar.gz
Merge remote-tracking branch 'source/development' into update-test
Merging main libgit2! Conflicts: CMakeLists.txt src/unix/map.c
Diffstat (limited to 'src/fileops.c')
-rw-r--r--src/fileops.c728
1 files changed, 329 insertions, 399 deletions
diff --git a/src/fileops.c b/src/fileops.c
index 73939349d..95a65893c 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -1,350 +1,283 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
#include "common.h"
#include "fileops.h"
#include <ctype.h>
-int gitfo_mkdir_2file(const char *file_path)
+int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
- const int mode = 0755; /* or 0777 ? */
- int error = GIT_SUCCESS;
- char target_folder_path[GIT_PATH_MAX];
+ int result = 0;
+ git_buf target_folder = GIT_BUF_INIT;
- error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
- if (error < GIT_SUCCESS)
- return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
+ if (git_path_dirname_r(&target_folder, file_path) < 0)
+ return -1;
/* Does the containing folder exist? */
- if (gitfo_isdir(target_folder_path)) {
- git__joinpath(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */
-
+ if (git_path_isdir(target_folder.ptr) == false)
/* Let's create the tree structure */
- error = gitfo_mkdir_recurs(target_folder_path, mode);
- if (error < GIT_SUCCESS)
- return error; /* The callee already takes care of setting the correct error message. */
- }
+ result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
- return GIT_SUCCESS;
+ git_buf_free(&target_folder);
+ return result;
}
-int gitfo_mktemp(char *path_out, const char *filename)
+int git_futils_mktmp(git_buf *path_out, const char *filename)
{
int fd;
- strcpy(path_out, filename);
- strcat(path_out, "_git2_XXXXXX");
-
-#if defined(_MSC_VER)
- /* FIXME: there may be race conditions when multi-threading
- * with the library */
- if (_mktemp_s(path_out, GIT_PATH_MAX) != 0)
- return git__throw(GIT_EOSERR, "Failed to make temporary file %s", path_out);
-
- fd = gitfo_creat(path_out, 0744);
-#else
- fd = mkstemp(path_out);
-#endif
+ git_buf_sets(path_out, filename);
+ git_buf_puts(path_out, "_git2_XXXXXX");
- return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out);
-}
+ if (git_buf_oom(path_out))
+ return -1;
-int gitfo_open(const char *path, int flags)
-{
- int fd = open(path, flags | O_BINARY);
- return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to open %s", path);
-}
+ if ((fd = p_mkstemp(path_out->ptr)) < 0) {
+ giterr_set(GITERR_OS,
+ "Failed to create temporary file '%s'", path_out->ptr);
+ return -1;
+ }
-int gitfo_creat(const char *path, int mode)
-{
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
- return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create file. Could not open %s", path);
+ return fd;
}
-int gitfo_creat_force(const char *path, int mode)
+int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
{
- if (gitfo_mkdir_2file(path) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to create file %s", path);
+ int fd;
- return gitfo_creat(path, mode);
-}
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
-int gitfo_read(git_file fd, void *buf, size_t cnt)
-{
- char *b = buf;
- while (cnt) {
- ssize_t r = read(fd, b, cnt);
- if (r < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return git__throw(GIT_EOSERR, "Failed to read from file");
- }
- if (!r) {
- errno = EPIPE;
- return git__throw(GIT_EOSERR, "Failed to read from file");
- }
- cnt -= r;
- b += r;
+ fd = p_creat(path, mode);
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create file '%s'", path);
+ return -1;
}
- return GIT_SUCCESS;
-}
-int gitfo_write(git_file fd, void *buf, size_t cnt)
-{
- char *b = buf;
- while (cnt) {
- ssize_t r = write(fd, b, cnt);
- if (r < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return git__throw(GIT_EOSERR, "Failed to write to file");
- }
- if (!r) {
- errno = EPIPE;
- return git__throw(GIT_EOSERR, "Failed to write to file");
- }
- cnt -= r;
- b += r;
- }
- return GIT_SUCCESS;
+ return fd;
}
-int gitfo_isdir(const char *path)
+int git_futils_creat_locked(const char *path, const mode_t mode)
{
- struct stat st;
- int len, stat_error;
+ int fd;
- if (!path)
- return git__throw(GIT_ENOTFOUND, "No path given to gitfo_isdir");
+#ifdef GIT_WIN32
+ wchar_t* buf;
- len = strlen(path);
+ buf = gitwin_to_utf16(path);
+ fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ git__free(buf);
+#else
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+#endif
- /* win32: stat path for folders cannot end in a slash */
- if (path[len - 1] == '/') {
- char *path_fixed = NULL;
- path_fixed = git__strdup(path);
- path_fixed[len - 1] = 0;
- stat_error = gitfo_stat(path_fixed, &st);
- free(path_fixed);
- } else {
- stat_error = gitfo_stat(path, &st);
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
+ return -1;
}
- if (stat_error < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "%s does not exist", path);
+ return fd;
+}
- if (!S_ISDIR(st.st_mode))
- return git__throw(GIT_ENOTFOUND, "%s is a file", path);
+int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
+{
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
- return GIT_SUCCESS;
+ return git_futils_creat_locked(path, mode);
}
-int gitfo_exists(const char *path)
+int git_futils_open_ro(const char *path)
{
- assert(path);
- return access(path, F_OK);
+ int fd = p_open(path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ fd = GIT_ENOTFOUND;
+ giterr_set(GITERR_OS, "Failed to open '%s'", path);
+ }
+ return fd;
}
-git_off_t gitfo_size(git_file fd)
+git_off_t git_futils_filesize(git_file fd)
{
struct stat sb;
- if (gitfo_fstat(fd, &sb))
- return git__throw(GIT_EOSERR, "Failed to get size of file. File missing or corrupted");
+
+ if (p_fstat(fd, &sb)) {
+ giterr_set(GITERR_OS, "Failed to stat file descriptor");
+ return -1;
+ }
+
return sb.st_size;
}
-int gitfo_read_file(gitfo_buf *obj, const char *path)
+mode_t git_futils_canonical_mode(mode_t raw_mode)
+{
+ if (S_ISREG(raw_mode))
+ return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
+ else if (S_ISLNK(raw_mode))
+ return S_IFLNK;
+ else if (S_ISGITLINK(raw_mode))
+ return S_IFGITLINK;
+ else if (S_ISDIR(raw_mode))
+ return S_IFDIR;
+ else
+ return 0;
+}
+
+int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
{
git_file fd;
size_t len;
- git_off_t size;
- unsigned char *buff;
+ struct stat st;
- assert(obj && path && *path);
+ assert(buf && path && *path);
- if ((fd = gitfo_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_ERROR, "Failed to open %s for reading", path);
+ if (updated != NULL)
+ *updated = 0;
- if (((size = gitfo_size(fd)) < 0) || !git__is_sizet(size+1)) {
- gitfo_close(fd);
- return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
- }
- len = (size_t) size;
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
- if ((buff = git__malloc(len + 1)) == NULL) {
- gitfo_close(fd);
- return GIT_ENOMEM;
+ if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+ p_close(fd);
+ giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
+ return -1;
}
- if (gitfo_read(fd, buff, len) < 0) {
- gitfo_close(fd);
- free(buff);
- return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
+ /*
+ * If we were given a time, we only want to read the file if it
+ * has been modified.
+ */
+ if (mtime != NULL && *mtime >= st.st_mtime) {
+ p_close(fd);
+ return 0;
}
- buff[len] = '\0';
- gitfo_close(fd);
+ if (mtime != NULL)
+ *mtime = st.st_mtime;
- obj->data = buff;
- obj->len = len;
+ len = (size_t) st.st_size;
- return GIT_SUCCESS;
-}
-
-void gitfo_free_buf(gitfo_buf *obj)
-{
- assert(obj);
- free(obj->data);
- obj->data = NULL;
-}
+ git_buf_clear(buf);
-int gitfo_mv(const char *from, const char *to)
-{
- int error;
-
-#ifdef GIT_WIN32
- /*
- * Win32 POSIX compilance my ass. If the destination
- * file exists, the `rename` call fails. This is as
- * close as it gets with the Win32 API.
- */
- error = MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
-#else
- /* Don't even try this on Win32 */
- if (!link(from, to)) {
- gitfo_unlink(from);
- return GIT_SUCCESS;
+ if (git_buf_grow(buf, len + 1) < 0) {
+ p_close(fd);
+ return -1;
}
- if (!rename(from, to))
- return GIT_SUCCESS;
+ buf->ptr[len] = '\0';
- error = GIT_EOSERR;
-#endif
+ while (len > 0) {
+ ssize_t read_size = p_read(fd, buf->ptr, len);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to move file from `%s` to `%s`", from, to);
+ if (read_size < 0) {
+ p_close(fd);
+ giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
+ return -1;
+ }
- return GIT_SUCCESS;
-}
+ len -= read_size;
+ buf->size += read_size;
+ }
-int gitfo_mv_force(const char *from, const char *to)
-{
- if (gitfo_mkdir_2file(to) < GIT_SUCCESS)
- return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
+ p_close(fd);
- return gitfo_mv(from, to); /* The callee already takes care of setting the correct error message. */
-}
+ if (updated != NULL)
+ *updated = 1;
-int gitfo_map_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
-{
- if (git__mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin) < GIT_SUCCESS)
- return GIT_EOSERR;
- return GIT_SUCCESS;
+ return 0;
}
-void gitfo_free_map(git_map *out)
+int git_futils_readbuffer(git_buf *buf, const char *path)
{
- git__munmap(out);
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
-int gitfo_dirent(
- char *path,
- size_t path_sz,
- int (*fn)(void *, char *),
- void *arg)
+int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
- size_t wd_len = strlen(path);
- DIR *dir;
- struct dirent *de;
-
- if (!wd_len || path_sz < wd_len + 2)
- return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path);
-
- while (path[wd_len - 1] == '/')
- wd_len--;
- path[wd_len++] = '/';
- path[wd_len] = '\0';
-
- dir = opendir(path);
- if (!dir)
- return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path);
-
- while ((de = readdir(dir)) != NULL) {
- size_t de_len;
- int result;
-
- /* always skip '.' and '..' */
- if (de->d_name[0] == '.') {
- if (de->d_name[1] == '\0')
- continue;
- if (de->d_name[1] == '.' && de->d_name[2] == '\0')
- continue;
- }
+ if (git_futils_mkpath2file(to, dirmode) < 0)
+ return -1;
- de_len = strlen(de->d_name);
- if (path_sz < wd_len + de_len + 1) {
- closedir(dir);
- return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path);
- }
-
- strcpy(path + wd_len, de->d_name);
- result = fn(arg, path);
- if (result < GIT_SUCCESS) {
- closedir(dir);
- return result; /* The callee is reponsible for setting the correct error message */
- }
- if (result > 0) {
- closedir(dir);
- return result;
- }
+ if (p_rename(from, to) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
+ return -1;
}
- closedir(dir);
- return GIT_SUCCESS;
+ return 0;
}
-
-int retrieve_path_root_offset(const char *path)
+int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
{
- int offset = 0;
-
-#ifdef GIT_WIN32
+ return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
+}
- /* Does the root of the path look like a windows drive ? */
- if (isalpha(path[0]) && (path[1] == ':'))
- offset += 2;
+int git_futils_mmap_ro_file(git_map *out, const char *path)
+{
+ git_file fd = git_futils_open_ro(path);
+ git_off_t len;
+ int result;
-#endif
+ if (fd < 0)
+ return fd;
- if (*(path + offset) == '/')
- return offset;
+ len = git_futils_filesize(fd);
+ if (!git__is_sizet(len)) {
+ giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
+ return -1;
+ }
- return -1; /* Not a real error. Rather a signal than the path is not rooted */
+ result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+ p_close(fd);
+ return result;
}
+void git_futils_mmap_free(git_map *out)
+{
+ p_munmap(out);
+}
-int gitfo_mkdir_recurs(const char *path, int mode)
+int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
- int error, root_path_offset;
+ git_buf make_path = GIT_BUF_INIT;
+ size_t start = 0;
char *pp, *sp;
- char *path_copy = git__strdup(path);
+ bool failed = false;
+
+ if (base != NULL) {
+ /*
+ * when a base is being provided, it is supposed to already exist.
+ * Therefore, no attempt is being made to recursively create this leading path
+ * segment. It's just skipped. */
+ start = strlen(base);
+ if (git_buf_joinpath(&make_path, base, path) < 0)
+ return -1;
+ } else {
+ int root_path_offset;
- if (path_copy == NULL)
- return GIT_ENOMEM;
+ if (git_buf_puts(&make_path, path) < 0)
+ return -1;
- error = GIT_SUCCESS;
- pp = path_copy;
+ root_path_offset = git_path_root(make_path.ptr);
+ if (root_path_offset > 0) {
+ /*
+ * On Windows, will skip the drive name (eg. C: or D:)
+ * or the leading part of a network path (eg. //computer_name ) */
+ start = root_path_offset;
+ }
+ }
- root_path_offset = retrieve_path_root_offset(pp);
- if (root_path_offset > 0)
- pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
+ pp = make_path.ptr + start;
- while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != 0) {
- if (sp != pp && gitfo_isdir(path_copy) < GIT_SUCCESS) {
+ while (!failed && (sp = strchr(pp, '/')) != NULL) {
+ if (sp != pp && git_path_isdir(make_path.ptr) == false) {
*sp = 0;
- error = gitfo_mkdir(path_copy, mode);
/* Do not choke while trying to recreate an existing directory */
- if (errno == EEXIST)
- error = GIT_SUCCESS;
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
*sp = '/';
}
@@ -352,201 +285,198 @@ int gitfo_mkdir_recurs(const char *path, int mode)
pp = sp + 1;
}
- if (*(pp - 1) != '/' && error == GIT_SUCCESS)
- error = gitfo_mkdir(path, mode);
+ if (*pp != '\0' && !failed) {
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
+ }
- free(path_copy);
+ git_buf_free(&make_path);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to recursively create `%s` tree structure", path);
+ if (failed) {
+ giterr_set(GITERR_OS,
+ "Failed to create directory structure at '%s'", path);
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
-static int retrieve_previous_path_component_start(const char *path)
+static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{
- int offset, len, root_offset, start = 0;
+ git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
+
+ assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
+ || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
+ || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
- root_offset = retrieve_path_root_offset(path);
- if (root_offset > -1)
- start += root_offset;
+ if (git_path_isdir(path->ptr) == true) {
+ if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
+ return -1;
- len = strlen(path);
- offset = len - 1;
+ if (p_rmdir(path->ptr) < 0) {
+ if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
+ return 0;
+
+ giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
+ return -1;
+ }
- /* Skip leading slash */
- if (path[start] == '/')
- start++;
+ return 0;
+ }
- /* Skip trailing slash */
- if (path[offset] == '/')
- offset--;
+ if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
+ if (p_unlink(path->ptr) < 0) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
+ return -1;
+ }
- if (offset < root_offset)
- return git__throw(GIT_ERROR, "Failed to retrieve path component. Wrong offset");
+ return 0;
+ }
- while (offset > start && path[offset-1] != '/') {
- offset--;
+ if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
+ return -1;
}
- return offset;
+ return 0;
}
-int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
+int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
{
- int len = 0, segment_len, only_dots, root_path_offset, error = GIT_SUCCESS;
- char *current;
- const char *buffer_out_start, *buffer_end;
-
- current = (char *)path;
- buffer_end = path + strlen(path);
- buffer_out_start = buffer_out;
-
- root_path_offset = retrieve_path_root_offset(path);
- if (root_path_offset < 0) {
- error = gitfo_getcwd(buffer_out, size);
- if (error < GIT_SUCCESS)
- return error; /* The callee already takes care of setting the correct error message. */
-
- len = strlen(buffer_out);
- buffer_out += len;
- }
+ int error;
+ git_buf p = GIT_BUF_INIT;
- while (current < buffer_end) {
- /* Prevent multiple slashes from being added to the output */
- if (*current == '/' && len > 0 && buffer_out_start[len - 1] == '/') {
- current++;
- continue;
- }
-
- only_dots = 1;
- segment_len = 0;
-
- /* Copy path segment to the output */
- while (current < buffer_end && *current != '/')
- {
- only_dots &= (*current == '.');
- *buffer_out++ = *current++;
- segment_len++;
- len++;
- }
+ error = git_buf_sets(&p, path);
+ if (!error)
+ error = _rmdir_recurs_foreach(&removal_type, &p);
+ git_buf_free(&p);
+ return error;
+}
- /* Skip current directory */
- if (only_dots && segment_len == 1)
- {
- current++;
- buffer_out -= segment_len;
- len -= segment_len;
- continue;
- }
+#ifdef GIT_WIN32
+struct win32_path {
+ wchar_t path[MAX_PATH];
+ DWORD len;
+};
- /* Handle the double-dot upward directory navigation */
- if (only_dots && segment_len == 2)
- {
- current++;
- buffer_out -= segment_len;
+static int win32_expand_path(struct win32_path *s_root, const wchar_t *templ)
+{
+ s_root->len = ExpandEnvironmentStringsW(templ, s_root->path, MAX_PATH);
+ return s_root->len ? 0 : -1;
+}
- *buffer_out ='\0';
- len = retrieve_previous_path_component_start(buffer_out_start);
+static int win32_find_file(git_buf *path, const struct win32_path *root, const char *filename)
+{
+ int error = 0;
+ size_t len;
+ wchar_t *file_utf16 = NULL;
+ char *file_utf8 = NULL;
- /* Are we escaping out of the root dir? */
- if (len < 0)
- return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path escapes out of the root directory", path);
+ if (!root || !filename || (len = strlen(filename)) == 0)
+ return GIT_ENOTFOUND;
- buffer_out = (char *)buffer_out_start + len;
- continue;
- }
+ /* allocate space for wchar_t path to file */
+ file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
+ GITERR_CHECK_ALLOC(file_utf16);
- /* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
- if (only_dots && segment_len > 0)
- return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path contains a segment with three `.` or more", path);
+ /* append root + '\\' + filename as wchar_t */
+ memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
- *buffer_out++ = '/';
- len++;
+ if (*filename == '/' || *filename == '\\')
+ filename++;
+
+ if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
+ (int)len + 1) {
+ error = -1;
+ goto cleanup;
+ }
+
+ /* check access */
+ if (_waccess(file_utf16, F_OK) < 0) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
}
- *buffer_out = '\0';
+ /* convert to utf8 */
+ if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
+ error = -1;
+ else {
+ git_path_mkposix(file_utf8);
+ git_buf_attach(path, file_utf8, 0);
+ }
- return GIT_SUCCESS;
+cleanup:
+ git__free(file_utf16);
+ return error;
}
+#endif
-int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path)
+int git_futils_find_system_file(git_buf *path, const char *filename)
{
- int error, path_len, i;
- const char* pattern = "/..";
-
- path_len = strlen(path);
-
- /* Let's make sure the filename isn't empty nor a dot */
- if (path_len == 0 || (path_len == 1 && *path == '.'))
- return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path is either empty or equals `.`", path);
+#ifdef GIT_WIN32
+ struct win32_path root;
- /* Let's make sure the filename doesn't end with "/", "/." or "/.." */
- for (i = 1; path_len > i && i < 4; i++) {
- if (!strncmp(path + path_len - i, pattern, i))
- return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
+ if (win32_expand_path(&root, L"%PROGRAMFILES%\\Git\\etc\\") < 0 ||
+ root.path[0] == L'%') /* i.e. no expansion happened */
+ {
+ giterr_set(GITERR_OS, "Cannot locate the system's Program Files directory");
+ return -1;
}
- error = gitfo_prettify_dir_path(buffer_out, size, path);
- if (error < GIT_SUCCESS)
- return error; /* The callee already takes care of setting the correct error message. */
+ if (win32_find_file(path, &root, filename) < 0) {
+ git_buf_clear(path);
+ return GIT_ENOTFOUND;
+ }
- path_len = strlen(buffer_out);
- if (path_len < 2) /* TODO: Fixme. We should also take of detecting Windows rooted path (probably through usage of retrieve_path_root_offset) */
- return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
+ return 0;
- /* Remove the trailing slash */
- buffer_out[path_len - 1] = '\0';
+#else
+ if (git_buf_joinpath(path, "/etc", filename) < 0)
+ return -1;
- return GIT_SUCCESS;
-}
+ if (git_path_exists(path->ptr) == true)
+ return 0;
-int gitfo_cmp_path(const char *name1, int len1, int isdir1,
- const char *name2, int len2, int isdir2)
-{
- int len = len1 < len2 ? len1 : len2;
- int cmp;
-
- cmp = memcmp(name1, name2, len);
- if (cmp)
- return cmp;
- if (len1 < len2)
- return ((!isdir1 && !isdir2) ? -1 :
- (isdir1 ? '/' - name2[len1] : name2[len1] - '/'));
- if (len1 > len2)
- return ((!isdir1 && !isdir2) ? 1 :
- (isdir2 ? name1[len2] - '/' : '/' - name1[len2]));
- return 0;
+ git_buf_clear(path);
+ return GIT_ENOTFOUND;
+#endif
}
-static void posixify_path(char *path)
+int git_futils_find_global_file(git_buf *path, const char *filename)
{
- while (*path) {
- if (*path == '\\')
- *path = '/';
+#ifdef GIT_WIN32
+ struct win32_path root;
- path++;
+ if (win32_expand_path(&root, L"%USERPROFILE%\\") < 0 ||
+ root.path[0] == L'%') /* i.e. no expansion happened */
+ {
+ giterr_set(GITERR_OS, "Cannot locate the user's profile directory");
+ return -1;
}
-}
-
-int gitfo_getcwd(char *buffer_out, size_t size)
-{
- char *cwd_buffer;
- assert(buffer_out && size > 0);
+ if (win32_find_file(path, &root, filename) < 0) {
+ git_buf_clear(path);
+ return GIT_ENOTFOUND;
+ }
-#ifdef GIT_WIN32
- cwd_buffer = _getcwd(buffer_out, size);
+ return 0;
#else
- cwd_buffer = getcwd(buffer_out, size);
-#endif
+ const char *home = getenv("HOME");
- if (cwd_buffer == NULL)
- return git__throw(GIT_EOSERR, "Failed to retrieve current working directory");
+ if (home == NULL) {
+ giterr_set(GITERR_OS, "Global file lookup failed. "
+ "Cannot locate the user's home directory");
+ return -1;
+ }
- posixify_path(buffer_out);
+ if (git_buf_joinpath(path, home, filename) < 0)
+ return -1;
- git__joinpath(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash
+ if (git_path_exists(path->ptr) == false) {
+ git_buf_clear(path);
+ return GIT_ENOTFOUND;
+ }
- return GIT_SUCCESS;
+ return 0;
+#endif
}