|  | /*	$NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $	*/ | 
|  | /*	$FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $	*/ | 
|  | /*	$OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $	*/ | 
|  |  | 
|  | /*- | 
|  | * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav | 
|  | * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | 
|  | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | 
|  | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #if HAVE_NBTOOL_CONFIG_H | 
|  | #include "nbtool_config.h" | 
|  | #endif | 
|  |  | 
|  | #include <sys/cdefs.h> | 
|  | __RCSID("$NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $"); | 
|  |  | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <err.h> | 
|  | #include <errno.h> | 
|  | #include <fnmatch.h> | 
|  | #include <fts.h> | 
|  | #include <libgen.h> | 
|  | #include <stdbool.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <wchar.h> | 
|  | #include <wctype.h> | 
|  |  | 
|  | #include "grep.h" | 
|  |  | 
|  | static bool	 first, first_global = true; | 
|  | static unsigned long long since_printed; | 
|  |  | 
|  | static int	 procline(struct str *l, int); | 
|  |  | 
|  | bool | 
|  | file_matching(const char *fname) | 
|  | { | 
|  | char *fname_base, *fname_copy; | 
|  | unsigned int i; | 
|  | bool ret; | 
|  |  | 
|  | ret = finclude ? false : true; | 
|  | fname_copy = grep_strdup(fname); | 
|  | fname_base = basename(fname_copy); | 
|  |  | 
|  | for (i = 0; i < fpatterns; ++i) { | 
|  | if (fnmatch(fpattern[i].pat, fname, 0) == 0 || | 
|  | fnmatch(fpattern[i].pat, fname_base, 0) == 0) { | 
|  | if (fpattern[i].mode == EXCL_PAT) | 
|  | return (false); | 
|  | else | 
|  | ret = true; | 
|  | } | 
|  | } | 
|  | free(fname_copy); | 
|  | return (ret); | 
|  | } | 
|  |  | 
|  | static inline bool | 
|  | dir_matching(const char *dname) | 
|  | { | 
|  | unsigned int i; | 
|  | bool ret; | 
|  |  | 
|  | ret = dinclude ? false : true; | 
|  |  | 
|  | for (i = 0; i < dpatterns; ++i) { | 
|  | if (dname != NULL && | 
|  | fnmatch(dname, dpattern[i].pat, 0) == 0) { | 
|  | if (dpattern[i].mode == EXCL_PAT) | 
|  | return (false); | 
|  | else | 
|  | ret = true; | 
|  | } | 
|  | } | 
|  | return (ret); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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; | 
|  | char *d, *dir = NULL; | 
|  | 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: | 
|  | errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno)); | 
|  | break; | 
|  | case FTS_D: | 
|  | /* FALLTHROUGH */ | 
|  | case FTS_DP: | 
|  | 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 (dexclude || dinclude) { | 
|  | if ((d = strrchr(p->fts_path, '/')) != NULL) { | 
|  | dir = grep_malloc(sizeof(char) * | 
|  | (d - p->fts_path + 1)); | 
|  | memcpy(dir, p->fts_path, | 
|  | d - p->fts_path); | 
|  | dir[d - p->fts_path] = '\0'; | 
|  | } | 
|  | ok = dir_matching(dir); | 
|  | free(dir); | 
|  | dir = NULL; | 
|  | } | 
|  | if (fexclude || finclude) | 
|  | ok &= file_matching(p->fts_path); | 
|  |  | 
|  | if (ok) | 
|  | c += procfile(p->fts_path); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | fts_close(fts); | 
|  | return (c); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Opens a file and processes it.  Each file is processed line-by-line | 
|  | * passing the lines to procline(). | 
|  | */ | 
|  | int | 
|  | procfile(const char *fn) | 
|  | { | 
|  | struct file *f; | 
|  | struct stat sb; | 
|  | struct str ln; | 
|  | mode_t s; | 
|  | int c, t; | 
|  |  | 
|  | if (mflag && (mcount <= 0)) | 
|  | return (0); | 
|  |  | 
|  | if (strcmp(fn, "-") == 0) { | 
|  | fn = label != NULL ? label : getstr(1); | 
|  | f = grep_open(NULL); | 
|  | } else { | 
|  | if (!stat(fn, &sb)) { | 
|  | /* Check if we need to process the file */ | 
|  | s = sb.st_mode & S_IFMT; | 
|  | if (s == S_IFDIR && dirbehave == DIR_SKIP) | 
|  | return (0); | 
|  | if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK | 
|  | || s == S_IFSOCK) && devbehave == DEV_SKIP) | 
|  | return (0); | 
|  | } | 
|  | f = grep_open(fn); | 
|  | } | 
|  | if (f == NULL) { | 
|  | if (!sflag) | 
|  | warn("%s", fn); | 
|  | if (errno == ENOENT) | 
|  | notfound = true; | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | ln.file = grep_malloc(strlen(fn) + 1); | 
|  | strcpy(ln.file, fn); | 
|  | ln.line_no = 0; | 
|  | ln.len = 0; | 
|  | tail = 0; | 
|  | ln.off = -1; | 
|  |  | 
|  | for (first = true, c = 0;  c == 0 || !(lflag || qflag); ) { | 
|  | ln.off += ln.len + 1; | 
|  | if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) | 
|  | break; | 
|  | if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep) | 
|  | --ln.len; | 
|  | ln.line_no++; | 
|  |  | 
|  | /* Return if we need to skip a binary file */ | 
|  | if (f->binary && binbehave == BINFILE_SKIP) { | 
|  | grep_close(f); | 
|  | free(ln.file); | 
|  | free(f); | 
|  | return (0); | 
|  | } | 
|  | /* Process the file line-by-line */ | 
|  | t = procline(&ln, f->binary); | 
|  | c += t; | 
|  |  | 
|  | /* Count the matches if we have a match limit */ | 
|  | if (mflag) { | 
|  | mcount -= t; | 
|  | if (mcount <= 0) | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (Bflag > 0) | 
|  | clearqueue(); | 
|  | grep_close(f); | 
|  |  | 
|  | if (cflag) { | 
|  | if (!hflag) | 
|  | printf("%s:", ln.file); | 
|  | printf("%u%c", c, line_sep); | 
|  | } | 
|  | if (lflag && !qflag && c != 0) | 
|  | printf("%s%c", fn, line_sep); | 
|  | if (Lflag && !qflag && c == 0) | 
|  | printf("%s%c", fn, line_sep); | 
|  | if (c && !cflag && !lflag && !Lflag && | 
|  | binbehave == BINFILE_BIN && f->binary && !qflag) | 
|  | printf(getstr(8), fn); | 
|  |  | 
|  | free(ln.file); | 
|  | free(f); | 
|  | return (c); | 
|  | } | 
|  |  | 
|  | #define iswword(x)	(iswalnum((wint_t)(x)) || (x) == L'_') | 
|  |  | 
|  | /* | 
|  | * Processes a line comparing it with the specified patterns.  Each pattern | 
|  | * is looped to be compared along with the full string, saving each and every | 
|  | * match, which is necessary to colorize the output and to count the | 
|  | * matches.  The matching lines are passed to printline() to display the | 
|  | * appropriate output. | 
|  | */ | 
|  | static int | 
|  | procline(struct str *l, int nottext) | 
|  | { | 
|  | regmatch_t matches[MAX_LINE_MATCHES]; | 
|  | regmatch_t pmatch; | 
|  | size_t st = 0; | 
|  | unsigned int i; | 
|  | int c = 0, m = 0, r = 0; | 
|  |  | 
|  | /* Loop to process the whole line */ | 
|  | while (st <= l->len) { | 
|  | pmatch.rm_so = st; | 
|  | pmatch.rm_eo = l->len; | 
|  |  | 
|  | /* Loop to compare with all the patterns */ | 
|  | for (i = 0; i < patterns; i++) { | 
|  | /* | 
|  | * XXX: grep_search() is a workaround for speed up and should be | 
|  | * removed in the future.  See fastgrep.c. | 
|  | */ | 
|  | if (fg_pattern[i].pattern) { | 
|  | r = grep_search(&fg_pattern[i], | 
|  | (unsigned char *)l->dat, | 
|  | l->len, &pmatch); | 
|  | r = (r == 0) ? 0 : REG_NOMATCH; | 
|  | st = pmatch.rm_eo; | 
|  | } else { | 
|  | r = regexec(&r_pattern[i], l->dat, 1, | 
|  | &pmatch, eflags); | 
|  | r = (r == 0) ? 0 : REG_NOMATCH; | 
|  | st = pmatch.rm_eo; | 
|  | } | 
|  | if (r == REG_NOMATCH) | 
|  | continue; | 
|  | /* Check for full match */ | 
|  | if (xflag && | 
|  | (pmatch.rm_so != 0 || | 
|  | (size_t)pmatch.rm_eo != l->len)) | 
|  | continue; | 
|  | /* Check for whole word match */ | 
|  | if (fg_pattern[i].word && pmatch.rm_so != 0) { | 
|  | wchar_t wbegin, wend; | 
|  |  | 
|  | wbegin = wend = L' '; | 
|  | if (pmatch.rm_so != 0 && | 
|  | sscanf(&l->dat[pmatch.rm_so - 1], | 
|  | "%lc", &wbegin) != 1) | 
|  | continue; | 
|  | if ((size_t)pmatch.rm_eo != l->len && | 
|  | sscanf(&l->dat[pmatch.rm_eo], | 
|  | "%lc", &wend) != 1) | 
|  | continue; | 
|  | if (iswword(wbegin) || iswword(wend)) | 
|  | continue; | 
|  | } | 
|  | c = 1; | 
|  | if (m < MAX_LINE_MATCHES) | 
|  | matches[m++] = pmatch; | 
|  | /* matches - skip further patterns */ | 
|  | if ((color != NULL && !oflag) || qflag || lflag) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (vflag) { | 
|  | c = !c; | 
|  | break; | 
|  | } | 
|  | /* One pass if we are not recording matches */ | 
|  | if ((color != NULL && !oflag) || qflag || lflag) | 
|  | break; | 
|  |  | 
|  | if (st == (size_t)pmatch.rm_so) | 
|  | break; 	/* No matches */ | 
|  | } | 
|  |  | 
|  | if (c && binbehave == BINFILE_BIN && nottext) | 
|  | return (c); /* Binary file */ | 
|  |  | 
|  | /* Dealing with the context */ | 
|  | if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) { | 
|  | if (c) { | 
|  | if ((Aflag || Bflag) && !first_global && | 
|  | (first || since_printed > Bflag)) | 
|  | printf("--\n"); | 
|  | tail = Aflag; | 
|  | if (Bflag > 0) | 
|  | printqueue(); | 
|  | printline(l, ':', matches, m); | 
|  | } else { | 
|  | printline(l, '-', matches, m); | 
|  | tail--; | 
|  | } | 
|  | first = false; | 
|  | first_global = false; | 
|  | since_printed = 0; | 
|  | } else { | 
|  | if (Bflag) | 
|  | enqueue(l); | 
|  | since_printed++; | 
|  | } | 
|  | return (c); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Safe malloc() for internal use. | 
|  | */ | 
|  | void * | 
|  | grep_malloc(size_t size) | 
|  | { | 
|  | void *ptr; | 
|  |  | 
|  | if ((ptr = malloc(size)) == NULL) | 
|  | err(2, "malloc"); | 
|  | return (ptr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Safe calloc() for internal use. | 
|  | */ | 
|  | void * | 
|  | grep_calloc(size_t nmemb, size_t size) | 
|  | { | 
|  | void *ptr; | 
|  |  | 
|  | if ((ptr = calloc(nmemb, size)) == NULL) | 
|  | err(2, "calloc"); | 
|  | return (ptr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Safe realloc() for internal use. | 
|  | */ | 
|  | void * | 
|  | grep_realloc(void *ptr, size_t size) | 
|  | { | 
|  |  | 
|  | if ((ptr = realloc(ptr, size)) == NULL) | 
|  | err(2, "realloc"); | 
|  | return (ptr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Safe strdup() for internal use. | 
|  | */ | 
|  | char * | 
|  | grep_strdup(const char *str) | 
|  | { | 
|  | char *ret; | 
|  |  | 
|  | if ((ret = strdup(str)) == NULL) | 
|  | err(2, "strdup"); | 
|  | return (ret); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Prints a matching line according to the command line options. | 
|  | */ | 
|  | void | 
|  | printline(struct str *line, int sep, regmatch_t *matches, int m) | 
|  | { | 
|  | size_t a = 0; | 
|  | int i, n = 0; | 
|  |  | 
|  | if (!hflag) { | 
|  | if (nullflag == 0) | 
|  | fputs(line->file, stdout); | 
|  | else { | 
|  | printf("%s", line->file); | 
|  | putchar(0); | 
|  | } | 
|  | ++n; | 
|  | } | 
|  | if (nflag) { | 
|  | if (n > 0) | 
|  | putchar(sep); | 
|  | printf("%d", line->line_no); | 
|  | ++n; | 
|  | } | 
|  | if (bflag) { | 
|  | if (n > 0) | 
|  | putchar(sep); | 
|  | printf("%lld", (long long)line->off); | 
|  | ++n; | 
|  | } | 
|  | if (n) | 
|  | putchar(sep); | 
|  | /* --color and -o */ | 
|  | if ((oflag || color) && m > 0) { | 
|  | for (i = 0; i < m; i++) { | 
|  | if (!oflag) | 
|  | fwrite(line->dat + a, matches[i].rm_so - a, 1, | 
|  | stdout); | 
|  | if (color) | 
|  | fprintf(stdout, "\33[%sm\33[K", color); | 
|  |  | 
|  | fwrite(line->dat + matches[i].rm_so, | 
|  | matches[i].rm_eo - matches[i].rm_so, 1, | 
|  | stdout); | 
|  | if (color) | 
|  | fprintf(stdout, "\33[m\33[K"); | 
|  | a = matches[i].rm_eo; | 
|  | if (oflag) | 
|  | putchar('\n'); | 
|  | } | 
|  | if (!oflag) { | 
|  | if (line->len - a > 0) | 
|  | fwrite(line->dat + a, line->len - a, 1, stdout); | 
|  | putchar(line_sep); | 
|  | } | 
|  | } else { | 
|  | fwrite(line->dat, line->len, 1, stdout); | 
|  | putchar(line_sep); | 
|  | } | 
|  | } |