| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* Copyright (C) the libgit2 contributors. All rights reserved. |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with |
|
5
|
|
|
|
|
|
|
* a Linking Exception. For full terms see the included COPYING file. |
|
6
|
|
|
|
|
|
|
*/ |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#include "attr_file.h" |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#include "repository.h" |
|
11
|
|
|
|
|
|
|
#include "filebuf.h" |
|
12
|
|
|
|
|
|
|
#include "attrcache.h" |
|
13
|
|
|
|
|
|
|
#include "git2/blob.h" |
|
14
|
|
|
|
|
|
|
#include "git2/tree.h" |
|
15
|
|
|
|
|
|
|
#include "blob.h" |
|
16
|
|
|
|
|
|
|
#include "index.h" |
|
17
|
|
|
|
|
|
|
#include "wildmatch.h" |
|
18
|
|
|
|
|
|
|
#include |
|
19
|
|
|
|
|
|
|
|
|
20
|
2426
|
|
|
|
|
|
static void attr_file_free(git_attr_file *file) |
|
21
|
|
|
|
|
|
|
{ |
|
22
|
2426
|
|
|
|
|
|
bool unlock = !git_mutex_lock(&file->lock); |
|
23
|
2426
|
|
|
|
|
|
git_attr_file__clear_rules(file, false); |
|
24
|
2426
|
|
|
|
|
|
git_pool_clear(&file->pool); |
|
25
|
2426
|
50
|
|
|
|
|
if (unlock) |
|
26
|
2426
|
|
|
|
|
|
git_mutex_unlock(&file->lock); |
|
27
|
2426
|
|
|
|
|
|
git_mutex_free(&file->lock); |
|
28
|
|
|
|
|
|
|
|
|
29
|
2426
|
|
|
|
|
|
git__memzero(file, sizeof(*file)); |
|
30
|
2426
|
|
|
|
|
|
git__free(file); |
|
31
|
2426
|
|
|
|
|
|
} |
|
32
|
|
|
|
|
|
|
|
|
33
|
2426
|
|
|
|
|
|
int git_attr_file__new( |
|
34
|
|
|
|
|
|
|
git_attr_file **out, |
|
35
|
|
|
|
|
|
|
git_attr_file_entry *entry, |
|
36
|
|
|
|
|
|
|
git_attr_file_source *source) |
|
37
|
|
|
|
|
|
|
{ |
|
38
|
2426
|
|
|
|
|
|
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file)); |
|
39
|
2426
|
50
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(attrs); |
|
40
|
|
|
|
|
|
|
|
|
41
|
2426
|
50
|
|
|
|
|
if (git_mutex_init(&attrs->lock) < 0) { |
|
42
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to initialize lock"); |
|
43
|
0
|
|
|
|
|
|
goto on_error; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
2426
|
50
|
|
|
|
|
if (git_pool_init(&attrs->pool, 1) < 0) |
|
47
|
0
|
|
|
|
|
|
goto on_error; |
|
48
|
|
|
|
|
|
|
|
|
49
|
2426
|
|
|
|
|
|
GIT_REFCOUNT_INC(attrs); |
|
50
|
2426
|
|
|
|
|
|
attrs->entry = entry; |
|
51
|
2426
|
|
|
|
|
|
memcpy(&attrs->source, source, sizeof(git_attr_file_source)); |
|
52
|
2426
|
|
|
|
|
|
*out = attrs; |
|
53
|
2426
|
|
|
|
|
|
return 0; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
on_error: |
|
56
|
0
|
|
|
|
|
|
git__free(attrs); |
|
57
|
0
|
|
|
|
|
|
return -1; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
2426
|
|
|
|
|
|
int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) |
|
61
|
|
|
|
|
|
|
{ |
|
62
|
|
|
|
|
|
|
unsigned int i; |
|
63
|
|
|
|
|
|
|
git_attr_rule *rule; |
|
64
|
|
|
|
|
|
|
|
|
65
|
2426
|
50
|
|
|
|
|
if (need_lock && git_mutex_lock(&file->lock) < 0) { |
|
|
|
0
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to lock attribute file"); |
|
67
|
0
|
|
|
|
|
|
return -1; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
2470
|
100
|
|
|
|
|
git_vector_foreach(&file->rules, i, rule) |
|
71
|
44
|
|
|
|
|
|
git_attr_rule__free(rule); |
|
72
|
2426
|
|
|
|
|
|
git_vector_free(&file->rules); |
|
73
|
|
|
|
|
|
|
|
|
74
|
2426
|
50
|
|
|
|
|
if (need_lock) |
|
75
|
0
|
|
|
|
|
|
git_mutex_unlock(&file->lock); |
|
76
|
|
|
|
|
|
|
|
|
77
|
2426
|
|
|
|
|
|
return 0; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
8544
|
|
|
|
|
|
void git_attr_file__free(git_attr_file *file) |
|
81
|
|
|
|
|
|
|
{ |
|
82
|
8544
|
100
|
|
|
|
|
if (!file) |
|
83
|
649
|
|
|
|
|
|
return; |
|
84
|
7895
|
100
|
|
|
|
|
GIT_REFCOUNT_DEC(file, attr_file_free); |
|
|
|
50
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
|
|
87
|
1272
|
|
|
|
|
|
static int attr_file_oid_from_index( |
|
88
|
|
|
|
|
|
|
git_oid *oid, git_repository *repo, const char *path) |
|
89
|
|
|
|
|
|
|
{ |
|
90
|
|
|
|
|
|
|
int error; |
|
91
|
|
|
|
|
|
|
git_index *idx; |
|
92
|
|
|
|
|
|
|
size_t pos; |
|
93
|
|
|
|
|
|
|
const git_index_entry *entry; |
|
94
|
|
|
|
|
|
|
|
|
95
|
1272
|
50
|
|
|
|
|
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || |
|
|
|
50
|
|
|
|
|
|
|
96
|
1272
|
|
|
|
|
|
(error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0) |
|
97
|
1272
|
|
|
|
|
|
return error; |
|
98
|
|
|
|
|
|
|
|
|
99
|
0
|
0
|
|
|
|
|
if (!(entry = git_index_get_byindex(idx, pos))) |
|
100
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
|
101
|
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
|
*oid = entry->id; |
|
103
|
1272
|
|
|
|
|
|
return 0; |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
3698
|
|
|
|
|
|
int git_attr_file__load( |
|
107
|
|
|
|
|
|
|
git_attr_file **out, |
|
108
|
|
|
|
|
|
|
git_repository *repo, |
|
109
|
|
|
|
|
|
|
git_attr_session *attr_session, |
|
110
|
|
|
|
|
|
|
git_attr_file_entry *entry, |
|
111
|
|
|
|
|
|
|
git_attr_file_source *source, |
|
112
|
|
|
|
|
|
|
git_attr_file_parser parser, |
|
113
|
|
|
|
|
|
|
bool allow_macros) |
|
114
|
|
|
|
|
|
|
{ |
|
115
|
3698
|
|
|
|
|
|
int error = 0; |
|
116
|
3698
|
|
|
|
|
|
git_commit *commit = NULL; |
|
117
|
3698
|
|
|
|
|
|
git_tree *tree = NULL; |
|
118
|
3698
|
|
|
|
|
|
git_tree_entry *tree_entry = NULL; |
|
119
|
3698
|
|
|
|
|
|
git_blob *blob = NULL; |
|
120
|
3698
|
|
|
|
|
|
git_str content = GIT_STR_INIT; |
|
121
|
|
|
|
|
|
|
const char *content_str; |
|
122
|
|
|
|
|
|
|
git_attr_file *file; |
|
123
|
|
|
|
|
|
|
struct stat st; |
|
124
|
3698
|
|
|
|
|
|
bool nonexistent = false; |
|
125
|
|
|
|
|
|
|
int bom_offset; |
|
126
|
|
|
|
|
|
|
git_str_bom_t bom; |
|
127
|
|
|
|
|
|
|
git_oid id; |
|
128
|
|
|
|
|
|
|
git_object_size_t blobsize; |
|
129
|
|
|
|
|
|
|
|
|
130
|
3698
|
|
|
|
|
|
*out = NULL; |
|
131
|
|
|
|
|
|
|
|
|
132
|
3698
|
|
|
|
|
|
switch (source->type) { |
|
133
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_MEMORY: |
|
134
|
|
|
|
|
|
|
/* in-memory attribute file doesn't need data */ |
|
135
|
14
|
|
|
|
|
|
break; |
|
136
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_INDEX: { |
|
137
|
1272
|
50
|
|
|
|
|
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || |
|
|
|
0
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
(error = git_blob_lookup(&blob, repo, &id)) < 0) |
|
139
|
1272
|
|
|
|
|
|
return error; |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
/* Do not assume that data straight from the ODB is NULL-terminated; |
|
142
|
|
|
|
|
|
|
* copy the contents of a file to a buffer to work on */ |
|
143
|
0
|
|
|
|
|
|
blobsize = git_blob_rawsize(blob); |
|
144
|
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_BLOBSIZE(blobsize); |
|
146
|
0
|
|
|
|
|
|
git_str_put(&content, git_blob_rawcontent(blob), (size_t)blobsize); |
|
147
|
0
|
|
|
|
|
|
break; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_FILE: { |
|
150
|
2412
|
|
|
|
|
|
int fd = -1; |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
/* For open or read errors, pretend that we got ENOTFOUND. */ |
|
153
|
|
|
|
|
|
|
/* TODO: issue warning when warning API is available */ |
|
154
|
|
|
|
|
|
|
|
|
155
|
2412
|
100
|
|
|
|
|
if (p_stat(entry->fullpath, &st) < 0 || |
|
|
|
50
|
|
|
|
|
|
|
156
|
15
|
50
|
|
|
|
|
S_ISDIR(st.st_mode) || |
|
157
|
15
|
50
|
|
|
|
|
(fd = git_futils_open_ro(entry->fullpath)) < 0 || |
|
158
|
15
|
|
|
|
|
|
(error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) |
|
159
|
2397
|
|
|
|
|
|
nonexistent = true; |
|
160
|
|
|
|
|
|
|
|
|
161
|
2412
|
100
|
|
|
|
|
if (fd >= 0) |
|
162
|
15
|
|
|
|
|
|
p_close(fd); |
|
163
|
|
|
|
|
|
|
|
|
164
|
2412
|
|
|
|
|
|
break; |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_HEAD: |
|
167
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_COMMIT: { |
|
168
|
0
|
0
|
|
|
|
|
if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) { |
|
169
|
0
|
0
|
|
|
|
|
if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 || |
|
|
|
0
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
(error = git_commit_tree(&tree, commit)) < 0) |
|
171
|
|
|
|
|
|
|
goto cleanup; |
|
172
|
|
|
|
|
|
|
} else { |
|
173
|
0
|
0
|
|
|
|
|
if ((error = git_repository_head_tree(&tree, repo)) < 0) |
|
174
|
0
|
|
|
|
|
|
goto cleanup; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
0
|
0
|
|
|
|
|
if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) { |
|
178
|
|
|
|
|
|
|
/* |
|
179
|
|
|
|
|
|
|
* If the attributes file does not exist, we can |
|
180
|
|
|
|
|
|
|
* cache an empty file for this commit to prevent |
|
181
|
|
|
|
|
|
|
* needless future lookups. |
|
182
|
|
|
|
|
|
|
*/ |
|
183
|
0
|
0
|
|
|
|
|
if (error == GIT_ENOTFOUND) { |
|
184
|
0
|
|
|
|
|
|
error = 0; |
|
185
|
0
|
|
|
|
|
|
break; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
goto cleanup; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
0
|
0
|
|
|
|
|
if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0) |
|
192
|
0
|
|
|
|
|
|
goto cleanup; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
/* |
|
195
|
|
|
|
|
|
|
* Do not assume that data straight from the ODB is NULL-terminated; |
|
196
|
|
|
|
|
|
|
* copy the contents of a file to a buffer to work on. |
|
197
|
|
|
|
|
|
|
*/ |
|
198
|
0
|
|
|
|
|
|
blobsize = git_blob_rawsize(blob); |
|
199
|
|
|
|
|
|
|
|
|
200
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_BLOBSIZE(blobsize); |
|
201
|
0
|
0
|
|
|
|
|
if ((error = git_str_put(&content, |
|
202
|
0
|
|
|
|
|
|
git_blob_rawcontent(blob), (size_t)blobsize)) < 0) |
|
203
|
0
|
|
|
|
|
|
goto cleanup; |
|
204
|
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
|
break; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
default: |
|
208
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type); |
|
209
|
0
|
|
|
|
|
|
return -1; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
2426
|
50
|
|
|
|
|
if ((error = git_attr_file__new(&file, entry, source)) < 0) |
|
213
|
0
|
|
|
|
|
|
goto cleanup; |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
/* advance over a UTF8 BOM */ |
|
216
|
2426
|
|
|
|
|
|
content_str = git_str_cstr(&content); |
|
217
|
2426
|
|
|
|
|
|
bom_offset = git_str_detect_bom(&bom, &content); |
|
218
|
|
|
|
|
|
|
|
|
219
|
2426
|
50
|
|
|
|
|
if (bom == GIT_STR_BOM_UTF8) |
|
220
|
0
|
|
|
|
|
|
content_str += bom_offset; |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
/* store the key of the attr_reader; don't bother with cache |
|
223
|
|
|
|
|
|
|
* invalidation during the same attr reader session. |
|
224
|
|
|
|
|
|
|
*/ |
|
225
|
2426
|
100
|
|
|
|
|
if (attr_session) |
|
226
|
117
|
|
|
|
|
|
file->session_key = attr_session->key; |
|
227
|
|
|
|
|
|
|
|
|
228
|
2426
|
100
|
|
|
|
|
if (parser && (error = parser(repo, file, content_str, allow_macros)) < 0) { |
|
|
|
50
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
|
git_attr_file__free(file); |
|
230
|
0
|
|
|
|
|
|
goto cleanup; |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
/* write cache breakers */ |
|
234
|
2426
|
100
|
|
|
|
|
if (nonexistent) |
|
235
|
2397
|
|
|
|
|
|
file->nonexistent = 1; |
|
236
|
29
|
50
|
|
|
|
|
else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX) |
|
237
|
0
|
|
|
|
|
|
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); |
|
238
|
29
|
50
|
|
|
|
|
else if (source->type == GIT_ATTR_FILE_SOURCE_HEAD) |
|
239
|
0
|
|
|
|
|
|
git_oid_cpy(&file->cache_data.oid, git_tree_id(tree)); |
|
240
|
29
|
50
|
|
|
|
|
else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) |
|
241
|
0
|
|
|
|
|
|
git_oid_cpy(&file->cache_data.oid, git_tree_id(tree)); |
|
242
|
29
|
100
|
|
|
|
|
else if (source->type == GIT_ATTR_FILE_SOURCE_FILE) |
|
243
|
15
|
|
|
|
|
|
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); |
|
244
|
|
|
|
|
|
|
/* else always cacheable */ |
|
245
|
|
|
|
|
|
|
|
|
246
|
2426
|
|
|
|
|
|
*out = file; |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
cleanup: |
|
249
|
2426
|
|
|
|
|
|
git_blob_free(blob); |
|
250
|
2426
|
|
|
|
|
|
git_tree_entry_free(tree_entry); |
|
251
|
2426
|
|
|
|
|
|
git_tree_free(tree); |
|
252
|
2426
|
|
|
|
|
|
git_commit_free(commit); |
|
253
|
2426
|
|
|
|
|
|
git_str_dispose(&content); |
|
254
|
|
|
|
|
|
|
|
|
255
|
3698
|
|
|
|
|
|
return error; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
3043
|
|
|
|
|
|
int git_attr_file__out_of_date( |
|
259
|
|
|
|
|
|
|
git_repository *repo, |
|
260
|
|
|
|
|
|
|
git_attr_session *attr_session, |
|
261
|
|
|
|
|
|
|
git_attr_file *file, |
|
262
|
|
|
|
|
|
|
git_attr_file_source *source) |
|
263
|
|
|
|
|
|
|
{ |
|
264
|
3043
|
50
|
|
|
|
|
if (!file) |
|
265
|
0
|
|
|
|
|
|
return 1; |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
/* we are never out of date if we just created this data in the same |
|
268
|
|
|
|
|
|
|
* attr_session; otherwise, nonexistent files must be invalidated |
|
269
|
|
|
|
|
|
|
*/ |
|
270
|
3043
|
100
|
|
|
|
|
if (attr_session && attr_session->key == file->session_key) |
|
|
|
100
|
|
|
|
|
|
|
271
|
361
|
|
|
|
|
|
return 0; |
|
272
|
2682
|
100
|
|
|
|
|
else if (file->nonexistent) |
|
273
|
2325
|
|
|
|
|
|
return 1; |
|
274
|
|
|
|
|
|
|
|
|
275
|
357
|
|
|
|
|
|
switch (file->source.type) { |
|
276
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_MEMORY: |
|
277
|
170
|
|
|
|
|
|
return 0; |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_FILE: |
|
280
|
187
|
|
|
|
|
|
return git_futils_filestamp_check( |
|
281
|
187
|
|
|
|
|
|
&file->cache_data.stamp, file->entry->fullpath); |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_INDEX: { |
|
284
|
|
|
|
|
|
|
int error; |
|
285
|
|
|
|
|
|
|
git_oid id; |
|
286
|
|
|
|
|
|
|
|
|
287
|
0
|
0
|
|
|
|
|
if ((error = attr_file_oid_from_index( |
|
288
|
0
|
|
|
|
|
|
&id, repo, file->entry->path)) < 0) |
|
289
|
0
|
|
|
|
|
|
return error; |
|
290
|
|
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
|
return (git_oid__cmp(&file->cache_data.oid, &id) != 0); |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_HEAD: { |
|
295
|
0
|
|
|
|
|
|
git_tree *tree = NULL; |
|
296
|
0
|
|
|
|
|
|
int error = git_repository_head_tree(&tree, repo); |
|
297
|
|
|
|
|
|
|
|
|
298
|
0
|
0
|
|
|
|
|
if (error < 0) |
|
299
|
0
|
|
|
|
|
|
return error; |
|
300
|
|
|
|
|
|
|
|
|
301
|
0
|
|
|
|
|
|
error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0); |
|
302
|
|
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
|
git_tree_free(tree); |
|
304
|
0
|
|
|
|
|
|
return error; |
|
305
|
|
|
|
|
|
|
} |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
case GIT_ATTR_FILE_SOURCE_COMMIT: { |
|
308
|
0
|
|
|
|
|
|
git_commit *commit = NULL; |
|
309
|
0
|
|
|
|
|
|
git_tree *tree = NULL; |
|
310
|
|
|
|
|
|
|
int error; |
|
311
|
|
|
|
|
|
|
|
|
312
|
0
|
0
|
|
|
|
|
if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0) |
|
313
|
0
|
|
|
|
|
|
return error; |
|
314
|
|
|
|
|
|
|
|
|
315
|
0
|
|
|
|
|
|
error = git_commit_tree(&tree, commit); |
|
316
|
0
|
|
|
|
|
|
git_commit_free(commit); |
|
317
|
|
|
|
|
|
|
|
|
318
|
0
|
0
|
|
|
|
|
if (error < 0) |
|
319
|
0
|
|
|
|
|
|
return error; |
|
320
|
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0); |
|
322
|
|
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
|
git_tree_free(tree); |
|
324
|
0
|
|
|
|
|
|
return error; |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
default: |
|
328
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type); |
|
329
|
0
|
|
|
|
|
|
return -1; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); |
|
334
|
|
|
|
|
|
|
static void git_attr_rule__clear(git_attr_rule *rule); |
|
335
|
|
|
|
|
|
|
static bool parse_optimized_patterns( |
|
336
|
|
|
|
|
|
|
git_attr_fnmatch *spec, |
|
337
|
|
|
|
|
|
|
git_pool *pool, |
|
338
|
|
|
|
|
|
|
const char *pattern); |
|
339
|
|
|
|
|
|
|
|
|
340
|
2059
|
|
|
|
|
|
int git_attr_file__parse_buffer( |
|
341
|
|
|
|
|
|
|
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros) |
|
342
|
|
|
|
|
|
|
{ |
|
343
|
2059
|
|
|
|
|
|
const char *scan = data, *context = NULL; |
|
344
|
2059
|
|
|
|
|
|
git_attr_rule *rule = NULL; |
|
345
|
2059
|
|
|
|
|
|
int error = 0; |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
/* If subdir file path, convert context for file paths */ |
|
348
|
4116
|
50
|
|
|
|
|
if (attrs->entry && git_fs_path_root(attrs->entry->path) < 0 && |
|
349
|
2057
|
|
|
|
|
|
!git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE)) |
|
350
|
95
|
|
|
|
|
|
context = attrs->entry->path; |
|
351
|
|
|
|
|
|
|
|
|
352
|
2059
|
50
|
|
|
|
|
if (git_mutex_lock(&attrs->lock) < 0) { |
|
353
|
0
|
|
|
|
|
|
git_error_set(GIT_ERROR_OS, "failed to lock attribute file"); |
|
354
|
0
|
|
|
|
|
|
return -1; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
2059
|
50
|
|
|
|
|
while (!error && *scan) { |
|
|
|
50
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
/* Allocate rule if needed, otherwise re-use previous rule */ |
|
359
|
0
|
0
|
|
|
|
|
if (!rule) { |
|
360
|
0
|
|
|
|
|
|
rule = git__calloc(1, sizeof(*rule)); |
|
361
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(rule); |
|
362
|
|
|
|
|
|
|
} else |
|
363
|
0
|
|
|
|
|
|
git_attr_rule__clear(rule); |
|
364
|
|
|
|
|
|
|
|
|
365
|
0
|
|
|
|
|
|
rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO; |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
/* Parse the next "pattern attr attr attr" line */ |
|
368
|
0
|
0
|
|
|
|
|
if ((error = git_attr_fnmatch__parse(&rule->match, &attrs->pool, context, &scan)) < 0 || |
|
|
|
0
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
(error = git_attr_assignment__parse(repo, &attrs->pool, &rule->assigns, &scan)) < 0) |
|
370
|
|
|
|
|
|
|
{ |
|
371
|
0
|
0
|
|
|
|
|
if (error != GIT_ENOTFOUND) |
|
372
|
0
|
|
|
|
|
|
goto out; |
|
373
|
0
|
|
|
|
|
|
error = 0; |
|
374
|
0
|
|
|
|
|
|
continue; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
0
|
0
|
|
|
|
|
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) { |
|
378
|
|
|
|
|
|
|
/* TODO: warning if macro found in file below repo root */ |
|
379
|
0
|
0
|
|
|
|
|
if (!allow_macros) |
|
380
|
0
|
|
|
|
|
|
continue; |
|
381
|
0
|
0
|
|
|
|
|
if ((error = git_attr_cache__insert_macro(repo, rule)) < 0) |
|
382
|
0
|
|
|
|
|
|
goto out; |
|
383
|
0
|
0
|
|
|
|
|
} else if ((error = git_vector_insert(&attrs->rules, rule)) < 0) |
|
384
|
0
|
|
|
|
|
|
goto out; |
|
385
|
|
|
|
|
|
|
|
|
386
|
0
|
|
|
|
|
|
rule = NULL; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
out: |
|
390
|
2059
|
|
|
|
|
|
git_mutex_unlock(&attrs->lock); |
|
391
|
2059
|
|
|
|
|
|
git_attr_rule__free(rule); |
|
392
|
|
|
|
|
|
|
|
|
393
|
2059
|
|
|
|
|
|
return error; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
4
|
|
|
|
|
|
uint32_t git_attr_file__name_hash(const char *name) |
|
397
|
|
|
|
|
|
|
{ |
|
398
|
4
|
|
|
|
|
|
uint32_t h = 5381; |
|
399
|
|
|
|
|
|
|
int c; |
|
400
|
|
|
|
|
|
|
|
|
401
|
4
|
50
|
|
|
|
|
GIT_ASSERT_ARG(name); |
|
402
|
|
|
|
|
|
|
|
|
403
|
24
|
100
|
|
|
|
|
while ((c = (int)*name++) != 0) |
|
404
|
20
|
|
|
|
|
|
h = ((h << 5) + h) + c; |
|
405
|
4
|
|
|
|
|
|
return h; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
|
int git_attr_file__lookup_one( |
|
409
|
|
|
|
|
|
|
git_attr_file *file, |
|
410
|
|
|
|
|
|
|
git_attr_path *path, |
|
411
|
|
|
|
|
|
|
const char *attr, |
|
412
|
|
|
|
|
|
|
const char **value) |
|
413
|
|
|
|
|
|
|
{ |
|
414
|
|
|
|
|
|
|
size_t i; |
|
415
|
|
|
|
|
|
|
git_attr_name name; |
|
416
|
|
|
|
|
|
|
git_attr_rule *rule; |
|
417
|
|
|
|
|
|
|
|
|
418
|
0
|
|
|
|
|
|
*value = NULL; |
|
419
|
|
|
|
|
|
|
|
|
420
|
0
|
|
|
|
|
|
name.name = attr; |
|
421
|
0
|
|
|
|
|
|
name.name_hash = git_attr_file__name_hash(attr); |
|
422
|
|
|
|
|
|
|
|
|
423
|
0
|
0
|
|
|
|
|
git_attr_file__foreach_matching_rule(file, path, i, rule) { |
|
|
|
0
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
size_t pos; |
|
425
|
|
|
|
|
|
|
|
|
426
|
0
|
0
|
|
|
|
|
if (!git_vector_bsearch(&pos, &rule->assigns, &name)) { |
|
427
|
0
|
|
|
|
|
|
*value = ((git_attr_assignment *) |
|
428
|
0
|
|
|
|
|
|
git_vector_get(&rule->assigns, pos))->value; |
|
429
|
0
|
|
|
|
|
|
break; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
0
|
|
|
|
|
|
return 0; |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
|
|
436
|
0
|
|
|
|
|
|
int git_attr_file__load_standalone(git_attr_file **out, const char *path) |
|
437
|
|
|
|
|
|
|
{ |
|
438
|
0
|
|
|
|
|
|
git_str content = GIT_STR_INIT; |
|
439
|
0
|
|
|
|
|
|
git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE }; |
|
440
|
0
|
|
|
|
|
|
git_attr_file *file = NULL; |
|
441
|
|
|
|
|
|
|
int error; |
|
442
|
|
|
|
|
|
|
|
|
443
|
0
|
0
|
|
|
|
|
if ((error = git_futils_readbuffer(&content, path)) < 0) |
|
444
|
0
|
|
|
|
|
|
goto out; |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
/* |
|
447
|
|
|
|
|
|
|
* Because the cache entry is allocated from the file's own pool, we |
|
448
|
|
|
|
|
|
|
* don't have to free it - freeing file+pool will free cache entry, too. |
|
449
|
|
|
|
|
|
|
*/ |
|
450
|
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
|
if ((error = git_attr_file__new(&file, NULL, &source)) < 0 || |
|
|
|
0
|
|
|
|
|
|
|
452
|
0
|
0
|
|
|
|
|
(error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 || |
|
453
|
0
|
|
|
|
|
|
(error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0) |
|
454
|
|
|
|
|
|
|
goto out; |
|
455
|
|
|
|
|
|
|
|
|
456
|
0
|
|
|
|
|
|
*out = file; |
|
457
|
|
|
|
|
|
|
out: |
|
458
|
0
|
0
|
|
|
|
|
if (error < 0) |
|
459
|
0
|
|
|
|
|
|
git_attr_file__free(file); |
|
460
|
0
|
|
|
|
|
|
git_str_dispose(&content); |
|
461
|
|
|
|
|
|
|
|
|
462
|
0
|
|
|
|
|
|
return error; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
|
|
|
|
|
|
|
|
465
|
2330
|
|
|
|
|
|
bool git_attr_fnmatch__match( |
|
466
|
|
|
|
|
|
|
git_attr_fnmatch *match, |
|
467
|
|
|
|
|
|
|
git_attr_path *path) |
|
468
|
|
|
|
|
|
|
{ |
|
469
|
2330
|
|
|
|
|
|
const char *relpath = path->path; |
|
470
|
|
|
|
|
|
|
const char *filename; |
|
471
|
2330
|
|
|
|
|
|
int flags = 0; |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
/* |
|
474
|
|
|
|
|
|
|
* If the rule was generated in a subdirectory, we must only |
|
475
|
|
|
|
|
|
|
* use it for paths inside that directory. We can thus return |
|
476
|
|
|
|
|
|
|
* a non-match if the prefixes don't match. |
|
477
|
|
|
|
|
|
|
*/ |
|
478
|
2330
|
50
|
|
|
|
|
if (match->containing_dir) { |
|
479
|
0
|
0
|
|
|
|
|
if (match->flags & GIT_ATTR_FNMATCH_ICASE) { |
|
480
|
0
|
0
|
|
|
|
|
if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length)) |
|
481
|
0
|
|
|
|
|
|
return 0; |
|
482
|
|
|
|
|
|
|
} else { |
|
483
|
0
|
0
|
|
|
|
|
if (git__prefixcmp(path->path, match->containing_dir)) |
|
484
|
0
|
|
|
|
|
|
return 0; |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
0
|
|
|
|
|
|
relpath += match->containing_dir_length; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
2330
|
50
|
|
|
|
|
if (match->flags & GIT_ATTR_FNMATCH_ICASE) |
|
491
|
0
|
|
|
|
|
|
flags |= WM_CASEFOLD; |
|
492
|
|
|
|
|
|
|
|
|
493
|
2330
|
50
|
|
|
|
|
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) { |
|
494
|
0
|
|
|
|
|
|
filename = relpath; |
|
495
|
0
|
|
|
|
|
|
flags |= WM_PATHNAME; |
|
496
|
|
|
|
|
|
|
} else { |
|
497
|
2330
|
|
|
|
|
|
filename = path->basename; |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
2330
|
50
|
|
|
|
|
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) { |
|
|
|
0
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
bool samename; |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
/* |
|
504
|
|
|
|
|
|
|
* for attribute checks or checks at the root of this match's |
|
505
|
|
|
|
|
|
|
* containing_dir (or root of the repository if no containing_dir), |
|
506
|
|
|
|
|
|
|
* do not match. |
|
507
|
|
|
|
|
|
|
*/ |
|
508
|
0
|
0
|
|
|
|
|
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) || |
|
|
|
0
|
|
|
|
|
|
|
509
|
0
|
|
|
|
|
|
path->basename == relpath) |
|
510
|
0
|
|
|
|
|
|
return false; |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
/* fail match if this is a file with same name as ignored folder */ |
|
513
|
0
|
|
|
|
|
|
samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? |
|
514
|
0
|
0
|
|
|
|
|
!strcasecmp(match->pattern, relpath) : |
|
515
|
0
|
|
|
|
|
|
!strcmp(match->pattern, relpath); |
|
516
|
|
|
|
|
|
|
|
|
517
|
0
|
0
|
|
|
|
|
if (samename) |
|
518
|
0
|
|
|
|
|
|
return false; |
|
519
|
|
|
|
|
|
|
|
|
520
|
0
|
|
|
|
|
|
return (wildmatch(match->pattern, relpath, flags) == WM_MATCH); |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
2330
|
|
|
|
|
|
return (wildmatch(match->pattern, filename, flags) == WM_MATCH); |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
0
|
|
|
|
|
|
bool git_attr_rule__match( |
|
527
|
|
|
|
|
|
|
git_attr_rule *rule, |
|
528
|
|
|
|
|
|
|
git_attr_path *path) |
|
529
|
|
|
|
|
|
|
{ |
|
530
|
0
|
|
|
|
|
|
bool matched = git_attr_fnmatch__match(&rule->match, path); |
|
531
|
|
|
|
|
|
|
|
|
532
|
0
|
0
|
|
|
|
|
if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) |
|
533
|
0
|
|
|
|
|
|
matched = !matched; |
|
534
|
|
|
|
|
|
|
|
|
535
|
0
|
|
|
|
|
|
return matched; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
0
|
|
|
|
|
|
git_attr_assignment *git_attr_rule__lookup_assignment( |
|
539
|
|
|
|
|
|
|
git_attr_rule *rule, const char *name) |
|
540
|
|
|
|
|
|
|
{ |
|
541
|
|
|
|
|
|
|
size_t pos; |
|
542
|
|
|
|
|
|
|
git_attr_name key; |
|
543
|
0
|
|
|
|
|
|
key.name = name; |
|
544
|
0
|
|
|
|
|
|
key.name_hash = git_attr_file__name_hash(name); |
|
545
|
|
|
|
|
|
|
|
|
546
|
0
|
0
|
|
|
|
|
if (git_vector_bsearch(&pos, &rule->assigns, &key)) |
|
547
|
0
|
|
|
|
|
|
return NULL; |
|
548
|
|
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
|
return git_vector_get(&rule->assigns, pos); |
|
550
|
|
|
|
|
|
|
} |
|
551
|
|
|
|
|
|
|
|
|
552
|
1394
|
|
|
|
|
|
int git_attr_path__init( |
|
553
|
|
|
|
|
|
|
git_attr_path *info, |
|
554
|
|
|
|
|
|
|
const char *path, |
|
555
|
|
|
|
|
|
|
const char *base, |
|
556
|
|
|
|
|
|
|
git_dir_flag dir_flag) |
|
557
|
|
|
|
|
|
|
{ |
|
558
|
|
|
|
|
|
|
ssize_t root; |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
/* build full path as best we can */ |
|
561
|
1394
|
|
|
|
|
|
git_str_init(&info->full, 0); |
|
562
|
|
|
|
|
|
|
|
|
563
|
1394
|
50
|
|
|
|
|
if (git_fs_path_join_unrooted(&info->full, path, base, &root) < 0) |
|
564
|
0
|
|
|
|
|
|
return -1; |
|
565
|
|
|
|
|
|
|
|
|
566
|
1394
|
|
|
|
|
|
info->path = info->full.ptr + root; |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
/* remove trailing slashes */ |
|
569
|
1911
|
50
|
|
|
|
|
while (info->full.size > 0) { |
|
570
|
1911
|
100
|
|
|
|
|
if (info->full.ptr[info->full.size - 1] != '/') |
|
571
|
1394
|
|
|
|
|
|
break; |
|
572
|
517
|
|
|
|
|
|
info->full.size--; |
|
573
|
|
|
|
|
|
|
} |
|
574
|
1394
|
|
|
|
|
|
info->full.ptr[info->full.size] = '\0'; |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
/* skip leading slashes in path */ |
|
577
|
1394
|
50
|
|
|
|
|
while (*info->path == '/') |
|
578
|
0
|
|
|
|
|
|
info->path++; |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
/* find trailing basename component */ |
|
581
|
1394
|
|
|
|
|
|
info->basename = strrchr(info->path, '/'); |
|
582
|
1394
|
100
|
|
|
|
|
if (info->basename) |
|
583
|
249
|
|
|
|
|
|
info->basename++; |
|
584
|
1394
|
100
|
|
|
|
|
if (!info->basename || !*info->basename) |
|
|
|
50
|
|
|
|
|
|
|
585
|
1145
|
|
|
|
|
|
info->basename = info->path; |
|
586
|
|
|
|
|
|
|
|
|
587
|
1394
|
|
|
|
|
|
switch (dir_flag) |
|
588
|
|
|
|
|
|
|
{ |
|
589
|
|
|
|
|
|
|
case GIT_DIR_FLAG_FALSE: |
|
590
|
0
|
|
|
|
|
|
info->is_dir = 0; |
|
591
|
0
|
|
|
|
|
|
break; |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
case GIT_DIR_FLAG_TRUE: |
|
594
|
355
|
|
|
|
|
|
info->is_dir = 1; |
|
595
|
355
|
|
|
|
|
|
break; |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
case GIT_DIR_FLAG_UNKNOWN: |
|
598
|
|
|
|
|
|
|
default: |
|
599
|
1039
|
|
|
|
|
|
info->is_dir = (int)git_fs_path_isdir(info->full.ptr); |
|
600
|
1039
|
|
|
|
|
|
break; |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
|
|
603
|
1394
|
|
|
|
|
|
return 0; |
|
604
|
|
|
|
|
|
|
} |
|
605
|
|
|
|
|
|
|
|
|
606
|
1394
|
|
|
|
|
|
void git_attr_path__free(git_attr_path *info) |
|
607
|
|
|
|
|
|
|
{ |
|
608
|
1394
|
|
|
|
|
|
git_str_dispose(&info->full); |
|
609
|
1394
|
|
|
|
|
|
info->path = NULL; |
|
610
|
1394
|
|
|
|
|
|
info->basename = NULL; |
|
611
|
1394
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
/* |
|
614
|
|
|
|
|
|
|
* From gitattributes(5): |
|
615
|
|
|
|
|
|
|
* |
|
616
|
|
|
|
|
|
|
* Patterns have the following format: |
|
617
|
|
|
|
|
|
|
* |
|
618
|
|
|
|
|
|
|
* - A blank line matches no files, so it can serve as a separator for |
|
619
|
|
|
|
|
|
|
* readability. |
|
620
|
|
|
|
|
|
|
* |
|
621
|
|
|
|
|
|
|
* - A line starting with # serves as a comment. |
|
622
|
|
|
|
|
|
|
* |
|
623
|
|
|
|
|
|
|
* - An optional prefix ! which negates the pattern; any matching file |
|
624
|
|
|
|
|
|
|
* excluded by a previous pattern will become included again. If a negated |
|
625
|
|
|
|
|
|
|
* pattern matches, this will override lower precedence patterns sources. |
|
626
|
|
|
|
|
|
|
* |
|
627
|
|
|
|
|
|
|
* - If the pattern ends with a slash, it is removed for the purpose of the |
|
628
|
|
|
|
|
|
|
* following description, but it would only find a match with a directory. In |
|
629
|
|
|
|
|
|
|
* other words, foo/ will match a directory foo and paths underneath it, but |
|
630
|
|
|
|
|
|
|
* will not match a regular file or a symbolic link foo (this is consistent |
|
631
|
|
|
|
|
|
|
* with the way how pathspec works in general in git). |
|
632
|
|
|
|
|
|
|
* |
|
633
|
|
|
|
|
|
|
* - If the pattern does not contain a slash /, git treats it as a shell glob |
|
634
|
|
|
|
|
|
|
* pattern and checks for a match against the pathname without leading |
|
635
|
|
|
|
|
|
|
* directories. |
|
636
|
|
|
|
|
|
|
* |
|
637
|
|
|
|
|
|
|
* - Otherwise, git treats the pattern as a shell glob suitable for consumption |
|
638
|
|
|
|
|
|
|
* by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will |
|
639
|
|
|
|
|
|
|
* not match a / in the pathname. For example, "Documentation/\*.html" matches |
|
640
|
|
|
|
|
|
|
* "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading |
|
641
|
|
|
|
|
|
|
* slash matches the beginning of the pathname; for example, "/\*.c" matches |
|
642
|
|
|
|
|
|
|
* "cat-file.c" but not "mozilla-sha1/sha1.c". |
|
643
|
|
|
|
|
|
|
*/ |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
/* |
|
646
|
|
|
|
|
|
|
* Determine the length of trailing spaces. Escaped spaces do not count as |
|
647
|
|
|
|
|
|
|
* trailing whitespace. |
|
648
|
|
|
|
|
|
|
*/ |
|
649
|
128
|
|
|
|
|
|
static size_t trailing_space_length(const char *p, size_t len) |
|
650
|
|
|
|
|
|
|
{ |
|
651
|
|
|
|
|
|
|
size_t n, i; |
|
652
|
128
|
50
|
|
|
|
|
for (n = len; n; n--) { |
|
653
|
128
|
50
|
|
|
|
|
if (p[n-1] != ' ' && p[n-1] != '\t') |
|
|
|
50
|
|
|
|
|
|
|
654
|
128
|
|
|
|
|
|
break; |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
/* |
|
657
|
|
|
|
|
|
|
* Count escape-characters before space. In case where it's an |
|
658
|
|
|
|
|
|
|
* even number of escape characters, then the escape char itself |
|
659
|
|
|
|
|
|
|
* is escaped and the whitespace is an unescaped whitespace. |
|
660
|
|
|
|
|
|
|
* Otherwise, the last escape char is not escaped and the |
|
661
|
|
|
|
|
|
|
* whitespace in an escaped whitespace. |
|
662
|
|
|
|
|
|
|
*/ |
|
663
|
0
|
|
|
|
|
|
i = n; |
|
664
|
0
|
0
|
|
|
|
|
while (i > 1 && p[i-2] == '\\') |
|
|
|
0
|
|
|
|
|
|
|
665
|
0
|
|
|
|
|
|
i--; |
|
666
|
0
|
0
|
|
|
|
|
if ((n - i) % 2) |
|
667
|
0
|
|
|
|
|
|
break; |
|
668
|
|
|
|
|
|
|
} |
|
669
|
128
|
|
|
|
|
|
return len - n; |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
|
|
672
|
128
|
|
|
|
|
|
static size_t unescape_spaces(char *str) |
|
673
|
|
|
|
|
|
|
{ |
|
674
|
128
|
|
|
|
|
|
char *scan, *pos = str; |
|
675
|
128
|
|
|
|
|
|
bool escaped = false; |
|
676
|
|
|
|
|
|
|
|
|
677
|
128
|
50
|
|
|
|
|
if (!str) |
|
678
|
0
|
|
|
|
|
|
return 0; |
|
679
|
|
|
|
|
|
|
|
|
680
|
765
|
100
|
|
|
|
|
for (scan = str; *scan; scan++) { |
|
681
|
637
|
50
|
|
|
|
|
if (!escaped && *scan == '\\') { |
|
|
|
50
|
|
|
|
|
|
|
682
|
0
|
|
|
|
|
|
escaped = true; |
|
683
|
0
|
|
|
|
|
|
continue; |
|
684
|
|
|
|
|
|
|
} |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
/* Only insert the escape character for escaped non-spaces */ |
|
687
|
637
|
50
|
|
|
|
|
if (escaped && !git__isspace(*scan)) |
|
|
|
0
|
|
|
|
|
|
|
688
|
0
|
|
|
|
|
|
*pos++ = '\\'; |
|
689
|
|
|
|
|
|
|
|
|
690
|
637
|
|
|
|
|
|
*pos++ = *scan; |
|
691
|
637
|
|
|
|
|
|
escaped = false; |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
128
|
50
|
|
|
|
|
if (pos != scan) |
|
695
|
0
|
|
|
|
|
|
*pos = '\0'; |
|
696
|
|
|
|
|
|
|
|
|
697
|
128
|
|
|
|
|
|
return (pos - str); |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
/* |
|
701
|
|
|
|
|
|
|
* This will return 0 if the spec was filled out, |
|
702
|
|
|
|
|
|
|
* GIT_ENOTFOUND if the fnmatch does not require matching, or |
|
703
|
|
|
|
|
|
|
* another error code there was an actual problem. |
|
704
|
|
|
|
|
|
|
*/ |
|
705
|
156
|
|
|
|
|
|
int git_attr_fnmatch__parse( |
|
706
|
|
|
|
|
|
|
git_attr_fnmatch *spec, |
|
707
|
|
|
|
|
|
|
git_pool *pool, |
|
708
|
|
|
|
|
|
|
const char *context, |
|
709
|
|
|
|
|
|
|
const char **base) |
|
710
|
|
|
|
|
|
|
{ |
|
711
|
|
|
|
|
|
|
const char *pattern, *scan; |
|
712
|
|
|
|
|
|
|
int slash_count, allow_space; |
|
713
|
|
|
|
|
|
|
bool escaped; |
|
714
|
|
|
|
|
|
|
|
|
715
|
156
|
50
|
|
|
|
|
GIT_ASSERT_ARG(spec); |
|
716
|
156
|
50
|
|
|
|
|
GIT_ASSERT_ARG(base && *base); |
|
|
|
50
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
|
718
|
156
|
50
|
|
|
|
|
if (parse_optimized_patterns(spec, pool, *base)) |
|
719
|
0
|
|
|
|
|
|
return 0; |
|
720
|
|
|
|
|
|
|
|
|
721
|
156
|
|
|
|
|
|
spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING); |
|
722
|
156
|
|
|
|
|
|
allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0); |
|
723
|
|
|
|
|
|
|
|
|
724
|
156
|
|
|
|
|
|
pattern = *base; |
|
725
|
|
|
|
|
|
|
|
|
726
|
156
|
50
|
|
|
|
|
while (!allow_space && git__isspace(*pattern)) |
|
|
|
0
|
|
|
|
|
|
|
727
|
0
|
|
|
|
|
|
pattern++; |
|
728
|
|
|
|
|
|
|
|
|
729
|
156
|
50
|
|
|
|
|
if (!*pattern || *pattern == '#' || *pattern == '\n' || |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
730
|
0
|
0
|
|
|
|
|
(*pattern == '\r' && *(pattern + 1) == '\n')) { |
|
731
|
28
|
|
|
|
|
|
*base = git__next_line(pattern); |
|
732
|
28
|
|
|
|
|
|
return GIT_ENOTFOUND; |
|
733
|
|
|
|
|
|
|
} |
|
734
|
|
|
|
|
|
|
|
|
735
|
128
|
50
|
|
|
|
|
if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) { |
|
|
|
0
|
|
|
|
|
|
|
736
|
0
|
0
|
|
|
|
|
if (strncmp(pattern, "[attr]", 6) == 0) { |
|
737
|
0
|
|
|
|
|
|
spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO; |
|
738
|
0
|
|
|
|
|
|
pattern += 6; |
|
739
|
|
|
|
|
|
|
} |
|
740
|
|
|
|
|
|
|
/* else a character range like [a-e]* which is accepted */ |
|
741
|
|
|
|
|
|
|
} |
|
742
|
|
|
|
|
|
|
|
|
743
|
128
|
50
|
|
|
|
|
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) { |
|
|
|
0
|
|
|
|
|
|
|
744
|
0
|
|
|
|
|
|
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE; |
|
745
|
0
|
|
|
|
|
|
pattern++; |
|
746
|
|
|
|
|
|
|
} |
|
747
|
|
|
|
|
|
|
|
|
748
|
128
|
|
|
|
|
|
slash_count = 0; |
|
749
|
128
|
|
|
|
|
|
escaped = false; |
|
750
|
|
|
|
|
|
|
/* Scan until a non-escaped whitespace. */ |
|
751
|
767
|
100
|
|
|
|
|
for (scan = pattern; *scan != '\0'; ++scan) { |
|
752
|
683
|
|
|
|
|
|
char c = *scan; |
|
753
|
|
|
|
|
|
|
|
|
754
|
683
|
50
|
|
|
|
|
if (c == '\\' && !escaped) { |
|
|
|
0
|
|
|
|
|
|
|
755
|
0
|
|
|
|
|
|
escaped = true; |
|
756
|
0
|
|
|
|
|
|
continue; |
|
757
|
683
|
100
|
|
|
|
|
} else if (git__isspace(c) && !escaped) { |
|
|
|
50
|
|
|
|
|
|
|
758
|
44
|
50
|
|
|
|
|
if (!allow_space || (c != ' ' && c != '\t' && c != '\r')) |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
break; |
|
760
|
639
|
100
|
|
|
|
|
} else if (c == '/') { |
|
761
|
11
|
|
|
|
|
|
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH; |
|
762
|
11
|
|
|
|
|
|
slash_count++; |
|
763
|
|
|
|
|
|
|
|
|
764
|
11
|
100
|
|
|
|
|
if (slash_count == 1 && pattern == scan) |
|
|
|
50
|
|
|
|
|
|
|
765
|
11
|
|
|
|
|
|
pattern++; |
|
766
|
628
|
100
|
|
|
|
|
} else if (git__iswildcard(c) && !escaped) { |
|
|
|
50
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
/* remember if we see an unescaped wildcard in pattern */ |
|
768
|
16
|
|
|
|
|
|
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD; |
|
769
|
|
|
|
|
|
|
} |
|
770
|
|
|
|
|
|
|
|
|
771
|
639
|
|
|
|
|
|
escaped = false; |
|
772
|
|
|
|
|
|
|
} |
|
773
|
|
|
|
|
|
|
|
|
774
|
128
|
|
|
|
|
|
*base = scan; |
|
775
|
|
|
|
|
|
|
|
|
776
|
128
|
50
|
|
|
|
|
if ((spec->length = scan - pattern) == 0) |
|
777
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
/* |
|
780
|
|
|
|
|
|
|
* Remove one trailing \r in case this is a CRLF delimited |
|
781
|
|
|
|
|
|
|
* file, in the case of Icon\r\r\n, we still leave the first |
|
782
|
|
|
|
|
|
|
* \r there to match against. |
|
783
|
|
|
|
|
|
|
*/ |
|
784
|
128
|
50
|
|
|
|
|
if (pattern[spec->length - 1] == '\r') |
|
785
|
0
|
0
|
|
|
|
|
if (--spec->length == 0) |
|
786
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
/* Remove trailing spaces. */ |
|
789
|
128
|
|
|
|
|
|
spec->length -= trailing_space_length(pattern, spec->length); |
|
790
|
|
|
|
|
|
|
|
|
791
|
128
|
50
|
|
|
|
|
if (spec->length == 0) |
|
792
|
0
|
|
|
|
|
|
return GIT_ENOTFOUND; |
|
793
|
|
|
|
|
|
|
|
|
794
|
128
|
100
|
|
|
|
|
if (pattern[spec->length - 1] == '/') { |
|
795
|
2
|
|
|
|
|
|
spec->length--; |
|
796
|
2
|
|
|
|
|
|
spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY; |
|
797
|
2
|
50
|
|
|
|
|
if (--slash_count <= 0) |
|
798
|
2
|
|
|
|
|
|
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
128
|
50
|
|
|
|
|
if (context) { |
|
802
|
0
|
|
|
|
|
|
char *slash = strrchr(context, '/'); |
|
803
|
|
|
|
|
|
|
size_t len; |
|
804
|
0
|
0
|
|
|
|
|
if (slash) { |
|
805
|
|
|
|
|
|
|
/* include the slash for easier matching */ |
|
806
|
0
|
|
|
|
|
|
len = slash - context + 1; |
|
807
|
0
|
|
|
|
|
|
spec->containing_dir = git_pool_strndup(pool, context, len); |
|
808
|
0
|
|
|
|
|
|
spec->containing_dir_length = len; |
|
809
|
|
|
|
|
|
|
} |
|
810
|
|
|
|
|
|
|
} |
|
811
|
|
|
|
|
|
|
|
|
812
|
128
|
|
|
|
|
|
spec->pattern = git_pool_strndup(pool, pattern, spec->length); |
|
813
|
|
|
|
|
|
|
|
|
814
|
128
|
50
|
|
|
|
|
if (!spec->pattern) { |
|
815
|
0
|
|
|
|
|
|
*base = git__next_line(pattern); |
|
816
|
0
|
|
|
|
|
|
return -1; |
|
817
|
|
|
|
|
|
|
} else { |
|
818
|
|
|
|
|
|
|
/* strip '\' that might have been used for internal whitespace */ |
|
819
|
128
|
|
|
|
|
|
spec->length = unescape_spaces(spec->pattern); |
|
820
|
|
|
|
|
|
|
} |
|
821
|
|
|
|
|
|
|
|
|
822
|
128
|
|
|
|
|
|
return 0; |
|
823
|
|
|
|
|
|
|
} |
|
824
|
|
|
|
|
|
|
|
|
825
|
156
|
|
|
|
|
|
static bool parse_optimized_patterns( |
|
826
|
|
|
|
|
|
|
git_attr_fnmatch *spec, |
|
827
|
|
|
|
|
|
|
git_pool *pool, |
|
828
|
|
|
|
|
|
|
const char *pattern) |
|
829
|
|
|
|
|
|
|
{ |
|
830
|
156
|
50
|
|
|
|
|
if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
831
|
0
|
|
|
|
|
|
spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL; |
|
832
|
0
|
|
|
|
|
|
spec->pattern = git_pool_strndup(pool, pattern, 1); |
|
833
|
0
|
|
|
|
|
|
spec->length = 1; |
|
834
|
|
|
|
|
|
|
|
|
835
|
0
|
|
|
|
|
|
return true; |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
|
|
838
|
156
|
|
|
|
|
|
return false; |
|
839
|
|
|
|
|
|
|
} |
|
840
|
|
|
|
|
|
|
|
|
841
|
64
|
|
|
|
|
|
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw) |
|
842
|
|
|
|
|
|
|
{ |
|
843
|
64
|
|
|
|
|
|
const git_attr_name *a = a_raw; |
|
844
|
64
|
|
|
|
|
|
const git_attr_name *b = b_raw; |
|
845
|
|
|
|
|
|
|
|
|
846
|
64
|
100
|
|
|
|
|
if (b->name_hash < a->name_hash) |
|
847
|
32
|
|
|
|
|
|
return 1; |
|
848
|
32
|
50
|
|
|
|
|
else if (b->name_hash > a->name_hash) |
|
849
|
32
|
|
|
|
|
|
return -1; |
|
850
|
|
|
|
|
|
|
else |
|
851
|
0
|
|
|
|
|
|
return strcmp(b->name, a->name); |
|
852
|
|
|
|
|
|
|
} |
|
853
|
|
|
|
|
|
|
|
|
854
|
64
|
|
|
|
|
|
static void git_attr_assignment__free(git_attr_assignment *assign) |
|
855
|
|
|
|
|
|
|
{ |
|
856
|
|
|
|
|
|
|
/* name and value are stored in a git_pool associated with the |
|
857
|
|
|
|
|
|
|
* git_attr_file, so they do not need to be freed here |
|
858
|
|
|
|
|
|
|
*/ |
|
859
|
64
|
|
|
|
|
|
assign->name = NULL; |
|
860
|
64
|
|
|
|
|
|
assign->value = NULL; |
|
861
|
64
|
|
|
|
|
|
git__free(assign); |
|
862
|
64
|
|
|
|
|
|
} |
|
863
|
|
|
|
|
|
|
|
|
864
|
0
|
|
|
|
|
|
static int merge_assignments(void **old_raw, void *new_raw) |
|
865
|
|
|
|
|
|
|
{ |
|
866
|
0
|
|
|
|
|
|
git_attr_assignment **old = (git_attr_assignment **)old_raw; |
|
867
|
0
|
|
|
|
|
|
git_attr_assignment *new = (git_attr_assignment *)new_raw; |
|
868
|
|
|
|
|
|
|
|
|
869
|
0
|
0
|
|
|
|
|
GIT_REFCOUNT_DEC(*old, git_attr_assignment__free); |
|
|
|
0
|
|
|
|
|
|
|
870
|
0
|
|
|
|
|
|
*old = new; |
|
871
|
0
|
|
|
|
|
|
return GIT_EEXISTS; |
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
|
|
874
|
16
|
|
|
|
|
|
int git_attr_assignment__parse( |
|
875
|
|
|
|
|
|
|
git_repository *repo, |
|
876
|
|
|
|
|
|
|
git_pool *pool, |
|
877
|
|
|
|
|
|
|
git_vector *assigns, |
|
878
|
|
|
|
|
|
|
const char **base) |
|
879
|
|
|
|
|
|
|
{ |
|
880
|
|
|
|
|
|
|
int error; |
|
881
|
16
|
|
|
|
|
|
const char *scan = *base; |
|
882
|
16
|
|
|
|
|
|
git_attr_assignment *assign = NULL; |
|
883
|
|
|
|
|
|
|
|
|
884
|
16
|
50
|
|
|
|
|
GIT_ASSERT_ARG(assigns && !assigns->length); |
|
|
|
50
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
|
|
886
|
16
|
|
|
|
|
|
git_vector_set_cmp(assigns, sort_by_hash_and_name); |
|
887
|
|
|
|
|
|
|
|
|
888
|
80
|
100
|
|
|
|
|
while (*scan && *scan != '\n') { |
|
|
|
50
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
const char *name_start, *value_start; |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
/* skip leading blanks */ |
|
892
|
112
|
100
|
|
|
|
|
while (git__isspace(*scan) && *scan != '\n') scan++; |
|
|
|
50
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
/* allocate assign if needed */ |
|
895
|
64
|
50
|
|
|
|
|
if (!assign) { |
|
896
|
64
|
|
|
|
|
|
assign = git__calloc(1, sizeof(git_attr_assignment)); |
|
897
|
64
|
50
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(assign); |
|
898
|
64
|
|
|
|
|
|
GIT_REFCOUNT_INC(assign); |
|
899
|
|
|
|
|
|
|
} |
|
900
|
|
|
|
|
|
|
|
|
901
|
64
|
|
|
|
|
|
assign->name_hash = 5381; |
|
902
|
64
|
|
|
|
|
|
assign->value = git_attr__true; |
|
903
|
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
/* look for magic name prefixes */ |
|
905
|
64
|
50
|
|
|
|
|
if (*scan == '-') { |
|
906
|
64
|
|
|
|
|
|
assign->value = git_attr__false; |
|
907
|
64
|
|
|
|
|
|
scan++; |
|
908
|
0
|
0
|
|
|
|
|
} else if (*scan == '!') { |
|
909
|
0
|
|
|
|
|
|
assign->value = git_attr__unset; /* explicit unspecified state */ |
|
910
|
0
|
|
|
|
|
|
scan++; |
|
911
|
0
|
0
|
|
|
|
|
} else if (*scan == '#') /* comment rest of line */ |
|
912
|
0
|
|
|
|
|
|
break; |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
/* find the name */ |
|
915
|
64
|
|
|
|
|
|
name_start = scan; |
|
916
|
336
|
100
|
|
|
|
|
while (*scan && !git__isspace(*scan) && *scan != '=') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
917
|
272
|
|
|
|
|
|
assign->name_hash = |
|
918
|
272
|
|
|
|
|
|
((assign->name_hash << 5) + assign->name_hash) + *scan; |
|
919
|
272
|
|
|
|
|
|
scan++; |
|
920
|
|
|
|
|
|
|
} |
|
921
|
64
|
50
|
|
|
|
|
if (scan == name_start) { |
|
922
|
|
|
|
|
|
|
/* must have found lone prefix (" - ") or leading = ("=foo") |
|
923
|
|
|
|
|
|
|
* or end of buffer -- advance until whitespace and continue |
|
924
|
|
|
|
|
|
|
*/ |
|
925
|
0
|
0
|
|
|
|
|
while (*scan && !git__isspace(*scan)) scan++; |
|
|
|
0
|
|
|
|
|
|
|
926
|
0
|
|
|
|
|
|
continue; |
|
927
|
|
|
|
|
|
|
} |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
/* allocate permanent storage for name */ |
|
930
|
64
|
|
|
|
|
|
assign->name = git_pool_strndup(pool, name_start, scan - name_start); |
|
931
|
64
|
50
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(assign->name); |
|
932
|
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
/* if there is an equals sign, find the value */ |
|
934
|
64
|
50
|
|
|
|
|
if (*scan == '=') { |
|
935
|
0
|
0
|
|
|
|
|
for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan); |
|
|
|
0
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
/* if we found a value, allocate permanent storage for it */ |
|
938
|
0
|
0
|
|
|
|
|
if (scan > value_start) { |
|
939
|
0
|
|
|
|
|
|
assign->value = git_pool_strndup(pool, value_start, scan - value_start); |
|
940
|
0
|
0
|
|
|
|
|
GIT_ERROR_CHECK_ALLOC(assign->value); |
|
941
|
|
|
|
|
|
|
} |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
/* expand macros (if given a repo with a macro cache) */ |
|
945
|
64
|
50
|
|
|
|
|
if (repo != NULL && assign->value == git_attr__true) { |
|
|
|
50
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
|
git_attr_rule *macro = |
|
947
|
0
|
|
|
|
|
|
git_attr_cache__lookup_macro(repo, assign->name); |
|
948
|
|
|
|
|
|
|
|
|
949
|
0
|
0
|
|
|
|
|
if (macro != NULL) { |
|
950
|
|
|
|
|
|
|
unsigned int i; |
|
951
|
|
|
|
|
|
|
git_attr_assignment *massign; |
|
952
|
|
|
|
|
|
|
|
|
953
|
0
|
0
|
|
|
|
|
git_vector_foreach(¯o->assigns, i, massign) { |
|
954
|
0
|
|
|
|
|
|
GIT_REFCOUNT_INC(massign); |
|
955
|
|
|
|
|
|
|
|
|
956
|
0
|
|
|
|
|
|
error = git_vector_insert_sorted( |
|
957
|
|
|
|
|
|
|
assigns, massign, &merge_assignments); |
|
958
|
0
|
0
|
|
|
|
|
if (error < 0 && error != GIT_EEXISTS) { |
|
|
|
0
|
|
|
|
|
|
|
959
|
0
|
|
|
|
|
|
git_attr_assignment__free(assign); |
|
960
|
0
|
|
|
|
|
|
return error; |
|
961
|
|
|
|
|
|
|
} |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
} |
|
964
|
|
|
|
|
|
|
} |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
/* insert allocated assign into vector */ |
|
967
|
64
|
|
|
|
|
|
error = git_vector_insert_sorted(assigns, assign, &merge_assignments); |
|
968
|
64
|
50
|
|
|
|
|
if (error < 0 && error != GIT_EEXISTS) |
|
|
|
0
|
|
|
|
|
|
|
969
|
0
|
|
|
|
|
|
return error; |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
/* clear assign since it is now "owned" by the vector */ |
|
972
|
64
|
|
|
|
|
|
assign = NULL; |
|
973
|
|
|
|
|
|
|
} |
|
974
|
|
|
|
|
|
|
|
|
975
|
16
|
50
|
|
|
|
|
if (assign != NULL) |
|
976
|
0
|
|
|
|
|
|
git_attr_assignment__free(assign); |
|
977
|
|
|
|
|
|
|
|
|
978
|
16
|
|
|
|
|
|
*base = git__next_line(scan); |
|
979
|
|
|
|
|
|
|
|
|
980
|
16
|
50
|
|
|
|
|
return (assigns->length == 0) ? GIT_ENOTFOUND : 0; |
|
981
|
|
|
|
|
|
|
} |
|
982
|
|
|
|
|
|
|
|
|
983
|
2119
|
|
|
|
|
|
static void git_attr_rule__clear(git_attr_rule *rule) |
|
984
|
|
|
|
|
|
|
{ |
|
985
|
|
|
|
|
|
|
unsigned int i; |
|
986
|
|
|
|
|
|
|
git_attr_assignment *assign; |
|
987
|
|
|
|
|
|
|
|
|
988
|
2119
|
100
|
|
|
|
|
if (!rule) |
|
989
|
2059
|
|
|
|
|
|
return; |
|
990
|
|
|
|
|
|
|
|
|
991
|
60
|
100
|
|
|
|
|
if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) { |
|
992
|
80
|
100
|
|
|
|
|
git_vector_foreach(&rule->assigns, i, assign) |
|
993
|
64
|
50
|
|
|
|
|
GIT_REFCOUNT_DEC(assign, git_attr_assignment__free); |
|
|
|
50
|
|
|
|
|
|
|
994
|
16
|
|
|
|
|
|
git_vector_free(&rule->assigns); |
|
995
|
|
|
|
|
|
|
} |
|
996
|
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
/* match.pattern is stored in a git_pool, so no need to free */ |
|
998
|
60
|
|
|
|
|
|
rule->match.pattern = NULL; |
|
999
|
60
|
|
|
|
|
|
rule->match.length = 0; |
|
1000
|
|
|
|
|
|
|
} |
|
1001
|
|
|
|
|
|
|
|
|
1002
|
2119
|
|
|
|
|
|
void git_attr_rule__free(git_attr_rule *rule) |
|
1003
|
|
|
|
|
|
|
{ |
|
1004
|
2119
|
|
|
|
|
|
git_attr_rule__clear(rule); |
|
1005
|
2119
|
|
|
|
|
|
git__free(rule); |
|
1006
|
2119
|
|
|
|
|
|
} |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
351
|
|
|
|
|
|
int git_attr_session__init(git_attr_session *session, git_repository *repo) |
|
1009
|
|
|
|
|
|
|
{ |
|
1010
|
351
|
50
|
|
|
|
|
GIT_ASSERT_ARG(repo); |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
351
|
|
|
|
|
|
memset(session, 0, sizeof(*session)); |
|
1013
|
351
|
|
|
|
|
|
session->key = git_atomic32_inc(&repo->attr_session_key); |
|
1014
|
|
|
|
|
|
|
|
|
1015
|
351
|
|
|
|
|
|
return 0; |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
351
|
|
|
|
|
|
void git_attr_session__free(git_attr_session *session) |
|
1019
|
|
|
|
|
|
|
{ |
|
1020
|
351
|
50
|
|
|
|
|
if (!session) |
|
1021
|
0
|
|
|
|
|
|
return; |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
351
|
|
|
|
|
|
git_str_dispose(&session->sysdir); |
|
1024
|
351
|
|
|
|
|
|
git_str_dispose(&session->tmp); |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
351
|
|
|
|
|
|
memset(session, 0, sizeof(git_attr_session)); |
|
1027
|
|
|
|
|
|
|
} |