git: 4e859e67dde2 - main - libdiff: Implement diff coloring.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 02 Oct 2024 15:55:55 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=4e859e67dde2cb4a583d340b27793a255f62f53e

commit 4e859e67dde2cb4a583d340b27793a255f62f53e
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-10-02 15:54:48 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-10-02 15:55:06 +0000

    libdiff: Implement diff coloring.
    
    This patch got accidentally left out when libdiff was imported.  The
    rest of the code (command-line option etc.) was present, just not the
    part that actually prints ANSI color codes.
    
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D46873
---
 contrib/libdiff/include/diff_output.h |  4 ++
 contrib/libdiff/lib/diff_output.c     | 69 ++++++++++++++++++++++++++++-------
 usr.bin/diff/diffreg_new.c            |  2 +
 3 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/contrib/libdiff/include/diff_output.h b/contrib/libdiff/include/diff_output.h
index d2568c5a2b50..500ee6427574 100644
--- a/contrib/libdiff/include/diff_output.h
+++ b/contrib/libdiff/include/diff_output.h
@@ -110,3 +110,7 @@ int diff_output_chunk_right_version(struct diff_output_info **output_info,
 
 const char *diff_output_get_label_left(const struct diff_input_info *info);
 const char *diff_output_get_label_right(const struct diff_input_info *info);
+
+void diff_output_set_colors(bool _color,
+			    const char *_del_code,
+			    const char *_add_code);
diff --git a/contrib/libdiff/lib/diff_output.c b/contrib/libdiff/lib/diff_output.c
index 3b84608882f6..2a0d95896e1a 100644
--- a/contrib/libdiff/lib/diff_output.c
+++ b/contrib/libdiff/lib/diff_output.c
@@ -30,6 +30,22 @@
 
 #include "diff_internal.h"
 
+static bool color;
+static const char *del_code = "31";
+static const char *add_code = "32";
+
+void
+diff_output_set_colors(bool _color,
+		       const char *_del_code,
+		       const char *_add_code)
+{
+	color = _color;
+	if (_del_code)
+		del_code = _del_code;
+	if (_add_code)
+		add_code = _add_code;
+}
+
 static int
 get_atom_byte(int *ch, struct diff_atom *atom, off_t off)
 {
@@ -66,12 +82,25 @@ diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
 	off_t outoff = 0, *offp;
 	uint8_t *typep;
 	int rc;
+	bool colored;
 
 	if (outinfo && outinfo->line_offsets.len > 0) {
 		unsigned int idx = outinfo->line_offsets.len - 1;
 		outoff = outinfo->line_offsets.head[idx];
 	}
 
+	if (color) {
+		colored = true;
+		if (*prefix == '-' || *prefix == '<')
+			printf("\033[%sm", del_code);
+		else if (*prefix == '+' || *prefix == '>')
+			printf("\033[%sm", add_code);
+		else
+			colored = false;
+	} else {
+		colored = false;
+	}
+
 	foreach_diff_atom(atom, start_atom, count) {
 		off_t outlen = 0;
 		int i, ch, nbuf = 0;
@@ -80,14 +109,16 @@ diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
 		size_t n;
 
 		n = strlcpy(buf, prefix, sizeof(buf));
-		if (n >= DIFF_OUTPUT_BUF_SIZE) /* leave room for '\n' */
-			return ENOBUFS;
+		if (n >= DIFF_OUTPUT_BUF_SIZE) { /* leave room for '\n' */
+			rc = ENOBUFS;
+			goto out;
+		}
 		nbuf += n;
 
 		if (len) {
 			rc = get_atom_byte(&ch, atom, len - 1);
 			if (rc)
-				return rc;
+				goto out;
 			if (ch == '\n')
 				len--;
 		}
@@ -95,11 +126,13 @@ diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
 		for (i = 0; i < len; i++) {
 			rc = get_atom_byte(&ch, atom, i);
 			if (rc)
-				return rc;
+				goto out;
 			if (nbuf >= DIFF_OUTPUT_BUF_SIZE) {
 				wlen = fwrite(buf, 1, nbuf, dest);
-				if (wlen != nbuf)
-					return errno;
+				if (wlen != nbuf) {
+					rc = errno;
+					goto out;
+				}
 				outlen += wlen;
 				nbuf = 0;
 			}
@@ -107,25 +140,35 @@ diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
 		}
 		buf[nbuf++] = '\n';
 		wlen = fwrite(buf, 1, nbuf, dest);
-		if (wlen != nbuf)
-			return errno;
+		if (wlen != nbuf) {
+			rc = errno;
+			goto out;
+		}
 		outlen += wlen;
 		if (outinfo) {
 			ARRAYLIST_ADD(offp, outinfo->line_offsets);
-			if (offp == NULL)
-				return ENOMEM;
+			if (offp == NULL) {
+				rc = ENOMEM;
+				goto out;
+			}
 			outoff += outlen;
 			*offp = outoff;
 			ARRAYLIST_ADD(typep, outinfo->line_types);
-			if (typep == NULL)
-				return ENOMEM;
+			if (typep == NULL) {
+				rc = ENOMEM;
+				goto out;
+			}
 			*typep = *prefix == ' ' ? DIFF_LINE_CONTEXT :
 			    *prefix == '-' ? DIFF_LINE_MINUS :
 			    *prefix == '+' ? DIFF_LINE_PLUS : DIFF_LINE_NONE;
 		}
 	}
 
-	return DIFF_RC_OK;
+	rc = DIFF_RC_OK;
+out:
+	if (colored)
+		printf("\033[m");
+	return rc;
 }
 
 int
diff --git a/usr.bin/diff/diffreg_new.c b/usr.bin/diff/diffreg_new.c
index a7a40ec12ec9..af9104559986 100644
--- a/usr.bin/diff/diffreg_new.c
+++ b/usr.bin/diff/diffreg_new.c
@@ -253,6 +253,8 @@ diffreg_new(char *file1, char *file2, int flags, int capsicum)
 		goto done;
 	}
 
+	if (color)
+		diff_output_set_colors(color, del_code, add_code);
 	if (diff_format == D_NORMAL) {
 		rc = diff_output_plain(NULL, stdout, &info, result, false);
 	} else if (diff_format == D_EDIT) {