util.c revision 322622
1220422Sgabor/* $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $ */ 2220422Sgabor/* $FreeBSD: stable/11/usr.bin/grep/util.c 322622 2017-08-17 17:09:28Z 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> 8210389Sgabor * All rights reserved. 9210389Sgabor * 10210389Sgabor * Redistribution and use in source and binary forms, with or without 11210389Sgabor * modification, are permitted provided that the following conditions 12210389Sgabor * are met: 13210389Sgabor * 1. Redistributions of source code must retain the above copyright 14210389Sgabor * notice, this list of conditions and the following disclaimer. 15210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright 16210389Sgabor * notice, this list of conditions and the following disclaimer in the 17210389Sgabor * documentation and/or other materials provided with the distribution. 18210389Sgabor * 19210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22210389Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29210389Sgabor * SUCH DAMAGE. 30210389Sgabor */ 31210389Sgabor 32210389Sgabor#include <sys/cdefs.h> 33210389Sgabor__FBSDID("$FreeBSD: stable/11/usr.bin/grep/util.c 322622 2017-08-17 17:09:28Z kevans $"); 34210389Sgabor 35210389Sgabor#include <sys/stat.h> 36210389Sgabor#include <sys/types.h> 37210389Sgabor 38210389Sgabor#include <ctype.h> 39210389Sgabor#include <err.h> 40210389Sgabor#include <errno.h> 41210389Sgabor#include <fnmatch.h> 42210389Sgabor#include <fts.h> 43210389Sgabor#include <libgen.h> 44210578Sgabor#include <stdbool.h> 45210389Sgabor#include <stdio.h> 46210389Sgabor#include <stdlib.h> 47210389Sgabor#include <string.h> 48210389Sgabor#include <unistd.h> 49210389Sgabor#include <wchar.h> 50210389Sgabor#include <wctype.h> 51210389Sgabor 52322582Skevans#ifndef WITHOUT_FASTMATCH 53226035Sgabor#include "fastmatch.h" 54322582Skevans#endif 55210389Sgabor#include "grep.h" 56210389Sgabor 57322587Skevansstatic bool first_match = true; 58210389Sgabor 59322587Skevans/* 60322587Skevans * Parsing context; used to hold things like matches made and 61322587Skevans * other useful bits 62322587Skevans */ 63322587Skevansstruct parsec { 64322622Skevans regmatch_t matches[MAX_MATCHES]; /* Matches made */ 65322610Skevans struct str ln; /* Current line */ 66322610Skevans size_t lnstart; /* Position in line */ 67322610Skevans size_t matchidx; /* Latest match index */ 68322610Skevans int printed; /* Metadata printed? */ 69322610Skevans bool binary; /* Binary file? */ 70322587Skevans}; 71322562Skevans 72322587Skevans 73322587Skevansstatic int procline(struct parsec *pc); 74322587Skevansstatic void printline(struct parsec *pc, int sep); 75322587Skevansstatic void printline_metadata(struct str *line, int sep); 76322587Skevans 77210578Sgaborbool 78210578Sgaborfile_matching(const char *fname) 79210578Sgabor{ 80322577Skevans char *fname_base, *fname_buf; 81210578Sgabor bool ret; 82210578Sgabor 83210578Sgabor ret = finclude ? false : true; 84322577Skevans fname_buf = strdup(fname); 85322577Skevans if (fname_buf == NULL) 86322577Skevans err(2, "strdup"); 87322577Skevans fname_base = basename(fname_buf); 88210578Sgabor 89210578Sgabor for (unsigned int i = 0; i < fpatterns; ++i) { 90220421Sgabor if (fnmatch(fpattern[i].pat, fname, 0) == 0 || 91220421Sgabor fnmatch(fpattern[i].pat, fname_base, 0) == 0) { 92322577Skevans if (fpattern[i].mode == EXCL_PAT) { 93322577Skevans ret = false; 94322577Skevans break; 95322577Skevans } else 96210578Sgabor ret = true; 97210578Sgabor } 98210578Sgabor } 99322577Skevans free(fname_buf); 100210578Sgabor return (ret); 101210578Sgabor} 102210578Sgabor 103211364Sgaborstatic inline bool 104210578Sgabordir_matching(const char *dname) 105210578Sgabor{ 106210578Sgabor bool ret; 107210578Sgabor 108210578Sgabor ret = dinclude ? false : true; 109210578Sgabor 110210578Sgabor for (unsigned int i = 0; i < dpatterns; ++i) { 111210578Sgabor if (dname != NULL && 112224938Sgabor fnmatch(dpattern[i].pat, dname, 0) == 0) { 113210578Sgabor if (dpattern[i].mode == EXCL_PAT) 114210578Sgabor return (false); 115210578Sgabor else 116210578Sgabor ret = true; 117210578Sgabor } 118210578Sgabor } 119210578Sgabor return (ret); 120210578Sgabor} 121210578Sgabor 122210389Sgabor/* 123210389Sgabor * Processes a directory when a recursive search is performed with 124210389Sgabor * the -R option. Each appropriate file is passed to procfile(). 125210389Sgabor */ 126210389Sgaborint 127210389Sgaborgrep_tree(char **argv) 128210389Sgabor{ 129210389Sgabor FTS *fts; 130210389Sgabor FTSENT *p; 131210389Sgabor int c, fts_flags; 132210389Sgabor bool ok; 133322564Skevans const char *wd[] = { ".", NULL }; 134210389Sgabor 135210389Sgabor c = fts_flags = 0; 136210389Sgabor 137210389Sgabor switch(linkbehave) { 138210389Sgabor case LINK_EXPLICIT: 139210389Sgabor fts_flags = FTS_COMFOLLOW; 140210389Sgabor break; 141210389Sgabor case LINK_SKIP: 142210389Sgabor fts_flags = FTS_PHYSICAL; 143210389Sgabor break; 144210389Sgabor default: 145210389Sgabor fts_flags = FTS_LOGICAL; 146210389Sgabor 147210389Sgabor } 148210389Sgabor 149210389Sgabor fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; 150210389Sgabor 151322564Skevans fts = fts_open((argv[0] == NULL) ? 152322564Skevans __DECONST(char * const *, wd) : argv, fts_flags, NULL); 153322564Skevans if (fts == NULL) 154210430Sdelphij err(2, "fts_open"); 155210389Sgabor while ((p = fts_read(fts)) != NULL) { 156210389Sgabor switch (p->fts_info) { 157210389Sgabor case FTS_DNR: 158210389Sgabor /* FALLTHROUGH */ 159210389Sgabor case FTS_ERR: 160228319Sgabor file_err = true; 161228097Sgabor if(!sflag) 162228097Sgabor warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 163210389Sgabor break; 164210389Sgabor case FTS_D: 165210389Sgabor /* FALLTHROUGH */ 166210389Sgabor case FTS_DP: 167224938Sgabor if (dexclude || dinclude) 168224938Sgabor if (!dir_matching(p->fts_name) || 169224938Sgabor !dir_matching(p->fts_path)) 170224938Sgabor fts_set(fts, p, FTS_SKIP); 171210389Sgabor break; 172210389Sgabor case FTS_DC: 173210389Sgabor /* Print a warning for recursive directory loop */ 174210389Sgabor warnx("warning: %s: recursive directory loop", 175210389Sgabor p->fts_path); 176210389Sgabor break; 177210389Sgabor default: 178210389Sgabor /* Check for file exclusion/inclusion */ 179210389Sgabor ok = true; 180210578Sgabor if (fexclude || finclude) 181210578Sgabor ok &= file_matching(p->fts_path); 182210389Sgabor 183210389Sgabor if (ok) 184210389Sgabor c += procfile(p->fts_path); 185210389Sgabor break; 186210389Sgabor } 187210389Sgabor } 188210389Sgabor 189210430Sdelphij fts_close(fts); 190210389Sgabor return (c); 191210389Sgabor} 192210389Sgabor 193210389Sgabor/* 194210389Sgabor * Opens a file and processes it. Each file is processed line-by-line 195210389Sgabor * passing the lines to procline(). 196210389Sgabor */ 197210389Sgaborint 198210389Sgaborprocfile(const char *fn) 199210389Sgabor{ 200322587Skevans struct parsec pc; 201322607Skevans long long tail; 202210389Sgabor struct file *f; 203210389Sgabor struct stat sb; 204322587Skevans struct str *ln; 205210389Sgabor mode_t s; 206322607Skevans int c, last_outed, t; 207322587Skevans bool doctx, printmatch, same_file; 208210389Sgabor 209210389Sgabor if (strcmp(fn, "-") == 0) { 210210389Sgabor fn = label != NULL ? label : getstr(1); 211211463Sgabor f = grep_open(NULL); 212210389Sgabor } else { 213210389Sgabor if (!stat(fn, &sb)) { 214210389Sgabor /* Check if we need to process the file */ 215210389Sgabor s = sb.st_mode & S_IFMT; 216210389Sgabor if (s == S_IFDIR && dirbehave == DIR_SKIP) 217210389Sgabor return (0); 218210389Sgabor if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK 219210389Sgabor || s == S_IFSOCK) && devbehave == DEV_SKIP) 220210389Sgabor return (0); 221210389Sgabor } 222210389Sgabor f = grep_open(fn); 223210389Sgabor } 224210389Sgabor if (f == NULL) { 225228319Sgabor file_err = true; 226210389Sgabor if (!sflag) 227210389Sgabor warn("%s", fn); 228210389Sgabor return (0); 229210389Sgabor } 230210389Sgabor 231322587Skevans /* Convenience */ 232322587Skevans ln = &pc.ln; 233322587Skevans pc.ln.file = grep_malloc(strlen(fn) + 1); 234322587Skevans strcpy(pc.ln.file, fn); 235322587Skevans pc.ln.line_no = 0; 236322587Skevans pc.ln.len = 0; 237322610Skevans pc.ln.boff = 0; 238322587Skevans pc.ln.off = -1; 239322587Skevans pc.binary = f->binary; 240322610Skevans pc.printed = 0; 241210389Sgabor tail = 0; 242322587Skevans last_outed = 0; 243322587Skevans same_file = false; 244322587Skevans doctx = false; 245322587Skevans printmatch = true; 246322587Skevans if ((pc.binary && binbehave == BINFILE_BIN) || cflag || qflag || 247322587Skevans lflag || Lflag) 248322587Skevans printmatch = false; 249322587Skevans if (printmatch && (Aflag != 0 || Bflag != 0)) 250322587Skevans doctx = true; 251322587Skevans mcount = mlimit; 252210389Sgabor 253210389Sgabor for (c = 0; c == 0 || !(lflag || qflag); ) { 254322610Skevans /* Reset per-line statistics */ 255322610Skevans pc.printed = 0; 256322587Skevans pc.matchidx = 0; 257322609Skevans pc.lnstart = 0; 258322610Skevans pc.ln.boff = 0; 259322587Skevans pc.ln.off += pc.ln.len + 1; 260322587Skevans if ((pc.ln.dat = grep_fgetln(f, &pc.ln.len)) == NULL || 261322587Skevans pc.ln.len == 0) { 262322587Skevans if (pc.ln.line_no == 0 && matchall) 263322587Skevans /* 264322587Skevans * An empty file with an empty pattern and the 265322587Skevans * -w flag does not match 266322587Skevans */ 267322587Skevans exit(matchall && wflag ? 1 : 0); 268210389Sgabor else 269210389Sgabor break; 270210389Sgabor } 271210389Sgabor 272322587Skevans if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol) 273322587Skevans --pc.ln.len; 274322587Skevans pc.ln.line_no++; 275322587Skevans 276210389Sgabor /* Return if we need to skip a binary file */ 277322587Skevans if (pc.binary && binbehave == BINFILE_SKIP) { 278210389Sgabor grep_close(f); 279322587Skevans free(pc.ln.file); 280210389Sgabor free(f); 281210389Sgabor return (0); 282210389Sgabor } 283322562Skevans 284322587Skevans if ((t = procline(&pc)) == 0) 285322587Skevans ++c; 286322587Skevans 287322587Skevans /* Deal with any -B context or context separators */ 288322587Skevans if (t == 0 && doctx) { 289322587Skevans if (!first_match && (!same_file || last_outed > 0)) 290322587Skevans printf("--\n"); 291322587Skevans if (Bflag > 0) 292322587Skevans printqueue(); 293322587Skevans tail = Aflag; 294322587Skevans } 295322587Skevans /* Print the matching line, but only if not quiet/binary */ 296322587Skevans if (t == 0 && printmatch) { 297322587Skevans printline(&pc, ':'); 298322622Skevans while (pc.matchidx >= MAX_MATCHES) { 299322609Skevans /* Reset matchidx and try again */ 300322609Skevans pc.matchidx = 0; 301322609Skevans if (procline(&pc) == 0) 302322609Skevans printline(&pc, ':'); 303322609Skevans else 304322609Skevans break; 305322609Skevans } 306322587Skevans first_match = false; 307322587Skevans same_file = true; 308322587Skevans last_outed = 0; 309322587Skevans } 310322587Skevans if (t != 0 && doctx) { 311322587Skevans /* Deal with any -A context */ 312322587Skevans if (tail > 0) { 313322610Skevans grep_printline(&pc.ln, '-'); 314322587Skevans tail--; 315322587Skevans if (Bflag > 0) 316322587Skevans clearqueue(); 317322562Skevans } else { 318322562Skevans /* 319322587Skevans * Enqueue non-matching lines for -B context. 320322587Skevans * If we're not actually doing -B context or if 321322587Skevans * the enqueue resulted in a line being rotated 322322587Skevans * out, then go ahead and increment last_outed 323322587Skevans * to signify a gap between context/match. 324322562Skevans */ 325322587Skevans if (Bflag == 0 || (Bflag > 0 && enqueue(ln))) 326322587Skevans ++last_outed; 327322562Skevans } 328210389Sgabor } 329322587Skevans 330322587Skevans /* Count the matches if we have a match limit */ 331322587Skevans if (t == 0 && mflag) { 332322587Skevans --mcount; 333322587Skevans if (mflag && mcount <= 0) 334322587Skevans break; 335322587Skevans } 336322587Skevans 337210389Sgabor } 338210389Sgabor if (Bflag > 0) 339210389Sgabor clearqueue(); 340210389Sgabor grep_close(f); 341210389Sgabor 342210389Sgabor if (cflag) { 343210389Sgabor if (!hflag) 344322587Skevans printf("%s:", pc.ln.file); 345210389Sgabor printf("%u\n", c); 346210389Sgabor } 347210461Sgabor if (lflag && !qflag && c != 0) 348228093Sgabor printf("%s%c", fn, nullflag ? 0 : '\n'); 349210461Sgabor if (Lflag && !qflag && c == 0) 350228093Sgabor printf("%s%c", fn, nullflag ? 0 : '\n'); 351210389Sgabor if (c && !cflag && !lflag && !Lflag && 352210389Sgabor binbehave == BINFILE_BIN && f->binary && !qflag) 353210622Sgabor printf(getstr(8), fn); 354210389Sgabor 355322587Skevans free(pc.ln.file); 356210389Sgabor free(f); 357210389Sgabor return (c); 358210389Sgabor} 359210389Sgabor 360210389Sgabor#define iswword(x) (iswalnum((x)) || (x) == L'_') 361210389Sgabor 362210389Sgabor/* 363210389Sgabor * Processes a line comparing it with the specified patterns. Each pattern 364210389Sgabor * is looped to be compared along with the full string, saving each and every 365210389Sgabor * match, which is necessary to colorize the output and to count the 366210389Sgabor * matches. The matching lines are passed to printline() to display the 367210389Sgabor * appropriate output. 368210389Sgabor */ 369220421Sgaborstatic int 370322587Skevansprocline(struct parsec *pc) 371210389Sgabor{ 372322587Skevans regmatch_t pmatch, lastmatch, chkmatch; 373322587Skevans wchar_t wbegin, wend; 374322609Skevans size_t st, nst; 375210389Sgabor unsigned int i; 376322587Skevans int c = 0, r = 0, lastmatches = 0, leflags = eflags; 377322587Skevans size_t startm = 0, matchidx; 378322609Skevans unsigned int retry; 379210389Sgabor 380322587Skevans matchidx = pc->matchidx; 381322587Skevans 382322587Skevans /* Special case: empty pattern with -w flag, check first character */ 383322587Skevans if (matchall && wflag) { 384322587Skevans if (pc->ln.len == 0) 385322587Skevans return (0); 386322587Skevans wend = L' '; 387322587Skevans if (sscanf(&pc->ln.dat[0], "%lc", &wend) != 1 || iswword(wend)) 388322587Skevans return (1); 389322587Skevans else 390322587Skevans return (0); 391322587Skevans } else if (matchall) 392322587Skevans return (0); 393322587Skevans 394322609Skevans st = pc->lnstart; 395322609Skevans nst = 0; 396322555Skevans /* Initialize to avoid a false positive warning from GCC. */ 397322555Skevans lastmatch.rm_so = lastmatch.rm_eo = 0; 398322555Skevans 399226035Sgabor /* Loop to process the whole line */ 400322587Skevans while (st <= pc->ln.len) { 401322555Skevans lastmatches = 0; 402322587Skevans startm = matchidx; 403322583Skevans retry = 0; 404322622Skevans if (st > 0 && pc->ln.dat[st - 1] != fileeol) 405322555Skevans leflags |= REG_NOTBOL; 406226035Sgabor /* Loop to compare with all the patterns */ 407226035Sgabor for (i = 0; i < patterns; i++) { 408322555Skevans pmatch.rm_so = st; 409322587Skevans pmatch.rm_eo = pc->ln.len; 410322582Skevans#ifndef WITHOUT_FASTMATCH 411226035Sgabor if (fg_pattern[i].pattern) 412226035Sgabor r = fastexec(&fg_pattern[i], 413322587Skevans pc->ln.dat, 1, &pmatch, leflags); 414226035Sgabor else 415322582Skevans#endif 416322587Skevans r = regexec(&r_pattern[i], pc->ln.dat, 1, 417322555Skevans &pmatch, leflags); 418322587Skevans if (r != 0) 419226035Sgabor continue; 420226035Sgabor /* Check for full match */ 421322587Skevans if (xflag && (pmatch.rm_so != 0 || 422322587Skevans (size_t)pmatch.rm_eo != pc->ln.len)) 423322587Skevans continue; 424226035Sgabor /* Check for whole word match */ 425322582Skevans#ifndef WITHOUT_FASTMATCH 426322587Skevans if (wflag || fg_pattern[i].word) { 427322582Skevans#else 428322587Skevans if (wflag) { 429322582Skevans#endif 430226035Sgabor wbegin = wend = L' '; 431226035Sgabor if (pmatch.rm_so != 0 && 432322587Skevans sscanf(&pc->ln.dat[pmatch.rm_so - 1], 433226035Sgabor "%lc", &wbegin) != 1) 434226035Sgabor r = REG_NOMATCH; 435226035Sgabor else if ((size_t)pmatch.rm_eo != 436322587Skevans pc->ln.len && 437322587Skevans sscanf(&pc->ln.dat[pmatch.rm_eo], 438226035Sgabor "%lc", &wend) != 1) 439226035Sgabor r = REG_NOMATCH; 440226035Sgabor else if (iswword(wbegin) || 441226035Sgabor iswword(wend)) 442226035Sgabor r = REG_NOMATCH; 443322583Skevans /* 444322583Skevans * If we're doing whole word matching and we 445322583Skevans * matched once, then we should try the pattern 446322587Skevans * again after advancing just past the start of 447322583Skevans * the earliest match. This allows the pattern 448322583Skevans * to match later on in the line and possibly 449322583Skevans * still match a whole word. 450322583Skevans */ 451322583Skevans if (r == REG_NOMATCH && 452322609Skevans (retry == pc->lnstart || 453322619Skevans (unsigned int)pmatch.rm_so + 1 < retry)) 454322583Skevans retry = pmatch.rm_so + 1; 455322587Skevans if (r == REG_NOMATCH) 456322587Skevans continue; 457210389Sgabor } 458322587Skevans lastmatches++; 459322587Skevans lastmatch = pmatch; 460322587Skevans 461322587Skevans if (matchidx == 0) 462322587Skevans c++; 463322587Skevans 464322587Skevans /* 465322587Skevans * Replace previous match if the new one is earlier 466322587Skevans * and/or longer. This will lead to some amount of 467322587Skevans * extra work if -o/--color are specified, but it's 468322587Skevans * worth it from a correctness point of view. 469322587Skevans */ 470322587Skevans if (matchidx > startm) { 471322587Skevans chkmatch = pc->matches[matchidx - 1]; 472322587Skevans if (pmatch.rm_so < chkmatch.rm_so || 473322587Skevans (pmatch.rm_so == chkmatch.rm_so && 474322587Skevans (pmatch.rm_eo - pmatch.rm_so) > 475322587Skevans (chkmatch.rm_eo - chkmatch.rm_so))) { 476322587Skevans pc->matches[matchidx - 1] = pmatch; 477322587Skevans nst = pmatch.rm_eo; 478322555Skevans } 479322587Skevans } else { 480322587Skevans /* Advance as normal if not */ 481322587Skevans pc->matches[matchidx++] = pmatch; 482322587Skevans nst = pmatch.rm_eo; 483210389Sgabor } 484322587Skevans /* avoid excessive matching - skip further patterns */ 485322587Skevans if ((color == NULL && !oflag) || qflag || lflag || 486322622Skevans matchidx >= MAX_MATCHES) { 487322609Skevans pc->lnstart = nst; 488322609Skevans lastmatches = 0; 489322587Skevans break; 490322609Skevans } 491226035Sgabor } 492210389Sgabor 493322583Skevans /* 494322583Skevans * Advance to just past the start of the earliest match, try 495322583Skevans * again just in case we still have a chance to match later in 496322583Skevans * the string. 497322583Skevans */ 498322609Skevans if (lastmatches == 0 && retry > pc->lnstart) { 499322583Skevans st = retry; 500322583Skevans continue; 501210389Sgabor } 502210389Sgabor 503226035Sgabor /* One pass if we are not recording matches */ 504270132Sgabor if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag)) 505226035Sgabor break; 506226035Sgabor 507322555Skevans /* If we didn't have any matches or REG_NOSUB set */ 508322555Skevans if (lastmatches == 0 || (cflags & REG_NOSUB)) 509322587Skevans nst = pc->ln.len; 510322555Skevans 511322555Skevans if (lastmatches == 0) 512322555Skevans /* No matches */ 513322555Skevans break; 514322555Skevans else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo) 515322555Skevans /* Zero-length match -- advance one more so we don't get stuck */ 516322555Skevans nst++; 517322555Skevans 518322555Skevans /* Advance st based on previous matches */ 519322555Skevans st = nst; 520322609Skevans pc->lnstart = st; 521226035Sgabor } 522226035Sgabor 523322587Skevans /* Reflect the new matchidx in the context */ 524322587Skevans pc->matchidx = matchidx; 525322583Skevans if (vflag) 526322583Skevans c = !c; 527322587Skevans return (c ? 0 : 1); 528210389Sgabor} 529210389Sgabor 530210389Sgabor/* 531210389Sgabor * Safe malloc() for internal use. 532210389Sgabor */ 533210389Sgaborvoid * 534210389Sgaborgrep_malloc(size_t size) 535210389Sgabor{ 536210389Sgabor void *ptr; 537210389Sgabor 538210389Sgabor if ((ptr = malloc(size)) == NULL) 539210389Sgabor err(2, "malloc"); 540210389Sgabor return (ptr); 541210389Sgabor} 542210389Sgabor 543210389Sgabor/* 544210389Sgabor * Safe calloc() for internal use. 545210389Sgabor */ 546210389Sgaborvoid * 547210389Sgaborgrep_calloc(size_t nmemb, size_t size) 548210389Sgabor{ 549210389Sgabor void *ptr; 550210389Sgabor 551210389Sgabor if ((ptr = calloc(nmemb, size)) == NULL) 552210389Sgabor err(2, "calloc"); 553210389Sgabor return (ptr); 554210389Sgabor} 555210389Sgabor 556210389Sgabor/* 557210389Sgabor * Safe realloc() for internal use. 558210389Sgabor */ 559210389Sgaborvoid * 560210389Sgaborgrep_realloc(void *ptr, size_t size) 561210389Sgabor{ 562210389Sgabor 563210389Sgabor if ((ptr = realloc(ptr, size)) == NULL) 564210389Sgabor err(2, "realloc"); 565210389Sgabor return (ptr); 566210389Sgabor} 567210389Sgabor 568210389Sgabor/* 569210578Sgabor * Safe strdup() for internal use. 570210578Sgabor */ 571210578Sgaborchar * 572210578Sgaborgrep_strdup(const char *str) 573210578Sgabor{ 574210578Sgabor char *ret; 575210578Sgabor 576210578Sgabor if ((ret = strdup(str)) == NULL) 577210578Sgabor err(2, "strdup"); 578210578Sgabor return (ret); 579210578Sgabor} 580210578Sgabor 581210578Sgabor/* 582322587Skevans * Print an entire line as-is, there are no inline matches to consider. This is 583322587Skevans * used for printing context. 584210389Sgabor */ 585322587Skevansvoid grep_printline(struct str *line, int sep) { 586322587Skevans printline_metadata(line, sep); 587322587Skevans fwrite(line->dat, line->len, 1, stdout); 588322587Skevans putchar(fileeol); 589322587Skevans} 590322587Skevans 591322587Skevansstatic void 592322587Skevansprintline_metadata(struct str *line, int sep) 593210389Sgabor{ 594322587Skevans bool printsep; 595210389Sgabor 596322587Skevans printsep = false; 597210389Sgabor if (!hflag) { 598228093Sgabor if (!nullflag) { 599210389Sgabor fputs(line->file, stdout); 600322587Skevans printsep = true; 601228093Sgabor } else { 602210389Sgabor printf("%s", line->file); 603210389Sgabor putchar(0); 604210389Sgabor } 605210389Sgabor } 606210389Sgabor if (nflag) { 607322587Skevans if (printsep) 608210389Sgabor putchar(sep); 609210389Sgabor printf("%d", line->line_no); 610322587Skevans printsep = true; 611210389Sgabor } 612210389Sgabor if (bflag) { 613322587Skevans if (printsep) 614210389Sgabor putchar(sep); 615322610Skevans printf("%lld", (long long)(line->off + line->boff)); 616322587Skevans printsep = true; 617210389Sgabor } 618322587Skevans if (printsep) 619210389Sgabor putchar(sep); 620322587Skevans} 621322587Skevans 622322587Skevans/* 623322587Skevans * Prints a matching line according to the command line options. 624322587Skevans */ 625322587Skevansstatic void 626322587Skevansprintline(struct parsec *pc, int sep) 627322587Skevans{ 628322587Skevans size_t a = 0; 629322587Skevans size_t i, matchidx; 630322587Skevans regmatch_t match; 631322587Skevans 632322587Skevans /* If matchall, everything matches but don't actually print for -o */ 633322587Skevans if (oflag && matchall) 634322587Skevans return; 635322587Skevans 636322587Skevans matchidx = pc->matchidx; 637322587Skevans 638210389Sgabor /* --color and -o */ 639322587Skevans if ((oflag || color) && matchidx > 0) { 640322610Skevans /* Only print metadata once per line if --color */ 641322610Skevans if (!oflag && pc->printed == 0) 642322610Skevans printline_metadata(&pc->ln, sep); 643322587Skevans for (i = 0; i < matchidx; i++) { 644322587Skevans match = pc->matches[i]; 645322555Skevans /* Don't output zero length matches */ 646322587Skevans if (match.rm_so == match.rm_eo) 647322555Skevans continue; 648322610Skevans /* 649322610Skevans * Metadata is printed on a per-line basis, so every 650322610Skevans * match gets file metadata with the -o flag. 651322610Skevans */ 652322610Skevans if (oflag) { 653322610Skevans pc->ln.boff = match.rm_so; 654322610Skevans printline_metadata(&pc->ln, sep); 655322610Skevans } else 656322587Skevans fwrite(pc->ln.dat + a, match.rm_so - a, 1, 657210389Sgabor stdout); 658322587Skevans if (color) 659210389Sgabor fprintf(stdout, "\33[%sm\33[K", color); 660322587Skevans fwrite(pc->ln.dat + match.rm_so, 661322587Skevans match.rm_eo - match.rm_so, 1, stdout); 662322587Skevans if (color) 663210389Sgabor fprintf(stdout, "\33[m\33[K"); 664322587Skevans a = match.rm_eo; 665210389Sgabor if (oflag) 666210389Sgabor putchar('\n'); 667210389Sgabor } 668210389Sgabor if (!oflag) { 669322587Skevans if (pc->ln.len - a > 0) 670322587Skevans fwrite(pc->ln.dat + a, pc->ln.len - a, 1, 671322587Skevans stdout); 672210389Sgabor putchar('\n'); 673210389Sgabor } 674322587Skevans } else 675322587Skevans grep_printline(&pc->ln, sep); 676322610Skevans pc->printed++; 677210389Sgabor} 678