#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <regex.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include "grep.h"
static int linesqueued;
static int procline(str_t *l, int);
static int grep_search(fastgrep_t *, char *, size_t, regmatch_t *pmatch, int);
#ifndef SMALL
static bool grep_cmp(const char *, const char *, size_t);
static void grep_revstr(unsigned char *, int);
#endif
int
grep_tree(char **argv)
{
FTS *fts;
FTSENT *p;
int c, fts_flags;
char *dot_argv[] = { ".", NULL };
char *path;
if (argv[0] == NULL)
argv = dot_argv;
c = 0;
fts_flags = FTS_PHYSICAL | FTS_NOSTAT | FTS_NOCHDIR;
if (!(fts = fts_open(argv, fts_flags, NULL)))
err(2, NULL);
while ((p = fts_read(fts)) != NULL) {
switch (p->fts_info) {
case FTS_DNR:
break;
case FTS_ERR:
file_err = 1;
if(!sflag)
warnc(p->fts_errno, "%s", p->fts_path);
break;
case FTS_D:
case FTS_DP:
break;
default:
path = p->fts_path;
if (argv == dot_argv && p->fts_pathlen >= 2)
path += 2;
c |= procfile(path);
break;
}
}
if (errno)
err(2, "fts_read");
fts_close(fts);
return c;
}
int
procfile(char *fn)
{
str_t ln;
file_t *f;
int t, z, nottext, overflow = 0;
unsigned long long c;
mcount = mlimit;
if (fn == NULL) {
fn = "(standard input)";
f = grep_fdopen(STDIN_FILENO);
} else {
f = grep_open(fn);
}
if (f == NULL) {
if (errno == EISDIR)
return 0;
file_err = 1;
if (!sflag)
warn("%s", fn);
return 0;
}
nottext = grep_bin_file(f);
if (nottext && binbehave == BIN_FILE_SKIP) {
grep_close(f);
return 0;
}
ln.file = fn;
if (labelname)
ln.file = (char *)labelname;
ln.line_no = 0;
ln.len = 0;
linesqueued = 0;
tail = 0;
ln.off = -1;
if (Bflag > 0)
initqueue();
for (c = 0; c == 0 || !(lflag || qflag); ) {
if (mflag && mlimit == 0)
break;
ln.off += ln.len + 1;
if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL)
break;
if (ln.len > 0 && ln.dat[ln.len - 1] == '\n')
--ln.len;
ln.line_no++;
z = tail;
if ((t = procline(&ln, nottext)) == 0 && Bflag > 0 && z == 0) {
enqueue(&ln);
linesqueued++;
}
if (ULLONG_MAX - c < (unsigned long long)t)
overflow = 1;
else
c += t;
if (mflag && mcount <= 0 && tail <= 0)
break;
}
if (Bflag > 0)
clearqueue();
grep_close(f);
if (cflag) {
if (!hflag)
printf("%s%c", ln.file, nullflag ? '\0' : ':');
printf("%llu%s\n", c, overflow ? "+" : "");
}
if (lflag && c != 0)
printf("%s%c", fn, nullflag ? '\0' : '\n');
if (Lflag && c == 0)
printf("%s%c", fn, nullflag ? '\0' : '\n');
if (c && !cflag && !lflag && !Lflag &&
binbehave == BIN_FILE_BIN && nottext && !qflag)
printf("Binary file %s matches\n", fn);
return overflow || c != 0;
}
#define isword(x) (isalnum((unsigned char)x) || (x) == '_')
static int
procline(str_t *l, int nottext)
{
regmatch_t pmatch = { 0 };
int c, i, r, counted;
regoff_t offset;
if (l->len > SSIZE_MAX) {
errx(2, "Line is too big to process");
}
c = 0;
i = 0;
counted = 0;
if (matchall) {
c = 1;
goto print;
}
if (mflag && mcount <= 0)
goto print;
for (i = 0; i < patterns; i++) {
offset = 0;
redo:
if (fg_pattern[i].pattern) {
int flags = 0;
if (offset)
flags |= REG_NOTBOL;
r = grep_search(&fg_pattern[i], l->dat + offset,
l->len - offset, &pmatch, flags);
pmatch.rm_so += offset;
pmatch.rm_eo += offset;
} else {
int flags = eflags;
if (offset)
flags |= REG_NOTBOL;
pmatch.rm_so = offset;
pmatch.rm_eo = l->len;
r = regexec(&r_pattern[i], l->dat, 1, &pmatch, flags);
}
if (r == 0 && xflag) {
if (pmatch.rm_so != 0 || pmatch.rm_eo != l->len)
r = REG_NOMATCH;
}
if (r == 0) {
c = 1;
if (oflag && pmatch.rm_so != pmatch.rm_eo)
goto print;
break;
}
}
if (oflag)
return c;
print:
if (vflag)
c = !c;
if (mflag && !counted) {
mcount -= c;
counted = 1;
}
if (c && binbehave == BIN_FILE_BIN && nottext)
return c;
if ((tail > 0 || c) && !cflag && !qflag) {
if (c) {
if (first > 0 && tail == 0 && (Aflag || (Bflag &&
Bflag < linesqueued)))
printf("--\n");
first = 1;
tail = Aflag;
if (Bflag > 0)
printqueue();
linesqueued = 0;
printline(l, ':', oflag ? &pmatch : NULL);
} else {
printline(l, '-', oflag ? &pmatch : NULL);
tail--;
}
}
if (oflag && !matchall) {
offset = pmatch.rm_eo;
goto redo;
}
return c;
}
#ifndef SMALL
void
fgrepcomp(fastgrep_t *fg, const unsigned char *pattern)
{
int i;
fg->patternLen = strlen(pattern);
fg->bol = 0;
fg->eol = 0;
fg->wmatch = wflag;
fg->reversedSearch = 0;
if (iflag) {
fg->pattern = grep_malloc(fg->patternLen + 1);
for (i = 0; i < fg->patternLen; i++)
fg->pattern[i] = toupper(pattern[i]);
fg->pattern[fg->patternLen] = '\0';
} else
fg->pattern = (unsigned char *)pattern;
for (i = 0; i <= UCHAR_MAX; i++)
fg->qsBc[i] = fg->patternLen;
for (i = 1; i < fg->patternLen; i++) {
fg->qsBc[fg->pattern[i]] = fg->patternLen - i;
if (iflag)
fg->qsBc[tolower(fg->pattern[i])] = fg->patternLen - i;
}
}
#endif
int
fastcomp(fastgrep_t *fg, const char *pattern)
{
#ifdef SMALL
return -1;
#else
int i;
int bol = 0;
int eol = 0;
int shiftPatternLen;
int hasDot = 0;
int firstHalfDot = -1;
int firstLastHalfDot = -1;
int lastHalfDot = 0;
fg->patternLen = strlen(pattern);
fg->bol = 0;
fg->eol = 0;
fg->wmatch = 0;
fg->reversedSearch = 0;
if (fg->patternLen > 0 && pattern[fg->patternLen - 1] == '$') {
eol++;
fg->eol = 1;
fg->patternLen--;
}
if (pattern[0] == '^') {
bol++;
fg->bol = 1;
fg->patternLen--;
}
if (wflag) {
int extra = Eflag ? 1 : 2;
fg->patternLen -= 14 + 2 * extra;
fg->wmatch = 7 + extra;
} else if (fg->patternLen >= 14 &&
strncmp(pattern + fg->bol, "[[:<:]]", 7) == 0 &&
strncmp(pattern + fg->bol + fg->patternLen - 7, "[[:>:]]", 7) == 0) {
fg->patternLen -= 14;
fg->wmatch = 7;
}
fg->pattern = grep_malloc(fg->patternLen + 1);
memcpy(fg->pattern, pattern + bol + fg->wmatch, fg->patternLen);
fg->pattern[fg->patternLen] = '\0';
for (i = 0; i < fg->patternLen; i++)
{
switch (fg->pattern[i]) {
case '.':
hasDot = i;
if (i < fg->patternLen / 2) {
if (firstHalfDot < 0)
firstHalfDot = i;
} else {
lastHalfDot = i;
if (firstLastHalfDot < 0)
firstLastHalfDot = i;
}
break;
case '(': case ')':
case '{': case '}':
case '?':
case '+':
case '|':
if (!Eflag)
goto nonspecial;
case '\\':
case '*':
case '[': case ']':
free(fg->pattern);
fg->pattern = NULL;
return (-1);
default:
nonspecial:
if (iflag)
fg->pattern[i] = toupper(fg->pattern[i]);
break;
}
}
if ((!(lflag || cflag || oflag)) && ((!(bol || eol)) &&
((lastHalfDot) && ((firstHalfDot < 0) ||
((fg->patternLen - (lastHalfDot + 1)) < firstHalfDot))))) {
fg->reversedSearch = 1;
hasDot = fg->patternLen - (firstHalfDot < 0 ?
firstLastHalfDot : firstHalfDot) - 1;
grep_revstr(fg->pattern, fg->patternLen);
}
shiftPatternLen = fg->patternLen - hasDot;
for (i = 0; i <= UCHAR_MAX; i++)
fg->qsBc[i] = shiftPatternLen;
for (i = hasDot + 1; i < fg->patternLen; i++) {
fg->qsBc[fg->pattern[i]] = fg->patternLen - i;
if (iflag)
fg->qsBc[tolower(fg->pattern[i])] = fg->patternLen - i;
}
if (fg->reversedSearch)
grep_revstr(fg->pattern, fg->patternLen);
return (0);
#endif
}
#define wmatch(d, l, s, e) \
((s == 0 || !isword(d[s-1])) && (e == l || !isword(d[e])) && \
e > s && isword(d[s]) && isword(d[e-1]))
static int
grep_search(fastgrep_t *fg, char *data, size_t dataLen, regmatch_t *pmatch,
int flags)
{
#ifdef SMALL
return 0;
#else
regoff_t j;
int rtrnVal = REG_NOMATCH;
pmatch->rm_so = -1;
pmatch->rm_eo = -1;
if (dataLen < fg->patternLen)
return (rtrnVal);
if (fg->bol || fg->eol) {
if (fg->bol && (flags & REG_NOTBOL))
return 0;
if (dataLen >= fg->patternLen) {
if (fg->eol)
j = dataLen - fg->patternLen;
else
j = 0;
if (!((fg->bol && fg->eol) && (dataLen != fg->patternLen)))
if (grep_cmp(fg->pattern, data + j,
fg->patternLen)) {
pmatch->rm_so = j;
pmatch->rm_eo = j + fg->patternLen;
if (!fg->wmatch || wmatch(data, dataLen,
pmatch->rm_so, pmatch->rm_eo))
rtrnVal = 0;
}
}
} else if (fg->reversedSearch) {
j = dataLen;
do {
if (grep_cmp(fg->pattern, data + j - fg->patternLen,
fg->patternLen)) {
pmatch->rm_so = j - fg->patternLen;
pmatch->rm_eo = j;
if (!fg->wmatch || wmatch(data, dataLen,
pmatch->rm_so, pmatch->rm_eo)) {
rtrnVal = 0;
break;
}
}
if (j == fg->patternLen)
break;
j -= fg->qsBc[(unsigned char)data[j - fg->patternLen - 1]];
} while (j >= fg->patternLen);
} else {
j = 0;
do {
if (grep_cmp(fg->pattern, data + j, fg->patternLen)) {
pmatch->rm_so = j;
pmatch->rm_eo = j + fg->patternLen;
if (fg->patternLen == 0 || !fg->wmatch ||
wmatch(data, dataLen, pmatch->rm_so,
pmatch->rm_eo)) {
rtrnVal = 0;
break;
}
}
if (j + fg->patternLen == dataLen)
break;
else
j += fg->qsBc[(unsigned char)data[j + fg->patternLen]];
} while (j <= (dataLen - fg->patternLen));
}
return (rtrnVal);
#endif
}
void *
grep_malloc(size_t size)
{
void *ptr;
if ((ptr = malloc(size)) == NULL)
err(2, "malloc");
return ptr;
}
void *
grep_calloc(size_t nmemb, size_t size)
{
void *ptr;
if ((ptr = calloc(nmemb, size)) == NULL)
err(2, "calloc");
return ptr;
}
void *
grep_realloc(void *ptr, size_t size)
{
if ((ptr = realloc(ptr, size)) == NULL)
err(2, "realloc");
return ptr;
}
void *
grep_reallocarray(void *ptr, size_t nmemb, size_t size)
{
if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
err(2, "reallocarray");
return ptr;
}
#ifndef SMALL
static bool
grep_cmp(const char *pattern, const char *data, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
if (((pattern[i] == data[i]) || (!Fflag && pattern[i] == '.'))
|| (iflag && pattern[i] == toupper((unsigned char)data[i])))
continue;
return false;
}
return true;
}
static void
grep_revstr(unsigned char *str, int len)
{
int i;
char c;
for (i = 0; i < len / 2; i++) {
c = str[i];
str[i] = str[len - i - 1];
str[len - i - 1] = c;
}
}
#endif
void
printline(str_t *line, int sep, regmatch_t *pmatch)
{
int n;
n = 0;
if (!hflag) {
fputs(line->file, stdout);
if (nullflag)
putchar(0);
else
++n;
}
if (nflag) {
if (n)
putchar(sep);
printf("%lld", line->line_no);
++n;
}
if (bflag) {
if (n)
putchar(sep);
printf("%lld", (long long)line->off +
(pmatch ? pmatch->rm_so : 0));
++n;
}
if (n)
putchar(sep);
if (pmatch)
fwrite(line->dat + pmatch->rm_so,
pmatch->rm_eo - pmatch->rm_so, 1, stdout);
else
fwrite(line->dat, line->len, 1, stdout);
putchar('\n');
}