Got rid of kluged value for am_sender, and instead added a new
[rsync/rsync.git] / exclude.c
index 6ea9767..fa11e44 100644 (file)
--- a/exclude.c
+++ b/exclude.c
@@ -53,7 +53,8 @@ struct filter_list_struct server_filter_list = { 0, 0, " [server]" };
 #define MAX_RULE_PREFIX (16)
 
 #define MODIFIERS_MERGE_FILE "-+Cenw"
-#define MODIFIERS_INCL_EXCL "/!C"
+#define MODIFIERS_INCL_EXCL "/!Crs"
+#define MODIFIERS_HIDE_PROTECT "/!"
 
 /* The dirbuf is set by push_local_filters() to the current subdirectory
  * relative to curr_dir that is being processed.  The path always has a
@@ -132,6 +133,19 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                        listp->debug_type);
        }
 
+       /* This flag also indicates that we're reading a list that
+        * needs to be filtered now, not post-filtered later. */
+       if (xflags & XFLG_ANCHORED2ABS) {
+               uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE);
+               if (am_sender) {
+                       if (mf == MATCHFLG_RECEIVER_SIDE)
+                               return;
+               } else {
+                       if (mf == MATCHFLG_SENDER_SIDE)
+                               return;
+               }
+       }
+
        if (!(ret = new(struct filter_struct)))
                out_of_memory("add_rule");
        memset(ret, 0, sizeof ret[0]);
@@ -278,7 +292,7 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
                }
                if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) {
                        rprintf(FERROR, "merge-file name overflows: %s\n",
-                               merge_file);
+                               safe_fname(merge_file));
                        return NULL;
                }
        } else {
@@ -291,7 +305,8 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
                goto done;
 
        if (dirbuf_len + fn_len >= MAXPATHLEN) {
-               rprintf(FERROR, "merge-file name overflows: %s\n", fn);
+               rprintf(FERROR, "merge-file name overflows: %s\n",
+                       safe_fname(fn));
                return NULL;
        }
        memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
@@ -604,6 +619,18 @@ int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir)
        return 0;
 }
 
+#define RULE_STRCMP(s,r) rule_strcmp((s), (r), sizeof (r) - 1)
+
+static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len)
+{
+       if (strncmp((char*)str, rule, rule_len) != 0)
+               return NULL;
+       if (isspace(str[rule_len]) || str[rule_len] == '_' || !str[rule_len])
+               return str + rule_len - 1;
+       if (str[rule_len] == ',')
+               return str + rule_len;
+       return NULL;
+}
 
 /* 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.
@@ -638,7 +665,7 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags,
         * for old include/exclude patterns where just "+ " and "- " are
         * allowed as optional prefixes.  */
        if (mflags & MATCHFLG_NO_PREFIXES) {
-               if (*s == '!')
+               if (*s == '!' && mflags & MATCHFLG_CVS_IGNORE)
                        new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
        } else if (xflags & XFLG_OLD_PREFIXES) {
                if (*s == '-' && s[1] == ' ') {
@@ -651,8 +678,52 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags,
                if (*s == '!')
                        new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
        } else {
-               char *mods = "";
+               char ch = 0, *mods = "";
                switch (*s) {
+               case 'c':
+                       if ((s = RULE_STRCMP(s, "clear")) != NULL)
+                               ch = '!';
+                       break;
+               case 'd':
+                       if ((s = RULE_STRCMP(s, "dir-merge")) != NULL)
+                               ch = ':';
+                       break;
+               case 'e':
+                       if ((s = RULE_STRCMP(s, "exclude")) != NULL)
+                               ch = '-';
+                       break;
+               case 'h':
+                       if ((s = RULE_STRCMP(s, "hide")) != NULL)
+                               ch = 'H';
+                       break;
+               case 'i':
+                       if ((s = RULE_STRCMP(s, "include")) != NULL)
+                               ch = '+';
+                       break;
+               case 'm':
+                       if ((s = RULE_STRCMP(s, "merge")) != NULL)
+                               ch = '.';
+                       break;
+               case 'p':
+                       if ((s = RULE_STRCMP(s, "protect")) != NULL)
+                               ch = 'P';
+                       break;
+               case 'r':
+                       if ((s = RULE_STRCMP(s, "risk")) != NULL)
+                               ch = 'R';
+                       break;
+               case 's':
+                       if ((s = RULE_STRCMP(s, "show")) != NULL)
+                               ch = 'S';
+                       break;
+
+               default:
+                       ch = *s;
+                       if (s[1] == ',')
+                               s++;
+                       break;
+               }
+               switch (ch) {
                case ':':
                        new_mflags |= MATCHFLG_PERDIR_MERGE
                                    | MATCHFLG_FINISH_SETUP;
@@ -667,6 +738,20 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags,
                case '-':
                        mods = MODIFIERS_INCL_EXCL;
                        break;
+               case 'S':
+                       new_mflags |= MATCHFLG_INCLUDE;
+                       /* FALL THROUGH */
+               case 'H':
+                       new_mflags |= MATCHFLG_SENDER_SIDE;
+                       mods = MODIFIERS_HIDE_PROTECT;
+                       break;
+               case 'R':
+                       new_mflags |= MATCHFLG_INCLUDE;
+                       /* FALL THROUGH */
+               case 'P':
+                       new_mflags |= MATCHFLG_RECEIVER_SIDE;
+                       mods = MODIFIERS_HIDE_PROTECT;
+                       break;
                case '!':
                        new_mflags |= MATCHFLG_CLEAR_LIST;
                        mods = NULL;
@@ -719,6 +804,12 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags,
                        case 'n':
                                new_mflags |= MATCHFLG_NO_INHERIT;
                                break;
+                       case 'r':
+                               new_mflags |= MATCHFLG_RECEIVER_SIDE;
+                               break;
+                       case 's':
+                               new_mflags |= MATCHFLG_SENDER_SIDE;
+                               break;
                        case 'w':
                                new_mflags |= MATCHFLG_WORD_SPLIT;
                                break;
@@ -935,15 +1026,15 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
        fclose(fp);
 }
 
-/* If the "sending" flag is > 0, the prefix is made compatible with the
+/* If the "for_xfer" flag is set, the prefix is made compatible with the
  * current protocol_version (if possible) or a NULL is returned (if not
  * possible). */
-char *get_rule_prefix(int match_flags, const char *pat, int sending,
+char *get_rule_prefix(int match_flags, const char *pat, int for_xfer,
                      unsigned int *plen_ptr)
 {
        static char buf[MAX_RULE_PREFIX+1];
        char *op = buf;
-       int legal_len = sending && protocol_version < 29 ? 1 : MAX_RULE_PREFIX;
+       int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX;
 
        if (match_flags & MATCHFLG_PERDIR_MERGE) {
                if (legal_len == 1)
@@ -973,32 +1064,53 @@ char *get_rule_prefix(int match_flags, const char *pat, int sending,
        }
        if (match_flags & MATCHFLG_EXCLUDE_SELF)
                *op++ = 'e';
-       if (op - buf > legal_len)
-               return NULL;
+       if (match_flags & MATCHFLG_SENDER_SIDE
+           && (!for_xfer || protocol_version >= 29))
+               *op++ = 's';
+       if (match_flags & MATCHFLG_RECEIVER_SIDE
+           && (!for_xfer || protocol_version >= 29
+            || (delete_excluded && am_sender)))
+               *op++ = 'r';
        if (legal_len)
                *op++ = ' ';
+       if (op - buf > legal_len)
+               return NULL;
        *op = '\0';
        if (plen_ptr)
                *plen_ptr = op - buf;
-       if (op - buf > MAX_RULE_PREFIX)
-               overflow("get_rule_prefix");
        return buf;
 }
 
 static void send_rules(int f_out, struct filter_list_struct *flp)
 {
-       struct filter_struct *ent;
+       struct filter_struct *ent, *prev = NULL;
 
        for (ent = flp->head; ent; ent = ent->next) {
                unsigned int len, plen, dlen;
+               int elide = 0;
                char *p;
 
+               if (ent->match_flags & MATCHFLG_SENDER_SIDE)
+                       elide = am_sender ? 1 : -1;
+               if (ent->match_flags & MATCHFLG_RECEIVER_SIDE)
+                       elide = elide ? 0 : am_sender ? -1 : 1;
+               else if (delete_excluded && !elide)
+                       elide = am_sender ? 1 : -1;
+               if (elide < 0) {
+                       if (prev)
+                               prev->next = ent->next;
+                       else
+                               flp->head = ent->next;
+               } else
+                       prev = ent;
+               if (elide > 0)
+                       continue;
                if (ent->match_flags & MATCHFLG_CVS_IGNORE
                    && !(ent->match_flags & MATCHFLG_MERGE_FILE)) {
-                       if (am_sender || protocol_version < 29) {
-                               send_rules(f_out, &cvs_filter_list);
+                       int f = am_sender || protocol_version < 29 ? f_out : -1;
+                       send_rules(f, &cvs_filter_list);
+                       if (f >= 0)
                                continue;
-                       }
                }
                p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen);
                if (!p) {
@@ -1006,6 +1118,8 @@ static void send_rules(int f_out, struct filter_list_struct *flp)
                                "filter rules are too modern for remote rsync.\n");
                        exit_cleanup(RERR_SYNTAX);
                }
+               if (f_out < 0)
+                       continue;
                len = strlen(ent->pattern);
                dlen = ent->match_flags & MATCHFLG_DIRECTORY ? 1 : 0;
                if (!(plen + len + dlen))
@@ -1017,12 +1131,16 @@ static void send_rules(int f_out, struct filter_list_struct *flp)
                if (dlen)
                        write_byte(f_out, '/');
        }
+       flp->tail = prev;
 }
 
 /* This is only called by the client. */
 void send_filter_list(int f_out)
 {
-       if (local_server || (am_sender && (!delete_mode || delete_excluded)))
+       int receiver_wants_list = delete_mode
+               && (!delete_excluded || protocol_version >= 29);
+
+       if (local_server || (am_sender && !receiver_wants_list))
                f_out = -1;
        if (cvs_exclude && am_sender) {
                if (protocol_version >= 29)
@@ -1035,10 +1153,10 @@ void send_filter_list(int f_out)
        if (list_only == 1 && !recurse)
                parse_rule(&filter_list, "/*/*", MATCHFLG_NO_PREFIXES, 0);
 
-       if (f_out >= 0) {
-               send_rules(f_out, &filter_list);
+       send_rules(f_out, &filter_list);
+
+       if (f_out >= 0)
                write_int(f_out, 0);
-       }
 
        if (cvs_exclude) {
                if (!am_sender || protocol_version < 29)
@@ -1053,9 +1171,11 @@ void recv_filter_list(int f_in)
 {
        char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */
        int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES;
+       int receiver_wants_list = delete_mode
+               && (!delete_excluded || protocol_version >= 29);
        unsigned int len;
 
-       if (!local_server && (am_sender || (delete_mode && !delete_excluded))) {
+       if (!local_server && (am_sender || receiver_wants_list)) {
                while ((len = read_int(f_in)) != 0) {
                        if (len >= sizeof line)
                                overflow("recv_rules");
@@ -1070,4 +1190,7 @@ void recv_filter_list(int f_in)
                if (local_server || am_sender)
                        parse_rule(&filter_list, "-C", 0, 0);
        }
+
+       if (local_server) /* filter out any rules that aren't for us. */
+               send_rules(-1, &filter_list);
 }