diff options
| author | Russell Belfer <arrbee@arrbee.com> | 2011-11-30 11:27:15 -0800 |
|---|---|---|
| committer | Russell Belfer <arrbee@arrbee.com> | 2011-12-07 23:08:15 -0800 |
| commit | 97769280ba9938ae27f6e06cbd0d5e8a768a86b9 (patch) | |
| tree | 4fe43e99acb55f904f6b586bd7c5158610f9512f /src/status.c | |
| parent | a22b14d32dd8d5f06f121aa154d45bac3b10a305 (diff) | |
| download | libgit2-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.c | 167 |
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; } |
