]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/grep/util.c
bsdgrep: Use implied working directory for -r if no directories are passed
[FreeBSD/FreeBSD.git] / usr.bin / grep / util.c
1 /*      $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $   */
2 /*      $FreeBSD$       */
3 /*      $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $  */
4
5 /*-
6  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
7  * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/stat.h>
36 #include <sys/types.h>
37
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fnmatch.h>
42 #include <fts.h>
43 #include <libgen.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <wchar.h>
50 #include <wctype.h>
51
52 #include "fastmatch.h"
53 #include "grep.h"
54
55 static int       linesqueued;
56 static int       procline(struct str *l, int);
57
58 static int       lasta;
59 static bool      ctxover;
60
61 bool
62 file_matching(const char *fname)
63 {
64         char *fname_base;
65         bool ret;
66
67         ret = finclude ? false : true;
68         fname_base = basename(fname);
69
70         for (unsigned int i = 0; i < fpatterns; ++i) {
71                 if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
72                     fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
73                         if (fpattern[i].mode == EXCL_PAT)
74                                 return (false);
75                         else
76                                 ret = true;
77                 }
78         }
79         return (ret);
80 }
81
82 static inline bool
83 dir_matching(const char *dname)
84 {
85         bool ret;
86
87         ret = dinclude ? false : true;
88
89         for (unsigned int i = 0; i < dpatterns; ++i) {
90                 if (dname != NULL &&
91                     fnmatch(dpattern[i].pat, dname, 0) == 0) {
92                         if (dpattern[i].mode == EXCL_PAT)
93                                 return (false);
94                         else
95                                 ret = true;
96                 }
97         }
98         return (ret);
99 }
100
101 /*
102  * Processes a directory when a recursive search is performed with
103  * the -R option.  Each appropriate file is passed to procfile().
104  */
105 int
106 grep_tree(char **argv)
107 {
108         FTS *fts;
109         FTSENT *p;
110         int c, fts_flags;
111         bool ok;
112         const char *wd[] = { ".", NULL };
113
114         c = fts_flags = 0;
115
116         switch(linkbehave) {
117         case LINK_EXPLICIT:
118                 fts_flags = FTS_COMFOLLOW;
119                 break;
120         case LINK_SKIP:
121                 fts_flags = FTS_PHYSICAL;
122                 break;
123         default:
124                 fts_flags = FTS_LOGICAL;
125                         
126         }
127
128         fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
129
130         fts = fts_open((argv[0] == NULL) ?
131             __DECONST(char * const *, wd) : argv, fts_flags, NULL);
132         if (fts == NULL)
133                 err(2, "fts_open");
134         while ((p = fts_read(fts)) != NULL) {
135                 switch (p->fts_info) {
136                 case FTS_DNR:
137                         /* FALLTHROUGH */
138                 case FTS_ERR:
139                         file_err = true;
140                         if(!sflag)
141                                 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
142                         break;
143                 case FTS_D:
144                         /* FALLTHROUGH */
145                 case FTS_DP:
146                         if (dexclude || dinclude)
147                                 if (!dir_matching(p->fts_name) ||
148                                     !dir_matching(p->fts_path))
149                                         fts_set(fts, p, FTS_SKIP);
150                         break;
151                 case FTS_DC:
152                         /* Print a warning for recursive directory loop */
153                         warnx("warning: %s: recursive directory loop",
154                                 p->fts_path);
155                         break;
156                 default:
157                         /* Check for file exclusion/inclusion */
158                         ok = true;
159                         if (fexclude || finclude)
160                                 ok &= file_matching(p->fts_path);
161
162                         if (ok)
163                                 c += procfile(p->fts_path);
164                         break;
165                 }
166         }
167
168         fts_close(fts);
169         return (c);
170 }
171
172 /*
173  * Opens a file and processes it.  Each file is processed line-by-line
174  * passing the lines to procline().
175  */
176 int
177 procfile(const char *fn)
178 {
179         struct file *f;
180         struct stat sb;
181         struct str ln;
182         mode_t s;
183         int c, t;
184
185         mcount = mlimit;
186
187         if (strcmp(fn, "-") == 0) {
188                 fn = label != NULL ? label : getstr(1);
189                 f = grep_open(NULL);
190         } else {
191                 if (!stat(fn, &sb)) {
192                         /* Check if we need to process the file */
193                         s = sb.st_mode & S_IFMT;
194                         if (s == S_IFDIR && dirbehave == DIR_SKIP)
195                                 return (0);
196                         if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
197                                 || s == S_IFSOCK) && devbehave == DEV_SKIP)
198                                         return (0);
199                 }
200                 f = grep_open(fn);
201         }
202         if (f == NULL) {
203                 file_err = true;
204                 if (!sflag)
205                         warn("%s", fn);
206                 return (0);
207         }
208
209         ln.file = grep_malloc(strlen(fn) + 1);
210         strcpy(ln.file, fn);
211         ln.line_no = 0;
212         ln.len = 0;
213         ctxover = false;
214         linesqueued = 0;
215         tail = 0;
216         lasta = 0;
217         ln.off = -1;
218
219         for (c = 0;  c == 0 || !(lflag || qflag); ) {
220                 ln.off += ln.len + 1;
221                 if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) {
222                         if (ln.line_no == 0 && matchall)
223                                 exit(0);
224                         else
225                                 break;
226                 }
227                 if (ln.len > 0 && ln.dat[ln.len - 1] == fileeol)
228                         --ln.len;
229                 ln.line_no++;
230
231                 /* Return if we need to skip a binary file */
232                 if (f->binary && binbehave == BINFILE_SKIP) {
233                         grep_close(f);
234                         free(ln.file);
235                         free(f);
236                         return (0);
237                 }
238
239                 /* Process the file line-by-line, enqueue non-matching lines */
240                 if ((t = procline(&ln, f->binary)) == 0 && Bflag > 0) {
241                         /* Except don't enqueue lines that appear in -A ctx */
242                         if (ln.line_no == 0 || lasta != ln.line_no) {
243                                 /* queue is maxed to Bflag number of lines */
244                                 enqueue(&ln);
245                                 linesqueued++;
246                                 ctxover = false;
247                         } else {
248                                 /*
249                                  * Indicate to procline() that we have ctx
250                                  * overlap and make sure queue is empty.
251                                  */
252                                 if (!ctxover)
253                                         clearqueue();
254                                 ctxover = true;
255                         }
256                 }
257                 c += t;
258                 if (mflag && mcount <= 0)
259                         break;
260         }
261         if (Bflag > 0)
262                 clearqueue();
263         grep_close(f);
264
265         if (cflag) {
266                 if (!hflag)
267                         printf("%s:", ln.file);
268                 printf("%u\n", c);
269         }
270         if (lflag && !qflag && c != 0)
271                 printf("%s%c", fn, nullflag ? 0 : '\n');
272         if (Lflag && !qflag && c == 0)
273                 printf("%s%c", fn, nullflag ? 0 : '\n');
274         if (c && !cflag && !lflag && !Lflag &&
275             binbehave == BINFILE_BIN && f->binary && !qflag)
276                 printf(getstr(8), fn);
277
278         free(ln.file);
279         free(f);
280         return (c);
281 }
282
283 #define iswword(x)      (iswalnum((x)) || (x) == L'_')
284
285 /*
286  * Processes a line comparing it with the specified patterns.  Each pattern
287  * is looped to be compared along with the full string, saving each and every
288  * match, which is necessary to colorize the output and to count the
289  * matches.  The matching lines are passed to printline() to display the
290  * appropriate output.
291  */
292 static int
293 procline(struct str *l, int nottext)
294 {
295         regmatch_t matches[MAX_LINE_MATCHES];
296         regmatch_t pmatch, lastmatch;
297         size_t st = 0, nst = 0;
298         unsigned int i;
299         int c = 0, m = 0, r = 0, lastmatches = 0, leflags = eflags;
300         int startm = 0;
301
302         /* Initialize to avoid a false positive warning from GCC. */
303         lastmatch.rm_so = lastmatch.rm_eo = 0;
304
305         /* Loop to process the whole line */
306         while (st <= l->len) {
307                 lastmatches = 0;
308                 startm = m;
309                 if (st > 0)
310                         leflags |= REG_NOTBOL;
311                 /* Loop to compare with all the patterns */
312                 for (i = 0; i < patterns; i++) {
313                         pmatch.rm_so = st;
314                         pmatch.rm_eo = l->len;
315                         if (fg_pattern[i].pattern)
316                                 r = fastexec(&fg_pattern[i],
317                                     l->dat, 1, &pmatch, leflags);
318                         else
319                                 r = regexec(&r_pattern[i], l->dat, 1,
320                                     &pmatch, leflags);
321                         r = (r == 0) ? 0 : REG_NOMATCH;
322                         if (r == REG_NOMATCH)
323                                 continue;
324                         /* Check for full match */
325                         if (r == 0 && xflag)
326                                 if (pmatch.rm_so != 0 ||
327                                     (size_t)pmatch.rm_eo != l->len)
328                                         r = REG_NOMATCH;
329                         /* Check for whole word match */
330                         if (r == 0 && (wflag || fg_pattern[i].word)) {
331                                 wchar_t wbegin, wend;
332
333                                 wbegin = wend = L' ';
334                                 if (pmatch.rm_so != 0 &&
335                                     sscanf(&l->dat[pmatch.rm_so - 1],
336                                     "%lc", &wbegin) != 1)
337                                         r = REG_NOMATCH;
338                                 else if ((size_t)pmatch.rm_eo !=
339                                     l->len &&
340                                     sscanf(&l->dat[pmatch.rm_eo],
341                                     "%lc", &wend) != 1)
342                                         r = REG_NOMATCH;
343                                 else if (iswword(wbegin) ||
344                                     iswword(wend))
345                                         r = REG_NOMATCH;
346                         }
347                         if (r == 0) {
348                                 lastmatches++;
349                                 lastmatch = pmatch;
350                                 if (m == 0)
351                                         c++;
352
353                                 if (m < MAX_LINE_MATCHES) {
354                                         /* Replace previous match if the new one is earlier and/or longer */
355                                         if (m > startm) {
356                                                 if (pmatch.rm_so < matches[m-1].rm_so ||
357                                                     (pmatch.rm_so == matches[m-1].rm_so && (pmatch.rm_eo - pmatch.rm_so) > (matches[m-1].rm_eo - matches[m-1].rm_so))) {
358                                                         matches[m-1] = pmatch;
359                                                         nst = pmatch.rm_eo;
360                                                 }
361                                         } else {
362                                                 /* Advance as normal if not */
363                                                 matches[m++] = pmatch;
364                                                 nst = pmatch.rm_eo;
365                                         }
366                                 }
367
368                                 /* matches - skip further patterns */
369                                 if ((color == NULL && !oflag) ||
370                                     qflag || lflag)
371                                         break;
372                         }
373                 }
374
375                 if (vflag) {
376                         c = !c;
377                         break;
378                 }
379
380                 /* One pass if we are not recording matches */
381                 if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag))
382                         break;
383
384                 /* If we didn't have any matches or REG_NOSUB set */
385                 if (lastmatches == 0 || (cflags & REG_NOSUB))
386                         nst = l->len;
387
388                 if (lastmatches == 0)
389                         /* No matches */
390                         break;
391                 else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo)
392                         /* Zero-length match -- advance one more so we don't get stuck */
393                         nst++;
394
395                 /* Advance st based on previous matches */
396                 st = nst;
397         }
398
399
400         /* Count the matches if we have a match limit */
401         if (mflag)
402                 mcount -= c;
403
404         if (c && binbehave == BINFILE_BIN && nottext)
405                 return (c); /* Binary file */
406
407         /* Dealing with the context */
408         if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
409                 if (c) {
410                         if (!first && !prev && !tail && (Bflag || Aflag) &&
411                             !ctxover)
412                                 printf("--\n");
413                         tail = Aflag;
414                         if (Bflag > 0) {
415                                 printqueue();
416                                 ctxover = false;
417                         }
418                         linesqueued = 0;
419                         printline(l, ':', matches, m);
420                 } else {
421                         /* Print -A lines following matches */
422                         lasta = l->line_no;
423                         printline(l, '-', matches, m);
424                         tail--;
425                 }
426         }
427
428         if (c) {
429                 prev = true;
430                 first = false;
431         } else
432                 prev = false;
433
434         return (c);
435 }
436
437 /*
438  * Safe malloc() for internal use.
439  */
440 void *
441 grep_malloc(size_t size)
442 {
443         void *ptr;
444
445         if ((ptr = malloc(size)) == NULL)
446                 err(2, "malloc");
447         return (ptr);
448 }
449
450 /*
451  * Safe calloc() for internal use.
452  */
453 void *
454 grep_calloc(size_t nmemb, size_t size)
455 {
456         void *ptr;
457
458         if ((ptr = calloc(nmemb, size)) == NULL)
459                 err(2, "calloc");
460         return (ptr);
461 }
462
463 /*
464  * Safe realloc() for internal use.
465  */
466 void *
467 grep_realloc(void *ptr, size_t size)
468 {
469
470         if ((ptr = realloc(ptr, size)) == NULL)
471                 err(2, "realloc");
472         return (ptr);
473 }
474
475 /*
476  * Safe strdup() for internal use.
477  */
478 char *
479 grep_strdup(const char *str)
480 {
481         char *ret;
482
483         if ((ret = strdup(str)) == NULL)
484                 err(2, "strdup");
485         return (ret);
486 }
487
488 /*
489  * Prints a matching line according to the command line options.
490  */
491 void
492 printline(struct str *line, int sep, regmatch_t *matches, int m)
493 {
494         size_t a = 0;
495         int i, n = 0;
496
497         /* If matchall, everything matches but don't actually print for -o */
498         if (oflag && matchall)
499                 return;
500
501         if (!hflag) {
502                 if (!nullflag) {
503                         fputs(line->file, stdout);
504                         ++n;
505                 } else {
506                         printf("%s", line->file);
507                         putchar(0);
508                 }
509         }
510         if (nflag) {
511                 if (n > 0)
512                         putchar(sep);
513                 printf("%d", line->line_no);
514                 ++n;
515         }
516         if (bflag) {
517                 if (n > 0)
518                         putchar(sep);
519                 printf("%lld", (long long)line->off);
520                 ++n;
521         }
522         if (n)
523                 putchar(sep);
524         /* --color and -o */
525         if ((oflag || color) && m > 0) {
526                 for (i = 0; i < m; i++) {
527                         /* Don't output zero length matches */
528                         if (matches[i].rm_so == matches[i].rm_eo)
529                                 continue;
530                         if (!oflag)
531                                 fwrite(line->dat + a, matches[i].rm_so - a, 1,
532                                     stdout);
533                         if (color) 
534                                 fprintf(stdout, "\33[%sm\33[K", color);
535
536                                 fwrite(line->dat + matches[i].rm_so, 
537                                     matches[i].rm_eo - matches[i].rm_so, 1,
538                                     stdout);
539                         if (color) 
540                                 fprintf(stdout, "\33[m\33[K");
541                         a = matches[i].rm_eo;
542                         if (oflag)
543                                 putchar('\n');
544                 }
545                 if (!oflag) {
546                         if (line->len - a > 0)
547                                 fwrite(line->dat + a, line->len - a, 1, stdout);
548                         putchar('\n');
549                 }
550         } else {
551                 fwrite(line->dat, line->len, 1, stdout);
552                 putchar(fileeol);
553         }
554 }