diff options
| author | frosty <frostinator33@airmail.cc> | 2026-10-03 13:30:21 -0400 |
|---|---|---|
| committer | frosty <frostinator33@airmail.cc> | 2026-10-03 13:30:21 -0400 |
| commit | 65500761a632d22057d3b3e709b91dde168e125b (patch) | |
| tree | 0ff30abf8de4120e5ed8db296688e0d85b65736b /src/Pattern/Pattern.c | |
| parent | cb716af752fb0b46aa8f78d3cbd5189a09642f39 (diff) | |
feat: add --exclude/--include
Diffstat (limited to 'src/Pattern/Pattern.c')
| -rw-r--r-- | src/Pattern/Pattern.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/Pattern/Pattern.c b/src/Pattern/Pattern.c new file mode 100644 index 0000000..9147e5e --- /dev/null +++ b/src/Pattern/Pattern.c @@ -0,0 +1,148 @@ +#define _POSIX_C_SOURCE 200809L + +#include "Pattern/Pattern.h" + +#include <fnmatch.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +static char *duplicate_string(const char *value, size_t length) { + char *copy = malloc(length + 1); + + if (copy != NULL) { + memcpy(copy, value, length); + copy[length] = '\0'; + } + return copy; +} + +static int grow_pattern_list(PatternList *list) { + size_t larger_capacity; + Pattern *larger_items; + + if (list->capacity > SIZE_MAX / (2 * sizeof(*list->items))) { + return -1; + } + + larger_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + larger_items = realloc(list->items, larger_capacity * sizeof(*list->items)); + if (larger_items == NULL) { + return -1; + } + + list->items = larger_items; + list->capacity = larger_capacity; + return 0; +} + +int pattern_list_append(PatternList *list, PatternKind kind, + const char *pattern) { + size_t length; + int directory_only; + Pattern *entry; + + if (pattern == NULL) { + return -1; + } + + length = strlen(pattern); + directory_only = length > 0 && pattern[length - 1] == '/'; + if (directory_only) { + --length; + } + if (length == 0) { + return -1; + } + + if (list->length == list->capacity && grow_pattern_list(list) != 0) { + return -1; + } + + entry = &list->items[list->length]; + entry->kind = kind; + entry->directory_only = directory_only; + entry->pattern = duplicate_string(pattern, length); + if (entry->pattern == NULL) { + return -1; + } + + ++list->length; + return 0; +} + +void pattern_list_clear(PatternList *list) { + size_t index; + + for (index = 0; index < list->length; ++index) { + free(list->items[index].pattern); + } + free(list->items); + list->items = NULL; + list->length = 0; + list->capacity = 0; +} + +static int has_include(const PatternList *list) { + size_t index; + + for (index = 0; index < list->length; ++index) { + if (list->items[index].kind == PATTERN_INCLUDE) { + return 1; + } + } + return 0; +} + +PatternKind pattern_list_default_kind(const PatternList *list) { + return has_include(list) ? PATTERN_EXCLUDE : PATTERN_INCLUDE; +} + +static const char *base_name(const char *path) { + const char *slash = strrchr(path, '/'); + + return slash == NULL ? path : slash + 1; +} + +static int matches_anchored(const char *pattern, const char *path) { + const char *cursor = path; + + if (fnmatch(pattern, path, FNM_PATHNAME) == 0) { + return 1; + } + while (*cursor != '\0') { + cursor = strchr(cursor, '/'); + if (cursor == NULL) { + break; + } + ++cursor; + if (fnmatch(pattern, cursor, FNM_PATHNAME) == 0) { + return 1; + } + } + return 0; +} + +static int matches_pattern(const Pattern *pattern, const char *path) { + if (strchr(pattern->pattern, '/') == NULL) { + return fnmatch(pattern->pattern, base_name(path), 0) == 0; + } + return matches_anchored(pattern->pattern, path); +} + +PatternKind pattern_list_resolve(const PatternList *list, const char *path, + int is_directory, PatternKind fallback) { + size_t index; + + for (index = 0; index < list->length; ++index) { + const Pattern *pattern = &list->items[index]; + + if (pattern->directory_only && !is_directory) { + continue; + } + if (matches_pattern(pattern, path)) { + return pattern->kind; + } + } + return fallback; +}
\ No newline at end of file |
