aboutsummaryrefslogtreecommitdiff
path: root/src/Pattern
diff options
context:
space:
mode:
Diffstat (limited to 'src/Pattern')
-rw-r--r--src/Pattern/Pattern.c148
-rw-r--r--src/Pattern/Pattern.h27
2 files changed, 175 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
diff --git a/src/Pattern/Pattern.h b/src/Pattern/Pattern.h
new file mode 100644
index 0000000..52b817f
--- /dev/null
+++ b/src/Pattern/Pattern.h
@@ -0,0 +1,27 @@
+#ifndef YAPSSG_PATTERN_H
+#define YAPSSG_PATTERN_H
+
+#include <stddef.h>
+
+typedef enum { PATTERN_INCLUDE, PATTERN_EXCLUDE } PatternKind;
+
+typedef struct {
+ PatternKind kind;
+ char *pattern;
+ int directory_only;
+} Pattern;
+
+typedef struct {
+ Pattern *items;
+ size_t length;
+ size_t capacity;
+} PatternList;
+
+int pattern_list_append(PatternList *list, PatternKind kind,
+ const char *pattern);
+void pattern_list_clear(PatternList *list);
+PatternKind pattern_list_default_kind(const PatternList *list);
+PatternKind pattern_list_resolve(const PatternList *list, const char *path,
+ int is_directory, PatternKind fallback);
+
+#endif \ No newline at end of file