busybox/findutils/grep.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini grep implementation for busybox using libc regex.
   4 *
   5 * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
   6 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
   7 *
   8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
   9 */
  10/* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */
  11/* BB_AUDIT GNU defects - always acts as -a.  */
  12/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */
  13/*
  14 * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> -
  15 * correction "-e pattern1 -e pattern2" logic and more optimizations.
  16 * precompiled regex
  17 */
  18/*
  19 * (C) 2006 Jac Goudsmit added -o option
  20 */
  21
  22#include "libbb.h"
  23#include "xregex.h"
  24
  25/* options */
  26#define OPTSTR_GREP \
  27        "lnqvscFiHhe:f:Lorm:" \
  28        IF_FEATURE_GREP_CONTEXT("A:B:C:") \
  29        IF_FEATURE_GREP_EGREP_ALIAS("E") \
  30        IF_DESKTOP("w") \
  31        IF_EXTRA_COMPAT("z") \
  32        "aI"
  33
  34/* ignored: -a "assume all files to be text" */
  35/* ignored: -I "assume binary files have no matches" */
  36
  37enum {
  38        OPTBIT_l, /* list matched file names only */
  39        OPTBIT_n, /* print line# */
  40        OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */
  41        OPTBIT_v, /* invert the match, to select non-matching lines */
  42        OPTBIT_s, /* suppress errors about file open errors */
  43        OPTBIT_c, /* count matches per file (suppresses normal output) */
  44        OPTBIT_F, /* literal match */
  45        OPTBIT_i, /* case-insensitive */
  46        OPTBIT_H, /* force filename display */
  47        OPTBIT_h, /* inhibit filename display */
  48        OPTBIT_e, /* -e PATTERN */
  49        OPTBIT_f, /* -f FILE_WITH_PATTERNS */
  50        OPTBIT_L, /* list unmatched file names only */
  51        OPTBIT_o, /* show only matching parts of lines */
  52        OPTBIT_r, /* recurse dirs */
  53        OPTBIT_m, /* -m MAX_MATCHES */
  54        IF_FEATURE_GREP_CONTEXT(    OPTBIT_A ,) /* -A NUM: after-match context */
  55        IF_FEATURE_GREP_CONTEXT(    OPTBIT_B ,) /* -B NUM: before-match context */
  56        IF_FEATURE_GREP_CONTEXT(    OPTBIT_C ,) /* -C NUM: -A and -B combined */
  57        IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
  58        IF_DESKTOP(                 OPTBIT_w ,) /* whole word match */
  59        IF_EXTRA_COMPAT(            OPTBIT_z ,) /* input is NUL terminated */
  60        OPT_l = 1 << OPTBIT_l,
  61        OPT_n = 1 << OPTBIT_n,
  62        OPT_q = 1 << OPTBIT_q,
  63        OPT_v = 1 << OPTBIT_v,
  64        OPT_s = 1 << OPTBIT_s,
  65        OPT_c = 1 << OPTBIT_c,
  66        OPT_F = 1 << OPTBIT_F,
  67        OPT_i = 1 << OPTBIT_i,
  68        OPT_H = 1 << OPTBIT_H,
  69        OPT_h = 1 << OPTBIT_h,
  70        OPT_e = 1 << OPTBIT_e,
  71        OPT_f = 1 << OPTBIT_f,
  72        OPT_L = 1 << OPTBIT_L,
  73        OPT_o = 1 << OPTBIT_o,
  74        OPT_r = 1 << OPTBIT_r,
  75        OPT_m = 1 << OPTBIT_m,
  76        OPT_A = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_A)) + 0,
  77        OPT_B = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_B)) + 0,
  78        OPT_C = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_C)) + 0,
  79        OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
  80        OPT_w = IF_DESKTOP(                 (1 << OPTBIT_w)) + 0,
  81        OPT_z = IF_EXTRA_COMPAT(            (1 << OPTBIT_z)) + 0,
  82};
  83
  84#define PRINT_FILES_WITH_MATCHES    (option_mask32 & OPT_l)
  85#define PRINT_LINE_NUM              (option_mask32 & OPT_n)
  86#define BE_QUIET                    (option_mask32 & OPT_q)
  87#define SUPPRESS_ERR_MSGS           (option_mask32 & OPT_s)
  88#define PRINT_MATCH_COUNTS          (option_mask32 & OPT_c)
  89#define FGREP_FLAG                  (option_mask32 & OPT_F)
  90#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
  91#define NUL_DELIMITED               (option_mask32 & OPT_z)
  92
  93struct globals {
  94        int max_matches;
  95#if !ENABLE_EXTRA_COMPAT
  96        int reflags;
  97#else
  98        RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */
  99#endif
 100        smalluint invert_search;
 101        smalluint print_filename;
 102        smalluint open_errors;
 103#if ENABLE_FEATURE_GREP_CONTEXT
 104        smalluint did_print_line;
 105        int lines_before;
 106        int lines_after;
 107        char **before_buf;
 108        IF_EXTRA_COMPAT(size_t *before_buf_size;)
 109        int last_line_printed;
 110#endif
 111        /* globals used internally */
 112        llist_t *pattern_head;   /* growable list of patterns to match */
 113        const char *cur_file;    /* the current file we are reading */
 114};
 115#define G (*(struct globals*)&bb_common_bufsiz1)
 116#define INIT_G() do { \
 117        struct G_sizecheck { \
 118                char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
 119        }; \
 120} while (0)
 121#define max_matches       (G.max_matches         )
 122#if !ENABLE_EXTRA_COMPAT
 123# define reflags          (G.reflags             )
 124#else
 125# define case_fold        (G.case_fold           )
 126/* http://www.delorie.com/gnu/docs/regex/regex_46.html */
 127# define reflags           re_syntax_options
 128# undef REG_NOSUB
 129# undef REG_EXTENDED
 130# undef REG_ICASE
 131# define REG_NOSUB    bug:is:here /* should not be used */
 132/* Just RE_SYNTAX_EGREP is not enough, need to enable {n[,[m]]} too */
 133# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
 134# define REG_ICASE    bug:is:here /* should not be used */
 135#endif
 136#define invert_search     (G.invert_search       )
 137#define print_filename    (G.print_filename      )
 138#define open_errors       (G.open_errors         )
 139#define did_print_line    (G.did_print_line      )
 140#define lines_before      (G.lines_before        )
 141#define lines_after       (G.lines_after         )
 142#define before_buf        (G.before_buf          )
 143#define before_buf_size   (G.before_buf_size     )
 144#define last_line_printed (G.last_line_printed   )
 145#define pattern_head      (G.pattern_head        )
 146#define cur_file          (G.cur_file            )
 147
 148
 149typedef struct grep_list_data_t {
 150        char *pattern;
 151/* for GNU regex, matched_range must be persistent across grep_file() calls */
 152#if !ENABLE_EXTRA_COMPAT
 153        regex_t compiled_regex;
 154        regmatch_t matched_range;
 155#else
 156        struct re_pattern_buffer compiled_regex;
 157        struct re_registers matched_range;
 158#endif
 159#define ALLOCATED 1
 160#define COMPILED 2
 161        int flg_mem_alocated_compiled;
 162} grep_list_data_t;
 163
 164#if !ENABLE_EXTRA_COMPAT
 165#define print_line(line, line_len, linenum, decoration) \
 166        print_line(line, linenum, decoration)
 167#endif
 168static void print_line(const char *line, size_t line_len, int linenum, char decoration)
 169{
 170#if ENABLE_FEATURE_GREP_CONTEXT
 171        /* Happens when we go to next file, immediately hit match
 172         * and try to print prev context... from prev file! Don't do it */
 173        if (linenum < 1)
 174                return;
 175        /* possibly print the little '--' separator */
 176        if ((lines_before || lines_after) && did_print_line
 177         && last_line_printed != linenum - 1
 178        ) {
 179                puts("--");
 180        }
 181        /* guard against printing "--" before first line of first file */
 182        did_print_line = 1;
 183        last_line_printed = linenum;
 184#endif
 185        if (print_filename)
 186                printf("%s%c", cur_file, decoration);
 187        if (PRINT_LINE_NUM)
 188                printf("%i%c", linenum, decoration);
 189        /* Emulate weird GNU grep behavior with -ov */
 190        if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
 191#if !ENABLE_EXTRA_COMPAT
 192                puts(line);
 193#else
 194                fwrite(line, 1, line_len, stdout);
 195                putchar(NUL_DELIMITED ? '\0' : '\n');
 196#endif
 197        }
 198}
 199
 200#if ENABLE_EXTRA_COMPAT
 201/* Unlike getline, this one removes trailing '\n' */
 202static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
 203{
 204        ssize_t res_sz;
 205        char *line;
 206        int delim = (NUL_DELIMITED ? '\0' : '\n');
 207
 208        res_sz = getdelim(line_ptr, line_alloc_len, delim, file);
 209        line = *line_ptr;
 210
 211        if (res_sz > 0) {
 212                if (line[res_sz - 1] == delim)
 213                        line[--res_sz] = '\0';
 214        } else {
 215                free(line); /* uclibc allocates a buffer even on EOF. WTF? */
 216        }
 217        return res_sz;
 218}
 219#endif
 220
 221static int grep_file(FILE *file)
 222{
 223        smalluint found;
 224        int linenum = 0;
 225        int nmatches = 0;
 226#if !ENABLE_EXTRA_COMPAT
 227        char *line;
 228#else
 229        char *line = NULL;
 230        ssize_t line_len;
 231        size_t line_alloc_len;
 232#define rm_so start[0]
 233#define rm_eo end[0]
 234#endif
 235#if ENABLE_FEATURE_GREP_CONTEXT
 236        int print_n_lines_after = 0;
 237        int curpos = 0; /* track where we are in the circular 'before' buffer */
 238        int idx = 0; /* used for iteration through the circular buffer */
 239#else
 240        enum { print_n_lines_after = 0 };
 241#endif /* ENABLE_FEATURE_GREP_CONTEXT */
 242
 243        while (
 244#if !ENABLE_EXTRA_COMPAT
 245                (line = xmalloc_fgetline(file)) != NULL
 246#else
 247                (line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
 248#endif
 249        ) {
 250                llist_t *pattern_ptr = pattern_head;
 251                grep_list_data_t *gl = gl; /* for gcc */
 252
 253                linenum++;
 254                found = 0;
 255                while (pattern_ptr) {
 256                        gl = (grep_list_data_t *)pattern_ptr->data;
 257                        if (FGREP_FLAG) {
 258                                found |= (strstr(line, gl->pattern) != NULL);
 259                        } else {
 260                                if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
 261                                        gl->flg_mem_alocated_compiled |= COMPILED;
 262#if !ENABLE_EXTRA_COMPAT
 263                                        xregcomp(&gl->compiled_regex, gl->pattern, reflags);
 264#else
 265                                        memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
 266                                        gl->compiled_regex.translate = case_fold; /* for -i */
 267                                        if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
 268                                                bb_error_msg_and_die("bad regex '%s'", gl->pattern);
 269#endif
 270                                }
 271#if !ENABLE_EXTRA_COMPAT
 272                                gl->matched_range.rm_so = 0;
 273                                gl->matched_range.rm_eo = 0;
 274#endif
 275                                if (
 276#if !ENABLE_EXTRA_COMPAT
 277                                        regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0
 278#else
 279                                        re_search(&gl->compiled_regex, line, line_len,
 280                                                        /*start:*/ 0, /*range:*/ line_len,
 281                                                        &gl->matched_range) >= 0
 282#endif
 283                                ) {
 284                                        if (!(option_mask32 & OPT_w))
 285                                                found = 1;
 286                                        else {
 287                                                char c = ' ';
 288                                                if (gl->matched_range.rm_so)
 289                                                        c = line[gl->matched_range.rm_so - 1];
 290                                                if (!isalnum(c) && c != '_') {
 291                                                        c = line[gl->matched_range.rm_eo];
 292                                                        if (!c || (!isalnum(c) && c != '_'))
 293                                                                found = 1;
 294                                                }
 295                                        }
 296                                }
 297                        }
 298                        /* If it's non-inverted search, we can stop
 299                         * at first match */
 300                        if (found && !invert_search)
 301                                goto do_found;
 302                        pattern_ptr = pattern_ptr->link;
 303                } /* while (pattern_ptr) */
 304
 305                if (found ^ invert_search) {
 306 do_found:
 307                        /* keep track of matches */
 308                        nmatches++;
 309
 310                        /* quiet/print (non)matching file names only? */
 311                        if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
 312                                free(line); /* we don't need line anymore */
 313                                if (BE_QUIET) {
 314                                        /* manpage says about -q:
 315                                         * "exit immediately with zero status
 316                                         * if any match is found,
 317                                         * even if errors were detected" */
 318                                        exit(EXIT_SUCCESS);
 319                                }
 320                                /* if we're just printing filenames, we stop after the first match */
 321                                if (PRINT_FILES_WITH_MATCHES) {
 322                                        puts(cur_file);
 323                                        /* fall through to "return 1" */
 324                                }
 325                                /* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */
 326                                return 1; /* one match */
 327                        }
 328
 329#if ENABLE_FEATURE_GREP_CONTEXT
 330                        /* Were we printing context and saw next (unwanted) match? */
 331                        if ((option_mask32 & OPT_m) && nmatches > max_matches)
 332                                break;
 333#endif
 334
 335                        /* print the matched line */
 336                        if (PRINT_MATCH_COUNTS == 0) {
 337#if ENABLE_FEATURE_GREP_CONTEXT
 338                                int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
 339
 340                                /* if we were told to print 'before' lines and there is at least
 341                                 * one line in the circular buffer, print them */
 342                                if (lines_before && before_buf[prevpos] != NULL) {
 343                                        int first_buf_entry_line_num = linenum - lines_before;
 344
 345                                        /* advance to the first entry in the circular buffer, and
 346                                         * figure out the line number is of the first line in the
 347                                         * buffer */
 348                                        idx = curpos;
 349                                        while (before_buf[idx] == NULL) {
 350                                                idx = (idx + 1) % lines_before;
 351                                                first_buf_entry_line_num++;
 352                                        }
 353
 354                                        /* now print each line in the buffer, clearing them as we go */
 355                                        while (before_buf[idx] != NULL) {
 356                                                print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
 357                                                free(before_buf[idx]);
 358                                                before_buf[idx] = NULL;
 359                                                idx = (idx + 1) % lines_before;
 360                                                first_buf_entry_line_num++;
 361                                        }
 362                                }
 363
 364                                /* make a note that we need to print 'after' lines */
 365                                print_n_lines_after = lines_after;
 366#endif
 367                                if (option_mask32 & OPT_o) {
 368                                        if (FGREP_FLAG) {
 369                                                /* -Fo just prints the pattern
 370                                                 * (unless -v: -Fov doesnt print anything at all) */
 371                                                if (found)
 372                                                        print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
 373                                        } else while (1) {
 374                                                unsigned end = gl->matched_range.rm_eo;
 375                                                char old = line[end];
 376                                                line[end] = '\0';
 377                                                print_line(line + gl->matched_range.rm_so,
 378                                                                end - gl->matched_range.rm_so,
 379                                                                linenum, ':');
 380                                                line[end] = old;
 381#if !ENABLE_EXTRA_COMPAT
 382                                                if (regexec(&gl->compiled_regex, line + end,
 383                                                                1, &gl->matched_range, REG_NOTBOL) != 0)
 384                                                        break;
 385                                                gl->matched_range.rm_so += end;
 386                                                gl->matched_range.rm_eo += end;
 387#else
 388                                                if (re_search(&gl->compiled_regex, line, line_len,
 389                                                                end, line_len - end,
 390                                                                &gl->matched_range) < 0)
 391                                                        break;
 392#endif
 393                                        }
 394                                } else {
 395                                        print_line(line, line_len, linenum, ':');
 396                                }
 397                        }
 398                }
 399#if ENABLE_FEATURE_GREP_CONTEXT
 400                else { /* no match */
 401                        /* if we need to print some context lines after the last match, do so */
 402                        if (print_n_lines_after) {
 403                                print_line(line, strlen(line), linenum, '-');
 404                                print_n_lines_after--;
 405                        } else if (lines_before) {
 406                                /* Add the line to the circular 'before' buffer */
 407                                free(before_buf[curpos]);
 408                                before_buf[curpos] = line;
 409                                IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
 410                                curpos = (curpos + 1) % lines_before;
 411                                /* avoid free(line) - we took the line */
 412                                line = NULL;
 413                        }
 414                }
 415
 416#endif /* ENABLE_FEATURE_GREP_CONTEXT */
 417#if !ENABLE_EXTRA_COMPAT
 418                free(line);
 419#endif
 420                /* Did we print all context after last requested match? */
 421                if ((option_mask32 & OPT_m)
 422                 && !print_n_lines_after
 423                 && nmatches == max_matches
 424                ) {
 425                        break;
 426                }
 427        } /* while (read line) */
 428
 429        /* special-case file post-processing for options where we don't print line
 430         * matches, just filenames and possibly match counts */
 431
 432        /* grep -c: print [filename:]count, even if count is zero */
 433        if (PRINT_MATCH_COUNTS) {
 434                if (print_filename)
 435                        printf("%s:", cur_file);
 436                printf("%d\n", nmatches);
 437        }
 438
 439        /* grep -L: print just the filename */
 440        if (PRINT_FILES_WITHOUT_MATCHES) {
 441                /* nmatches is zero, no need to check it:
 442                 * we return 1 early if we detected a match
 443                 * and PRINT_FILES_WITHOUT_MATCHES is set */
 444                puts(cur_file);
 445        }
 446
 447        return nmatches;
 448}
 449
 450#if ENABLE_FEATURE_CLEAN_UP
 451#define new_grep_list_data(p, m) add_grep_list_data(p, m)
 452static char *add_grep_list_data(char *pattern, int flg_used_mem)
 453#else
 454#define new_grep_list_data(p, m) add_grep_list_data(p)
 455static char *add_grep_list_data(char *pattern)
 456#endif
 457{
 458        grep_list_data_t *gl = xzalloc(sizeof(*gl));
 459        gl->pattern = pattern;
 460#if ENABLE_FEATURE_CLEAN_UP
 461        gl->flg_mem_alocated_compiled = flg_used_mem;
 462#else
 463        /*gl->flg_mem_alocated_compiled = 0;*/
 464#endif
 465        return (char *)gl;
 466}
 467
 468static void load_regexes_from_file(llist_t *fopt)
 469{
 470        char *line;
 471        FILE *f;
 472
 473        while (fopt) {
 474                llist_t *cur = fopt;
 475                char *ffile = cur->data;
 476
 477                fopt = cur->link;
 478                free(cur);
 479                f = xfopen_stdin(ffile);
 480                while ((line = xmalloc_fgetline(f)) != NULL) {
 481                        llist_add_to(&pattern_head,
 482                                new_grep_list_data(line, ALLOCATED));
 483                }
 484        }
 485}
 486
 487static int FAST_FUNC file_action_grep(const char *filename,
 488                        struct stat *statbuf UNUSED_PARAM,
 489                        void* matched,
 490                        int depth UNUSED_PARAM)
 491{
 492        FILE *file = fopen_for_read(filename);
 493        if (file == NULL) {
 494                if (!SUPPRESS_ERR_MSGS)
 495                        bb_simple_perror_msg(filename);
 496                open_errors = 1;
 497                return 0;
 498        }
 499        cur_file = filename;
 500        *(int*)matched += grep_file(file);
 501        fclose(file);
 502        return 1;
 503}
 504
 505static int grep_dir(const char *dir)
 506{
 507        int matched = 0;
 508        recursive_action(dir,
 509                /* recurse=yes */ ACTION_RECURSE |
 510                /* followLinks=no */
 511                /* depthFirst=yes */ ACTION_DEPTHFIRST,
 512                /* fileAction= */ file_action_grep,
 513                /* dirAction= */ NULL,
 514                /* userData= */ &matched,
 515                /* depth= */ 0);
 516        return matched;
 517}
 518
 519int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 520int grep_main(int argc, char **argv)
 521{
 522        FILE *file;
 523        int matched;
 524        llist_t *fopt = NULL;
 525
 526        /* do normal option parsing */
 527#if ENABLE_FEATURE_GREP_CONTEXT
 528        int Copt;
 529
 530        /* -H unsets -h; -C unsets -A,-B; -e,-f are lists;
 531         * -m,-A,-B,-C have numeric param */
 532        opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
 533        getopt32(argv,
 534                OPTSTR_GREP,
 535                &pattern_head, &fopt, &max_matches,
 536                &lines_after, &lines_before, &Copt);
 537
 538        if (option_mask32 & OPT_C) {
 539                /* -C unsets prev -A and -B, but following -A or -B
 540                   may override it */
 541                if (!(option_mask32 & OPT_A)) /* not overridden */
 542                        lines_after = Copt;
 543                if (!(option_mask32 & OPT_B)) /* not overridden */
 544                        lines_before = Copt;
 545        }
 546        /* sanity checks */
 547        if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
 548                option_mask32 &= ~OPT_n;
 549                lines_before = 0;
 550                lines_after = 0;
 551        } else if (lines_before > 0) {
 552                before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
 553                IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
 554        }
 555#else
 556        /* with auto sanity checks */
 557        /* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
 558        opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
 559        getopt32(argv, OPTSTR_GREP,
 560                &pattern_head, &fopt, &max_matches);
 561#endif
 562        invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
 563
 564        if (pattern_head != NULL) {
 565                /* convert char **argv to grep_list_data_t */
 566                llist_t *cur;
 567
 568                for (cur = pattern_head; cur; cur = cur->link)
 569                        cur->data = new_grep_list_data(cur->data, 0);
 570        }
 571        if (option_mask32 & OPT_f)
 572                load_regexes_from_file(fopt);
 573
 574        if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
 575                option_mask32 |= OPT_F;
 576
 577#if !ENABLE_EXTRA_COMPAT
 578        if (!(option_mask32 & (OPT_o | OPT_w)))
 579                reflags = REG_NOSUB;
 580#endif
 581
 582        if (ENABLE_FEATURE_GREP_EGREP_ALIAS
 583         && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
 584        ) {
 585                reflags |= REG_EXTENDED;
 586        }
 587#if ENABLE_EXTRA_COMPAT
 588        else {
 589                reflags = RE_SYNTAX_GREP;
 590        }
 591#endif
 592
 593        if (option_mask32 & OPT_i) {
 594#if !ENABLE_EXTRA_COMPAT
 595                reflags |= REG_ICASE;
 596#else
 597                int i;
 598                case_fold = xmalloc(256);
 599                for (i = 0; i < 256; i++)
 600                        case_fold[i] = (unsigned char)i;
 601                for (i = 'a'; i <= 'z'; i++)
 602                        case_fold[i] = (unsigned char)(i - ('a' - 'A'));
 603#endif
 604        }
 605
 606        argv += optind;
 607        argc -= optind;
 608
 609        /* if we didn't get a pattern from -e and no command file was specified,
 610         * first parameter should be the pattern. no pattern, no worky */
 611        if (pattern_head == NULL) {
 612                char *pattern;
 613                if (*argv == NULL)
 614                        bb_show_usage();
 615                pattern = new_grep_list_data(*argv++, 0);
 616                llist_add_to(&pattern_head, pattern);
 617                argc--;
 618        }
 619
 620        /* argv[0..(argc-1)] should be names of file to grep through. If
 621         * there is more than one file to grep, we will print the filenames. */
 622        if (argc > 1)
 623                print_filename = 1;
 624        /* -H / -h of course override */
 625        if (option_mask32 & OPT_H)
 626                print_filename = 1;
 627        if (option_mask32 & OPT_h)
 628                print_filename = 0;
 629
 630        /* If no files were specified, or '-' was specified, take input from
 631         * stdin. Otherwise, we grep through all the files specified. */
 632        matched = 0;
 633        do {
 634                cur_file = *argv++;
 635                file = stdin;
 636                if (!cur_file || LONE_DASH(cur_file)) {
 637                        cur_file = "(standard input)";
 638                } else {
 639                        if (option_mask32 & OPT_r) {
 640                                struct stat st;
 641                                if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
 642                                        if (!(option_mask32 & OPT_h))
 643                                                print_filename = 1;
 644                                        matched += grep_dir(cur_file);
 645                                        goto grep_done;
 646                                }
 647                        }
 648                        /* else: fopen(dir) will succeed, but reading won't */
 649                        file = fopen_for_read(cur_file);
 650                        if (file == NULL) {
 651                                if (!SUPPRESS_ERR_MSGS)
 652                                        bb_simple_perror_msg(cur_file);
 653                                open_errors = 1;
 654                                continue;
 655                        }
 656                }
 657                matched += grep_file(file);
 658                fclose_if_not_stdin(file);
 659 grep_done: ;
 660        } while (--argc > 0);
 661
 662        /* destroy all the elments in the pattern list */
 663        if (ENABLE_FEATURE_CLEAN_UP) {
 664                while (pattern_head) {
 665                        llist_t *pattern_head_ptr = pattern_head;
 666                        grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
 667
 668                        pattern_head = pattern_head->link;
 669                        if (gl->flg_mem_alocated_compiled & ALLOCATED)
 670                                free(gl->pattern);
 671                        if (gl->flg_mem_alocated_compiled & COMPILED)
 672                                regfree(&gl->compiled_regex);
 673                        free(gl);
 674                        free(pattern_head_ptr);
 675                }
 676        }
 677        /* 0 = success, 1 = failed, 2 = error */
 678        if (open_errors)
 679                return 2;
 680        return !matched; /* invert return value: 0 = success, 1 = failed */
 681}
 682