summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/attr.c131
-rw-r--r--src/attr.h21
-rw-r--r--src/attr_file.c171
-rw-r--r--src/attr_file.h43
-rw-r--r--src/config.c79
-rw-r--r--src/config.h1
-rw-r--r--src/fileops.c131
-rw-r--r--src/fileops.h24
-rw-r--r--src/repository.c2
-rw-r--r--src/repository.h2
-rw-r--r--src/util.h1
-rw-r--r--src/win32/utf-conv.c5
-rw-r--r--src/win32/utf-conv.h1
13 files changed, 436 insertions, 176 deletions
diff --git a/src/attr.c b/src/attr.c
index d8e7095b1..fac2fa4b9 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -1,19 +1,17 @@
-#include "attr.h"
-#include "buffer.h"
+#include "repository.h"
#include "fileops.h"
#include "config.h"
#include <ctype.h>
#define GIT_ATTR_FILE_INREPO "info/attributes"
#define GIT_ATTR_FILE ".gitattributes"
-#define GIT_ATTR_FILE_SYSTEM "/etc/gitattributes"
-#if GIT_WIN32
-#define GIT_ATTR_FILE_WIN32 L"%PROGRAMFILES%\\Git\\etc\\gitattributes"
-#endif
+#define GIT_ATTR_FILE_SYSTEM "gitattributes"
static int collect_attr_files(
git_repository *repo, const char *path, git_vector *files);
+static int attr_cache_init(git_repository *repo);
+
int git_attr_get(
git_repository *repo, const char *pathname,
@@ -180,6 +178,44 @@ cleanup:
}
+int git_attr_add_macro(
+ git_repository *repo,
+ const char *name,
+ const char *values)
+{
+ int error;
+ git_attr_rule *macro = NULL;
+
+ if ((error = attr_cache_init(repo)) < GIT_SUCCESS)
+ return error;
+
+ macro = git__calloc(1, sizeof(git_attr_rule));
+ if (!macro)
+ return GIT_ENOMEM;
+
+ macro->match.pattern = git__strdup(name);
+ if (!macro->match.pattern) {
+ git__free(macro);
+ return GIT_ENOMEM;
+ }
+
+ macro->match.length = strlen(macro->match.pattern);
+ macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
+
+ error = git_attr_assignment__parse(repo, &macro->assigns, &values);
+
+ if (error == GIT_SUCCESS)
+ error = git_attr_cache__insert_macro(repo, macro);
+
+ if (error < GIT_SUCCESS) {
+ git_attr_rule__free(macro);
+ git__free(macro);
+ }
+
+ return error;
+}
+
+
/* add git_attr_file to vector of files, loading if needed */
static int push_attrs(
git_repository *repo,
@@ -193,13 +229,6 @@ static int push_attrs(
git_attr_file *file;
int add_to_cache = 0;
- if (cache->files == NULL) {
- cache->files = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->files)
- return git__throw(GIT_ENOMEM, "Could not create attribute cache");
- }
-
if ((error = git_path_prettify(&path, filename, base)) < GIT_SUCCESS) {
if (error == GIT_EOSERR)
/* file was not found -- ignore error */
@@ -210,7 +239,7 @@ static int push_attrs(
/* either get attr_file from cache or read from disk */
file = git_hashtable_lookup(cache->files, path.ptr);
if (file == NULL) {
- error = git_attr_file__from_file(&file, path.ptr);
+ error = git_attr_file__from_file(repo, path.ptr, &file);
add_to_cache = (error == GIT_SUCCESS);
}
@@ -238,6 +267,9 @@ static int collect_attr_files(
git_config *cfg;
const char *workdir = git_repository_workdir(repo);
+ if ((error = attr_cache_init(repo)) < GIT_SUCCESS)
+ goto cleanup;
+
if ((error = git_vector_init(files, 4, NULL)) < GIT_SUCCESS)
goto cleanup;
@@ -288,8 +320,13 @@ static int collect_attr_files(
git_config_free(cfg);
}
- if (error == GIT_SUCCESS)
- error = push_attrs(repo, files, NULL, GIT_ATTR_FILE_SYSTEM);
+ if (error == GIT_SUCCESS) {
+ error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+ if (error == GIT_SUCCESS)
+ error = push_attrs(repo, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
+ error = GIT_SUCCESS;
+ }
cleanup:
if (error < GIT_SUCCESS) {
@@ -302,10 +339,64 @@ static int collect_attr_files(
}
-void git_repository__attr_cache_free(git_attr_cache *attrs)
+static int attr_cache_init(git_repository *repo)
{
- if (attrs && attrs->files) {
- git_hashtable_free(attrs->files);
- attrs->files = NULL;
+ int error = GIT_SUCCESS;
+ git_attr_cache *cache = &repo->attrcache;
+
+ if (cache->initialized)
+ return GIT_SUCCESS;
+
+ if (cache->files == NULL) {
+ cache->files = git_hashtable_alloc(
+ 8, git_hash__strhash_cb, git_hash__strcmp_cb);
+ if (!cache->files)
+ return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
+ }
+
+ if (cache->macros == NULL) {
+ cache->macros = git_hashtable_alloc(
+ 8, git_hash__strhash_cb, git_hash__strcmp_cb);
+ if (!cache->macros)
+ return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
}
+
+ cache->initialized = 1;
+
+ /* insert default macros */
+ error = git_attr_add_macro(repo, "binary", "-diff -crlf");
+
+ return error;
+}
+
+
+void git_attr_cache_flush(
+ git_repository *repo)
+{
+ if (!repo)
+ return;
+
+ if (repo->attrcache.files) {
+ const void *GIT_UNUSED(name);
+ git_attr_file *file;
+
+ GIT_HASHTABLE_FOREACH(repo->attrcache.files, name, file,
+ git_attr_file__free(file));
+
+ git_hashtable_free(repo->attrcache.files);
+ repo->attrcache.files = NULL;
+ }
+
+ if (repo->attrcache.macros) {
+ const void *GIT_UNUSED(name);
+ git_attr_rule *rule;
+
+ GIT_HASHTABLE_FOREACH(repo->attrcache.macros, name, rule,
+ git_attr_rule__free(rule));
+
+ git_hashtable_free(repo->attrcache.macros);
+ repo->attrcache.macros = NULL;
+ }
+
+ repo->attrcache.initialized = 0;
}
diff --git a/src/attr.h b/src/attr.h
deleted file mode 100644
index 518fb9d3b..000000000
--- a/src/attr.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2009-2011 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_attr_h__
-#define INCLUDE_attr_h__
-
-#include "hashtable.h"
-#include "attr_file.h"
-
-/* EXPORT */
-typedef struct {
- git_hashtable *files; /* hash path to git_attr_file */
-} git_attr_cache;
-
-extern void git_repository__attr_cache_free(git_attr_cache *attrs);
-
-#endif
-
diff --git a/src/attr_file.c b/src/attr_file.c
index 5d159db00..0b1eb1f67 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -1,17 +1,33 @@
#include "common.h"
-#include "attr_file.h"
+#include "repository.h"
#include "filebuf.h"
#include <ctype.h>
const char *git_attr__true = "[internal]__TRUE__";
const char *git_attr__false = "[internal]__FALSE__";
-static int parse_fnmatch(git_attr_fnmatch *spec, const char **base);
-static int parse_assigns(git_vector *assigns, const char **base);
-static int free_rule(git_attr_rule *rule);
+static int git_attr_fnmatch__parse(git_attr_fnmatch *spec, const char **base);
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
-int git_attr_file__from_buffer(git_attr_file **out, const char *buffer)
+int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
+{
+ unsigned int i;
+ git_attr_assignment *assign;
+
+ if (macro->assigns.length == 0)
+ return git__throw(GIT_EMISSINGOBJDATA, "git attribute macro with no values");
+
+ git_vector_foreach(&macro->assigns, i, assign) {
+ GIT_REFCOUNT_OWN(assign, macro);
+ GIT_REFCOUNT_INC(assign);
+ }
+
+ return git_hashtable_insert(
+ repo->attrcache.macros, macro->match.pattern, macro);
+}
+
+int git_attr_file__from_buffer(
+ git_repository *repo, const char *buffer, git_attr_file **out)
{
int error = GIT_SUCCESS;
git_attr_file *attrs = NULL;
@@ -42,13 +58,21 @@ int git_attr_file__from_buffer(git_attr_file **out, const char *buffer)
}
/* parse the next "pattern attr attr attr" line */
- if (!(error = parse_fnmatch(&rule->match, &scan)) &&
- !(error = parse_assigns(&rule->assigns, &scan)))
- error = git_vector_insert(&attrs->rules, rule);
+ if (!(error = git_attr_fnmatch__parse(&rule->match, &scan)) &&
+ !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
+ {
+ if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
+ /* should generate error/warning if this is coming from any
+ * file other than .gitattributes at repo root.
+ */
+ error = git_attr_cache__insert_macro(repo, rule);
+ else
+ error = git_vector_insert(&attrs->rules, rule);
+ }
/* if the rule wasn't a pattern, on to the next */
if (error != GIT_SUCCESS) {
- free_rule(rule); /* release anything partially allocated */
+ git_attr_rule__free(rule); /* free anything partially allocated */
if (error == GIT_ENOTFOUND)
error = GIT_SUCCESS;
} else {
@@ -58,6 +82,7 @@ int git_attr_file__from_buffer(git_attr_file **out, const char *buffer)
cleanup:
if (error != GIT_SUCCESS) {
+ git__free(rule);
git_attr_file__free(attrs);
git__free(attrs);
} else {
@@ -67,7 +92,8 @@ cleanup:
return error;
}
-int git_attr_file__from_file(git_attr_file **out, const char *path)
+int git_attr_file__from_file(
+ git_repository *repo, const char *path, git_attr_file **out)
{
int error = GIT_SUCCESS;
git_fbuffer fbuf = GIT_FBUFFER_INIT;
@@ -75,7 +101,7 @@ int git_attr_file__from_file(git_attr_file **out, const char *path)
*out = NULL;
if ((error = git_futils_readbuffer(&fbuf, path)) < GIT_SUCCESS ||
- (error = git_attr_file__from_buffer(out, fbuf.data)) < GIT_SUCCESS)
+ (error = git_attr_file__from_buffer(repo, fbuf.data, out)) < GIT_SUCCESS)
{
git__rethrow(error, "Could not open attribute file '%s'", path);
} else {
@@ -97,7 +123,7 @@ void git_attr_file__free(git_attr_file *file)
return;
git_vector_foreach(&file->rules, i, rule) {
- free_rule(rule);
+ git_attr_rule__free(rule);
}
git_vector_free(&file->rules);
@@ -153,15 +179,15 @@ int git_attr_rule__match_path(
{
int matched = FNM_NOMATCH;
- if (rule->match.directory && !path->is_dir)
+ if (rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
return matched;
- if (rule->match.fullpath)
+ if (rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH)
matched = p_fnmatch(rule->match.pattern, path->path, FNM_PATHNAME);
else
matched = p_fnmatch(rule->match.pattern, path->basename, 0);
- if (rule->match.negative)
+ if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
matched = (matched == GIT_SUCCESS) ? FNM_NOMATCH : GIT_SUCCESS;
return matched;
@@ -232,7 +258,7 @@ int git_attr_path__init(
* GIT_ENOTFOUND if the fnmatch does not require matching, or
* another error code there was an actual problem.
*/
-static int parse_fnmatch(
+static int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
const char **base)
{
@@ -251,21 +277,31 @@ static int parse_fnmatch(
goto skip_to_eol;
}
+ spec->flags = 0;
+
+ if (*pattern == '[') {
+ if (strncmp(pattern, "[attr]", 6) == 0) {
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
+ pattern += 6;
+ } else {
+ /* unrecognized meta instructions - skip the line */
+ error = GIT_ENOTFOUND;
+ goto skip_to_eol;
+ }
+ }
+
if (*pattern == '!') {
- spec->negative = 1;
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
pattern++;
- } else {
- spec->negative = 0;
}
- spec->fullpath = 0;
slash_count = 0;
for (scan = pattern; *scan != '\0'; ++scan) {
if (isspace(*scan) && *(scan - 1) != '\\')
break;
if (*scan == '/') {
- spec->fullpath = 1;
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
slash_count++;
}
}
@@ -292,11 +328,9 @@ static int parse_fnmatch(
if (pattern[spec->length - 1] == '/') {
spec->length--;
spec->pattern[spec->length] = '\0';
- spec->directory = 1;
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
if (--slash_count <= 0)
- spec->fullpath = 0;
- } else {
- spec->directory = 0;
+ spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
return GIT_SUCCESS;
@@ -323,7 +357,21 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
return strcmp(b->name, a->name);
}
-static int parse_assigns(
+static void free_assign(git_attr_assignment *assign)
+{
+ git__free(assign->name);
+ assign->name = NULL;
+
+ if (assign->is_allocated) {
+ git__free((void *)assign->value);
+ assign->value = NULL;
+ }
+
+ git__free(assign);
+}
+
+int git_attr_assignment__parse(
+ git_repository *repo,
git_vector *assigns,
const char **base)
{
@@ -333,7 +381,7 @@ static int parse_assigns(
assert(assigns && !assigns->length);
- while (*scan && *scan != '\n') {
+ while (*scan && *scan != '\n' && error == GIT_SUCCESS) {
const char *name_start, *value_start;
/* skip leading blanks */
@@ -369,8 +417,7 @@ static int parse_assigns(
((assign->name_hash << 5) + assign->name_hash) + *scan;
scan++;
}
- assign->name_len = scan - name_start;
- if (assign->name_len <= 0) {
+ if (scan == name_start) {
/* must have found lone prefix (" - ") or leading = ("=foo")
* or end of buffer -- advance until whitespace and continue
*/
@@ -378,6 +425,13 @@ static int parse_assigns(
continue;
}
+ /* allocate permanent storage for name */
+ assign->name = git__strndup(name_start, scan - name_start);
+ if (!assign->name) {
+ error = GIT_ENOMEM;
+ break;
+ }
+
/* if there is an equals sign, find the value */
if (*scan == '=') {
for (value_start = ++scan; *scan && !isspace(*scan); ++scan);
@@ -394,11 +448,34 @@ static int parse_assigns(
}
}
- /* allocate permanent storage for name */
- assign->name = git__strndup(name_start, assign->name_len);
- if (!assign->name) {
- error = GIT_ENOMEM;
- break;
+ /* expand macros (if given a repo) */
+ if (repo != NULL) {
+ git_attr_rule *macro =
+ git_hashtable_lookup(repo->attrcache.macros, assign->name);
+
+ if (macro != NULL) {
+ unsigned int i;
+ git_attr_assignment *massign;
+
+ /* issue warning: if assign->value != GIT_ATTR_TRUE */
+
+ git__free(assign->name);
+ assign->name = NULL;
+ if (assign->is_allocated) {
+ git__free((void *)assign->value);
+ assign->value = NULL;
+ }
+
+ git_vector_foreach(&macro->assigns, i, massign) {
+ error = git_vector_insert(assigns, massign);
+ if (error != GIT_SUCCESS)
+ break;
+ GIT_REFCOUNT_INC(&massign->rc);
+ }
+
+ /* continue to next assignment */
+ continue;
+ }
}
/* insert allocated assign into vector */
@@ -417,40 +494,34 @@ static int parse_assigns(
git_vector_sort(assigns);
}
- if (assign != NULL) {
- git__free(assign->name);
- if (assign->is_allocated)
- git__free((void *)assign->value);
- git__free(assign);
- }
+ if (assign != NULL)
+ free_assign(assign);
while (*scan && *scan != '\n') scan++;
+ if (*scan == '\n') scan++;
+
*base = scan;
return error;
}
-static int free_rule(git_attr_rule *rule)
+void git_attr_rule__free(git_attr_rule *rule)
{
unsigned int i;
git_attr_assignment *assign;
if (!rule)
- return GIT_SUCCESS;
+ return;
git__free(rule->match.pattern);
rule->match.pattern = NULL;
rule->match.length = 0;
git_vector_foreach(&rule->assigns, i, assign) {
- git__free(assign->name);
- assign->name = NULL;
-
- if (assign->is_allocated) {
- git__free((void *)assign->value);
- assign->value = NULL;
- }
+ if (GIT_REFCOUNT_OWNER(assign) == rule)
+ GIT_REFCOUNT_OWN(assign, NULL);
+ GIT_REFCOUNT_DEC(assign, free_assign);
}
- return GIT_SUCCESS;
+ git_vector_free(&rule->assigns);
}
diff --git a/src/attr_file.h b/src/attr_file.h
index 4774f148c..bed440d61 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -9,36 +9,41 @@
#include "git2/attr.h"
#include "vector.h"
+#include "hashtable.h"
+
+#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
+#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
+#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
+#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
typedef struct {
char *pattern;
size_t length;
- int negative;
- int directory;
- int fullpath;
+ unsigned int flags;
} git_attr_fnmatch;
typedef struct {
+ git_refcount unused;
const char *name;
- unsigned long name_hash;
+ unsigned long name_hash;
} git_attr_name;
typedef struct {
+ git_refcount rc; /* for macros */
char *name;
unsigned long name_hash;
- size_t name_len;
const char *value;
int is_allocated;
} git_attr_assignment;
typedef struct {
git_attr_fnmatch match;
- git_vector assigns; /* <git_attr_assignment*> */
+ git_vector assigns; /* vector of <git_attr_assignment*> */
} git_attr_rule;
typedef struct {
- char *path;
- git_vector rules; /* <git_attr_rule*> */
+ char *path; /* cache the path this was loaded from */
+ git_vector rules; /* vector of <git_attr_rule*> */
} git_attr_file;
typedef struct {
@@ -47,12 +52,20 @@ typedef struct {
int is_dir;
} git_attr_path;
+typedef struct {
+ int initialized;
+ git_hashtable *files; /* hash path to git_attr_file */
+ git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
+} git_attr_cache;
+
/*
* git_attr_file API
*/
-extern int git_attr_file__from_buffer(git_attr_file **out, const char *buf);
-extern int git_attr_file__from_file(git_attr_file **out, const char *path);
+extern int git_attr_file__from_buffer(
+ git_repository *repo, const char *buf, git_attr_file **out);
+extern int git_attr_file__from_file(
+ git_repository *repo, const char *path, git_attr_file **out);
extern void git_attr_file__free(git_attr_file *file);
@@ -74,6 +87,8 @@ extern unsigned long git_attr_file__name_hash(const char *name);
* other utilities
*/
+extern void git_attr_rule__free(git_attr_rule *rule);
+
extern int git_attr_rule__match_path(
git_attr_rule *rule,
const git_attr_path *path);
@@ -84,4 +99,12 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
extern int git_attr_path__init(
git_attr_path *info, const char *path);
+extern int git_attr_assignment__parse(
+ git_repository *repo, /* needed to expand macros */
+ git_vector *assigns,
+ const char **scan);
+
+extern int git_attr_cache__insert_macro(
+ git_repository *repo, git_attr_rule *macro);
+
#endif
diff --git a/src/config.c b/src/config.c
index ed7c947ed..29f1ee27c 100644
--- a/src/config.c
+++ b/src/config.c
@@ -337,6 +337,11 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out)
return git__throw(error, "Config value '%s' not found", name);
}
+int git_config_find_global_r(git_buf *path)
+{
+ return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+}
+
int git_config_find_global(char *global_config_path)
{
git_buf path = GIT_BUF_INIT;
@@ -354,79 +359,9 @@ int git_config_find_global(char *global_config_path)
return error;
}
-int git_config_find_global_r(git_buf *path)
+int git_config_find_system_r(git_buf *path)
{
- int error;
- const char *home = getenv("HOME");
-
-#ifdef GIT_WIN32
- if (home == NULL)
- home = getenv("USERPROFILE");
-#endif
-
- if (home == NULL)
- return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");
-
- if ((error = git_buf_joinpath(path, home, GIT_CONFIG_FILENAME)) < GIT_SUCCESS)
- return error;
-
- if (git_futils_exists(path->ptr) < GIT_SUCCESS) {
- git_buf_clear(path);
- return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");
- }
-
- return GIT_SUCCESS;
-}
-
-
-
-#if GIT_WIN32
-static int win32_find_system(git_buf *system_config_path)
-{
- const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig";
- wchar_t *apphome_utf16;
- char *apphome_utf8;
- DWORD size, ret;
-
- size = ExpandEnvironmentStringsW(query, NULL, 0);
- /* The function gave us the full size of the buffer in chars, including NUL */
- apphome_utf16 = git__malloc(size * sizeof(wchar_t));
- if (apphome_utf16 == NULL)
- return GIT_ENOMEM;
-
- ret = ExpandEnvironmentStringsW(query, apphome_utf16, size);
- if (ret != size)
- return git__throw(GIT_ERROR, "Failed to expand environment strings");
-
- if (_waccess(apphome_utf16, F_OK) < 0) {
- git__free(apphome_utf16);
- return GIT_ENOTFOUND;
- }
-
- apphome_utf8 = gitwin_from_utf16(apphome_utf16);
- git__free(apphome_utf16);
-
- git_buf_attach(system_config_path, apphome_utf8, 0);
-
- return GIT_SUCCESS;
-}
-#endif
-
-int git_config_find_system_r(git_buf *system_config_path)
-{
- if (git_buf_sets(system_config_path, "/etc/gitconfig") < GIT_SUCCESS)
- return git_buf_lasterror(system_config_path);
-
- if (git_futils_exists(system_config_path->ptr) == GIT_SUCCESS)
- return GIT_SUCCESS;
-
- git_buf_clear(system_config_path);
-
-#if GIT_WIN32
- return win32_find_system(system_config_path);
-#else
- return GIT_ENOTFOUND;
-#endif
+ return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
int git_config_find_system(char *system_config_path)
diff --git a/src/config.h b/src/config.h
index fc639c6d4..6345b0a5d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -14,6 +14,7 @@
#define GIT_CONFIG_FILENAME ".gitconfig"
#define GIT_CONFIG_FILENAME_INREPO "config"
+#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
#define GIT_CONFIG_FILE_MODE 0666
struct git_config {
diff --git a/src/fileops.c b/src/fileops.c
index fb2f954d7..5eb7bf6ec 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -403,3 +403,134 @@ int git_futils_contains_file(git_buf *base, const char *file, int append_if_exis
return _check_dir_contents(base, file, append_if_exists, &git_futils_isfile);
}
+int git_futils_find_global_file(git_buf *path, const char *filename)
+{
+ int error;
+ const char *home = getenv("HOME");
+
+#ifdef GIT_WIN32
+ if (home == NULL)
+ home = getenv("USERPROFILE");
+#endif
+
+ if (home == NULL)
+ return git__throw(GIT_EOSERR, "Failed to open global %s file. "
+ "Cannot locate the user's home directory.", filename);
+
+ if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS)
+ return error;
+
+ if (git_futils_exists(path->ptr) < GIT_SUCCESS) {
+ git_buf_clear(path);
+ return GIT_ENOTFOUND;
+ }
+
+ return GIT_SUCCESS;
+}
+
+#ifdef GIT_WIN32
+typedef struct {
+ wchar_t *path;
+ DWORD len;
+} win32_path;
+
+static const win32_path *win32_system_root(void)
+{
+ static win32_path s_root = { 0, 0 };
+
+ if (s_root.path == NULL) {
+ const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
+
+ s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
+
+ if (s_root.len <= 0) {
+ git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ return NULL;
+ }
+
+ s_root.path = git__calloc(s_root.len, sizeof(wchar_t));
+ if (s_root.path == NULL)
+ return NULL;
+
+ if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
+ git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ git__free(s_root.path);
+ s_root.path = NULL;
+ return NULL;
+ }
+ }
+
+ return &s_root;
+}
+
+static int win32_find_system_file(git_buf *path, const char *filename)
+{
+ int error = GIT_SUCCESS;
+ const win32_path *root = win32_system_root();
+ size_t len;
+ wchar_t *file_utf16 = NULL, *scan;
+ char *file_utf8 = NULL;
+
+ if (!root || !filename || (len = strlen(filename)) == 0)
+ return GIT_ENOTFOUND;
+
+ /* allocate space for wchar_t path to file */
+ file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
+ if (!file_utf16)
+ return GIT_ENOMEM;
+
+ /* append root + '\\' + filename as wchar_t */
+ memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
+
+ if (*filename == '/' || *filename == '\\')
+ filename++;
+
+ if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
+ (int)len) {
+ error = git__throw(GIT_EOSERR, "Failed to build file path");
+ goto cleanup;
+ }
+
+ for (scan = file_utf16; *scan; scan++)
+ if (*scan == L'/')
+ *scan = L'\\';
+
+ /* check access */
+ if (_waccess(file_utf16, F_OK) < 0) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ /* convert to utf8 */
+ if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
+ error = GIT_ENOMEM;
+
+ if (file_utf8) {
+ git_path_mkposix(file_utf8);
+ git_buf_attach(path, file_utf8, 0);
+ }
+
+cleanup:
+ git__free(file_utf16);
+
+ return error;
+}
+#endif
+
+int git_futils_find_system_file(git_buf *path, const char *filename)
+{
+ if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS)
+ return git_buf_lasterror(path);
+
+ if (git_futils_exists(path->ptr) == GIT_SUCCESS)
+ return GIT_SUCCESS;
+
+ git_buf_clear(path);
+
+#ifdef GIT_WIN32
+ return win32_find_system_file(path, filename);
+#else
+ return GIT_ENOTFOUND;
+#endif
+}
+
diff --git a/src/fileops.h b/src/fileops.h
index df135d0db..31f3e6a91 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -165,4 +165,28 @@ extern int git_futils_direach(
extern int git_futils_cmp_path(const char *name1, int len1, int isdir1,
const char *name2, int len2, int isdir2);
+/**
+ * Find a "global" file (i.e. one in a user's home directory).
+ *
+ * @param pathbuf buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return
+ * - GIT_SUCCESS if found;
+ * - GIT_ENOTFOUND if not found;
+ * - GIT_EOSERR on an unspecified OS related error.
+ */
+extern int git_futils_find_global_file(git_buf *path, const char *filename);
+
+/**
+ * Find a "system" file (i.e. one shared for all users of the system).
+ *
+ * @param pathbuf buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return
+ * - GIT_SUCCESS if found;
+ * - GIT_ENOTFOUND if not found;
+ * - GIT_EOSERR on an unspecified OS related error.
+ */
+extern int git_futils_find_system_file(git_buf *path, const char *filename);
+
#endif /* INCLUDE_fileops_h__ */
diff --git a/src/repository.c b/src/repository.c
index e0d4c6387..a94ecce55 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -59,7 +59,7 @@ void git_repository_free(git_repository *repo)
git_cache_free(&repo->objects);
git_repository__refcache_free(&repo->references);
- git_repository__attr_cache_free(&repo->attrcache);
+ git_attr_cache_flush(repo);
git__free(repo->path_repository);
git__free(repo->workdir);
diff --git a/src/repository.h b/src/repository.h
index 5274fc1d0..82052158a 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -19,7 +19,7 @@
#include "refs.h"
#include "buffer.h"
#include "odb.h"
-#include "attr.h"
+#include "attr_file.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
diff --git a/src/util.h b/src/util.h
index be978a6a5..4b1104b7b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -109,7 +109,6 @@ extern void **git__bsearch(const void *key, void **base, size_t nmemb,
int (*compar)(const void *, const void *));
extern int git__strcmp_cb(const void *a, const void *b);
-extern uint32_t git__strhash_cb(const void *key, int hash_id);
typedef struct {
short refcount;
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index b41c78f92..b1b838eb7 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -57,6 +57,11 @@ wchar_t* gitwin_to_utf16(const char* str)
return ret;
}
+int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
+{
+ return MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, len);
+}
+
char* gitwin_from_utf16(const wchar_t* str)
{
char* ret;
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index da03e3385..bbb5c4f69 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -11,6 +11,7 @@
#define INCLUDE_git_utfconv_h__
wchar_t* gitwin_to_utf16(const char* str);
+int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
char* gitwin_from_utf16(const wchar_t* str);
#endif