diff options
Diffstat (limited to 'filter.c')
-rw-r--r-- | filter.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..312704a --- /dev/null +++ b/filter.c @@ -0,0 +1,115 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <assert.h> +#include "filter.h" + + +struct filter_rule parse_exclude_rule(const char *rule) { + const size_t len = strlen(rule); + + const bool open_left = rule[0] == '*'; + if (open_left) { + if (len < 4 || memcmp(rule, "**/", 3) != 0) { + fprintf(stderr, "Error: A filter rule starting with '*' must start with '**/'.\n"); + exit(1); + } + } else { + if (rule[0] != '/') { + fprintf(stderr, "Error: A filter rule with a non-wildcard left-hand side must start with '/'.\n"); + exit(1); + } + } + + const bool open_right = len > 0 && rule[len - 1] == '*'; + if (open_right) { + if (len < 4 || memcmp(rule + len - 3, "/**", 3) != 0) { + fprintf(stderr, "Error: A filter rule ending with '*' must end with '/**'.\n"); + exit(1); + } + } + + const size_t str_start = open_left ? 3 : 1; + const size_t str_len = len - str_start - (open_right ? 3 : 0); + + for (size_t i = 0; i < str_len; i++) { + if (rule[str_start + i] == '*') { + fprintf(stderr, "Error: Wildcards in the middle of a pattern unsupported.\n"); + exit(1); + } + } + + char *str = malloc(str_len + 1); + memcpy(str, rule + str_start, str_len); + str[str_len] = '\0'; + + return (struct filter_rule){ + .ftype = FILTER_EXCLUDE, + .ptype = open_left && open_right ? PATTERN_INFIX + : open_left ? PATTERN_SUFFIX + : open_right ? PATTERN_PREFIX + : PATTERN_EQUAL, + .str = str, + .len = str_len, + }; +} + +bool filter_rule_allows(const struct filter_rule rule, const char *path) { + assert(rule.ftype == FILTER_EXCLUDE); + + switch (rule.ptype) { + case PATTERN_EQUAL: + if (strcmp(path, rule.str) == 0) return false; + break; + + case PATTERN_PREFIX: + if (strncmp(path, rule.str, rule.len) == 0 && + (path[rule.len] == '/' || path[rule.len] == '\0')) + return false; + break; + + case PATTERN_SUFFIX: { + const size_t pathlen = strlen(path); + if (pathlen >= rule.len && + memcmp(path + pathlen - rule.len, rule.str, rule.len) == 0 && + (pathlen == rule.len || path[pathlen - rule.len - 1] == '/')) + return false; + break; + } + + case PATTERN_INFIX: { + const size_t pathlen = strlen(path); + if (strncmp(path, rule.str, rule.len) == 0 && + (path[rule.len] == '\0' || path[rule.len] == '/')) return false; + for (size_t i = 0; i < pathlen; i++) { + if (path[i] == '/') { + if (strncmp(path + i+1, rule.str, rule.len) == 0 && + (path[i+1 + rule.len] == '\0' || path[i+1 + rule.len] == '/')) return false; + } + } + break; + } + } + + return true; +} + +void filter_rule_debugdump(FILE *stream, const struct filter_rule rule) { + const char *ftype_str; + switch (rule.ftype) { + case FILTER_EXCLUDE: ftype_str = "exclude"; break; + default: ftype_str = "?unknown_ftype"; break; + } + + const char *ptype_str; + switch (rule.ptype) { + case PATTERN_EQUAL: ptype_str = "equal"; break; + case PATTERN_PREFIX: ptype_str = "prefix"; break; + case PATTERN_SUFFIX: ptype_str = "suffix"; break; + case PATTERN_INFIX: ptype_str = "infix"; break; + default: ptype_str = "?unknown_ptype"; break; + } + + fprintf(stream, "%s %s <%s>\n", ftype_str, ptype_str, rule.str); +} |