+/* Get the next include/exclude arg from the string. The token will not
+ * be '\0' terminated, so use the returned length to limit the string.
+ * Also, be sure to add this length to the returned pointer before passing
+ * it back to ask for the next token. This routine parses the "!" (list-
+ * clearing) token and (if xflags does NOT contain XFLG_NO_PREFIXES) the
+ * +/- prefixes for overriding the include/exclude mode. The *flag_ptr
+ * value will also be set to the MATCHFLG_* bits for the current token.
+ */
+static const char *get_filter_tok(const char *p, int xflags,
+ unsigned int *len_ptr, unsigned int *flag_ptr)
+{
+ const unsigned char *s = (const unsigned char *)p;
+ unsigned int len, mflags = 0;
+ int empty_pat_is_OK = 0;
+
+ if (xflags & XFLG_WORD_SPLIT) {
+ /* Skip over any initial whitespace. */
+ while (isspace(*s))
+ s++;
+ /* Update to point to real start of rule. */
+ p = (const char *)s;
+ }
+ if (!*s)
+ return NULL;
+
+ /* Figure out what kind of a filter rule "s" is pointing at. */
+ if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE))) {
+ char *mods = "";
+ switch (*s) {
+ case ':':
+ mflags |= MATCHFLG_PERDIR_MERGE
+ | MATCHFLG_FINISH_SETUP;
+ /* FALL THROUGH */
+ case '.':
+ mflags |= MATCHFLG_MERGE_FILE;
+ mods = "-+Cens";
+ break;
+ case '+':
+ mflags |= MATCHFLG_INCLUDE;
+ /* FALL THROUGH */
+ case '-':
+ mods = "/";
+ break;
+ case '!':
+ mflags |= MATCHFLG_CLEAR_LIST;
+ mods = NULL;
+ break;
+ default:
+ rprintf(FERROR, "Unknown filter rule: %s\n", p);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ while (mods && *++s && *s != ' ' && *s != '_') {
+ if (strchr(mods, *s) == NULL) {
+ if (xflags & XFLG_WORD_SPLIT && isspace(*s)) {
+ s--;
+ break;
+ }
+ rprintf(FERROR,
+ "unknown modifier '%c' in filter rule: %s\n",
+ *s, p);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ switch (*s) {
+ case '-':
+ mflags |= MATCHFLG_NO_PREFIXES;
+ break;
+ case '+':
+ mflags |= MATCHFLG_NO_PREFIXES
+ | MATCHFLG_INCLUDE;
+ break;
+ case '/':
+ mflags |= MATCHFLG_ABS_PATH;
+ break;
+ case 'C':
+ empty_pat_is_OK = 1;
+ mflags |= MATCHFLG_NO_PREFIXES
+ | MATCHFLG_WORD_SPLIT
+ | MATCHFLG_NO_INHERIT;
+ break;
+ case 'e':
+ mflags |= MATCHFLG_EXCLUDE_SELF;
+ break;
+ case 'n':
+ mflags |= MATCHFLG_NO_INHERIT;
+ break;
+ case 's':
+ mflags |= MATCHFLG_WORD_SPLIT;
+ break;
+ }
+ }
+ if (*s)
+ s++;
+ } else if (!(xflags & XFLG_NO_PREFIXES)
+ && (*s == '-' || *s == '+') && s[1] == ' ') {
+ if (*s == '+')
+ mflags |= MATCHFLG_INCLUDE;
+ s += 2;
+ } else {
+ if (xflags & XFLG_DEF_INCLUDE)
+ mflags |= MATCHFLG_INCLUDE;
+ if (*s == '!')
+ mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
+ }
+
+ if (xflags & XFLG_DIRECTORY)
+ mflags |= MATCHFLG_DIRECTORY;
+
+ if (xflags & XFLG_WORD_SPLIT) {
+ const unsigned char *cp = s;
+ /* Token ends at whitespace or the end of the string. */
+ while (!isspace(*cp) && *cp != '\0')
+ cp++;
+ len = cp - s;
+ } else
+ len = strlen((char*)s);
+
+ if (mflags & MATCHFLG_CLEAR_LIST) {
+ if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE)) && len) {
+ rprintf(FERROR,
+ "'!' rule has trailing characters: %s\n", p);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (len > 1)
+ mflags &= ~MATCHFLG_CLEAR_LIST;
+ } else if (!len && !empty_pat_is_OK) {
+ rprintf(FERROR, "unexpected end of filter rule: %s\n", p);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ *len_ptr = len;
+ *flag_ptr = mflags;
+ return (const char *)s;