From 0cfcff5daac50d5a4ba41d5125b108cdfceed832 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 11 Jan 2012 20:41:55 -0800 Subject: [PATCH] Convert git_path_walk_up to regular function This gets rid of the crazy macro version of git_path_walk_up and makes it into a normal function that takes a callback parameter. This turned out not to be too messy. --- src/attr.c | 29 +++++++++++++++-------------- src/ignore.c | 22 ++++++++++++++++------ src/path.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/path.h | 31 +++++++++++-------------------- tests-clay/core/path.c | 28 ++++++++++++++++++++++------ 5 files changed, 106 insertions(+), 46 deletions(-) diff --git a/src/attr.c b/src/attr.c index 0c08fc0cf..06a6601b4 100644 --- a/src/attr.c +++ b/src/attr.c @@ -256,6 +256,17 @@ cleanup: #define push_attrs(R,S,B,F) \ git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file) +typedef struct { + git_repository *repo; + git_vector *files; +} attr_walk_up_info; + +static int push_one_attr(void *ref, git_buf *path) +{ + attr_walk_up_info *info = (attr_walk_up_info *)ref; + return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE); +} + static int collect_attr_files( git_repository *repo, const char *path, git_vector *files) { @@ -263,6 +274,7 @@ static int collect_attr_files( git_buf dir = GIT_BUF_INIT; git_config *cfg; const char *workdir = git_repository_workdir(repo); + attr_walk_up_info info; if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS) goto cleanup; @@ -284,20 +296,9 @@ static int collect_attr_files( if (error < GIT_SUCCESS) goto cleanup; - if (workdir && git__prefixcmp(dir.ptr, workdir) == 0) { - ssize_t rootlen = (ssize_t)strlen(workdir); - - do { - error = push_attrs(repo, files, dir.ptr, GIT_ATTR_FILE); - if (error == GIT_SUCCESS) { - git_path_dirname_r(&dir, dir.ptr); - git_path_to_dir(&dir); - error = git_buf_lasterror(&dir); - } - } while (!error && dir.size >= rootlen); - } else { - error = push_attrs(repo, files, dir.ptr, GIT_ATTR_FILE); - } + info.repo = repo; + info.files = files; + error = git_path_walk_up(&dir, workdir, push_one_attr, &info); if (error < GIT_SUCCESS) goto cleanup; diff --git a/src/ignore.c b/src/ignore.c index 7639b7ba9..fa71d4941 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -64,12 +64,24 @@ static int load_ignore_file( #define push_ignore(R,S,B,F) \ git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file) +typedef struct { + git_repository *repo; + git_vector *stack; +} ignore_walk_up_info; + +static int push_one_ignore(void *ref, git_buf *path) +{ + ignore_walk_up_info *info = (ignore_walk_up_info *)ref; + return push_ignore(info->repo, info->stack, path->ptr, GIT_IGNORE_FILE); +} + int git_ignore__for_path(git_repository *repo, const char *path, git_vector *stack) { int error = GIT_SUCCESS; - git_buf dir = GIT_BUF_INIT, scan; + git_buf dir = GIT_BUF_INIT; git_config *cfg; const char *workdir = git_repository_workdir(repo); + ignore_walk_up_info info; if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS) goto cleanup; @@ -82,11 +94,9 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_vector *sta goto cleanup; /* load .gitignore up the path */ - git_path_walk_up(&dir, &scan, workdir, { - error = push_ignore(repo, stack, scan.ptr, GIT_IGNORE_FILE); - if (error < GIT_SUCCESS) break; - }); - if (error < GIT_SUCCESS) + info.repo = repo; + info.stack = stack; + if ((error = git_path_walk_up(&dir, workdir, push_one_ignore, &info)) < GIT_SUCCESS) goto cleanup; /* load .git/info/exclude */ diff --git a/src/path.c b/src/path.c index f9663b7e5..4888123bf 100644 --- a/src/path.c +++ b/src/path.c @@ -305,3 +305,45 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url) return error; } + +int git_path_walk_up( + git_buf *path, + const char *ceiling, + int (*cb)(void *data, git_buf *), + void *data) +{ + int error = GIT_SUCCESS; + git_buf iter; + ssize_t stop = 0, scan; + char oldc = '\0'; + + assert(path && cb); + + if (ceiling != NULL) { + if (git__prefixcmp(path->ptr, ceiling) == GIT_SUCCESS) + stop = (ssize_t)strlen(ceiling); + else + stop = path->size; + } + scan = path->size; + + iter.ptr = path->ptr; + iter.size = path->size; + + while (scan >= stop) { + if ((error = cb(data, &iter)) < GIT_SUCCESS) + break; + iter.ptr[scan] = oldc; + scan = git_buf_rfind_next(&iter, '/'); + if (scan >= 0) { + scan++; + oldc = iter.ptr[scan]; + iter.size = scan; + iter.ptr[scan] = '\0'; + } + } + + iter.ptr[scan] = oldc; + + return error; +} diff --git a/src/path.h b/src/path.h index ceb3bb533..e59c19ad9 100644 --- a/src/path.h +++ b/src/path.h @@ -77,27 +77,18 @@ GIT_INLINE(void) git_path_mkposix(char *path) extern int git__percent_decode(git_buf *decoded_out, const char *input); extern int git_path_fromurl(git_buf *local_path_out, const char *file_url); -/* - * Use as: +/** + * Invoke callback directory by directory up the path until the ceiling + * is reached (inclusive of a final call at the root_path). * - * git_path_walk_up( - * git_buf *path, git_buf *iterator, const char *root_path, - * ... CALLBACK CODE ...) - * - * to invoke callback directory by directory up the path until the root_path - * is reached (inclusive of a final call at the root_path). If root path is - * NULL or the path is not contained in the root_path, then the callback - * code will be invoked just once on input path. + * If the ceiling is NULL, this will walk all the way up to the root. + * If the ceiling is not a prefix of the path, the callback will be + * invoked a single time on the verbatim input path. Returning anything + * other than GIT_SUCCESS from the callback function will stop the + * iteration and propogate the error to the caller. */ -#define git_path_walk_up(B,IB,ROOT,CODE) do { \ - ssize_t _stop = ((ROOT) && git__prefixcmp((B)->ptr, (ROOT))) ? (ssize_t)strlen(ROOT) : (B)->size; \ - ssize_t _scan = (B)->size; char _oldc = '\0'; \ - (IB)->ptr = (B)->ptr; (IB)->size = (B)->size; \ - while (_scan >= _stop) { \ - CODE; \ - (IB)->ptr[_scan] = _oldc; \ - _scan = git_buf_rfind_next((IB), '/'); \ - if (_scan >= 0) { _scan++; _oldc = (IB)->ptr[_scan]; (IB)->size = _scan; (IB)->ptr[_scan] = '\0'; } \ - } (IB)->ptr[_scan] = _oldc; } while (0) +extern int git_path_walk_up( + git_buf *path, const char *ceiling, + int (*cb)(void *data, git_buf *), void *data); #endif diff --git a/tests-clay/core/path.c b/tests-clay/core/path.c index 712ceb4e0..1a77a1f15 100644 --- a/tests-clay/core/path.c +++ b/tests-clay/core/path.c @@ -337,9 +337,23 @@ void test_core_path__10_fromurl(void) check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0); } +typedef struct { + int expect_idx; + char **expect; +} check_walkup_info; + +static int check_one_walkup_step(void *ref, git_buf *path) +{ + check_walkup_info *info = (check_walkup_info *)ref; + cl_assert(info->expect[info->expect_idx] != NULL); + cl_assert_strequal(info->expect[info->expect_idx], path->ptr); + info->expect_idx++; + return GIT_SUCCESS; +} + void test_core_path__11_walkup(void) { - git_buf p = GIT_BUF_INIT, iter; + git_buf p = GIT_BUF_INIT; char *expect[] = { "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, @@ -353,16 +367,18 @@ void test_core_path__11_walkup(void) }; char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL }; int i, j; + check_walkup_info info; + + info.expect = expect; for (i = 0, j = 0; expect[i] != NULL; i++, j++) { - int cb_count = 0; git_buf_sets(&p, expect[i]); - git_path_walk_up(&p, &iter, root[j], { - cl_assert(expect[i + cb_count] != NULL); - cl_assert_strequal(expect[i + cb_count], iter.ptr); - cb_count++; }); + info.expect_idx = i; + cl_git_pass( + git_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); cl_assert_strequal(p.ptr, expect[i]);