Commit 97769280 by Russell Belfer

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.
parent a22b14d3
......@@ -67,12 +67,13 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error, islnk;
int error = GIT_SUCCESS;
int islnk = 0;
int fd = 0;
char full_path[GIT_PATH_MAX];
git_buf full_path = GIT_BUF_INIT;
char buffer[2048];
git_off_t size;
git_odb_stream *stream;
git_odb_stream *stream = NULL;
struct stat st;
const char *workdir;
git_odb *odb;
......@@ -81,11 +82,14 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (workdir == NULL)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
git_path_join(full_path, workdir, path);
error = git_buf_joinpath(&full_path, workdir, path);
if (error < GIT_SUCCESS)
return error;
error = p_lstat(full_path, &st);
error = p_lstat(full_path.ptr, &st);
if (error < 0) {
return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
goto cleanup;
}
islnk = S_ISLNK(st.st_mode);
......@@ -93,18 +97,18 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
return error;
goto cleanup;
if (!islnk) {
if ((fd = p_open(full_path, O_RDONLY)) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path);
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr
);
goto cleanup;
}
}
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) {
if (!islnk)
p_close(fd);
return git__rethrow(error, "Failed to create blob");
}
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
goto cleanup;
while (size > 0) {
ssize_t read_len;
......@@ -112,13 +116,11 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (!islnk)
read_len = p_read(fd, buffer, sizeof(buffer));
else
read_len = p_readlink(full_path, buffer, sizeof(buffer));
read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer));
if (read_len < 0) {
if (!islnk)
p_close(fd);
stream->free(stream);
return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
goto cleanup;
}
stream->write(stream, buffer, read_len);
......@@ -126,10 +128,15 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
}
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (!islnk)
cleanup:
if (stream)
stream->free(stream);
if (!islnk && fd)
p_close(fd);
git_buf_free(&full_path);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create blob");
return error == GIT_SUCCESS ? GIT_SUCCESS :
git__rethrow(error, "Failed to create blob");
}
......@@ -15,7 +15,8 @@ char git_buf_initbuf[1];
#define ENSURE_SIZE(b, d) \
if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return;
return GIT_ENOMEM;
void git_buf_init(git_buf *buf, size_t initial_size)
{
......@@ -29,6 +30,14 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
if (error != GIT_SUCCESS)
buf->asize = -1;
return error;
}
int git_buf_try_grow(git_buf *buf, size_t target_size)
{
char *new_ptr;
size_t new_size;
......@@ -55,10 +64,9 @@ int git_buf_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr) {
buf->asize = -1;
/* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
return GIT_ENOMEM;
}
buf->asize = new_size;
buf->ptr = new_ptr;
......@@ -93,7 +101,12 @@ int git_buf_oom(const git_buf *buf)
return (buf->asize < 0);
}
void git_buf_set(git_buf *buf, const char *data, size_t len)
int git_buf_lasterror(const git_buf *buf)
{
return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
{
if (len == 0 || data == NULL) {
git_buf_clear(buf);
......@@ -103,35 +116,38 @@ void git_buf_set(git_buf *buf, const char *data, size_t len)
buf->size = len;
buf->ptr[buf->size] = '\0';
}
return GIT_SUCCESS;
}
void git_buf_sets(git_buf *buf, const char *string)
int git_buf_sets(git_buf *buf, const char *string)
{
git_buf_set(buf, string, string ? strlen(string) : 0);
return git_buf_set(buf, string, string ? strlen(string) : 0);
}
void git_buf_putc(git_buf *buf, char c)
int git_buf_putc(git_buf *buf, char c)
{
ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
}
void git_buf_put(git_buf *buf, const char *data, size_t len)
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
}
void git_buf_puts(git_buf *buf, const char *string)
int git_buf_puts(git_buf *buf, const char *string)
{
assert(string);
git_buf_put(buf, string, strlen(string));
return git_buf_put(buf, string, strlen(string));
}
void git_buf_printf(git_buf *buf, const char *format, ...)
int git_buf_printf(git_buf *buf, const char *format, ...)
{
int len;
va_list arglist;
......@@ -145,16 +161,18 @@ void git_buf_printf(git_buf *buf, const char *format, ...)
if (len < 0) {
buf->asize = -1;
return;
return GIT_ENOMEM;
}
if (len + 1 <= buf->asize - buf->size) {
buf->size += len;
return;
break;
}
ENSURE_SIZE(buf, buf->size + len + 1);
}
return GIT_SUCCESS;
}
const char *git_buf_cstr(git_buf *buf)
......@@ -162,7 +180,7 @@ const char *git_buf_cstr(git_buf *buf)
return buf->ptr;
}
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf)
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
{
size_t copylen;
......@@ -190,6 +208,14 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
void git_buf_truncate(git_buf *buf, ssize_t len)
{
if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
}
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
{
git_buf t = *buf_a;
......@@ -197,7 +223,7 @@ void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
*buf_b = t;
}
char *git_buf_take_cstr(git_buf *buf)
char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
......@@ -209,18 +235,34 @@ char *git_buf_take_cstr(git_buf *buf)
return data;
}
void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
{
/* Make two passes to avoid multiple reallocation */
git_buf_free(buf);
if (ptr) {
buf->ptr = ptr;
buf->size = strlen(ptr);
if (asize)
buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
else /* pass 0 to fall back on strlen + 1 */
buf->asize = buf->size + 1;
} else {
git_buf_grow(buf, asize);
}
}
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
int i;
int i, error = GIT_SUCCESS;
size_t total_size = 0;
char *out;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
/* Make two passes to avoid multiple reallocation */
va_start(ap, nbuf);
for (i = 0; i < nbuf; ++i) {
const char* segment;
......@@ -237,7 +279,10 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
}
va_end(ap);
ENSURE_SIZE(buf, buf->size + total_size + 1);
/* expand buffer if needed */
if (total_size > 0 &&
(error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
return error;
out = buf->ptr + buf->size;
......@@ -274,14 +319,17 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
/* set size based on num characters actually written */
buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
return error;
}
void git_buf_join(
int git_buf_join(
git_buf *buf,
char separator,
const char *str_a,
const char *str_b)
{
int error = GIT_SUCCESS;
size_t strlen_a = strlen(str_a);
size_t strlen_b = strlen(str_b);
int need_sep = 0;
......@@ -293,7 +341,9 @@ void git_buf_join(
need_sep = 1;
}
ENSURE_SIZE(buf, strlen_a + strlen_b + need_sep + 1);
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);
if (need_sep)
......@@ -302,4 +352,6 @@ void git_buf_join(
buf->size = strlen_a + strlen_b + need_sep;
buf->ptr[buf->size] = '\0';
return error;
}
......@@ -18,34 +18,87 @@ extern char git_buf_initbuf[];
#define GIT_BUF_INIT { git_buf_initbuf, 0, 0 }
/**
* Initialize a git_buf structure.
*
* For the cases where GIT_BUF_INIT cannot be used to do static
* initialization.
*/
void git_buf_init(git_buf *buf, size_t initial_size);
/**
* Grow the buffer to hold at least `target_size` bytes.
*
* If the allocation fails, this will return an error and the buffer
* will be marked as invalid for future operations. The existing
* contents of the buffer will be preserved however.
* @return GIT_SUCCESS or GIT_ENOMEM on failure
*/
int git_buf_grow(git_buf *buf, size_t target_size);
/**
* Attempt to grow the buffer to hold at least `target_size` bytes.
*
* This is just like `git_buf_grow` except that even if the allocation
* fails, the git_buf will still be left in a valid state.
*/
int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
*
* Any function that writes to a git_buf can fail due to memory allocation
* issues. If one fails, the git_buf will be marked with an OOM error and
* further calls to modify the buffer will fail. You just check
* git_buf_oom() at the end of your sequence and it will be true if you ran
* out of memory at any point with that buffer.
* further calls to modify the buffer will fail. Check git_buf_oom() at the
* end of your sequence and it will be true if you ran out of memory at any
* point with that buffer.
* @return 0 if no error, 1 if allocation error.
*/
int git_buf_oom(const git_buf *buf);
void git_buf_set(git_buf *buf, const char *data, size_t len);
void git_buf_sets(git_buf *buf, const char *string);
void git_buf_putc(git_buf *buf, char c);
void git_buf_put(git_buf *buf, const char *data, size_t len);
void git_buf_puts(git_buf *buf, const char *string);
void git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
/**
* Just like git_buf_oom, except returns appropriate error code.
* @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
*/
int git_buf_lasterror(const git_buf *buf);
/*
* The functions below that return int values, will return GIT_ENOMEM
* if they fail to expand the git_buf when they are called, otherwise
* GIT_SUCCESS. Passing a git_buf that has failed an allocation will
* automatically return GIT_ENOMEM for all further calls. As a result,
* you can ignore the return code of these functions and call them in a
* series then just call git_buf_lasterror at the end.
*/
int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
void git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
void git_buf_truncate(git_buf *buf, ssize_t len);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
/**
* Join two strings as paths, inserting a slash between as needed.
* @return error code or GIT_SUCCESS
*/
GIT_INLINE (int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
{
return git_buf_join(buf, '/', a, b);
}
const char *git_buf_cstr(git_buf *buf);
char *git_buf_take_cstr(git_buf *buf);
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf);
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
......
......@@ -129,7 +129,8 @@ int git_commit_create(
git_buf_puts(&commit, message);
if (git_buf_oom(&commit)) {
error = git__throw(GIT_ENOMEM, "Not enough memory to build the commit data");
error = git__throw(git_buf_lasterror(&commit),
"Not enough memory to build the commit data");
goto cleanup;
}
......
......@@ -330,9 +330,25 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out)
int git_config_find_global(char *global_config_path)
{
const char *home;
git_buf path = GIT_BUF_INIT;
int error = git_config_find_global_r(&path);
if (error == GIT_SUCCESS) {
if (path.size > GIT_PATH_MAX)
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
else
git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
}
git_buf_free(&path);
home = getenv("HOME");
return error;
}
int git_config_find_global_r(git_buf *path)
{
int error;
const char *home = getenv("HOME");
#ifdef GIT_WIN32
if (home == NULL)
......@@ -342,10 +358,13 @@ int git_config_find_global(char *global_config_path)
if (home == NULL)
return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");
git_path_join(global_config_path, home, GIT_CONFIG_FILENAME);
if ((error = git_buf_joinpath(path, home, GIT_CONFIG_FILENAME)) < GIT_SUCCESS)
return error;
if (git_futils_exists(global_config_path) < GIT_SUCCESS)
if (git_futils_exists(path->ptr) < GIT_SUCCESS) {
git_buf_clear(path);
return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");
}
return GIT_SUCCESS;
}
......@@ -353,7 +372,7 @@ int git_config_find_global(char *global_config_path)
#if GIT_WIN32
static int win32_find_system(char *system_config_path)
static int win32_find_system(git_buf *system_config_path)
{
const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig";
wchar_t *apphome_utf16;
......@@ -378,25 +397,21 @@ static int win32_find_system(char *system_config_path)
apphome_utf8 = gitwin_from_utf16(apphome_utf16);
git__free(apphome_utf16);
if (strlen(apphome_utf8) >= GIT_PATH_MAX) {
git__free(apphome_utf8);
return git__throw(GIT_ESHORTBUFFER, "Path is too long");
}
git_buf_attach(system_config_path, apphome_utf8, 0);
strcpy(system_config_path, apphome_utf8);
git__free(apphome_utf8);
return GIT_SUCCESS;
}
#endif
int git_config_find_system(char *system_config_path)
int git_config_find_system_r(git_buf *system_config_path)
{
const char *etc = "/etc/gitconfig";
if (git_buf_sets(system_config_path, "/etc/gitconfig") < GIT_SUCCESS)
return git_buf_lasterror(system_config_path);
if (git_futils_exists(etc) == GIT_SUCCESS) {
memcpy(system_config_path, etc, strlen(etc) + 1);
if (git_futils_exists(system_config_path->ptr) == GIT_SUCCESS)
return GIT_SUCCESS;
}
git_buf_clear(system_config_path);
#if GIT_WIN32
return win32_find_system(system_config_path);
......@@ -405,6 +420,23 @@ int git_config_find_system(char *system_config_path)
#endif
}
int git_config_find_system(char *system_config_path)
{
git_buf path = GIT_BUF_INIT;
int error = git_config_find_system_r(&path);
if (error == GIT_SUCCESS) {
if (path.size > GIT_PATH_MAX)
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
else
git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
}
git_buf_free(&path);
return error;
}
int git_config_open_global(git_config **out)
{
int error;
......
......@@ -21,4 +21,7 @@ struct git_config {
git_vector files;
};
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
#endif
......@@ -115,25 +115,31 @@ int git_fetch_download_pack(char **out, git_remote *remote)
}
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
GIT_SOCKET fd, git_repository *repo)
int git_fetch__download_pack(
char **out,
const char *buffered,
size_t buffered_size,
GIT_SOCKET fd,
git_repository *repo)
{
git_filebuf file = GIT_FILEBUF_INIT;
int error;
char buff[1024], path[GIT_PATH_MAX];
char buff[1024];
git_buf path = GIT_BUF_INIT;
static const char suff[] = "/objects/pack/pack-received";
gitno_buffer buf;
git_path_join(path, repo->path_repository, suff);
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
if (memcmp(buffered, "PACK", strlen("PACK"))) {
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
}
error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
error = git_buf_joinpath(&path, repo->path_repository, suff);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -166,7 +172,7 @@ int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_s
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&file);
git_buf_free(&path);
return error;
}
......@@ -196,18 +196,19 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
/* If we are writing to a temp file */
if (flags & GIT_FILEBUF_TEMPORARY) {
char tmp_path[GIT_PATH_MAX];
git_buf tmp_path = GIT_BUF_INIT;
/* Open the file as temporary for locking */
file->fd = git_futils_mktmp(tmp_path, path);
file->fd = git_futils_mktmp(&tmp_path, path);
if (file->fd < 0) {
git_buf_free(&tmp_path);
error = GIT_EOSERR;
goto cleanup;
}
/* No original path */
file->path_original = NULL;
file->path_lock = git__strdup(tmp_path);
file->path_lock = git_buf_detach(&tmp_path);
if (file->path_lock == NULL) {
error = GIT_ENOMEM;
......
......@@ -10,35 +10,40 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
int error = GIT_SUCCESS;
char target_folder_path[GIT_PATH_MAX];
int error;
git_buf target_folder = GIT_BUF_INIT;
error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
if (error < GIT_SUCCESS)
error = git_path_dirname_r(&target_folder, file_path);
if (error < GIT_SUCCESS) {
git_buf_free(&target_folder);
return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
} else {
/* reset error */
error = GIT_SUCCESS;
}
/* Does the containing folder exist? */
if (git_futils_isdir(target_folder_path)) {
git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */
if (git_futils_isdir(target_folder.ptr) != GIT_SUCCESS)
/* Let's create the tree structure */
error = git_futils_mkdir_r(target_folder_path, mode);
if (error < GIT_SUCCESS)
return error; /* The callee already takes care of setting the correct error message. */
}
error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
return GIT_SUCCESS;
git_buf_free(&target_folder);
return error;
}
int git_futils_mktmp(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");
git_buf_sets(path_out, filename);
git_buf_puts(path_out, "_git2_XXXXXX");
if (git_buf_oom(path_out))
return git__rethrow(git_buf_lasterror(path_out),
"Failed to create temporary file for %s", filename);
if ((fd = p_mkstemp(path_out)) < 0)
return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out);
if ((fd = p_mkstemp(path_out->ptr)) < 0)
return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
return fd;
}
......@@ -180,6 +185,14 @@ int git_futils_readbuffer(git_fbuffer *obj, const char *path)
return git_futils_readbuffer_updated(obj, path, NULL, NULL);
}
void git_futils_fbuffer_rtrim(git_fbuffer *obj)
{
unsigned char *buff = obj->data;
while (obj->len > 0 && isspace(buff[obj->len - 1]))
obj->len--;
buff[obj->len] = '\0';
}
void git_futils_freebuffer(git_fbuffer *obj)
{
assert(obj);
......@@ -215,76 +228,72 @@ GIT_INLINE(int) is_dot_or_dotdot(const char *name)
}
int git_futils_direach(
char *path,
size_t path_sz,
int (*fn)(void *, char *),
git_buf *path,
int (*fn)(void *, git_buf *),
void *arg)
{
size_t wd_len = strlen(path);
ssize_t wd_len;
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';
if (git_path_to_dir(path) < GIT_SUCCESS)
return git_buf_lasterror(path);
dir = opendir(path);
wd_len = path->size;
dir = opendir(path->ptr);
if (!dir)
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path);
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr);
while ((de = readdir(dir)) != NULL) {
size_t de_len;
int result;
if (is_dot_or_dotdot(de->d_name))
continue;
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);
}
if (git_buf_puts(path, de->d_name) < GIT_SUCCESS)
return git_buf_lasterror(path);
strcpy(path + wd_len, de->d_name);
result = fn(arg, path);
if (result < GIT_SUCCESS) {
git_buf_truncate(path, wd_len); /* restore 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;
}
}
closedir(dir);
return GIT_SUCCESS;
}
int git_futils_mkdir_r(const char *path, const mode_t 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;
char *pp, *sp;
char *path_copy = git__strdup(path);
if (path_copy == NULL)
return GIT_ENOMEM;
if (base != NULL) {
start = strlen(base);
error = git_buf_joinpath(&make_path, base, path);
} else {
start = 0;
error = git_buf_puts(&make_path, path);
}
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create `%s` tree structure", path);
error = GIT_SUCCESS;
pp = path_copy;
pp = make_path.ptr + start;
root_path_offset = git_path_root(pp);
root_path_offset = git_path_root(make_path.ptr);
if (root_path_offset > 0)
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) {
if (sp != pp && git_futils_isdir(make_path.ptr) < GIT_SUCCESS) {
*sp = 0;
error = p_mkdir(path_copy, mode);
error = p_mkdir(make_path.ptr, mode);
/* Do not choke while trying to recreate an existing directory */
if (errno == EEXIST)
......@@ -297,12 +306,12 @@ int git_futils_mkdir_r(const char *path, const mode_t mode)
}
if (*pp != '\0' && error == GIT_SUCCESS) {
error = p_mkdir(path, mode);
error = p_mkdir(make_path.ptr, mode);
if (errno == EEXIST)
error = GIT_SUCCESS;
}
git__free(path_copy);
git_buf_free(&make_path);
if (error < GIT_SUCCESS)
return git__throw(error, "Failed to recursively create `%s` tree structure", path);
......@@ -310,32 +319,34 @@ int git_futils_mkdir_r(const char *path, const mode_t mode)
return GIT_SUCCESS;
}
static int _rmdir_recurs_foreach(void *opaque, char *path)
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{
int error = GIT_SUCCESS;
int force = *(int *)opaque;
if (git_futils_isdir(path) == GIT_SUCCESS) {
size_t root_size = strlen(path);
if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_foreach, opaque)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to remove directory `%s`", path);
path[root_size] = '\0';
return p_rmdir(path);
if (git_futils_isdir(path->ptr) == GIT_SUCCESS) {
error = git_futils_direach(path, _rmdir_recurs_foreach, opaque);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
return p_rmdir(path->ptr);
} else if (force) {
return p_unlink(path);
return p_unlink(path->ptr);
}
return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path);
return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
}
int git_futils_rmdir_r(const char *path, int force)
{
char p[GIT_PATH_MAX];
strncpy(p, path, GIT_PATH_MAX);
return _rmdir_recurs_foreach(&force, p);
int error;
git_buf p = GIT_BUF_INIT;
error = git_buf_sets(&p, path);
if (error == GIT_SUCCESS)
error = _rmdir_recurs_foreach(&force, &p);
git_buf_free(&p);
return error;
}
int git_futils_cmp_path(const char *name1, int len1, int isdir1,
......@@ -356,3 +367,39 @@ int git_futils_cmp_path(const char *name1, int len1, int isdir1,
return 0;
}
static int _check_dir_contents(
git_buf *dir,
const char *sub,
int append_on_success,
int (*predicate)(const char *))
{
int error = GIT_SUCCESS;
size_t dir_size = dir->size;
size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */
if ((error = git_buf_try_grow(dir, dir_size + sub_size + 2)) < GIT_SUCCESS)
return error;
/* save excursion */
git_buf_joinpath(dir, dir->ptr, sub);
error = (*predicate)(dir->ptr);
/* restore excursion */
if (!append_on_success || error != GIT_SUCCESS)
git_buf_truncate(dir, dir_size);
return error;
}
int git_futils_contains_dir(git_buf *base, const char *subdir, int append_if_exists)
{
return _check_dir_contents(base, subdir, append_if_exists, &git_futils_isdir);
}
int git_futils_contains_file(git_buf *base, const char *file, int append_if_exists)
{
return _check_dir_contents(base, file, append_if_exists, &git_futils_isfile);
}
......@@ -28,6 +28,7 @@ typedef struct { /* file io buffer */
extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
extern void git_futils_freebuffer(git_fbuffer *obj);
extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
/**
* File utils
......@@ -72,9 +73,25 @@ extern int git_futils_isdir(const char *path);
extern int git_futils_isfile(const char *path);
/**
* Check if the given path contains the given subdirectory.
*
* If `append_if_exists` is true, then the subdir will be appended to the
* parent path if it does exists.
*/
extern int git_futils_contains_dir(git_buf *parent, const char *subdir, int append_if_exists);
/**
* Check if the given path contains the given file
*
* If `append_if_exists` is true, then the filename will be appended to the
* parent path if it does exists.
*/
extern int git_futils_contains_file(git_buf *parent, const char *file, int append_if_exists);
/**
* Create a path recursively
*/
extern int git_futils_mkdir_r(const char *path, const mode_t mode);
extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
/**
* Create all the folders required to contain
......@@ -85,9 +102,11 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
extern int git_futils_rmdir_r(const char *path, int force);
/**
* Create and open a temporary file with a `_git2_` suffix
* Create and open a temporary file with a `_git2_` suffix.
* Writes the filename into path_out.
* @return On success, an open file descriptor, else an error code < 0.
*/
extern int git_futils_mktmp(char *path_out, const char *filename);
extern int git_futils_mktmp(git_buf *path_out, const char *filename);
/**
* Move a file on the filesystem, create the
......@@ -133,16 +152,14 @@ extern void git_futils_mmap_free(git_map *map);
*
* @param pathbuf buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
* @param pathmax maximum allocation of pathbuf.
* @param fn function to invoke with each entry. The first arg is
* the input state and the second arg is pathbuf. The function
* may modify the pathbuf, but only by appending new text.
* @param state to pass to fn as the first arg.
*/
extern int git_futils_direach(
char *pathbuf,
size_t pathmax,
int (*fn)(void *, char *),
git_buf *pathbuf,
int (*fn)(void *, git_buf *),
void *state);
extern int git_futils_cmp_path(const char *name1, int len1, int isdir1,
......
......@@ -294,40 +294,30 @@ git_index_entry *git_index_get(git_index *index, unsigned int n)
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
{
git_index_entry *entry;
char full_path[GIT_PATH_MAX];
git_index_entry *entry = NULL;
struct stat st;
git_oid oid;
int error;
const char *workdir;
if (INDEX_OWNER(index) == NULL)
return git__throw(GIT_EBAREINDEX,
"Failed to initialize entry. Repository is bare");
workdir = git_repository_workdir(INDEX_OWNER(index));
if (workdir == NULL)
return git__throw(GIT_EBAREINDEX,
"Failed to initialize entry. Cannot resolved workdir");
git_path_join(full_path, workdir, rel_path);
if (p_lstat(full_path, &st) < 0)
return git__throw(GIT_ENOTFOUND,
"Failed to initialize entry. '%s' cannot be opened", full_path);
if (stage < 0 || stage > 3)
return git__throw(GIT_ERROR,
"Failed to initialize entry. Invalid stage %i", stage);
/* There is no need to validate the rel_path here, since it will be
* immediately validated by the call to git_blob_create_fromfile.
*/
/* write the blob to disk and get the oid */
if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to initialize index entry");
entry = git__malloc(sizeof(git_index_entry));
entry = git__calloc(1, sizeof(git_index_entry));
if (!entry)
return GIT_ENOMEM;
memset(entry, 0x0, sizeof(git_index_entry));
entry->ctime.seconds = (git_time_t)st.st_ctime;
entry->mtime.seconds = (git_time_t)st.st_mtime;
......
......@@ -151,28 +151,35 @@ cleanup:
return error;
}
static void index_path(char *path, git_indexer *idx)
static int index_path(git_buf *path, git_indexer *idx)
{
char *ptr;
const char prefix[] = "pack-", suffix[] = ".idx";
size_t slash = (size_t)path->size;
ptr = strrchr(path, '/') + 1;
/* search backwards for '/' */
while (slash > 0 && path->ptr[slash - 1] != '/')
slash--;
memcpy(ptr, prefix, strlen(prefix));
ptr += strlen(prefix);
git_oid_fmt(ptr, &idx->hash);
ptr += GIT_OID_HEXSZ;
memcpy(ptr, suffix, strlen(suffix) + 1);
if (git_buf_grow(path, slash + 1 + strlen(prefix) +
GIT_OID_HEXSZ + strlen(suffix) + 1) < GIT_SUCCESS)
return GIT_ENOMEM;
git_buf_truncate(path, slash + 1);
git_buf_puts(path, prefix);
git_oid_fmt(path->ptr + path->size, &idx->hash);
path->size += GIT_OID_HEXSZ;
git_buf_puts(path, suffix);
return git_buf_lasterror(path);
}
int git_indexer_write(git_indexer *idx)
{
git_mwindow *w = NULL;
int error;
size_t namelen;
unsigned int i, long_offsets = 0, left;
struct git_pack_idx_header hdr;
char filename[GIT_PATH_MAX];
git_buf filename = GIT_BUF_INIT;
struct entry *entry;
void *packfile_hash;
git_oid file_hash;
......@@ -180,16 +187,23 @@ int git_indexer_write(git_indexer *idx)
git_vector_sort(&idx->objects);
namelen = strlen(idx->pack->pack_name);
memcpy(filename, idx->pack->pack_name, namelen);
memcpy(filename + namelen - strlen("pack"), "idx", strlen("idx") + 1);
git_buf_sets(&filename, idx->pack->pack_name);
git_buf_truncate(&filename, filename.size - strlen("pack"));
git_buf_puts(&filename, "idx");
error = git_filebuf_open(&idx->file, filename, GIT_FILEBUF_HASH_CONTENTS);
if ((error = git_buf_lasterror(&filename)) < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);
if (error < GIT_SUCCESS)
goto cleanup;
/* Write out the header */
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
hdr.idx_version = htonl(2);
error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));
if (error < GIT_SUCCESS)
goto cleanup;
/* Write out the fanout table */
for (i = 0; i < 256; ++i) {
......@@ -270,14 +284,18 @@ int git_indexer_write(git_indexer *idx)
goto cleanup;
/* Figure out what the final name should be */
index_path(filename, idx);
error = index_path(&filename, idx);
if (error < GIT_SUCCESS)
goto cleanup;
/* Commit file */
error = git_filebuf_commit_at(&idx->file, filename, GIT_PACK_FILE_MODE);
error = git_filebuf_commit_at(&idx->file, filename.ptr, GIT_PACK_FILE_MODE);
cleanup:
git_mwindow_free_all(&idx->pack->mwf);
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&idx->file);
git_buf_free(&filename);
return error;
}
......
......@@ -344,40 +344,47 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
static int load_alternates(git_odb *odb, const char *objects_dir)
{
char alternates_path[GIT_PATH_MAX];
char *buffer, *alternate;
git_buf alternates_path = GIT_BUF_INIT;
char *buffer;
git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
int error;
git_path_join(alternates_path, objects_dir, GIT_ALTERNATES_FILE);
error = git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE);
if (error < GIT_SUCCESS)
return error;
if (git_futils_exists(alternates_path) < GIT_SUCCESS)
if (git_futils_exists(alternates_path.ptr) < GIT_SUCCESS) {
git_buf_free(&alternates_path);
return GIT_SUCCESS;
}
if (git_futils_readbuffer(&alternates_buf, alternates_path) < GIT_SUCCESS)
if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < GIT_SUCCESS) {
git_buf_free(&alternates_path);
return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
}
buffer = (char *)alternates_buf.data;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
char full_path[GIT_PATH_MAX];
if (*alternate == '\0' || *alternate == '#')
continue;
/* relative path: build based on the current `objects` folder */
if (*alternate == '.') {
git_path_join(full_path, objects_dir, alternate);
alternate = full_path;
error = git_buf_joinpath(&alternates_path, objects_dir, alternate);
if (error < GIT_SUCCESS)
break;
alternate = git_buf_cstr(&alternates_path);
}
if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS)
break;
}
git_buf_free(&alternates_path);
git_futils_freebuffer(&alternates_buf);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to load alternates");
......
......@@ -133,7 +133,7 @@ static int pack_window_contains(git_mwindow *win, off_t offset);
static int packfile_sort__cb(const void *a_, const void *b_);
static int packfile_load__cb(void *_data, char *path);
static int packfile_load__cb(void *_data, git_buf *path);
static int packfile_refresh_all(struct pack_backend *backend);
static int pack_entry_find(struct git_pack_entry *e,
......@@ -207,23 +207,23 @@ static int packfile_sort__cb(const void *a_, const void *b_)
static int packfile_load__cb(void *_data, char *path)
static int packfile_load__cb(void *_data, git_buf *path)
{
struct pack_backend *backend = (struct pack_backend *)_data;
struct git_pack_file *pack;
int error;
size_t i;
if (git__suffixcmp(path, ".idx") != 0)
if (git__suffixcmp(path->ptr, ".idx") != 0)
return GIT_SUCCESS; /* not an index */
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
if (memcmp(p->pack_name, path, strlen(path) - strlen(".idx")) == 0)
if (memcmp(p->pack_name, path->ptr, path->size - strlen(".idx")) == 0)
return GIT_SUCCESS;
}
error = git_packfile_check(&pack, path);
error = git_packfile_check(&pack, path->ptr);
if (error == GIT_ENOTFOUND) {
/* ignore missing .pack file as git does */
return GIT_SUCCESS;
......@@ -250,11 +250,13 @@ static int packfile_refresh_all(struct pack_backend *backend)
return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found");
if (st.st_mtime != backend->pack_folder_mtime) {
char path[GIT_PATH_MAX];
strcpy(path, backend->pack_folder);
git_buf path = GIT_BUF_INIT;
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
error = git_futils_direach(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend);
error = git_futils_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to refresh packfiles");
......@@ -451,27 +453,25 @@ static void pack_backend__free(git_odb_backend *_backend)
int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
{
struct pack_backend *backend;
char path[GIT_PATH_MAX];
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
backend = git__calloc(1, sizeof(struct pack_backend));
if (backend == NULL)
return GIT_ENOMEM;
if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < GIT_SUCCESS) {
git__free(backend);
return GIT_ENOMEM;
}
error = git_vector_init(&backend->packs, 8, packfile_sort__cb);
if (error < GIT_SUCCESS)
goto cleanup;
git_path_join(path, objects_dir, "pack");
if (git_futils_isdir(path) == GIT_SUCCESS) {
backend->pack_folder = git__strdup(path);
backend->pack_folder_mtime = 0;
error = git_buf_joinpath(&path, objects_dir, "pack");
if (error < GIT_SUCCESS)
goto cleanup;
if (backend->pack_folder == NULL) {
git__free(backend);
return GIT_ENOMEM;
}
if (git_futils_isdir(git_buf_cstr(&path)) == GIT_SUCCESS) {
backend->pack_folder = git_buf_detach(&path);
backend->pack_folder_mtime = 0;
}
backend->parent.read = &pack_backend__read;
......@@ -481,5 +481,11 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
backend->parent.free = &pack_backend__free;
*backend_out = (git_odb_backend *)backend;
return GIT_SUCCESS;
cleanup:
if (error < GIT_SUCCESS)
git__free(backend);
git_buf_free(&path);
return error;
}
......@@ -16,7 +16,7 @@
* Based on the Android implementation, BSD licensed.
* Check http://android.git.kernel.org/
*/
int git_path_basename_r(char *buffer, size_t bufflen, const char *path)
int git_path_basename_r(git_buf *buffer, const char *path)
{
const char *endp, *startp;
int len, result;
......@@ -49,18 +49,13 @@ int git_path_basename_r(char *buffer, size_t bufflen, const char *path)
Exit:
result = len;
if (buffer == NULL) {
return result;
}
if (len > (int)bufflen-1) {
len = (int)bufflen-1;
result = GIT_ENOMEM;
}
if (len >= 0) {
memmove(buffer, startp, len);
buffer[len] = 0;
if (buffer != NULL) {
if (git_buf_set(buffer, startp, len) < GIT_SUCCESS)
return git__rethrow(git_buf_lasterror(buffer),
"Could not get basename of '%s'", path);
}
return result;
}
......@@ -68,7 +63,7 @@ Exit:
* Based on the Android implementation, BSD licensed.
* Check http://android.git.kernel.org/
*/
int git_path_dirname_r(char *buffer, size_t bufflen, const char *path)
int git_path_dirname_r(git_buf *buffer, const char *path)
{
const char *endp;
int result, len;
......@@ -114,59 +109,39 @@ int git_path_dirname_r(char *buffer, size_t bufflen, const char *path)
Exit:
result = len;
if (len+1 > GIT_PATH_MAX) {
return GIT_ENOMEM;
}
if (buffer == NULL)
return result;
if (len > (int)bufflen-1) {
len = (int)bufflen-1;
result = GIT_ENOMEM;
if (buffer != NULL) {
if (git_buf_set(buffer, path, len) < GIT_SUCCESS)
return git__rethrow(git_buf_lasterror(buffer),
"Could not get dirname of '%s'", path);
}
if (len >= 0) {
memmove(buffer, path, len);
buffer[len] = 0;
}
return result;
}
char *git_path_dirname(const char *path)
{
char *dname = NULL;
int len;
git_buf buf = GIT_BUF_INIT;
char *dirname;
len = (path ? strlen(path) : 0) + 2;
dname = (char *)git__malloc(len);
if (dname == NULL)
return NULL;
git_path_dirname_r(&buf, path);
dirname = git_buf_detach(&buf);
git_buf_free(&buf); /* avoid memleak if error occurs */
if (git_path_dirname_r(dname, len, path) < GIT_SUCCESS) {
git__free(dname);
return NULL;
}
return dname;
return dirname;
}
char *git_path_basename(const char *path)
{
char *bname = NULL;
int len;
len = (path ? strlen(path) : 0) + 2;
bname = (char *)git__malloc(len);
if (bname == NULL)
return NULL;
git_buf buf = GIT_BUF_INIT;
char *basename;
if (git_path_basename_r(bname, len, path) < GIT_SUCCESS) {
git__free(bname);
return NULL;
}
git_path_basename_r(&buf, path);
basename = git_buf_detach(&buf);
git_buf_free(&buf); /* avoid memleak if error occurs */
return bname;
return basename;
}
......@@ -188,39 +163,6 @@ const char *git_path_topdir(const char *path)
return &path[i + 1];
}
void git_path_join_n(char *buffer_out, int count, ...)
{
va_list ap;
int i;
char *buffer_start = buffer_out;
va_start(ap, count);
for (i = 0; i < count; ++i) {
const char *path;
int len;
path = va_arg(ap, const char *);
assert((i == 0) || path != buffer_start);
if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/')
path++;
if (!*path)
continue;
len = strlen(path);
memmove(buffer_out, path, len);
buffer_out = buffer_out + len;
if (i < count - 1 && buffer_out[-1] != '/')
*buffer_out++ = '/';
}
va_end(ap);
*buffer_out = '\0';
}
int git_path_root(const char *path)
{
int offset = 0;
......@@ -237,34 +179,61 @@ int git_path_root(const char *path)
return -1; /* Not a real error. Rather a signal than the path is not rooted */
}
int git_path_prettify(char *path_out, const char *path, const char *base)
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{
char *result;
char *result = NULL;
int error = GIT_SUCCESS;
git_buf_clear(path_out);
/* construct path if needed */
if (base != NULL && git_path_root(path) < 0) {
if ((error = git_buf_joinpath(path_out, base, path)) < GIT_SUCCESS)
return error;
path = path_out->ptr;
}
/* allow realpath to allocate the buffer */
if (path != NULL)
result = p_realpath(path, NULL);
if (base == NULL || git_path_root(path) >= 0) {
result = p_realpath(path, path_out);
if (result) {
error = git_buf_sets(path_out, result);
git__free(result);
} else {
char aux_path[GIT_PATH_MAX];
git_path_join(aux_path, base, path);
result = p_realpath(aux_path, path_out);
error = GIT_EOSERR;
}
return result ? GIT_SUCCESS : GIT_EOSERR;
return error;
}
int git_path_prettify_dir(char *path_out, const char *path, const char *base)
int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
{
size_t end;
int error = git_path_prettify(path_out, path, base);
if (git_path_prettify(path_out, path, base) < GIT_SUCCESS)
return GIT_EOSERR;
if (error == GIT_SUCCESS)
error = git_path_to_dir(path_out);
end = strlen(path_out);
return error;
}
if (end && path_out[end - 1] != '/') {
path_out[end] = '/';
path_out[end + 1] = '\0';
}
int git_path_to_dir(git_buf *path)
{
if (path->asize > 0 &&
path->size > 0 &&
path->ptr[path->size - 1] != '/')
git_buf_putc(path, '/');
return GIT_SUCCESS;
return git_buf_lasterror(path);
}
void git_path_string_to_dir(char* path, size_t size)
{
size_t end = strlen(path);
if (end && path[end - 1] != '/' && end < size) {
path[end] = '/';
path[end + 1] = '\0';
}
}
......@@ -8,6 +8,7 @@
#define INCLUDE_path_h__
#include "common.h"
#include "buffer.h"
/*
* The dirname() function shall take a pointer to a character string
......@@ -22,11 +23,13 @@
* The `git_path_dirname` implementation is thread safe. The returned
* string must be manually free'd.
*
* The `git_path_dirname_r` implementation expects a string allocated
* by the user with big enough size.
* The `git_path_dirname_r` implementation writes the dirname to a `git_buf`
* if the buffer pointer is not NULL.
* It returns an error code < 0 if there is an allocation error, otherwise
* the length of the dirname (which will be > 0).
*/
extern char *git_path_dirname(const char *path);
extern int git_path_dirname_r(char *buffer, size_t bufflen, const char *path);
extern int git_path_dirname_r(git_buf *buffer, const char *path);
/*
* This function returns the basename of the file, which is the last
......@@ -40,32 +43,22 @@ extern int git_path_dirname_r(char *buffer, size_t bufflen, const char *path);
* The `git_path_basename` implementation is thread safe. The returned
* string must be manually free'd.
*
* The `git_path_basename_r` implementation expects a string allocated
* by the user with big enough size.
* The `git_path_basename_r` implementation writes the basename to a `git_buf`.
* It returns an error code < 0 if there is an allocation error, otherwise
* the length of the basename (which will be >= 0).
*/
extern char *git_path_basename(const char *path);
extern int git_path_basename_r(char *buffer, size_t bufflen, const char *path);
extern int git_path_basename_r(git_buf *buffer, const char *path);
extern const char *git_path_topdir(const char *path);
/**
* Join two paths together. Takes care of properly fixing the
* middle slashes and everything
*
* The paths are joined together into buffer_out; this is expected
* to be an user allocated buffer of `GIT_PATH_MAX` size
*/
extern void git_path_join_n(char *buffer_out, int npath, ...);
GIT_INLINE(void) git_path_join(char *buffer_out, const char *path_a, const char *path_b)
{
git_path_join_n(buffer_out, 2, path_a, path_b);
}
extern int git_path_root(const char *path);
int git_path_root(const char *path);
extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);
int git_path_prettify(char *path_out, const char *path, const char *base);
int git_path_prettify_dir(char *path_out, const char *path, const char *base);
extern int git_path_to_dir(git_buf *path);
extern void git_path_string_to_dir(char* path, size_t size);
#ifdef GIT_WIN32
GIT_INLINE(void) git_path_mkposix(char *path)
......
......@@ -268,8 +268,7 @@ void git_pkt_free(git_pkt *pkt)
int git_pkt_buffer_flush(git_buf *buf)
{
git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
}
int git_pkt_send_flush(int s)
......@@ -291,9 +290,7 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
git_buf_grow(buf, buf->size + len);
git_oid_fmt(oid, &head->oid);
git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
}
static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, GIT_SOCKET fd)
......@@ -401,8 +398,7 @@ int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
memset(oidhex, 0x0, sizeof(oidhex));
git_oid_fmt(oidhex, oid);
git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
}
int git_pkt_send_have(git_oid *oid, int fd)
......@@ -416,8 +412,7 @@ int git_pkt_send_have(git_oid *oid, int fd)
int git_pkt_buffer_done(git_buf *buf)
{
git_buf_puts(buf, pkt_done_str);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_puts(buf, pkt_done_str);
}
int git_pkt_send_done(int fd)
......
......@@ -35,7 +35,8 @@ int p_getcwd(char *buffer_out, size_t size)
git_path_mkposix(buffer_out);
git_path_join(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash
git_path_string_to_dir(buffer_out, size); //Ensure the path ends with a trailing slash
return GIT_SUCCESS;
}
......
......@@ -51,7 +51,7 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_puts(&log, oid_new);
git_signature__writebuf(&log, " ", committer);
log.size--; /* drop LF */
git_buf_truncate(&log, log.size - 1); /* drop LF */
if (msg) {
if (strchr(msg, '\n')) {
......@@ -65,15 +65,21 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_putc(&log, '\n');
if ((error = git_buf_lasterror(&log)) < GIT_SUCCESS) {
git_buf_free(&log);
return git__rethrow(error, "Failed to write reflog. Memory allocation failure");
}
if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) {
git_buf_free(&log);
return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path);
return git__rethrow(error, "Failed to write reflog. Cannot open reflog `%s`", log_path);
}
git_filebuf_write(&fbuf, log.ptr, log.size);
error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
git_buf_free(&log);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog");
}
......@@ -176,7 +182,7 @@ void git_reflog_free(git_reflog *reflog)
int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
char log_path[GIT_PATH_MAX];
git_buf log_path = GIT_BUF_INIT;
git_fbuffer log_file = GIT_FBUFFER_INIT;
git_reflog *log = NULL;
......@@ -185,23 +191,28 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
if ((error = reflog_init(&log, ref)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to read reflog. Cannot init reflog");
git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) {
git_reflog_free(log);
return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path);
if ((error = git_futils_readbuffer(&log_file, log_path.ptr)) < GIT_SUCCESS) {
git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path.ptr);
goto cleanup;
}
error = reflog_parse(log, log_file.data, log_file.len);
git_futils_freebuffer(&log_file);
if (error == GIT_SUCCESS)
*reflog = log;
if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read reflog");
else
*reflog = log;
cleanup:
if (error != GIT_SUCCESS && log != NULL)
git_reflog_free(log);
git_futils_freebuffer(&log_file);
git_buf_free(&log_path);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog");
return error;
}
int git_reflog_write(git_reference *ref, const git_oid *oid_old,
......@@ -210,7 +221,7 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
int error;
char old[GIT_OID_HEXSZ+1];
char new[GIT_OID_HEXSZ+1];
char log_path[GIT_PATH_MAX];
git_buf log_path = GIT_BUF_INIT;
git_reference *r;
const git_oid *oid;
......@@ -220,65 +231,83 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
oid = git_reference_oid(r);
if (oid == NULL) {
git_reference_free(r);
return git__throw(GIT_ERROR,
error = git__throw(GIT_ERROR,
"Failed to write reflog. Cannot resolve reference `%s`", r->name);
git_reference_free(r);
return error;
}
git_reference_free(r);
git_oid_to_string(new, GIT_OID_HEXSZ+1, oid);
git_path_join_n(log_path, 3,
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
git_reference_free(r);
if (git_futils_exists(log_path)) {
error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE);
if (git_futils_exists(log_path.ptr)) {
error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error,
git__rethrow(error,
"Failed to write reflog. Cannot create reflog directory");
} else if (git_futils_isfile(log_path)) {
return git__throw(GIT_ERROR,
"Failed to write reflog. `%s` is directory", log_path);
} else if (git_futils_isfile(log_path.ptr)) {
error = git__throw(GIT_ERROR,
"Failed to write reflog. `%s` is directory", log_path.ptr);
} else if (oid_old == NULL) {
return git__throw(GIT_ERROR,
error = git__throw(GIT_ERROR,
"Failed to write reflog. Old OID cannot be NULL for existing reference");
}
if (error < GIT_SUCCESS)
goto cleanup;
if (oid_old)
git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old);
git_oid_to_string(old, sizeof(old), oid_old);
else
p_snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0);
p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);
error = reflog_write(log_path.ptr, old, new, committer, msg);
return reflog_write(log_path, old, new, committer, msg);
cleanup:
git_buf_free(&log_path);
return error;
}
int git_reflog_rename(git_reference *ref, const char *new_name)
{
char old_path[GIT_PATH_MAX];
char new_path[GIT_PATH_MAX];
int error;
git_buf old_path = GIT_BUF_INIT;
git_buf new_path = GIT_BUF_INIT;
if (git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name) &&
git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, new_name))
error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path));
else
error = GIT_ENOMEM;
git_path_join_n(old_path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name);
git_path_join_n(new_path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, new_name);
git_buf_free(&old_path);
git_buf_free(&new_path);
return p_rename(old_path, new_path);
return error;
}
int git_reflog_delete(git_reference *ref)
{
char path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
git_buf path = GIT_BUF_INIT;
error = git_buf_join_n(&path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
git_path_join_n(path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name);
if (error == GIT_SUCCESS && git_futils_exists(path.ptr) == 0)
error = p_unlink(path.ptr);
if (git_futils_exists(path))
return GIT_SUCCESS;
git_buf_free(&path);
return p_unlink(path);
return error;
}
unsigned int git_reflog_entrycount(git_reflog *reflog)
......
......@@ -93,3 +93,21 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
return GIT_SUCCESS;
}
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
{
if (git_buf_sets(out, spec->dst) < GIT_SUCCESS)
return git_buf_lasterror(out);
/*
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
if (out->size > 0 && out->ptr[out->size - 1] != '*')
return GIT_SUCCESS;
git_buf_truncate(out, out->size - 1); /* remove trailing '*' */
git_buf_puts(out, name);
return git_buf_lasterror(out);
}
......@@ -8,6 +8,7 @@
#define INCLUDE_refspec_h__
#include "git2/refspec.h"
#include "buffer.h"
struct git_refspec {
struct git_refspec *next;
......@@ -20,4 +21,15 @@ struct git_refspec {
int git_refspec_parse(struct git_refspec *refspec, const char *str);
/**
* Transform a reference to its target following the refspec's rules,
* and writes the results into a git_buf.
*
* @param out where to store the target name
* @param spec the refspec
* @param name the name of the reference to transform
* @return GIT_SUCCESS or error if buffer allocation fails
*/
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name);
#endif
......@@ -263,7 +263,7 @@ int git_remote_update_tips(git_remote *remote)
{
int error = GIT_SUCCESS;
unsigned int i = 0;
char refname[GIT_PATH_MAX];
git_buf refname = GIT_BUF_INIT;
git_vector *refs = &remote->refs;
git_remote_head *head;
git_reference *ref;
......@@ -271,8 +271,6 @@ int git_remote_update_tips(git_remote *remote)
assert(remote);
memset(refname, 0x0, sizeof(refname));
if (refs->length == 0)
return GIT_SUCCESS;
......@@ -289,18 +287,20 @@ int git_remote_update_tips(git_remote *remote)
for (; i < refs->length; ++i) {
head = refs->contents[i];
error = git_refspec_transform(refname, sizeof(refname), spec, head->name);
error = git_refspec_transform_r(&refname, spec, head->name);
if (error < GIT_SUCCESS)
return error;
break;
error = git_reference_create_oid(&ref, remote->repo, refname, &head->oid, 1);
error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1);
if (error < GIT_SUCCESS)
return error;
break;
git_reference_free(ref);
}
return GIT_SUCCESS;
git_buf_free(&refname);
return error;
}
int git_remote_connected(git_remote *remote)
......
......@@ -16,7 +16,9 @@ void git_signature_free(git_signature *sig)
return;
git__free(sig->name);
sig->name = NULL;
git__free(sig->email);
sig->email = NULL;
git__free(sig);
}
......
......@@ -148,15 +148,22 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
return GIT_SUCCESS;
}
static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name)
static int retrieve_tag_reference(
git_reference **tag_reference_out,
git_buf *ref_name_out,
git_repository *repo,
const char *tag_name)
{
git_reference *tag_ref;
int error;
*tag_reference_out = NULL;
git_path_join(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_lookup(&tag_ref, repo, ref_name_out);
error = git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to retrieve tag reference");
error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to retrieve tag reference");
......@@ -184,9 +191,10 @@ static int write_tag_annotation(
git_buf_putc(&tag, '\n');
git_buf_puts(&tag, message);
if (git_buf_oom(&tag)) {
error = git_buf_lasterror(&tag);
if (error < GIT_SUCCESS) {
git_buf_free(&tag);
return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data");
return git__rethrow(error, "Not enough memory to build the tag data");
}
error = git_repository_odb__weakptr(&odb, repo);
......@@ -215,56 +223,54 @@ static int git_tag_create__internal(
int create_tag_annotation)
{
git_reference *new_ref = NULL;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
int error, should_update_ref = 0;
const char *errmsg = "Failed to create tag";
assert(repo && tag_name && target);
assert(!create_tag_annotation || (tagger && message));
if (git_object_owner(target) != repo)
return git__throw(GIT_EINVALIDARGS, "The given target does not belong to this repository");
error = retrieve_tag_reference(&new_ref, ref_name, repo, tag_name);
switch (error) {
case GIT_SUCCESS:
case GIT_ENOTFOUND:
break;
return git__throw(GIT_EINVALIDARGS,
"The given target does not belong to this repository");
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
goto cleanup;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
error = GIT_EEXISTS;
errmsg = "Tag already exists";
goto cleanup;
} else {
should_update_ref = 1;
}
}
if (create_tag_annotation) {
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return error;
}
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
goto cleanup;
} else
git_oid_cpy(oid, git_object_id(target));
if (!should_update_ref)
error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
else
error = git_reference_set_oid(new_ref, oid);
cleanup:
git_reference_free(new_ref);
git_buf_free(&ref_name);
if (error < GIT_SUCCESS)
git__rethrow(error, "%s", errmsg);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
return error;
}
int git_tag_create(
......@@ -293,12 +299,13 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
{
git_tag tag;
int error, should_update_ref = 0;
const char *errmsg = "Failed to create tag";
git_odb *odb;
git_odb_stream *stream;
git_odb_object *target_obj;
git_reference *new_ref = NULL;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
assert(oid && buffer);
......@@ -310,78 +317,77 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
/* validate the buffer */
if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create tag");
goto cleanup;
/* validate the target */
if ((error = git_odb_read(&target_obj, odb, &tag.target)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create tag");
goto cleanup;
if (tag.type != target_obj->raw.type)
return git__throw(error, "The type for the given target is invalid");
if (tag.type != target_obj->raw.type) {
error = GIT_EINVALIDTYPE;
errmsg = "The type for the given target is invalid";
goto cleanup;
}
git_odb_object_free(target_obj);
error = retrieve_tag_reference(&new_ref, ref_name, repo, tag.tag_name);
switch (error) {
case GIT_SUCCESS:
case GIT_ENOTFOUND:
break;
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag.tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
goto cleanup;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
error = GIT_EEXISTS;
errmsg = "Tag already exists";
goto cleanup;
} else {
should_update_ref = 1;
}
}
/* write the buffer */
if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS)
goto cleanup;
stream->write(stream, buffer, strlen(buffer));
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
if (error < GIT_SUCCESS)
goto cleanup;
if (!should_update_ref)
error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
else
error = git_reference_set_oid(new_ref, oid);
cleanup:
git_reference_free(new_ref);
git_signature_free(tag.tagger);
git__free(tag.tag_name);
git__free(tag.message);
git_buf_free(&ref_name);
if (error < GIT_SUCCESS)
git__rethrow(error, "%s", errmsg);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
return error;
}
int git_tag_delete(git_repository *repo, const char *tag_name)
{
int error;
git_reference *tag_ref;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
git_buf_free(&ref_name);
error = retrieve_tag_reference(&tag_ref, ref_name, repo, tag_name);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to delete tag");
......
......@@ -68,7 +68,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
git_buf_put(request, url, delim - url);
git_buf_putc(request, '\0');
return git_buf_oom(request);
return git_buf_lasterror(request);
}
static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
......
......@@ -77,10 +77,7 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
}
git_buf_puts(buf, "\r\n");
if (git_buf_oom(buf))
return GIT_ENOMEM;
return GIT_SUCCESS;
return git_buf_lasterror(buf);
}
static int do_connect(transport_http *t, const char *host, const char *port)
......@@ -608,8 +605,9 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
char buffer[1024];
gitno_buffer buf;
download_pack_cbdata data;
git_filebuf file;
char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-received\0";
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
char suff[] = "/objects/pack/pack-received\0";
/*
* This is part of the previous response, so we don't want to
......@@ -625,13 +623,15 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
git_path_join(path, repo->path_repository, suff);
if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
}
error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
error = git_buf_joinpath(&path, repo->path_repository, suff);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -671,6 +671,7 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&file);
git_buf_free(&path);
return error;
}
......
......@@ -577,9 +577,9 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
}
if (git_buf_oom(&tree)) {
if ((error = git_buf_lasterror(&tree)) < GIT_SUCCESS) {
git_buf_free(&tree);
return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data");
return git__rethrow(error, "Not enough memory to build the tree data");
}
error = git_repository_odb__weakptr(&odb, repo);
......@@ -631,7 +631,7 @@ void git_treebuilder_free(git_treebuilder *bld)
static int tree_frompath(
git_tree **parent_out,
git_tree *root,
const char *treeentry_path,
git_buf *treeentry_path,
int offset)
{
char *slash_pos = NULL;
......@@ -639,11 +639,11 @@ static int tree_frompath(
int error = GIT_SUCCESS;
git_tree *subtree;
if (!*(treeentry_path + offset))
if (!*(treeentry_path->ptr + offset))
return git__rethrow(GIT_EINVALIDPATH,
"Invalid relative path to a tree entry '%s'.", treeentry_path);
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
slash_pos = (char *)strchr(treeentry_path + offset, '/');
slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/');
if (slash_pos == NULL)
return git_tree_lookup(
......@@ -652,13 +652,13 @@ static int tree_frompath(
git_object_id((const git_object *)root)
);
if (slash_pos == treeentry_path + offset)
if (slash_pos == treeentry_path->ptr + offset)
return git__rethrow(GIT_EINVALIDPATH,
"Invalid relative path to a tree entry '%s'.", treeentry_path);
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
*slash_pos = '\0';
entry = git_tree_entry_byname(root, treeentry_path + offset);
entry = git_tree_entry_byname(root, treeentry_path->ptr + offset);
if (slash_pos != NULL)
*slash_pos = '/';
......@@ -666,7 +666,7 @@ static int tree_frompath(
if (entry == NULL)
return git__rethrow(GIT_ENOTFOUND,
"No tree entry can be found from "
"the given tree and relative path '%s'.", treeentry_path);
"the given tree and relative path '%s'.", treeentry_path->ptr);
error = git_tree_lookup(&subtree, root->object.repo, &entry->oid);
......@@ -677,7 +677,7 @@ static int tree_frompath(
parent_out,
subtree,
treeentry_path,
slash_pos - treeentry_path + 1
(slash_pos - treeentry_path->ptr) + 1
);
git_tree_free(subtree);
......@@ -689,70 +689,82 @@ int git_tree_get_subtree(
git_tree *root,
const char *subtree_path)
{
char buffer[GIT_PATH_MAX];
int error;
git_buf buffer = GIT_BUF_INIT;
assert(subtree && root && subtree_path);
strncpy(buffer, subtree_path, GIT_PATH_MAX);
return tree_frompath(subtree, root, buffer, 0);
if ((error = git_buf_sets(&buffer, subtree_path)) == GIT_SUCCESS)
error = tree_frompath(subtree, root, &buffer, 0);
git_buf_free(&buffer);
return error;
}
static int tree_walk_post(
git_tree *tree,
git_treewalk_cb callback,
char *root,
size_t root_len,
git_buf *path,
void *payload)
{
int error;
int error = GIT_SUCCESS;
unsigned int i;
for (i = 0; i < tree->entries.length; ++i) {
git_tree_entry *entry = tree->entries.contents[i];
root[root_len] = '\0';
if (callback(root, entry, payload) < 0)
if (callback(path->ptr, entry, payload) < 0)
continue;
if (ENTRY_IS_TREE(entry)) {
git_tree *subtree;
size_t path_len = path->size;
if ((error = git_tree_lookup(
&subtree, tree->object.repo, &entry->oid)) < 0)
return error;
break;
strcpy(root + root_len, entry->filename);
root[root_len + entry->filename_len] = '/';
/* append the next entry to the path */
git_buf_puts(path, entry->filename);
git_buf_putc(path, '/');
if ((error = git_buf_lasterror(path)) < GIT_SUCCESS)
break;
tree_walk_post(subtree,
callback, root,
root_len + entry->filename_len + 1,
payload
);
error = tree_walk_post(subtree, callback, path, payload);
if (error < GIT_SUCCESS)
break;
git_buf_truncate(path, path_len);
git_tree_free(subtree);
}
}
return GIT_SUCCESS;
return error;
}
int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
{
char root_path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
git_buf root_path = GIT_BUF_INIT;
root_path[0] = '\0';
switch (mode) {
case GIT_TREEWALK_POST:
return tree_walk_post(tree, callback, root_path, 0, payload);
error = tree_walk_post(tree, callback, &root_path, payload);
break;
case GIT_TREEWALK_PRE:
return git__throw(GIT_ENOTIMPLEMENTED,
error = git__throw(GIT_ENOTIMPLEMENTED,
"Preorder tree walking is still not implemented");
break;
default:
return git__throw(GIT_EINVALIDARGS,
error = git__throw(GIT_EINVALIDARGS,
"Invalid walking mode for tree walk");
break;
}
git_buf_free(&root_path);
return error;
}
......@@ -304,32 +304,37 @@ int p_hide_directory__w32(const char *path)
char *p_realpath(const char *orig_path, char *buffer)
{
int ret, alloc = 0;
int ret;
wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
if (buffer == NULL) {
buffer = (char *)git__malloc(GIT_PATH_MAX);
alloc = 1;
}
ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
git__free(orig_path_w);
if (!ret || ret > GIT_PATH_MAX) {
git__free(buffer_w);
if (alloc) git__free(buffer);
return NULL;
buffer = NULL;
goto done;
}
if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) {
git__free(buffer_w);
if (alloc) git__free(buffer);
if (buffer == NULL) {
int buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
if (!buffer_sz ||
!(buffer = (char *)git__malloc(buffer_sz)) ||
!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))
{
git__free(buffer);
buffer = NULL;
}
} else {
if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL))
buffer = NULL;
}
done:
git__free(buffer_w);
git_path_mkposix(buffer);
if (buffer)
git_path_mkposix(buffer);
return buffer;
}
......
......@@ -111,6 +111,7 @@ 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_rmdir__delete_recursive(void);
extern void test_core_rmdir__fail_to_delete_non_empty_dir(void);
extern void test_core_rmdir__initialize(void);
......
......@@ -42,4 +42,13 @@ GIT_INLINE(void) cl_assert_strequal_internal(
}
}
/*
* Some utility macros for building long strings
*/
#define REP4(STR) STR STR STR STR
#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP256(STR) REP16(REP16(STR))
#define REP1024(STR) REP4(REP256(STR))
#endif
......@@ -171,7 +171,8 @@ static const struct clay_func _clay_cb_core_path[] = {
{"1", &test_core_path__1},
{"2", &test_core_path__2},
{"5", &test_core_path__5},
{"6", &test_core_path__6}
{"6", &test_core_path__6},
{"7", &test_core_path__7}
};
static const struct clay_func _clay_cb_core_rmdir[] = {
{"delete_recursive", &test_core_rmdir__delete_recursive},
......@@ -351,7 +352,7 @@ static const struct clay_suite _clay_suites[] = {
"core::path",
{NULL, NULL},
{NULL, NULL},
_clay_cb_core_path, 5
_clay_cb_core_path, 6
},
{
"core::rmdir",
......@@ -494,7 +495,7 @@ static const struct clay_suite _clay_suites[] = {
};
static size_t _clay_suite_count = 34;
static size_t _clay_callback_count = 113;
static size_t _clay_callback_count = 114;
/* Core test functions */
static void
......
......@@ -5,9 +5,6 @@
const char *test_string = TESTSTR;
const char *test_string_x2 = TESTSTR TESTSTR;
#define REP4(STR) STR STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP1024(STR) REP16(REP16(REP4(STR)))
#define TESTSTR_4096 REP1024("1234")
#define TESTSTR_8192 REP1024("12341234")
const char *test_4096 = TESTSTR_4096;
......@@ -52,7 +49,7 @@ void test_core_buffer__2(void)
{
git_buf buf = GIT_BUF_INIT;
int i;
char data[100];
char data[128];
cl_assert(buf.size == 0);
......@@ -135,22 +132,28 @@ void test_core_buffer__2(void)
git_buf_puts(&buf, REP4("0123456789"));
cl_assert(git_buf_oom(&buf) == 0);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal(REP4("0123456789"), data);
git_buf_copy_cstr(data, 11, &buf);
cl_assert_strequal(data, "0123456789");
cl_assert_strequal("0123456789", data);
git_buf_copy_cstr(data, 3, &buf);
cl_assert_strequal(data, "01");
cl_assert_strequal("01", data);
git_buf_copy_cstr(data, 1, &buf);
cl_assert_strequal(data, "");
cl_assert_strequal("", data);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal(REP4("0123456789"), data);
git_buf_sets(&buf, REP256("x"));
git_buf_copy_cstr(data, sizeof(data), &buf);
/* since sizeof(data) == 128, only 127 bytes should be copied */
cl_assert_strequal(REP4(REP16("x")) REP16("x") REP16("x")
REP16("x") "xxxxxxxxxxxxxxx", data);
git_buf_free(&buf);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, "");
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal("", data);
}
/* let's do some tests with larger buffers to push our limits */
......@@ -340,9 +343,10 @@ void test_core_buffer__6(void)
}
/* test take cstr data */
/* test detach/attach data */
void test_core_buffer__7(void)
{
const char *fun = "This is fun";
git_buf a = GIT_BUF_INIT;
char *b = NULL;
......@@ -350,18 +354,36 @@ void test_core_buffer__7(void)
cl_assert(git_buf_oom(&a) == 0);
cl_assert_strequal("foo", git_buf_cstr(&a));
b = git_buf_take_cstr(&a);
b = git_buf_detach(&a);
cl_assert_strequal("foo", b);
cl_assert_strequal("", a.ptr);
git__free(b);
b = git_buf_take_cstr(&a);
b = git_buf_detach(&a);
cl_assert_strequal(NULL, b);
cl_assert_strequal("", a.ptr);
git_buf_free(&a);
b = git__strdup(fun);
git_buf_attach(&a, b, 0);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
git_buf_free(&a);
b = git__strdup(fun);
git_buf_attach(&a, b, strlen(fun) + 1);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
git_buf_free(&a);
}
......
......@@ -9,10 +9,10 @@ typedef struct name_data {
typedef struct walk_data {
char *sub; /* sub-directory name */
name_data *names; /* name state data */
git_buf path;
} walk_data;
static char path_buffer[GIT_PATH_MAX];
static char *top_dir = "dir-walk";
static walk_data *state_loc;
......@@ -27,7 +27,8 @@ static void setup(walk_data *d)
if (strcmp(d->sub, ".") != 0)
cl_must_pass(p_mkdir(d->sub, 0777));
strcpy(path_buffer, d->sub);
cl_git_pass(git_buf_sets(&d->path, d->sub));
state_loc = d;
for (n = d->names; n->name; n++) {
......@@ -53,6 +54,8 @@ static void dirent_cleanup__cb(void *_d)
cl_must_pass(p_chdir(".."));
cl_must_pass(p_rmdir(top_dir));
git_buf_free(&d->path);
}
static void check_counts(walk_data *d)
......@@ -64,7 +67,7 @@ static void check_counts(walk_data *d)
}
}
static int one_entry(void *state, char *path)
static int one_entry(void *state, git_buf *path)
{
walk_data *d = (walk_data *) state;
name_data *n;
......@@ -72,11 +75,11 @@ static int one_entry(void *state, char *path)
if (state != state_loc)
return GIT_ERROR;
if (path != path_buffer)
if (path != &d->path)
return GIT_ERROR;
for (n = d->names; n->name; n++) {
if (!strcmp(n->name, path)) {
if (!strcmp(n->name, path->ptr)) {
n->count++;
return 0;
}
......@@ -85,7 +88,7 @@ static int one_entry(void *state, char *path)
return GIT_ERROR;
}
static int dont_call_me(void *GIT_UNUSED(state), char *GIT_UNUSED(path))
static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
{
GIT_UNUSED_ARG(state)
GIT_UNUSED_ARG(path)
......@@ -102,7 +105,8 @@ static name_data dot_names[] = {
};
static walk_data dot = {
".",
dot_names
dot_names,
GIT_BUF_INIT
};
/* make sure that the '.' folder is not traversed */
......@@ -111,8 +115,7 @@ void test_core_dirent__dont_traverse_dot(void)
cl_set_cleanup(&dirent_cleanup__cb, &dot);
setup(&dot);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&dot.path,
one_entry,
&dot));
......@@ -128,7 +131,8 @@ static name_data sub_names[] = {
};
static walk_data sub = {
"sub",
sub_names
sub_names,
GIT_BUF_INIT
};
/* traverse a subfolder */
......@@ -137,8 +141,7 @@ void test_core_dirent__traverse_subfolder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub);
setup(&sub);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&sub.path,
one_entry,
&sub));
......@@ -148,7 +151,8 @@ void test_core_dirent__traverse_subfolder(void)
static walk_data sub_slash = {
"sub/",
sub_names
sub_names,
GIT_BUF_INIT
};
/* traverse a slash-terminated subfolder */
......@@ -157,8 +161,7 @@ void test_core_dirent__traverse_slash_terminated_folder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
setup(&sub_slash);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&sub_slash.path,
one_entry,
&sub_slash));
......@@ -171,7 +174,8 @@ static name_data empty_names[] = {
};
static walk_data empty = {
"empty",
empty_names
empty_names,
GIT_BUF_INIT
};
/* make sure that empty folders are not traversed */
......@@ -180,16 +184,14 @@ void test_core_dirent__dont_traverse_empty_folders(void)
cl_set_cleanup(&dirent_cleanup__cb, &empty);
setup(&empty);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&empty.path,
one_entry,
&empty));
check_counts(&empty);
/* make sure callback not called */
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&empty.path,
dont_call_me,
&empty));
}
......@@ -204,7 +206,8 @@ static name_data odd_names[] = {
};
static walk_data odd = {
"odd",
odd_names
odd_names,
GIT_BUF_INIT
};
/* make sure that strange looking filenames ('..c') are traversed */
......@@ -213,8 +216,7 @@ void test_core_dirent__traverse_weird_filenames(void)
cl_set_cleanup(&dirent_cleanup__cb, &odd);
setup(&odd);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&odd.path,
one_entry,
&odd));
......
......@@ -4,26 +4,30 @@
static void
check_dirname(const char *A, const char *B)
{
char dir[64], *dir2;
git_buf dir = GIT_BUF_INIT;
char *dir2;
cl_assert(git_path_dirname_r(dir, sizeof(dir), A) >= 0);
cl_assert(strcmp(dir, B) == 0);
cl_assert((dir2 = git_path_dirname(A)) != NULL);
cl_assert(strcmp(dir2, B) == 0);
cl_assert(git_path_dirname_r(&dir, A) >= 0);
cl_assert_strequal(B, dir.ptr);
git_buf_free(&dir);
cl_assert((dir2 = git_path_dirname(A)) != NULL);
cl_assert_strequal(B, dir2);
git__free(dir2);
}
static void
check_basename(const char *A, const char *B)
{
char base[64], *base2;
git_buf base = GIT_BUF_INIT;
char *base2;
cl_assert(git_path_basename_r(base, sizeof(base), A) >= 0);
cl_assert(strcmp(base, B) == 0);
cl_assert((base2 = git_path_basename(A)) != NULL);
cl_assert(strcmp(base2, B) == 0);
cl_assert(git_path_basename_r(&base, A) >= 0);
cl_assert_strequal(B, base.ptr);
git_buf_free(&base);
cl_assert((base2 = git_path_basename(A)) != NULL);
cl_assert_strequal(B, base2);
git__free(base2);
}
......@@ -33,16 +37,18 @@ check_topdir(const char *A, const char *B)
const char *dir;
cl_assert((dir = git_path_topdir(A)) != NULL);
cl_assert(strcmp(dir, B) == 0);
cl_assert_strequal(B, dir);
}
static void
check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_buf joined_path = GIT_BUF_INIT;
git_path_join(joined_path, path_a, path_b);
cl_assert(strcmp(joined_path, expected_path) == 0);
cl_git_pass(git_buf_joinpath(&joined_path, path_a, path_b));
cl_assert_strequal(expected_path, joined_path.ptr);
git_buf_free(&joined_path);
}
static void
......@@ -53,17 +59,19 @@ check_joinpath_n(
const char *path_d,
const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_buf joined_path = GIT_BUF_INIT;
cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
path_a, path_b, path_c, path_d));
cl_assert_strequal(expected_path, joined_path.ptr);
git_path_join_n(joined_path, 4, path_a, path_b, path_c, path_d);
cl_assert(strcmp(joined_path, expected_path) == 0);
git_buf_free(&joined_path);
}
/* get the dirname of a path */
void test_core_path__0(void)
{
check_dirname(NULL, ".");
check_dirname("", ".");
check_dirname("a", ".");
......@@ -77,6 +85,8 @@ void test_core_path__0(void)
check_dirname("usr/lib/", "usr");
check_dirname("usr/lib//", "usr");
check_dirname(".git/", ".");
check_dirname(REP16("/abc"), REP15("/abc"));
}
/* get the base name of a path */
......@@ -91,6 +101,9 @@ void test_core_path__1(void)
check_basename("/usr/lib", "lib");
check_basename("/usr/lib//", "lib");
check_basename("usr/lib", "lib");
check_basename(REP16("/abc"), "abc");
check_basename(REP1024("/abc"), "abc");
}
/* get the latest component in a path */
......@@ -125,6 +138,20 @@ void test_core_path__5(void)
check_joinpath("/a", "/b/", "/a/b/");
check_joinpath("/a/", "b/", "/a/b/");
check_joinpath("/a/", "/b/", "/a/b/");
check_joinpath("/abcd", "/defg", "/abcd/defg");
check_joinpath("/abcd", "/defg/", "/abcd/defg/");
check_joinpath("/abcd/", "defg/", "/abcd/defg/");
check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
REP1024("aaaa") "/" REP1024("bbbb"));
check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
REP1024("/aaaa") REP1024("/bbbb"));
}
/* properly join path components for more than one path */
......@@ -136,4 +163,74 @@ void test_core_path__6(void)
check_joinpath_n("", "", "", "a", "a");
check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
REP1024("a") "/" REP1024("b") "/"
REP1024("c") "/" REP1024("d"));
check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
REP1024("/a") REP1024("/b")
REP1024("/c") REP1024("/d"));
}
static void
check_path_to_dir(
const char* path,
const char* expected)
{
git_buf tgt = GIT_BUF_INIT;
git_buf_sets(&tgt, path);
cl_git_pass(git_path_to_dir(&tgt));
cl_assert_strequal(expected, tgt.ptr);
git_buf_free(&tgt);
}
static void
check_string_to_dir(
const char* path,
int maxlen,
const char* expected)
{
int len = strlen(path);
char *buf = git__malloc(len + 2);
strncpy(buf, path, len + 2);
git_path_string_to_dir(buf, maxlen);
cl_assert_strequal(expected, buf);
git__free(buf);
}
/* convert paths to dirs */
void test_core_path__7(void)
{
check_path_to_dir("", "");
check_path_to_dir(".", "./");
check_path_to_dir("./", "./");
check_path_to_dir("a/", "a/");
check_path_to_dir("ab", "ab/");
/* make sure we try just under and just over an expansion that will
* require a realloc
*/
check_path_to_dir("abcdef", "abcdef/");
check_path_to_dir("abcdefg", "abcdefg/");
check_path_to_dir("abcdefgh", "abcdefgh/");
check_path_to_dir("abcdefghi", "abcdefghi/");
check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
check_string_to_dir("", 1, "");
check_string_to_dir(".", 1, ".");
check_string_to_dir(".", 2, "./");
check_string_to_dir(".", 3, "./");
check_string_to_dir("abcd", 3, "abcd");
check_string_to_dir("abcd", 4, "abcd");
check_string_to_dir("abcd", 5, "abcd/");
check_string_to_dir("abcd", 6, "abcd/");
}
......@@ -5,24 +5,26 @@ static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
void test_core_rmdir__initialize(void)
{
char path[GIT_PATH_MAX];
git_buf path = GIT_BUF_INIT;
cl_must_pass(p_mkdir(empty_tmp_dir, 0777));
git_path_join(path, empty_tmp_dir, "/one");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_one");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_two");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_two/three");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/two");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/two"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_buf_free(&path);
}
/* make sure empty dir can be deleted recusively */
......@@ -34,17 +36,19 @@ void test_core_rmdir__delete_recursive(void)
/* make sure non-empty dir cannot be deleted recusively */
void test_core_rmdir__fail_to_delete_non_empty_dir(void)
{
char file[GIT_PATH_MAX];
git_buf file = GIT_BUF_INIT;
int fd;
git_path_join(file, empty_tmp_dir, "/two/file.txt");
cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
fd = p_creat(file, 0666);
fd = p_creat(file.ptr, 0666);
cl_assert(fd >= 0);
cl_must_pass(p_close(fd));
cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
cl_must_pass(p_unlink(file));
cl_must_pass(p_unlink(file.ptr));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
git_buf_free(&file);
}
......@@ -77,17 +77,19 @@ void test_repo_init__bare_repo_noslash(void)
#if 0
BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping out of the current working directory")
char path_repository[GIT_PATH_MAX];
git_buf path_repository = GIT_BUF_INIT;
char current_workdir[GIT_PATH_MAX];
const mode_t mode = 0777;
git_repository* repo;
must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
git_path_join(path_repository, TEMP_REPO_FOLDER, "a/b/c/");
must_pass(git_futils_mkdir_r(path_repository, mode));
must_pass(git_buf_joinpath(&path_repository, TEMP_REPO_FOLDER, "a/b/c/"));
must_pass(git_futils_mkdir_r(path_repository.ptr, mode));
must_pass(chdir(path_repository));
must_pass(chdir(path_repository.ptr));
git_buf_free(&path_repository);
must_pass(git_repository_init(&repo, "../d/e.git", 1));
must_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
......
......@@ -26,23 +26,24 @@ void test_repo_open__standard_empty_repo(void)
/* TODO TODO */
#if 0
BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of the current working directory")
char new_current_workdir[GIT_PATH_MAX];
char current_workdir[GIT_PATH_MAX];
char path_repository[GIT_PATH_MAX];
git_buf new_current_workdir = GIT_BUF_INIT;
git_buf path_repository = GIT_BUF_INIT;
const mode_t mode = 0777;
git_repository* repo;
/* Setup the repository to open */
must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
strcpy(path_repository, current_workdir);
git_path_join_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git");
must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository));
must_pass(git_buf_join_n(&path_repository, 3, current_workdir, TEMP_REPO_FOLDER, "a/d/e.git"));
must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository.ptr));
git_buf_free(&path_repository);
/* Change the current working directory */
git_path_join(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/");
must_pass(git_futils_mkdir_r(new_current_workdir, mode));
must_pass(chdir(new_current_workdir));
must_pass(git_buf_joinpath(&new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/"));
must_pass(git_futils_mkdir_r(new_current_workdir.ptr, mode));
must_pass(chdir(new_current_workdir.ptr));
git_buf_free(&new_current_workdir);
must_pass(git_repository_open(&repo, "../../d/e.git"));
......
......@@ -105,14 +105,15 @@ END_TEST
BEGIN_TEST(path0, "get the dirname of a path")
char dir[64], *dir2;
git_buf dir = GIT_BUF_INIT;
char *dir2;
#define DIRNAME_TEST(A, B) { \
must_be_true(git_path_dirname_r(dir, sizeof(dir), A) >= 0); \
must_be_true(strcmp(dir, B) == 0); \
must_be_true(git_path_dirname_r(&dir, A) >= 0); \
must_be_true(strcmp(B, dir.ptr) == 0); \
must_be_true((dir2 = git_path_dirname(A)) != NULL); \
must_be_true(strcmp(dir2, B) == 0); \
git__free(dir2); \
git__free(dir2); \
}
DIRNAME_TEST(NULL, ".");
......@@ -131,16 +132,18 @@ BEGIN_TEST(path0, "get the dirname of a path")
#undef DIRNAME_TEST
git_buf_free(&dir);
END_TEST
BEGIN_TEST(path1, "get the base name of a path")
char base[64], *base2;
git_buf base = GIT_BUF_INIT;
char *base2;
#define BASENAME_TEST(A, B) { \
must_be_true(git_path_basename_r(base, sizeof(base), A) >= 0); \
must_be_true(strcmp(base, B) == 0); \
must_be_true(git_path_basename_r(&base, A) >= 0); \
must_be_true(strcmp(B, base.ptr) == 0); \
must_be_true((base2 = git_path_basename(A)) != NULL); \
must_be_true(strcmp(base2, B) == 0); \
must_be_true(strcmp(base2, B) == 0); \
git__free(base2); \
}
......@@ -156,6 +159,7 @@ BEGIN_TEST(path1, "get the base name of a path")
#undef BASENAME_TEST
git_buf_free(&base);
END_TEST
BEGIN_TEST(path2, "get the latest component in a path")
......@@ -184,9 +188,13 @@ END_TEST
static int ensure_joinpath(const char *path_a, const char *path_b, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_path_join(joined_path, path_a, path_b);
return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR;
int error = GIT_SUCCESS;
git_buf joined_path = GIT_BUF_INIT;
if (!(error = git_buf_joinpath(&joined_path, path_a, path_b)))
error = strcmp(joined_path.ptr, expected_path) == 0 ?
GIT_SUCCESS : GIT_ERROR;
git_buf_free(&joined_path);
return error;
}
BEGIN_TEST(path5, "properly join path components")
......@@ -206,9 +214,14 @@ END_TEST
static int ensure_joinpath_n(const char *path_a, const char *path_b, const char *path_c, const char *path_d, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_path_join_n(joined_path, 4, path_a, path_b, path_c, path_d);
return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR;
int error = GIT_SUCCESS;
git_buf joined_path = GIT_BUF_INIT;
if (!(error = git_buf_join_n(&joined_path, '/', 4,
path_a, path_b, path_c, path_d)))
error = strcmp(joined_path.ptr, expected_path) == 0 ?
GIT_SUCCESS : GIT_ERROR;
git_buf_free(&joined_path);
return error;
}
BEGIN_TEST(path6, "properly join path components for more than one path")
......@@ -228,10 +241,10 @@ typedef struct name_data {
typedef struct walk_data {
char *sub; /* sub-directory name */
name_data *names; /* name state data */
git_buf path; /* buffer to store path */
} walk_data;
static char path_buffer[GIT_PATH_MAX];
static char *top_dir = "dir-walk";
static walk_data *state_loc;
......@@ -260,7 +273,9 @@ static int setup(walk_data *d)
if (p_mkdir(d->sub, 0777) < 0)
return error("can't mkdir(\"%s\")", d->sub);
strcpy(path_buffer, d->sub);
if (git_buf_sets(&d->path, d->sub) < 0)
return error("can't allocate space for \"%s\"", d->sub);
state_loc = d;
for (n = d->names; n->name; n++) {
......@@ -278,6 +293,8 @@ static int knockdown(walk_data *d)
{
name_data *n;
git_buf_free(&d->path);
for (n = d->names; n->name; n++) {
if (p_unlink(n->name) < 0)
return error("can't unlink(\"%s\")", n->name);
......@@ -308,7 +325,7 @@ static int check_counts(walk_data *d)
return ret;
}
static int one_entry(void *state, char *path)
static int one_entry(void *state, git_buf *path)
{
walk_data *d = (walk_data *) state;
name_data *n;
......@@ -316,11 +333,11 @@ static int one_entry(void *state, char *path)
if (state != state_loc)
return GIT_ERROR;
if (path != path_buffer)
if (path != &d->path)
return GIT_ERROR;
for (n = d->names; n->name; n++) {
if (!strcmp(n->name, path)) {
if (!strcmp(n->name, path->ptr)) {
n->count++;
return 0;
}
......@@ -338,15 +355,14 @@ static name_data dot_names[] = {
};
static walk_data dot = {
".",
dot_names
dot_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent0, "make sure that the '.' folder is not traversed")
must_pass(setup(&dot));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&dot.path,
one_entry,
&dot));
......@@ -363,15 +379,15 @@ static name_data sub_names[] = {
};
static walk_data sub = {
"sub",
sub_names
sub_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent1, "traverse a subfolder")
must_pass(setup(&sub));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&sub.path,
one_entry,
&sub));
......@@ -382,15 +398,15 @@ END_TEST
static walk_data sub_slash = {
"sub/",
sub_names
sub_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent2, "traverse a slash-terminated subfolder")
must_pass(setup(&sub_slash));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&sub_slash.path,
one_entry,
&sub_slash));
......@@ -404,10 +420,11 @@ static name_data empty_names[] = {
};
static walk_data empty = {
"empty",
empty_names
empty_names,
GIT_BUF_INIT
};
static int dont_call_me(void *GIT_UNUSED(state), char *GIT_UNUSED(path))
static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
{
GIT_UNUSED_ARG(state)
GIT_UNUSED_ARG(path)
......@@ -418,16 +435,14 @@ BEGIN_TEST(dirent3, "make sure that empty folders are not traversed")
must_pass(setup(&empty));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&empty.path,
one_entry,
&empty));
must_pass(check_counts(&empty));
/* make sure callback not called */
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&empty.path,
dont_call_me,
&empty));
......@@ -444,15 +459,15 @@ static name_data odd_names[] = {
};
static walk_data odd = {
"odd",
odd_names
odd_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traversed")
must_pass(setup(&odd));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&odd.path,
one_entry,
&odd));
......@@ -508,32 +523,24 @@ static char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
static int setup_empty_tmp_dir(void)
{
char path[GIT_PATH_MAX];
if (p_mkdir(empty_tmp_dir, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_one");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_two");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_two/three");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/two");
if (p_mkdir(path, 0777))
return -1;
return 0;
git_buf path = GIT_BUF_INIT;
int error =
p_mkdir(empty_tmp_dir, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/two") ||
p_mkdir(path.ptr, 0777);
git_buf_free(&path);
return error ? -1 : 0;
}
BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively")
......@@ -542,17 +549,18 @@ BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively")
END_TEST
BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively")
char file[GIT_PATH_MAX];
git_buf file = GIT_BUF_INIT;
int fd;
must_pass(setup_empty_tmp_dir());
git_path_join(file, empty_tmp_dir, "/two/file.txt");
fd = p_creat(file, 0777);
must_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
fd = p_creat(file.ptr, 0777);
must_pass(fd);
must_pass(p_close(fd));
must_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
must_pass(p_unlink(file));
must_pass(p_unlink(file.ptr));
must_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
git_buf_free(&file);
END_TEST
BEGIN_TEST(strtol0, "parsing out 32 integers from a string")
......
......@@ -87,31 +87,37 @@ static int write_file(const char *path, const char *content)
}
//no check is performed on ceiling_dirs length, so be sure it's long enough
static int append_ceiling_dir(char *ceiling_dirs, const char *path)
static int append_ceiling_dir(git_buf *ceiling_dirs, const char *path)
{
int len = strlen(ceiling_dirs);
git_buf pretty_path = GIT_BUF_INIT;
int error;
char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' };
error = git_path_prettify_dir(ceiling_dirs + len + (len ? 1 : 0), path, NULL);
error = git_path_prettify_dir(&pretty_path, path, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to append ceiling directory.");
if (len)
ceiling_dirs[len] = GIT_PATH_LIST_SEPARATOR;
if (ceiling_dirs->size > 0)
git_buf_puts(ceiling_dirs, ceiling_separator);
git_buf_puts(ceiling_dirs, pretty_path.ptr);
return GIT_SUCCESS;
git_buf_free(&pretty_path);
return git_buf_lasterror(ceiling_dirs);
}
BEGIN_TEST(discover0, "test discover")
git_repository *repo;
char ceiling_dirs[GIT_PATH_MAX * 2] = "";
git_buf ceiling_dirs_buf = GIT_BUF_INIT;
const char *ceiling_dirs;
char repository_path[GIT_PATH_MAX];
char sub_repository_path[GIT_PATH_MAX];
char found_path[GIT_PATH_MAX];
const mode_t mode = 0777;
git_futils_mkdir_r(DISCOVER_FOLDER, mode);
must_pass(append_ceiling_dir(ceiling_dirs, TEMP_REPO_FOLDER));
git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode);
must_pass(append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER));
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
......@@ -120,15 +126,15 @@ BEGIN_TEST(discover0, "test discover")
git_repository_free(repo);
must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"));
......@@ -137,20 +143,22 @@ BEGIN_TEST(discover0, "test discover")
must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"));
must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode));
must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
must_pass(append_ceiling_dir(ceiling_dirs, SUB_REPOSITORY_FOLDER));
must_pass(append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER));
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
//this must pass as ceiling_directories cannot predent the current
//working directory to be checked
must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
......@@ -166,6 +174,7 @@ BEGIN_TEST(discover0, "test discover")
must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
git_repository_free(repo);
git_buf_free(&ceiling_dirs_buf);
END_TEST
BEGIN_SUITE(repository)
......
......@@ -392,15 +392,15 @@ END_TEST
BEGIN_TEST(singlestatus3, "test retrieving status for a new file in an empty repository")
git_repository *repo;
unsigned int status_flags;
char file_path[GIT_PATH_MAX];
git_buf file_path = GIT_BUF_INIT;
char filename[] = "new_file";
int fd;
must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
git_path_join(file_path, TEMP_REPO_FOLDER, filename);
fd = p_creat(file_path, 0666);
must_pass(git_buf_joinpath(&file_path, TEMP_REPO_FOLDER, filename));
fd = p_creat(file_path.ptr, 0666);
must_pass(fd);
must_pass(p_write(fd, "new_file\n", 9));
must_pass(p_close(fd));
......@@ -411,6 +411,7 @@ BEGIN_TEST(singlestatus3, "test retrieving status for a new file in an empty rep
must_be_true(status_flags == GIT_STATUS_WT_NEW);
git_repository_free(repo);
git_buf_free(&file_path);
git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
END_TEST
......
......@@ -225,42 +225,49 @@ int cmp_files(const char *a, const char *b)
}
typedef struct {
size_t src_len, dst_len;
char *dst;
git_buf src;
size_t src_baselen;
git_buf dst;
size_t dst_baselen;
} copydir_data;
static int copy_filesystem_element_recurs(void *_data, char *source)
static int copy_filesystem_element_recurs(void *_data, git_buf *source)
{
copydir_data *data = (copydir_data *)_data;
data->dst[data->dst_len] = 0;
git_path_join(data->dst, data->dst, source + data->src_len);
git_buf_truncate(&data->dst, data->dst_baselen);
git_buf_joinpath(&data->dst, data->dst.ptr, source->ptr + data->src_baselen);
if (git_futils_isdir(source) == GIT_SUCCESS)
return git_futils_direach(source, GIT_PATH_MAX, copy_filesystem_element_recurs, _data);
return copy_file(source, data->dst);
if (git_futils_isdir(source->ptr) == GIT_SUCCESS)
return git_futils_direach(source, copy_filesystem_element_recurs, _data);
else
return copy_file(source->ptr, data->dst.ptr);
}
int copydir_recurs(const char *source_directory_path, const char *destination_directory_path)
int copydir_recurs(
const char *source_directory_path,
const char *destination_directory_path)
{
char source_buffer[GIT_PATH_MAX];
char dest_buffer[GIT_PATH_MAX];
copydir_data data;
int error;
copydir_data data = { GIT_BUF_INIT, 0, GIT_BUF_INIT, 0 };
/* Source has to exist, Destination hast to _not_ exist */
if (git_futils_isdir(source_directory_path) != GIT_SUCCESS ||
git_futils_isdir(destination_directory_path) == GIT_SUCCESS)
return GIT_EINVALIDPATH;
git_path_join(source_buffer, source_directory_path, "");
data.src_len = strlen(source_buffer);
git_buf_joinpath(&data.src, source_directory_path, "");
data.src_baselen = data.src.size;
git_buf_joinpath(&data.dst, destination_directory_path, "");
data.dst_baselen = data.dst.size;
error = copy_filesystem_element_recurs(&data, &data.src);
git_path_join(dest_buffer, destination_directory_path, "");
data.dst = dest_buffer;
data.dst_len = strlen(dest_buffer);
git_buf_free(&data.src);
git_buf_free(&data.dst);
return copy_filesystem_element_recurs(&data, source_buffer);
return error;
}
int open_temp_repo(git_repository **repo, const char *path)
......@@ -281,30 +288,51 @@ void close_temp_repo(git_repository *repo)
}
}
static int remove_placeholders_recurs(void *filename, char *path)
typedef struct {
const char *filename;
size_t filename_len;
} remove_data;
static int remove_placeholders_recurs(void *_data, git_buf *path)
{
char passed_filename[GIT_PATH_MAX];
char *data = (char *)filename;
remove_data *data = (remove_data *)_data;
size_t pathlen;
if (!git_futils_isdir(path))
return git_futils_direach(path, GIT_PATH_MAX, remove_placeholders_recurs, data);
if (!git_futils_isdir(path->ptr))
return git_futils_direach(path, remove_placeholders_recurs, data);
if (git_path_basename_r(passed_filename, sizeof(passed_filename), path) < GIT_SUCCESS)
return GIT_EINVALIDPATH;
pathlen = path->size;
if (!strcmp(data, passed_filename))
return p_unlink(path);
if (pathlen < data->filename_len)
return GIT_SUCCESS;
/* if path ends in '/'+filename (or equals filename) */
if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
(pathlen == data->filename_len ||
path->ptr[pathlen - data->filename_len - 1] == '/'))
return p_unlink(path->ptr);
return GIT_SUCCESS;
}
int remove_placeholders(char *directory_path, char *filename)
int remove_placeholders(const char *directory_path, const char *filename)
{
char buffer[GIT_PATH_MAX];
int error;
remove_data data;
git_buf buffer = GIT_BUF_INIT;
if (git_futils_isdir(directory_path))
return GIT_EINVALIDPATH;
strcpy(buffer, directory_path);
return remove_placeholders_recurs(filename, buffer);
if ((error = git_buf_sets(&buffer, directory_path)) < GIT_SUCCESS)
return error;
data.filename = filename;
data.filename_len = strlen(filename);
error = remove_placeholders_recurs(&data, &buffer);
git_buf_free(&buffer);
return error;
}
......@@ -74,7 +74,7 @@ extern int cmp_files(const char *a, const char *b);
extern int copy_file(const char *source, const char *dest);
extern int rmdir_recurs(const char *directory_path);
extern int copydir_recurs(const char *source_directory_path, const char *destination_directory_path);
extern int remove_placeholders(char *directory_path, char *filename);
extern int remove_placeholders(const char *directory_path, const char *filename);
extern int open_temp_repo(git_repository **repo, const char *path);
extern void close_temp_repo(git_repository *repo);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment