From owner-dev-commits-src-all@freebsd.org Wed Sep 15 23:59:56 2021 Return-Path: Delivered-To: dev-commits-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id E81FC6709C0; Wed, 15 Sep 2021 23:59:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4H8xzH5965z3mtl; Wed, 15 Sep 2021 23:59:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 85E8A17469; Wed, 15 Sep 2021 23:59:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 18FNxtFi011221; Wed, 15 Sep 2021 23:59:55 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 18FNxtbP011220; Wed, 15 Sep 2021 23:59:55 GMT (envelope-from git) Date: Wed, 15 Sep 2021 23:59:55 GMT Message-Id: <202109152359.18FNxtbP011220@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Piotr Pawel Stefaniak Subject: git: f38702e5a52e - main - diff(1): Add --color support MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: pstef X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: f38702e5a52e1350b9349bc297ccd0b50b1941c3 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for all branches of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Sep 2021 23:59:56 -0000 The branch main has been updated by pstef: URL: https://cgit.FreeBSD.org/src/commit/?id=f38702e5a52e1350b9349bc297ccd0b50b1941c3 commit f38702e5a52e1350b9349bc297ccd0b50b1941c3 Author: Cameron Katri AuthorDate: 2021-09-05 00:10:41 +0000 Commit: Piotr Pawel Stefaniak CommitDate: 2021-09-15 23:46:44 +0000 diff(1): Add --color support Adds a --color flag to diff(1) that supports the same options as GNU's diff(1). The colors are customizable with the env var DIFFCOLORS in a format similar to grep(1)'s GREPCOLORS. An example would be 04;36:41 for additions to be underlined light blue, and deletions have a red background. Differential Revision: https://reviews.freebsd.org/D30545 --- usr.bin/diff/diff.1 | 35 ++++++++++++++++++++++++++++++++ usr.bin/diff/diff.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++- usr.bin/diff/diff.h | 10 ++++++++- usr.bin/diff/diffreg.c | 21 ++++++++++++++++++- 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1 index e0a790f6efb6..6056ddd3ac76 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,21 @@ 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 -color= Ns Oo Ar when Oc +Color the additions green, and removals red, or the value in the +.Ev DIFFCOLORS +environment variable. +The possible values of +.Ar when +are +.Dq Cm never , +.Dq Cm always +and +.Dq Cm auto . +.Cm auto +will use color if the output is a tty and the +.Ev COLORTERM +environment variable is set to a non-empty string. .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 @@ -592,6 +613,20 @@ As in identical pairs (where num1 = num2) are abbreviated as a single number. +.Sh ENVIRONMENT +.Bl -tag -width DIFFCOLORS +.It Ev DIFFCOLORS +The value of this variable is the form +.Ar add Ns : Ns Ar rm , +where +.Ar add +is the ASCII escape sequence for additions and +.Ar rm +is the ASCII escape sequence for deletions. +If this is unset, +.Nm +uses green for additions and red for removals. +.El .Sh FILES .Bl -tag -width /tmp/diff.XXXXXXXX -compact .It Pa /tmp/diff.XXXXXXXX diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c index a5966e74dbcc..4fc3094035d9 100644 --- a/usr.bin/diff/diff.c +++ b/usr.bin/diff/diff.c @@ -39,11 +39,13 @@ __FBSDID("$FreeBSD$"); #include "xmalloc.h" bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag; -bool ignore_file_case, suppress_common; +bool ignore_file_case, suppress_common, color; int diff_format, diff_context, status; int tabsize = 8, width = 130; +static int colorflag = COLORFLAG_NEVER; 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; @@ -58,6 +60,7 @@ enum { OPT_HORIZON_LINES, OPT_CHANGED_GROUP_FORMAT, OPT_SUPPRESS_COMMON, + OPT_COLOR, }; static struct option longopts[] = { @@ -98,6 +101,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'} }; @@ -108,6 +112,7 @@ static void push_ignore_pats(char *); static void read_excludes_file(char *file); static void set_argstr(char **, char **); static char *splice(char *, char *); +static bool do_color(void); int main(int argc, char **argv) @@ -301,6 +306,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 +332,22 @@ main(int argc, char **argv) argc -= optind; argv += optind; + if (do_color()) { + char *p; + const char *env; + + color = true; + add_code = "32"; + del_code = "31"; + env = getenv("DIFFCOLORS"); + if (env != NULL && *env != '\0' && (p = strdup(env))) { + add_code = p; + strsep(&p, ":"); + if (p != NULL) + del_code = p; + } + } + #ifdef __OpenBSD__ if (pledge("stdio rpath tmppath", NULL) == -1) err(2, "pledge"); @@ -545,6 +577,27 @@ conflicting_format(void) usage(); } +static bool +do_color(void) +{ + const char *p, *p2; + + switch (colorflag) { + case COLORFLAG_AUTO: + p = getenv("CLICOLOR"); + p2 = getenv("COLORTERM"); + if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0')) + return isatty(STDOUT_FILENO); + break; + case COLORFLAG_ALWAYS: + return (true); + case COLORFLAG_NEVER: + return (false); + } + + return (false); +} + static char * splice(char *dir, char *path) { diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h index 7ae700810fc6..5164fe22ace4 100644 --- a/usr.bin/diff/diff.h +++ b/usr.bin/diff/diff.h @@ -87,17 +87,25 @@ #define D_SKIPPED2 6 /* path2 was a special file */ #define D_ERROR 7 /* A file access error occurred */ +/* + * Color options + */ +#define COLORFLAG_NEVER 0 +#define COLORFLAG_AUTO 1 +#define COLORFLAG_ALWAYS 2 + struct excludes { char *pattern; struct excludes *next; }; extern bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag; -extern bool ignore_file_case, suppress_common; +extern bool ignore_file_case, suppress_common, color; extern int diff_format, diff_context, status; 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 c9095ec46e88..47c1934a6a65 100644 --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -1140,13 +1140,23 @@ proceed: } } 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("\033[m"); print_space(hw + padding + 1 , padding, *pflags); fetch(ixnew, c, d, f2, '\0', 0, *pflags); printf("\n"); @@ -1220,6 +1230,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 || @@ -1290,12 +1304,17 @@ fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags) } /* when side-by-side, do not print a newline */ if (diff_format != D_SIDEBYSIDE || c != '\n') { - printf("%c", c); + if (color && c == '\n') + printf("\033[m%c", c); + else + printf("%c", c); col++; } } } } + if (color && diff_format == D_SIDEBYSIDE) + printf("\033[m"); return (col); }