From 823add4685b99c18cb2bb46b50c6d2403fcf31ff Mon Sep 17 00:00:00 2001
From: David Gustavsson <david.e.gustavsson@gmail.com>
Date: Wed, 17 Aug 2022 14:54:03 +0200
Subject: [PATCH] Version diff coloring

---
 src/pacman/conf.c |  4 +++
 src/pacman/conf.h |  2 ++
 src/pacman/util.c | 80 ++++++++++++++++++++++++++++++++++++++++++-----
 src/pacman/util.h |  2 ++
 4 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/src/pacman/conf.c b/src/pacman/conf.c
index f9edf75b..d5c598b9 100644
--- a/src/pacman/conf.c
+++ b/src/pacman/conf.c
@@ -80,6 +80,8 @@ void enable_colors(int colors)
 		colstr->err     = BOLDRED;
 		colstr->faint   = GREY46;
 		colstr->nocolor = NOCOLOR;
+		colstr->diffrem = RED;
+		colstr->diffadd = GREEN;
 	} else {
 		colstr->colon   = ":: ";
 		colstr->title   = "";
@@ -91,6 +93,8 @@ void enable_colors(int colors)
 		colstr->err     = "";
 		colstr->faint   = "";
 		colstr->nocolor = "";
+		colstr->diffrem = "";
+		colstr->diffadd = "";
 	}
 }
 
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index f7916ca9..782b5758 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -33,6 +33,8 @@ typedef struct __colstr_t {
 	const char *err;
 	const char *faint;
 	const char *nocolor;
+	const char *diffrem;
+	const char *diffadd;
 } colstr_t;
 
 typedef struct __config_repo_t {
diff --git a/src/pacman/util.c b/src/pacman/util.c
index 5f5c7c54..5f2c7c3f 100644
--- a/src/pacman/util.c
+++ b/src/pacman/util.c
@@ -34,6 +34,7 @@
 #include <limits.h>
 #include <wchar.h>
 #include <wctype.h>
+#include <ctype.h>
 #ifdef HAVE_TERMIOS_H
 #include <termios.h> /* tcflush */
 #endif
@@ -285,8 +286,7 @@ int rmrf(const char *path)
 	}
 }
 
-/* output a string, but wrap words properly with a specified indentation
- */
+/* output a string, but wrap words properly with a specified indentation */
 void indentprint(const char *str, unsigned short indent, unsigned short cols)
 {
 	wchar_t *wcstr;
@@ -869,15 +869,21 @@ static alpm_list_t *create_verbose_row(pm_target_t *target)
 {
 	char *str;
 	off_t size = 0;
+	size_t split;
 	double human_size;
 	const char *label;
+	const char *old_version_in;
+	const char *new_version_in;
+	char *old_version;
+	char *new_version;
 	alpm_list_t *ret = NULL;
 
 	/* a row consists of the package name, */
 	if(target->install) {
 		const alpm_db_t *db = alpm_pkg_get_db(target->install);
 		if(db) {
-			pm_asprintf(&str, "%s/%s", alpm_db_get_name(db), alpm_pkg_get_name(target->install));
+			pm_asprintf(&str, "%s/%s", alpm_db_get_name(db),
+					alpm_pkg_get_name(target->install));
 		} else {
 			pm_asprintf(&str, "%s", alpm_pkg_get_name(target->install));
 		}
@@ -887,14 +893,43 @@ static alpm_list_t *create_verbose_row(pm_target_t *target)
 	add_table_cell(&ret, str, CELL_NORMAL | CELL_FREE);
 
 	/* old and new versions */
-	pm_asprintf(&str, "%s",
-			target->remove != NULL ? alpm_pkg_get_version(target->remove) : "");
+	if(target->remove) {
+		old_version_in = alpm_pkg_get_version(target->remove);
+	} else {
+		old_version_in = "";
+	}
+	old_version = malloc((strlen(old_version_in) + strlen(config->colstr.diffrem)
+				+ strlen(config->colstr.nocolor) + 1)*sizeof(char));
+	strcpy(old_version, (char *)old_version_in);
+
+	if(target->install) {
+		new_version_in = alpm_pkg_get_version(target->install);
+	} else {
+		new_version_in = "";
+	}
+	new_version = malloc((strlen(new_version_in) + strlen(config->colstr.diffadd)
+				+ strlen(config->colstr.nocolor) + 1)*sizeof(char));
+	strcpy(new_version, (char *)new_version_in);
+
+	split = alpm_diff(old_version_in, new_version_in);
+	if(strlen(old_version_in) > 0) {
+		insertstring(old_version, config->colstr.diffrem, split);
+		strcpy(old_version + strlen(old_version), config->colstr.nocolor);
+	}
+	if(strlen(new_version_in) > 0) {
+		insertstring(new_version, config->colstr.diffadd, split);
+		strcpy(new_version + strlen(new_version), config->colstr.nocolor);
+	}
+
+	pm_asprintf(&str, "%s", old_version);
 	add_table_cell(&ret, str, CELL_NORMAL | CELL_FREE);
 
-	pm_asprintf(&str, "%s",
-			target->install != NULL ? alpm_pkg_get_version(target->install) : "");
+	pm_asprintf(&str, "%s", new_version);
 	add_table_cell(&ret, str, CELL_NORMAL | CELL_FREE);
 
+	free(old_version);
+	free(new_version);
+
 	/* and size */
 	size -= target->remove ? alpm_pkg_get_isize(target->remove) : 0;
 	size += target->install ? alpm_pkg_get_isize(target->install) : 0;
@@ -1942,3 +1977,34 @@ void console_erase_line(void)
 {
 	printf("\x1B[K");
 }
+
+/* inserts string insert into string before index i, modifying string */
+void insertstring(char *string, const char *insert, size_t i)
+{
+	char *temp = malloc((strlen(string + i) + 1)*sizeof(char));
+	strcpy(temp, string + i);
+	strcpy(string + i, insert);
+	strcpy(string + i + strlen(insert), temp);
+	free(temp);
+}
+
+size_t alpm_diff(const char *a, const char *b)
+{
+	/*
+	 * Find the index where `a` and `b` differ
+	 */
+	size_t i = 0;
+	size_t split = 0;
+	while(a[i] == b[i]) {
+		if(a[i] == '\0') {
+			split = i+1;
+			return split;
+		}
+		if(isalnum(a[i]) == 0) {
+			/* Non-alphanumeric character */
+			split = i+1;
+		}
+		i++;
+	}
+	return split;
+}
diff --git a/src/pacman/util.h b/src/pacman/util.h
index 52e79915..cd474996 100644
--- a/src/pacman/util.h
+++ b/src/pacman/util.h
@@ -88,6 +88,8 @@ void console_cursor_move_down(unsigned int lines);
 void console_cursor_move_end(void);
 /* Erases line from the current cursor position till the end of the line */
 void console_erase_line(void);
+void insertstring(char *string, const char *insert, size_t i);
+size_t alpm_diff(const char *a, const char *b);
 
 int pm_printf(alpm_loglevel_t level, const char *format, ...) __attribute__((format(printf,2,3)));
 int pm_asprintf(char **string, const char *format, ...) __attribute__((format(printf,2,3)));
-- 
2.37.2

