]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/grep/util.c
bsdgrep: Use grep_strdup instead of grep_malloc+strcpy
[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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7  *
8  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
9  * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
10  * Copyright (C) 2017 Kyle Evans <kevans@FreeBSD.org>
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fnmatch.h>
45 #include <fts.h>
46 #include <libgen.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <wchar.h>
53 #include <wctype.h>
54
55 #ifndef WITHOUT_FASTMATCH
56 #include "fastmatch.h"
57 #endif
58 #include "grep.h"
59
60 static bool      first_match = true;
61
62 /*
63  * Parsing context; used to hold things like matches made and
64  * other useful bits
65  */
66 struct parsec {
67         regmatch_t      matches[MAX_MATCHES];           /* Matches made */
68         /* XXX TODO: This should be a chunk, not a line */
69         struct str      ln;                             /* Current line */
70         size_t          lnstart;                        /* Position in line */
71         size_t          matchidx;                       /* Latest match index */
72         int             printed;                        /* Metadata printed? */
73         bool            binary;                         /* Binary file? */
74 };
75
76 /*
77  * Match printing context
78  */
79 struct mprintc {
80         long long       tail;           /* Number of trailing lines to record */
81         int             last_outed;     /* Number of lines since last output */
82         bool            doctx;          /* Printing context? */
83         bool            printmatch;     /* Printing matches? */
84         bool            same_file;      /* Same file as previously printed? */
85 };
86
87 static void procmatch_match(struct mprintc *mc, struct parsec *pc);
88 static void procmatch_nomatch(struct mprintc *mc, struct parsec *pc);
89 static bool procmatches(struct mprintc *mc, struct parsec *pc, bool matched);
90 #ifdef WITH_INTERNAL_NOSPEC
91 static int litexec(const struct pat *pat, const char *string,
92     size_t nmatch, regmatch_t pmatch[]);
93 #endif
94 static int procline(struct parsec *pc);
95 static void printline(struct parsec *pc, int sep);
96 static void printline_metadata(struct str *line, int sep);
97
98 bool
99 file_matching(const char *fname)
100 {
101         char *fname_base, *fname_buf;
102         bool ret;
103
104         ret = finclude ? false : true;
105         fname_buf = strdup(fname);
106         if (fname_buf == NULL)
107                 err(2, "strdup");
108         fname_base = basename(fname_buf);
109
110         for (unsigned int i = 0; i < fpatterns; ++i) {
111                 if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
112                     fnmatch(fpattern[i].pat, fname_base, 0) == 0)
113                         /*
114                          * The last pattern matched wins exclusion/inclusion
115                          * rights, so we can't reasonably bail out early here.
116                          */
117                         ret = (fpattern[i].mode != EXCL_PAT);
118         }
119         free(fname_buf);
120         return (ret);
121 }
122
123 static inline bool
124 dir_matching(const char *dname)
125 {
126         bool ret;
127
128         ret = dinclude ? false : true;
129
130         for (unsigned int i = 0; i < dpatterns; ++i) {
131                 if (dname != NULL && fnmatch(dpattern[i].pat, dname, 0) == 0)
132                         /*
133                          * The last pattern matched wins exclusion/inclusion
134                          * rights, so we can't reasonably bail out early here.
135                          */
136                         ret = (dpattern[i].mode != EXCL_PAT);
137         }
138         return (ret);
139 }
140
141 /*
142  * Processes a directory when a recursive search is performed with
143  * the -R option.  Each appropriate file is passed to procfile().
144  */
145 int
146 grep_tree(char **argv)
147 {
148         FTS *fts;
149         FTSENT *p;
150         int c, fts_flags;
151         bool ok;
152         const char *wd[] = { ".", NULL };
153
154         c = fts_flags = 0;
155
156         switch(linkbehave) {
157         case LINK_EXPLICIT:
158                 fts_flags = FTS_COMFOLLOW;
159                 break;
160         case LINK_SKIP:
161                 fts_flags = FTS_PHYSICAL;
162                 break;
163         default:
164                 fts_flags = FTS_LOGICAL;
165         }
166
167         fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
168
169         fts = fts_open((argv[0] == NULL) ?
170             __DECONST(char * const *, wd) : argv, fts_flags, NULL);
171         if (fts == NULL)
172                 err(2, "fts_open");
173         while ((p = fts_read(fts)) != NULL) {
174                 switch (p->fts_info) {
175                 case FTS_DNR:
176                         /* FALLTHROUGH */
177                 case FTS_ERR:
178                         file_err = true;
179                         if(!sflag)
180                                 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
181                         break;
182                 case FTS_D:
183                         /* FALLTHROUGH */
184                 case FTS_DP:
185                         if (dexclude || dinclude)
186                                 if (!dir_matching(p->fts_name) ||
187                                     !dir_matching(p->fts_path))
188                                         fts_set(fts, p, FTS_SKIP);
189                         break;
190                 case FTS_DC:
191                         /* Print a warning for recursive directory loop */
192                         warnx("warning: %s: recursive directory loop",
193                             p->fts_path);
194                         break;
195                 default:
196                         /* Check for file exclusion/inclusion */
197                         ok = true;
198                         if (fexclude || finclude)
199                                 ok &= file_matching(p->fts_path);
200
201                         if (ok)
202                                 c += procfile(p->fts_path);
203                         break;
204                 }
205         }
206
207         fts_close(fts);
208         return (c);
209 }
210
211 static void
212 procmatch_match(struct mprintc *mc, struct parsec *pc)
213 {
214
215         if (mc->doctx) {
216                 if (!first_match && (!mc->same_file || mc->last_outed > 0))
217                         printf("--\n");
218                 if (Bflag > 0)
219                         printqueue();
220                 mc->tail = Aflag;
221         }
222
223         /* Print the matching line, but only if not quiet/binary */
224         if (mc->printmatch) {
225                 printline(pc, ':');
226                 while (pc->matchidx >= MAX_MATCHES) {
227                         /* Reset matchidx and try again */
228                         pc->matchidx = 0;
229                         if (procline(pc) == 0)
230                                 printline(pc, ':');
231                         else
232                                 break;
233                 }
234                 first_match = false;
235                 mc->same_file = true;
236                 mc->last_outed = 0;
237         }
238 }
239
240 static void
241 procmatch_nomatch(struct mprintc *mc, struct parsec *pc)
242 {
243
244         /* Deal with any -A context as needed */
245         if (mc->tail > 0) {
246                 grep_printline(&pc->ln, '-');
247                 mc->tail--;
248                 if (Bflag > 0)
249                         clearqueue();
250         } else if (Bflag == 0 || (Bflag > 0 && enqueue(&pc->ln)))
251                 /*
252                  * Enqueue non-matching lines for -B context. If we're not
253                  * actually doing -B context or if the enqueue resulted in a
254                  * line being rotated out, then go ahead and increment
255                  * last_outed to signify a gap between context/match.
256                  */
257                 ++mc->last_outed;
258 }
259
260 /*
261  * Process any matches in the current parsing context, return a boolean
262  * indicating whether we should halt any further processing or not. 'true' to
263  * continue processing, 'false' to halt.
264  */
265 static bool
266 procmatches(struct mprintc *mc, struct parsec *pc, bool matched)
267 {
268
269         /*
270          * XXX TODO: This should loop over pc->matches and handle things on a
271          * line-by-line basis, setting up a `struct str` as needed.
272          */
273         /* Deal with any -B context or context separators */
274         if (matched) {
275                 procmatch_match(mc, pc);
276
277                 /* Count the matches if we have a match limit */
278                 if (mflag) {
279                         /* XXX TODO: Decrement by number of matched lines */
280                         mcount -= 1;
281                         if (mflag && mcount <= 0)
282                                 return (false);
283                 }
284         } else if (mc->doctx)
285                 procmatch_nomatch(mc, pc);
286
287         return (true);
288 }
289
290 /*
291  * Opens a file and processes it.  Each file is processed line-by-line
292  * passing the lines to procline().
293  */
294 int
295 procfile(const char *fn)
296 {
297         struct parsec pc;
298         struct mprintc mc;
299         struct file *f;
300         struct stat sb;
301         mode_t s;
302         int c, t;
303
304         if (strcmp(fn, "-") == 0) {
305                 fn = label != NULL ? label : getstr(1);
306                 f = grep_open(NULL);
307         } else {
308                 if (stat(fn, &sb) == 0) {
309                         /* Check if we need to process the file */
310                         s = sb.st_mode & S_IFMT;
311                         if (dirbehave == DIR_SKIP && s == S_IFDIR)
312                                 return (0);
313                         if (devbehave == DEV_SKIP && (s == S_IFIFO ||
314                             s == S_IFCHR || s == S_IFBLK || s == S_IFSOCK))
315                                 return (0);
316                 }
317                 f = grep_open(fn);
318         }
319         if (f == NULL) {
320                 file_err = true;
321                 if (!sflag)
322                         warn("%s", fn);
323                 return (0);
324         }
325
326         pc.ln.file = grep_strdup(fn);
327         pc.ln.line_no = 0;
328         pc.ln.len = 0;
329         pc.ln.boff = 0;
330         pc.ln.off = -1;
331         pc.binary = f->binary;
332         memset(&mc, 0, sizeof(mc));
333         mc.printmatch = true;
334         if ((pc.binary && binbehave == BINFILE_BIN) || cflag || qflag ||
335             lflag || Lflag)
336                 mc.printmatch = false;
337         if (mc.printmatch && (Aflag != 0 || Bflag != 0))
338                 mc.doctx = true;
339         mcount = mlimit;
340
341         for (c = 0;  c == 0 || !(lflag || qflag); ) {
342                 /*
343                  * XXX TODO: We need to revisit this in a chunking world. We're
344                  * not going to be doing per-line statistics because of the
345                  * overhead involved. procmatches can figure that stuff out as
346                  * needed. */
347                 /* Reset per-line statistics */
348                 pc.printed = 0;
349                 pc.matchidx = 0;
350                 pc.lnstart = 0;
351                 pc.ln.boff = 0;
352                 pc.ln.off += pc.ln.len + 1;
353                 /* XXX TODO: Grab a chunk */
354                 if ((pc.ln.dat = grep_fgetln(f, &pc.ln.len)) == NULL ||
355                     pc.ln.len == 0)
356                         break;
357
358                 if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol)
359                         --pc.ln.len;
360                 pc.ln.line_no++;
361
362                 /* Return if we need to skip a binary file */
363                 if (pc.binary && binbehave == BINFILE_SKIP) {
364                         grep_close(f);
365                         free(pc.ln.file);
366                         free(f);
367                         return (0);
368                 }
369
370                 if ((t = procline(&pc)) == 0)
371                         ++c;
372
373                 /* Halt processing if we hit our match limit */
374                 if (!procmatches(&mc, &pc, t == 0))
375                         break;
376         }
377         if (Bflag > 0)
378                 clearqueue();
379         grep_close(f);
380
381         if (cflag) {
382                 if (!hflag)
383                         printf("%s:", pc.ln.file);
384                 printf("%u\n", c);
385         }
386         if (lflag && !qflag && c != 0)
387                 printf("%s%c", fn, nullflag ? 0 : '\n');
388         if (Lflag && !qflag && c == 0)
389                 printf("%s%c", fn, nullflag ? 0 : '\n');
390         if (c && !cflag && !lflag && !Lflag &&
391             binbehave == BINFILE_BIN && f->binary && !qflag)
392                 printf(getstr(8), fn);
393
394         free(pc.ln.file);
395         free(f);
396         return (c);
397 }
398
399 #ifdef WITH_INTERNAL_NOSPEC
400 /*
401  * Internal implementation of literal string search within a string, modeled
402  * after regexec(3), for use when the regex(3) implementation doesn't offer
403  * either REG_NOSPEC or REG_LITERAL. This does not apply in the default FreeBSD
404  * config, but in other scenarios such as building against libgnuregex or on
405  * some non-FreeBSD OSes.
406  */
407 static int
408 litexec(const struct pat *pat, const char *string, size_t nmatch,
409     regmatch_t pmatch[])
410 {
411         char *(*strstr_fn)(const char *, const char *);
412         char *sub, *subject;
413         const char *search;
414         size_t idx, n, ofs, stringlen;
415
416         if (cflags & REG_ICASE)
417                 strstr_fn = strcasestr;
418         else
419                 strstr_fn = strstr;
420         idx = 0;
421         ofs = pmatch[0].rm_so;
422         stringlen = pmatch[0].rm_eo;
423         if (ofs >= stringlen)
424                 return (REG_NOMATCH);
425         subject = strndup(string, stringlen);
426         if (subject == NULL)
427                 return (REG_ESPACE);
428         for (n = 0; ofs < stringlen;) {
429                 search = (subject + ofs);
430                 if ((unsigned long)pat->len > strlen(search))
431                         break;
432                 sub = strstr_fn(search, pat->pat);
433                 /*
434                  * Ignoring the empty string possibility due to context: grep optimizes
435                  * for empty patterns and will never reach this point.
436                  */
437                 if (sub == NULL)
438                         break;
439                 ++n;
440                 /* Fill in pmatch if necessary */
441                 if (nmatch > 0) {
442                         pmatch[idx].rm_so = ofs + (sub - search);
443                         pmatch[idx].rm_eo = pmatch[idx].rm_so + pat->len;
444                         if (++idx == nmatch)
445                                 break;
446                         ofs = pmatch[idx].rm_so + 1;
447                 } else
448                         /* We only needed to know if we match or not */
449                         break;
450         }
451         free(subject);
452         if (n > 0 && nmatch > 0)
453                 for (n = idx; n < nmatch; ++n)
454                         pmatch[n].rm_so = pmatch[n].rm_eo = -1;
455
456         return (n > 0 ? 0 : REG_NOMATCH);
457 }
458 #endif /* WITH_INTERNAL_NOSPEC */
459
460 #define iswword(x)      (iswalnum((x)) || (x) == L'_')
461
462 /*
463  * Processes a line comparing it with the specified patterns.  Each pattern
464  * is looped to be compared along with the full string, saving each and every
465  * match, which is necessary to colorize the output and to count the
466  * matches.  The matching lines are passed to printline() to display the
467  * appropriate output.
468  */
469 static int
470 procline(struct parsec *pc)
471 {
472         regmatch_t pmatch, lastmatch, chkmatch;
473         wchar_t wbegin, wend;
474         size_t st, nst;
475         unsigned int i;
476         int c = 0, r = 0, lastmatches = 0, leflags = eflags;
477         size_t startm = 0, matchidx;
478         unsigned int retry;
479
480         matchidx = pc->matchidx;
481
482         /* Special case: empty pattern with -w flag, check first character */
483         if (matchall && wflag) {
484                 if (pc->ln.len == 0)
485                         return (0);
486                 wend = L' ';
487                 if (sscanf(&pc->ln.dat[0], "%lc", &wend) != 1 || iswword(wend))
488                         return (1);
489                 else
490                         return (0);
491         } else if (matchall)
492                 return (0);
493
494         st = pc->lnstart;
495         nst = 0;
496         /* Initialize to avoid a false positive warning from GCC. */
497         lastmatch.rm_so = lastmatch.rm_eo = 0;
498
499         /* Loop to process the whole line */
500         while (st <= pc->ln.len) {
501                 lastmatches = 0;
502                 startm = matchidx;
503                 retry = 0;
504                 if (st > 0 && pc->ln.dat[st - 1] != fileeol)
505                         leflags |= REG_NOTBOL;
506                 /* Loop to compare with all the patterns */
507                 for (i = 0; i < patterns; i++) {
508                         pmatch.rm_so = st;
509                         pmatch.rm_eo = pc->ln.len;
510 #ifdef WITH_INTERNAL_NOSPEC
511                         if (grepbehave == GREP_FIXED)
512                                 r = litexec(&pattern[i], pc->ln.dat, 1, &pmatch);
513                         else
514 #endif
515 #ifndef WITHOUT_FASTMATCH
516                         if (fg_pattern[i].pattern)
517                                 r = fastexec(&fg_pattern[i],
518                                     pc->ln.dat, 1, &pmatch, leflags);
519                         else
520 #endif
521                                 r = regexec(&r_pattern[i], pc->ln.dat, 1,
522                                     &pmatch, leflags);
523                         if (r != 0)
524                                 continue;
525                         /* Check for full match */
526                         if (xflag && (pmatch.rm_so != 0 ||
527                             (size_t)pmatch.rm_eo != pc->ln.len))
528                                 continue;
529                         /* Check for whole word match */
530 #ifndef WITHOUT_FASTMATCH
531                         if (wflag || fg_pattern[i].word) {
532 #else
533                         if (wflag) {
534 #endif
535                                 wbegin = wend = L' ';
536                                 if (pmatch.rm_so != 0 &&
537                                     sscanf(&pc->ln.dat[pmatch.rm_so - 1],
538                                     "%lc", &wbegin) != 1)
539                                         r = REG_NOMATCH;
540                                 else if ((size_t)pmatch.rm_eo !=
541                                     pc->ln.len &&
542                                     sscanf(&pc->ln.dat[pmatch.rm_eo],
543                                     "%lc", &wend) != 1)
544                                         r = REG_NOMATCH;
545                                 else if (iswword(wbegin) ||
546                                     iswword(wend))
547                                         r = REG_NOMATCH;
548                                 /*
549                                  * If we're doing whole word matching and we
550                                  * matched once, then we should try the pattern
551                                  * again after advancing just past the start of
552                                  * the earliest match. This allows the pattern
553                                  * to  match later on in the line and possibly
554                                  * still match a whole word.
555                                  */
556                                 if (r == REG_NOMATCH &&
557                                     (retry == pc->lnstart ||
558                                     (unsigned int)pmatch.rm_so + 1 < retry))
559                                         retry = pmatch.rm_so + 1;
560                                 if (r == REG_NOMATCH)
561                                         continue;
562                         }
563                         lastmatches++;
564                         lastmatch = pmatch;
565
566                         if (matchidx == 0)
567                                 c++;
568
569                         /*
570                          * Replace previous match if the new one is earlier
571                          * and/or longer. This will lead to some amount of
572                          * extra work if -o/--color are specified, but it's
573                          * worth it from a correctness point of view.
574                          */
575                         if (matchidx > startm) {
576                                 chkmatch = pc->matches[matchidx - 1];
577                                 if (pmatch.rm_so < chkmatch.rm_so ||
578                                     (pmatch.rm_so == chkmatch.rm_so &&
579                                     (pmatch.rm_eo - pmatch.rm_so) >
580                                     (chkmatch.rm_eo - chkmatch.rm_so))) {
581                                         pc->matches[matchidx - 1] = pmatch;
582                                         nst = pmatch.rm_eo;
583                                 }
584                         } else {
585                                 /* Advance as normal if not */
586                                 pc->matches[matchidx++] = pmatch;
587                                 nst = pmatch.rm_eo;
588                         }
589                         /* avoid excessive matching - skip further patterns */
590                         if ((color == NULL && !oflag) || qflag || lflag ||
591                             matchidx >= MAX_MATCHES) {
592                                 pc->lnstart = nst;
593                                 lastmatches = 0;
594                                 break;
595                         }
596                 }
597
598                 /*
599                  * Advance to just past the start of the earliest match, try
600                  * again just in case we still have a chance to match later in
601                  * the string.
602                  */
603                 if (lastmatches == 0 && retry > pc->lnstart) {
604                         st = retry;
605                         continue;
606                 }
607
608                 /* XXX TODO: We will need to keep going, since we're chunky */
609                 /* One pass if we are not recording matches */
610                 if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag))
611                         break;
612
613                 /* If we didn't have any matches or REG_NOSUB set */
614                 if (lastmatches == 0 || (cflags & REG_NOSUB))
615                         nst = pc->ln.len;
616
617                 if (lastmatches == 0)
618                         /* No matches */
619                         break;
620                 else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo)
621                         /* Zero-length match -- advance one more so we don't get stuck */
622                         nst++;
623
624                 /* Advance st based on previous matches */
625                 st = nst;
626                 pc->lnstart = st;
627         }
628
629         /* Reflect the new matchidx in the context */
630         pc->matchidx = matchidx;
631         if (vflag)
632                 c = !c;
633         return (c ? 0 : 1);
634 }
635
636 /*
637  * Safe malloc() for internal use.
638  */
639 void *
640 grep_malloc(size_t size)
641 {
642         void *ptr;
643
644         if ((ptr = malloc(size)) == NULL)
645                 err(2, "malloc");
646         return (ptr);
647 }
648
649 /*
650  * Safe calloc() for internal use.
651  */
652 void *
653 grep_calloc(size_t nmemb, size_t size)
654 {
655         void *ptr;
656
657         if ((ptr = calloc(nmemb, size)) == NULL)
658                 err(2, "calloc");
659         return (ptr);
660 }
661
662 /*
663  * Safe realloc() for internal use.
664  */
665 void *
666 grep_realloc(void *ptr, size_t size)
667 {
668
669         if ((ptr = realloc(ptr, size)) == NULL)
670                 err(2, "realloc");
671         return (ptr);
672 }
673
674 /*
675  * Safe strdup() for internal use.
676  */
677 char *
678 grep_strdup(const char *str)
679 {
680         char *ret;
681
682         if ((ret = strdup(str)) == NULL)
683                 err(2, "strdup");
684         return (ret);
685 }
686
687 /*
688  * Print an entire line as-is, there are no inline matches to consider. This is
689  * used for printing context.
690  */
691 void grep_printline(struct str *line, int sep) {
692         printline_metadata(line, sep);
693         fwrite(line->dat, line->len, 1, stdout);
694         putchar(fileeol);
695 }
696
697 static void
698 printline_metadata(struct str *line, int sep)
699 {
700         bool printsep;
701
702         printsep = false;
703         if (!hflag) {
704                 if (!nullflag) {
705                         fputs(line->file, stdout);
706                         printsep = true;
707                 } else {
708                         printf("%s", line->file);
709                         putchar(0);
710                 }
711         }
712         if (nflag) {
713                 if (printsep)
714                         putchar(sep);
715                 printf("%d", line->line_no);
716                 printsep = true;
717         }
718         if (bflag) {
719                 if (printsep)
720                         putchar(sep);
721                 printf("%lld", (long long)(line->off + line->boff));
722                 printsep = true;
723         }
724         if (printsep)
725                 putchar(sep);
726 }
727
728 /*
729  * Prints a matching line according to the command line options.
730  */
731 static void
732 printline(struct parsec *pc, int sep)
733 {
734         size_t a = 0;
735         size_t i, matchidx;
736         regmatch_t match;
737
738         /* If matchall, everything matches but don't actually print for -o */
739         if (oflag && matchall)
740                 return;
741
742         matchidx = pc->matchidx;
743
744         /* --color and -o */
745         if ((oflag || color) && matchidx > 0) {
746                 /* Only print metadata once per line if --color */
747                 if (!oflag && pc->printed == 0)
748                         printline_metadata(&pc->ln, sep);
749                 for (i = 0; i < matchidx; i++) {
750                         match = pc->matches[i];
751                         /* Don't output zero length matches */
752                         if (match.rm_so == match.rm_eo)
753                                 continue;
754                         /*
755                          * Metadata is printed on a per-line basis, so every
756                          * match gets file metadata with the -o flag.
757                          */
758                         if (oflag) {
759                                 pc->ln.boff = match.rm_so;
760                                 printline_metadata(&pc->ln, sep);
761                         } else
762                                 fwrite(pc->ln.dat + a, match.rm_so - a, 1,
763                                     stdout);
764                         if (color)
765                                 fprintf(stdout, "\33[%sm\33[K", color);
766                         fwrite(pc->ln.dat + match.rm_so,
767                             match.rm_eo - match.rm_so, 1, stdout);
768                         if (color)
769                                 fprintf(stdout, "\33[m\33[K");
770                         a = match.rm_eo;
771                         if (oflag)
772                                 putchar('\n');
773                 }
774                 if (!oflag) {
775                         if (pc->ln.len - a > 0)
776                                 fwrite(pc->ln.dat + a, pc->ln.len - a, 1,
777                                     stdout);
778                         putchar('\n');
779                 }
780         } else
781                 grep_printline(&pc->ln, sep);
782         pc->printed++;
783 }