Tweaked some whitespace to match the latest version from autoconf.
[rsync/rsync.git] / exclude.c
index 36faa96..c6d9ec8 100644 (file)
--- a/exclude.c
+++ b/exclude.c
@@ -34,6 +34,7 @@ extern int list_only;
 extern int recurse;
 extern int io_error;
 extern int local_server;
+extern int prune_empty_dirs;
 extern int delete_mode;
 extern int delete_excluded;
 extern int cvs_exclude;
@@ -46,8 +47,8 @@ extern unsigned int curr_dir_len;
 extern unsigned int module_dirlen;
 
 struct filter_list_struct filter_list = { 0, 0, "" };
-struct filter_list_struct cvs_filter_list = { 0, 0, " [cvsignore]" };
-struct filter_list_struct server_filter_list = { 0, 0, " [server]" };
+struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" };
+struct filter_list_struct server_filter_list = { 0, 0, " [daemon]" };
 
 /* Need room enough for ":MODS " prefix plus some room to grow. */
 #define MAX_RULE_PREFIX (16)
@@ -133,9 +134,9 @@ 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
+       /* These flags also indicate that we're reading a list that
         * needs to be filtered now, not post-filtered later. */
-       if (xflags & XFLG_ANCHORED2ABS) {
+       if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)) {
                uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE);
                if (am_sender) {
                        if (mf == MATCHFLG_RECEIVER_SIDE)
@@ -150,10 +151,14 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                out_of_memory("add_rule");
        memset(ret, 0, sizeof ret[0]);
 
-       if (xflags & XFLG_ANCHORED2ABS && *pat == '/'
-           && !(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))) {
+       if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))
+        && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')
+         || (xflags & XFLG_ABS_IF_SLASH && strchr(pat, '/') != NULL))) {
                mflags |= MATCHFLG_ABS_PATH;
-               ex_len = dirbuf_len - module_dirlen - 1;
+               if (*pat == '/')
+                       ex_len = dirbuf_len - module_dirlen - 1;
+               else
+                       ex_len = 0;
        } else
                ex_len = 0;
        if (!(ret->pattern = new_array(char, ex_len + pat_len + 1)))
@@ -170,6 +175,12 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                        /* If the pattern starts with **, note that. */
                        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;
                }
        }
 
@@ -292,7 +303,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",
-                               safe_fname(merge_file));
+                               merge_file);
                        return NULL;
                }
        } else {
@@ -305,8 +316,7 @@ 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",
-                       safe_fname(fn));
+               rprintf(FERROR, "merge-file name overflows: %s\n", fn);
                return NULL;
        }
        memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
@@ -488,71 +498,69 @@ void pop_local_filters(void *mem)
 
 static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
 {
-       char *p, full_name[MAXPATHLEN];
-       int match_start = 0;
+       int slash_handling, str_cnt = 0, anchored_match = 0;
        int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1;
-       char *pattern = ex->pattern;
+       char *p, *pattern = ex->pattern;
+       const char *strings[16]; /* more than enough */
 
        if (!*name)
                return 0;
 
-       /* If the pattern does not have any slashes AND it does not have
-        * a "**" (which could match a slash), then we just match the
-        * name portion of the path. */
        if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
+               /* If the pattern does not have any slashes AND it does
+                * not have a "**" (which could match a slash), then we
+                * just match the name portion of the path. */
                if ((p = strrchr(name,'/')) != NULL)
                        name = p+1;
-       }
-       else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
+       } else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
            && curr_dir_len > module_dirlen + 1) {
-               pathjoin(full_name, sizeof full_name,
-                        curr_dir + module_dirlen + 1, name);
-               name = full_name;
+               /* If we're matching against an absolute-path pattern,
+                * we need to prepend our full path info. */
+               strings[str_cnt++] = curr_dir + module_dirlen + 1;
+               strings[str_cnt++] = "/";
+       } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *name != '/') {
+               /* Allow "**"+"/" to match at the start of the string. */
+               strings[str_cnt++] = "/";
        }
-
-       if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir)
+       strings[str_cnt++] = name;
+       if (name_is_dir) {
+               /* Allow a trailing "/"+"***" to match the directory. */
+               if (ex->match_flags & MATCHFLG_WILD3_SUFFIX)
+                       strings[str_cnt++] = "/";
+       } else if (ex->match_flags & MATCHFLG_DIRECTORY)
                return !ret_match;
+       strings[str_cnt] = NULL;
 
        if (*pattern == '/') {
-               match_start = 1;
+               anchored_match = 1;
                pattern++;
-               if (*name == '/')
-                       name++;
+               if (strings[0][0] == '/')
+                       strings[0]++;
        }
 
-       if (ex->match_flags & MATCHFLG_WILD) {
+       if (!anchored_match && ex->u.slash_cnt
+           && !(ex->match_flags & MATCHFLG_WILD2)) {
                /* A non-anchored match with an infix slash and no "**"
                 * needs to match the last slash_cnt+1 name elements. */
-               if (!match_start && ex->u.slash_cnt
-                   && !(ex->match_flags & MATCHFLG_WILD2)) {
-                       int cnt = ex->u.slash_cnt + 1;
-                       for (p = name + strlen(name) - 1; p >= name; p--) {
-                               if (*p == '/' && !--cnt)
-                                       break;
-                       }
-                       name = p+1;
-               }
-               if (wildmatch(pattern, name))
+               slash_handling = ex->u.slash_cnt + 1;
+       } else if (!anchored_match && !(ex->match_flags & MATCHFLG_WILD2_PREFIX)
+                                  && ex->match_flags & MATCHFLG_WILD2) {
+               /* A non-anchored match with an infix or trailing "**" (but not
+                * a prefixed "**") needs to try matching after every slash. */
+               slash_handling = -1;
+       } else {
+               /* The pattern matches only at the start of the path or name. */
+               slash_handling = 0;
+       }
+
+       if (ex->match_flags & MATCHFLG_WILD) {
+               if (wildmatch_array(pattern, strings, slash_handling))
                        return ret_match;
-               if (ex->match_flags & MATCHFLG_WILD2_PREFIX) {
-                       /* If the **-prefixed pattern has a '/' as the next
-                        * character, then try to match the rest of the
-                        * pattern at the root. */
-                       if (pattern[2] == '/' && wildmatch(pattern+3, name))
-                               return ret_match;
-               }
-               else if (!match_start && ex->match_flags & MATCHFLG_WILD2) {
-                       /* A non-anchored match with an infix or trailing "**"
-                        * (but not a prefixed "**") needs to try matching
-                        * after every slash. */
-                       while ((name = strchr(name, '/')) != NULL) {
-                               name++;
-                               if (wildmatch(pattern, name))
-                                       return ret_match;
-                       }
-               }
-       } else if (match_start) {
-               if (strcmp(name,pattern) == 0)
+       } else if (str_cnt > 1) {
+               if (litmatch_array(pattern, strings, slash_handling))
+                       return ret_match;
+       } else if (anchored_match) {
+               if (strcmp(strings[0], pattern) == 0)
                        return ret_match;
        } else {
                int l1 = strlen(name);
@@ -577,11 +585,13 @@ static void report_filter_result(char const *name,
         * case we add it back in here. */
 
        if (verbose >= 2) {
-               rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n",
-                       who_am_i(),
-                       ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex",
-                       name_is_dir ? "directory" : "file", name, ent->pattern,
-                       ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
+               static char *actions[2][2]
+                   = { {"show", "hid"}, {"risk", "protect"} };
+               const char *w = who_am_i();
+               rprintf(FINFO, "[%s] %sing %s %s because of pattern %s%s%s\n",
+                   w, actions[*w!='s'][!(ent->match_flags&MATCHFLG_INCLUDE)],
+                   name_is_dir ? "directory" : "file", name, ent->pattern,
+                   ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
        }
 }
 
@@ -827,7 +837,8 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags,
                len = strlen((char*)s);
 
        if (new_mflags & MATCHFLG_CLEAR_LIST) {
-               if (!(xflags & XFLG_OLD_PREFIXES) && len) {
+               if (!(mflags & MATCHFLG_NO_PREFIXES)
+                && !(xflags & XFLG_OLD_PREFIXES) && len) {
                        rprintf(FERROR,
                                "'!' rule has trailing characters: %s\n", p);
                        exit_cleanup(RERR_SYNTAX);
@@ -954,7 +965,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
                       uint32 mflags, int xflags)
 {
        FILE *fp;
-       char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */
+       char line[BIGPATHBUFLEN];
        char *eob = line + sizeof line - 1;
        int word_split = mflags & MATCHFLG_WORD_SPLIT;
 
@@ -976,7 +987,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
 
        if (verbose > 2) {
                rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n",
-                       who_am_i(), safe_fname(fname), mflags, xflags,
+                       who_am_i(), fname, mflags, xflags,
                        fp ? "" : " [not found]");
        }
 
@@ -985,7 +996,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
                        rsyserr(FERROR, errno,
                                "failed to open %sclude file %s",
                                mflags & MATCHFLG_INCLUDE ? "in" : "ex",
-                               safe_fname(fname));
+                               fname);
                        exit_cleanup(RERR_FILEIO);
                }
                return;
@@ -997,8 +1008,10 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
                int ch, overflow = 0;
                while (1) {
                        if ((ch = getc(fp)) == EOF) {
-                               if (ferror(fp) && errno == EINTR)
+                               if (ferror(fp) && errno == EINTR) {
+                                       clearerr(fp);
                                        continue;
+                               }
                                break;
                        }
                        if (word_split && isspace(ch))
@@ -1135,8 +1148,8 @@ static void send_rules(int f_out, struct filter_list_struct *flp)
 /* This is only called by the client. */
 void send_filter_list(int f_out)
 {
-       int receiver_wants_list = delete_mode
-               && (!delete_excluded || protocol_version >= 29);
+       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;
@@ -1167,16 +1180,17 @@ void send_filter_list(int f_out)
 /* This is only called by the server. */
 void recv_filter_list(int f_in)
 {
-       char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */
+       char line[BIGPATHBUFLEN];
        int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES;
-       int receiver_wants_list = delete_mode
-               && (!delete_excluded || protocol_version >= 29);
+       int receiver_wants_list = prune_empty_dirs
+           || (delete_mode
+            && (!delete_excluded || protocol_version >= 29));
        unsigned int len;
 
        if (!local_server && (am_sender || receiver_wants_list)) {
                while ((len = read_int(f_in)) != 0) {
                        if (len >= sizeof line)
-                               overflow("recv_rules");
+                               overflow_exit("recv_rules");
                        read_sbuf(f_in, line, len);
                        parse_rule(&filter_list, line, 0, xflags);
                }