grep: add proper support for pattern_list
authorSören Tempel <soeren+git@soeren-tempel.net>
Mon, 30 Mar 2020 09:02:08 +0000 (11:02 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 29 Apr 2020 22:20:18 +0000 (00:20 +0200)
From POSIX.1-2008:

The pattern_list's value shall consist of one or more patterns
separated by <newline> characters;

As such, given patterns need to be split at newline characters. Without
doing so, busybox grep will interpret the newline as part of the pattern
which is not in accordance with POSIX.

See also: https://bugs.busybox.net/show_bug.cgi?id=12721

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
findutils/grep.c
testsuite/grep.tests

index 84a6f7b1cdd8bc19a95eb2af2785e8ef42e8db6c..55e9c0a8f924ba68305ea788f2d2e02240635512 100644 (file)
@@ -650,6 +650,13 @@ static void load_regexes_from_file(llist_t *fopt)
        }
 }
 
+static void load_pattern_list(llist_t **lst, char *pattern)
+{
+       char *p;
+       while ((p = strsep(&pattern, "\n")) != NULL)
+               llist_add_to(lst, new_grep_list_data(p, 0));
+}
+
 static int FAST_FUNC file_action_grep(const char *filename,
                        struct stat *statbuf,
                        void* matched,
@@ -754,10 +761,12 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
 #endif
        invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
 
-       {       /* convert char **argv to grep_list_data_t */
-               llist_t *cur;
+       {       /* convert char **argv to pattern_list */
+               llist_t *cur, *new = NULL;
                for (cur = pattern_head; cur; cur = cur->link)
-                       cur->data = new_grep_list_data(cur->data, 0);
+                       load_pattern_list(&new, cur->data);
+               llist_free(pattern_head, NULL);
+               pattern_head = new;
        }
        if (option_mask32 & OPT_f) {
                load_regexes_from_file(fopt);
@@ -806,11 +815,9 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
        /* if we didn't get a pattern from -e and no command file was specified,
         * first parameter should be the pattern. no pattern, no worky */
        if (pattern_head == NULL) {
-               char *pattern;
                if (*argv == NULL)
                        bb_show_usage();
-               pattern = new_grep_list_data(*argv++, 0);
-               llist_add_to(&pattern_head, pattern);
+               load_pattern_list(&pattern_head, *argv++);
        }
 
        /* argv[0..(argc-1)] should be names of file to grep through. If
index 9c1f354994f1a6e38a3e85f9ed1ff5c3eadcfd2c..e3827881069380dc08ea821eba3f3c3020d4ba94 100755 (executable)
@@ -190,6 +190,18 @@ testing "grep -x -v -e EXP1 -e EXP2 finds nothing if either EXP matches" \
        "" \
        "  aa bb cc\n"
 
+testing "grep PATTERN can be a newline-delimited list" \
+       'grep -Fv "$(printf "foo\nbar\n")"' \
+       "baz\n" \
+       "" \
+       "foo\nbar\nbaz\n"
+
+testing "grep -e PATTERN can be a newline-delimited list" \
+       'grep -Fv -e "$(printf "foo\nbar\n")"' \
+       "baz\n" \
+       "" \
+       "foo\nbar\nbaz\n"
+
 # -r on symlink to dir should recurse into dir
 mkdir -p grep.testdir/foo
 echo bar > grep.testdir/foo/file