+ set_filter_dir(dir, dirlen);
+
+ if (!mergelist_cnt)
+ return NULL;
+
+ push = new_array(struct filter_list_struct, mergelist_cnt);
+ if (!push)
+ out_of_memory("push_local_filters");
+
+ for (i = 0, ap = push; i < mergelist_cnt; i++) {
+ memcpy(ap++, mergelist_parents[i]->u.mergelist,
+ sizeof (struct filter_list_struct));
+ }
+
+ /* Note: parse_filter_file() might increase mergelist_cnt, so keep
+ * this loop separate from the above loop. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ struct filter_struct *ex = mergelist_parents[i];
+ struct filter_list_struct *lp = ex->u.mergelist;
+
+ if (verbose > 2) {
+ rprintf(FINFO, "[%s] pushing filter list%s\n",
+ who_am_i(), lp->debug_type);
+ }
+
+ lp->tail = NULL; /* Switch any local rules to inherited. */
+ if (ex->match_flags & MATCHFLG_NO_INHERIT)
+ lp->head = NULL;
+
+ if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
+ ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
+ if (setup_merge_file(ex, lp))
+ set_filter_dir(dir, dirlen);
+ }
+
+ if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len) {
+ parse_filter_file(lp, dirbuf, ex->match_flags,
+ XFLG_ANCHORED2ABS);
+ } else {
+ io_error |= IOERR_GENERAL;
+ rprintf(FINFO,
+ "cannot add local filter rules in long-named directory: %s\n",
+ full_fname(dirbuf));
+ }
+ dirbuf[dirbuf_len] = '\0';
+ }
+
+ return (void*)push;
+}
+
+void pop_local_filters(void *mem)
+{
+ struct filter_list_struct *ap, *pop = (struct filter_list_struct*)mem;
+ int i;
+
+ for (i = mergelist_cnt; i-- > 0; ) {
+ struct filter_struct *ex = mergelist_parents[i];
+ struct filter_list_struct *lp = ex->u.mergelist;
+
+ if (verbose > 2) {
+ rprintf(FINFO, "[%s] popping filter list%s\n",
+ who_am_i(), lp->debug_type);
+ }
+
+ clear_filter_list(lp);
+ }
+
+ if (!pop)
+ return;
+
+ for (i = 0, ap = pop; i < mergelist_cnt; i++) {
+ memcpy(mergelist_parents[i]->u.mergelist, ap++,
+ sizeof (struct filter_list_struct));
+ }
+
+ free(pop);
+}
+
+static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
+{
+ int slash_handling, str_cnt = 0, anchored_match = 0;
+ int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1;
+ char *p, *pattern = ex->pattern;
+ const char *strings[16]; /* more than enough */
+
+ if (!*name)
+ return 0;
+
+ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
+ /* If the pattern does not have any slashes AND it does
+ * not have a "**" (which could match a slash), then we
+ * just match the name portion of the path. */
+ if ((p = strrchr(name,'/')) != NULL)
+ name = p+1;
+ } else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
+ && curr_dir_len > module_dirlen + 1) {
+ /* If we're matching against an absolute-path pattern,
+ * we need to prepend our full path info. */
+ strings[str_cnt++] = curr_dir + module_dirlen + 1;
+ strings[str_cnt++] = "/";
+ } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *name != '/') {
+ /* Allow "**"+"/" to match at the start of the string. */
+ strings[str_cnt++] = "/";
+ }
+ strings[str_cnt++] = name;
+ if (name_is_dir) {
+ /* Allow a trailing "/"+"***" to match the directory. */
+ if (ex->match_flags & MATCHFLG_WILD3_SUFFIX)
+ strings[str_cnt++] = "/";
+ } else if (ex->match_flags & MATCHFLG_DIRECTORY)
+ return !ret_match;
+ strings[str_cnt] = NULL;