summaryrefslogtreecommitdiff
path: root/src/attr_file.c
diff options
context:
space:
mode:
authorRussell Belfer <arrbee@arrbee.com>2012-01-16 16:58:27 -0800
committerRussell Belfer <arrbee@arrbee.com>2012-01-16 16:58:27 -0800
commita51cd8e6f6724079a552b75e014f792f3f68e158 (patch)
treefcf526f2a7575e215d95589d1a8e07277981fd18 /src/attr_file.c
parent6e03b12f5715cb3f5cb5c8be6512e041cdf44a05 (diff)
downloadlibgit2-a51cd8e6f6724079a552b75e014f792f3f68e158.tar.gz
Fix handling of relative paths for attrs
Per issue #533, the handling of relative paths in attribute and ignore files was not right. Fixed this by pre-joining the relative path of the attribute/ignore file onto the match string when a full path match is required. Unfortunately, fixing this required a bit more code than I would have liked because I had to juggle things around so that the fnmatch parser would have sufficient information to prepend the relative path when it was needed.
Diffstat (limited to 'src/attr_file.c')
-rw-r--r--src/attr_file.c113
1 files changed, 71 insertions, 42 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index 5ea07c984..f6eaad69d 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -31,21 +31,46 @@ int git_attr_file__new(git_attr_file **attrs_ptr)
return error;
}
+int git_attr_file__set_path(
+ git_repository *repo, const char *path, git_attr_file *file)
+{
+ if (file->path != NULL) {
+ git__free(file->path);
+ file->path = NULL;
+ }
+
+ if (repo == NULL)
+ file->path = git__strdup(path);
+ else {
+ const char *workdir = git_repository_workdir(repo);
+
+ if (workdir && git__prefixcmp(path, workdir) == 0)
+ file->path = git__strdup(path + strlen(workdir));
+ else
+ file->path = git__strdup(path);
+ }
+
+ return (file->path == NULL) ? GIT_ENOMEM : GIT_SUCCESS;
+}
+
int git_attr_file__from_buffer(
- git_repository *repo, const char *buffer, git_attr_file **out)
+ git_repository *repo, const char *buffer, git_attr_file *attrs)
{
int error = GIT_SUCCESS;
- git_attr_file *attrs = NULL;
const char *scan = NULL;
+ char *context = NULL;
git_attr_rule *rule = NULL;
- *out = NULL;
-
- if ((error = git_attr_file__new(&attrs)) < GIT_SUCCESS)
- goto cleanup;
+ assert(buffer && attrs);
scan = buffer;
+ if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) {
+ context = git__strndup(attrs->path,
+ strlen(attrs->path) - strlen(GIT_ATTR_FILE));
+ if (!context) error = GIT_ENOMEM;
+ }
+
while (error == GIT_SUCCESS && *scan) {
/* allocate rule if needed */
if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
@@ -54,7 +79,7 @@ int git_attr_file__from_buffer(
}
/* parse the next "pattern attr attr attr" line */
- if (!(error = git_attr_fnmatch__parse(&rule->match, &scan)) &&
+ if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) &&
!(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
@@ -76,35 +101,30 @@ int git_attr_file__from_buffer(
}
}
-cleanup:
- if (error != GIT_SUCCESS) {
- git_attr_rule__free(rule);
- git_attr_file__free(attrs);
- } else {
- *out = attrs;
- }
+ git_attr_rule__free(rule);
+ git__free(context);
return error;
}
int git_attr_file__from_file(
- git_repository *repo, const char *path, git_attr_file **out)
+ git_repository *repo, const char *path, git_attr_file *file)
{
int error = GIT_SUCCESS;
git_fbuffer fbuf = GIT_FBUFFER_INIT;
- *out = NULL;
+ assert(path && file);
- if ((error = git_futils_readbuffer(&fbuf, path)) < 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 {
- /* save path (okay to fail) */
- (*out)->path = git__strdup(path);
- }
+ if (file->path == NULL)
+ error = git_attr_file__set_path(repo, path, file);
+
+ if (error == GIT_SUCCESS &&
+ (error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
+ error = git_attr_file__from_buffer(repo, fbuf.data, file);
git_futils_freebuffer(&fbuf);
+ if (error != GIT_SUCCESS)
+ git__rethrow(error, "Could not open attribute file '%s'", path);
return error;
}
@@ -267,6 +287,7 @@ int git_attr_path__init(
*/
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ const char *source,
const char **base)
{
const char *pattern, *scan;
@@ -312,32 +333,40 @@ int git_attr_fnmatch__parse(
*base = scan;
spec->length = scan - pattern;
- spec->pattern = git__strndup(pattern, spec->length);
-
- if (!spec->pattern) {
- *base = git__next_line(pattern);
- return GIT_ENOMEM;
- } else {
- /* remove '\' that might have be used for internal whitespace */
- char *from = spec->pattern, *to = spec->pattern;
- while (*from) {
- if (*from == '\\') {
- from++;
- spec->length--;
- }
- *to++ = *from++;
- }
- *to = '\0';
- }
if (pattern[spec->length - 1] == '/') {
spec->length--;
- spec->pattern[spec->length] = '\0';
spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
if (--slash_count <= 0)
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
+ if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
+ source != NULL && git_path_root(pattern) < 0)
+ {
+ size_t sourcelen = strlen(source);
+ /* given an unrooted fullpath match from a file inside a repo,
+ * prefix the pattern with the relative directory of the source file
+ */
+ spec->pattern = git__malloc(sourcelen + spec->length + 1);
+ if (spec->pattern) {
+ memcpy(spec->pattern, source, sourcelen);
+ memcpy(spec->pattern + sourcelen, pattern, spec->length);
+ spec->length += sourcelen;
+ spec->pattern[spec->length] = '\0';
+ }
+ } else {
+ spec->pattern = git__strndup(pattern, spec->length);
+ }
+
+ if (!spec->pattern) {
+ *base = git__next_line(pattern);
+ return GIT_ENOMEM;
+ } else {
+ /* remove '\' that might have be used for internal whitespace */
+ spec->length = git__removechar(spec->pattern, '\\');
+ }
+
return GIT_SUCCESS;
}