#define _POSIX_C_SOURCE 200809L #include "Pattern/Pattern.h" #include #include #include #include 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; }