diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/attr.c | 131 | ||||
| -rw-r--r-- | src/attr.h | 21 | ||||
| -rw-r--r-- | src/attr_file.c | 171 | ||||
| -rw-r--r-- | src/attr_file.h | 43 | ||||
| -rw-r--r-- | src/config.c | 79 | ||||
| -rw-r--r-- | src/config.h | 1 | ||||
| -rw-r--r-- | src/fileops.c | 131 | ||||
| -rw-r--r-- | src/fileops.h | 24 | ||||
| -rw-r--r-- | src/repository.c | 2 | ||||
| -rw-r--r-- | src/repository.h | 2 | ||||
| -rw-r--r-- | src/util.h | 1 | ||||
| -rw-r--r-- | src/win32/utf-conv.c | 5 | ||||
| -rw-r--r-- | src/win32/utf-conv.h | 1 |
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, ¯o->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(¯o->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(¯o->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 |
