+ if (new_mflags & MATCHFLG_MERGE_FILE) {
+ unsigned int len;
+ if (!pat_len) {
+ cp = ".cvsignore";
+ pat_len = 10;
+ }
+ len = pat_len;
+ if (new_mflags & MATCHFLG_EXCLUDE_SELF) {
+ const char *name = cp + len;
+ while (name > cp && name[-1] != '/') name--;
+ len -= name - cp;
+ add_rule(listp, name, len, 0, 0);
+ new_mflags &= ~MATCHFLG_EXCLUDE_SELF;
+ len = pat_len;
+ }
+ if (new_mflags & MATCHFLG_PERDIR_MERGE) {
+ if (parent_dirscan) {
+ if (!(p = parse_merge_name(cp, &len,
+ module_dirlen)))
+ continue;
+ add_rule(listp, p, len, new_mflags, 0);
+ continue;
+ }
+ } else {
+ if (!(p = parse_merge_name(cp, &len, 0)))
+ continue;
+ parse_filter_file(listp, p, new_mflags,
+ XFLG_FATAL_ERRORS);
+ continue;
+ }
+ }
+
+ add_rule(listp, cp, pat_len, new_mflags, xflags);
+
+ if (new_mflags & MATCHFLG_CVS_IGNORE
+ && !(new_mflags & MATCHFLG_MERGE_FILE))
+ get_cvs_excludes(new_mflags);
+ }
+}
+
+
+void parse_filter_file(struct filter_list_struct *listp, const char *fname,
+ uint32 mflags, int xflags)
+{
+ FILE *fp;
+ char line[BIGPATHBUFLEN];
+ char *eob = line + sizeof line - 1;
+ int word_split = mflags & MATCHFLG_WORD_SPLIT;
+
+ if (!fname || !*fname)
+ return;
+
+ if (*fname != '-' || fname[1] || am_server) {
+ if (daemon_filter_list.head) {
+ strlcpy(line, fname, sizeof line);
+ clean_fname(line, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(&daemon_filter_list, FLOG, line, 0) < 0)
+ fp = NULL;
+ else
+ fp = fopen(line, "rb");
+ } else
+ fp = fopen(fname, "rb");
+ } else
+ fp = stdin;
+
+ if (verbose > 2) {
+ rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n",
+ who_am_i(), fname, mflags, xflags,
+ fp ? "" : " [not found]");
+ }
+
+ if (!fp) {
+ if (xflags & XFLG_FATAL_ERRORS) {
+ rsyserr(FERROR, errno,
+ "failed to open %sclude file %s",
+ mflags & MATCHFLG_INCLUDE ? "in" : "ex",
+ fname);
+ exit_cleanup(RERR_FILEIO);
+ }
+ return;
+ }
+ dirbuf[dirbuf_len] = '\0';
+
+ while (1) {
+ char *s = line;
+ int ch, overflow = 0;
+ while (1) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp) && errno == EINTR) {
+ clearerr(fp);
+ continue;
+ }
+ break;
+ }
+ if (word_split && isspace(ch))
+ break;
+ if (eol_nulls? !ch : (ch == '\n' || ch == '\r'))
+ break;
+ if (s < eob)
+ *s++ = ch;
+ else
+ overflow = 1;
+ }
+ if (overflow) {
+ rprintf(FERROR, "discarding over-long filter: %s...\n", line);
+ s = line;
+ }
+ *s = '\0';
+ /* Skip an empty token and (when line parsing) comments. */
+ if (*line && (word_split || (*line != ';' && *line != '#')))
+ parse_rule(listp, line, mflags, xflags);
+ if (ch == EOF)
+ break;
+ }
+ fclose(fp);
+}
+
+/* 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 for_xfer,
+ unsigned int *plen_ptr)
+{
+ static char buf[MAX_RULE_PREFIX+1];
+ char *op = buf;
+ int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX-1;
+
+ if (match_flags & MATCHFLG_PERDIR_MERGE) {
+ if (legal_len == 1)
+ return NULL;
+ *op++ = ':';
+ } else if (match_flags & MATCHFLG_INCLUDE)
+ *op++ = '+';
+ else if (legal_len != 1
+ || ((*pat == '-' || *pat == '+') && pat[1] == ' '))
+ *op++ = '-';
+ else
+ legal_len = 0;
+
+ if (match_flags & MATCHFLG_NEGATE)
+ *op++ = '!';
+ if (match_flags & MATCHFLG_CVS_IGNORE)
+ *op++ = 'C';
+ else {
+ if (match_flags & MATCHFLG_NO_INHERIT)
+ *op++ = 'n';
+ if (match_flags & MATCHFLG_WORD_SPLIT)
+ *op++ = 'w';
+ if (match_flags & MATCHFLG_NO_PREFIXES) {
+ if (match_flags & MATCHFLG_INCLUDE)
+ *op++ = '+';
+ else
+ *op++ = '-';
+ }
+ }
+ if (match_flags & MATCHFLG_EXCLUDE_SELF)
+ *op++ = 'e';
+ 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 (match_flags & MATCHFLG_PERISHABLE) {
+ if (!for_xfer || protocol_version >= 30)
+ *op++ = 'p';
+ else if (am_sender)
+ return NULL;
+ }
+ if (op - buf > legal_len)
+ return NULL;
+ if (legal_len)
+ *op++ = ' ';
+ *op = '\0';
+ if (plen_ptr)
+ *plen_ptr = op - buf;
+ return buf;
+}
+
+static void send_rules(int f_out, struct filter_list_struct *flp)
+{
+ struct filter_struct *ent, *prev = NULL;
+
+ for (ent = flp->head; ent; ent = ent->next) {
+ unsigned int len, plen, dlen;
+ int elide = 0;
+ char *p;
+
+ /* Note we need to check delete_excluded here in addition to
+ * the code in parse_rule_tok() because some rules may have
+ * been added before we found the --delete-excluded option.
+ * We must also elide any CVS merge-file rules to avoid a
+ * backward compatibility problem, and we elide any no-prefix
+ * merge files as an optimization (since they can only have
+ * include/exclude rules). */
+ 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
+ && (!(ent->match_flags & MATCHFLG_PERDIR_MERGE)
+ || ent->match_flags & MATCHFLG_NO_PREFIXES))
+ 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)) {
+ int f = am_sender || protocol_version < 29 ? f_out : -2;
+ send_rules(f, &cvs_filter_list);
+ if (f == f_out)
+ continue;
+ }
+ p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen);
+ if (!p) {
+ rprintf(FERROR,
+ "filter rules are too modern for remote rsync.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (f_out < 0)
+ continue;
+ len = strlen(ent->pattern);
+ dlen = ent->match_flags & MATCHFLG_DIRECTORY ? 1 : 0;
+ if (!(plen + len + dlen))
+ continue;
+ write_int(f_out, plen + len + dlen);
+ if (plen)
+ write_buf(f_out, p, plen);
+ write_buf(f_out, ent->pattern, len);
+ if (dlen)
+ write_byte(f_out, '/');
+ }
+ flp->tail = prev;
+}
+
+/* This is only called by the client. */
+void send_filter_list(int f_out)
+{
+ int receiver_wants_list = prune_empty_dirs
+ || (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)
+ parse_rule(&filter_list, ":C", 0, 0);
+ parse_rule(&filter_list, "-C", 0, 0);
+ }
+
+ send_rules(f_out, &filter_list);
+
+ if (f_out >= 0)
+ write_int(f_out, 0);
+
+ if (cvs_exclude) {
+ if (!am_sender || protocol_version < 29)
+ parse_rule(&filter_list, ":C", 0, 0);
+ if (!am_sender)
+ parse_rule(&filter_list, "-C", 0, 0);
+ }
+}
+
+/* This is only called by the server. */
+void recv_filter_list(int f_in)