From bb184f1a4e8a27d3b5608bcedbb62e7ca4a200fd Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 23 Dec 2018 16:22:16 -0600 Subject: Add grep --color --- lib/lib.h | 2 ++ lib/xwrap.c | 37 +++++++++++++++----- toys/posix/grep.c | 101 ++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 103 insertions(+), 37 deletions(-) diff --git a/lib/lib.h b/lib/lib.h index 5d806508..10308ff6 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -125,6 +125,8 @@ char *xstrdup(char *s); void *xmemdup(void *s, long len); char *xmprintf(char *format, ...) printf_format; void xprintf(char *format, ...) printf_format; +void xputsl(char *s, int len); +void xputsn(char *s); void xputs(char *s); void xputc(char c); void xflush(void); diff --git a/lib/xwrap.c b/lib/xwrap.c index b2416a4e..7e602d5f 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -141,6 +141,11 @@ char *xmprintf(char *format, ...) return ret; } +void xflush(void) +{ + if (fflush(0) || ferror(stdout)) perror_exit("write"); +} + void xprintf(char *format, ...) { va_list va; @@ -148,23 +153,39 @@ void xprintf(char *format, ...) vprintf(format, va); va_end(va); - if (fflush(stdout) || ferror(stdout)) perror_exit("write"); + xflush(); } -void xputs(char *s) +// Put string with length (does not append newline) +void xputsl(char *s, int len) +{ + int out; + + while (len != (out = fwrite(s, 1, len, stdout))) { + if (out<1) perror_exit("write"); + len -= out; + s += out; + } + xflush(); +} + +// xputs with no newline +void xputsn(char *s) { - if (EOF == puts(s) || fflush(stdout) || ferror(stdout)) perror_exit("write"); + xputsl(s, strlen(s)); } -void xputc(char c) +// Write string to stdout with newline, flushing and checking for errors +void xputs(char *s) { - if (EOF == fputc(c, stdout) || fflush(stdout) || ferror(stdout)) - perror_exit("write"); + puts(s); + xflush(); } -void xflush(void) +void xputc(char c) { - if (fflush(stdout) || ferror(stdout)) perror_exit("write");; + if (EOF == fputc(c, stdout)) perror_exit("write"); + xflush(); } // This is called through the XVFORK macro because parent/child of vfork diff --git a/toys/posix/grep.c b/toys/posix/grep.c index 105ab3bc..2cf078b1 100644 --- a/toys/posix/grep.c +++ b/toys/posix/grep.c @@ -4,11 +4,13 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html * - * TODO: --color - * * Posix doesn't even specify -r, documenting deviations from it is silly. +* echo hello | grep -w '' +* echo '' | grep -w '' +* echo hello | grep -f rm_so = (s-line); - skip = mm->rm_eo = (s-line)+strlen(seek->arg); + mm->rm_eo = (s-line)+strlen(seek->arg); } else rc = 1; // Handle regex matches } else { - mm->rm_so = INT_MAX; + int baseline = mm->rm_eo; + + mm->rm_so = mm->rm_eo = INT_MAX; rc = 1; for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) { @@ -195,14 +208,12 @@ static void do_grep(int fd, char *name) // If we got a match, is it a _better_ match? if (!shoe->rc && (shoe->m.rm_so < mm->rm_so || - (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= skip))) + (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= mm->rm_eo))) { mm = &shoe->m; - skip = mm->rm_eo; rc = 0; } } - baseline = skip; } if (!rc && FLAG(x)) @@ -221,21 +232,22 @@ static void do_grep(int fd, char *name) } if (c) { start += mm->rm_so+1; - continue; } } if (FLAG(v)) { if (FLAG(o)) { - if (rc) skip = mm->rm_eo = strlen(start); - else if (!mm->rm_so) { - start += skip; + if (rc) { + mm->rm_so = 0; + mm->rm_eo = ulen-(start-line); + } else if (!mm->rm_so) { + start += mm->rm_eo; continue; } else mm->rm_eo = mm->rm_so; } else { if (!rc) break; - mm->rm_eo = strlen(start); + mm->rm_eo = ulen-(start-line); } mm->rm_so = 0; } else if (rc) break; @@ -260,12 +272,16 @@ static void do_grep(int fd, char *name) if (FLAG(o)) if (mm->rm_eo == mm->rm_so) break; +// TODO checking this twice if (!FLAG(c)) { long bcount = 1 + offset + (start-line) + (FLAG(o) ? mm->rm_so : 0); if (bin) printf("Binary file %s matches\n", name); - else if (!FLAG(o)) { + else if (FLAG(o)) + outline(start+mm->rm_so, ':', name, lcount, bcount, + mm->rm_eo-mm->rm_so); + else { while (dlb) { struct double_list *dl = dlist_pop(&dlb); unsigned *uu = (void *)(dl->data+((strlen(dl->data)+1)|3)+1); @@ -276,19 +292,34 @@ static void do_grep(int fd, char *name) before--; } - outline(line, ':', name, lcount, bcount, ulen); + if (matched==1) + outline(FLAG(color) ? 0 : line, ':', name, lcount, bcount, ulen); + if (FLAG(color)) { + xputsn(TT.grey); + if (mm->rm_so) xputsl(line, mm->rm_so); + xputsn(TT.red); + xputsl(line+mm->rm_so, mm->rm_eo-mm->rm_so); + } + if (TT.A) after = TT.A+1; - } else outline(start+mm->rm_so, ':', name, lcount, bcount, - mm->rm_eo-mm->rm_so); + } } - start += skip; - if (!FLAG(o)) break; + start += mm->rm_eo; + if (mm->rm_so == mm->rm_eo) break; + if (!FLAG(o) && FLAG(color)) break; } while (*start); offset += len; - if (matched) mcount++; - else { + if (matched) { + // Finish off pending line color fragment. + if (FLAG(color) && !FLAG(o)) { + xputsn(TT.grey); + if (ulen > start-line) xputsl(start, ulen-(start-line)); + xputc(TT.outdelim); + } + mcount++; + } else { int discard = (after || TT.B); if (after && --after) { @@ -348,6 +379,7 @@ static void parse_regex(void) // Split lines at \n, add individual lines to new list. do { +// TODO: NUL terminated input shouldn't split -e at \n ss = strchr(s, '\n'); if (ss) *(ss++) = 0; new = xmalloc(sizeof(struct arg_list)); @@ -420,6 +452,17 @@ void grep_main(void) { char **ss = toys.optargs; + if (FLAG(color) && (!TT.color || !strcmp(TT.color, "auto")) && !isatty(1)) + toys.optflags &= ~FLAG_color; + + if (FLAG(color)) { + TT.purple = "\033[35m"; + TT.cyan = "\033[36m"; + TT.red = "\033[1;31m"; + TT.green = "\033[32m"; + TT.grey = "\033[0m"; + } else TT.purple = TT.cyan = TT.red = TT.green = TT.grey = ""; + // Grep exits with 2 for errors toys.exitval = 2; -- cgit v1.2.3