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

Christian Hesse list at eworm.de
Wed Jul 8 18:21:12 AEST 2015


Roland Mainz <roland.mainz at nrubsig.org> on Tue, 2015/07/07 16:25:
> General comments:
> 1. Not all locales use UTF-8 as encoding but can still use the Unicode
> characters you use (e.g. GB18030 is a modern example and it's use is
> mandated by all software vendors in PRC China). A quick solution is to
> use |iconv()| to convert the UTF-8 byte sequences to the local
> encoding (see
> http://svn.nrubsig.org/svn/people/gisburn/code/ucs4towchar_t/ucs4towchar_t.c
> - that could should be easy to modify). Note that if |iconf()|
> produces an empty string in a character-by-character conversion it
> means that the destination locale cannot represent that character in
> the local encoding (you have to fall-back to the ASCII representation
> then (this will also eliminate the need for the |setlocale()|&&co.
> testing)).
> 
> 2. UTF-8 sequences in ISO C code are not portable and a lot of
> compilers will choke on that (e.g. if they are in a non-UTF-8 locale
> like "C", "POSIX" or any non-UTF-8 multibyte locale). Correct fix
> would be to provide the UTF-8 byte sequences for the characters as
> plain C strings escaped in octal or hexadecimal notation (and then
> squish them through |iconf()|)
> 
> 3. Not all UTF-8 locales (or locale aliases) have "UTF-8" in their
> name (for example the name "en_US" is allowed to be an alias for
> "en_US.UTF-8" (this quickly becomes messy in the Chinese/Japanese
> environments, e.g. where "ja_JP" can be anything from ja_JP.PCK to
> ja_JP.UTF-8))

Ok, this adds some more complexity. Looks like we can not just use a given
value to get it right in all cases (for all locales, encodings, ...).

This code goes though a loop to calculate the needed byte sequences once.
Does that work everywhere?

Probably we have to fiddle with header files. Not all systems do have iconv.h
and langinfo.h (and corresponding libraries), no?

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..2f5a2f7 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -44,6 +44,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <resolv.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#include <unistd.h>
+#include <iconv.h>
+#include <langinfo.h>
+#endif /* HAVE_LOCALE_H */
 #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 HAVE_LOCALE_H
+	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
+		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;
 }

-- 
main(a){char*c=/*    Schoene Gruesse                         */"B?IJj;MEH"
"CX:;",b;for(a/*    Chris           get my mail address:    */=0;b=c[a++];)
putchar(b-1/(/*               gcc -o sig sig.c && ./sig    */b/42*2-3)*42);}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 473 bytes
Desc: OpenPGP digital signature
URL: <http://lists.mindrot.org/pipermail/openssh-unix-dev/attachments/20150708/87c15d24/attachment-0001.bin>


More information about the openssh-unix-dev mailing list