+ if (cp == ret->pattern)
+ mflags |= MATCHFLG_WILD2_PREFIX;
+ /* If the pattern ends with ***, note that. */
+ if (pat_len >= 3
+ && ret->pattern[pat_len-3] == '*'
+ && ret->pattern[pat_len-2] == '*'
+ && ret->pattern[pat_len-1] == '*')
+ mflags |= MATCHFLG_WILD3_SUFFIX;
+ }
+ }
+
+ if (mflags & MATCHFLG_PERDIR_MERGE) {
+ struct filter_list_struct *lp;
+ unsigned int len;
+ int i;
+
+ if ((cp = strrchr(ret->pattern, '/')) != NULL)
+ cp++;
+ else
+ cp = ret->pattern;
+
+ /* If the local merge file was already mentioned, don't
+ * add it again. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ struct filter_struct *ex = mergelist_parents[i];
+ const char *s = strrchr(ex->pattern, '/');
+ if (s)
+ s++;
+ else
+ s = ex->pattern;
+ len = strlen(s);
+ if (len == pat_len - (cp - ret->pattern)
+ && memcmp(s, cp, len) == 0) {
+ free_filter(ret);
+ return;
+ }
+ }
+
+ if (!(lp = new_array(struct filter_list_struct, 1)))
+ out_of_memory("add_rule");
+ lp->head = lp->tail = NULL;
+ if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0)
+ out_of_memory("add_rule");
+ ret->u.mergelist = lp;
+
+ if (mergelist_cnt == mergelist_size) {
+ mergelist_size += 5;
+ mergelist_parents = realloc_array(mergelist_parents,
+ struct filter_struct *,
+ mergelist_size);
+ if (!mergelist_parents)
+ out_of_memory("add_rule");
+ }
+ mergelist_parents[mergelist_cnt++] = ret;
+ } else
+ ret->u.slash_cnt = slash_cnt;
+
+ ret->match_flags = mflags;
+
+ if (!listp->tail) {
+ ret->next = listp->head;
+ listp->head = listp->tail = ret;
+ } else {
+ ret->next = listp->tail->next;
+ listp->tail->next = ret;
+ listp->tail = ret;
+ }
+}
+
+static void clear_filter_list(struct filter_list_struct *listp)
+{
+ if (listp->tail) {
+ struct filter_struct *ent, *next;
+ /* Truncate any inherited items from the local list. */
+ listp->tail->next = NULL;
+ /* Now free everything that is left. */
+ for (ent = listp->head; ent; ent = next) {
+ next = ent->next;
+ free_filter(ent);
+ }
+ }
+
+ listp->head = listp->tail = NULL;
+}
+
+/* This returns an expanded (absolute) filename for the merge-file name if
+ * the name has any slashes in it OR if the parent_dirscan var is True;
+ * otherwise it returns the original merge_file name. If the len_ptr value
+ * is non-NULL the merge_file name is limited by the referenced length
+ * value and will be updated with the length of the resulting name. We
+ * always return a name that is null terminated, even if the merge_file
+ * name was not. */
+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
+ unsigned int prefix_skip)
+{
+ static char buf[MAXPATHLEN];
+ char *fn, tmpbuf[MAXPATHLEN];
+ unsigned int fn_len;
+
+ if (!parent_dirscan && *merge_file != '/') {
+ /* Return the name unchanged it doesn't have any slashes. */
+ if (len_ptr) {
+ const char *p = merge_file + *len_ptr;
+ while (--p > merge_file && *p != '/') {}
+ if (p == merge_file) {
+ strlcpy(buf, merge_file, *len_ptr + 1);
+ return buf;
+ }
+ } else if (strchr(merge_file, '/') == NULL)
+ return (char *)merge_file;
+ }
+
+ fn = *merge_file == '/' ? buf : tmpbuf;
+ if (sanitize_paths) {
+ const char *r = prefix_skip ? "/" : NULL;
+ /* null-terminate the name if it isn't already */
+ if (len_ptr && merge_file[*len_ptr]) {
+ char *to = fn == buf ? tmpbuf : buf;
+ strlcpy(to, merge_file, *len_ptr + 1);
+ merge_file = to;