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