1#include <apr_file_io.h>
2#include <apr_strings.h>
6#include "ft_traverse.h"
10#define MATCH_VECTOR_SIZE 210
12static apr_status_t traverse_recursive(
ft_conf_t *conf,
const char *filename, apr_pool_t *gc_pool,
struct stats
const *stats,
18 apr_int32_t statmask =
19 APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_TYPE | APR_FINFO_USER | APR_FINFO_GROUP | APR_FINFO_UPROT |
22 apr_uint32_t hash_value;
25 if (!is_option_set(conf->mask, OPTION_FSYML)) {
26 statmask |= APR_FINFO_LINK;
29 if (APR_SUCCESS != (status = apr_stat(&finfo, filename, statmask, gc_pool))) {
30 if (is_option_set(conf->mask, OPTION_FSYML)) {
31 statmask ^= APR_FINFO_LINK;
32 if ((APR_SUCCESS == apr_stat(&finfo, filename, statmask, gc_pool)) && (finfo.filetype & APR_LNK)) {
33 if (is_option_set(conf->mask, OPTION_VERBO)) {
34 (void) fprintf(stderr,
"Skipping : [%s] (broken link)\n", filename);
40 DEBUG_ERR(
"error calling apr_stat on filename %s : %s", filename, apr_strerror(status, errbuf, 128));
44 if (conf->respect_gitignore && parent_ctx) {
47 if (is_option_set(conf->mask, OPTION_VERBO)) {
48 (void) fprintf(stderr,
"Ignoring (gitignore): [%s]\n", filename);
54 if (0 != conf->userid) {
55 if (finfo.user == conf->userid) {
56 if (!(APR_UREAD & finfo.protection)) {
57 if (is_option_set(conf->mask, OPTION_VERBO)) {
58 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
63 else if (NULL !=
napr_hash_search(conf->gids, &finfo.group,
sizeof(gid_t), &hash_value)) {
64 if (!(APR_GREAD & finfo.protection)) {
65 if (is_option_set(conf->mask, OPTION_VERBO)) {
66 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
71 else if (!(APR_WREAD & finfo.protection)) {
72 if (is_option_set(conf->mask, OPTION_VERBO)) {
73 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
79 if (APR_DIR == finfo.filetype) {
80 if (0 != conf->userid) {
81 if (finfo.user == conf->userid) {
82 if (!(APR_UEXECUTE & finfo.protection)) {
83 if (is_option_set(conf->mask, OPTION_VERBO)) {
84 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
89 else if (NULL !=
napr_hash_search(conf->gids, &finfo.group,
sizeof(gid_t), &hash_value)) {
90 if (!(APR_GEXECUTE & finfo.protection)) {
91 if (is_option_set(conf->mask, OPTION_VERBO)) {
92 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
97 else if (!(APR_WEXECUTE & finfo.protection)) {
98 if (is_option_set(conf->mask, OPTION_VERBO)) {
99 (void) fprintf(stderr,
"Skipping : [%s] (bad permission)\n", filename);
105 if (APR_SUCCESS != (status = apr_dir_open(&dir, filename, gc_pool))) {
106 DEBUG_ERR(
"error calling apr_dir_open(%s): %s", filename, apr_strerror(status, errbuf, 128));
109 fname_len = strlen(filename);
112 if (conf->respect_gitignore) {
113 const char *gitignore_path = apr_pstrcat(gc_pool, filename,
"/.gitignore", NULL);
114 apr_finfo_t gitignore_finfo;
116 if (APR_SUCCESS == apr_stat(&gitignore_finfo, gitignore_path, APR_FINFO_TYPE, gc_pool)
117 && gitignore_finfo.filetype == APR_REG) {
120 current_ctx = local_ctx;
121 if (is_option_set(conf->mask, OPTION_VERBO)) {
122 (void) fprintf(stderr,
"Loaded .gitignore from: [%s]\n", filename);
127 while ((APR_SUCCESS == (status = apr_dir_read(&finfo, APR_FINFO_NAME | APR_FINFO_TYPE, dir)))
128 && (NULL != finfo.name)) {
130 apr_size_t fullname_len;
132 struct stats const *ancestor;
134 if (NULL !=
napr_hash_search(conf->ig_files, finfo.name, strlen(finfo.name), NULL)) {
138 if (
'.' == finfo.name[0] && !is_option_set(conf->mask, OPTION_SHOW_HIDDEN)) {
142 if (APR_DIR == finfo.filetype && !is_option_set(conf->mask, OPTION_RECSD)) {
146 fullname = apr_pstrcat(gc_pool, filename, (
'/' == filename[fname_len - 1]) ?
"" :
"/", finfo.name, NULL);
147 fullname_len = strlen(fullname);
149 if ((NULL != conf->ig_regex) && (APR_DIR != finfo.filetype)) {
151 int ovector[MATCH_VECTOR_SIZE];
152 match_code = pcre_exec(conf->ig_regex, NULL, fullname, fullname_len, 0, 0, ovector, MATCH_VECTOR_SIZE);
153 if (match_code >= 0) {
158 if ((NULL != conf->wl_regex) && (APR_DIR != finfo.filetype)) {
160 int ovector[MATCH_VECTOR_SIZE];
161 match_code = pcre_exec(conf->wl_regex, NULL, fullname, fullname_len, 0, 0, ovector, MATCH_VECTOR_SIZE);
162 if (match_code < 0) {
168 if (stats->stat.inode) {
169 for (ancestor = stats; (ancestor = ancestor->parent) != 0;) {
170 if (ancestor->stat.inode == stats->stat.inode && ancestor->stat.device == stats->stat.device) {
171 if (is_option_set(conf->mask, OPTION_VERBO)) {
172 (void) fprintf(stderr,
"Warning: %s: recursive directory loop\n", filename);
179 child.parent = stats;
182 status = traverse_recursive(conf, fullname, gc_pool, &child, current_ctx);
184 if (APR_SUCCESS != status) {
185 DEBUG_ERR(
"error recursively calling traverse_recursive: %s", apr_strerror(status, errbuf, 128));
189 if ((APR_SUCCESS != status) && (APR_ENOENT != status)) {
190 DEBUG_ERR(
"error calling apr_dir_read: %s", apr_strerror(status, errbuf, 128));
194 if (APR_SUCCESS != (status = apr_dir_close(dir))) {
195 DEBUG_ERR(
"error calling apr_dir_close: %s", apr_strerror(status, errbuf, 128));
199 else if (APR_REG == finfo.filetype || ((APR_LNK == finfo.filetype) && (is_option_set(conf->mask, OPTION_FSYML)))) {
200 if (finfo.size >= conf->minsize && (conf->maxsize == 0 || finfo.size <= conf->maxsize)) {
204 file = apr_palloc(conf->pool,
sizeof(
struct ft_file_t));
205 file->path = apr_pstrdup(conf->pool, filename);
206 file->size = finfo.size;
207 file->mtime = finfo.mtime;
209 if ((conf->p_path) && (strlen(filename) >= conf->p_path_len)
210 && ((is_option_set(conf->mask, OPTION_ICASE) && !strncasecmp(filename, conf->p_path, conf->p_path_len))
211 || (!is_option_set(conf->mask, OPTION_ICASE) && !memcmp(filename, conf->p_path, conf->p_path_len)))) {
212 file->prioritized |= 0x1;
215 file->prioritized &= 0x0;
217 file->cvec_ok &= 0x0;
220 if (NULL == (fsize =
napr_hash_search(conf->sizes, &finfo.size,
sizeof(apr_off_t), &hash_value))) {
221 fsize = apr_palloc(conf->pool,
sizeof(
struct ft_fsize_t));
222 fsize->val = finfo.size;
223 fsize->chksum_array = NULL;
224 fsize->nb_checksumed = 0;
235apr_status_t ft_traverse_path(
ft_conf_t *conf,
const char *path)
240 if (APR_SUCCESS != (status = apr_pool_create(&gc_pool, conf->pool))) {
242 DEBUG_ERR(
"error calling apr_pool_create: %s", apr_strerror(status, errbuf, 128));
246 status = traverse_recursive(conf, path, gc_pool, NULL, conf->global_ignores);
248 apr_pool_destroy(gc_pool);
UTIL debug output macros.
#define DEBUG_ERR(str, arg...)
Display error message at the level error.
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.
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.
ft_ignore_match_result_t
Result codes for an ignore match operation.
@ FT_IGNORE_MATCH_IGNORED
The path is matched by an ignore pattern.
apr_status_t napr_hash_set(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
Inserts or updates an item in the hash table.
void * napr_hash_search(napr_hash_t *hash, const void *key, apr_size_t key_len, apr_uint32_t *hash_value)
Searches the hash table for an item.
int napr_heap_insert(napr_heap_t *heap, void *datum)
Inserts an element into the heap, maintaining the heap property.
Main configuration structure for the ftwin application.
Represents the ignore rules for a specific directory and its descendants.