File Coverage

deps/libgit2/src/libgit2/pathspec.c
Criterion Covered Total %
statement 250 351 71.2
branch 149 300 49.6
condition n/a
subroutine n/a
pod n/a
total 399 651 61.2


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 "pathspec.h"
9              
10             #include "git2/pathspec.h"
11             #include "git2/diff.h"
12             #include "attr_file.h"
13             #include "iterator.h"
14             #include "repository.h"
15             #include "index.h"
16             #include "bitvec.h"
17             #include "diff.h"
18             #include "wildmatch.h"
19              
20             /* what is the common non-wildcard prefix for all items in the pathspec */
21 260           char *git_pathspec_prefix(const git_strarray *pathspec)
22             {
23 260           git_str prefix = GIT_STR_INIT;
24             const char *scan;
25              
26 296 50         if (!pathspec || !pathspec->count ||
27 36           git_str_common_prefix(&prefix, pathspec->strings, pathspec->count) < 0)
28 224           return NULL;
29              
30             /* diff prefix will only be leading non-wildcards */
31 177 100         for (scan = prefix.ptr; *scan; ++scan) {
32 151 100         if (git__iswildcard(*scan) &&
    100          
33 9 50         (scan == prefix.ptr || (*(scan - 1) != '\\')))
34             break;
35             }
36 36           git_str_truncate(&prefix, scan - prefix.ptr);
37              
38 36 100         if (prefix.size <= 0) {
39 4           git_str_dispose(&prefix);
40 4           return NULL;
41             }
42              
43 32           git_str_unescape(&prefix);
44              
45 260           return git_str_detach(&prefix);
46             }
47              
48             /* is there anything in the spec that needs to be filtered on */
49 316           bool git_pathspec_is_empty(const git_strarray *pathspec)
50             {
51             size_t i;
52              
53 316 50         if (pathspec == NULL)
54 0           return true;
55              
56 316 100         for (i = 0; i < pathspec->count; ++i) {
57 74           const char *str = pathspec->strings[i];
58              
59 74 50         if (str && str[0])
    50          
60 74           return false;
61             }
62              
63 242           return true;
64             }
65              
66             /* build a vector of fnmatch patterns to evaluate efficiently */
67 316           int git_pathspec__vinit(
68             git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
69             {
70             size_t i;
71              
72 316           memset(vspec, 0, sizeof(*vspec));
73              
74 316 100         if (git_pathspec_is_empty(strspec))
75 242           return 0;
76              
77 74 50         if (git_vector_init(vspec, strspec->count, NULL) < 0)
78 0           return -1;
79              
80 158 100         for (i = 0; i < strspec->count; ++i) {
81             int ret;
82 84           const char *pattern = strspec->strings[i];
83 84           git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
84 84 50         if (!match)
85 0           return -1;
86              
87 84           match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
88              
89 84           ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
90 84 50         if (ret == GIT_ENOTFOUND) {
91 0           git__free(match);
92 0           continue;
93 84 50         } else if (ret < 0) {
94 0           git__free(match);
95 0           return ret;
96             }
97              
98 84 50         if (git_vector_insert(vspec, match) < 0)
99 84           return -1;
100             }
101              
102 74           return 0;
103             }
104              
105             /* free data from the pathspec vector */
106 371           void git_pathspec__vfree(git_vector *vspec)
107             {
108 371           git_vector_free_deep(vspec);
109 371           }
110              
111             struct pathspec_match_context {
112             int wildmatch_flags;
113             int (*strcomp)(const char *, const char *);
114             int (*strncomp)(const char *, const char *, size_t);
115             };
116              
117 40           static void pathspec_match_context_init(
118             struct pathspec_match_context *ctxt,
119             bool disable_fnmatch,
120             bool casefold)
121             {
122 40 50         if (disable_fnmatch)
123 0           ctxt->wildmatch_flags = -1;
124 40 100         else if (casefold)
125 3           ctxt->wildmatch_flags = WM_CASEFOLD;
126             else
127 37           ctxt->wildmatch_flags = 0;
128              
129 40 100         if (casefold) {
130 3           ctxt->strcomp = git__strcasecmp;
131 3           ctxt->strncomp = git__strncasecmp;
132             } else {
133 37           ctxt->strcomp = git__strcmp;
134 37           ctxt->strncomp = git__strncmp;
135             }
136 40           }
137              
138 57           static int pathspec_match_one(
139             const git_attr_fnmatch *match,
140             struct pathspec_match_context *ctxt,
141             const char *path)
142             {
143 57           int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : WM_NOMATCH;
144              
145 57 50         if (result == WM_NOMATCH)
146 57           result = ctxt->strcomp(match->pattern, path) ? WM_NOMATCH : 0;
147              
148 57 50         if (ctxt->wildmatch_flags >= 0 && result == WM_NOMATCH)
    100          
149 40           result = wildmatch(match->pattern, path, ctxt->wildmatch_flags);
150              
151             /* if we didn't match, look for exact dirname prefix match */
152 57 100         if (result == WM_NOMATCH &&
    100          
153 9 100         (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
154 12 100         ctxt->strncomp(path, match->pattern, match->length) == 0 &&
155 3           path[match->length] == '/')
156 1           result = 0;
157              
158             /* if we didn't match and this is a negative match, check for exact
159             * match of filename with leading '!'
160             */
161 57 100         if (result == WM_NOMATCH &&
    50          
162 0 0         (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
163 0 0         *path == '!' &&
164 0 0         ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
165 0 0         (!path[match->length + 1] || path[match->length + 1] == '/'))
166 0           return 1;
167              
168 57 100         if (result == 0)
169 37           return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
170 20           return -1;
171             }
172              
173 50           static int git_pathspec__match_at(
174             size_t *matched_at,
175             const git_vector *vspec,
176             struct pathspec_match_context *ctxt,
177             const char *path0,
178             const char *path1)
179             {
180 50           int result = GIT_ENOTFOUND;
181 50           size_t i = 0;
182             const git_attr_fnmatch *match;
183              
184 68 100         git_vector_foreach(vspec, i, match) {
185 55 50         if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
    100          
186 37           break;
187 18 50         if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
    0          
188 0           break;
189             }
190              
191 50           *matched_at = i;
192 50           return result;
193             }
194              
195             /* match a path against the vectorized pathspec */
196 716           bool git_pathspec__match(
197             const git_vector *vspec,
198             const char *path,
199             bool disable_fnmatch,
200             bool casefold,
201             const char **matched_pathspec,
202             size_t *matched_at)
203             {
204             int result;
205             size_t pos;
206             struct pathspec_match_context ctxt;
207              
208 716 100         if (matched_pathspec)
209 651           *matched_pathspec = NULL;
210 716 50         if (matched_at)
211 0           *matched_at = GIT_PATHSPEC_NOMATCH;
212              
213 716 50         if (!vspec || !vspec->length)
    100          
214 687           return true;
215              
216 29           pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
217              
218 29           result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
219 29 100         if (result >= 0) {
220 19 100         if (matched_pathspec) {
221 18           const git_attr_fnmatch *match = git_vector_get(vspec, pos);
222 18           *matched_pathspec = match->pattern;
223             }
224              
225 19 50         if (matched_at)
226 0           *matched_at = pos;
227             }
228              
229 716           return (result > 0);
230             }
231              
232              
233 20           int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
234             {
235 20           int error = 0;
236              
237 20           memset(ps, 0, sizeof(*ps));
238              
239 20           ps->prefix = git_pathspec_prefix(paths);
240              
241 20 50         if ((error = git_pool_init(&ps->pool, 1)) < 0 ||
    50          
242 20           (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
243 0           git_pathspec__clear(ps);
244              
245 20           return error;
246             }
247              
248 20           void git_pathspec__clear(git_pathspec *ps)
249             {
250 20           git__free(ps->prefix);
251 20           git_pathspec__vfree(&ps->pathspec);
252 20           git_pool_clear(&ps->pool);
253 20           memset(ps, 0, sizeof(*ps));
254 20           }
255              
256 7           int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
257             {
258 7           int error = 0;
259 7           git_pathspec *ps = git__malloc(sizeof(git_pathspec));
260 7 50         GIT_ERROR_CHECK_ALLOC(ps);
261              
262 7 50         if ((error = git_pathspec__init(ps, pathspec)) < 0) {
263 0           git__free(ps);
264 0           return error;
265             }
266              
267 7           GIT_REFCOUNT_INC(ps);
268 7           *out = ps;
269 7           return 0;
270             }
271              
272 7           static void pathspec_free(git_pathspec *ps)
273             {
274 7           git_pathspec__clear(ps);
275 7           git__free(ps);
276 7           }
277              
278 18           void git_pathspec_free(git_pathspec *ps)
279             {
280 18 50         if (!ps)
281 0           return;
282 18 100         GIT_REFCOUNT_DEC(ps, pathspec_free);
    50          
283             }
284              
285 0           int git_pathspec_matches_path(
286             const git_pathspec *ps, uint32_t flags, const char *path)
287             {
288 0           bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
289 0           bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
290              
291 0 0         GIT_ASSERT_ARG(ps);
292 0 0         GIT_ASSERT_ARG(path);
293              
294 0           return (0 != git_pathspec__match(
295             &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
296             }
297              
298 11           static void pathspec_match_free(git_pathspec_match_list *m)
299             {
300 11 50         if (!m)
301 0           return;
302              
303 11           git_pathspec_free(m->pathspec);
304 11           m->pathspec = NULL;
305              
306 11           git_array_clear(m->matches);
307 11           git_array_clear(m->failures);
308 11           git_pool_clear(&m->pool);
309 11           git__free(m);
310             }
311              
312 11           static git_pathspec_match_list *pathspec_match_alloc(
313             git_pathspec *ps, int datatype)
314             {
315 11           git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
316 11 50         if (!m)
317 0           return NULL;
318              
319 11 50         if (git_pool_init(&m->pool, 1) < 0)
320 0           return NULL;
321              
322             /* need to keep reference to pathspec and increment refcount because
323             * failures array stores pointers to the pattern strings of the
324             * pathspec that had no matches
325             */
326 11           GIT_REFCOUNT_INC(ps);
327 11           m->pathspec = ps;
328 11           m->datatype = datatype;
329              
330 11           return m;
331             }
332              
333 18           GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
334             {
335 18 100         if (!git_bitvec_get(used, pos)) {
336 9           git_bitvec_set(used, pos, true);
337 9           return 1;
338             }
339              
340 9           return 0;
341             }
342              
343 2           static size_t pathspec_mark_remaining(
344             git_bitvec *used,
345             git_vector *patterns,
346             struct pathspec_match_context *ctxt,
347             size_t start,
348             const char *path0,
349             const char *path1)
350             {
351 2           size_t count = 0;
352              
353 2 50         if (path1 == path0)
354 0           path1 = NULL;
355              
356 4 100         for (; start < patterns->length; ++start) {
357 2           const git_attr_fnmatch *pat = git_vector_get(patterns, start);
358              
359 2 50         if (git_bitvec_get(used, start))
360 0           continue;
361              
362 2 50         if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
    50          
363 0           count += pathspec_mark_pattern(used, start);
364 2 50         else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
    0          
365 0           count += pathspec_mark_pattern(used, start);
366             }
367              
368 2           return count;
369             }
370              
371 2           static int pathspec_build_failure_array(
372             git_pathspec_string_array_t *failures,
373             git_vector *patterns,
374             git_bitvec *used,
375             git_pool *pool)
376             {
377             size_t pos;
378             char **failed;
379             const git_attr_fnmatch *pat;
380              
381 5 100         for (pos = 0; pos < patterns->length; ++pos) {
382 3 100         if (git_bitvec_get(used, pos))
383 1           continue;
384              
385 2 50         if ((failed = git_array_alloc(*failures)) == NULL)
    50          
    50          
386 0           return -1;
387              
388 2           pat = git_vector_get(patterns, pos);
389              
390 2 50         if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
391 0           return -1;
392             }
393              
394 2           return 0;
395             }
396              
397 11           static int pathspec_match_from_iterator(
398             git_pathspec_match_list **out,
399             git_iterator *iter,
400             uint32_t flags,
401             git_pathspec *ps)
402             {
403 11           int error = 0;
404 11           git_pathspec_match_list *m = NULL;
405 11           const git_index_entry *entry = NULL;
406             struct pathspec_match_context ctxt;
407 11           git_vector *patterns = &ps->pathspec;
408 11 50         bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
    100          
409 11 50         bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
    50          
410 11           size_t pos, used_ct = 0, found_files = 0;
411 11           git_index *index = NULL;
412             git_bitvec used_patterns;
413             char **file;
414              
415 11 50         if (git_bitvec_init(&used_patterns, patterns->length) < 0)
416 0           return -1;
417              
418 11 50         if (out) {
419 11           *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
420 11 50         GIT_ERROR_CHECK_ALLOC(m);
421             }
422              
423 11 50         if ((error = git_iterator_reset_range(iter, ps->prefix, ps->prefix)) < 0)
424 0           goto done;
425              
426 17 100         if (git_iterator_type(iter) == GIT_ITERATOR_WORKDIR &&
    50          
427 6           (error = git_repository_index__weakptr(
428             &index, git_iterator_owner(iter))) < 0)
429 0           goto done;
430              
431 11           pathspec_match_context_init(
432 11           &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
433 11           git_iterator_ignore_case(iter));
434              
435 32 100         while (!(error = git_iterator_advance(&entry, iter))) {
436             /* search for match with entry->path */
437 21           int result = git_pathspec__match_at(
438 21           &pos, patterns, &ctxt, entry->path, NULL);
439              
440             /* no matches for this path */
441 21 100         if (result < 0)
442 3           continue;
443              
444             /* if result was a negative pattern match, then don't list file */
445 18 50         if (!result) {
446 0           used_ct += pathspec_mark_pattern(&used_patterns, pos);
447 0           continue;
448             }
449              
450             /* check if path is ignored and untracked */
451 24           if (index != NULL &&
452 6 0         git_iterator_current_is_ignored(iter) &&
453 0           git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0)
454 0           continue;
455              
456             /* mark the matched pattern as used */
457 18           used_ct += pathspec_mark_pattern(&used_patterns, pos);
458 18           ++found_files;
459              
460             /* if find_failures is on, check if any later patterns also match */
461 18 100         if (find_failures && used_ct < patterns->length)
    100          
462 2           used_ct += pathspec_mark_remaining(
463 2           &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
464              
465             /* if only looking at failures, exit early or just continue */
466 18 50         if (failures_only || !out) {
    50          
467 0 0         if (used_ct == patterns->length)
468 0           break;
469 0           continue;
470             }
471              
472             /* insert matched path into matches array */
473 36 100         if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
    50          
474 18           (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
475 0           error = -1;
476 0           goto done;
477             }
478             }
479              
480 11 50         if (error < 0 && error != GIT_ITEROVER)
    50          
481 0           goto done;
482 11           error = 0;
483              
484             /* insert patterns that had no matches into failures array */
485 11 100         if (find_failures && used_ct < patterns->length &&
    100          
    50          
486 2           (error = pathspec_build_failure_array(
487             &m->failures, patterns, &used_patterns, &m->pool)) < 0)
488 0           goto done;
489              
490             /* if every pattern failed to match, then we have failed */
491 11 100         if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
    50          
492 1           git_error_set(GIT_ERROR_INVALID, "no matching files were found");
493 1           error = GIT_ENOTFOUND;
494             }
495              
496             done:
497 11           git_bitvec_free(&used_patterns);
498              
499 11 100         if (error < 0) {
500 1           pathspec_match_free(m);
501 1 50         if (out) *out = NULL;
502             }
503              
504 11           return error;
505             }
506              
507 11           static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
508             {
509 11           git_iterator_flag_t f = 0;
510              
511 11 100         if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
512 3           f |= GIT_ITERATOR_IGNORE_CASE;
513 8 50         else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
514 0           f |= GIT_ITERATOR_DONT_IGNORE_CASE;
515              
516 11           return f;
517             }
518              
519 6           int git_pathspec_match_workdir(
520             git_pathspec_match_list **out,
521             git_repository *repo,
522             uint32_t flags,
523             git_pathspec *ps)
524             {
525             git_iterator *iter;
526 6           git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
527 6           int error = 0;
528              
529 6 50         GIT_ASSERT_ARG(repo);
530              
531 6           iter_opts.flags = pathspec_match_iter_flags(flags);
532              
533 6 50         if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) {
534 6           error = pathspec_match_from_iterator(out, iter, flags, ps);
535 6           git_iterator_free(iter);
536             }
537              
538 6           return error;
539             }
540              
541 1           int git_pathspec_match_index(
542             git_pathspec_match_list **out,
543             git_index *index,
544             uint32_t flags,
545             git_pathspec *ps)
546             {
547             git_iterator *iter;
548 1           git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
549 1           int error = 0;
550              
551 1 50         GIT_ASSERT_ARG(index);
552              
553 1           iter_opts.flags = pathspec_match_iter_flags(flags);
554              
555 1 50         if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) {
556 1           error = pathspec_match_from_iterator(out, iter, flags, ps);
557 1           git_iterator_free(iter);
558             }
559              
560 1           return error;
561             }
562              
563 4           int git_pathspec_match_tree(
564             git_pathspec_match_list **out,
565             git_tree *tree,
566             uint32_t flags,
567             git_pathspec *ps)
568             {
569             git_iterator *iter;
570 4           git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
571 4           int error = 0;
572              
573 4 50         GIT_ASSERT_ARG(tree);
574              
575 4           iter_opts.flags = pathspec_match_iter_flags(flags);
576              
577 4 50         if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) {
578 4           error = pathspec_match_from_iterator(out, iter, flags, ps);
579 4           git_iterator_free(iter);
580             }
581              
582 4           return error;
583             }
584              
585 0           int git_pathspec_match_diff(
586             git_pathspec_match_list **out,
587             git_diff *diff,
588             uint32_t flags,
589             git_pathspec *ps)
590             {
591 0           int error = 0;
592 0           git_pathspec_match_list *m = NULL;
593             struct pathspec_match_context ctxt;
594 0           git_vector *patterns = &ps->pathspec;
595 0 0         bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
    0          
596 0 0         bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
    0          
597 0           size_t i, pos, used_ct = 0, found_deltas = 0;
598             const git_diff_delta *delta, **match;
599             git_bitvec used_patterns;
600              
601 0 0         GIT_ASSERT_ARG(diff);
602              
603 0 0         if (git_bitvec_init(&used_patterns, patterns->length) < 0)
604 0           return -1;
605              
606 0 0         if (out) {
607 0           *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
608 0 0         GIT_ERROR_CHECK_ALLOC(m);
609             }
610              
611 0           pathspec_match_context_init(
612 0           &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
613 0           git_diff_is_sorted_icase(diff));
614              
615 0 0         git_vector_foreach(&diff->deltas, i, delta) {
616             /* search for match with delta */
617 0           int result = git_pathspec__match_at(
618             &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
619              
620             /* no matches for this path */
621 0 0         if (result < 0)
622 0           continue;
623              
624             /* mark the matched pattern as used */
625 0           used_ct += pathspec_mark_pattern(&used_patterns, pos);
626              
627             /* if result was a negative pattern match, then don't list file */
628 0 0         if (!result)
629 0           continue;
630              
631 0           ++found_deltas;
632              
633             /* if find_failures is on, check if any later patterns also match */
634 0 0         if (find_failures && used_ct < patterns->length)
    0          
635 0           used_ct += pathspec_mark_remaining(
636             &used_patterns, patterns, &ctxt, pos + 1,
637             delta->old_file.path, delta->new_file.path);
638              
639             /* if only looking at failures, exit early or just continue */
640 0 0         if (failures_only || !out) {
    0          
641 0 0         if (used_ct == patterns->length)
642 0           break;
643 0           continue;
644             }
645              
646             /* insert matched delta into matches array */
647 0 0         if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
    0          
    0          
648 0           error = -1;
649 0           goto done;
650             } else {
651 0           *match = delta;
652             }
653             }
654              
655             /* insert patterns that had no matches into failures array */
656 0 0         if (find_failures && used_ct < patterns->length &&
    0          
    0          
657 0           (error = pathspec_build_failure_array(
658             &m->failures, patterns, &used_patterns, &m->pool)) < 0)
659 0           goto done;
660              
661             /* if every pattern failed to match, then we have failed */
662 0 0         if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
    0          
663 0           git_error_set(GIT_ERROR_INVALID, "no matching deltas were found");
664 0           error = GIT_ENOTFOUND;
665             }
666              
667             done:
668 0           git_bitvec_free(&used_patterns);
669              
670 0 0         if (error < 0) {
671 0           pathspec_match_free(m);
672 0 0         if (out) *out = NULL;
673             }
674              
675 0           return error;
676             }
677              
678 10           void git_pathspec_match_list_free(git_pathspec_match_list *m)
679             {
680 10 50         if (m)
681 10           pathspec_match_free(m);
682 10           }
683              
684 15           size_t git_pathspec_match_list_entrycount(
685             const git_pathspec_match_list *m)
686             {
687 15 50         return m ? git_array_size(m->matches) : 0;
688             }
689              
690 12           const char *git_pathspec_match_list_entry(
691             const git_pathspec_match_list *m, size_t pos)
692             {
693 12 50         if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
    50          
    50          
694 12           !git_array_valid_index(m->matches, pos))
695 0           return NULL;
696              
697 12 50         return *((const char **)git_array_get(m->matches, pos));
698             }
699              
700 0           const git_diff_delta *git_pathspec_match_list_diff_entry(
701             const git_pathspec_match_list *m, size_t pos)
702             {
703 0 0         if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
    0          
    0          
704 0           !git_array_valid_index(m->matches, pos))
705 0           return NULL;
706              
707 0 0         return *((const git_diff_delta **)git_array_get(m->matches, pos));
708             }
709              
710 4           size_t git_pathspec_match_list_failed_entrycount(
711             const git_pathspec_match_list *m)
712             {
713 4 50         return m ? git_array_size(m->failures) : 0;
714             }
715              
716 1           const char * git_pathspec_match_list_failed_entry(
717             const git_pathspec_match_list *m, size_t pos)
718             {
719 1 50         char **entry = m ? git_array_get(m->failures, pos) : NULL;
    50          
720              
721 1 50         return entry ? *entry : NULL;
722             }