1 #include <string.h>
2 
3 #include "git2.h"
4 
5 #include "egit.h"
6 #include "egit-util.h"
7 #include "interface.h"
8 #include "egit-pathspec.h"
9 
10 EGIT_DOC(pathspec_new, "PATHSPECS",
11          "Compile a pathspec object from a PATHSPECS list of strings.");
egit_pathspec_new(emacs_env * env,emacs_value _pathspecs)12 emacs_value egit_pathspec_new(emacs_env *env, emacs_value _pathspecs)
13 {
14     git_strarray pathspecs;
15     if (!egit_strarray_from_list(&pathspecs, env, _pathspecs)) {
16         return esym_nil;
17     }
18 
19     git_pathspec* spec = NULL;
20     int retval = git_pathspec_new(&spec, &pathspecs);
21     egit_strarray_dispose(&pathspecs);
22     EGIT_CHECK_ERROR(retval);
23 
24     return egit_wrap(env, EGIT_PATHSPEC, spec, NULL);
25 }
26 
extract_flags(int32_t * out,emacs_env * env,emacs_value _flags)27 static emacs_value extract_flags(int32_t *out, emacs_env* env, emacs_value _flags) {
28     {
29         EM_DOLIST(_flag, _flags, flags_label);
30         git_pathspec_flag_t flag = 0;
31         em_findsym_pathspec_flag(&flag, env, _flag, true);
32         *out |= flag;
33         EM_DOLIST_END(flags_label);
34     }
35     return esym_t;
36 }
37 
38 EGIT_DOC(pathspec_matches_path, "PATHSPEC FLAGS PATH",
39          "Try to match a PATH against a PATHSPEC.\n"
40          "\n"
41          "FLAGS should be nil or a list with the following symbols:\n"
42          "  - ignore-case: forces match to ignore case\n"
43          "  - use-case: forces case sensitive match\n"
44          "  - no-glob: disables glob patterns and just uses simple string "
45          "comparison for matching\n"
46          "\n"
47          "Unlike most of the other pathspec matching functions, this will not "
48          "fall back on the native case-sensitivity for your platform. "
49          "You must explicitly pass flags to control case sensitivity or else "
50          "this will fall back on being case sensitive.");
egit_pathspec_matches_path(emacs_env * env,emacs_value _pathspec,emacs_value _flags,emacs_value _path)51 emacs_value egit_pathspec_matches_path(emacs_env *env, emacs_value _pathspec,
52                                        emacs_value _flags, emacs_value _path)
53 {
54     EGIT_ASSERT_PATHSPEC(_pathspec);
55     EM_ASSERT_STRING(_path);
56 
57     git_pathspec *pathspec = EGIT_EXTRACT(_pathspec);
58     int32_t flags = 0;
59     extract_flags(&flags, env, _flags);
60     char *path = EM_EXTRACT_STRING(_path);
61 
62     bool retval = git_pathspec_matches_path(pathspec, flags, path);
63     free(path);
64     return retval ? esym_t : esym_nil;
65 }
66 
67 EGIT_DOC(pathspec_match_list_entrycount, "PATHSPEC-MATCH-LIST",
68          "Get the number of items in a match list.");
egit_pathspec_match_list_entrycount(emacs_env * env,emacs_value _match_list)69 emacs_value egit_pathspec_match_list_entrycount(emacs_env *env,
70                                                 emacs_value _match_list) {
71     EGIT_ASSERT_PATHSPEC_MATCH_LIST(_match_list);
72     git_pathspec_match_list *match_list = EGIT_EXTRACT(_match_list);
73     return EM_INTEGER(git_pathspec_match_list_entrycount(match_list));
74 }
75 
76 EGIT_DOC(pathspec_match_list_entry, "PATHSPEC-MATCH-LIST POSITION",
77          "Get a matching filename by position.\n"
78          "\n"
79          "This routine cannot be used if the match list was generated by "
80          "`libgit-pathspec-match-diff'. If so, it will always return nil.");
egit_pathspec_match_list_entry(emacs_env * env,emacs_value _match_list,emacs_value _pos)81 emacs_value egit_pathspec_match_list_entry(emacs_env *env,
82                                            emacs_value _match_list,
83                                            emacs_value _pos) {
84     EGIT_ASSERT_PATHSPEC_MATCH_LIST(_match_list);
85     EM_ASSERT_INTEGER(_pos);
86     git_pathspec_match_list *match_list = EGIT_EXTRACT(_match_list);
87     size_t pos = EM_EXTRACT_INTEGER(_pos);
88     const char *filename = git_pathspec_match_list_entry(match_list, pos);
89     if (!filename) {
90         return esym_nil;
91     }
92     return EM_STRING(filename);
93 }
94 
95 EGIT_DOC(pathspec_match_list_diff_entry, "PATHSPEC-MATCH-LIST POSITION",
96          "Get a matching diff delta by position.\n"
97          "\n"
98          "This routine can only be used if the match list was generated by "
99          "`libgit-pathspec-match-diff'. Otherwise it will always return nil.");
egit_pathspec_match_list_diff_entry(emacs_env * env,emacs_value _match_list,emacs_value _pos)100 emacs_value egit_pathspec_match_list_diff_entry(emacs_env *env,
101                                                 emacs_value _match_list,
102                                                 emacs_value _pos) {
103     EGIT_ASSERT_PATHSPEC_MATCH_LIST(_match_list);
104     EM_ASSERT_INTEGER(_pos);
105     git_pathspec_match_list *match_list = EGIT_EXTRACT(_match_list);
106     size_t pos = EM_EXTRACT_INTEGER(_pos);
107     const git_diff_delta *delta = git_pathspec_match_list_diff_entry(match_list, pos);
108     if (!delta) {
109         return esym_nil;
110     }
111     return egit_wrap(env, EGIT_DIFF_DELTA, delta, NULL);
112 }
113 
114 EGIT_DOC(pathspec_match_list_failed_entrycount, "PATHSPEC-MATCH-LIST",
115          "Get the number of pathspec items that did not match.\n"
116          "\n"
117          "This will be zero unless you passed `find-failures' when "
118          "generating the pathspec match list.");
egit_pathspec_match_list_failed_entrycount(emacs_env * env,emacs_value _match_list)119 emacs_value egit_pathspec_match_list_failed_entrycount(emacs_env *env,
120                                                        emacs_value _match_list) {
121     EGIT_ASSERT_PATHSPEC_MATCH_LIST(_match_list);
122     git_pathspec_match_list *match_list = EGIT_EXTRACT(_match_list);
123     return EM_INTEGER(git_pathspec_match_list_failed_entrycount(match_list));
124 }
125 
126 EGIT_DOC(pathspec_match_list_failed_entry, "PATHSPEC-MATCH-LIST POSITION",
127          "Get an original pathspec string that had no matches.\n"
128          "\n"
129          "This will be return nil for positions out of range.");
egit_pathspec_match_list_failed_entry(emacs_env * env,emacs_value _match_list,emacs_value _pos)130 emacs_value egit_pathspec_match_list_failed_entry(emacs_env *env,
131                                                   emacs_value _match_list,
132                                                   emacs_value _pos) {
133     EGIT_ASSERT_PATHSPEC_MATCH_LIST(_match_list);
134     EM_ASSERT_INTEGER(_pos);
135     git_pathspec_match_list *match_list = EGIT_EXTRACT(_match_list);
136     size_t pos = EM_EXTRACT_INTEGER(_pos);
137     const char *filename = git_pathspec_match_list_failed_entry(match_list, pos);
138     if (!filename) {
139         return esym_nil;
140     }
141     return EM_STRING(filename);
142 }
143 
144 EGIT_DOC(pathspec_match_workdir, "REPO FLAGS PATHSPEC",
145          "Match a PATHSPEC against the working directory of a REPO repository.\n"
146          "\n"
147          "This matches the pathspec against the current files in the working "
148          "directory of the repository. It is an error to invoke this on a bare"
149          "repo. This handles git ignores (i.e. ignored files will not be"
150          "considered to match the PATHSPEC unless the file is tracked in the "
151          "index).\n"
152          "\n"
153          "FLAGS should be nil or a list with the following symbols:\n"
154          "  - ignore-case: forces match to ignore case\n"
155          "  - use-case: forces case sensitive match\n"
156          "  - no-glob: disables glob patterns and just uses simple string "
157          "comparison for matching\n"
158          "  - no-match-error: signal an error if no matches are found; "
159          "otherwise no matches is still success, but "
160          "`libgit-pathspec-match-list-entrycount' will indicate 0 matches.\n"
161          "  - find-failures: means that the `libgit-pathspec-match-list' object "
162          "should track which patterns matched which files so that at the end of "
163          "the match we can identify patterns that did not match any files.\n"
164          "  - failures-only: means that the `libgit-pathspec-match-list' object "
165          "does not need to keep the actual matching filenames. Use this to "
166          "just test if there were any matches at all or in combination with "
167          "`find-failures' to validate a pathspec.");
egit_pathspec_match_workdir(emacs_env * env,emacs_value _repo,emacs_value _flags,emacs_value _pathspec)168 emacs_value egit_pathspec_match_workdir(emacs_env *env,
169                                         emacs_value _repo,
170                                         emacs_value _flags,
171                                         emacs_value _pathspec)
172 {
173     EGIT_ASSERT_REPOSITORY(_repo);
174     EGIT_ASSERT_PATHSPEC(_pathspec);
175 
176     git_repository *repo = EGIT_EXTRACT(_repo);
177     git_pathspec *pathspec = EGIT_EXTRACT(_pathspec);
178     int32_t flags = 0;
179     extract_flags(&flags, env, _flags);
180 
181     git_pathspec_match_list *match_list;
182     int retval = git_pathspec_match_workdir(&match_list, repo, flags,
183                                             pathspec);
184     EGIT_CHECK_ERROR(retval);
185 
186     return egit_wrap(env, EGIT_PATHSPEC_MATCH_LIST, match_list, NULL);
187 }
188 
189 EGIT_DOC(pathspec_match_index, "INDEX FLAGS PATHSPEC",
190          "Match a PATHSPEC against an INDEX.\n"
191          "\n"
192          "FLAGS should be nil or a list with the following symbols:\n"
193          "  - ignore-case: forces match to ignore case\n"
194          "  - use-case: forces case sensitive match\n"
195          "  - no-glob: disables glob patterns and just uses simple string "
196          "comparison for matching\n"
197          "  - no-match-error: signal an error if no matches are found; "
198          "otherwise no matches is still success, but "
199          "`libgit-pathspec-match-list-entrycount' will indicate 0 matches.\n"
200          "  - find-failures: means that the `libgit-pathspec-match-list' object "
201          "should track which patterns matched which files so that at the end of "
202          "the match we can identify patterns that did not match any files.\n"
203          "  - failures-only: means that the `libgit-pathspec-match-list' object "
204          "does not need to keep the actual matching filenames. Use this to "
205          "just test if there were any matches at all or in combination with "
206          "`find-failures' to validate a pathspec.");
egit_pathspec_match_index(emacs_env * env,emacs_value _index,emacs_value _flags,emacs_value _pathspec)207 emacs_value egit_pathspec_match_index(emacs_env *env,
208                                         emacs_value _index,
209                                         emacs_value _flags,
210                                         emacs_value _pathspec)
211 {
212     EGIT_ASSERT_INDEX(_index);
213     EGIT_ASSERT_PATHSPEC(_pathspec);
214 
215     git_index *index = EGIT_EXTRACT(_index);
216     git_pathspec *pathspec = EGIT_EXTRACT(_pathspec);
217     int32_t flags = 0;
218     extract_flags(&flags, env, _flags);
219 
220     git_pathspec_match_list *match_list;
221     int retval = git_pathspec_match_index(&match_list, index, flags,
222                                           pathspec);
223     EGIT_CHECK_ERROR(retval);
224 
225     return egit_wrap(env, EGIT_PATHSPEC_MATCH_LIST, match_list, NULL);
226 }
227 
228 EGIT_DOC(pathspec_match_tree, "TREE FLAGS PATHSPEC",
229          "Match a PATHSPEC against a TREE.\n"
230          "\n"
231          "FLAGS should be nil or a list with the following symbols:\n"
232          "  - ignore-case: forces match to ignore case\n"
233          "  - use-case: forces case sensitive match\n"
234          "  - no-glob: disables glob patterns and just uses simple string "
235          "comparison for matching\n"
236          "  - no-match-error: signal an error if no matches are found; "
237          "otherwise no matches is still success, but "
238          "`libgit-pathspec-match-list-entrycount' will indicate 0 matches.\n"
239          "  - find-failures: means that the `libgit-pathspec-match-list' object "
240          "should track which patterns matched which files so that at the end of "
241          "the match we can identify patterns that did not match any files.\n"
242          "  - failures-only: means that the `libgit-pathspec-match-list' object "
243          "does not need to keep the actual matching filenames. Use this to "
244          "just test if there were any matches at all or in combination with "
245          "`find-failures' to validate a pathspec.");
egit_pathspec_match_tree(emacs_env * env,emacs_value _tree,emacs_value _flags,emacs_value _pathspec)246 emacs_value egit_pathspec_match_tree(emacs_env *env,
247                                      emacs_value _tree,
248                                      emacs_value _flags,
249                                      emacs_value _pathspec)
250 {
251     EGIT_ASSERT_TREE(_tree);
252     EGIT_ASSERT_PATHSPEC(_pathspec);
253 
254     git_tree *tree = EGIT_EXTRACT(_tree);
255     git_pathspec *pathspec = EGIT_EXTRACT(_pathspec);
256     int32_t flags = 0;
257     extract_flags(&flags, env, _flags);
258 
259     git_pathspec_match_list *match_list;
260     int retval = git_pathspec_match_tree(&match_list, tree, flags,
261                                          pathspec);
262     EGIT_CHECK_ERROR(retval);
263 
264     return egit_wrap(env, EGIT_PATHSPEC_MATCH_LIST, match_list, NULL);
265 }
266 
267 EGIT_DOC(pathspec_match_diff, "DIFF FLAGS PATHSPEC",
268          "Match a PATHSPEC against a DIFF.\n"
269          "\n"
270          "FLAGS should be nil or a list with the following symbols:\n"
271          "  - ignore-case: forces match to ignore case\n"
272          "  - use-case: forces case sensitive match\n"
273          "  - no-glob: disables glob patterns and just uses simple string "
274          "comparison for matching\n"
275          "  - no-match-error: signal an error if no matches are found; "
276          "otherwise no matches is still success, but "
277          "`libgit-pathspec-match-list-entrycount' will indicate 0 matches.\n"
278          "  - find-failures: means that the `libgit-pathspec-match-list' object "
279          "should track which patterns matched which files so that at the end of "
280          "the match we can identify patterns that did not match any files.\n"
281          "  - failures-only: means that the `libgit-pathspec-match-list' object "
282          "does not need to keep the actual matching filenames. Use this to "
283          "just test if there were any matches at all or in combination with "
284          "`find-failures' to validate a pathspec.");
egit_pathspec_match_diff(emacs_env * env,emacs_value _diff,emacs_value _flags,emacs_value _pathspec)285 emacs_value egit_pathspec_match_diff(emacs_env *env,
286                                      emacs_value _diff,
287                                      emacs_value _flags,
288                                      emacs_value _pathspec)
289 {
290     EGIT_ASSERT_DIFF(_diff);
291     EGIT_ASSERT_PATHSPEC(_pathspec);
292 
293     git_diff *diff = EGIT_EXTRACT(_diff);
294     git_pathspec *pathspec = EGIT_EXTRACT(_pathspec);
295     int32_t flags = 0;
296     extract_flags(&flags, env, _flags);
297 
298     git_pathspec_match_list *match_list;
299     int retval = git_pathspec_match_diff(&match_list, diff, flags,
300                                          pathspec);
301     EGIT_CHECK_ERROR(retval);
302 
303     return egit_wrap(env, EGIT_PATHSPEC_MATCH_LIST, match_list, NULL);
304 }
305