diff --git a/src/buffer.c b/src/buffer.c index 27e20d562..def3496ce 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -328,6 +328,10 @@ int git_buf_join( size_t strlen_a = strlen(str_a); size_t strlen_b = strlen(str_b); int need_sep = 0; + ssize_t offset_a = -1; + + /* not safe to have str_b point internally to the buffer */ + assert(str_b < buf->ptr || str_b > buf->ptr + buf->size); /* figure out if we need to insert a separator */ if (separator && strlen_a) { @@ -336,14 +340,24 @@ int git_buf_join( need_sep = 1; } + /* str_a could be part of the buffer */ + if (str_a >= buf->ptr && str_a < buf->ptr + buf->size) + offset_a = str_a - buf->ptr; + error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1); if (error < GIT_SUCCESS) return error; - memmove(buf->ptr, str_a, strlen_a); + /* fix up internal pointers */ + if (offset_a >= 0) + str_a = buf->ptr + offset_a; + + /* do the actual copying */ + if (offset_a != 0) + memmove(buf->ptr, str_a, strlen_a); if (need_sep) buf->ptr[strlen_a] = separator; - memmove(buf->ptr + strlen_a + need_sep, str_b, strlen_b); + memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b); buf->size = strlen_a + strlen_b + need_sep; buf->ptr[buf->size] = '\0'; diff --git a/tests-clay/clay.h b/tests-clay/clay.h index 3f09d4f06..5db24a4c9 100644 --- a/tests-clay/clay.h +++ b/tests-clay/clay.h @@ -106,12 +106,13 @@ extern void test_core_filebuf__4(void); extern void test_core_filebuf__5(void); extern void test_core_oid__initialize(void); extern void test_core_oid__streq(void); -extern void test_core_path__0(void); -extern void test_core_path__1(void); -extern void test_core_path__2(void); -extern void test_core_path__5(void); -extern void test_core_path__6(void); -extern void test_core_path__7(void); +extern void test_core_path__0_dirname(void); +extern void test_core_path__1_basename(void); +extern void test_core_path__2_topdir(void); +extern void test_core_path__5_joins(void); +extern void test_core_path__6_long_joins(void); +extern void test_core_path__7_path_to_dir(void); +extern void test_core_path__8_self_join(void); extern void test_core_rmdir__delete_recursive(void); extern void test_core_rmdir__fail_to_delete_non_empty_dir(void); extern void test_core_rmdir__initialize(void); diff --git a/tests-clay/clay_main.c b/tests-clay/clay_main.c index ce3b66b87..80c13f429 100644 --- a/tests-clay/clay_main.c +++ b/tests-clay/clay_main.c @@ -167,12 +167,13 @@ static const struct clay_func _clay_cb_core_oid[] = { {"streq", &test_core_oid__streq} }; static const struct clay_func _clay_cb_core_path[] = { - {"0", &test_core_path__0}, - {"1", &test_core_path__1}, - {"2", &test_core_path__2}, - {"5", &test_core_path__5}, - {"6", &test_core_path__6}, - {"7", &test_core_path__7} + {"0_dirname", &test_core_path__0_dirname}, + {"1_basename", &test_core_path__1_basename}, + {"2_topdir", &test_core_path__2_topdir}, + {"5_joins", &test_core_path__5_joins}, + {"6_long_joins", &test_core_path__6_long_joins}, + {"7_path_to_dir", &test_core_path__7_path_to_dir}, + {"8_self_join", &test_core_path__8_self_join} }; static const struct clay_func _clay_cb_core_rmdir[] = { {"delete_recursive", &test_core_rmdir__delete_recursive}, @@ -361,7 +362,7 @@ static const struct clay_suite _clay_suites[] = { "core::path", {NULL, NULL}, {NULL, NULL}, - _clay_cb_core_path, 6 + _clay_cb_core_path, 7 }, { "core::rmdir", @@ -516,7 +517,7 @@ static const struct clay_suite _clay_suites[] = { }; static size_t _clay_suite_count = 36; -static size_t _clay_callback_count = 119; +static size_t _clay_callback_count = 120; /* Core test functions */ static void diff --git a/tests-clay/core/path.c b/tests-clay/core/path.c index 1a588a39f..49f85f085 100644 --- a/tests-clay/core/path.c +++ b/tests-clay/core/path.c @@ -70,7 +70,7 @@ check_joinpath_n( /* get the dirname of a path */ -void test_core_path__0(void) +void test_core_path__0_dirname(void) { check_dirname(NULL, "."); check_dirname("", "."); @@ -90,7 +90,7 @@ void test_core_path__0(void) } /* get the base name of a path */ -void test_core_path__1(void) +void test_core_path__1_basename(void) { check_basename(NULL, "."); check_basename("", "."); @@ -107,7 +107,7 @@ void test_core_path__1(void) } /* get the latest component in a path */ -void test_core_path__2(void) +void test_core_path__2_topdir(void) { check_topdir(".git/", ".git/"); check_topdir("/.git/", ".git/"); @@ -124,7 +124,7 @@ void test_core_path__2(void) } /* properly join path components */ -void test_core_path__5(void) +void test_core_path__5_joins(void) { check_joinpath("", "", ""); check_joinpath("", "a", "a"); @@ -148,6 +148,10 @@ void test_core_path__5(void) check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/"); check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/"); + check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/"); + check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/")); + check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/"); + check_joinpath(REP1024("aaaa"), REP1024("bbbb"), REP1024("aaaa") "/" REP1024("bbbb")); check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"), @@ -155,7 +159,7 @@ void test_core_path__5(void) } /* properly join path components for more than one path */ -void test_core_path__6(void) +void test_core_path__6_long_joins(void) { check_joinpath_n("", "", "", "", ""); check_joinpath_n("", "a", "", "", "a/"); @@ -208,7 +212,7 @@ check_string_to_dir( } /* convert paths to dirs */ -void test_core_path__7(void) +void test_core_path__7_path_to_dir(void) { check_path_to_dir("", ""); check_path_to_dir(".", "./"); @@ -234,3 +238,38 @@ void test_core_path__7(void) check_string_to_dir("abcd", 5, "abcd/"); check_string_to_dir("abcd", 6, "abcd/"); } + +/* join path to itself */ +void test_core_path__8_self_join(void) +{ + git_buf path = GIT_BUF_INIT; + ssize_t asize = 0; + + asize = path.asize; + cl_git_pass(git_buf_sets(&path, "/foo")); + cl_assert_strequal(path.ptr, "/foo"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_buf_joinpath(&path, path.ptr, "this is a new string")); + cl_assert_strequal(path.ptr, "/foo/this is a new string"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_buf_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer")); + cl_assert_strequal(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer"); + cl_assert(asize < path.asize); + + git_buf_free(&path); + cl_git_pass(git_buf_sets(&path, "/foo/bar")); + + cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "baz")); + cl_assert_strequal(path.ptr, "/bar/baz"); + + asize = path.asize; + cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc")); + cl_assert_strequal(path.ptr, "/baz/somethinglongenoughtorealloc"); + cl_assert(asize < path.asize); + + git_buf_free(&path); +} diff --git a/tests/test_helpers.c b/tests/test_helpers.c index d4ed10d94..40b3499bb 100644 --- a/tests/test_helpers.c +++ b/tests/test_helpers.c @@ -236,7 +236,7 @@ static int copy_filesystem_element_recurs(void *_data, git_buf *source) copydir_data *data = (copydir_data *)_data; git_buf_truncate(&data->dst, data->dst_baselen); - git_buf_joinpath(&data->dst, data->dst.ptr, source->ptr + data->src_baselen); + git_buf_puts(&data->dst, source->ptr + data->src_baselen); if (git_futils_isdir(source->ptr) == GIT_SUCCESS) return git_futils_direach(source, copy_filesystem_element_recurs, _data);