summaryrefslogtreecommitdiff
path: root/src/status.c
diff options
context:
space:
mode:
authorRussell Belfer <arrbee@arrbee.com>2011-11-30 11:27:15 -0800
committerRussell Belfer <arrbee@arrbee.com>2011-12-07 23:08:15 -0800
commit97769280ba9938ae27f6e06cbd0d5e8a768a86b9 (patch)
tree4fe43e99acb55f904f6b586bd7c5158610f9512f /src/status.c
parenta22b14d32dd8d5f06f121aa154d45bac3b10a305 (diff)
downloadlibgit2-97769280ba9938ae27f6e06cbd0d5e8a768a86b9.tar.gz
Use git_buf for path storage instead of stack-based buffers
This converts virtually all of the places that allocate GIT_PATH_MAX buffers on the stack for manipulating paths to use git_buf objects instead. The patch is pretty careful not to touch the public API for libgit2, so there are a few places that still use GIT_PATH_MAX. This extends and changes some details of the git_buf implementation to add a couple of extra functions and to make error handling easier. This includes serious alterations to all the path.c functions, and several of the fileops.c ones, too. Also, there are a number of new functions that parallel existing ones except that use a git_buf instead of a stack-based buffer (such as git_config_find_global_r that exists alongsize git_config_find_global). This also modifies the win32 version of p_realpath to allocate whatever buffer size is needed to accommodate the realpath instead of hardcoding a GIT_PATH_MAX limit, but that change needs to be tested still.
Diffstat (limited to 'src/status.c')
-rw-r--r--src/status.c167
1 files changed, 94 insertions, 73 deletions
diff --git a/src/status.c b/src/status.c
index 26dd11ea8..05e07be0a 100644
--- a/src/status.c
+++ b/src/status.c
@@ -28,12 +28,10 @@ struct status_entry {
static struct status_entry *status_entry_new(git_vector *entries, const char *path)
{
- struct status_entry *e = git__malloc(sizeof(*e) + strlen(path) + 1);
+ struct status_entry *e = git__calloc(sizeof(*e) + strlen(path) + 1, 1);
if (e == NULL)
return NULL;
- memset(e, 0x0, sizeof(*e));
-
if (entries != NULL)
git_vector_insert(entries, e);
@@ -73,7 +71,7 @@ static void status_entry_update_from_index(struct status_entry *e, git_index *in
status_entry_update_from_index_entry(e, index_entry);
}
-static int status_entry_update_from_workdir(struct status_entry *e, char* full_path)
+static int status_entry_update_from_workdir(struct status_entry *e, const char* full_path)
{
struct stat filest;
@@ -125,7 +123,7 @@ struct status_st {
git_tree *tree;
int workdir_path_len;
- char* head_tree_relative_path;
+ git_buf head_tree_relative_path;
int head_tree_relative_path_len;
unsigned int tree_position;
unsigned int index_position;
@@ -155,6 +153,7 @@ static int retrieve_head_tree(git_tree **tree_out, git_repository *repo)
return git__rethrow(error, "The tip of HEAD can't be retrieved");
git_reference_free(resolved_head_ref);
+
if ((error = git_commit_tree(&tree, head_commit)) < GIT_SUCCESS) {
error = git__rethrow(error, "The tree of HEAD can't be retrieved");
goto exit;
@@ -174,12 +173,15 @@ enum path_type {
GIT_STATUS_PATH_FOLDER,
};
-static int dirent_cb(void *state, char *full_path);
+static int dirent_cb(void *state, git_buf *full_path);
static int alphasorted_futils_direach(
- char *path, size_t path_sz,
- int (*fn)(void *, char *), void *arg);
+ git_buf *path, int (*fn)(void *, git_buf *), void *arg);
-static int process_folder(struct status_st *st, const git_tree_entry *tree_entry, char *full_path, enum path_type path_type)
+static int process_folder(
+ struct status_st *st,
+ const git_tree_entry *tree_entry,
+ git_buf *full_path,
+ enum path_type path_type)
{
git_object *subtree = NULL;
git_tree *pushed_tree = NULL;
@@ -209,10 +211,9 @@ static int process_folder(struct status_st *st, const git_tree_entry *tree_entry
}
if (full_path != NULL && path_type == GIT_STATUS_PATH_FOLDER)
- error = alphasorted_futils_direach(full_path, GIT_PATH_MAX, dirent_cb, st);
- else {
+ error = alphasorted_futils_direach(full_path, dirent_cb, st);
+ else
error = dirent_cb(st, NULL);
- }
if (tree_entry_type == GIT_OBJ_TREE) {
git_object_free(subtree);
@@ -229,7 +230,7 @@ static int store_if_changed(struct status_st *st, struct status_entry *e)
{
int error;
if ((error = status_entry_update_flags(e)) < GIT_SUCCESS)
- return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path);
+ return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path);
if (e->status_flags == GIT_STATUS_CURRENT) {
git__free(e);
@@ -243,7 +244,7 @@ static int determine_status(struct status_st *st,
int in_head, int in_index, int in_workdir,
const git_tree_entry *tree_entry,
const git_index_entry *index_entry,
- char *full_path,
+ git_buf *full_path,
const char *status_path,
enum path_type path_type)
{
@@ -273,7 +274,8 @@ static int determine_status(struct status_st *st,
}
if (in_workdir)
- if ((error = status_entry_update_from_workdir(e, full_path)) < GIT_SUCCESS)
+ if ((error = status_entry_update_from_workdir(e, full_path->ptr
+)) < GIT_SUCCESS)
return error; /* The callee has already set the error message */
return store_if_changed(st, e);
@@ -284,7 +286,7 @@ static int determine_status(struct status_st *st,
return process_folder(st, tree_entry, full_path, path_type);
}
-static int path_type_from(char *full_path, int is_dir)
+static int path_type_from(git_buf *full_path, int is_dir)
{
if (full_path == NULL)
return GIT_STATUS_PATH_NULL;
@@ -292,7 +294,7 @@ static int path_type_from(char *full_path, int is_dir)
if (!is_dir)
return GIT_STATUS_PATH_FILE;
- if (!git__suffixcmp(full_path, "/" DOT_GIT "/"))
+ if (!git__suffixcmp(full_path->ptr, "/" DOT_GIT "/"))
return GIT_STATUS_PATH_IGNORE;
return GIT_STATUS_PATH_FOLDER;
@@ -330,7 +332,7 @@ static int compare(const char *left, const char *right)
/* Greatly inspired from JGit IndexTreeWalker */
/* https://github.com/spearce/jgit/blob/ed47e29c777accfa78c6f50685a5df2b8f5b8ff5/org.spearce.jgit/src/org/spearce/jgit/lib/IndexTreeWalker.java#L88 */
-static int dirent_cb(void *state, char *a)
+static int dirent_cb(void *state, git_buf *a)
{
const git_tree_entry *m;
const git_index_entry *entry;
@@ -346,7 +348,7 @@ static int dirent_cb(void *state, char *a)
if (path_type == GIT_STATUS_PATH_IGNORE)
return GIT_SUCCESS; /* Let's skip the ".git" directory */
- a_name = (path_type != GIT_STATUS_PATH_NULL) ? a + st->workdir_path_len : NULL;
+ a_name = (path_type != GIT_STATUS_PATH_NULL) ? a->ptr + st->workdir_path_len : NULL;
while (1) {
if (st->tree == NULL)
@@ -360,15 +362,18 @@ static int dirent_cb(void *state, char *a)
return GIT_SUCCESS;
if (m != NULL) {
- st->head_tree_relative_path[st->head_tree_relative_path_len] = '\0';
-
+ git_buf_truncate(&st->head_tree_relative_path,
+ st->head_tree_relative_path_len);
+ git_buf_joinpath(&st->head_tree_relative_path,
+ st->head_tree_relative_path.ptr, m->filename);
/* When the tree entry is a folder, append a forward slash to its name */
if (git_tree_entry_type(m) == GIT_OBJ_TREE)
- git_path_join_n(st->head_tree_relative_path, 3, st->head_tree_relative_path, m->filename, "");
- else
- git_path_join(st->head_tree_relative_path, st->head_tree_relative_path, m->filename);
-
- m_name = st->head_tree_relative_path;
+ git_path_to_dir(&st->head_tree_relative_path);
+
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
+
+ m_name = st->head_tree_relative_path.ptr;
} else
m_name = NULL;
@@ -383,7 +388,7 @@ static int dirent_cb(void *state, char *a)
pi = ((cmpmi >= 0) && (cmpai >= 0)) ? i_name : NULL;
if((error = determine_status(st, pm != NULL, pi != NULL, pa != NULL, m, entry, a, status_path(pm, pi, pa), path_type)) < GIT_SUCCESS)
- return git__rethrow(error, "An error occured while determining the status of '%s'", a);
+ return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
if ((pa != NULL) || (path_type == GIT_STATUS_PATH_FOLDER))
return GIT_SUCCESS;
@@ -404,9 +409,8 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
{
git_vector entries;
git_index *index = NULL;
- char temp_path[GIT_PATH_MAX];
- char tree_path[GIT_PATH_MAX] = "";
- struct status_st dirent_st;
+ git_buf temp_path = GIT_BUF_INIT;
+ struct status_st dirent_st = {0};
int error = GIT_SUCCESS;
unsigned int i;
git_tree *tree;
@@ -435,23 +439,21 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
dirent_st.tree = tree;
dirent_st.index = index;
dirent_st.vector = &entries;
- dirent_st.head_tree_relative_path = tree_path;
+ git_buf_init(&dirent_st.head_tree_relative_path, 0);
dirent_st.head_tree_relative_path_len = 0;
dirent_st.is_dir = 1;
- strcpy(temp_path, workdir);
-
- if (git_futils_isdir(temp_path)) {
+ if (git_futils_isdir(workdir)) {
error = git__throw(GIT_EINVALIDPATH,
"Failed to determine status of file '%s'. "
- "The given path doesn't lead to a folder", temp_path);
+ "The given path doesn't lead to a folder", workdir);
goto exit;
}
+ git_buf_sets(&temp_path, workdir);
+
error = alphasorted_futils_direach(
- temp_path, sizeof(temp_path),
- dirent_cb, &dirent_st
- );
+ &temp_path, dirent_cb, &dirent_st);
if (error < GIT_SUCCESS)
error = git__rethrow(error,
@@ -477,6 +479,8 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
}
exit:
+ git_buf_free(&dirent_st.head_tree_relative_path);
+ git_buf_free(&temp_path);
git_vector_free(&entries);
git_tree_free(tree);
return error;
@@ -521,7 +525,7 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char
{
struct status_entry *e;
git_index *index = NULL;
- char temp_path[GIT_PATH_MAX];
+ git_buf temp_path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
git_tree *tree = NULL;
const char *workdir;
@@ -532,60 +536,72 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char
return git__throw(GIT_ERROR,
"Cannot retrieve status on a bare repository");
- git_path_join(temp_path, workdir, path);
- if (git_futils_isdir(temp_path) == GIT_SUCCESS)
+ if ((error = git_buf_joinpath(&temp_path, workdir, path)) < GIT_SUCCESS)
+ return git__rethrow(error,
+ "Failed to determine status of file '%s'", path);
+
+ if (git_futils_isdir(temp_path.ptr) == GIT_SUCCESS) {
+ git_buf_free(&temp_path);
return git__throw(GIT_EINVALIDPATH,
"Failed to determine status of file '%s'. "
"Given path leads to a folder, not a file", path);
+ }
e = status_entry_new(NULL, path);
- if (e == NULL)
+ if (e == NULL) {
+ git_buf_free(&temp_path);
return GIT_ENOMEM;
+ }
/* Find file in Workdir */
- if (git_futils_exists(temp_path) == GIT_SUCCESS) {
- if ((error = status_entry_update_from_workdir(e, temp_path)) < GIT_SUCCESS)
- goto exit; /* The callee has already set the error message */
+ if (git_futils_exists(temp_path.ptr) == GIT_SUCCESS) {
+ if ((error = status_entry_update_from_workdir(e, temp_path.ptr)) < GIT_SUCCESS)
+ goto cleanup; /* The callee has already set the error message */
}
/* Find file in Index */
if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
- error = git__rethrow(error,
+ git__rethrow(error,
"Failed to determine status of file '%s'."
"Index can't be opened", path);
- goto exit;
+ goto cleanup;
}
status_entry_update_from_index(e, index);
if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
- error = git__rethrow(error,
+ git__rethrow(error,
"Failed to determine status of file '%s'", path);
- goto exit;
+ goto cleanup;
}
/* If the repository is not empty, try and locate the file in HEAD */
if (tree != NULL) {
- strcpy(temp_path, path);
+ if ((error = git_buf_sets(&temp_path, path)) < GIT_SUCCESS) {
+ git__rethrow(error,
+ "Failed to determine status of file '%s'", path);
+ goto cleanup;
+ }
- error = recurse_tree_entry(tree, e, temp_path);
+ error = recurse_tree_entry(tree, e, temp_path.ptr);
if (error < GIT_SUCCESS) {
- error = git__rethrow(error,
+ git__rethrow(error,
"Failed to determine status of file '%s'. "
"An error occured while processing the tree", path);
- goto exit;
+ goto cleanup;
}
}
/* Determine status */
if ((error = status_entry_update_flags(e)) < GIT_SUCCESS) {
- error = git__throw(error, "Nonexistent file");
- goto exit;
+ git__throw(error, "Nonexistent file");
+ goto cleanup;
}
*status_flags = e->status_flags;
-exit:
+cleanup:
+ git_buf_free(&temp_path);
git_tree_free(tree);
git__free(e);
return error;
@@ -600,37 +616,34 @@ exit:
struct alphasorted_dirent_info {
int is_dir;
-
char path[GIT_FLEX_ARRAY]; /* more */
};
-static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const char *path)
+static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const git_buf *path)
{
int is_dir, size;
struct alphasorted_dirent_info *di;
- is_dir = git_futils_isdir(path) == GIT_SUCCESS ? 1 : 0;
- size = sizeof(*di) + (is_dir ? GIT_PATH_MAX : strlen(path)) + 2;
+ is_dir = git_futils_isdir(path->ptr) == GIT_SUCCESS ? 1 : 0;
+ size = sizeof(*di) + path->size + is_dir + 1;
- di = git__malloc(size);
+ di = git__calloc(size, 1);
if (di == NULL)
return NULL;
- memset(di, 0x0, size);
-
- strcpy(di->path, path);
+ git_buf_copy_cstr(di->path, path->size + 1, path);
if (is_dir) {
di->is_dir = 1;
- /*
- * Append a forward slash to the name to force folders
+ /*
+ * Append a forward slash to the name to force folders
* to be ordered in a similar way than in a tree
*
* The file "subdir" should appear before the file "subdir.txt"
* The folder "subdir" should appear after the file "subdir.txt"
*/
- di->path[strlen(path)] = '/';
+ di->path[path->size] = '/';
}
return di;
@@ -644,7 +657,7 @@ static int alphasorted_dirent_info_cmp(const void *a, const void *b)
return strcmp(stra->path, strb->path);
}
-static int alphasorted_dirent_cb(void *state, char *full_path)
+static int alphasorted_dirent_cb(void *state, git_buf *full_path)
{
struct alphasorted_dirent_info *entry;
git_vector *entry_names;
@@ -664,34 +677,42 @@ static int alphasorted_dirent_cb(void *state, char *full_path)
}
static int alphasorted_futils_direach(
- char *path,
- size_t path_sz,
- int (*fn)(void *, char *),
+ git_buf *path,
+ int (*fn)(void *, git_buf *),
void *arg)
{
struct alphasorted_dirent_info *entry;
git_vector entry_names;
unsigned int idx;
int error = GIT_SUCCESS;
+ git_buf entry_path = GIT_BUF_INIT;
if (git_vector_init(&entry_names, 16, alphasorted_dirent_info_cmp) < GIT_SUCCESS)
return GIT_ENOMEM;
- error = git_futils_direach(path, path_sz, alphasorted_dirent_cb, &entry_names);
+ error = git_futils_direach(path, alphasorted_dirent_cb, &entry_names);
git_vector_sort(&entry_names);
for (idx = 0; idx < entry_names.length; ++idx) {
entry = (struct alphasorted_dirent_info *)git_vector_get(&entry_names, idx);
+ /* We have to walk the entire vector even if there was an error,
+ * in order to free up memory, but we stop making callbacks after
+ * an error.
+ */
+ if (error == GIT_SUCCESS)
+ error = git_buf_sets(&entry_path, entry->path);
+
if (error == GIT_SUCCESS) {
((struct status_st *)arg)->is_dir = entry->is_dir;
- error = fn(arg, entry->path);
+ error = fn(arg, &entry_path);
}
git__free(entry);
}
+ git_buf_free(&entry_path);
git_vector_free(&entry_names);
return error;
}