[PATCH 1/1 v3] paint visual host key with unicode box-drawing characters

Christian Hesse mail at eworm.de
Thu Jul 9 18:34:04 AEST 2015


Signed-off-by: Christian Hesse <mail at eworm.de>
---
 configure.ac |  11 ++++++
 defines.h    |   5 +++
 log.c        |   3 +-
 sshkey.c     | 108 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 4 files changed, 106 insertions(+), 21 deletions(-)

diff --git a/configure.ac b/configure.ac
index bb0095f..7e3965f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -383,6 +383,8 @@ AC_CHECK_HEADERS([ \
 	inttypes.h \
 	limits.h \
 	locale.h \
+	iconv.h \
+	langinfo.h \
 	login.h \
 	maillock.h \
 	ndir.h \
@@ -4641,6 +4643,15 @@ AC_ARG_ENABLE([pututxline],
 		fi
 	]
 )
+AC_ARG_ENABLE([unicode],
+	[  --disable-unicode       disable use of unicode [no]],
+	[
+		if test "x$enableval" = "xno" ; then
+			AC_DEFINE([DISABLE_UNICODE], [1],
+				[Define if you don't want to use unicode])
+		fi
+	]
+)
 AC_ARG_WITH([lastlog],
   [  --with-lastlog=FILE|DIR specify lastlog location [common locations]],
 	[
diff --git a/defines.h b/defines.h
index fa0ccba..7ea69cc 100644
--- a/defines.h
+++ b/defines.h
@@ -850,4 +850,9 @@ struct winsize {
 # endif /* gcc version */
 #endif /* __predict_true */
 
+#if defined(HAVE_LOCALE_H) && defined(HAVE_ICONV_H) && \
+    defined(HAVE_LANGINFO_H) && !defined(DISABLE_UNICODE)
+# define USE_UNICODE
+#endif
+
 #endif /* _DEFINES_H */
diff --git a/log.c b/log.c
index 32e1d2e..7463617 100644
--- a/log.c
+++ b/log.c
@@ -444,8 +444,9 @@ do_log(LogLevel level, const char *fmt, va_list args)
 		tmp_handler(level, fmtbuf, log_handler_ctx);
 		log_handler = tmp_handler;
 	} else if (log_on_stderr) {
-		snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf);
+		/* we want unicode multi byte characters, so do not use fmtbuf here */
 		(void)write(log_stderr_fd, msgbuf, strlen(msgbuf));
+		(void)write(log_stderr_fd, "\r\n", 2);
 	} else {
 #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
 		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
diff --git a/sshkey.c b/sshkey.c
index cfe5980..554087f 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -44,6 +44,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <resolv.h>
+#ifdef USE_UNICODE
+#include <locale.h>
+#include <unistd.h>
+#include <iconv.h>
+#include <langinfo.h>
+#endif /* USE_UNICODE */
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif /* HAVE_UTIL_H */
@@ -1088,17 +1094,75 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
 	 * Chars to be used after each other every time the worm
 	 * intersects with itself.  Matter of taste.
 	 */
+	char    *border_ascii[] = { "+", "-", "[", "]", "+", "|", "+", "+" };
+	char   **border;
 	char	*augmentation_string = " .o+=*BOX@%&#/^SE";
-	char	*retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X];
+	char	*retval, *p, title[FLDSIZE_X - 2], hash[FLDSIZE_X - 2];
 	u_char	 field[FLDSIZE_X][FLDSIZE_Y];
 	size_t	 i, tlen, hlen;
 	u_int	 b;
 	int	 x, y, r;
 	size_t	 len = strlen(augmentation_string) - 1;
 
-	if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
+	if ((retval = malloc((FLDSIZE_X + 7) * FLDSIZE_Y + FLDSIZE_X * 3 * 2)) == NULL)
 		return NULL;
 
+#ifdef USE_UNICODE
+	iconv_t	 cd;
+	/* unicode character codes for box drawing
+	 * http://unicode.org/charts/PDF/U2500.pdf */
+	uint32_t border_unicode[] = {
+			0x250c, /* ┌ upper left */
+			0x2500, /* ─ horizontal */
+			0x2524, /* ┤ left of title/hash */
+			0x251c, /* ├ right of title/hash */
+			0x2510, /* ┐ upper right */
+			0x2502, /* │ vertical */
+			0x2514, /* └ lower left */
+			0x2518  /* ┘ lower right */ };
+	/* border_buffer is array of array of char
+	 * we use this to have statically allocated buffer */
+	char border_buffer[8][5];
+	/* border_encoded is array of pointer to char */
+	char *border_encoded[8];
+
+	if (isatty(fileno(stdout)) == 1) {
+		/* initialize locale */
+		setlocale(LC_ALL, "");
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		cd = iconv_open(nl_langinfo(CODESET), "UTF32LE");
+#elif __BYTE_ORDER == __BIG_ENDIAN
+		cd = iconv_open(nl_langinfo(CODESET), "UTF32BE");
+#else
+#error Unknown __BYTE_ORDER
+#endif
+
+		/* encode the border elements */
+		for (int i = 0; i < 8; i++) {
+			size_t in_size = sizeof(uint32_t);;
+			size_t out_size = sizeof(border_buffer[i]);
+			char *input = (char *) &border_unicode[i];
+			char *output = border_buffer[i];
+
+			memset(border_buffer[i], 0, out_size);
+			iconv(cd, &input, &in_size, &output, &out_size);
+
+			/* if iconv() was successful we have a string with non-zero length
+			 * fall back to ascii otherwise */
+			if (border_buffer[i][0] != 0)
+				border_encoded[i] = border_buffer[i];
+			else
+				border_encoded[i] = border_ascii[i];
+		}
+
+		iconv_close(cd);
+
+		border = border_encoded;
+	} else
+#endif /* USE_UNICODE */
+		border = border_ascii;
+
 	/* initialize field */
 	memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
 	x = FLDSIZE_X / 2;
@@ -1132,47 +1196,51 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len,
 	field[x][y] = len;
 
 	/* assemble title */
-	r = snprintf(title, sizeof(title), "[%s %u]",
+	r = snprintf(title, sizeof(title), "%s %u",
 		sshkey_type(k), sshkey_size(k));
-	/* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */
+	/* If "type size" won't fit, then try "type"; fits "ED25519-CERT" */
 	if (r < 0 || r > (int)sizeof(title))
-		r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k));
+		r = snprintf(title, sizeof(title), "%s", sshkey_type(k));
 	tlen = (r <= 0) ? 0 : strlen(title);
 
 	/* assemble hash ID. */
-	r = snprintf(hash, sizeof(hash), "[%s]", alg);
+	r = snprintf(hash, sizeof(hash), "%s", alg);
 	hlen = (r <= 0) ? 0 : strlen(hash);
 
 	/* output upper border */
 	p = retval;
-	*p++ = '+';
-	for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++)
-		*p++ = '-';
+	p += sprintf(p, "%s", border[0]);
+	for (i = 0; i < (FLDSIZE_X - tlen - 2) / 2; i++)
+		p += sprintf(p, "%s", border[1]);
+	p += sprintf(p, "%s", border[2]);
 	memcpy(p, title, tlen);
 	p += tlen;
-	for (i += tlen; i < FLDSIZE_X; i++)
-		*p++ = '-';
-	*p++ = '+';
+	p += sprintf(p, "%s", border[3]);
+	for (i += tlen + 2; i < FLDSIZE_X; i++)
+		p += sprintf(p, "%s", border[1]);
+	p += sprintf(p, "%s", border[4]);
 	*p++ = '\n';
 
 	/* output content */
 	for (y = 0; y < FLDSIZE_Y; y++) {
-		*p++ = '|';
+		p += sprintf(p, "%s", border[5]);
 		for (x = 0; x < FLDSIZE_X; x++)
 			*p++ = augmentation_string[MIN(field[x][y], len)];
-		*p++ = '|';
+		p += sprintf(p, "%s", border[5]);
 		*p++ = '\n';
 	}
 
 	/* output lower border */
-	*p++ = '+';
-	for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++)
-		*p++ = '-';
+	p += sprintf(p, "%s", border[6]);
+	for (i = 0; i < (FLDSIZE_X - hlen - 2) / 2; i++)
+		p += sprintf(p, "%s", border[1]);
+	p += sprintf(p, "%s", border[2]);
 	memcpy(p, hash, hlen);
 	p += hlen;
-	for (i += hlen; i < FLDSIZE_X; i++)
-		*p++ = '-';
-	*p++ = '+';
+	p += sprintf(p, "%s", border[3]);
+	for (i += hlen + 2; i < FLDSIZE_X; i++)
+		p += sprintf(p, "%s", border[1]);
+	p += sprintf(p, "%s", border[7]);
 
 	return retval;
 }
-- 
2.4.5



More information about the openssh-unix-dev mailing list