summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-09-25 14:47:32 -0700
committerBen Straub <bs@github.com>2013-09-25 14:47:32 -0700
commit41dd999d12946ca2085ad315e9c5e8e57cc4e4f6 (patch)
tree0f9b3f7beefa40809a913e1e215b75dfe5a87115 /src
parentf7db1b6f26837b44a5cb8956c23bbbdff28ba4f7 (diff)
parentac316e743878908df762cc0ea07a71cbee5c5802 (diff)
downloadlibgit2-41dd999d12946ca2085ad315e9c5e8e57cc4e4f6.tar.gz
Merge branch 'development' into blame
Diffstat (limited to 'src')
-rw-r--r--src/array.h2
-rw-r--r--src/attr.c8
-rw-r--r--src/attr_file.c2
-rw-r--r--src/blob.c76
-rw-r--r--src/branch.c2
-rw-r--r--src/buf_text.c21
-rw-r--r--src/buf_text.h8
-rw-r--r--src/buffer.c27
-rw-r--r--src/buffer.h40
-rw-r--r--src/checkout.c65
-rw-r--r--src/clone.c5
-rw-r--r--src/config_file.c326
-rw-r--r--src/crlf.c230
-rw-r--r--src/diff.c15
-rw-r--r--src/diff_file.c43
-rw-r--r--src/diff_print.c9
-rw-r--r--src/fileops.c52
-rw-r--r--src/fileops.h16
-rw-r--r--src/filter.c666
-rw-r--r--src/filter.h71
-rw-r--r--src/global.c38
-rw-r--r--src/global.h4
-rw-r--r--src/hash.h2
-rw-r--r--src/hash/hash_generic.h1
-rw-r--r--src/hash/hash_openssl.h1
-rw-r--r--src/hash/hash_win32.c20
-rw-r--r--src/ident.c125
-rw-r--r--src/index.c8
-rw-r--r--src/indexer.c90
-rw-r--r--src/merge.c2
-rw-r--r--src/netops.c1
-rw-r--r--src/odb.c23
-rw-r--r--src/odb.h3
-rw-r--r--src/oid.c2
-rw-r--r--src/path.c12
-rw-r--r--src/posix.h2
-rw-r--r--src/refdb.c2
-rw-r--r--src/refdb_fs.c2
-rw-r--r--src/remote.c50
-rw-r--r--src/repository.c52
-rw-r--r--src/revwalk.c43
-rw-r--r--src/stash.c2
-rw-r--r--src/status.c2
-rw-r--r--src/submodule.c2
-rw-r--r--src/transports/http.c169
-rw-r--r--src/transports/ssh.c36
-rw-r--r--src/transports/winhttp.c321
-rw-r--r--src/util.c16
-rw-r--r--src/win32/findfile.c24
-rw-r--r--src/win32/findfile.h2
-rw-r--r--src/win32/pthread.c19
-rw-r--r--src/win32/pthread.h1
52 files changed, 1865 insertions, 896 deletions
diff --git a/src/array.h b/src/array.h
index b82079bd8..d7272d78c 100644
--- a/src/array.h
+++ b/src/array.h
@@ -59,7 +59,7 @@ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
#define git_array_alloc(a) \
((a).size >= (a).asize) ? \
git_array_grow(&(a), sizeof(*(a).ptr)) : \
- (a).ptr ? &(a).ptr[(a).size++] : NULL
+ ((a).ptr ? &(a).ptr[(a).size++] : NULL)
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
diff --git a/src/attr.c b/src/attr.c
index 6cdff29f9..7946db4d6 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -26,7 +26,6 @@ git_attr_t git_attr_value(const char *attr)
return GIT_ATTR_VALUE_T;
}
-
static int collect_attr_files(
git_repository *repo,
uint32_t flags,
@@ -103,8 +102,6 @@ int git_attr_get_many(
attr_get_many_info *info = NULL;
size_t num_found = 0;
- memset((void *)values, 0, sizeof(const char *) * num_attr);
-
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
@@ -141,6 +138,11 @@ int git_attr_get_many(
}
}
+ for (k = 0; k < num_attr; k++) {
+ if (!info[k].found)
+ values[k] = NULL;
+ }
+
cleanup:
git_vector_free(&files);
git_attr_path__free(&path);
diff --git a/src/attr_file.c b/src/attr_file.c
index 92702df98..4eb732436 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -39,7 +39,7 @@ int git_attr_file__new(
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
GITERR_CHECK_ALLOC(attrs->key);
- attrs->key[0] = '0' + from;
+ attrs->key[0] = '0' + (char)from;
attrs->key[1] = '#';
memcpy(&attrs->key[2], path, len);
attrs->key[len + 2] = '\0';
diff --git a/src/blob.c b/src/blob.c
index 6a289f43b..e18db4dfc 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -108,29 +108,21 @@ static int write_file_filtered(
git_off_t *size,
git_odb *odb,
const char *full_path,
- git_vector *filters)
+ git_filter_list *fl)
{
int error;
- git_buf source = GIT_BUF_INIT;
- git_buf dest = GIT_BUF_INIT;
+ git_buf tgt = GIT_BUF_INIT;
- if ((error = git_futils_readbuffer(&source, full_path)) < 0)
- return error;
-
- error = git_filters_apply(&dest, &source, filters);
-
- /* Free the source as soon as possible. This can be big in memory,
- * and we don't want to ODB write to choke */
- git_buf_free(&source);
+ error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
/* Write the file to disk if it was properly filtered */
if (!error) {
- *size = dest.size;
+ *size = tgt.size;
- error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+ error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
}
- git_buf_free(&dest);
+ git_buf_free(&tgt);
return error;
}
@@ -198,29 +190,25 @@ int git_blob__create_from_paths(
if (S_ISLNK(mode)) {
error = write_symlink(oid, odb, content_path, (size_t)size);
} else {
- git_vector write_filters = GIT_VECTOR_INIT;
- int filter_count = 0;
+ git_filter_list *fl = NULL;
- if (try_load_filters) {
+ if (try_load_filters)
/* Load the filters for writing this file to the ODB */
- filter_count = git_filters_load(
- &write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
- }
+ error = git_filter_list_load(
+ &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
- if (filter_count < 0) {
- /* Negative value means there was a critical error */
- error = filter_count;
- } else if (filter_count == 0) {
+ if (error < 0)
+ /* well, that didn't work */;
+ else if (fl == NULL)
/* No filters need to be applied to the document: we can stream
* directly from disk */
error = write_file_stream(oid, odb, content_path, size);
- } else {
+ else {
/* We need to apply one or more filters */
- error = write_file_filtered(
- oid, &size, odb, content_path, &write_filters);
- }
+ error = write_file_filtered(oid, &size, odb, content_path, fl);
- git_filters_free(&write_filters);
+ git_filter_list_free(fl);
+ }
/*
* TODO: eventually support streaming filtered files, for files
@@ -333,8 +321,34 @@ int git_blob_is_binary(git_blob *blob)
assert(blob);
- content.ptr = blob->odb_object->buffer;
- content.size = min(blob->odb_object->cached.size, 4000);
+ content.ptr = blob->odb_object->buffer;
+ content.size = min(blob->odb_object->cached.size, 4000);
+ content.asize = 0;
return git_buf_text_is_binary(&content);
}
+
+int git_blob_filtered_content(
+ git_buf *out,
+ git_blob *blob,
+ const char *path,
+ int check_for_binary_data)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+
+ assert(blob && path && out);
+
+ if (check_for_binary_data && git_blob_is_binary(blob))
+ return 0;
+
+ if (!(error = git_filter_list_load(
+ &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
+
+ error = git_filter_list_apply_to_blob(out, fl, blob);
+
+ git_filter_list_free(fl);
+ }
+
+ return error;
+}
diff --git a/src/branch.c b/src/branch.c
index 7064fa7fc..3a745c127 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -585,7 +585,7 @@ int git_branch_is_head(
error = git_repository_head(&head, git_reference_owner(branch));
- if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+ if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
return false;
if (error < 0)
diff --git a/src/buf_text.c b/src/buf_text.c
index ecf592b51..631feb3f8 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -70,10 +70,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
assert(tgt != src);
if (!next)
- return GIT_ENOTFOUND;
+ return git_buf_set(tgt, src->ptr, src->size);
/* reduce reallocs while in the loop */
- if (git_buf_grow(tgt, src->size) < 0)
+ if (git_buf_grow(tgt, src->size + 1) < 0)
return -1;
out = tgt->ptr;
tgt->size = 0;
@@ -81,20 +81,25 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
/* Find the next \r and copy whole chunk up to there to tgt */
for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
if (next > scan) {
- size_t copylen = next - scan;
+ size_t copylen = (size_t)(next - scan);
memcpy(out, scan, copylen);
out += copylen;
}
/* Do not drop \r unless it is followed by \n */
- if (next[1] != '\n')
+ if (next + 1 == scan_end || next[1] != '\n')
*out++ = '\r';
}
/* Copy remaining input into dest */
- memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
- out += (scan_end - scan);
- tgt->size = out - tgt->ptr;
+ if (scan < scan_end) {
+ size_t remaining = (size_t)(scan_end - scan);
+ memcpy(out, scan, remaining);
+ out += remaining;
+ }
+
+ tgt->size = (size_t)(out - tgt->ptr);
+ tgt->ptr[tgt->size] = '\0';
return 0;
}
@@ -109,7 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
assert(tgt != src);
if (!next)
- return GIT_ENOTFOUND;
+ return git_buf_set(tgt, src->ptr, src->size);
/* attempt to reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
diff --git a/src/buf_text.h b/src/buf_text.h
index 58e4e26a7..3ac9d1443 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,16 +56,16 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf);
/**
- * Replace all \r\n with \n (or do nothing if no \r\n are found)
+ * Replace all \r\n with \n. Does not modify \r without trailing \n.
*
- * @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
+ * @return 0 on success, -1 on memory error
*/
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
/**
- * Replace all \n with \r\n (or do nothing if no \n are found)
+ * Replace all \n with \r\n. Does not modify existing \r\n.
*
- * @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
+ * @return 0 on success, -1 on memory error
*/
extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
diff --git a/src/buffer.c b/src/buffer.c
index b5b2fd678..20682322e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -6,6 +6,7 @@
*/
#include "buffer.h"
#include "posix.h"
+#include "git2/buffer.h"
#include <stdarg.h>
#include <ctype.h>
@@ -31,7 +32,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
git_buf_grow(buf, initial_size);
}
-int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
+int git_buf_try_grow(
+ git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external)
{
char *new_ptr;
size_t new_size;
@@ -39,6 +41,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
if (buf->ptr == git_buf__oom)
return -1;
+ if (!target_size)
+ target_size = buf->size;
+
if (target_size <= buf->asize)
return 0;
@@ -66,6 +71,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return -1;
}
+ if (preserve_external && !buf->asize && buf->ptr != NULL && buf->size > 0)
+ memcpy(new_ptr, buf->ptr, min(buf->size, new_size));
+
buf->asize = new_size;
buf->ptr = new_ptr;
@@ -77,11 +85,16 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return 0;
}
+int git_buf_grow(git_buf *buffer, size_t target_size)
+{
+ return git_buf_try_grow(buffer, target_size, true, true);
+}
+
void git_buf_free(git_buf *buf)
{
if (!buf) return;
- if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
+ if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
@@ -90,11 +103,15 @@ void git_buf_free(git_buf *buf)
void git_buf_clear(git_buf *buf)
{
buf->size = 0;
+
+ if (!buf->ptr)
+ buf->ptr = git_buf__initbuf;
+
if (buf->asize > 0)
buf->ptr[0] = '\0';
}
-int git_buf_set(git_buf *buf, const char *data, size_t len)
+int git_buf_set(git_buf *buf, const void *data, size_t len)
{
if (len == 0 || data == NULL) {
git_buf_clear(buf);
@@ -137,7 +154,7 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
-static const char b64str[64] =
+static const char b64str[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
@@ -194,6 +211,8 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
format, args
);
+ va_end(args);
+
if (len < 0) {
git__free(buf->ptr);
buf->ptr = git_buf__oom;
diff --git a/src/buffer.h b/src/buffer.h
index f3e1d506f..4ca9d4d94 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -9,18 +9,26 @@
#include "common.h"
#include "git2/strarray.h"
+#include "git2/buffer.h"
#include <stdarg.h>
-typedef struct {
- char *ptr;
- size_t asize, size;
-} git_buf;
+/* typedef struct {
+ * char *ptr;
+ * size_t asize, size;
+ * } git_buf;
+ */
extern char git_buf__initbuf[];
extern char git_buf__oom[];
+/* Use to initialize buffer structure when git_buf is on stack */
#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
+GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
+{
+ return (buf->ptr != NULL && buf->asize > 0);
+}
+
/**
* Initialize a git_buf structure.
*
@@ -32,27 +40,16 @@ extern void git_buf_init(git_buf *buf, size_t initial_size);
/**
* Attempt to grow the buffer to hold at least `target_size` bytes.
*
- * If the allocation fails, this will return an error. If mark_oom is true,
+ * If the allocation fails, this will return an error. If `mark_oom` is true,
* this will mark the buffer as invalid for future operations; if false,
* existing buffer content will be preserved, but calling code must handle
- * that buffer was not expanded.
+ * that buffer was not expanded. If `preserve_external` is true, then any
+ * existing data pointed to be `ptr` even if `asize` is zero will be copied
+ * into the newly allocated buffer.
*/
-extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom);
-
-/**
- * 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, invaliding contents.
- *
- * @return 0 on success or -1 on failure
- */
-GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
-{
- return git_buf_try_grow(buf, target_size, true);
-}
+extern int git_buf_try_grow(
+ git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external);
-extern void git_buf_free(git_buf *buf);
extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
extern char *git_buf_detach(git_buf *buf);
extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
@@ -81,7 +78,6 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
* return code of these functions and call them in a series then just call
* git_buf_oom 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);
diff --git a/src/checkout.c b/src/checkout.c
index aae354ca6..0e9d11bff 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -678,7 +678,7 @@ fail:
static int buffer_to_file(
struct stat *st,
- git_buf *buffer,
+ git_buf *buf,
const char *path,
mode_t dir_mode,
int file_open_flags,
@@ -690,7 +690,7 @@ static int buffer_to_file(
return error;
if ((error = git_futils_writebuffer(
- buffer, path, file_open_flags, file_mode)) < 0)
+ buf, path, file_open_flags, file_mode)) < 0)
return error;
if (st != NULL && (error = p_stat(path, st)) < 0)
@@ -710,57 +710,28 @@ static int blob_content_to_file(
mode_t entry_filemode,
git_checkout_opts *opts)
{
- int error = -1, nb_filters = 0;
- mode_t file_mode = opts->file_mode;
- bool dont_free_filtered;
- git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
- git_vector filters = GIT_VECTOR_INIT;
-
- /* Create a fake git_buf from the blob raw data... */
- filtered.ptr = (void *)git_blob_rawcontent(blob);
- filtered.size = (size_t)git_blob_rawsize(blob);
- /* ... and make sure it doesn't get unexpectedly freed */
- dont_free_filtered = true;
-
- if (!opts->disable_filters &&
- !git_buf_text_is_binary(&filtered) &&
- (nb_filters = git_filters_load(
- &filters,
- git_object_owner((git_object *)blob),
- path,
- GIT_FILTER_TO_WORKTREE)) > 0)
- {
- /* reset 'filtered' so it can be a filter target */
- git_buf_init(&filtered, 0);
- dont_free_filtered = false;
- }
-
- if (nb_filters < 0)
- return nb_filters;
+ int error = 0;
+ mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
+ git_buf out = GIT_BUF_INIT;
+ git_filter_list *fl = NULL;
- if (nb_filters > 0) {
- if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
- goto cleanup;
+ if (!opts->disable_filters)
+ error = git_filter_list_load(
+ &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE);
- if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
- goto cleanup;
- }
+ if (!error)
+ error = git_filter_list_apply_to_blob(&out, fl, blob);
- /* Allow overriding of file mode */
- if (!file_mode)
- file_mode = entry_filemode;
+ git_filter_list_free(fl);
- error = buffer_to_file(
- st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
+ if (!error) {
+ error = buffer_to_file(
+ st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
- if (!error)
st->st_mode = entry_filemode;
-cleanup:
- git_filters_free(&filters);
- git_buf_free(&unfiltered);
- if (!dont_free_filtered)
- git_buf_free(&filtered);
+ git_buf_free(&out);
+ }
return error;
}
@@ -1232,7 +1203,7 @@ static int checkout_data_init(
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
- if (error == GIT_EORPHANEDHEAD) {
+ if (error == GIT_EUNBORNBRANCH) {
error = 0;
giterr_clear();
}
diff --git a/src/clone.c b/src/clone.c
index 5b8fc5e45..ff251be1b 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -415,7 +415,7 @@ static bool should_checkout(
if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
return false;
- return !git_repository_head_orphan(repo);
+ return !git_repository_head_unborn(repo);
}
static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions)
@@ -427,8 +427,7 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s
/* Provide defaults for null pointers */
if (!dst->remote_name) dst->remote_name = "origin";
- if (!dst->init_options)
- {
+ if (!dst->init_options) {
dst->init_options = initOptions;
initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
if (dst->bare)
diff --git a/src/config_file.c b/src/config_file.c
index efc9df965..1a845d8ba 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -15,6 +15,7 @@
#include "git2/sys/config.h"
#include "git2/types.h"
#include "strmap.h"
+#include "array.h"
#include <ctype.h>
#include <sys/types.h>
@@ -25,6 +26,7 @@ GIT__USE_STRMAP;
typedef struct cvar_t {
struct cvar_t *next;
git_config_entry *entry;
+ int included; /* whether this is part of [include] */
} cvar_t;
typedef struct git_config_file_iter {
@@ -33,6 +35,8 @@ typedef struct git_config_file_iter {
cvar_t* next_var;
} git_config_file_iter;
+/* Max depth for [include] directives */
+#define MAX_INCLUDE_DEPTH 10
#define CVAR_LIST_HEAD(list) ((list)->head)
@@ -72,34 +76,37 @@ typedef struct git_config_file_iter {
(iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
(iter) = (tmp))
+struct reader {
+ time_t file_mtime;
+ size_t file_size;
+ char *file_path;
+ git_buf buffer;
+ char *read_ptr;
+ int line_number;
+ int eof;
+};
+
typedef struct {
git_config_backend parent;
git_strmap *values;
- struct {
- git_buf buffer;
- char *read_ptr;
- int line_number;
- int eof;
- } reader;
+ git_array_t(struct reader) readers;
char *file_path;
- time_t file_mtime;
- size_t file_size;
git_config_level_t level;
} diskfile_backend;
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level);
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
+static int parse_variable(struct reader *reader, char **var_name, char **var_value);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
-static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+static void set_parse_error(struct reader *reader, int col, const char *error_str)
{
giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
- error_str, backend->file_path, backend->reader.line_number, col);
+ error_str, reader->file_path, reader->line_number, col);
}
static void cvar_free(cvar_t *var)
@@ -125,7 +132,7 @@ int git_config_file_normalize_section(char *start, char *end)
if (end && scan >= end)
break;
if (isalnum(*scan))
- *scan = tolower(*scan);
+ *scan = (char)tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
}
@@ -156,6 +163,7 @@ static void free_vars(git_strmap *values)
static int config_open(git_config_backend *cfg, git_config_level_t level)
{
int res;
+ struct reader *reader;
diskfile_backend *b = (diskfile_backend *)cfg;
b->level = level;
@@ -163,32 +171,52 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
- git_buf_init(&b->reader.buffer, 0);
+ git_array_init(b->readers);
+ reader = git_array_alloc(b->readers);
+ memset(reader, 0, sizeof(struct reader));
+
+ reader->file_path = git__strdup(b->file_path);
+ GITERR_CHECK_ALLOC(reader->file_path);
+
+ git_buf_init(&reader->buffer, 0);
res = git_futils_readbuffer_updated(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
+ &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
return 0;
- if (res < 0 || (res = config_parse(b, level)) < 0) {
+ if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
free_vars(b->values);
b->values = NULL;
}
- git_buf_free(&b->reader.buffer);
+ reader = git_array_get(b->readers, 0);
+ git_buf_free(&reader->buffer);
return res;
}
static int config_refresh(git_config_backend *cfg)
{
- int res, updated = 0;
+ int res = 0, updated = 0, any_updated = 0;
diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *old_values;
+ struct reader *reader;
+ uint32_t i;
- res = git_futils_readbuffer_updated(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
- if (res < 0 || !updated)
+ for (i = 0; i < git_array_size(b->readers); i++) {
+ reader = git_array_get(b->readers, i);
+ res = git_futils_readbuffer_updated(
+ &reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated);
+
+ if (res < 0)
+ return (res == GIT_ENOTFOUND) ? 0 : res;
+
+ if (updated)
+ any_updated = 1;
+ }
+
+ if (!any_updated)
return (res == GIT_ENOTFOUND) ? 0 : res;
/* need to reload - store old values and prep for reload */
@@ -196,24 +224,31 @@ static int config_refresh(git_config_backend *cfg)
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
- if ((res = config_parse(b, b->level)) < 0) {
+ if ((res = config_parse(b, reader, b->level, 0)) < 0) {
free_vars(b->values);
b->values = old_values;
} else {
free_vars(old_values);
}
- git_buf_free(&b->reader.buffer);
+ git_buf_free(&reader->buffer);
return res;
}
static void backend_free(git_config_backend *_backend)
{
diskfile_backend *backend = (diskfile_backend *)_backend;
+ uint32_t i;
if (backend == NULL)
return;
+ for (i = 0; i < git_array_size(backend->readers); i++) {
+ struct reader *r = git_array_get(backend->readers, i);
+ git__free(r->file_path);
+ }
+ git_array_clear(backend->readers);
+
git__free(backend->file_path);
free_vars(backend->values);
git__free(backend);
@@ -258,6 +293,8 @@ static int config_iterator_new(
diskfile_backend *b = (diskfile_backend *)backend;
git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
+ GIT_UNUSED(b);
+
GITERR_CHECK_ALLOC(it);
it->parent.backend = backend;
@@ -361,6 +398,7 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
char *key;
khiter_t pos;
int error;
+ cvar_t *var;
if ((error = git_config__normalize_name(name, &key)) < 0)
return error;
@@ -372,7 +410,11 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
if (!git_strmap_valid_index(b->values, pos))
return GIT_ENOTFOUND;
- *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry;
+ var = git_strmap_value_at(b->values, pos);
+ while (var->next)
+ var = var->next;
+
+ *out = var->entry;
return 0;
}
@@ -515,26 +557,26 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
return 0;
}
-static int cfg_getchar_raw(diskfile_backend *cfg)
+static int reader_getchar_raw(struct reader *reader)
{
int c;
- c = *cfg->reader.read_ptr++;
+ c = *reader->read_ptr++;
/*
Win 32 line breaks: if we find a \r\n sequence,
return only the \n as a newline
*/
- if (c == '\r' && *cfg->reader.read_ptr == '\n') {
- cfg->reader.read_ptr++;
+ if (c == '\r' && *reader->read_ptr == '\n') {
+ reader->read_ptr++;
c = '\n';
}
if (c == '\n')
- cfg->reader.line_number++;
+ reader->line_number++;
if (c == 0) {
- cfg->reader.eof = 1;
+ reader->eof = 1;
c = '\n';
}
@@ -544,21 +586,23 @@ static int cfg_getchar_raw(diskfile_backend *cfg)
#define SKIP_WHITESPACE (1 << 1)
#define SKIP_COMMENTS (1 << 2)
-static int cfg_getchar(diskfile_backend *cfg_file, int flags)
+static int reader_getchar(struct reader *reader, int flags)
{
const int skip_whitespace = (flags & SKIP_WHITESPACE);
const int skip_comments = (flags & SKIP_COMMENTS);
int c;
- assert(cfg_file->reader.read_ptr);
+ assert(reader->read_ptr);
- do c = cfg_getchar_raw(cfg_file);
- while (skip_whitespace && git__isspace(c) &&
- !cfg_file->reader.eof);
+ do {
+ c = reader_getchar_raw(reader);
+ } while (skip_whitespace && git__isspace(c) &&
+ !reader->eof);
if (skip_comments && (c == '#' || c == ';')) {
- do c = cfg_getchar_raw(cfg_file);
- while (c != '\n');
+ do {
+ c = reader_getchar_raw(reader);
+ } while (c != '\n');
}
return c;
@@ -567,23 +611,23 @@ static int cfg_getchar(diskfile_backend *cfg_file, int flags)
/*
* Read the next char, but don't move the reading pointer.
*/
-static int cfg_peek(diskfile_backend *cfg, int flags)
+static int reader_peek(struct reader *reader, int flags)
{
void *old_read_ptr;
int old_lineno, old_eof;
int ret;
- assert(cfg->reader.read_ptr);
+ assert(reader->read_ptr);
- old_read_ptr = cfg->reader.read_ptr;
- old_lineno = cfg->reader.line_number;
- old_eof = cfg->reader.eof;
+ old_read_ptr = reader->read_ptr;
+ old_lineno = reader->line_number;
+ old_eof = reader->eof;
- ret = cfg_getchar(cfg, flags);
+ ret = reader_getchar(reader, flags);
- cfg->reader.read_ptr = old_read_ptr;
- cfg->reader.line_number = old_lineno;
- cfg->reader.eof = old_eof;
+ reader->read_ptr = old_read_ptr;
+ reader->line_number = old_lineno;
+ reader->eof = old_eof;
return ret;
}
@@ -591,13 +635,13 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
-static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
+static char *reader_readline(struct reader *reader, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
size_t line_len;
- line_src = cfg->reader.read_ptr;
+ line_src = reader->read_ptr;
if (skip_whitespace) {
/* Skip empty empty lines */
@@ -626,10 +670,10 @@ static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
line_end++;
if (*line_end == '\0')
- cfg->reader.eof = 1;
+ reader->eof = 1;
- cfg->reader.line_number++;
- cfg->reader.read_ptr = line_end;
+ reader->line_number++;
+ reader->read_ptr = line_end;
return line;
}
@@ -637,11 +681,11 @@ static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
/*
* Consume a line, without storing it anywhere
*/
-static void cfg_consume_line(diskfile_backend *cfg)
+static void reader_consume_line(struct reader *reader)
{
char *line_start, *line_end;
- line_start = cfg->reader.read_ptr;
+ line_start = reader->read_ptr;
line_end = strchr(line_start, '\n');
/* No newline at EOF */
if(line_end == NULL){
@@ -652,10 +696,10 @@ static void cfg_consume_line(diskfile_backend *cfg)
line_end++;
if (*line_end == '\0')
- cfg->reader.eof = 1;
+ reader->eof = 1;
- cfg->reader.line_number++;
- cfg->reader.read_ptr = line_end;
+ reader->line_number++;
+ reader->read_ptr = line_end;
}
GIT_INLINE(int) config_keychar(int c)
@@ -663,7 +707,7 @@ GIT_INLINE(int) config_keychar(int c)
return isalnum(c) || c == '-';
}
-static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
+static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
{
int c, rpos;
char *first_quote, *last_quote;
@@ -679,7 +723,7 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
last_quote = strrchr(line, '"');
if (last_quote - first_quote == 0) {
- set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ set_parse_error(reader, 0, "Missing closing quotation mark in section header");
return -1;
}
@@ -698,14 +742,14 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
*/
do {
if (quote_marks == 2) {
- set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
+ set_parse_error(reader, rpos, "Unexpected text after closing quotes");
git_buf_free(&buf);
return -1;
}
switch (c) {
case 0:
- set_parse_error(cfg, 0, "Unexpected end-of-line in section header");
+ set_parse_error(reader, 0, "Unexpected end-of-line in section header");
git_buf_free(&buf);
return -1;
@@ -719,7 +763,7 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
switch (c) {
case '"':
if (&line[rpos-1] == last_quote) {
- set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ set_parse_error(reader, 0, "Missing closing quotation mark in section header");
git_buf_free(&buf);
return -1;
}
@@ -728,7 +772,7 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
break;
default:
- set_parse_error(cfg, rpos, "Unsupported escape sequence");
+ set_parse_error(reader, rpos, "Unsupported escape sequence");
git_buf_free(&buf);
return -1;
}
@@ -737,21 +781,21 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
break;
}
- git_buf_putc(&buf, c);
+ git_buf_putc(&buf, (char)c);
} while ((c = line[rpos++]) != ']');
*section_name = git_buf_detach(&buf);
return 0;
}
-static int parse_section_header(diskfile_backend *cfg, char **section_out)
+static int parse_section_header(struct reader *reader, char **section_out)
{
char *name, *name_end;
int name_length, c, pos;
int result;
char *line;
- line = cfg_readline(cfg, true);
+ line = reader_readline(reader, true);
if (line == NULL)
return -1;
@@ -759,7 +803,7 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
name_end = strchr(line, ']');
if (name_end == NULL) {
git__free(line);
- set_parse_error(cfg, 0, "Missing ']' in section header");
+ set_parse_error(reader, 0, "Missing ']' in section header");
return -1;
}
@@ -778,14 +822,14 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
do {
if (git__isspace(c)){
name[name_length] = '\0';
- result = parse_section_header_ext(cfg, line, name, section_out);
+ result = parse_section_header_ext(reader, line, name, section_out);
git__free(line);
git__free(name);
return result;
}
if (!config_keychar(c) && c != '.') {
- set_parse_error(cfg, pos, "Unexpected character in header");
+ set_parse_error(reader, pos, "Unexpected character in header");
goto fail_parse;
}
@@ -794,7 +838,7 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
} while ((c = line[pos++]) != ']');
if (line[pos - 1] != ']') {
- set_parse_error(cfg, pos, "Unexpected end of file");
+ set_parse_error(reader, pos, "Unexpected end of file");
goto fail_parse;
}
@@ -811,14 +855,14 @@ fail_parse:
return -1;
}
-static int skip_bom(diskfile_backend *cfg)
+static int skip_bom(struct reader *reader)
{
git_bom_t bom;
int bom_offset = git_buf_text_detect_bom(&bom,
- &cfg->reader.buffer, cfg->reader.read_ptr - cfg->reader.buffer.ptr);
+ &reader->buffer, reader->read_ptr - reader->buffer.ptr);
if (bom == GIT_BOM_UTF8)
- cfg->reader.read_ptr += bom_offset;
+ reader->read_ptr += bom_offset;
/* TODO: reference implementation is pretty stupid with BoM */
@@ -888,7 +932,16 @@ static int strip_comments(char *line, int in_quotes)
return quote_count;
}
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
+static int included_path(git_buf *out, const char *dir, const char *path)
+{
+ /* From the user's home */
+ if (path[0] == '~' && path[1] == '/')
+ return git_futils_find_global_file(out, &path[1]);
+
+ return git_path_join_unrooted(out, path, dir, NULL);
+}
+
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
{
int c;
char *current_section = NULL;
@@ -898,39 +951,46 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
git_buf buf = GIT_BUF_INIT;
int result = 0;
khiter_t pos;
+ uint32_t reader_idx;
+
+ if (depth >= MAX_INCLUDE_DEPTH) {
+ giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
+ return -1;
+ }
+ reader_idx = git_array_size(cfg_file->readers) - 1;
/* Initialize the reading position */
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
- cfg_file->reader.eof = 0;
+ reader->read_ptr = reader->buffer.ptr;
+ reader->eof = 0;
/* If the file is empty, there's nothing for us to do */
- if (*cfg_file->reader.read_ptr == '\0')
+ if (*reader->read_ptr == '\0')
return 0;
- skip_bom(cfg_file);
+ skip_bom(reader);
- while (result == 0 && !cfg_file->reader.eof) {
+ while (result == 0 && !reader->eof) {
- c = cfg_peek(cfg_file, SKIP_WHITESPACE);
+ c = reader_peek(reader, SKIP_WHITESPACE);
switch (c) {
case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
- cfg_file->reader.eof = 1;
+ reader->eof = 1;
break;
case '[': /* section header, new section begins */
git__free(current_section);
current_section = NULL;
- result = parse_section_header(cfg_file, &current_section);
+ result = parse_section_header(reader, &current_section);
break;
case ';':
case '#':
- cfg_consume_line(cfg_file);
+ reader_consume_line(reader);
break;
default: /* assume variable declaration */
- result = parse_variable(cfg_file, &var_name, &var_value);
+ result = parse_variable(reader, &var_name, &var_value);
if (result < 0)
break;
@@ -951,6 +1011,7 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
var->entry->name = git_buf_detach(&buf);
var->entry->value = var_value;
var->entry->level = level;
+ var->included = !!depth;
/* Add or append the new config option */
pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
@@ -967,6 +1028,42 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
existing->next = var;
}
+ if (!git__strcmp(var->entry->name, "include.path")) {
+ struct reader *r;
+ git_buf path = GIT_BUF_INIT;
+ char *dir;
+ uint32_t index;
+
+ r = git_array_alloc(cfg_file->readers);
+ /* The reader may have been reallocated */
+ reader = git_array_get(cfg_file->readers, reader_idx);
+ memset(r, 0, sizeof(struct reader));
+ if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
+ break;
+
+ /* We need to know out index in the array, as the next config_parse call may realloc */
+ index = git_array_size(cfg_file->readers) - 1;
+ dir = git_buf_detach(&path);
+ result = included_path(&path, dir, var->entry->value);
+ git__free(dir);
+
+ if (result < 0)
+ break;
+
+ r->file_path = git_buf_detach(&path);
+ git_buf_init(&r->buffer, 0);
+ if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
+ &r->file_size, NULL)) < 0)
+ break;
+
+ result = config_parse(cfg_file, r, level, depth+1);
+ r = git_array_get(cfg_file->readers, index);
+ git_buf_free(&r->buffer);
+
+ if (result < 0)
+ break;
+ }
+
break;
}
}
@@ -1015,20 +1112,21 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
const char *pre_end = NULL, *post_start = NULL, *data_start;
char *current_section = NULL, *section, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
+ struct reader *reader = git_array_get(cfg->readers, 0);
/* We need to read in our own config file */
- result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
+ result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
/* Initialise the reading position */
if (result == GIT_ENOTFOUND) {
- cfg->reader.read_ptr = NULL;
- cfg->reader.eof = 1;
+ reader->read_ptr = NULL;
+ reader->eof = 1;
data_start = NULL;
- git_buf_clear(&cfg->reader.buffer);
+ git_buf_clear(&reader->buffer);
} else if (result == 0) {
- cfg->reader.read_ptr = cfg->reader.buffer.ptr;
- cfg->reader.eof = 0;
- data_start = cfg->reader.read_ptr;
+ reader->read_ptr = reader->buffer.ptr;
+ reader->eof = 0;
+ data_start = reader->read_ptr;
} else {
return -1; /* OS error when reading the file */
}
@@ -1037,13 +1135,13 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
return -1;
- skip_bom(cfg);
+ skip_bom(reader);
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
- while (!cfg->reader.eof) {
- c = cfg_peek(cfg, SKIP_WHITESPACE);
+ while (!reader->eof) {
+ c = reader_peek(reader, SKIP_WHITESPACE);
if (c == '\0') { /* We've arrived at the end of the file */
break;
@@ -1056,11 +1154,11 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* new section. If we actually want to replace it, the
* default case will take care of updating them.
*/
- pre_end = post_start = cfg->reader.read_ptr;
+ pre_end = post_start = reader->read_ptr;
git__free(current_section);
current_section = NULL;
- if (parse_section_header(cfg, &current_section) < 0)
+ if (parse_section_header(reader, &current_section) < 0)
goto rewrite_fail;
/* Keep track of when it stops matching */
@@ -1069,7 +1167,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
}
else if (c == ';' || c == '#') {
- cfg_consume_line(cfg);
+ reader_consume_line(reader);
}
else {
@@ -1085,15 +1183,15 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
*/
if (!section_matches) {
if (!last_section_matched) {
- cfg_consume_line(cfg);
+ reader_consume_line(reader);
continue;
}
} else {
int has_matched = 0;
char *var_name, *var_value;
- pre_end = cfg->reader.read_ptr;
- if (parse_variable(cfg, &var_name, &var_value) < 0)
+ pre_end = reader->read_ptr;
+ if (parse_variable(reader, &var_name, &var_value) < 0)
goto rewrite_fail;
/* First try to match the name of the variable */
@@ -1112,7 +1210,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (!has_matched)
continue;
- post_start = cfg->reader.read_ptr;
+ post_start = reader->read_ptr;
}
/* We've found the variable we wanted to change, so
@@ -1155,12 +1253,12 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
*/
if (write_trailer) {
/* Write out rest of the file */
- git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+ git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
} else {
if (preg_replaced) {
git_filebuf_printf(&file, "\n%s", data_start);
} else {
- git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+ git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
/* And now if we just need to add a variable */
if (!section_matches && write_section(&file, section) < 0)
@@ -1176,7 +1274,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
}
/* If we are here, there is at least a section line */
- if (cfg->reader.buffer.size > 0 && *(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n')
+ if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
git_filebuf_write(&file, "\n", 1);
git_filebuf_printf(&file, "\t%s = %s\n", name, value);
@@ -1187,10 +1285,10 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git__free(current_section);
/* refresh stats - if this errors, then commit will error too */
- (void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
+ (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
- git_buf_free(&cfg->reader.buffer);
+ git_buf_free(&reader->buffer);
return result;
@@ -1199,7 +1297,7 @@ rewrite_fail:
git__free(current_section);
git_filebuf_cleanup(&file);
- git_buf_free(&cfg->reader.buffer);
+ git_buf_free(&reader->buffer);
return -1;
}
@@ -1291,19 +1389,19 @@ static int is_multiline_var(const char *str)
return (end > str) && (count & 1);
}
-static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
+static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
{
char *line = NULL, *proc_line = NULL;
int quote_count;
/* Check that the next line exists */
- line = cfg_readline(cfg, false);
+ line = reader_readline(reader, false);
if (line == NULL)
return -1;
/* We've reached the end of the file, there is input missing */
if (line[0] == '\0') {
- set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+ set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
git__free(line);
return -1;
}
@@ -1313,7 +1411,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
/* If it was just a comment, pretend it didn't exist */
if (line[0] == '\0') {
git__free(line);
- return parse_multiline_variable(cfg, value, quote_count);
+ return parse_multiline_variable(reader, value, quote_count);
/* TODO: unbounded recursion. This **could** be exploitable */
}
@@ -1338,19 +1436,19 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
* keep putting stuff in the buffer
*/
if (is_multiline_var(value->ptr))
- return parse_multiline_variable(cfg, value, quote_count);
+ return parse_multiline_variable(reader, value, quote_count);
return 0;
}
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
+static int parse_variable(struct reader *reader, char **var_name, char **var_value)
{
const char *var_end = NULL;
const char *value_start = NULL;
char *line;
int quote_count;
- line = cfg_readline(cfg, true);
+ line = reader_readline(reader, true);
if (line == NULL)
return -1;
@@ -1385,7 +1483,7 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
GITERR_CHECK_ALLOC(proc_line);
git_buf_puts(&multi_value, proc_line);
git__free(proc_line);
- if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
+ if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
git__free(*var_name);
git__free(line);
git_buf_free(&multi_value);
diff --git a/src/crlf.c b/src/crlf.c
index 65039f9cc..b25c02cce 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -8,6 +8,7 @@
#include "git2/attr.h"
#include "git2/blob.h"
#include "git2/index.h"
+#include "git2/sys/filter.h"
#include "common.h"
#include "fileops.h"
@@ -19,13 +20,11 @@
struct crlf_attrs {
int crlf_action;
int eol;
+ int auto_crlf;
};
struct crlf_filter {
git_filter f;
- struct crlf_attrs attrs;
- git_repository *repo;
- char path[GIT_FLEX_ARRAY];
};
static int check_crlf(const char *value)
@@ -76,41 +75,10 @@ static int crlf_input_action(struct crlf_attrs *ca)
return ca->crlf_action;
}
-static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+static int has_cr_in_index(const git_filter_source *src)
{
-#define NUM_CONV_ATTRS 3
-
- static const char *attr_names[NUM_CONV_ATTRS] = {
- "crlf", "eol", "text",
- };
-
- const char *attr_vals[NUM_CONV_ATTRS];
- int error;
-
- error = git_attr_get_many(attr_vals,
- repo, 0, path, NUM_CONV_ATTRS, attr_names);
-
- if (error == GIT_ENOTFOUND) {
- ca->crlf_action = GIT_CRLF_GUESS;
- ca->eol = GIT_EOL_UNSET;
- return 0;
- }
-
- if (error == 0) {
- ca->crlf_action = check_crlf(attr_vals[2]); /* text */
- if (ca->crlf_action == GIT_CRLF_GUESS)
- ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
-
- ca->eol = check_eol(attr_vals[1]); /* eol */
- return 0;
- }
-
- return -1;
-}
-
-static int has_cr_in_index(git_filter *self)
-{
- struct crlf_filter *filter = (struct crlf_filter *)self;
+ git_repository *repo = git_filter_source_repo(src);
+ const char *path = git_filter_source_path(src);
git_index *index;
const git_index_entry *entry;
git_blob *blob;
@@ -118,19 +86,22 @@ static int has_cr_in_index(git_filter *self)
git_off_t blobsize;
bool found_cr;
- if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+ if (!path)
+ return false;
+
+ if (git_repository_index__weakptr(&index, repo) < 0) {
giterr_clear();
return false;
}
- if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
- !(entry = git_index_get_bypath(index, filter->path, 1)))
+ if (!(entry = git_index_get_bypath(index, path, 0)) &&
+ !(entry = git_index_get_bypath(index, path, 1)))
return false;
if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
return true;
- if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+ if (git_blob_lookup(&blob, repo, &entry->oid) < 0)
return false;
blobcontent = git_blob_rawcontent(blob);
@@ -147,27 +118,24 @@ static int has_cr_in_index(git_filter *self)
}
static int crlf_apply_to_odb(
- git_filter *self, git_buf *dest, const git_buf *source)
+ struct crlf_attrs *ca,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
-
- assert(self && dest && source);
-
/* Empty file? Nothing to do */
- if (git_buf_len(source) == 0)
+ if (!git_buf_len(from))
return 0;
/* Heuristics to see if we can skip the conversion.
* Straight from Core Git.
*/
- if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
- filter->attrs.crlf_action == GIT_CRLF_GUESS) {
-
+ if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
git_buf_text_stats stats;
- /* Check heuristics for binary vs text... */
- if (git_buf_text_gather_stats(&stats, source, false))
- return -1;
+ /* Check heuristics for binary vs text - returns true if binary */
+ if (git_buf_text_gather_stats(&stats, from, false))
+ return GIT_PASSTHROUGH;
/*
* We're currently not going to even try to convert stuff
@@ -175,28 +143,28 @@ static int crlf_apply_to_odb(
* stuff?
*/
if (stats.cr != stats.crlf)
- return -1;
+ return GIT_PASSTHROUGH;
- if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+ if (ca->crlf_action == GIT_CRLF_GUESS) {
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
*/
- if (has_cr_in_index(self))
- return -1;
+ if (has_cr_in_index(src))
+ return GIT_PASSTHROUGH;
}
if (!stats.cr)
- return -1;
+ return GIT_PASSTHROUGH;
}
/* Actually drop the carriage returns */
- return git_buf_text_crlf_to_lf(dest, source);
+ return git_buf_text_crlf_to_lf(to, from);
}
-static const char *line_ending(struct crlf_filter *filter)
+static const char *line_ending(struct crlf_attrs *ca)
{
- switch (filter->attrs.crlf_action) {
+ switch (ca->crlf_action) {
case GIT_CRLF_BINARY:
case GIT_CRLF_INPUT:
return "\n";
@@ -213,11 +181,9 @@ static const char *line_ending(struct crlf_filter *filter)
goto line_ending_error;
}
- switch (filter->attrs.eol) {
+ switch (ca->eol) {
case GIT_EOL_UNSET:
- return GIT_EOL_NATIVE == GIT_EOL_CRLF
- ? "\r\n"
- : "\n";
+ return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n";
case GIT_EOL_CRLF:
return "\r\n";
@@ -235,41 +201,64 @@ line_ending_error:
}
static int crlf_apply_to_workdir(
- git_filter *self, git_buf *dest, const git_buf *source)
+ struct crlf_attrs *ca, git_buf *to, const git_buf *from)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
const char *workdir_ending = NULL;
- assert(self && dest && source);
-
/* Empty file? Nothing to do. */
- if (git_buf_len(source) == 0)
- return -1;
+ if (git_buf_len(from) == 0)
+ return 0;
+
+ /* Don't filter binary files */
+ if (git_buf_text_is_binary(from))
+ return GIT_PASSTHROUGH;
/* Determine proper line ending */
- workdir_ending = line_ending(filter);
+ workdir_ending = line_ending(ca);
if (!workdir_ending)
return -1;
- if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
- return -1;
- /* for now, only lf->crlf conversion is supported here */
- assert(!strcmp("\r\n", workdir_ending));
- return git_buf_text_lf_to_crlf(dest, source);
+ if (!strcmp("\n", workdir_ending)) {
+ if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf)
+ return GIT_PASSTHROUGH;
+
+ if (git_buf_find(from, '\r') < 0)
+ return GIT_PASSTHROUGH;
+
+ if (git_buf_text_crlf_to_lf(to, from) < 0)
+ return -1;
+ } else {
+ /* only other supported option is lf->crlf conversion */
+ assert(!strcmp("\r\n", workdir_ending));
+
+ if (git_buf_text_lf_to_crlf(to, from) < 0)
+ return -1;
+ }
+
+ return 0;
}
-static int find_and_add_filter(
- git_vector *filters, git_repository *repo, const char *path,
- int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source))
+static int crlf_check(
+ git_filter *self,
+ void **payload, /* points to NULL ptr on entry, may be set */
+ const git_filter_source *src,
+ const char **attr_values)
{
- struct crlf_attrs ca;
- struct crlf_filter *filter;
- size_t pathlen;
int error;
+ struct crlf_attrs ca;
+
+ GIT_UNUSED(self);
- /* Load gitattributes for the path */
- if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
- return error;
+ if (!attr_values) {
+ ca.crlf_action = GIT_CRLF_GUESS;
+ ca.eol = GIT_EOL_UNSET;
+ } else {
+ ca.crlf_action = check_crlf(attr_values[2]); /* text */
+ if (ca.crlf_action == GIT_CRLF_GUESS)
+ ca.crlf_action = check_crlf(attr_values[0]); /* clrf */
+ ca.eol = check_eol(attr_values[1]); /* eol */
+ }
+ ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
/*
* Use the core Git logic to see if we should perform CRLF for this file
@@ -278,41 +267,64 @@ static int find_and_add_filter(
ca.crlf_action = crlf_input_action(&ca);
if (ca.crlf_action == GIT_CRLF_BINARY)
- return 0;
+ return GIT_PASSTHROUGH;
if (ca.crlf_action == GIT_CRLF_GUESS) {
- int auto_crlf;
-
- if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+ error = git_repository__cvar(
+ &ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
+ if (error < 0)
return error;
- if (auto_crlf == GIT_AUTO_CRLF_FALSE)
- return 0;
+ if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return GIT_PASSTHROUGH;
}
- /* If we're good, we create a new filter object and push it
- * into the filters array */
- pathlen = strlen(path);
- filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
- GITERR_CHECK_ALLOC(filter);
+ *payload = git__malloc(sizeof(ca));
+ GITERR_CHECK_ALLOC(*payload);
+ memcpy(*payload, &ca, sizeof(ca));
+
+ return 0;
+}
- filter->f.apply = apply;
- filter->f.do_free = NULL;
- memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
- filter->repo = repo;
- memcpy(filter->path, path, pathlen + 1);
+static int crlf_apply(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
+{
+ /* initialize payload in case `check` was bypassed */
+ if (!*payload) {
+ int error = crlf_check(self, payload, src, NULL);
+ if (error < 0 && error != GIT_PASSTHROUGH)
+ return error;
+ }
- return git_vector_insert(filters, filter);
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ return crlf_apply_to_workdir(*payload, to, from);
+ else
+ return crlf_apply_to_odb(*payload, to, from, src);
}
-int git_filter_add__crlf_to_odb(
- git_vector *filters, git_repository *repo, const char *path)
+static void crlf_cleanup(
+ git_filter *self,
+ void *payload)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
+ GIT_UNUSED(self);
+ git__free(payload);
}
-int git_filter_add__crlf_to_workdir(
- git_vector *filters, git_repository *repo, const char *path)
+git_filter *git_crlf_filter_new(void)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
+ struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
+
+ f->f.version = GIT_FILTER_VERSION;
+ f->f.attributes = "crlf eol text";
+ f->f.initialize = NULL;
+ f->f.shutdown = git_filter_free;
+ f->f.check = crlf_check;
+ f->f.apply = crlf_apply;
+ f->f.cleanup = crlf_cleanup;
+
+ return (git_filter *)f;
}
diff --git a/src/diff.c b/src/diff.c
index 77dbbd8bc..39facce60 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -440,7 +440,8 @@ static int diff_list_apply_options(
/* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) {
- diff->opts.context_lines = config_int(cfg, "diff.context", 3);
+ int context = config_int(cfg, "diff.context", 3);
+ diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3;
/* add other defaults here */
}
@@ -568,21 +569,21 @@ int git_diff__oid_for_file(
giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
result = -1;
} else {
- git_vector filters = GIT_VECTOR_INIT;
+ git_filter_list *fl = NULL;
- result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
- if (result >= 0) {
+ result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
+ if (!result) {
int fd = git_futils_open_ro(full_path.ptr);
if (fd < 0)
result = fd;
else {
result = git_odb__hashfd_filtered(
- oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
+ oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
p_close(fd);
}
- }
- git_filters_free(&filters);
+ git_filter_list_free(fl);
+ }
}
cleanup:
diff --git a/src/diff_file.c b/src/diff_file.c
index bcfef13cd..5939ee8b8 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -296,9 +296,9 @@ static int diff_file_content_load_workdir_file(
git_diff_file_content *fc, git_buf *path)
{
int error = 0;
- git_vector filters = GIT_VECTOR_INIT;
- git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+ git_filter_list *fl = NULL;
git_file fd = git_futils_open_ro(git_buf_cstr(path));
+ git_buf raw = GIT_BUF_INIT;
if (fd < 0)
return fd;
@@ -310,41 +310,38 @@ static int diff_file_content_load_workdir_file(
if (diff_file_content_binary_by_size(fc))
goto cleanup;
- if ((error = git_filters_load(
- &filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
+ if ((error = git_filter_list_load(
+ &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
goto cleanup;
- /* error >= is a filter count */
- if (error == 0) {
+ /* if there are no filters, try to mmap the file */
+ if (fl == NULL) {
if (!(error = git_futils_mmap_ro(
- &fc->map, fd, 0, (size_t)fc->file->size)))
+ &fc->map, fd, 0, (size_t)fc->file->size))) {
fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
- else /* fall through to try readbuffer below */
- giterr_clear();
+ goto cleanup;
+ }
+
+ /* if mmap failed, fall through to try readbuffer below */
+ giterr_clear();
}
- if (error != 0) {
- error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
- if (error < 0)
- goto cleanup;
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
+ git_buf out = GIT_BUF_INIT;
- if (!filters.length)
- git_buf_swap(&filtered, &raw);
- else
- error = git_filters_apply(&filtered, &raw, &filters);
+ error = git_filter_list_apply_to_data(&out, fl, &raw);
+
+ git_buf_free(&raw);
if (!error) {
- fc->map.len = git_buf_len(&filtered);
- fc->map.data = git_buf_detach(&filtered);
+ fc->map.len = out.size;
+ fc->map.data = out.ptr;
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
}
-
- git_buf_free(&raw);
- git_buf_free(&filtered);
}
cleanup:
- git_filters_free(&filters);
+ git_filter_list_free(fl);
p_close(fd);
return error;
diff --git a/src/diff_print.c b/src/diff_print.c
index ee4b5fc17..fd18b67e3 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -336,7 +336,7 @@ static int diff_print_patch_hunk(
return 0;
git_buf_clear(pi->buf);
- if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
+ if (git_buf_put(pi->buf, header, header_len) < 0)
return -1;
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
@@ -360,13 +360,14 @@ static int diff_print_patch_line(
return 0;
git_buf_clear(pi->buf);
+ git_buf_grow(pi->buf, content_len + 2);
if (line_origin == GIT_DIFF_LINE_ADDITION ||
line_origin == GIT_DIFF_LINE_DELETION ||
line_origin == GIT_DIFF_LINE_CONTEXT)
- git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
- else if (content_len > 0)
- git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
+ git_buf_putc(pi->buf, line_origin);
+
+ git_buf_put(pi->buf, content, content_len);
if (git_buf_oom(pi->buf))
return -1;
diff --git a/src/fileops.c b/src/fileops.c
index 126d45f26..5a5041cc3 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -6,6 +6,7 @@
*/
#include "common.h"
#include "fileops.h"
+#include "global.h"
#include <ctype.h>
#if GIT_WIN32
#include "win32/findfile.h"
@@ -55,18 +56,8 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode
int git_futils_creat_locked(const char *path, const mode_t mode)
{
- int fd;
-
-#ifdef GIT_WIN32
- git_win32_path buf;
-
- git_win32_path_from_c(buf, path);
- fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
+ int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#else
- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC |
- O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#endif
if (fd < 0) {
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
@@ -592,7 +583,7 @@ clean_up:
static int git_futils_guess_system_dirs(git_buf *out)
{
#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out);
+ return git_win32__find_system_dirs(out, L"etc\\");
#else
return git_buf_sets(out, "/etc");
#endif
@@ -624,17 +615,34 @@ static int git_futils_guess_xdg_dirs(git_buf *out)
#endif
}
+static int git_futils_guess_template_dirs(git_buf *out)
+{
+#ifdef GIT_WIN32
+ return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
+#else
+ return git_buf_sets(out, "/usr/share/git-core/templates");
+#endif
+}
+
typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
- { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
+ { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
git_futils_guess_system_dirs,
git_futils_guess_global_dirs,
git_futils_guess_xdg_dirs,
+ git_futils_guess_template_dirs,
};
+static void git_futils_dirs_global_shutdown(void)
+{
+ int i;
+ for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
+ git_buf_free(&git_futils__dirs[i]);
+}
+
int git_futils_dirs_global_init(void)
{
git_futils_dir_t i;
@@ -644,6 +652,8 @@ int git_futils_dirs_global_init(void)
for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
error = git_futils_dirs_get(&path, i);
+ git__on_shutdown(git_futils_dirs_global_shutdown);
+
return error;
}
@@ -726,13 +736,6 @@ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
}
-void git_futils_dirs_free(void)
-{
- int i;
- for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
- git_buf_free(&git_futils__dirs[i]);
-}
-
static int git_futils_find_in_dirlist(
git_buf *path, const char *name, git_futils_dir_t which, const char *label)
{
@@ -753,7 +756,8 @@ static int git_futils_find_in_dirlist(
continue;
GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
- GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
+ if (name)
+ GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
if (git_path_exists(path->ptr))
return 0;
@@ -782,6 +786,12 @@ int git_futils_find_xdg_file(git_buf *path, const char *filename)
path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
}
+int git_futils_find_template_dir(git_buf *path)
+{
+ return git_futils_find_in_dirlist(
+ path, NULL, GIT_FUTILS_DIR_TEMPLATE, "template");
+}
+
int git_futils_fake_symlink(const char *old, const char *new)
{
int retcode = GIT_ERROR;
diff --git a/src/fileops.h b/src/fileops.h
index 02f79b9e7..6bd693b99 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -306,11 +306,20 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
*/
extern int git_futils_find_system_file(git_buf *path, const char *filename);
+/**
+ * Find template directory.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_futils_find_template_dir(git_buf *path);
+
typedef enum {
GIT_FUTILS_DIR_SYSTEM = 0,
GIT_FUTILS_DIR_GLOBAL = 1,
GIT_FUTILS_DIR_XDG = 2,
- GIT_FUTILS_DIR__MAX = 3,
+ GIT_FUTILS_DIR_TEMPLATE = 3,
+ GIT_FUTILS_DIR__MAX = 4,
} git_futils_dir_t;
/**
@@ -354,11 +363,6 @@ extern int git_futils_dirs_get_str(
extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
/**
- * Release / reset all search paths
- */
-extern void git_futils_dirs_free(void);
-
-/**
* Create a "fake" symlink (text file containing the target path).
*
* @param new symlink file to be created
diff --git a/src/filter.c b/src/filter.c
index 9f749dcbd..9f866fe88 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -10,68 +10,594 @@
#include "hash.h"
#include "filter.h"
#include "repository.h"
+#include "global.h"
+#include "git2/sys/filter.h"
#include "git2/config.h"
#include "blob.h"
+#include "attr_file.h"
+#include "array.h"
-int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+struct git_filter_source {
+ git_repository *repo;
+ const char *path;
+ git_oid oid; /* zero if unknown (which is likely) */
+ uint16_t filemode; /* zero if unknown */
+ git_filter_mode_t mode;
+};
+
+typedef struct {
+ git_filter *filter;
+ void *payload;
+} git_filter_entry;
+
+struct git_filter_list {
+ git_array_t(git_filter_entry) filters;
+ git_filter_source source;
+ char path[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+ const char *filter_name;
+ git_filter *filter;
+ int priority;
+ int initialized;
+ size_t nattrs, nmatches;
+ char *attrdata;
+ const char *attrs[GIT_FLEX_ARRAY];
+} git_filter_def;
+
+static int filter_def_priority_cmp(const void *a, const void *b)
{
- int error;
+ int pa = ((const git_filter_def *)a)->priority;
+ int pb = ((const git_filter_def *)b)->priority;
+ return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
+}
- if (mode == GIT_FILTER_TO_ODB) {
- /* Load the CRLF cleanup filter when writing to the ODB */
- error = git_filter_add__crlf_to_odb(filters, repo, path);
- if (error < 0)
- return error;
- } else {
- error = git_filter_add__crlf_to_workdir(filters, repo, path);
- if (error < 0)
- return error;
+struct filter_registry {
+ git_vector filters;
+};
+
+static struct filter_registry *git__filter_registry = NULL;
+
+static void filter_registry_shutdown(void)
+{
+ struct filter_registry *reg = NULL;
+ size_t pos;
+ git_filter_def *fdef;
+
+ if ((reg = git__swap(git__filter_registry, NULL)) == NULL)
+ return;
+
+ git_vector_foreach(&reg->filters, pos, fdef) {
+ if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->attrdata);
+ git__free(fdef);
}
- return (int)filters->length;
+ git_vector_free(&reg->filters);
+ git__free(reg);
}
-void git_filters_free(git_vector *filters)
+static int filter_registry_initialize(void)
{
- size_t i;
- git_filter *filter;
+ int error = 0;
+ struct filter_registry *reg;
+
+ if (git__filter_registry)
+ return 0;
+
+ reg = git__calloc(1, sizeof(struct filter_registry));
+ GITERR_CHECK_ALLOC(reg);
+
+ if ((error = git_vector_init(
+ &reg->filters, 2, filter_def_priority_cmp)) < 0)
+ goto cleanup;
+
+ reg = git__compare_and_swap(&git__filter_registry, NULL, reg);
+ if (reg != NULL)
+ goto cleanup;
+
+ git__on_shutdown(filter_registry_shutdown);
+
+ /* try to register both default filters */
+ {
+ git_filter *crlf = git_crlf_filter_new();
+ git_filter *ident = git_ident_filter_new();
+
+ if (crlf && git_filter_register(
+ GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0)
+ crlf = NULL;
+ if (ident && git_filter_register(
+ GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
+ ident = NULL;
+
+ if (!crlf || !ident)
+ return -1;
+ }
+
+ return 0;
+
+cleanup:
+ git_vector_free(&reg->filters);
+ git__free(reg);
+ return error;
+}
+
+static int filter_def_scan_attrs(
+ git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
+{
+ const char *start, *scan = attr_str;
+ int has_eq;
+
+ *nattr = *nmatch = 0;
+
+ if (!scan)
+ return 0;
+
+ while (*scan) {
+ while (git__isspace(*scan)) scan++;
+
+ for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
+ if (*scan == '=')
+ has_eq = 1;
+ }
+
+ if (scan > start) {
+ (*nattr)++;
+ if (has_eq || *start == '-' || *start == '+' || *start == '!')
+ (*nmatch)++;
- git_vector_foreach(filters, i, filter) {
- if (filter->do_free != NULL)
- filter->do_free(filter);
- else
- git__free(filter);
+ if (has_eq)
+ git_buf_putc(attrs, '=');
+ git_buf_put(attrs, start, scan - start);
+ git_buf_putc(attrs, '\0');
+ }
}
- git_vector_free(filters);
+ return 0;
}
-int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+static void filter_def_set_attrs(git_filter_def *fdef)
{
+ char *scan = fdef->attrdata;
size_t i;
- unsigned int src;
- git_buf *dbuffer[2];
- dbuffer[0] = source;
- dbuffer[1] = dest;
+ for (i = 0; i < fdef->nattrs; ++i) {
+ const char *name, *value;
+
+ switch (*scan) {
+ case '=':
+ name = scan + 1;
+ for (scan++; *scan != '='; scan++) /* find '=' */;
+ *scan++ = '\0';
+ value = scan;
+ break;
+ case '-':
+ name = scan + 1; value = git_attr__false; break;
+ case '+':
+ name = scan + 1; value = git_attr__true; break;
+ case '!':
+ name = scan + 1; value = git_attr__unset; break;
+ default:
+ name = scan; value = NULL; break;
+ }
+
+ fdef->attrs[i] = name;
+ fdef->attrs[i + fdef->nattrs] = value;
+
+ scan += strlen(scan) + 1;
+ }
+}
+
+static int filter_def_name_key_check(const void *key, const void *fdef)
+{
+ const char *name =
+ fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
+ return name ? git__strcmp(key, name) : -1;
+}
- src = 0;
+static int filter_def_filter_key_check(const void *key, const void *fdef)
+{
+ const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
+ return (key == filter) ? 0 : -1;
+}
+
+static int filter_registry_find(size_t *pos, const char *name)
+{
+ return git_vector_search2(
+ pos, &git__filter_registry->filters, filter_def_name_key_check, name);
+}
- if (git_buf_len(source) == 0) {
- git_buf_clear(dest);
+static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
+{
+ git_filter_def *fdef = NULL;
+
+ if (!filter_registry_find(pos, name))
+ fdef = git_vector_get(&git__filter_registry->filters, *pos);
+
+ return fdef;
+}
+
+int git_filter_register(
+ const char *name, git_filter *filter, int priority)
+{
+ git_filter_def *fdef;
+ size_t nattr = 0, nmatch = 0;
+ git_buf attrs = GIT_BUF_INIT;
+
+ if (filter_registry_initialize() < 0)
+ return -1;
+
+ if (!filter_registry_find(NULL, name)) {
+ giterr_set(
+ GITERR_FILTER, "Attempt to reregister existing filter '%s'", name);
+ return GIT_EEXISTS;
+ }
+
+ if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
+ return -1;
+
+ fdef = git__calloc(
+ sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1);
+ GITERR_CHECK_ALLOC(fdef);
+
+ fdef->filter_name = name;
+ fdef->filter = filter;
+ fdef->priority = priority;
+ fdef->nattrs = nattr;
+ fdef->nmatches = nmatch;
+ fdef->attrdata = git_buf_detach(&attrs);
+
+ filter_def_set_attrs(fdef);
+
+ if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) {
+ git__free(fdef->attrdata);
+ git__free(fdef);
+ return -1;
+ }
+
+ git_vector_sort(&git__filter_registry->filters);
+ return 0;
+}
+
+int git_filter_unregister(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+
+ /* cannot unregister default filters */
+ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
+ giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
+ return -1;
+ }
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
+ giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
+ return GIT_ENOTFOUND;
+ }
+
+ (void)git_vector_remove(&git__filter_registry->filters, pos);
+
+ if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->attrdata);
+ git__free(fdef);
+
+ return 0;
+}
+
+static int filter_initialize(git_filter_def *fdef)
+{
+ int error = 0;
+
+ if (!fdef->initialized &&
+ fdef->filter &&
+ fdef->filter->initialize &&
+ (error = fdef->filter->initialize(fdef->filter)) < 0)
+ {
+ /* auto-unregister if initialize fails */
+ git_filter_unregister(fdef->filter_name);
+ return error;
+ }
+
+ fdef->initialized = true;
+ return 0;
+}
+
+git_filter *git_filter_lookup(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+
+ if (filter_registry_initialize() < 0)
+ return NULL;
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL)
+ return NULL;
+
+ if (!fdef->initialized && filter_initialize(fdef) < 0)
+ return NULL;
+
+ return fdef->filter;
+}
+
+void git_filter_free(git_filter *filter)
+{
+ git__free(filter);
+}
+
+git_repository *git_filter_source_repo(const git_filter_source *src)
+{
+ return src->repo;
+}
+
+const char *git_filter_source_path(const git_filter_source *src)
+{
+ return src->path;
+}
+
+uint16_t git_filter_source_filemode(const git_filter_source *src)
+{
+ return src->filemode;
+}
+
+const git_oid *git_filter_source_id(const git_filter_source *src)
+{
+ return git_oid_iszero(&src->oid) ? NULL : &src->oid;
+}
+
+git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
+{
+ return src->mode;
+}
+
+static int filter_list_new(
+ git_filter_list **out, const git_filter_source *src)
+{
+ git_filter_list *fl = NULL;
+ size_t pathlen = src->path ? strlen(src->path) : 0;
+
+ fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1);
+ GITERR_CHECK_ALLOC(fl);
+
+ if (src->path)
+ memcpy(fl->path, src->path, pathlen);
+ fl->source.repo = src->repo;
+ fl->source.path = fl->path;
+ fl->source.mode = src->mode;
+
+ *out = fl;
+ return 0;
+}
+
+static int filter_list_check_attributes(
+ const char ***out, git_filter_def *fdef, const git_filter_source *src)
+{
+ int error;
+ size_t i;
+ const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
+ GITERR_CHECK_ALLOC(strs);
+
+ error = git_attr_get_many(
+ strs, src->repo, 0, src->path, fdef->nattrs, fdef->attrs);
+
+ /* if no values were found but no matches are needed, it's okay! */
+ if (error == GIT_ENOTFOUND && !fdef->nmatches) {
+ giterr_clear();
+ git__free((void *)strs);
return 0;
}
- /* Pre-grow the destination buffer to more or less the size
- * we expect it to have */
- if (git_buf_grow(dest, git_buf_len(source)) < 0)
+ for (i = 0; !error && i < fdef->nattrs; ++i) {
+ const char *want = fdef->attrs[fdef->nattrs + i];
+ git_attr_t want_type, found_type;
+
+ if (!want)
+ continue;
+
+ want_type = git_attr_value(want);
+ found_type = git_attr_value(strs[i]);
+
+ if (want_type != found_type ||
+ (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
+ error = GIT_ENOTFOUND;
+ }
+
+ if (error)
+ git__free((void *)strs);
+ else
+ *out = strs;
+
+ return error;
+}
+
+int git_filter_list_new(
+ git_filter_list **out, git_repository *repo, git_filter_mode_t mode)
+{
+ git_filter_source src = { 0 };
+ src.repo = repo;
+ src.path = NULL;
+ src.mode = mode;
+ return filter_list_new(out, &src);
+}
+
+int git_filter_list_load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+ git_filter_source src = { 0 };
+ git_filter_entry *fe;
+ size_t idx;
+ git_filter_def *fdef;
+
+ if (filter_registry_initialize() < 0)
+ return -1;
+
+ src.repo = repo;
+ src.path = path;
+ src.mode = mode;
+ if (blob)
+ git_oid_cpy(&src.oid, git_blob_id(blob));
+
+ git_vector_foreach(&git__filter_registry->filters, idx, fdef) {
+ const char **values = NULL;
+ void *payload = NULL;
+
+ if (!fdef || !fdef->filter)
+ continue;
+
+ if (fdef->nattrs > 0) {
+ error = filter_list_check_attributes(&values, fdef, &src);
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ continue;
+ } else if (error < 0)
+ break;
+ }
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ break;
+
+ if (fdef->filter->check)
+ error = fdef->filter->check(
+ fdef->filter, &payload, &src, values);
+
+ git__free((void *)values);
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ else if (error < 0)
+ break;
+ else {
+ if (!fl && (error = filter_list_new(&fl, &src)) < 0)
+ return error;
+
+ fe = git_array_alloc(fl->filters);
+ GITERR_CHECK_ALLOC(fe);
+ fe->filter = fdef->filter;
+ fe->payload = payload;
+ }
+ }
+
+ if (error && fl != NULL) {
+ git_array_clear(fl->filters);
+ git__free(fl);
+ fl = NULL;
+ }
+
+ *filters = fl;
+ return error;
+}
+
+void git_filter_list_free(git_filter_list *fl)
+{
+ uint32_t i;
+
+ if (!fl)
+ return;
+
+ for (i = 0; i < git_array_size(fl->filters); ++i) {
+ git_filter_entry *fe = git_array_get(fl->filters, i);
+ if (fe->filter->cleanup)
+ fe->filter->cleanup(fe->filter, fe->payload);
+ }
+
+ git_array_clear(fl->filters);
+ git__free(fl);
+}
+
+int git_filter_list_push(
+ git_filter_list *fl, git_filter *filter, void *payload)
+{
+ int error = 0;
+ size_t pos;
+ git_filter_def *fdef;
+ git_filter_entry *fe;
+
+ assert(fl && filter);
+
+ if (git_vector_search2(
+ &pos, &git__filter_registry->filters,
+ filter_def_filter_key_check, filter) < 0) {
+ giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
return -1;
+ }
+
+ fdef = git_vector_get(&git__filter_registry->filters, pos);
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ return error;
+
+ fe = git_array_alloc(fl->filters);
+ GITERR_CHECK_ALLOC(fe);
+ fe->filter = filter;
+ fe->payload = payload;
+
+ return 0;
+}
+
+size_t git_filter_list_length(const git_filter_list *fl)
+{
+ return fl ? git_array_size(fl->filters) : 0;
+}
+
+static int filter_list_out_buffer_from_raw(
+ git_buf *out, const void *ptr, size_t size)
+{
+ if (git_buf_is_allocated(out))
+ git_buf_free(out);
- for (i = 0; i < filters->length; ++i) {
- git_filter *filter = git_vector_get(filters, i);
- unsigned int dst = 1 - src;
+ if (!size) {
+ git_buf_init(out, 0);
+ } else {
+ out->ptr = (char *)ptr;
+ out->asize = 0;
+ out->size = size;
+ }
+
+ return 0;
+}
+
+int git_filter_list_apply_to_data(
+ git_buf *tgt, git_filter_list *fl, git_buf *src)
+{
+ int error = 0;
+ uint32_t i;
+ git_buf *dbuffer[2], local = GIT_BUF_INIT;
+ unsigned int si = 0;
- git_buf_clear(dbuffer[dst]);
+ if (!fl)
+ return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size);
+
+ dbuffer[0] = src;
+ dbuffer[1] = tgt;
+
+ /* if `src` buffer is reallocable, then use it, otherwise copy it */
+ if (!git_buf_is_allocated(src)) {
+ if (git_buf_set(&local, src->ptr, src->size) < 0)
+ return -1;
+ dbuffer[0] = &local;
+ }
+
+ for (i = 0; i < git_array_size(fl->filters); ++i) {
+ unsigned int di = 1 - si;
+ uint32_t fidx = (fl->source.mode == GIT_FILTER_TO_WORKTREE) ?
+ i : git_array_size(fl->filters) - 1 - i;
+ git_filter_entry *fe = git_array_get(fl->filters, fidx);
+
+ dbuffer[di]->size = 0;
/* Apply the filter from dbuffer[src] to the other buffer;
* if the filtering is canceled by the user mid-filter,
@@ -79,16 +605,72 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
* of the double buffering (so that the text goes through
* cleanly).
*/
- if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
- src = dst;
- if (git_buf_oom(dbuffer[dst]))
- return -1;
+ error = fe->filter->apply(
+ fe->filter, &fe->payload, dbuffer[di], dbuffer[si], &fl->source);
+
+ if (error == GIT_PASSTHROUGH) {
+ /* PASSTHROUGH means filter decided not to process the buffer */
+ error = 0;
+ } else if (!error) {
+ git_buf_shorten(dbuffer[di], 0); /* force NUL termination */
+ si = di; /* swap buffers */
+ } else {
+ tgt->size = 0;
+ return error;
+ }
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
- if (src != 1)
- git_buf_swap(dest, source);
+ if (si != 1)
+ git_buf_swap(dbuffer[0], dbuffer[1]);
+
+ git_buf_free(&local); /* don't leak if we allocated locally */
return 0;
}
+
+int git_filter_list_apply_to_file(
+ git_buf *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path)
+{
+ int error;
+ const char *base = repo ? git_repository_workdir(repo) : NULL;
+ git_buf abspath = GIT_BUF_INIT, raw = GIT_BUF_INIT;
+
+ if (!(error = git_path_join_unrooted(&abspath, path, base, NULL)) &&
+ !(error = git_futils_readbuffer(&raw, abspath.ptr)))
+ {
+ error = git_filter_list_apply_to_data(out, filters, &raw);
+
+ git_buf_free(&raw);
+ }
+
+ git_buf_free(&abspath);
+ return error;
+}
+
+int git_filter_list_apply_to_blob(
+ git_buf *out,
+ git_filter_list *filters,
+ git_blob *blob)
+{
+ git_buf in = GIT_BUF_INIT;
+ git_off_t rawsize = git_blob_rawsize(blob);
+
+ if (!git__is_sizet(rawsize)) {
+ giterr_set(GITERR_OS, "Blob is too large to filter");
+ return -1;
+ }
+
+ in.ptr = (char *)git_blob_rawcontent(blob);
+ in.asize = 0;
+ in.size = (size_t)rawsize;
+
+ if (filters)
+ git_oid_cpy(&filters->source.oid, git_blob_id(blob));
+
+ return git_filter_list_apply_to_data(out, filters, &in);
+}
diff --git a/src/filter.h b/src/filter.h
index 42a44ebdb..d0ace0f9a 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -8,19 +8,7 @@
#define INCLUDE_filter_h__
#include "common.h"
-#include "buffer.h"
-#include "git2/odb.h"
-#include "git2/repository.h"
-
-typedef struct git_filter {
- int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
- void (*do_free)(struct git_filter *self);
-} git_filter;
-
-typedef enum {
- GIT_FILTER_TO_WORKTREE,
- GIT_FILTER_TO_ODB
-} git_filter_mode;
+#include "git2/filter.h"
typedef enum {
GIT_CRLF_GUESS = -1,
@@ -31,64 +19,13 @@ typedef enum {
GIT_CRLF_AUTO,
} git_crlf_t;
-/*
- * FILTER API
- */
-
-/*
- * For any given path in the working directory, fill the `filters`
- * array with the relevant filters that need to be applied.
- *
- * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
- * filters that will be used when checking out a file to the working
- * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
- * a file to the ODB.
- *
- * @param filters Vector where to store all the loaded filters
- * @param repo Repository object that contains `path`
- * @param path Relative path of the file to be filtered
- * @param mode Filtering direction (WT->ODB or ODB->WT)
- * @return the number of filters loaded for the file (0 if the file
- * doesn't need filtering), or a negative error code
- */
-extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
-
-/*
- * Apply one or more filters to a file.
- *
- * The file must have been loaded as a `git_buf` object. Both the `source`
- * and `dest` buffers are owned by the caller and must be freed once
- * they are no longer needed.
- *
- * NOTE: Because of the double-buffering schema, the `source` buffer that contains
- * the original file may be tampered once the filtering is complete. Regardless,
- * the `dest` buffer will always contain the final result of the filtering
- *
- * @param dest Buffer to store the result of the filtering
- * @param source Buffer containing the document to filter
- * @param filters A non-empty vector of filters as supplied by `git_filters_load`
- * @return 0 on success, an error code otherwise
- */
-extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
-
-/*
- * Free the `filters` array generated by `git_filters_load`.
- *
- * Note that this frees both the array and its contents. The array will
- * be clean/reusable after this call.
- *
- * @param filters A filters array as supplied by `git_filters_load`
- */
-extern void git_filters_free(git_vector *filters);
+extern void git_filter_free(git_filter *filter);
/*
* Available filters
*/
-/* Strip CRLF, from Worktree to ODB */
-extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
-
-/* Add CRLF, from ODB to worktree */
-extern int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path);
+extern git_filter *git_crlf_filter_new(void);
+extern git_filter *git_ident_filter_new(void);
#endif
diff --git a/src/global.c b/src/global.c
index b504e5e0a..4f024f17e 100644
--- a/src/global.c
+++ b/src/global.c
@@ -14,6 +14,28 @@
git_mutex git__mwindow_mutex;
+#define MAX_SHUTDOWN_CB 8
+
+git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
+git_atomic git__n_shutdown_callbacks;
+
+void git__on_shutdown(git_global_shutdown_fn callback)
+{
+ int count = git_atomic_inc(&git__n_shutdown_callbacks);
+ assert(count <= MAX_SHUTDOWN_CB);
+ git__shutdown_callbacks[count - 1] = callback;
+}
+
+static void git__shutdown(void)
+{
+ int pos;
+
+ while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
+ if (git__shutdown_callbacks[pos])
+ git__shutdown_callbacks[pos]();
+ }
+}
+
/**
* Handle the global state with TLS
*
@@ -79,9 +101,7 @@ int git_threads_init(void)
void git_threads_shutdown(void)
{
/* Shut down any subsystems that have global state */
- win32_pthread_shutdown();
- git_futils_dirs_free();
- git_hash_global_shutdown();
+ git__shutdown();
TlsFree(_tls_index);
_tls_init = 0;
@@ -140,6 +160,9 @@ int git_threads_init(void)
void git_threads_shutdown(void)
{
+ /* Shut down any subsystems that have global state */
+ git__shutdown();
+
if (_tls_init) {
void *ptr = pthread_getspecific(_tls_key);
pthread_setspecific(_tls_key, NULL);
@@ -149,10 +172,6 @@ void git_threads_shutdown(void)
pthread_key_delete(_tls_key);
_tls_init = 0;
git_mutex_free(&git__mwindow_mutex);
-
- /* Shut down any subsystems that have global state */
- git_hash_global_shutdown();
- git_futils_dirs_free();
}
git_global_st *git__global_state(void)
@@ -179,15 +198,14 @@ static git_global_st __state;
int git_threads_init(void)
{
- /* noop */
+ /* noop */
return 0;
}
void git_threads_shutdown(void)
{
/* Shut down any subsystems that have global state */
- git_hash_global_shutdown();
- git_futils_dirs_free();
+ git__shutdown();
}
git_global_st *git__global_state(void)
diff --git a/src/global.h b/src/global.h
index badbc0883..778250376 100644
--- a/src/global.h
+++ b/src/global.h
@@ -21,4 +21,8 @@ extern git_mutex git__mwindow_mutex;
#define GIT_GLOBAL (git__global_state())
+typedef void (*git_global_shutdown_fn)(void);
+
+extern void git__on_shutdown(git_global_shutdown_fn callback);
+
#endif
diff --git a/src/hash.h b/src/hash.h
index 5b848981f..c47f33549 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -13,8 +13,6 @@ typedef struct git_hash_prov git_hash_prov;
typedef struct git_hash_ctx git_hash_ctx;
int git_hash_global_init(void);
-void git_hash_global_shutdown(void);
-
int git_hash_ctx_init(git_hash_ctx *ctx);
void git_hash_ctx_cleanup(git_hash_ctx *ctx);
diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h
index 6b60c98c4..daeb1cda8 100644
--- a/src/hash/hash_generic.h
+++ b/src/hash/hash_generic.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
};
#define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
diff --git a/src/hash/hash_openssl.h b/src/hash/hash_openssl.h
index f83279a5a..9a55d472d 100644
--- a/src/hash/hash_openssl.h
+++ b/src/hash/hash_openssl.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
};
#define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c
index 095ceb359..bb2231364 100644
--- a/src/hash/hash_win32.c
+++ b/src/hash/hash_win32.c
@@ -89,7 +89,15 @@ GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
hash_prov.type = INVALID;
}
-int git_hash_global_init()
+static void git_hash_global_shutdown(void)
+{
+ if (hash_prov.type == CNG)
+ hash_cng_prov_shutdown();
+ else if(hash_prov.type == CRYPTOAPI)
+ hash_cryptoapi_prov_shutdown();
+}
+
+int git_hash_global_init(void)
{
int error = 0;
@@ -99,15 +107,9 @@ int git_hash_global_init()
if ((error = hash_cng_prov_init()) < 0)
error = hash_cryptoapi_prov_init();
- return error;
-}
+ git__on_shutdown(git_hash_global_shutdown);
-void git_hash_global_shutdown()
-{
- if (hash_prov.type == CNG)
- hash_cng_prov_shutdown();
- else if(hash_prov.type == CRYPTOAPI)
- hash_cryptoapi_prov_shutdown();
+ return error;
}
/* CryptoAPI: available in Windows XP and newer */
diff --git a/src/ident.c b/src/ident.c
new file mode 100644
index 000000000..51630879d
--- /dev/null
+++ b/src/ident.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/sys/filter.h"
+#include "filter.h"
+#include "buffer.h"
+#include "buf_text.h"
+
+static int ident_find_id(
+ const char **id_start, const char **id_end, const char *start, size_t len)
+{
+ const char *end = start + len, *found = NULL;
+
+ while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
+ size_t remaining = (size_t)(end - found) - 1;
+ if (remaining < 3)
+ return GIT_ENOTFOUND;
+
+ start = found + 1;
+ len = remaining;
+
+ if (start[0] == 'I' && start[1] == 'd')
+ break;
+ }
+
+ if (len < 3 || !found)
+ return GIT_ENOTFOUND;
+ *id_start = found;
+
+ if ((found = memchr(start + 2, '$', len - 2)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *id_end = found + 1;
+ return 0;
+}
+
+static int ident_insert_id(
+ git_buf *to, const git_buf *from, const git_filter_source *src)
+{
+ char oid[GIT_OID_HEXSZ+1];
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ /* replace $Id$ with blob id */
+
+ if (!git_filter_source_id(src))
+ return GIT_PASSTHROUGH;
+
+ git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 5 /* "$Id: " */ + GIT_OID_HEXSZ + 1 /* "$" */ +
+ (size_t)(from_end - id_end);
+
+ if (git_buf_grow(to, need_size) < 0)
+ return -1;
+
+ git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_buf_put(to, "$Id: ", 5);
+ git_buf_put(to, oid, GIT_OID_HEXSZ);
+ git_buf_putc(to, '$');
+ git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_remove_id(
+ git_buf *to, const git_buf *from)
+{
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 4 /* "$Id$" */ + (size_t)(from_end - id_end);
+
+ if (git_buf_grow(to, need_size) < 0)
+ return -1;
+
+ git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_buf_put(to, "$Id$", 4);
+ git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_apply(
+ git_filter *self,
+ void **payload,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
+{
+ GIT_UNUSED(self); GIT_UNUSED(payload);
+
+ /* Don't filter binary files */
+ if (git_buf_text_is_binary(from))
+ return GIT_PASSTHROUGH;
+
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ return ident_insert_id(to, from, src);
+ else
+ return ident_remove_id(to, from);
+}
+
+git_filter *git_ident_filter_new(void)
+{
+ git_filter *f = git__calloc(1, sizeof(git_filter));
+
+ f->version = GIT_FILTER_VERSION;
+ f->attributes = "+ident"; /* apply to files with ident attribute set */
+ f->shutdown = git_filter_free;
+ f->apply = ident_apply;
+
+ return f;
+}
diff --git a/src/index.c b/src/index.c
index 9b32222a7..21a8d31d1 100644
--- a/src/index.c
+++ b/src/index.c
@@ -410,7 +410,7 @@ static int create_index_error(int error, const char *msg)
int git_index_set_caps(git_index *index, unsigned int caps)
{
- int old_ignore_case;
+ unsigned int old_ignore_case;
assert(index);
@@ -438,7 +438,7 @@ int git_index_set_caps(git_index *index, unsigned int caps)
}
if (old_ignore_case != index->ignore_case) {
- git_index__set_ignore_case(index, index->ignore_case);
+ git_index__set_ignore_case(index, (bool)index->ignore_case);
}
return 0;
@@ -2092,7 +2092,7 @@ int git_index_add_all(
/* check if path actually matches */
if (!git_pathspec__match(
- &ps.pathspec, wd->path, no_fnmatch, ignorecase, &match, NULL))
+ &ps.pathspec, wd->path, no_fnmatch, (bool)ignorecase, &match, NULL))
continue;
/* skip ignored items that are not already in the index */
@@ -2184,7 +2184,7 @@ static int index_apply_to_all(
/* check if path actually matches */
if (!git_pathspec__match(
- &ps.pathspec, entry->path, false, index->ignore_case,
+ &ps.pathspec, entry->path, false, (bool)index->ignore_case,
&match, NULL))
continue;
diff --git a/src/indexer.c b/src/indexer.c
index 09f962934..ceb11f0b6 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -47,6 +47,11 @@ struct git_indexer_stream {
git_transfer_progress_callback progress_cb;
void *progress_payload;
char objbuf[8*1024];
+
+ /* Fields for calculating the packfile trailer (hash of everything before it) */
+ char inbuf[GIT_OID_RAWSZ];
+ int inbuf_len;
+ git_hash_ctx trailer;
};
struct delta_info {
@@ -121,6 +126,7 @@ int git_indexer_stream_new(
GITERR_CHECK_ALLOC(idx);
idx->progress_cb = progress_cb;
idx->progress_payload = progress_payload;
+ git_hash_ctx_init(&idx->trailer);
error = git_buf_joinpath(&path, prefix, suff);
if (error < 0)
@@ -322,7 +328,6 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
entry->offset = (uint32_t)entry_start;
}
- /* FIXME: Parse the object instead of hashing it */
if (git_odb__hashobj(&oid, obj) < 0) {
giterr_set(GITERR_INDEXER, "Failed to hash object");
goto on_error;
@@ -370,6 +375,43 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *
return idx->progress_cb(stats, idx->progress_payload);
}
+/* Hash everything but the last 20B of input */
+static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
+{
+ int to_expell, to_keep;
+
+ if (size == 0)
+ return;
+
+ /* Easy case, dump the buffer and the data minus the last 20 bytes */
+ if (size >= 20) {
+ git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
+ git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
+
+ data += size - GIT_OID_RAWSZ;
+ memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
+ idx->inbuf_len = GIT_OID_RAWSZ;
+ return;
+ }
+
+ /* We can just append */
+ if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
+ memcpy(idx->inbuf + idx->inbuf_len, data, size);
+ idx->inbuf_len += size;
+ return;
+ }
+
+ /* We need to partially drain the buffer and then append */
+ to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
+ to_keep = abs(idx->inbuf_len - to_expell);
+
+ git_hash_update(&idx->trailer, idx->inbuf, to_expell);
+
+ memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
+ memcpy(idx->inbuf + to_keep, data, size);
+ idx->inbuf_len += size - to_expell;
+}
+
int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error = -1;
@@ -384,6 +426,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;
+ hash_partially(idx, data, size);
+
/* Make sure we set the new size of the pack */
if (idx->opened_pack) {
idx->pack->mwf.size += size;
@@ -576,17 +620,33 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
struct git_pack_idx_header hdr;
git_buf filename = GIT_BUF_INIT;
struct entry *entry;
- void *packfile_hash;
- git_oid file_hash;
+ git_oid trailer_hash, file_hash;
git_hash_ctx ctx;
git_filebuf index_file = {0};
+ void *packfile_trailer;
if (git_hash_ctx_init(&ctx) < 0)
return -1;
/* Test for this before resolve_deltas(), as it plays with idx->off */
- if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
- giterr_set(GITERR_INDEXER, "Indexing error: unexpected data at the end of the pack");
+ if (idx->off < idx->pack->mwf.size - 20) {
+ giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
+ return -1;
+ }
+
+ packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+ if (packfile_trailer == NULL) {
+ git_mwindow_close(&w);
+ goto on_error;
+ }
+
+ /* Compare the packfile trailer as it was sent to us and what we calculated */
+ git_oid_fromraw(&file_hash, packfile_trailer);
+ git_mwindow_close(&w);
+
+ git_hash_final(&trailer_hash, &idx->trailer);
+ if (git_oid_cmp(&file_hash, &trailer_hash)) {
+ giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
return -1;
}
@@ -595,7 +655,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
return -1;
if (stats->indexed_objects != stats->total_objects) {
- giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+ giterr_set(GITERR_INDEXER, "early EOF");
return -1;
}
@@ -658,23 +718,15 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
}
- /* Write out the packfile trailer */
- packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
- if (packfile_hash == NULL) {
- git_mwindow_close(&w);
+ /* Write out the packfile trailer to the index */
+ if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
goto on_error;
- }
-
- memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
- git_mwindow_close(&w);
-
- git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
- /* Write out the packfile trailer to the idx file as well */
- if (git_filebuf_hash(&file_hash, &index_file) < 0)
+ /* Write out the hash of the idx */
+ if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
goto on_error;
- git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
+ git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
/* Figure out what the final name should be */
if (index_path_stream(&filename, idx, ".idx") < 0)
diff --git a/src/merge.c b/src/merge.c
index 2e94ce1cd..b07e8c52f 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -58,7 +58,7 @@ struct merge_diff_df_data {
/* Merge base computation */
-int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
+int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
{
git_revwalk *walk;
git_vector list;
diff --git a/src/netops.c b/src/netops.c
index 803c2696a..c1e74546f 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -585,7 +585,6 @@ int gitno_extract_url_parts(
const char *start;
/*
- *
* ==> [user[:pass]@]hostname.tld[:port]/resource
*/
diff --git a/src/odb.c b/src/odb.c
index a0bfec403..b2c138aae 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -168,7 +168,6 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
error = -1;
goto done;
- return -1;
}
error = git_hash_final(out, &ctx);
@@ -179,28 +178,30 @@ done:
}
int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
+ git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl)
{
int error;
git_buf raw = GIT_BUF_INIT;
- git_buf filtered = GIT_BUF_INIT;
- if (!filters || !filters->length)
+ if (!fl)
return git_odb__hashfd(out, fd, size, type);
/* size of data is used in header, so we have to read the whole file
* into memory to apply filters before beginning to calculate the hash
*/
- if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
- error = git_filters_apply(&filtered, &raw, filters);
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
+ git_buf post = GIT_BUF_INIT;
+
+ error = git_filter_list_apply_to_data(&post, fl, &raw);
- git_buf_free(&raw);
+ git_buf_free(&raw);
- if (!error)
- error = git_odb_hash(out, filtered.ptr, filtered.size, type);
+ if (!error)
+ error = git_odb_hash(out, post.ptr, post.size, type);
- git_buf_free(&filtered);
+ git_buf_free(&post);
+ }
return error;
}
@@ -621,7 +622,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
git_odb_backend *b = internal->backend;
if (b->exists != NULL)
- found = b->exists(b, id);
+ found = (bool)b->exists(b, id);
}
return (int)found;
diff --git a/src/odb.h b/src/odb.h
index 0d9f9e2ea..61dd9a7fd 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -14,6 +14,7 @@
#include "vector.h"
#include "cache.h"
#include "posix.h"
+#include "filter.h"
#define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECT_DIR_MODE 0777
@@ -66,7 +67,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
* Acts just like git_odb__hashfd with the addition of filters...
*/
int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t len, git_otype type, git_vector *filters);
+ git_oid *out, git_file fd, size_t len, git_otype type, git_filter_list *fl);
/*
* Hash a `path`, assuming it could be a POSIX symlink: if the path is a
diff --git a/src/oid.c b/src/oid.c
index a70b7e099..d56b6af24 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -211,7 +211,7 @@ int git_oid_strcmp(const git_oid *oid_a, const char *str)
for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
if ((hexval = git__fromhex(*str++)) < 0)
return -1;
- strval = hexval << 4;
+ strval = (unsigned char)(hexval << 4);
if (*str) {
if ((hexval = git__fromhex(*str++)) < 0)
return -1;
diff --git a/src/path.c b/src/path.c
index 56b0b49ca..9d8e90361 100644
--- a/src/path.c
+++ b/src/path.c
@@ -565,7 +565,7 @@ static bool _check_dir_contents(
size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */
- if (git_buf_try_grow(dir, dir_size + sub_size + 2, false) < 0)
+ if (git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0)
return false;
/* save excursion */
@@ -902,8 +902,16 @@ int git_path_dirload_with_stat(
git_buf_truncate(&full, prefix_len);
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
- (error = git_path_lstat(full.ptr, &ps->st)) < 0)
+ (error = git_path_lstat(full.ptr, &ps->st)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ git_vector_remove(contents, i--);
+ continue;
+ }
+
break;
+ }
if (S_ISDIR(ps->st.st_mode)) {
if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0)
diff --git a/src/posix.h b/src/posix.h
index de2aa0b73..14c92b93d 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -39,7 +39,7 @@ typedef int git_file;
*
* Some of the methods are slightly wrapped to provide
* saner defaults. Some of these methods are emulated
- * in Windows platforns.
+ * in Windows platforms.
*
* Use your manpages to check the docs on these.
*/
diff --git a/src/refdb.c b/src/refdb.c
index 4de7188b2..7df61a577 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -201,5 +201,5 @@ int git_refdb_rename(
int git_refdb_delete(struct git_refdb *db, const char *ref_name)
{
assert(db && db->backend);
- return db->backend->delete(db->backend, ref_name);
+ return db->backend->del(db->backend, ref_name);
}
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 894ff7c84..0ba711eb7 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1096,7 +1096,7 @@ int git_refdb_backend_fs(
backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator;
backend->parent.write = &refdb_fs_backend__write;
- backend->parent.delete = &refdb_fs_backend__delete;
+ backend->parent.del = &refdb_fs_backend__delete;
backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress;
backend->parent.free = &refdb_fs_backend__free;
diff --git a/src/remote.c b/src/remote.c
index bdc8e0e67..95b907ff1 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -18,8 +18,6 @@
#include "refspec.h"
#include "fetchhead.h"
-#include <regex.h>
-
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
{
git_refspec *spec;
@@ -362,7 +360,7 @@ cleanup:
static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
{
git_buf name = GIT_BUF_INIT;
- int push;
+ unsigned int push;
const char *dir;
size_t i;
int error = 0;
@@ -806,7 +804,7 @@ static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vec
(!git_reference_is_branch(resolved_ref)) ||
(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
- /* Not an error if HEAD is orphaned or no tracking branch */
+ /* Not an error if HEAD is unborn or no tracking branch */
if (error == GIT_ENOTFOUND)
error = 0;
@@ -1075,35 +1073,28 @@ void git_remote_free(git_remote *remote)
git__free(remote);
}
-struct cb_data {
- git_vector *list;
- regex_t *preg;
-};
-
-static int remote_list_cb(const git_config_entry *entry, void *data_)
+static int remote_list_cb(const git_config_entry *entry, void *payload)
{
- struct cb_data *data = (struct cb_data *)data_;
- size_t nmatch = 2;
- regmatch_t pmatch[2];
- const char *name = entry->name;
+ git_vector *list = payload;
+ const char *name = entry->name + strlen("remote.");
+ size_t namelen = strlen(name);
+ char *remote_name;
- if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
- char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
- GITERR_CHECK_ALLOC(remote_name);
+ /* we know name matches "remote.<stuff>.(push)?url" */
- if (git_vector_insert(data->list, remote_name) < 0)
- return -1;
- }
+ if (!strcmp(&name[namelen - 4], ".url"))
+ remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
+ else
+ remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
+ GITERR_CHECK_ALLOC(remote_name);
- return 0;
+ return git_vector_insert(list, remote_name);
}
int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{
git_config *cfg;
git_vector list;
- regex_t preg;
- struct cb_data data;
int error;
if (git_repository_config__weakptr(&cfg, repo) < 0)
@@ -1112,18 +1103,13 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
if (git_vector_init(&list, 4, git__strcmp_cb) < 0)
return -1;
- if (regcomp(&preg, "^remote\\.(.*)\\.(push)?url$", REG_EXTENDED) < 0) {
- giterr_set(GITERR_OS, "Remote catch regex failed to compile");
- return -1;
- }
+ error = git_config_foreach_match(
+ cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
- data.list = &list;
- data.preg = &preg;
- error = git_config_foreach(cfg, remote_list_cb, &data);
- regfree(&preg);
if (error < 0) {
size_t i;
char *elem;
+
git_vector_foreach(&list, i, elem) {
git__free(elem);
}
@@ -1549,7 +1535,7 @@ int git_remote_add_push(git_remote *remote, const char *refspec)
return add_refspec(remote, refspec, false);
}
-static int copy_refspecs(git_strarray *array, git_remote *remote, int push)
+static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
{
size_t i;
git_vector refspecs;
diff --git a/src/repository.c b/src/repository.c
index eae22ce51..0d7c09484 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -33,8 +33,6 @@
#define GIT_REPO_VERSION 0
-#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-
static void set_odb(git_repository *repo, git_odb *odb)
{
if (odb) {
@@ -1134,31 +1132,34 @@ static int repo_init_structure(
/* Copy external template if requested */
if (external_tpl) {
- git_config *cfg;
- const char *tdir;
+ git_config *cfg = NULL;
+ const char *tdir = NULL;
+ bool default_template = false;
+ git_buf template_buf = GIT_BUF_INIT;
if (opts->template_path)
tdir = opts->template_path;
- else if ((error = git_config_open_default(&cfg)) < 0)
- return error;
- else {
+ else if ((error = git_config_open_default(&cfg)) >= 0) {
error = git_config_get_string(&tdir, cfg, "init.templatedir");
-
- git_config_free(cfg);
-
- if (error && error != GIT_ENOTFOUND)
- return error;
-
giterr_clear();
- tdir = GIT_TEMPLATE_DIR;
}
- error = git_futils_cp_r(tdir, repo_dir,
- GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
- GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+ if (!tdir) {
+ if (!(error = git_futils_find_template_dir(&template_buf)))
+ tdir = template_buf.ptr;
+ default_template = true;
+ }
+
+ if (tdir)
+ error = git_futils_cp_r(tdir, repo_dir,
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
+ GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+
+ git_buf_free(&template_buf);
+ git_config_free(cfg);
if (error < 0) {
- if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+ if (!default_template)
return error;
/* if template was default, ignore error and use internal */
@@ -1451,10 +1452,10 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
git_reference_free(head);
- return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
+ return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
}
-int git_repository_head_orphan(git_repository *repo)
+int git_repository_head_unborn(git_repository *repo)
{
git_reference *ref = NULL;
int error;
@@ -1462,7 +1463,7 @@ int git_repository_head_orphan(git_repository *repo)
error = git_repository_head(&ref, repo);
git_reference_free(ref);
- if (error == GIT_EORPHANEDHEAD)
+ if (error == GIT_EUNBORNBRANCH)
return 1;
if (error < 0)
@@ -1649,7 +1650,7 @@ int git_repository_hashfile(
const char *as_path)
{
int error;
- git_vector filters = GIT_VECTOR_INIT;
+ git_filter_list *fl = NULL;
git_file fd = -1;
git_off_t len;
git_buf full_path = GIT_BUF_INIT;
@@ -1671,7 +1672,8 @@ int git_repository_hashfile(
/* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) {
- error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
+ error = git_filter_list_load(
+ &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
if (error < 0)
return error;
} else {
@@ -1698,12 +1700,12 @@ int git_repository_hashfile(
goto cleanup;
}
- error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
cleanup:
if (fd >= 0)
p_close(fd);
- git_filters_free(&filters);
+ git_filter_list_free(fl);
git_buf_free(&full_path);
return error;
diff --git a/src/revwalk.c b/src/revwalk.c
index 9e1e39ca4..3dd14b419 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -14,8 +14,6 @@
#include "git2/revparse.h"
#include "merge.h"
-#include <regex.h>
-
git_commit_list_node *git_revwalk__commit_lookup(
git_revwalk *walk, const git_oid *oid)
{
@@ -181,48 +179,35 @@ static int push_glob_cb(const char *refname, void *data_)
static int push_glob(git_revwalk *walk, const char *glob, int hide)
{
+ int error = 0;
git_buf buf = GIT_BUF_INIT;
struct push_cb_data data;
- regex_t preg;
+ size_t wildcard;
assert(walk && glob);
/* refs/ is implied if not given in the glob */
- if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {
- git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);
- } else {
+ if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
+ git_buf_joinpath(&buf, GIT_REFS_DIR, glob);
+ else
git_buf_puts(&buf, glob);
- }
/* If no '?', '*' or '[' exist, we append '/ *' to the glob */
- memset(&preg, 0x0, sizeof(regex_t));
- if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
- giterr_set(GITERR_OS, "Regex failed to compile");
- git_buf_free(&buf);
- return -1;
- }
-
- if (regexec(&preg, glob, 0, NULL, 0))
- git_buf_puts(&buf, "/*");
-
- if (git_buf_oom(&buf))
- goto on_error;
+ wildcard = strcspn(glob, "?*[");
+ if (!glob[wildcard])
+ git_buf_put(&buf, "/*", 2);
data.walk = walk;
data.hide = hide;
- if (git_reference_foreach_glob(
- walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0)
- goto on_error;
-
- regfree(&preg);
- git_buf_free(&buf);
- return 0;
+ if (git_buf_oom(&buf))
+ error = -1;
+ else
+ error = git_reference_foreach_glob(
+ walk->repo, git_buf_cstr(&buf), push_glob_cb, &data);
-on_error:
- regfree(&preg);
git_buf_free(&buf);
- return -1;
+ return error;
}
int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
diff --git a/src/stash.c b/src/stash.c
index 48f19144d..ab4a68575 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -27,7 +27,7 @@ static int retrieve_head(git_reference **out, git_repository *repo)
{
int error = git_repository_head(out, repo);
- if (error == GIT_EORPHANEDHEAD)
+ if (error == GIT_EUNBORNBRANCH)
return create_error(error, "You do not have the initial commit yet.");
return error;
diff --git a/src/status.c b/src/status.c
index 4a0d65092..be40b9f83 100644
--- a/src/status.c
+++ b/src/status.c
@@ -252,7 +252,7 @@ int git_status_list_new(
/* if there is no HEAD, that's okay - we'll make an empty iterator */
if (((error = git_repository_head_tree(&head, repo)) < 0) &&
- error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) {
+ error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH) {
git_index_free(index); /* release index */
return error;
}
diff --git a/src/submodule.c b/src/submodule.c
index 40bda9a41..121383b9c 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1557,7 +1557,7 @@ static void submodule_get_wd_status(
if (ign == GIT_SUBMODULE_IGNORE_NONE)
opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
- /* if we don't have an orphaned head, check diff with index */
+ /* if we don't have an unborn head, check diff with index */
if (git_repository_head_tree(&sm_head, sm_repo) < 0)
giterr_clear();
else {
diff --git a/src/transports/http.c b/src/transports/http.c
index eca06ead2..ab2f9a47f 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -59,7 +59,7 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_socket socket;
- const char *path;
+ char *path;
char *host;
char *port;
char *user_from_url;
@@ -125,15 +125,9 @@ static int gen_request(
size_t content_length)
{
http_subtransport *t = OWNING_SUBTRANSPORT(s);
+ const char *path = t->path ? t->path : "/";
- if (!t->path)
- t->path = "/";
-
- /* If we were redirected, make sure to respect that here */
- if (s->redirect_url)
- git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
- else
- git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+ git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->host);
@@ -209,7 +203,7 @@ static int on_header_ready(http_subtransport *t)
}
else if (!strcasecmp("Location", git_buf_cstr(name))) {
if (!t->location) {
- t->location= git__strdup(git_buf_cstr(value));
+ t->location = git__strdup(git_buf_cstr(value));
GITERR_CHECK_ALLOC(t->location);
}
}
@@ -255,6 +249,98 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0;
}
+static void free_connection_data(http_subtransport *t)
+{
+ if (t->host) {
+ git__free(t->host);
+ t->host = NULL;
+ }
+
+ if (t->port) {
+ git__free(t->port);
+ t->port = NULL;
+ }
+
+ if (t->user_from_url) {
+ git__free(t->user_from_url);
+ t->user_from_url = NULL;
+ }
+
+ if (t->pass_from_url) {
+ git__free(t->pass_from_url);
+ t->pass_from_url = NULL;
+ }
+
+ if (t->path) {
+ git__free(t->path);
+ t->path = NULL;
+ }
+}
+
+static int set_connection_data_from_url(
+ http_subtransport *t, const char *url, const char *service_suffix)
+{
+ int error = 0;
+ const char *default_port = NULL;
+ char *original_host = NULL;
+
+ if (!git__prefixcmp(url, prefix_http)) {
+ url = url + strlen(prefix_http);
+ default_port = "80";
+
+ if (t->use_ssl) {
+ giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
+ return -1;
+ }
+ }
+
+ if (!git__prefixcmp(url, prefix_https)) {
+ url += strlen(prefix_https);
+ default_port = "443";
+ t->use_ssl = 1;
+ }
+
+ if (!default_port) {
+ giterr_set(GITERR_NET, "Unrecognized URL prefix");
+ return -1;
+ }
+
+ /* preserve original host name for checking */
+ original_host = t->host;
+ t->host = NULL;
+
+ free_connection_data(t);
+
+ error = gitno_extract_url_parts(
+ &t->host, &t->port, &t->user_from_url, &t->pass_from_url,
+ url, default_port);
+
+ if (!error) {
+ const char *path = strchr(url, '/');
+ size_t pathlen = strlen(path);
+ size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
+
+ if (suffixlen &&
+ !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
+ t->path = git__strndup(path, pathlen - suffixlen);
+ else
+ t->path = git__strdup(path);
+
+ /* Allow '/'-led urls, or a change of protocol */
+ if (original_host != NULL) {
+ if (strcmp(original_host, t->host) && t->location[0] != '/') {
+ giterr_set(GITERR_NET, "Cross host redirect not allowed");
+ error = -1;
+ }
+
+ git__free(original_host);
+ }
+ }
+
+ return error;
+}
+
+
static int on_headers_complete(http_parser *parser)
{
parser_context *ctx = (parser_context *) parser->data;
@@ -308,10 +394,8 @@ static int on_headers_complete(http_parser *parser)
return t->parse_error = PARSE_ERROR_GENERIC;
}
- if (t->location[0] != '/') {
- giterr_set(GITERR_NET, "Only relative redirects are supported");
+ if (set_connection_data_from_url(t, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
- }
/* Set the redirect URL on the stream. This is a transfer of
* ownership of the memory. */
@@ -822,50 +906,31 @@ static int http_action(
git_smart_service_t action)
{
http_subtransport *t = (http_subtransport *)subtransport;
- const char *default_port = NULL;
int ret;
if (!stream)
return -1;
if (!t->host || !t->port || !t->path) {
- if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
- default_port = "80";
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if (!default_port)
- return -1;
-
- if ((ret = gitno_extract_url_parts(&t->host, &t->port,
- &t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
+ if ((ret = set_connection_data_from_url(t, url, NULL)) < 0)
return ret;
-
- t->path = strchr(url, '/');
}
if (http_connect(t) < 0)
return -1;
- switch (action)
- {
- case GIT_SERVICE_UPLOADPACK_LS:
- return http_uploadpack_ls(t, stream);
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return http_uploadpack_ls(t, stream);
- case GIT_SERVICE_UPLOADPACK:
- return http_uploadpack(t, stream);
+ case GIT_SERVICE_UPLOADPACK:
+ return http_uploadpack(t, stream);
- case GIT_SERVICE_RECEIVEPACK_LS:
- return http_receivepack_ls(t, stream);
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return http_receivepack_ls(t, stream);
- case GIT_SERVICE_RECEIVEPACK:
- return http_receivepack(t, stream);
+ case GIT_SERVICE_RECEIVEPACK:
+ return http_receivepack(t, stream);
}
*stream = NULL;
@@ -893,25 +958,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
+ free_connection_data(t);
return 0;
}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index bf62bd185..62f3f0bbf 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -37,6 +37,14 @@ typedef struct {
git_cred *cred;
} ssh_subtransport;
+static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
+{
+ char *ssherr;
+ libssh2_session_last_error(session, &ssherr, NULL, 0);
+
+ giterr_set(GITERR_SSH, "%s: %s", errmsg, ssherr);
+}
+
/*
* Create a git protocol request.
*
@@ -81,8 +89,8 @@ static int send_command(ssh_stream *s)
goto cleanup;
error = libssh2_channel_exec(s->channel, request.ptr);
- if (error < 0) {
- giterr_set(GITERR_NET, "SSH could not execute request");
+ if (error < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not execute request");
goto cleanup;
}
@@ -107,8 +115,8 @@ static int ssh_stream_read(
if (!s->sent_command && send_command(s) < 0)
return -1;
- if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < 0) {
- giterr_set(GITERR_NET, "SSH could not read data");
+ if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read data");;
return -1;
}
@@ -127,8 +135,8 @@ static int ssh_stream_write(
if (!s->sent_command && send_command(s) < 0)
return -1;
- if (libssh2_channel_write(s->channel, buffer, len) < 0) {
- giterr_set(GITERR_NET, "SSH could not write data");
+ if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not write data");
return -1;
}
@@ -212,7 +220,7 @@ static int git_ssh_extract_url_parts(
at = strchr(url, '@');
if (at) {
- start = at+1;
+ start = at + 1;
*username = git__substrdup(url, at - url);
GITERR_CHECK_ALLOC(*username);
} else {
@@ -262,8 +270,8 @@ static int _git_ssh_authenticate_session(
}
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
- if (rc != 0) {
- giterr_set(GITERR_NET, "Failed to authenticate SSH session");
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(session, "Failed to authenticate SSH session");
return -1;
}
@@ -289,9 +297,9 @@ static int _git_ssh_session_create(
rc = libssh2_session_startup(s, socket.socket);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
- if (0 != rc) {
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "Failed to start SSH session");
libssh2_session_free(s);
- giterr_set(GITERR_NET, "Failed to start SSH session");
return -1;
}
@@ -346,11 +354,11 @@ static int _git_ssh_setup_conn(
goto on_error;
if (!t->cred) {
- giterr_set(GITERR_NET, "Callback failed to initialize SSH credentials");
+ giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
goto on_error;
}
} else {
- giterr_set(GITERR_NET, "Cannot set up SSH connection without credentials");
+ giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials");
goto on_error;
}
assert(t->cred);
@@ -368,7 +376,7 @@ static int _git_ssh_setup_conn(
channel = libssh2_channel_open_session(session);
if (!channel) {
- giterr_set(GITERR_NET, "Failed to open SSH channel");
+ ssh_error(session, "Failed to open SSH channel");
goto on_error;
}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 29d4ba619..377f2ef97 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -73,7 +73,7 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
- const char *path;
+ char *path;
char *host;
char *port;
char *user_from_url;
@@ -152,6 +152,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
wchar_t *types[] = { L"*/*", NULL };
BOOL peerdist = FALSE;
int error = -1, wide_len;
+ unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */
git_buf_printf(&buf, "%s%s", t->path, s->service_url);
@@ -195,7 +196,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0)
+ if (git_remote__get_http_proxy(t->owner->owner, !!t->use_ssl, &proxy_url) < 0)
goto on_error;
if (proxy_url) {
@@ -244,6 +245,17 @@ static int winhttp_stream_connect(winhttp_stream *s)
git__free(proxy_wide);
}
+ /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
+ * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
+ */
+ if (!WinHttpSetOption(s->request,
+ WINHTTP_OPTION_DISABLE_FEATURE,
+ &disable_redirects,
+ sizeof(disable_redirects))) {
+ giterr_set(GITERR_OS, "Failed to disable redirects");
+ goto on_error;
+ }
+
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
* adds itself. This option may not be supported by the underlying
* platform, so we do not error-check it */
@@ -380,6 +392,142 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0;
}
+static void free_connection_data(winhttp_subtransport *t)
+{
+ if (t->host) {
+ git__free(t->host);
+ t->host = NULL;
+ }
+
+ if (t->port) {
+ git__free(t->port);
+ t->port = NULL;
+ }
+
+ if (t->user_from_url) {
+ git__free(t->user_from_url);
+ t->user_from_url = NULL;
+ }
+
+ if (t->pass_from_url) {
+ git__free(t->pass_from_url);
+ t->pass_from_url = NULL;
+ }
+
+ if (t->path) {
+ git__free(t->path);
+ t->path = NULL;
+ }
+}
+
+static int set_connection_data_from_url(
+ winhttp_subtransport *t, const char *url, const char *service_suffix)
+{
+ int error = 0;
+ const char *default_port = NULL;
+ char *original_host = NULL;
+ const char *original_url = url;
+
+ if (!git__prefixcmp(url, prefix_http)) {
+ url += strlen(prefix_http);
+ default_port = "80";
+
+ if (t->use_ssl) {
+ giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
+ return -1;
+ }
+ }
+
+ if (!git__prefixcmp(url, prefix_https)) {
+ url += strlen(prefix_https);
+ default_port = "443";
+ t->use_ssl = 1;
+ }
+
+ if (!default_port) {
+ giterr_set(GITERR_NET, "Unrecognized URL prefix");
+ return -1;
+ }
+
+ /* preserve original host name for checking */
+ original_host = t->host;
+ t->host = NULL;
+
+ free_connection_data(t);
+
+ error = gitno_extract_url_parts(
+ &t->host, &t->port, &t->user_from_url, &t->pass_from_url,
+ url, default_port);
+
+ if (!error) {
+ const char *path = strchr(url, '/');
+ size_t pathlen = strlen(path);
+ size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
+
+ if (suffixlen &&
+ !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
+ t->path = git__strndup(path, pathlen - suffixlen);
+ else
+ t->path = git__strdup(path);
+
+ /* Allow '/'-led urls, or a change of protocol */
+ if (original_host != NULL) {
+ if (strcmp(original_host, t->host) && original_url[0] != '/') {
+ giterr_set(GITERR_NET, "Cross host redirect not allowed");
+ error = -1;
+ }
+
+ git__free(original_host);
+ }
+ }
+
+ return error;
+}
+
+static int winhttp_connect(
+ winhttp_subtransport *t,
+ const char *url)
+{
+ wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
+ git_win32_path host;
+ int32_t port;
+ const char *default_port = "80";
+
+ /* Prepare port */
+ if (git__strtol32(&port, t->port, NULL, 10) < 0)
+ return -1;
+
+ /* Prepare host */
+ git_win32_path_from_c(host, t->host);
+
+ /* Establish session */
+ t->session = WinHttpOpen(
+ ua,
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+
+ if (!t->session) {
+ giterr_set(GITERR_OS, "Failed to init WinHTTP");
+ return -1;
+ }
+
+ /* Establish connection */
+ t->connection = WinHttpConnect(
+ t->session,
+ host,
+ (INTERNET_PORT) port,
+ 0);
+
+ if (!t->connection) {
+ giterr_set(GITERR_OS, "Failed to connect to host");
+ return -1;
+ }
+
+ return 0;
+}
+
static int winhttp_stream_read(
git_smart_subtransport_stream *stream,
char *buffer,
@@ -511,50 +659,52 @@ replay:
/* Check for Windows 7. This workaround is only necessary on
* Windows Vista and earlier. Windows 7 is version 6.1. */
- if (!git_has_win32_version(6, 1, 0)) {
- wchar_t *location;
- DWORD location_length;
- int redirect_cmp;
-
- /* OK, fetch the Location header from the redirect. */
- if (WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- WINHTTP_NO_OUTPUT_BUFFER,
- &location_length,
- WINHTTP_NO_HEADER_INDEX) ||
- GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- return -1;
- }
-
- location = git__malloc(location_length);
- GITERR_CHECK_ALLOC(location);
-
- if (!WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- location,
- &location_length,
- WINHTTP_NO_HEADER_INDEX)) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- git__free(location);
- return -1;
- }
+ wchar_t *location;
+ DWORD location_length;
+ char *location8;
+
+ /* OK, fetch the Location header from the redirect. */
+ if (WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ WINHTTP_NO_OUTPUT_BUFFER,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ giterr_set(GITERR_OS, "Failed to read Location header");
+ return -1;
+ }
- /* Compare the Location header with the request URI */
- redirect_cmp = wcscmp(location, s->request_uri);
+ location = git__malloc(location_length);
+ location8 = git__malloc(location_length);
+ GITERR_CHECK_ALLOC(location);
+
+ if (!WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ location,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX)) {
+ giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location);
-
- if (!redirect_cmp) {
- /* Replay the request */
- WinHttpCloseHandle(s->request);
- s->request = NULL;
- s->sent_request = 0;
-
- goto replay;
- }
+ return -1;
+ }
+ git__utf16_to_8(location8, location_length, location);
+ git__free(location);
+
+ /* Replay the request */
+ WinHttpCloseHandle(s->request);
+ s->request = NULL;
+ s->sent_request = 0;
+
+ if (!git__prefixcmp_icase(location8, prefix_https)) {
+ /* Upgrade to secure connection; disconnect and start over */
+ set_connection_data_from_url(t, location8, s->service_url);
+ winhttp_connect(t, location8);
}
+
+ git__free(location8);
+ goto replay;
}
/* Handle authentication failures */
@@ -888,68 +1038,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream
return 0;
}
-static int winhttp_connect(
- winhttp_subtransport *t,
- const char *url)
-{
- wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
- git_win32_path host;
- int32_t port;
- const char *default_port = "80";
- int ret;
-
- if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
- default_port = "80";
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
- &t->pass_from_url, url, default_port)) < 0)
- return ret;
-
- t->path = strchr(url, '/');
-
- /* Prepare port */
- if (git__strtol32(&port, t->port, NULL, 10) < 0)
- return -1;
-
- /* Prepare host */
- git_win32_path_from_c(host, t->host);
-
- /* Establish session */
- t->session = WinHttpOpen(
- ua,
- WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
- WINHTTP_NO_PROXY_NAME,
- WINHTTP_NO_PROXY_BYPASS,
- 0);
-
- if (!t->session) {
- giterr_set(GITERR_OS, "Failed to init WinHTTP");
- return -1;
- }
-
- /* Establish connection */
- t->connection = WinHttpConnect(
- t->session,
- host,
- port,
- 0);
-
- if (!t->connection) {
- giterr_set(GITERR_OS, "Failed to connect to host");
- return -1;
- }
-
- return 0;
-}
-
static int winhttp_uploadpack_ls(
winhttp_subtransport *t,
winhttp_stream *s)
@@ -1014,7 +1102,8 @@ static int winhttp_action(
int ret = -1;
if (!t->connection &&
- winhttp_connect(t, url) < 0)
+ (set_connection_data_from_url(t, url, NULL) < 0 ||
+ winhttp_connect(t, url) < 0))
return -1;
if (winhttp_stream_alloc(t, &s) < 0)
@@ -1056,25 +1145,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0;
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
+ free_connection_data(t);
if (t->cred) {
t->cred->free(t->cred);
diff --git a/src/util.c b/src/util.c
index d0c326ae5..162ed6f70 100644
--- a/src/util.c
+++ b/src/util.c
@@ -117,6 +117,19 @@ int git_libgit2_opts(int key, ...)
*(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
*(va_arg(ap, ssize_t *)) = git_cache__max_storage;
break;
+
+ case GIT_OPT_GET_TEMPLATE_PATH:
+ {
+ char *out = va_arg(ap, char *);
+ size_t outlen = va_arg(ap, size_t);
+
+ error = git_futils_dirs_get_str(out, outlen, GIT_FUTILS_DIR_TEMPLATE);
+ }
+ break;
+
+ case GIT_OPT_SET_TEMPLATE_PATH:
+ error = git_futils_dirs_set(GIT_FUTILS_DIR_TEMPLATE, va_arg(ap, const char *));
+ break;
}
va_end(ap);
@@ -679,6 +692,9 @@ size_t git__unescape(char *str)
{
char *scan, *pos = str;
+ if (!str)
+ return 0;
+
for (scan = str; *scan; pos++, scan++) {
if (*scan == '\\' && *(scan + 1) != '\0')
scan++; /* skip '\' but include next char */
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index a1c11fcfb..a9e812e28 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -86,7 +86,7 @@ static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
return (path != base) ? path : NULL;
}
-static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
+static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
{
wchar_t *env = _wgetenv(L"PATH"), lastch;
struct git_win32__path root;
@@ -110,8 +110,8 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
wcscpy(&root.path[root.len], gitexe);
if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
- /* replace "bin\\" or "cmd\\" with "etc\\" */
- wcscpy(&root.path[root.len - 4], L"etc\\");
+ /* replace "bin\\" or "cmd\\" with subdir */
+ wcscpy(&root.path[root.len - 4], subdir);
win32_path_to_8(buf, root.path);
return 0;
@@ -122,7 +122,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
}
static int win32_find_git_in_registry(
- git_buf *buf, const HKEY hieve, const wchar_t *key)
+ git_buf *buf, const HKEY hieve, const wchar_t *key, const wchar_t *subdir)
{
HKEY hKey;
DWORD dwType = REG_SZ;
@@ -130,9 +130,9 @@ static int win32_find_git_in_registry(
assert(buf);
- path16.len = 0;
+ path16.len = MAX_PATH;
- if (RegOpenKeyExW(hieve, key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
+ if (RegOpenKeyExW(hieve, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType,
(LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS)
{
@@ -143,7 +143,7 @@ static int win32_find_git_in_registry(
return -1;
}
- wcscat(path16.path, L"etc\\");
+ wcscat(path16.path, subdir);
path16.len += 4;
win32_path_to_8(buf, path16.path);
@@ -180,26 +180,26 @@ static int win32_find_existing_dirs(
return (git_buf_oom(out) ? -1 : 0);
}
-int git_win32__find_system_dirs(git_buf *out)
+int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
{
git_buf buf = GIT_BUF_INIT;
/* directories where git.exe & git.cmd are found */
- if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size)
+ if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
git_buf_set(out, buf.ptr, buf.size);
else
git_buf_clear(out);
- if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size)
+ if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
/* directories where git is installed according to registry */
if (!win32_find_git_in_registry(
- &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size)
+ &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
if (!win32_find_git_in_registry(
- &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size)
+ &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
git_buf_free(&buf);
diff --git a/src/win32/findfile.h b/src/win32/findfile.h
index fc79e1b72..11bf7e620 100644
--- a/src/win32/findfile.h
+++ b/src/win32/findfile.h
@@ -19,7 +19,7 @@ extern int git_win32__expand_path(
extern int git_win32__find_file(
git_buf *path, const struct git_win32__path *root, const char *filename);
-extern int git_win32__find_system_dirs(git_buf *out);
+extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
extern int git_win32__find_global_dirs(git_buf *out);
extern int git_win32__find_xdg_dirs(git_buf *out);
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index d50ace695..db8927471 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -6,6 +6,7 @@
*/
#include "pthread.h"
+#include "../global.h"
int pthread_create(
pthread_t *GIT_RESTRICT thread,
@@ -217,6 +218,14 @@ int pthread_rwlock_destroy(pthread_rwlock_t *lock)
}
+static void win32_pthread_shutdown(void)
+{
+ if (win32_kernel32_dll) {
+ FreeLibrary(win32_kernel32_dll);
+ win32_kernel32_dll = NULL;
+ }
+}
+
int win32_pthread_initialize(void)
{
if (win32_kernel32_dll)
@@ -239,15 +248,7 @@ int win32_pthread_initialize(void)
win32_srwlock_release_exclusive = (win32_srwlock_fn)
GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
- return 0;
-}
-
-int win32_pthread_shutdown(void)
-{
- if (win32_kernel32_dll) {
- FreeLibrary(win32_kernel32_dll);
- win32_kernel32_dll = NULL;
- }
+ git__on_shutdown(win32_pthread_shutdown);
return 0;
}
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 2ba2ca552..af5b121f0 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -69,6 +69,5 @@ int pthread_rwlock_wrunlock(pthread_rwlock_t *);
int pthread_rwlock_destroy(pthread_rwlock_t *);
extern int win32_pthread_initialize(void);
-extern int win32_pthread_shutdown(void);
#endif