+ while (1) {
+ char *s = line;
+ int ch, overflow = 0;
+ while (1) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp) && errno == EINTR)
+ 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;
+
+ 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_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 (op - buf > legal_len)
+ return NULL;
+ if (legal_len)
+ *op++ = ' ';
+ *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;
+
+ for (ent = flp->head; ent; ent = ent->next) {
+ unsigned int len, plen, dlen;
+ char *p;
+
+ 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);
+ 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_SYNTAX);
+ }
+ 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, '/');
+ }
+}
+
+/* This is only called by the client. */
+void send_filter_list(int f_out)
+{
+ if (local_server || (am_sender && (!delete_mode || delete_excluded)))
+ 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);
+ }
+
+ /* This is a complete hack - blame Rusty. FIXME!
+ * Remove this hack when older rsyncs (below 2.6.4) are gone. */
+ if (list_only == 1 && !recurse)
+ parse_rule(&filter_list, "/*/*", MATCHFLG_NO_PREFIXES, 0);
+
+ if (f_out >= 0) {
+ send_rules(f_out, &filter_list);
+ 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);
+ }
+}