📄 grep.c
字号:
static int filename_mask; /* If zero, output nulls after filenames. */static int out_quiet; /* Suppress all normal output. */static int out_invert; /* Print nonmatching stuff. */static int out_file; /* Print filenames. */static int out_line; /* Print line numbers. */static int out_byte; /* Print byte offsets. */static int out_before; /* Lines of leading context. */static int out_after; /* Lines of trailing context. */static int count_matches; /* Count matching lines. */static int list_files; /* List matching files. */static int no_filenames; /* Suppress file names. */static off_t max_count; /* Stop after outputting this many lines from an input file. */static int line_buffered; /* If nonzero, use line buffering, i.e. fflush everyline out. */static char *label = NULL; /* Fake filename for stdin *//* Internal variables to keep track of byte count, context, etc. */static uintmax_t totalcc; /* Total character count before bufbeg. */static char const *lastnl; /* Pointer after last newline counted. */static char const *lastout; /* Pointer after last character output; NULL if no character has been output or if it's conceptually before bufbeg. */static uintmax_t totalnl; /* Total newline count before lastnl. */static off_t outleft; /* Maximum number of lines to be output. */static int pending; /* Pending lines of output. Always kept 0 if out_quiet is true. */static int done_on_match; /* Stop scanning file on first match. */static int exit_on_match; /* Exit on first match. */#if defined(HAVE_DOS_FILE_CONTENTS)# include "dosbuf.c"#endif/* Add two numbers that count input bytes or lines, and report an error if the addition overflows. */static uintmax_tadd_count (uintmax_t a, uintmax_t b){ uintmax_t sum = a + b; if (sum < a) error (2, 0, _("input is too large to count")); return sum;}static voidnlscan (char const *lim){ size_t newlines = 0; char const *beg; for (beg = lastnl; beg != lim; beg = memchr (beg, eolbyte, lim - beg), beg++) newlines++; totalnl = add_count (totalnl, newlines); lastnl = lim;}/* Print a byte offset, followed by a character separator. */static voidprint_offset_sep (uintmax_t pos, char sep){ /* Do not rely on printf to print pos, since uintmax_t may be longer than long, and long long is not portable. */ char buf[sizeof pos * CHAR_BIT]; char *p = buf + sizeof buf - 1; *p = sep; do *--p = '0' + pos % 10; while ((pos /= 10) != 0); fwrite (p, 1, buf + sizeof buf - p, stdout);}static voidprline (char const *beg, char const *lim, int sep){ if (out_file) printf ("%s%c", filename, sep & filename_mask); if (out_line) { nlscan (beg); totalnl = add_count (totalnl, 1); print_offset_sep (totalnl, sep); lastnl = lim; } if (out_byte) { uintmax_t pos = add_count (totalcc, beg - bufbeg);#if defined(HAVE_DOS_FILE_CONTENTS) pos = dossified_pos (pos);#endif print_offset_sep (pos, sep); } if (only_matching) { size_t match_size; size_t match_offset; while ((match_offset = (*execute) (beg, lim - beg, &match_size, 1)) != (size_t) -1) { char const *b = beg + match_offset; if (b == lim) break; if(color_option) printf("\33[%sm", grep_color); fwrite(b, sizeof (char), match_size, stdout); if(color_option) fputs("\33[00m", stdout); fputs("\n", stdout); beg = b + match_size; } lastout = lim; if(line_buffered) fflush(stdout); return; } if (color_option) { size_t match_size; size_t match_offset; if(match_icase) { /* Yuck, this is tricky */ char *buf = (char*) xmalloc (lim - beg); char *ibeg = buf; char *ilim = ibeg + (lim - beg); int i; for (i = 0; i < lim - beg; i++) ibeg[i] = tolower (beg[i]); while ((match_offset = (*execute) (ibeg, ilim-ibeg, &match_size, 1)) != (size_t) -1) { char const *b = beg + match_offset; if (b == lim) break; fwrite (beg, sizeof (char), match_offset, stdout); printf ("\33[%sm", grep_color); fwrite (b, sizeof (char), match_size, stdout); fputs ("\33[00m", stdout); beg = b + match_size; ibeg = ibeg + match_offset + match_size; } fwrite (beg, 1, lim - beg, stdout); free (buf); lastout = lim; return; } while ((match_offset = (*execute) (beg, lim - beg, &match_size, 1)) != (size_t) -1) { char const *b = beg + match_offset; /* Avoid matching the empty line at the end of the buffer. */ if (b == lim) break; fwrite (beg, sizeof (char), match_offset, stdout); printf ("\33[%sm", grep_color); fwrite (b, sizeof (char), match_size, stdout); fputs ("\33[00m", stdout); beg = b + match_size; } } fwrite (beg, 1, lim - beg, stdout); if (ferror (stdout)) error (0, errno, _("writing output")); lastout = lim; if (line_buffered) fflush (stdout);}/* Print pending lines of trailing context prior to LIM. Trailing context ends at the next matching line when OUTLEFT is 0. */static voidprpending (char const *lim){ if (!lastout) lastout = bufbeg; while (pending > 0 && lastout < lim) { char const *nl = memchr (lastout, eolbyte, lim - lastout); size_t match_size; --pending; if (outleft || (((*execute) (lastout, nl - lastout, &match_size, 0) == (size_t) -1) == !out_invert)) prline (lastout, nl + 1, '-'); else pending = 0; }}/* Print the lines between BEG and LIM. Deal with context crap. If NLINESP is non-null, store a count of lines between BEG and LIM. */static voidprtext (char const *beg, char const *lim, int *nlinesp){ static int used; /* avoid printing "--" before any output */ char const *bp, *p; char eol = eolbyte; int i, n; if (!out_quiet && pending > 0) prpending (beg); p = beg; if (!out_quiet) { /* Deal with leading context crap. */ bp = lastout ? lastout : bufbeg; for (i = 0; i < out_before; ++i) if (p > bp) do --p; while (p[-1] != eol); /* We only print the "--" separator if our output is discontiguous from the last output in the file. */ if ((out_before || out_after) && used && p != lastout) puts ("--"); while (p < beg) { char const *nl = memchr (p, eol, beg - p); nl++; prline (p, nl, '-'); p = nl; } } if (nlinesp) { /* Caller wants a line count. */ for (n = 0; p < lim && n < outleft; n++) { char const *nl = memchr (p, eol, lim - p); nl++; if (!out_quiet) prline (p, nl, ':'); p = nl; } *nlinesp = n; /* relying on it that this function is never called when outleft = 0. */ after_last_match = bufoffset - (buflim - p); } else if (!out_quiet) prline (beg, lim, ':'); pending = out_quiet ? 0 : out_after; used = 1;}/* Scan the specified portion of the buffer, matching lines (or between matching lines if OUT_INVERT is true). Return a count of lines printed. */static intgrepbuf (char const *beg, char const *lim){ int nlines, n; register char const *p; size_t match_offset; size_t match_size; nlines = 0; p = beg; while ((match_offset = (*execute) (p, lim - p, &match_size, 0)) != (size_t) -1) { char const *b = p + match_offset; char const *endp = b + match_size; /* Avoid matching the empty line at the end of the buffer. */ if (b == lim) break; if (!out_invert) { prtext (b, endp, (int *) 0); nlines++; outleft--; if (!outleft || done_on_match) { if (exit_on_match) exit (0); after_last_match = bufoffset - (buflim - endp); return nlines; } } else if (p < b) { prtext (p, b, &n); nlines += n; outleft -= n; if (!outleft) return nlines; } p = endp; } if (out_invert && p < lim) { prtext (p, lim, &n); nlines += n; outleft -= n; } return nlines;}/* Search a given file. Normally, return a count of lines printed; but if the file is a directory and we search it recursively, then return -2 if there was a match, and -1 otherwise. */static intgrep (int fd, char const *file, struct stats *stats){ int nlines, i; int not_text; size_t residue, save; char oldc; char *beg; char *lim; char eol = eolbyte; if (!reset (fd, file, stats)) return 0; if (file && directories == RECURSE_DIRECTORIES && S_ISDIR (stats->stat.st_mode)) { /* Close fd now, so that we don't open a lot of file descriptors when we recurse deeply. */ if (close (fd) != 0) error (0, errno, "%s", file); return grepdir (file, stats) - 2; } totalcc = 0; lastout = 0; totalnl = 0; outleft = max_count; after_last_match = 0; pending = 0; nlines = 0; residue = 0; save = 0; if (! fillbuf (save, stats)) { if (! is_EISDIR (errno, file)) suppressible_error (filename, errno); return 0; } not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet) || binary_files == WITHOUT_MATCH_BINARY_FILES) && memchr (bufbeg, eol ? '\0' : '\200', buflim - bufbeg)); if (not_text && binary_files == WITHOUT_MATCH_BINARY_FILES) return 0; done_on_match += not_text; out_quiet += not_text; for (;;) { lastnl = bufbeg; if (lastout) lastout = bufbeg; beg = bufbeg + save; /* no more data to scan (eof) except for maybe a residue -> break */ if (beg == buflim) break; /* Determine new residue (the length of an incomplete line at the end of the buffer, 0 means there is no incomplete last line). */ oldc = beg[-1]; beg[-1] = eol; for (lim = buflim; lim[-1] != eol; lim--) continue; beg[-1] = oldc; if (lim == beg) lim = beg - residue; beg -= residue; residue = buflim - lim; if (beg < lim) { if (outleft) nlines += grepbuf (beg, lim); if (pending) prpending (lim); if((!outleft && !pending) || (nlines && done_on_match && !out_invert)) goto finish_grep; } /* The last OUT_BEFORE lines at the end of the buffer will be needed as leading context if there is a matching line at the begin of the next data. Make beg point to their begin. */ i = 0; beg = lim; while (i < out_before && beg > bufbeg && beg != lastout) { ++i; do --beg; while (beg[-1] != eol); } /* detect if leading context is discontinuous from last printed line. */ if (beg != lastout) lastout = 0; /* Handle some details and read more data to scan. */ save = residue + lim - beg; if (out_byte) totalcc = add_count (totalcc, buflim - bufbeg - save); if (out_line) nlscan (beg); if (! fillbuf (save, stats)) { if (! is_EISDIR (errno, file)) suppressible_error (filename, errno); goto finish_grep; } } if (residue) { *buflim++ = eol; if (outleft) nlines += grepbuf (bufbeg + save - residue, buflim); if (pending) prpending (buflim); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -