int main(int argc, char *argv[]) { char **aargv, **eargv, *eopts; char *ep; const char *pn; long long l; unsigned int aargc, eargc, i; int c, lastc, needpattern, newarg, prevoptind; setlocale(LC_ALL, ""); #ifndef WITHOUT_NLS catalog = catopen("grep", NL_CAT_LOCALE); #endif /* Check what is the program name of the binary. In this way we can have all the funcionalities in one binary without the need of scripting and using ugly hacks. */ pn = getprogname(); if (pn[0] == 'b' && pn[1] == 'z') { filebehave = FILE_BZIP; pn += 2; } else if (pn[0] == 'x' && pn[1] == 'z') { filebehave = FILE_XZ; pn += 2; } else if (pn[0] == 'l' && pn[1] == 'z') { filebehave = FILE_LZMA; pn += 2; } else if (pn[0] == 'r') { dirbehave = DIR_RECURSE; Hflag = true; } else if (pn[0] == 'z') { filebehave = FILE_GZIP; pn += 1; } switch (pn[0]) { case 'e': grepbehave = GREP_EXTENDED; break; case 'f': grepbehave = GREP_FIXED; break; } lastc = '\0'; newarg = 1; prevoptind = 1; needpattern = 1; fileeol = '\n'; eopts = getenv("GREP_OPTIONS"); /* support for extra arguments in GREP_OPTIONS */ eargc = 0; if (eopts != NULL && eopts[0] != '\0') { char *str; /* make an estimation of how many extra arguments we have */ for (unsigned int j = 0; j < strlen(eopts); j++) if (eopts[j] == ' ') eargc++; eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); eargc = 0; /* parse extra arguments */ while ((str = strsep(&eopts, " ")) != NULL) if (str[0] != '\0') eargv[eargc++] = grep_strdup(str); aargv = (char **)grep_calloc(eargc + argc + 1, sizeof(char *)); aargv[0] = argv[0]; for (i = 0; i < eargc; i++) aargv[i + 1] = eargv[i]; for (int j = 1; j < argc; j++, i++) aargv[i + 1] = argv[j]; aargc = eargc + argc; } else { aargv = argv; aargc = argc; } while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != -1)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (newarg || !isdigit(lastc)) Aflag = 0; else if (Aflag > LLONG_MAX / 10 - 1) { errno = ERANGE; err(2, NULL); } Aflag = Bflag = (Aflag * 10) + (c - '0'); break; case 'C': if (optarg == NULL) { Aflag = Bflag = 2; break; } /* FALLTHROUGH */ case 'A': /* FALLTHROUGH */ case 'B': errno = 0; l = strtoll(optarg, &ep, 10); if (errno == ERANGE || errno == EINVAL) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } else if (l < 0) { errno = EINVAL; err(2, "context argument must be non-negative"); } if (c == 'A') Aflag = l; else if (c == 'B') Bflag = l; else Aflag = Bflag = l; break; case 'a': binbehave = BINFILE_TEXT; break; case 'b': bflag = true; break; case 'c': cflag = true; break; case 'D': if (strcasecmp(optarg, "skip") == 0) devbehave = DEV_SKIP; else if (strcasecmp(optarg, "read") == 0) devbehave = DEV_READ; else errx(2, getstr(3), "--devices"); break; case 'd': if (strcasecmp("recurse", optarg) == 0) { Hflag = true; dirbehave = DIR_RECURSE; } else if (strcasecmp("skip", optarg) == 0) dirbehave = DIR_SKIP; else if (strcasecmp("read", optarg) == 0) dirbehave = DIR_READ; else errx(2, getstr(3), "--directories"); break; case 'E': grepbehave = GREP_EXTENDED; break; case 'e': { char *token; char *string = optarg; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); } needpattern = 0; break; case 'F': grepbehave = GREP_FIXED; break; case 'f': read_patterns(optarg); needpattern = 0; break; case 'G': grepbehave = GREP_BASIC; break; case 'H': Hflag = true; break; case 'h': Hflag = false; hflag = true; break; case 'I': binbehave = BINFILE_SKIP; break; case 'i': case 'y': iflag = true; cflags |= REG_ICASE; break; case 'J': #ifdef WITHOUT_BZIP2 errno = EOPNOTSUPP; err(2, "bzip2 support was disabled at compile-time"); #endif filebehave = FILE_BZIP; break; case 'L': lflag = false; Lflag = true; break; case 'l': Lflag = false; lflag = true; break; case 'm': mflag = true; errno = 0; mlimit = mcount = strtoll(optarg, &ep, 10); if (((errno == ERANGE) && (mcount == LLONG_MAX)) || ((errno == EINVAL) && (mcount == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } break; case 'M': filebehave = FILE_LZMA; break; case 'n': nflag = true; break; case 'O': linkbehave = LINK_EXPLICIT; break; case 'o': oflag = true; cflags &= ~REG_NOSUB; break; case 'p': linkbehave = LINK_SKIP; break; case 'q': qflag = true; break; case 'S': linkbehave = LINK_READ; break; case 'R': case 'r': dirbehave = DIR_RECURSE; Hflag = true; break; case 's': sflag = true; break; case 'U': binbehave = BINFILE_BIN; break; case 'u': case MMAP_OPT: filebehave = FILE_MMAP; break; case 'V': #ifdef WITH_GNU printf(getstr(10), getprogname(), VERSION); #else printf(getstr(9), getprogname(), VERSION); #endif exit(0); case 'v': vflag = true; break; case 'w': wflag = true; cflags &= ~REG_NOSUB; break; case 'x': xflag = true; cflags &= ~REG_NOSUB; break; case 'X': filebehave = FILE_XZ; break; case 'z': fileeol = '\0'; break; case 'Z': filebehave = FILE_GZIP; break; case BIN_OPT: if (strcasecmp("binary", optarg) == 0) binbehave = BINFILE_BIN; else if (strcasecmp("without-match", optarg) == 0) binbehave = BINFILE_SKIP; else if (strcasecmp("text", optarg) == 0) binbehave = BINFILE_TEXT; else errx(2, getstr(3), "--binary-files"); break; case COLOR_OPT: color = NULL; if (optarg == NULL || strcasecmp("auto", optarg) == 0 || strcasecmp("tty", optarg) == 0 || strcasecmp("if-tty", optarg) == 0) { char *term; term = getenv("TERM"); if (isatty(STDOUT_FILENO) && term != NULL && strcasecmp(term, "dumb") != 0) color = init_color("01;31"); } else if (strcasecmp("always", optarg) == 0 || strcasecmp("yes", optarg) == 0 || strcasecmp("force", optarg) == 0) { color = init_color("01;31"); } else if (strcasecmp("never", optarg) != 0 && strcasecmp("none", optarg) != 0 && strcasecmp("no", optarg) != 0) errx(2, getstr(3), "--color"); cflags &= ~REG_NOSUB; break; case LABEL_OPT: label = optarg; break; case LINEBUF_OPT: lbflag = true; break; case NULL_OPT: nullflag = true; break; case R_INCLUDE_OPT: finclude = true; add_fpattern(optarg, INCL_PAT); break; case R_EXCLUDE_OPT: fexclude = true; add_fpattern(optarg, EXCL_PAT); break; case R_DINCLUDE_OPT: dinclude = true; add_dpattern(optarg, INCL_PAT); break; case R_DEXCLUDE_OPT: dexclude = true; add_dpattern(optarg, EXCL_PAT); break; case HELP_OPT: default: usage(); } lastc = c; newarg = optind != prevoptind; prevoptind = optind; } aargc -= optind; aargv += optind; /* Empty pattern file matches nothing */ if (!needpattern && (patterns == 0)) exit(1); /* Fail if we don't have any pattern */ if (aargc == 0 && needpattern) usage(); /* Process patterns from command line */ if (aargc != 0 && needpattern) { char *token; char *string = *aargv; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); --aargc; ++aargv; } switch (grepbehave) { case GREP_BASIC: break; case GREP_FIXED: /* * regex(3) implementations that support fixed-string searches generally * define either REG_NOSPEC or REG_LITERAL. Set the appropriate flag * here. If neither are defined, GREP_FIXED later implies that the * internal literal matcher should be used. Other cflags that have * the same interpretation as REG_NOSPEC and REG_LITERAL should be * similarly added here, and grep.h should be amended to take this into * consideration when defining WITH_INTERNAL_NOSPEC. */ #if defined(REG_NOSPEC) cflags |= REG_NOSPEC; #elif defined(REG_LITERAL) cflags |= REG_LITERAL; #endif break; case GREP_EXTENDED: cflags |= REG_EXTENDED; break; default: /* NOTREACHED */ usage(); } #ifndef WITHOUT_FASTMATCH fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); #endif r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); /* Don't process any patterns if we have a blank one */ #ifdef WITH_INTERNAL_NOSPEC if (!matchall && grepbehave != GREP_FIXED) { #else if (!matchall) { #endif /* Check if cheating is allowed (always is for fgrep). */ for (i = 0; i < patterns; ++i) { #ifndef WITHOUT_FASTMATCH /* * Attempt compilation with fastmatch regex and * fallback to regex(3) if it fails. */ if (fastncomp(&fg_pattern[i], pattern[i].pat, pattern[i].len, cflags) == 0) continue; #endif c = regcomp(&r_pattern[i], pattern[i].pat, cflags); if (c != 0) { regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF); errx(2, "%s", re_error); } } } if (lbflag) setlinebuf(stdout); if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true; if (aargc == 0 && dirbehave != DIR_RECURSE) exit(!procfile("-")); if (dirbehave == DIR_RECURSE) c = grep_tree(aargv); else for (c = 0; aargc--; ++aargv) { if ((finclude || fexclude) && !file_matching(*aargv)) continue; c+= procfile(*aargv); } #ifndef WITHOUT_NLS catclose(catalog); #endif /* Find out the correct return value according to the results and the command line option. */ exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); }
/* * Processes a directory when a recursive search is performed with * the -R option. Each appropriate file is passed to procfile(). */ int grep_tree(char **argv) { FTS *fts; FTSENT *p; int c, fts_flags; bool ok; c = fts_flags = 0; switch(linkbehave) { case LINK_EXPLICIT: fts_flags = FTS_COMFOLLOW; break; case LINK_SKIP: fts_flags = FTS_PHYSICAL; break; default: fts_flags = FTS_LOGICAL; } fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; if (!(fts = fts_open(argv, fts_flags, NULL))) err(2, "fts_open"); while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DNR: /* FALLTHROUGH */ case FTS_ERR: file_err = true; if(!sflag) warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); break; case FTS_D: /* FALLTHROUGH */ case FTS_DP: if (dexclude || dinclude) if (!dir_matching(p->fts_name) || !dir_matching(p->fts_path)) fts_set(fts, p, FTS_SKIP); break; case FTS_DC: /* Print a warning for recursive directory loop */ warnx("warning: %s: recursive directory loop", p->fts_path); break; default: /* Check for file exclusion/inclusion */ ok = true; if (fexclude || finclude) ok &= file_matching(p->fts_path); if (ok) c += procfile(p->fts_path); break; } } fts_close(fts); return (c); }