24#include <apr_file_io.h>
25#include <apr_strings.h>
41static char *
ft_glob_to_pcre(
const char *pattern, apr_pool_t *pool,
unsigned int *flags)
44 const char *pattern_ptr = pattern;
45 char *result_ptr = result;
46 int starts_with_slash = 0;
51 if (*pattern_ptr ==
'!') {
52 *flags |= FT_IGNORE_NEGATE;
54 while (isspace(*pattern_ptr)) {
60 if (*pattern_ptr ==
'/') {
61 starts_with_slash = 1;
66 const char *end = pattern_ptr + strlen(pattern_ptr) - 1;
67 while (end > pattern_ptr && isspace(*end)) {
71 *flags |= FT_IGNORE_DIR_ONLY;
75 if (starts_with_slash) {
88 while (*pattern_ptr) {
90 if (*pattern_ptr ==
'/' && *flags & FT_IGNORE_DIR_ONLY && *(pattern_ptr + 1) ==
'\0') {
94 if (*pattern_ptr ==
'\\' && *(pattern_ptr + 1)) {
98 *result_ptr++ = *pattern_ptr++;
100 else if (*pattern_ptr ==
'*') {
101 if (*(pattern_ptr + 1) ==
'*') {
103 if (*(pattern_ptr + 2) ==
'/') {
105 const char *pcre_pattern =
"(.*/)?";
106 strcpy(result_ptr, pcre_pattern);
107 result_ptr += strlen(pcre_pattern);
110 else if (*(pattern_ptr - 1) ==
'/' || pattern_ptr == pattern) {
112 const char *pcre_pattern =
".*";
113 strcpy(result_ptr, pcre_pattern);
114 result_ptr += strlen(pcre_pattern);
119 const char *pcre_pattern =
"[^/]*";
120 strcpy(result_ptr, pcre_pattern);
121 result_ptr += strlen(pcre_pattern);
127 const char *pcre_pattern =
"[^/]*";
128 strcpy(result_ptr, pcre_pattern);
129 result_ptr += strlen(pcre_pattern);
133 else if (*pattern_ptr ==
'?') {
135 const char *pcre_pattern =
"[^/]";
136 strcpy(result_ptr, pcre_pattern);
137 result_ptr += strlen(pcre_pattern);
140 else if (*pattern_ptr ==
'[') {
144 if (*pattern_ptr ==
'!') {
148 while (*pattern_ptr && *pattern_ptr !=
']') {
149 if (*pattern_ptr ==
'\\' && *(pattern_ptr + 1)) {
151 *result_ptr++ =
'\\';
153 *result_ptr++ = *pattern_ptr++;
155 if (*pattern_ptr ==
']')
156 *result_ptr++ = *pattern_ptr++;
158 else if (*pattern_ptr ==
'/') {
162 else if (strchr(
".^$+{}()|", *pattern_ptr)) {
164 *result_ptr++ =
'\\';
165 *result_ptr++ = *pattern_ptr++;
168 *result_ptr++ = *pattern_ptr++;
173 if (*flags & FT_IGNORE_DIR_ONLY) {
174 strcpy(result_ptr,
"/?$");
190 ctx->
base_dir = apr_pstrdup(pool, base_dir);
200 unsigned int flags = 0;
208 trimmed = pattern_str;
209 while (isspace(*trimmed))
213 if (*trimmed ==
'\0' || *trimmed ==
'#') {
221 regex = pcre_compile(regex_str, 0, &error, &erroffset, NULL);
223 DEBUG_ERR(
"Failed to compile pattern '%s': %s", trimmed, error);
229 pattern->
regex = regex;
231 pattern->
flags = flags;
245 status = apr_file_open(&file, filepath, APR_READ, APR_OS_DEFAULT, ctx->
pool);
246 if (status != APR_SUCCESS) {
250 while (apr_file_gets(line,
sizeof(line), file) == APR_SUCCESS) {
252 apr_size_t len = strlen(line);
253 if (len > 0 && line[len - 1] ==
'\n') {
254 line[len - 1] =
'\0';
257 if (len > 0 && line[len - 1] ==
'\r') {
258 line[len - 1] =
'\0';
264 (void) apr_file_close(file);
272 const char *relative_path;
274 if (!ctx || !fullpath) {
279 for (current_ctx = ctx; current_ctx != NULL; current_ctx = current_ctx->
parent) {
286 while (*relative_path ==
'/')
295 for (i = 0; i < current_ctx->
patterns->nelts; i++) {
300 if ((pattern->
flags & FT_IGNORE_DIR_ONLY) && !is_dir) {
305 match = pcre_exec(pattern->
regex, NULL, relative_path, strlen(relative_path), 0, 0, NULL, 0);
309 if (pattern->
flags & FT_IGNORE_NEGATE) {
UTIL debug output macros.
#define DEBUG_ERR(str, arg...)
Display error message at the level error.
apr_status_t ft_ignore_add_pattern_str(ft_ignore_context_t *ctx, const char *pattern_str)
Adds a single pattern string to a context.
ft_ignore_context_t * ft_ignore_context_create(apr_pool_t *pool, ft_ignore_context_t *parent, const char *base_dir)
Creates a new ignore context.
apr_status_t ft_ignore_load_file(ft_ignore_context_t *ctx, const char *filepath)
Loads and parses an ignore file (like .gitignore) into a context.
static char * ft_glob_to_pcre(const char *pattern, apr_pool_t *pool, unsigned int *flags)
Convert Git glob pattern to PCRE regex Handles: *, **, ?, [abc], !, /, escapes.
static const size_t MAX_PATTERN_LEN
The maximum length of a pattern string.
ft_ignore_match_result_t ft_ignore_match(ft_ignore_context_t *ctx, const char *fullpath, int is_dir)
Checks if a given path should be ignored based on the hierarchical context.
Interface for handling hierarchical ignore patterns, similar to .gitignore.
ft_ignore_match_result_t
Result codes for an ignore match operation.
@ FT_IGNORE_MATCH_NONE
The path is not matched by any pattern.
@ FT_IGNORE_MATCH_WHITELISTED
The path is matched by a negation (whitelist) pattern.
@ FT_IGNORE_MATCH_IGNORED
The path is matched by an ignore pattern.
Represents the ignore rules for a specific directory and its descendants.
apr_array_header_t * patterns
Array of ft_ignore_pattern_t pointers defined at this level.
const char * base_dir
The absolute path to the directory this context is anchored to.
struct ft_ignore_context_t * parent
Pointer to the parent directory's context, or NULL if root.
apr_size_t base_dir_len
The length of the base directory path.
apr_pool_t * pool
The memory pool used for allocations within this context.
Represents a single compiled ignore pattern.
unsigned int flags
Flags for the pattern (e.g., FT_IGNORE_NEGATE).
const char * pattern_str
The original, uncompiled pattern string for debugging.
pcre * regex
The compiled PCRE pattern.