10f66f451Sopenharmony_ci/* grep.c - show lines matching regular expressions 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2013 CE Strake <strake888 at gmail.com> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Posix doesn't even specify -r, documenting deviations from it is silly. 80f66f451Sopenharmony_ci* echo hello | grep -w '' 90f66f451Sopenharmony_ci* echo '' | grep -w '' 100f66f451Sopenharmony_ci* echo hello | grep -f </dev/null 110f66f451Sopenharmony_ci* 120f66f451Sopenharmony_ci 130f66f451Sopenharmony_ciUSE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) 140f66f451Sopenharmony_ciUSE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) 150f66f451Sopenharmony_ciUSE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ciconfig GREP 180f66f451Sopenharmony_ci bool "grep" 190f66f451Sopenharmony_ci default y 200f66f451Sopenharmony_ci help 210f66f451Sopenharmony_ci usage: grep [-EFrivwcloqsHbhn] [-ABC NUM] [-m MAX] [-e REGEX]... [-MS PATTERN]... [-f REGFILE] [FILE]... 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci Show lines matching regular expressions. If no -e, first argument is 240f66f451Sopenharmony_ci regular expression to match. With no files (or "-" filename) read stdin. 250f66f451Sopenharmony_ci Returns 0 if matched, 1 if no match found, 2 for command errors. 260f66f451Sopenharmony_ci 270f66f451Sopenharmony_ci -e Regex to match. (May be repeated.) 280f66f451Sopenharmony_ci -f File listing regular expressions to match. 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ci file search: 310f66f451Sopenharmony_ci -r Recurse into subdirectories (defaults FILE to ".") 320f66f451Sopenharmony_ci -R Recurse into subdirectories and symlinks to directories 330f66f451Sopenharmony_ci -M Match filename pattern (--include) 340f66f451Sopenharmony_ci -S Skip filename pattern (--exclude) 350f66f451Sopenharmony_ci --exclude-dir=PATTERN Skip directory pattern 360f66f451Sopenharmony_ci -I Ignore binary files 370f66f451Sopenharmony_ci 380f66f451Sopenharmony_ci match type: 390f66f451Sopenharmony_ci -A Show NUM lines after -B Show NUM lines before match 400f66f451Sopenharmony_ci -C NUM lines context (A+B) -E extended regex syntax 410f66f451Sopenharmony_ci -F fixed (literal match) -a always text (not binary) 420f66f451Sopenharmony_ci -i case insensitive -m match MAX many lines 430f66f451Sopenharmony_ci -v invert match -w whole word (implies -E) 440f66f451Sopenharmony_ci -x whole line -z input NUL terminated 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci display modes: (default: matched line) 470f66f451Sopenharmony_ci -c count of matching lines -l show only matching filenames 480f66f451Sopenharmony_ci -o only matching part -q quiet (errors only) 490f66f451Sopenharmony_ci -s silent (no error msg) -Z output NUL terminated 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci output prefix (default: filename if checking more than 1 file) 520f66f451Sopenharmony_ci -H force filename -b byte offset of match 530f66f451Sopenharmony_ci -h hide filename -n line number of match 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ciconfig EGREP 560f66f451Sopenharmony_ci bool 570f66f451Sopenharmony_ci default y 580f66f451Sopenharmony_ci depends on GREP 590f66f451Sopenharmony_ci 600f66f451Sopenharmony_ciconfig FGREP 610f66f451Sopenharmony_ci bool 620f66f451Sopenharmony_ci default y 630f66f451Sopenharmony_ci depends on GREP 640f66f451Sopenharmony_ci*/ 650f66f451Sopenharmony_ci 660f66f451Sopenharmony_ci#define FOR_grep 670f66f451Sopenharmony_ci#include "toys.h" 680f66f451Sopenharmony_ci#include <regex.h> 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ciGLOBALS( 710f66f451Sopenharmony_ci long m, A, B, C; 720f66f451Sopenharmony_ci struct arg_list *f, *e, *M, *S, *exclude_dir; 730f66f451Sopenharmony_ci char *color; 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_ci char *purple, *cyan, *red, *green, *grey; 760f66f451Sopenharmony_ci struct double_list *reg; 770f66f451Sopenharmony_ci char indelim, outdelim; 780f66f451Sopenharmony_ci int found, tried; 790f66f451Sopenharmony_ci) 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_cistruct reg { 820f66f451Sopenharmony_ci struct reg *next, *prev; 830f66f451Sopenharmony_ci int rc; 840f66f451Sopenharmony_ci regex_t r; 850f66f451Sopenharmony_ci regmatch_t m; 860f66f451Sopenharmony_ci}; 870f66f451Sopenharmony_ci 880f66f451Sopenharmony_cistatic void numdash(long num, char dash) 890f66f451Sopenharmony_ci{ 900f66f451Sopenharmony_ci printf("%s%ld%s%c", TT.green, num, TT.cyan, dash); 910f66f451Sopenharmony_ci} 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci// Emit line with various potential prefixes and delimiter 940f66f451Sopenharmony_cistatic void outline(char *line, char dash, char *name, long lcount, long bcount, 950f66f451Sopenharmony_ci unsigned trim) 960f66f451Sopenharmony_ci{ 970f66f451Sopenharmony_ci if (!trim && FLAG(o)) return; 980f66f451Sopenharmony_ci if (name && FLAG(H)) printf("%s%s%s%c", TT.purple, name, TT.cyan, dash); 990f66f451Sopenharmony_ci if (FLAG(c)) { 1000f66f451Sopenharmony_ci printf("%s%ld", TT.grey, lcount); 1010f66f451Sopenharmony_ci xputc(TT.outdelim); 1020f66f451Sopenharmony_ci } else if (lcount && FLAG(n)) numdash(lcount, dash); 1030f66f451Sopenharmony_ci if (bcount && FLAG(b)) numdash(bcount-1, dash); 1040f66f451Sopenharmony_ci if (line) { 1050f66f451Sopenharmony_ci if (FLAG(color)) xputsn(FLAG(o) ? TT.red : TT.grey); 1060f66f451Sopenharmony_ci // support embedded NUL bytes in output 1070f66f451Sopenharmony_ci xputsl(line, trim); 1080f66f451Sopenharmony_ci xputc(TT.outdelim); 1090f66f451Sopenharmony_ci } 1100f66f451Sopenharmony_ci} 1110f66f451Sopenharmony_ci 1120f66f451Sopenharmony_ci// Show matches in one file 1130f66f451Sopenharmony_cistatic void do_grep(int fd, char *name) 1140f66f451Sopenharmony_ci{ 1150f66f451Sopenharmony_ci long lcount = 0, mcount = 0, offset = 0, after = 0, before = 0; 1160f66f451Sopenharmony_ci struct double_list *dlb = 0; 1170f66f451Sopenharmony_ci char *bars = 0; 1180f66f451Sopenharmony_ci FILE *file; 1190f66f451Sopenharmony_ci int bin = 0; 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci if (!FLAG(r)) TT.tried++; 1220f66f451Sopenharmony_ci if (!fd) name = "(standard input)"; 1230f66f451Sopenharmony_ci 1240f66f451Sopenharmony_ci // Only run binary file check on lseekable files. 1250f66f451Sopenharmony_ci if (!FLAG(a) && !lseek(fd, 0, SEEK_CUR)) { 1260f66f451Sopenharmony_ci char buf[256]; 1270f66f451Sopenharmony_ci int len, i = 0; 1280f66f451Sopenharmony_ci unsigned wc; 1290f66f451Sopenharmony_ci 1300f66f451Sopenharmony_ci // If the first 256 bytes don't parse as utf8, call it binary. 1310f66f451Sopenharmony_ci if (0<(len = read(fd, buf, 256))) { 1320f66f451Sopenharmony_ci lseek(fd, -len, SEEK_CUR); 1330f66f451Sopenharmony_ci while (i<len) { 1340f66f451Sopenharmony_ci bin = utf8towc(&wc, buf+i, len-i); 1350f66f451Sopenharmony_ci if (bin == -2) i = len; 1360f66f451Sopenharmony_ci if (bin<1) break; 1370f66f451Sopenharmony_ci i += bin; 1380f66f451Sopenharmony_ci } 1390f66f451Sopenharmony_ci bin = i!=len; 1400f66f451Sopenharmony_ci } 1410f66f451Sopenharmony_ci if (bin && FLAG(I)) return; 1420f66f451Sopenharmony_ci } 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci if (!(file = fdopen(fd, "r"))) return perror_msg("%s", name); 1450f66f451Sopenharmony_ci 1460f66f451Sopenharmony_ci // Loop through lines of input 1470f66f451Sopenharmony_ci for (;;) { 1480f66f451Sopenharmony_ci char *line = 0, *start; 1490f66f451Sopenharmony_ci struct reg *shoe; 1500f66f451Sopenharmony_ci size_t ulen; 1510f66f451Sopenharmony_ci long len; 1520f66f451Sopenharmony_ci int matched = 0, rc = 1; 1530f66f451Sopenharmony_ci 1540f66f451Sopenharmony_ci // get next line, check and trim delimiter 1550f66f451Sopenharmony_ci lcount++; 1560f66f451Sopenharmony_ci errno = 0; 1570f66f451Sopenharmony_ci ulen = len = getdelim(&line, &ulen, TT.indelim, file); 1580f66f451Sopenharmony_ci if (errno) perror_msg("%s", name); 1590f66f451Sopenharmony_ci if (len<1) break; 1600f66f451Sopenharmony_ci if (line[ulen-1] == TT.indelim) line[--ulen] = 0; 1610f66f451Sopenharmony_ci 1620f66f451Sopenharmony_ci // Prepare for next line 1630f66f451Sopenharmony_ci start = line; 1640f66f451Sopenharmony_ci if (TT.reg) for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) 1650f66f451Sopenharmony_ci shoe->rc = 0; 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_ci // Loop to handle multiple matches in same line 1680f66f451Sopenharmony_ci do { 1690f66f451Sopenharmony_ci regmatch_t *mm = (void *)toybuf; 1700f66f451Sopenharmony_ci 1710f66f451Sopenharmony_ci // Handle "fixed" (literal) matches 1720f66f451Sopenharmony_ci if (FLAG(F)) { 1730f66f451Sopenharmony_ci struct arg_list *seek, fseek; 1740f66f451Sopenharmony_ci char *s = 0; 1750f66f451Sopenharmony_ci 1760f66f451Sopenharmony_ci for (seek = TT.e; seek; seek = seek->next) { 1770f66f451Sopenharmony_ci if (FLAG(x)) { 1780f66f451Sopenharmony_ci if (!(FLAG(i) ? strcasecmp : strcmp)(seek->arg, line)) s = line; 1790f66f451Sopenharmony_ci } else if (!*seek->arg) { 1800f66f451Sopenharmony_ci // No need to set fseek.next because this will match every line. 1810f66f451Sopenharmony_ci seek = &fseek; 1820f66f451Sopenharmony_ci fseek.arg = s = line; 1830f66f451Sopenharmony_ci } else if (FLAG(i)) s = strcasestr(start, seek->arg); 1840f66f451Sopenharmony_ci else s = strstr(start, seek->arg); 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci if (s) break; 1870f66f451Sopenharmony_ci } 1880f66f451Sopenharmony_ci 1890f66f451Sopenharmony_ci if (s) { 1900f66f451Sopenharmony_ci rc = 0; 1910f66f451Sopenharmony_ci mm->rm_so = (s-start); 1920f66f451Sopenharmony_ci mm->rm_eo = (s-start)+strlen(seek->arg); 1930f66f451Sopenharmony_ci } else rc = 1; 1940f66f451Sopenharmony_ci 1950f66f451Sopenharmony_ci // Handle regex matches 1960f66f451Sopenharmony_ci } else { 1970f66f451Sopenharmony_ci int baseline = mm->rm_eo; 1980f66f451Sopenharmony_ci 1990f66f451Sopenharmony_ci mm->rm_so = mm->rm_eo = INT_MAX; 2000f66f451Sopenharmony_ci rc = 1; 2010f66f451Sopenharmony_ci for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) { 2020f66f451Sopenharmony_ci 2030f66f451Sopenharmony_ci // Do we need to re-check this regex? 2040f66f451Sopenharmony_ci if (!shoe->rc) { 2050f66f451Sopenharmony_ci shoe->m.rm_so -= baseline; 2060f66f451Sopenharmony_ci shoe->m.rm_eo -= baseline; 2070f66f451Sopenharmony_ci if (!matched || shoe->m.rm_so<0) 2080f66f451Sopenharmony_ci shoe->rc = regexec0(&shoe->r, start, ulen-(start-line), 1, 2090f66f451Sopenharmony_ci &shoe->m, start==line ? 0 : REG_NOTBOL); 2100f66f451Sopenharmony_ci } 2110f66f451Sopenharmony_ci 2120f66f451Sopenharmony_ci // If we got a match, is it a _better_ match? 2130f66f451Sopenharmony_ci if (!shoe->rc && (shoe->m.rm_so < mm->rm_so || 2140f66f451Sopenharmony_ci (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= mm->rm_eo))) 2150f66f451Sopenharmony_ci { 2160f66f451Sopenharmony_ci mm = &shoe->m; 2170f66f451Sopenharmony_ci rc = 0; 2180f66f451Sopenharmony_ci } 2190f66f451Sopenharmony_ci } 2200f66f451Sopenharmony_ci } 2210f66f451Sopenharmony_ci 2220f66f451Sopenharmony_ci if (!rc && FLAG(x)) 2230f66f451Sopenharmony_ci if (mm->rm_so || line[mm->rm_eo]) rc = 1; 2240f66f451Sopenharmony_ci 2250f66f451Sopenharmony_ci if (!rc && FLAG(w)) { 2260f66f451Sopenharmony_ci char c = 0; 2270f66f451Sopenharmony_ci 2280f66f451Sopenharmony_ci if ((start+mm->rm_so)!=line) { 2290f66f451Sopenharmony_ci c = start[mm->rm_so-1]; 2300f66f451Sopenharmony_ci if (!isalnum(c) && c != '_') c = 0; 2310f66f451Sopenharmony_ci } 2320f66f451Sopenharmony_ci if (!c) { 2330f66f451Sopenharmony_ci c = start[mm->rm_eo]; 2340f66f451Sopenharmony_ci if (!isalnum(c) && c != '_') c = 0; 2350f66f451Sopenharmony_ci } 2360f66f451Sopenharmony_ci if (c) { 2370f66f451Sopenharmony_ci start += mm->rm_so+1; 2380f66f451Sopenharmony_ci continue; 2390f66f451Sopenharmony_ci } 2400f66f451Sopenharmony_ci } 2410f66f451Sopenharmony_ci 2420f66f451Sopenharmony_ci if (FLAG(v)) { 2430f66f451Sopenharmony_ci if (FLAG(o)) { 2440f66f451Sopenharmony_ci if (rc) { 2450f66f451Sopenharmony_ci mm->rm_so = 0; 2460f66f451Sopenharmony_ci mm->rm_eo = ulen-(start-line); 2470f66f451Sopenharmony_ci } else if (!mm->rm_so) { 2480f66f451Sopenharmony_ci start += mm->rm_eo; 2490f66f451Sopenharmony_ci continue; 2500f66f451Sopenharmony_ci } else mm->rm_eo = mm->rm_so; 2510f66f451Sopenharmony_ci } else { 2520f66f451Sopenharmony_ci if (!rc) break; 2530f66f451Sopenharmony_ci mm->rm_eo = ulen-(start-line); 2540f66f451Sopenharmony_ci } 2550f66f451Sopenharmony_ci mm->rm_so = 0; 2560f66f451Sopenharmony_ci } else if (rc) break; 2570f66f451Sopenharmony_ci 2580f66f451Sopenharmony_ci // At least one line we didn't print since match while -ABC active 2590f66f451Sopenharmony_ci if (bars) { 2600f66f451Sopenharmony_ci xputs(bars); 2610f66f451Sopenharmony_ci bars = 0; 2620f66f451Sopenharmony_ci } 2630f66f451Sopenharmony_ci matched++; 2640f66f451Sopenharmony_ci TT.found = 1; 2650f66f451Sopenharmony_ci if (FLAG(q)) { 2660f66f451Sopenharmony_ci toys.exitval = 0; 2670f66f451Sopenharmony_ci xexit(); 2680f66f451Sopenharmony_ci } 2690f66f451Sopenharmony_ci if (FLAG(l)) { 2700f66f451Sopenharmony_ci xprintf("%s%c", name, TT.outdelim); 2710f66f451Sopenharmony_ci free(line); 2720f66f451Sopenharmony_ci fclose(file); 2730f66f451Sopenharmony_ci return; 2740f66f451Sopenharmony_ci } 2750f66f451Sopenharmony_ci 2760f66f451Sopenharmony_ci if (!FLAG(c)) { 2770f66f451Sopenharmony_ci long bcount = 1 + offset + (start-line) + (FLAG(o) ? mm->rm_so : 0); 2780f66f451Sopenharmony_ci 2790f66f451Sopenharmony_ci if (bin) printf("Binary file %s matches\n", name); 2800f66f451Sopenharmony_ci else if (FLAG(o)) 2810f66f451Sopenharmony_ci outline(start+mm->rm_so, ':', name, lcount, bcount, 2820f66f451Sopenharmony_ci mm->rm_eo-mm->rm_so); 2830f66f451Sopenharmony_ci else { 2840f66f451Sopenharmony_ci while (dlb) { 2850f66f451Sopenharmony_ci struct double_list *dl = dlist_pop(&dlb); 2860f66f451Sopenharmony_ci unsigned *uu = (void *)(dl->data+(strlen(dl->data)|3)+1); 2870f66f451Sopenharmony_ci 2880f66f451Sopenharmony_ci outline(dl->data, '-', name, lcount-before, uu[0]+1, uu[1]); 2890f66f451Sopenharmony_ci free(dl->data); 2900f66f451Sopenharmony_ci free(dl); 2910f66f451Sopenharmony_ci before--; 2920f66f451Sopenharmony_ci } 2930f66f451Sopenharmony_ci 2940f66f451Sopenharmony_ci if (matched==1) 2950f66f451Sopenharmony_ci outline(FLAG(color) ? 0 : line, ':', name, lcount, bcount, ulen); 2960f66f451Sopenharmony_ci if (FLAG(color)) { 2970f66f451Sopenharmony_ci xputsn(TT.grey); 2980f66f451Sopenharmony_ci if (mm->rm_so) xputsl(line, mm->rm_so); 2990f66f451Sopenharmony_ci xputsn(TT.red); 3000f66f451Sopenharmony_ci xputsl(line+mm->rm_so, mm->rm_eo-mm->rm_so); 3010f66f451Sopenharmony_ci } 3020f66f451Sopenharmony_ci 3030f66f451Sopenharmony_ci if (TT.A) after = TT.A+1; 3040f66f451Sopenharmony_ci } 3050f66f451Sopenharmony_ci } 3060f66f451Sopenharmony_ci 3070f66f451Sopenharmony_ci start += mm->rm_eo; 3080f66f451Sopenharmony_ci if (mm->rm_so == mm->rm_eo) break; 3090f66f451Sopenharmony_ci if (!FLAG(o) && FLAG(color)) break; 3100f66f451Sopenharmony_ci } while (*start); 3110f66f451Sopenharmony_ci offset += len; 3120f66f451Sopenharmony_ci 3130f66f451Sopenharmony_ci if (matched) { 3140f66f451Sopenharmony_ci // Finish off pending line color fragment. 3150f66f451Sopenharmony_ci if (FLAG(color) && !FLAG(o)) { 3160f66f451Sopenharmony_ci xputsn(TT.grey); 3170f66f451Sopenharmony_ci if (ulen > start-line) xputsl(start, ulen-(start-line)); 3180f66f451Sopenharmony_ci xputc(TT.outdelim); 3190f66f451Sopenharmony_ci } 3200f66f451Sopenharmony_ci mcount++; 3210f66f451Sopenharmony_ci } else { 3220f66f451Sopenharmony_ci int discard = (after || TT.B); 3230f66f451Sopenharmony_ci 3240f66f451Sopenharmony_ci if (after && --after) { 3250f66f451Sopenharmony_ci outline(line, '-', name, lcount, 0, ulen); 3260f66f451Sopenharmony_ci discard = 0; 3270f66f451Sopenharmony_ci } 3280f66f451Sopenharmony_ci if (discard && TT.B) { 3290f66f451Sopenharmony_ci unsigned *uu, ul = (ulen|3)+1; 3300f66f451Sopenharmony_ci 3310f66f451Sopenharmony_ci line = xrealloc(line, ul+8); 3320f66f451Sopenharmony_ci uu = (void *)(line+ul); 3330f66f451Sopenharmony_ci uu[0] = offset-len; 3340f66f451Sopenharmony_ci uu[1] = ulen; 3350f66f451Sopenharmony_ci dlist_add(&dlb, line); 3360f66f451Sopenharmony_ci line = 0; 3370f66f451Sopenharmony_ci if (++before>TT.B) { 3380f66f451Sopenharmony_ci struct double_list *dl; 3390f66f451Sopenharmony_ci 3400f66f451Sopenharmony_ci dl = dlist_pop(&dlb); 3410f66f451Sopenharmony_ci free(dl->data); 3420f66f451Sopenharmony_ci free(dl); 3430f66f451Sopenharmony_ci before--; 3440f66f451Sopenharmony_ci } else discard = 0; 3450f66f451Sopenharmony_ci } 3460f66f451Sopenharmony_ci // If we discarded a line while displaying context, show bars before next 3470f66f451Sopenharmony_ci // line (but don't show them now in case that was last match in file) 3480f66f451Sopenharmony_ci if (discard && mcount) bars = "--"; 3490f66f451Sopenharmony_ci } 3500f66f451Sopenharmony_ci free(line); 3510f66f451Sopenharmony_ci 3520f66f451Sopenharmony_ci if (FLAG(m) && mcount >= TT.m) break; 3530f66f451Sopenharmony_ci } 3540f66f451Sopenharmony_ci 3550f66f451Sopenharmony_ci if (FLAG(c)) outline(0, ':', name, mcount, 0, 1); 3560f66f451Sopenharmony_ci 3570f66f451Sopenharmony_ci // loopfiles will also close the fd, but this frees an (opaque) struct. 3580f66f451Sopenharmony_ci fclose(file); 3590f66f451Sopenharmony_ci while (dlb) { 3600f66f451Sopenharmony_ci struct double_list *dl = dlist_pop(&dlb); 3610f66f451Sopenharmony_ci 3620f66f451Sopenharmony_ci free(dl->data); 3630f66f451Sopenharmony_ci free(dl); 3640f66f451Sopenharmony_ci } 3650f66f451Sopenharmony_ci} 3660f66f451Sopenharmony_ci 3670f66f451Sopenharmony_cistatic void parse_regex(void) 3680f66f451Sopenharmony_ci{ 3690f66f451Sopenharmony_ci struct arg_list *al, *new, *list = NULL; 3700f66f451Sopenharmony_ci char *s, *ss; 3710f66f451Sopenharmony_ci 3720f66f451Sopenharmony_ci // Add all -f lines to -e list. (Yes, this is leaking allocation context for 3730f66f451Sopenharmony_ci // exit to free. Not supporting nofork for this command any time soon.) 3740f66f451Sopenharmony_ci al = TT.f ? TT.f : TT.e; 3750f66f451Sopenharmony_ci while (al) { 3760f66f451Sopenharmony_ci if (TT.f) s = ss = xreadfile(al->arg, 0, 0); 3770f66f451Sopenharmony_ci else s = ss = al->arg; 3780f66f451Sopenharmony_ci 3790f66f451Sopenharmony_ci // Split lines at \n, add individual lines to new list. 3800f66f451Sopenharmony_ci do { 3810f66f451Sopenharmony_ci// TODO: NUL terminated input shouldn't split -e at \n 3820f66f451Sopenharmony_ci ss = strchr(s, '\n'); 3830f66f451Sopenharmony_ci if (ss) *(ss++) = 0; 3840f66f451Sopenharmony_ci new = xmalloc(sizeof(struct arg_list)); 3850f66f451Sopenharmony_ci new->next = list; 3860f66f451Sopenharmony_ci new->arg = s; 3870f66f451Sopenharmony_ci list = new; 3880f66f451Sopenharmony_ci s = ss; 3890f66f451Sopenharmony_ci } while (ss && *s); 3900f66f451Sopenharmony_ci 3910f66f451Sopenharmony_ci // Advance, when we run out of -f switch to -e. 3920f66f451Sopenharmony_ci al = al->next; 3930f66f451Sopenharmony_ci if (!al && TT.f) { 3940f66f451Sopenharmony_ci TT.f = 0; 3950f66f451Sopenharmony_ci al = TT.e; 3960f66f451Sopenharmony_ci } 3970f66f451Sopenharmony_ci } 3980f66f451Sopenharmony_ci TT.e = list; 3990f66f451Sopenharmony_ci 4000f66f451Sopenharmony_ci if (!FLAG(F)) { 4010f66f451Sopenharmony_ci // Convert regex list 4020f66f451Sopenharmony_ci for (al = TT.e; al; al = al->next) { 4030f66f451Sopenharmony_ci struct reg *shoe; 4040f66f451Sopenharmony_ci 4050f66f451Sopenharmony_ci if (FLAG(o) && !*al->arg) continue; 4060f66f451Sopenharmony_ci dlist_add_nomalloc(&TT.reg, (void *)(shoe = xmalloc(sizeof(struct reg)))); 4070f66f451Sopenharmony_ci xregcomp(&shoe->r, al->arg, 4080f66f451Sopenharmony_ci (REG_EXTENDED*!!FLAG(E))|(REG_ICASE*!!FLAG(i))); 4090f66f451Sopenharmony_ci } 4100f66f451Sopenharmony_ci dlist_terminate(TT.reg); 4110f66f451Sopenharmony_ci } 4120f66f451Sopenharmony_ci} 4130f66f451Sopenharmony_ci 4140f66f451Sopenharmony_cistatic int do_grep_r(struct dirtree *new) 4150f66f451Sopenharmony_ci{ 4160f66f451Sopenharmony_ci struct arg_list *al; 4170f66f451Sopenharmony_ci char *name; 4180f66f451Sopenharmony_ci 4190f66f451Sopenharmony_ci if (!new->parent) TT.tried++; 4200f66f451Sopenharmony_ci if (!dirtree_notdotdot(new)) return 0; 4210f66f451Sopenharmony_ci if (S_ISDIR(new->st.st_mode)) { 4220f66f451Sopenharmony_ci for (al = TT.exclude_dir; al; al = al->next) 4230f66f451Sopenharmony_ci if (!fnmatch(al->arg, new->name, 0)) return 0; 4240f66f451Sopenharmony_ci return DIRTREE_RECURSE|(FLAG(R)?DIRTREE_SYMFOLLOW:0); 4250f66f451Sopenharmony_ci } 4260f66f451Sopenharmony_ci if (TT.S || TT.M) { 4270f66f451Sopenharmony_ci for (al = TT.S; al; al = al->next) 4280f66f451Sopenharmony_ci if (!fnmatch(al->arg, new->name, 0)) return 0; 4290f66f451Sopenharmony_ci 4300f66f451Sopenharmony_ci if (TT.M) { 4310f66f451Sopenharmony_ci for (al = TT.M; al; al = al->next) 4320f66f451Sopenharmony_ci if (!fnmatch(al->arg, new->name, 0)) break; 4330f66f451Sopenharmony_ci 4340f66f451Sopenharmony_ci if (!al) return 0; 4350f66f451Sopenharmony_ci } 4360f66f451Sopenharmony_ci } 4370f66f451Sopenharmony_ci 4380f66f451Sopenharmony_ci // "grep -r onefile" doesn't show filenames, but "grep -r onedir" should. 4390f66f451Sopenharmony_ci if (new->parent && !FLAG(h)) toys.optflags |= FLAG_H; 4400f66f451Sopenharmony_ci 4410f66f451Sopenharmony_ci name = dirtree_path(new, 0); 4420f66f451Sopenharmony_ci do_grep(openat(dirtree_parentfd(new), new->name, 0), name); 4430f66f451Sopenharmony_ci free(name); 4440f66f451Sopenharmony_ci 4450f66f451Sopenharmony_ci return 0; 4460f66f451Sopenharmony_ci} 4470f66f451Sopenharmony_ci 4480f66f451Sopenharmony_civoid grep_main(void) 4490f66f451Sopenharmony_ci{ 4500f66f451Sopenharmony_ci char **ss = toys.optargs; 4510f66f451Sopenharmony_ci 4520f66f451Sopenharmony_ci if (FLAG(color) && (!TT.color || !strcmp(TT.color, "auto")) && !isatty(1)) 4530f66f451Sopenharmony_ci toys.optflags &= ~FLAG_color; 4540f66f451Sopenharmony_ci 4550f66f451Sopenharmony_ci if (FLAG(color)) { 4560f66f451Sopenharmony_ci TT.purple = "\033[35m"; 4570f66f451Sopenharmony_ci TT.cyan = "\033[36m"; 4580f66f451Sopenharmony_ci TT.red = "\033[1;31m"; 4590f66f451Sopenharmony_ci TT.green = "\033[32m"; 4600f66f451Sopenharmony_ci TT.grey = "\033[0m"; 4610f66f451Sopenharmony_ci } else TT.purple = TT.cyan = TT.red = TT.green = TT.grey = ""; 4620f66f451Sopenharmony_ci 4630f66f451Sopenharmony_ci if (FLAG(R)) toys.optflags |= FLAG_r; 4640f66f451Sopenharmony_ci 4650f66f451Sopenharmony_ci // Grep exits with 2 for errors 4660f66f451Sopenharmony_ci toys.exitval = 2; 4670f66f451Sopenharmony_ci 4680f66f451Sopenharmony_ci if (!TT.A) TT.A = TT.C; 4690f66f451Sopenharmony_ci if (!TT.B) TT.B = TT.C; 4700f66f451Sopenharmony_ci 4710f66f451Sopenharmony_ci TT.indelim = '\n' * !FLAG(z); 4720f66f451Sopenharmony_ci TT.outdelim = '\n' * !FLAG(Z); 4730f66f451Sopenharmony_ci 4740f66f451Sopenharmony_ci // Handle egrep and fgrep 4750f66f451Sopenharmony_ci if (*toys.which->name == 'e') toys.optflags |= FLAG_E; 4760f66f451Sopenharmony_ci if (*toys.which->name == 'f') toys.optflags |= FLAG_F; 4770f66f451Sopenharmony_ci 4780f66f451Sopenharmony_ci if (!TT.e && !TT.f) { 4790f66f451Sopenharmony_ci if (!*ss) error_exit("no REGEX"); 4800f66f451Sopenharmony_ci TT.e = xzalloc(sizeof(struct arg_list)); 4810f66f451Sopenharmony_ci TT.e->arg = *(ss++); 4820f66f451Sopenharmony_ci toys.optc--; 4830f66f451Sopenharmony_ci } 4840f66f451Sopenharmony_ci 4850f66f451Sopenharmony_ci parse_regex(); 4860f66f451Sopenharmony_ci 4870f66f451Sopenharmony_ci if (!FLAG(h) && toys.optc>1) toys.optflags |= FLAG_H; 4880f66f451Sopenharmony_ci 4890f66f451Sopenharmony_ci if (FLAG(s)) { 4900f66f451Sopenharmony_ci close(2); 4910f66f451Sopenharmony_ci xopen_stdio("/dev/null", O_RDWR); 4920f66f451Sopenharmony_ci } 4930f66f451Sopenharmony_ci 4940f66f451Sopenharmony_ci if (FLAG(r)) { 4950f66f451Sopenharmony_ci // Iterate through -r arguments. Use "." as default if none provided. 4960f66f451Sopenharmony_ci for (ss = *ss ? ss : (char *[]){".", 0}; *ss; ss++) { 4970f66f451Sopenharmony_ci if (!strcmp(*ss, "-")) do_grep(0, *ss); 4980f66f451Sopenharmony_ci else dirtree_read(*ss, do_grep_r); 4990f66f451Sopenharmony_ci } 5000f66f451Sopenharmony_ci } else loopfiles_rw(ss, O_RDONLY|WARN_ONLY, 0, do_grep); 5010f66f451Sopenharmony_ci if (TT.tried >= toys.optc || (FLAG(q)&&TT.found)) toys.exitval = !TT.found; 5020f66f451Sopenharmony_ci} 503