util.c revision 322777
1220422Sgabor/* $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $ */ 2220422Sgabor/* $FreeBSD: stable/11/usr.bin/grep/util.c 322777 2017-08-22 02:03:01Z kevans $ */ 3210389Sgabor/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ 4210389Sgabor 5210389Sgabor/*- 6211496Sdes * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav 7210389Sgabor * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> 8322625Skevans * Copyright (C) 2017 Kyle Evans <kevans@FreeBSD.org> 9210389Sgabor * All rights reserved. 10210389Sgabor * 11210389Sgabor * Redistribution and use in source and binary forms, with or without 12210389Sgabor * modification, are permitted provided that the following conditions 13210389Sgabor * are met: 14210389Sgabor * 1. Redistributions of source code must retain the above copyright 15210389Sgabor * notice, this list of conditions and the following disclaimer. 16210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright 17210389Sgabor * notice, this list of conditions and the following disclaimer in the 18210389Sgabor * documentation and/or other materials provided with the distribution. 19210389Sgabor * 20210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23210389Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30210389Sgabor * SUCH DAMAGE. 31210389Sgabor */ 32210389Sgabor 33210389Sgabor#include <sys/cdefs.h> 34210389Sgabor__FBSDID("$FreeBSD: stable/11/usr.bin/grep/util.c 322777 2017-08-22 02:03:01Z kevans $"); 35210389Sgabor 36210389Sgabor#include <sys/stat.h> 37210389Sgabor#include <sys/types.h> 38210389Sgabor 39210389Sgabor#include <ctype.h> 40210389Sgabor#include <err.h> 41210389Sgabor#include <errno.h> 42210389Sgabor#include <fnmatch.h> 43210389Sgabor#include <fts.h> 44210389Sgabor#include <libgen.h> 45210578Sgabor#include <stdbool.h> 46210389Sgabor#include <stdio.h> 47210389Sgabor#include <stdlib.h> 48210389Sgabor#include <string.h> 49210389Sgabor#include <unistd.h> 50210389Sgabor#include <wchar.h> 51210389Sgabor#include <wctype.h> 52210389Sgabor 53322582Skevans#ifndef WITHOUT_FASTMATCH 54226035Sgabor#include "fastmatch.h" 55322582Skevans#endif 56210389Sgabor#include "grep.h" 57210389Sgabor 58322587Skevansstatic bool first_match = true; 59210389Sgabor 60322587Skevans/* 61322587Skevans * Parsing context; used to hold things like matches made and 62322587Skevans * other useful bits 63322587Skevans */ 64322587Skevansstruct parsec { 65322622Skevans regmatch_t matches[MAX_MATCHES]; /* Matches made */ 66322610Skevans struct str ln; /* Current line */ 67322610Skevans size_t lnstart; /* Position in line */ 68322610Skevans size_t matchidx; /* Latest match index */ 69322610Skevans int printed; /* Metadata printed? */ 70322610Skevans bool binary; /* Binary file? */ 71322587Skevans}; 72322562Skevans 73322587Skevans 74322587Skevansstatic int procline(struct parsec *pc); 75322587Skevansstatic void printline(struct parsec *pc, int sep); 76322587Skevansstatic void printline_metadata(struct str *line, int sep); 77322587Skevans 78210578Sgaborbool 79210578Sgaborfile_matching(const char *fname) 80210578Sgabor{ 81322577Skevans char *fname_base, *fname_buf; 82210578Sgabor bool ret; 83210578Sgabor 84210578Sgabor ret = finclude ? false : true; 85322577Skevans fname_buf = strdup(fname); 86322577Skevans if (fname_buf == NULL) 87322577Skevans err(2, "strdup"); 88322577Skevans fname_base = basename(fname_buf); 89210578Sgabor 90210578Sgabor for (unsigned int i = 0; i < fpatterns; ++i) { 91220421Sgabor if (fnmatch(fpattern[i].pat, fname, 0) == 0 || 92220421Sgabor fnmatch(fpattern[i].pat, fname_base, 0) == 0) { 93322577Skevans if (fpattern[i].mode == EXCL_PAT) { 94322577Skevans ret = false; 95322577Skevans break; 96322577Skevans } else 97210578Sgabor ret = true; 98210578Sgabor } 99210578Sgabor } 100322577Skevans free(fname_buf); 101210578Sgabor return (ret); 102210578Sgabor} 103210578Sgabor 104211364Sgaborstatic inline bool 105210578Sgabordir_matching(const char *dname) 106210578Sgabor{ 107210578Sgabor bool ret; 108210578Sgabor 109210578Sgabor ret = dinclude ? false : true; 110210578Sgabor 111210578Sgabor for (unsigned int i = 0; i < dpatterns; ++i) { 112210578Sgabor if (dname != NULL && 113224938Sgabor fnmatch(dpattern[i].pat, dname, 0) == 0) { 114210578Sgabor if (dpattern[i].mode == EXCL_PAT) 115210578Sgabor return (false); 116210578Sgabor else 117210578Sgabor ret = true; 118210578Sgabor } 119210578Sgabor } 120210578Sgabor return (ret); 121210578Sgabor} 122210578Sgabor 123210389Sgabor/* 124210389Sgabor * Processes a directory when a recursive search is performed with 125210389Sgabor * the -R option. Each appropriate file is passed to procfile(). 126210389Sgabor */ 127210389Sgaborint 128210389Sgaborgrep_tree(char **argv) 129210389Sgabor{ 130210389Sgabor FTS *fts; 131210389Sgabor FTSENT *p; 132210389Sgabor int c, fts_flags; 133210389Sgabor bool ok; 134322564Skevans const char *wd[] = { ".", NULL }; 135210389Sgabor 136210389Sgabor c = fts_flags = 0; 137210389Sgabor 138210389Sgabor switch(linkbehave) { 139210389Sgabor case LINK_EXPLICIT: 140210389Sgabor fts_flags = FTS_COMFOLLOW; 141210389Sgabor break; 142210389Sgabor case LINK_SKIP: 143210389Sgabor fts_flags = FTS_PHYSICAL; 144210389Sgabor break; 145210389Sgabor default: 146210389Sgabor fts_flags = FTS_LOGICAL; 147210389Sgabor 148210389Sgabor } 149210389Sgabor 150210389Sgabor fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; 151210389Sgabor 152322564Skevans fts = fts_open((argv[0] == NULL) ? 153322564Skevans __DECONST(char * const *, wd) : argv, fts_flags, NULL); 154322564Skevans if (fts == NULL) 155210430Sdelphij err(2, "fts_open"); 156210389Sgabor while ((p = fts_read(fts)) != NULL) { 157210389Sgabor switch (p->fts_info) { 158210389Sgabor case FTS_DNR: 159210389Sgabor /* FALLTHROUGH */ 160210389Sgabor case FTS_ERR: 161228319Sgabor file_err = true; 162228097Sgabor if(!sflag) 163228097Sgabor warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 164210389Sgabor break; 165210389Sgabor case FTS_D: 166210389Sgabor /* FALLTHROUGH */ 167210389Sgabor case FTS_DP: 168224938Sgabor if (dexclude || dinclude) 169224938Sgabor if (!dir_matching(p->fts_name) || 170224938Sgabor !dir_matching(p->fts_path)) 171224938Sgabor fts_set(fts, p, FTS_SKIP); 172210389Sgabor break; 173210389Sgabor case FTS_DC: 174210389Sgabor /* Print a warning for recursive directory loop */ 175210389Sgabor warnx("warning: %s: recursive directory loop", 176210389Sgabor p->fts_path); 177210389Sgabor break; 178210389Sgabor default: 179210389Sgabor /* Check for file exclusion/inclusion */ 180210389Sgabor ok = true; 181210578Sgabor if (fexclude || finclude) 182210578Sgabor ok &= file_matching(p->fts_path); 183210389Sgabor 184210389Sgabor if (ok) 185210389Sgabor c += procfile(p->fts_path); 186210389Sgabor break; 187210389Sgabor } 188210389Sgabor } 189210389Sgabor 190210430Sdelphij fts_close(fts); 191210389Sgabor return (c); 192210389Sgabor} 193210389Sgabor 194210389Sgabor/* 195210389Sgabor * Opens a file and processes it. Each file is processed line-by-line 196210389Sgabor * passing the lines to procline(). 197210389Sgabor */ 198210389Sgaborint 199210389Sgaborprocfile(const char *fn) 200210389Sgabor{ 201322587Skevans struct parsec pc; 202322607Skevans long long tail; 203210389Sgabor struct file *f; 204210389Sgabor struct stat sb; 205322587Skevans struct str *ln; 206210389Sgabor mode_t s; 207322607Skevans int c, last_outed, t; 208322587Skevans bool doctx, printmatch, same_file; 209210389Sgabor 210210389Sgabor if (strcmp(fn, "-") == 0) { 211210389Sgabor fn = label != NULL ? label : getstr(1); 212211463Sgabor f = grep_open(NULL); 213210389Sgabor } else { 214210389Sgabor if (!stat(fn, &sb)) { 215210389Sgabor /* Check if we need to process the file */ 216210389Sgabor s = sb.st_mode & S_IFMT; 217210389Sgabor if (s == S_IFDIR && dirbehave == DIR_SKIP) 218210389Sgabor return (0); 219210389Sgabor if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK 220210389Sgabor || s == S_IFSOCK) && devbehave == DEV_SKIP) 221210389Sgabor return (0); 222210389Sgabor } 223210389Sgabor f = grep_open(fn); 224210389Sgabor } 225210389Sgabor if (f == NULL) { 226228319Sgabor file_err = true; 227210389Sgabor if (!sflag) 228210389Sgabor warn("%s", fn); 229210389Sgabor return (0); 230210389Sgabor } 231210389Sgabor 232322587Skevans /* Convenience */ 233322587Skevans ln = &pc.ln; 234322587Skevans pc.ln.file = grep_malloc(strlen(fn) + 1); 235322587Skevans strcpy(pc.ln.file, fn); 236322587Skevans pc.ln.line_no = 0; 237322587Skevans pc.ln.len = 0; 238322610Skevans pc.ln.boff = 0; 239322587Skevans pc.ln.off = -1; 240322587Skevans pc.binary = f->binary; 241322610Skevans pc.printed = 0; 242210389Sgabor tail = 0; 243322587Skevans last_outed = 0; 244322587Skevans same_file = false; 245322587Skevans doctx = false; 246322587Skevans printmatch = true; 247322587Skevans if ((pc.binary && binbehave == BINFILE_BIN) || cflag || qflag || 248322587Skevans lflag || Lflag) 249322587Skevans printmatch = false; 250322587Skevans if (printmatch && (Aflag != 0 || Bflag != 0)) 251322587Skevans doctx = true; 252322587Skevans mcount = mlimit; 253210389Sgabor 254210389Sgabor for (c = 0; c == 0 || !(lflag || qflag); ) { 255322610Skevans /* Reset per-line statistics */ 256322610Skevans pc.printed = 0; 257322587Skevans pc.matchidx = 0; 258322609Skevans pc.lnstart = 0; 259322610Skevans pc.ln.boff = 0; 260322587Skevans pc.ln.off += pc.ln.len + 1; 261322587Skevans if ((pc.ln.dat = grep_fgetln(f, &pc.ln.len)) == NULL || 262322777Skevans pc.ln.len == 0) 263322777Skevans break; 264210389Sgabor 265322587Skevans if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol) 266322587Skevans --pc.ln.len; 267322587Skevans pc.ln.line_no++; 268322587Skevans 269210389Sgabor /* Return if we need to skip a binary file */ 270322587Skevans if (pc.binary && binbehave == BINFILE_SKIP) { 271210389Sgabor grep_close(f); 272322587Skevans free(pc.ln.file); 273210389Sgabor free(f); 274210389Sgabor return (0); 275210389Sgabor } 276322562Skevans 277322587Skevans if ((t = procline(&pc)) == 0) 278322587Skevans ++c; 279322587Skevans 280322587Skevans /* Deal with any -B context or context separators */ 281322587Skevans if (t == 0 && doctx) { 282322587Skevans if (!first_match && (!same_file || last_outed > 0)) 283322587Skevans printf("--\n"); 284322587Skevans if (Bflag > 0) 285322587Skevans printqueue(); 286322587Skevans tail = Aflag; 287322587Skevans } 288322587Skevans /* Print the matching line, but only if not quiet/binary */ 289322587Skevans if (t == 0 && printmatch) { 290322587Skevans printline(&pc, ':'); 291322622Skevans while (pc.matchidx >= MAX_MATCHES) { 292322609Skevans /* Reset matchidx and try again */ 293322609Skevans pc.matchidx = 0; 294322609Skevans if (procline(&pc) == 0) 295322609Skevans printline(&pc, ':'); 296322609Skevans else 297322609Skevans break; 298322609Skevans } 299322587Skevans first_match = false; 300322587Skevans same_file = true; 301322587Skevans last_outed = 0; 302322587Skevans } 303322587Skevans if (t != 0 && doctx) { 304322587Skevans /* Deal with any -A context */ 305322587Skevans if (tail > 0) { 306322610Skevans grep_printline(&pc.ln, '-'); 307322587Skevans tail--; 308322587Skevans if (Bflag > 0) 309322587Skevans clearqueue(); 310322562Skevans } else { 311322562Skevans /* 312322587Skevans * Enqueue non-matching lines for -B context. 313322587Skevans * If we're not actually doing -B context or if 314322587Skevans * the enqueue resulted in a line being rotated 315322587Skevans * out, then go ahead and increment last_outed 316322587Skevans * to signify a gap between context/match. 317322562Skevans */ 318322587Skevans if (Bflag == 0 || (Bflag > 0 && enqueue(ln))) 319322587Skevans ++last_outed; 320322562Skevans } 321210389Sgabor } 322322587Skevans 323322587Skevans /* Count the matches if we have a match limit */ 324322587Skevans if (t == 0 && mflag) { 325322587Skevans --mcount; 326322587Skevans if (mflag && mcount <= 0) 327322587Skevans break; 328322587Skevans } 329322587Skevans 330210389Sgabor } 331210389Sgabor if (Bflag > 0) 332210389Sgabor clearqueue(); 333210389Sgabor grep_close(f); 334210389Sgabor 335210389Sgabor if (cflag) { 336210389Sgabor if (!hflag) 337322587Skevans printf("%s:", pc.ln.file); 338210389Sgabor printf("%u\n", c); 339210389Sgabor } 340210461Sgabor if (lflag && !qflag && c != 0) 341228093Sgabor printf("%s%c", fn, nullflag ? 0 : '\n'); 342210461Sgabor if (Lflag && !qflag && c == 0) 343228093Sgabor printf("%s%c", fn, nullflag ? 0 : '\n'); 344210389Sgabor if (c && !cflag && !lflag && !Lflag && 345210389Sgabor binbehave == BINFILE_BIN && f->binary && !qflag) 346210622Sgabor printf(getstr(8), fn); 347210389Sgabor 348322587Skevans free(pc.ln.file); 349210389Sgabor free(f); 350210389Sgabor return (c); 351210389Sgabor} 352210389Sgabor 353210389Sgabor#define iswword(x) (iswalnum((x)) || (x) == L'_') 354210389Sgabor 355210389Sgabor/* 356210389Sgabor * Processes a line comparing it with the specified patterns. Each pattern 357210389Sgabor * is looped to be compared along with the full string, saving each and every 358210389Sgabor * match, which is necessary to colorize the output and to count the 359210389Sgabor * matches. The matching lines are passed to printline() to display the 360210389Sgabor * appropriate output. 361210389Sgabor */ 362220421Sgaborstatic int 363322587Skevansprocline(struct parsec *pc) 364210389Sgabor{ 365322587Skevans regmatch_t pmatch, lastmatch, chkmatch; 366322587Skevans wchar_t wbegin, wend; 367322609Skevans size_t st, nst; 368210389Sgabor unsigned int i; 369322587Skevans int c = 0, r = 0, lastmatches = 0, leflags = eflags; 370322587Skevans size_t startm = 0, matchidx; 371322609Skevans unsigned int retry; 372210389Sgabor 373322587Skevans matchidx = pc->matchidx; 374322587Skevans 375322587Skevans /* Special case: empty pattern with -w flag, check first character */ 376322587Skevans if (matchall && wflag) { 377322587Skevans if (pc->ln.len == 0) 378322587Skevans return (0); 379322587Skevans wend = L' '; 380322587Skevans if (sscanf(&pc->ln.dat[0], "%lc", &wend) != 1 || iswword(wend)) 381322587Skevans return (1); 382322587Skevans else 383322587Skevans return (0); 384322587Skevans } else if (matchall) 385322587Skevans return (0); 386322587Skevans 387322609Skevans st = pc->lnstart; 388322609Skevans nst = 0; 389322555Skevans /* Initialize to avoid a false positive warning from GCC. */ 390322555Skevans lastmatch.rm_so = lastmatch.rm_eo = 0; 391322555Skevans 392226035Sgabor /* Loop to process the whole line */ 393322587Skevans while (st <= pc->ln.len) { 394322555Skevans lastmatches = 0; 395322587Skevans startm = matchidx; 396322583Skevans retry = 0; 397322622Skevans if (st > 0 && pc->ln.dat[st - 1] != fileeol) 398322555Skevans leflags |= REG_NOTBOL; 399226035Sgabor /* Loop to compare with all the patterns */ 400226035Sgabor for (i = 0; i < patterns; i++) { 401322555Skevans pmatch.rm_so = st; 402322587Skevans pmatch.rm_eo = pc->ln.len; 403322582Skevans#ifndef WITHOUT_FASTMATCH 404226035Sgabor if (fg_pattern[i].pattern) 405226035Sgabor r = fastexec(&fg_pattern[i], 406322587Skevans pc->ln.dat, 1, &pmatch, leflags); 407226035Sgabor else 408322582Skevans#endif 409322587Skevans r = regexec(&r_pattern[i], pc->ln.dat, 1, 410322555Skevans &pmatch, leflags); 411322587Skevans if (r != 0) 412226035Sgabor continue; 413226035Sgabor /* Check for full match */ 414322587Skevans if (xflag && (pmatch.rm_so != 0 || 415322587Skevans (size_t)pmatch.rm_eo != pc->ln.len)) 416322587Skevans continue; 417226035Sgabor /* Check for whole word match */ 418322582Skevans#ifndef WITHOUT_FASTMATCH 419322587Skevans if (wflag || fg_pattern[i].word) { 420322582Skevans#else 421322587Skevans if (wflag) { 422322582Skevans#endif 423226035Sgabor wbegin = wend = L' '; 424226035Sgabor if (pmatch.rm_so != 0 && 425322587Skevans sscanf(&pc->ln.dat[pmatch.rm_so - 1], 426226035Sgabor "%lc", &wbegin) != 1) 427226035Sgabor r = REG_NOMATCH; 428226035Sgabor else if ((size_t)pmatch.rm_eo != 429322587Skevans pc->ln.len && 430322587Skevans sscanf(&pc->ln.dat[pmatch.rm_eo], 431226035Sgabor "%lc", &wend) != 1) 432226035Sgabor r = REG_NOMATCH; 433226035Sgabor else if (iswword(wbegin) || 434226035Sgabor iswword(wend)) 435226035Sgabor r = REG_NOMATCH; 436322583Skevans /* 437322583Skevans * If we're doing whole word matching and we 438322583Skevans * matched once, then we should try the pattern 439322587Skevans * again after advancing just past the start of 440322583Skevans * the earliest match. This allows the pattern 441322583Skevans * to match later on in the line and possibly 442322583Skevans * still match a whole word. 443322583Skevans */ 444322583Skevans if (r == REG_NOMATCH && 445322609Skevans (retry == pc->lnstart || 446322619Skevans (unsigned int)pmatch.rm_so + 1 < retry)) 447322583Skevans retry = pmatch.rm_so + 1; 448322587Skevans if (r == REG_NOMATCH) 449322587Skevans continue; 450210389Sgabor } 451322587Skevans lastmatches++; 452322587Skevans lastmatch = pmatch; 453322587Skevans 454322587Skevans if (matchidx == 0) 455322587Skevans c++; 456322587Skevans 457322587Skevans /* 458322587Skevans * Replace previous match if the new one is earlier 459322587Skevans * and/or longer. This will lead to some amount of 460322587Skevans * extra work if -o/--color are specified, but it's 461322587Skevans * worth it from a correctness point of view. 462322587Skevans */ 463322587Skevans if (matchidx > startm) { 464322587Skevans chkmatch = pc->matches[matchidx - 1]; 465322587Skevans if (pmatch.rm_so < chkmatch.rm_so || 466322587Skevans (pmatch.rm_so == chkmatch.rm_so && 467322587Skevans (pmatch.rm_eo - pmatch.rm_so) > 468322587Skevans (chkmatch.rm_eo - chkmatch.rm_so))) { 469322587Skevans pc->matches[matchidx - 1] = pmatch; 470322587Skevans nst = pmatch.rm_eo; 471322555Skevans } 472322587Skevans } else { 473322587Skevans /* Advance as normal if not */ 474322587Skevans pc->matches[matchidx++] = pmatch; 475322587Skevans nst = pmatch.rm_eo; 476210389Sgabor } 477322587Skevans /* avoid excessive matching - skip further patterns */ 478322587Skevans if ((color == NULL && !oflag) || qflag || lflag || 479322622Skevans matchidx >= MAX_MATCHES) { 480322609Skevans pc->lnstart = nst; 481322609Skevans lastmatches = 0; 482322587Skevans break; 483322609Skevans } 484226035Sgabor } 485210389Sgabor 486322583Skevans /* 487322583Skevans * Advance to just past the start of the earliest match, try 488322583Skevans * again just in case we still have a chance to match later in 489322583Skevans * the string. 490322583Skevans */ 491322609Skevans if (lastmatches == 0 && retry > pc->lnstart) { 492322583Skevans st = retry; 493322583Skevans continue; 494210389Sgabor } 495210389Sgabor 496226035Sgabor /* One pass if we are not recording matches */ 497270132Sgabor if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag)) 498226035Sgabor break; 499226035Sgabor 500322555Skevans /* If we didn't have any matches or REG_NOSUB set */ 501322555Skevans if (lastmatches == 0 || (cflags & REG_NOSUB)) 502322587Skevans nst = pc->ln.len; 503322555Skevans 504322555Skevans if (lastmatches == 0) 505322555Skevans /* No matches */ 506322555Skevans break; 507322555Skevans else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo) 508322555Skevans /* Zero-length match -- advance one more so we don't get stuck */ 509322555Skevans nst++; 510322555Skevans 511322555Skevans /* Advance st based on previous matches */ 512322555Skevans st = nst; 513322609Skevans pc->lnstart = st; 514226035Sgabor } 515226035Sgabor 516322587Skevans /* Reflect the new matchidx in the context */ 517322587Skevans pc->matchidx = matchidx; 518322583Skevans if (vflag) 519322583Skevans c = !c; 520322587Skevans return (c ? 0 : 1); 521210389Sgabor} 522210389Sgabor 523210389Sgabor/* 524210389Sgabor * Safe malloc() for internal use. 525210389Sgabor */ 526210389Sgaborvoid * 527210389Sgaborgrep_malloc(size_t size) 528210389Sgabor{ 529210389Sgabor void *ptr; 530210389Sgabor 531210389Sgabor if ((ptr = malloc(size)) == NULL) 532210389Sgabor err(2, "malloc"); 533210389Sgabor return (ptr); 534210389Sgabor} 535210389Sgabor 536210389Sgabor/* 537210389Sgabor * Safe calloc() for internal use. 538210389Sgabor */ 539210389Sgaborvoid * 540210389Sgaborgrep_calloc(size_t nmemb, size_t size) 541210389Sgabor{ 542210389Sgabor void *ptr; 543210389Sgabor 544210389Sgabor if ((ptr = calloc(nmemb, size)) == NULL) 545210389Sgabor err(2, "calloc"); 546210389Sgabor return (ptr); 547210389Sgabor} 548210389Sgabor 549210389Sgabor/* 550210389Sgabor * Safe realloc() for internal use. 551210389Sgabor */ 552210389Sgaborvoid * 553210389Sgaborgrep_realloc(void *ptr, size_t size) 554210389Sgabor{ 555210389Sgabor 556210389Sgabor if ((ptr = realloc(ptr, size)) == NULL) 557210389Sgabor err(2, "realloc"); 558210389Sgabor return (ptr); 559210389Sgabor} 560210389Sgabor 561210389Sgabor/* 562210578Sgabor * Safe strdup() for internal use. 563210578Sgabor */ 564210578Sgaborchar * 565210578Sgaborgrep_strdup(const char *str) 566210578Sgabor{ 567210578Sgabor char *ret; 568210578Sgabor 569210578Sgabor if ((ret = strdup(str)) == NULL) 570210578Sgabor err(2, "strdup"); 571210578Sgabor return (ret); 572210578Sgabor} 573210578Sgabor 574210578Sgabor/* 575322587Skevans * Print an entire line as-is, there are no inline matches to consider. This is 576322587Skevans * used for printing context. 577210389Sgabor */ 578322587Skevansvoid grep_printline(struct str *line, int sep) { 579322587Skevans printline_metadata(line, sep); 580322587Skevans fwrite(line->dat, line->len, 1, stdout); 581322587Skevans putchar(fileeol); 582322587Skevans} 583322587Skevans 584322587Skevansstatic void 585322587Skevansprintline_metadata(struct str *line, int sep) 586210389Sgabor{ 587322587Skevans bool printsep; 588210389Sgabor 589322587Skevans printsep = false; 590210389Sgabor if (!hflag) { 591228093Sgabor if (!nullflag) { 592210389Sgabor fputs(line->file, stdout); 593322587Skevans printsep = true; 594228093Sgabor } else { 595210389Sgabor printf("%s", line->file); 596210389Sgabor putchar(0); 597210389Sgabor } 598210389Sgabor } 599210389Sgabor if (nflag) { 600322587Skevans if (printsep) 601210389Sgabor putchar(sep); 602210389Sgabor printf("%d", line->line_no); 603322587Skevans printsep = true; 604210389Sgabor } 605210389Sgabor if (bflag) { 606322587Skevans if (printsep) 607210389Sgabor putchar(sep); 608322610Skevans printf("%lld", (long long)(line->off + line->boff)); 609322587Skevans printsep = true; 610210389Sgabor } 611322587Skevans if (printsep) 612210389Sgabor putchar(sep); 613322587Skevans} 614322587Skevans 615322587Skevans/* 616322587Skevans * Prints a matching line according to the command line options. 617322587Skevans */ 618322587Skevansstatic void 619322587Skevansprintline(struct parsec *pc, int sep) 620322587Skevans{ 621322587Skevans size_t a = 0; 622322587Skevans size_t i, matchidx; 623322587Skevans regmatch_t match; 624322587Skevans 625322587Skevans /* If matchall, everything matches but don't actually print for -o */ 626322587Skevans if (oflag && matchall) 627322587Skevans return; 628322587Skevans 629322587Skevans matchidx = pc->matchidx; 630322587Skevans 631210389Sgabor /* --color and -o */ 632322587Skevans if ((oflag || color) && matchidx > 0) { 633322610Skevans /* Only print metadata once per line if --color */ 634322610Skevans if (!oflag && pc->printed == 0) 635322610Skevans printline_metadata(&pc->ln, sep); 636322587Skevans for (i = 0; i < matchidx; i++) { 637322587Skevans match = pc->matches[i]; 638322555Skevans /* Don't output zero length matches */ 639322587Skevans if (match.rm_so == match.rm_eo) 640322555Skevans continue; 641322610Skevans /* 642322610Skevans * Metadata is printed on a per-line basis, so every 643322610Skevans * match gets file metadata with the -o flag. 644322610Skevans */ 645322610Skevans if (oflag) { 646322610Skevans pc->ln.boff = match.rm_so; 647322610Skevans printline_metadata(&pc->ln, sep); 648322610Skevans } else 649322587Skevans fwrite(pc->ln.dat + a, match.rm_so - a, 1, 650210389Sgabor stdout); 651322587Skevans if (color) 652210389Sgabor fprintf(stdout, "\33[%sm\33[K", color); 653322587Skevans fwrite(pc->ln.dat + match.rm_so, 654322587Skevans match.rm_eo - match.rm_so, 1, stdout); 655322587Skevans if (color) 656210389Sgabor fprintf(stdout, "\33[m\33[K"); 657322587Skevans a = match.rm_eo; 658210389Sgabor if (oflag) 659210389Sgabor putchar('\n'); 660210389Sgabor } 661210389Sgabor if (!oflag) { 662322587Skevans if (pc->ln.len - a > 0) 663322587Skevans fwrite(pc->ln.dat + a, pc->ln.len - a, 1, 664322587Skevans stdout); 665210389Sgabor putchar('\n'); 666210389Sgabor } 667322587Skevans } else 668322587Skevans grep_printline(&pc->ln, sep); 669322610Skevans pc->printed++; 670210389Sgabor} 671