From 873c33e92f8a379c3f318f264b2f138658003530 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Fri, 28 May 2021 15:21:45 -0400 Subject: [PATCH] diff(1): Add --color support --- usr.bin/diff/diff.1 | 17 ++++++++++ usr.bin/diff/diff.c | 70 +++++++++++++++++++++++++++++++++++++++++- usr.bin/diff/diff.h | 3 +- usr.bin/diff/diffreg.c | 16 ++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1 index e0a790f6efb..50fd43902d5 100644 --- a/usr.bin/diff/diff.1 +++ b/usr.bin/diff/diff.1 @@ -44,6 +44,7 @@ .Fl n | q | u | y .Oc .Op Fl -brief +.Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT .Op Fl -ed .Op Fl -expand-tabs @@ -71,6 +72,7 @@ .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl L Ar label | Fl -label Ar label .Op Fl -brief +.Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT .Op Fl -ed .Op Fl -expand-tabs @@ -96,6 +98,7 @@ .Op Fl aBbdiltw .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl -brief +.Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT .Op Fl -ed .Op Fl -expand-tabs @@ -122,6 +125,7 @@ .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl L Ar label | Fl -label Ar label .Op Fl -brief +.Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT .Op Fl -ed .Op Fl -expand-tabs @@ -150,6 +154,7 @@ .Fl n | q | u .Oc .Op Fl -brief +.Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT .Op Fl -context .Op Fl -ed @@ -184,6 +189,7 @@ .Ar dir1 dir2 .Nm diff .Op Fl aBbditwW +.Op Fl -color Ns = Ns Ar when .Op Fl -expand-tabs .Op Fl -ignore-all-blanks .Op Fl -ignore-blank-lines @@ -332,6 +338,17 @@ Causes chunks that include only blank lines to be ignored. .It Fl b -ignore-space-change Causes trailing blanks (spaces and tabs) to be ignored, and other strings of blanks to compare equal. +.It Fl Fl color= Ns Oo Ar when Oc +Mark up the matching text with the expression stored in the +.Ev DIFFCOLOR +environment variable. +The possible values of +.Ar when +are +.Dq Cm never , +.Dq Cm always +and +.Dq Cm auto . .It Fl d -minimal Try very hard to produce a diff as small as possible. This may consume a lot of processing power and memory when processing diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c index 1bad6226f49..d9a732db768 100644 --- a/usr.bin/diff/diff.c +++ b/usr.bin/diff/diff.c @@ -38,11 +38,18 @@ __FBSDID("$FreeBSD$"); #include "diff.h" #include "xmalloc.h" -int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag, Wflag; +#define COLORFLAG_UNSET -1 +#define COLORFLAG_NEVER 0 +#define COLORFLAG_AUTO 0 +#define COLORFLAG_ALWAYS 0 + +int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag, Wflag, color = 0; int diff_format, diff_context, status, ignore_file_case, suppress_common; int tabsize = 8, width = 130; +static int colorflag = COLORFLAG_UNSET; char *start, *ifdefname, *diffargs, *label[2], *ignore_pats; char *group_format = NULL; +const char *add_code, *del_code; struct stat stb1, stb2; struct excludes *excludes_list; regex_t ignore_re; @@ -57,6 +64,7 @@ enum { OPT_HORIZON_LINES, OPT_CHANGED_GROUP_FORMAT, OPT_SUPPRESS_COMMON, + OPT_COLOR, }; static struct option longopts[] = { @@ -97,6 +105,7 @@ static struct option longopts[] = { { "tabsize", required_argument, NULL, OPT_TSIZE }, { "changed-group-format", required_argument, NULL, OPT_CHANGED_GROUP_FORMAT}, { "suppress-common-lines", no_argument, NULL, OPT_SUPPRESS_COMMON }, + { "color", optional_argument, NULL, OPT_COLOR }, { NULL, 0, 0, '\0'} }; @@ -106,6 +115,8 @@ void push_excludes(char *); void push_ignore_pats(char *); void read_excludes_file(char *file); void set_argstr(char **, char **); +static const char *init_code(int, const char *); +static int do_color(void); int main(int argc, char **argv) @@ -301,6 +312,17 @@ main(int argc, char **argv) case OPT_SUPPRESS_COMMON: suppress_common = 1; break; + case OPT_COLOR: + if (optarg == NULL || strncmp(optarg, "auto", 4) == 0) + colorflag = COLORFLAG_AUTO; + else if (strncmp(optarg, "always", 6) == 0) + colorflag = COLORFLAG_ALWAYS; + else if (strncmp(optarg, "never", 5) == 0) + colorflag = COLORFLAG_NEVER; + else + errx(2, "unsupported --color value '%s' (must be always, auto, or never)", + optarg); + break; default: usage(); break; @@ -316,6 +338,12 @@ main(int argc, char **argv) argc -= optind; argv += optind; + if (do_color()) { + color = 1; + add_code = init_code(1, "32"); + del_code = init_code(2, "31"); + } + #ifdef __OpenBSD__ if (pledge("stdio rpath tmppath", NULL) == -1) err(2, "pledge"); @@ -550,3 +578,43 @@ conflicting_format(void) fprintf(stderr, "error: conflicting output format options.\n"); usage(); } + +static int +do_color(void) +{ + const char *p, *p2; + int ret = 0; + + p = getenv("CLICOLOR"); + p2 = getenv("COLORTERM"); + + if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0')) + ret = isatty(STDOUT_FILENO) ? 1 : 0; + + if (colorflag == COLORFLAG_AUTO) + ret = isatty(STDOUT_FILENO) ? 1 : 0; + else if (colorflag == COLORFLAG_ALWAYS) + ret = 1; + else if (colorflag == COLORFLAG_NEVER) + ret = 0; + + return ret; +} + +static const char * +init_code(int i, const char *rv) +{ + char *buf, *p, *env; + int j; + + env = getenv("DIFFCOLORS"); + if (env != NULL && *env != '\0') { + p = strdup(env); + for (j = 0; j < i; j++) + buf = strsep(&p, ":"); + free(p); + if (buf != NULL) + return buf; + } + return rv; +} diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h index b5536bd7bf7..5eccce51210 100644 --- a/usr.bin/diff/diff.h +++ b/usr.bin/diff/diff.h @@ -90,12 +90,13 @@ struct excludes { struct excludes *next; }; -extern int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag, Wflag; +extern int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag, Wflag, color; extern int diff_format, diff_context, status, ignore_file_case; extern int suppress_common; extern int tabsize, width; extern char *start, *ifdefname, *diffargs, *label[2], *ignore_pats; extern char *group_format; +extern const char *add_code, *del_code; extern struct stat stb1, stb2; extern struct excludes *excludes_list; extern regex_t ignore_re; diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c index 651ec88df90..5f1b0d5ad94 100644 --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -1183,13 +1183,23 @@ change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d, } } if (diff_format == D_SIDEBYSIDE) { + if (color && (a>b)) + printf("\033[%sm", add_code); + else if (color && (c>d)) + printf("\033[%sm", del_code); if (a > b) { print_space(0, hw + padding , *pflags); } else { nc = fetch(ixold, a, b, f1, '\0', 1, *pflags); print_space(nc, hw - nc + padding, *pflags); } + if (color && (a>b)) + printf("\033[%sm", add_code); + else if (color && (c>d)) + printf("\033[%sm", del_code); printf("%c", (a>b)? '>' : ((c>d)? '<' : '|')); + if (color && (c>d)) + printf("\33[m"); print_space(hw + padding + 1 , padding, *pflags); fetch(ixnew, c, d, f2, '\0', 0, *pflags); printf("\n"); @@ -1263,6 +1273,10 @@ fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags) nc = hw; if ((diff_format != D_IFDEF && diff_format != D_GFORMAT) && ch != '\0') { + if (color && (ch == '>' || ch == '+')) + printf("\033[%sm", add_code); + else if (color && (ch == '<' || ch == '-')) + printf("\033[%sm", del_code); printf("%c", ch); if (Tflag && (diff_format == D_NORMAL || diff_format == D_CONTEXT || @@ -1335,6 +1349,8 @@ fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags) } } } + if (color) + printf("\33[m"); return col; } -- 2.32.0