Index: auparse/auparse-defs.h
===================================================================
--- auparse/auparse-defs.h	(revision 108)
+++ auparse/auparse-defs.h	(working copy)
@@ -77,7 +77,7 @@
 	AUPARSE_TYPE_SOCKADDR, AUPARSE_TYPE_FLAGS, AUPARSE_TYPE_PROMISC,
 	AUPARSE_TYPE_CAPABILITY, AUPARSE_TYPE_SUCCESS, AUPARSE_TYPE_A0,
 	AUPARSE_TYPE_A1, AUPARSE_TYPE_A2, AUPARSE_TYPE_SIGNAL, 
-	AUPARSE_TYPE_LIST } auparse_type_t;
+	AUPARSE_TYPE_LIST, AUPARSE_TYPE_TTY_DATA } auparse_type_t;
 
 #ifdef __cplusplus
 }
Index: auparse/interpret.c
===================================================================
--- auparse/interpret.c	(revision 108)
+++ auparse/interpret.c	(working copy)
@@ -79,7 +79,7 @@
  * This function will take a pointer to a 2 byte Ascii character buffer and
  * return the actual hex value.
  */
-static unsigned char x2c(unsigned char *buf)
+static unsigned char x2c(const unsigned char *buf)
 {
         static const char AsciiArray[17] = "0123456789ABCDEF";
         char *ptr;
@@ -95,8 +95,18 @@
         return total;
 }
 
+static int is_hex_string(const char *str)
+{
+	while (*str) {
+		if (!isxdigit(*str))
+			return 0;
+		str++;
+	}
+	return 1;
+}
+
 /* returns a freshly malloc'ed and converted buffer */
-const char *au_unescape(char *buf)
+char *au_unescape(char *buf)
 {
         int len, i;
         char saved, *str, *ptr = buf;
@@ -852,6 +862,134 @@
 	return out;
 }
 
+struct string_buf {
+	char *buf; /* NULL if was ever out of memory */
+	size_t allocated;
+	size_t pos;
+};
+
+/* Append c to buf. */
+static void append_char(struct string_buf *buf, char c)
+{
+	if (buf->buf == NULL)
+		return;
+	if (buf->pos == buf->allocated) {
+		char *p;
+
+		buf->allocated *= 2;
+		p = realloc(buf->buf, buf->allocated);
+		if (p == NULL) {
+			free(buf->buf);
+			buf->buf = NULL;
+			return;
+		}
+		buf->buf = p;
+	}
+	buf->buf[buf->pos] = c;
+	buf->pos++;
+}
+
+/* Represent c as a character within a quoted string, and append it to buf. */
+static void tty_append_printable_char(struct string_buf *buf, unsigned char c)
+{
+	if (c < 0x20 || c > 0x7E) {
+		append_char(buf, '\\');
+		append_char(buf, '0' + ((c >> 6) & 07));
+		append_char(buf, '0' + ((c >> 3) & 07));
+		append_char(buf, '0' + (c & 07));
+	} else {
+		if (c == '\\' || c ==  '"')
+			append_char(buf, '\\');
+		append_char(buf, c);
+	}
+}
+
+/* Search for a name of a sequence of TTY bytes.
+   If found, return the name and advance *INPUT.  Return NULL otherwise. */
+static const char *tty_find_named_key(unsigned char **input, size_t input_len)
+{
+	/* NUL-terminated list of (sequence, NUL, name, NUL) entries.
+	   First match wins, even if a longer match were possible later */
+	static const unsigned char named_keys[] =
+#define E(SEQ, NAME) SEQ "\0" NAME "\0"
+#include "tty_named_keys.h"
+#undef E
+		"\0";
+
+	unsigned char *src;
+	const unsigned char *nk;
+
+	src = *input;
+	if (*src >= ' ' && *src != 0x7F)
+		return NULL; /* Fast path */
+	nk = named_keys;
+	do {
+		const unsigned char *p;
+		size_t nk_len;
+
+		p = strchr(nk, '\0');
+		nk_len = p - nk;
+		if (nk_len <= input_len && memcmp(src, nk, nk_len) == 0) {
+			*input += nk_len;
+			return p + 1;
+		}
+		nk = strchr(p + 1, '\0') + 1;
+	} while (*nk != '\0');
+	return NULL;
+}
+
+static const char *print_tty_data(const char *raw_data)
+{
+	struct string_buf buf;
+	int in_printable;
+	unsigned char *data, *data_pos, *data_end;
+
+	if (!is_hex_string(raw_data))
+		return strdup(raw_data);
+	data = au_unescape((char *)raw_data);
+	if (data == NULL)
+		return NULL;
+	data_end = data + strlen(raw_data) / 2;
+
+	buf.allocated = 10;
+	buf.buf = malloc(buf.allocated); /* NULL handled in append_char() */
+	buf.pos = 0;
+	in_printable = 0;
+	data_pos = data;
+	while (data_pos < data_end) {
+		/* FIXME: Unicode */
+		const char *desc;
+
+		desc = tty_find_named_key(&data_pos, data_end - data_pos);
+		if (desc != NULL) {
+			if (in_printable != 0) {
+				append_char(&buf, '"');
+				in_printable = 0;
+			}
+			if (buf.pos != 0)
+				append_char(&buf, ',');
+			while (*desc != '\0') {
+				append_char(&buf, *desc);
+				desc++;
+			}
+		} else {
+			if (in_printable == 0) {
+				if (buf.pos != 0)
+					append_char(&buf, ',');
+				append_char(&buf, '"');
+				in_printable = 1;
+			}
+			tty_append_printable_char(&buf, *data_pos);
+			data_pos++;
+		}
+	}
+	if (in_printable != 0)
+		append_char(&buf, '"');
+	append_char(&buf, '\0');
+	free(data);
+	return buf.buf;
+}
+
 int lookup_type(const char *name)
 {
 	int i;
@@ -861,16 +999,6 @@
 	return AUPARSE_TYPE_UNCLASSIFIED;
 }
 
-static int is_hex_string(const char *str)
-{
-	while (*str) {
-		if (!isxdigit(*str))
-			return 0;
-		str++;
-	}
-	return 1;
-}
-
 const char *interpret(const rnode *r)
 {
 	const nvlist *nv = &r->nv;
@@ -885,6 +1013,8 @@
 		type = AUPARSE_TYPE_ESCAPED;
 	else if (r->type == AUDIT_AVC && strcmp(name, "saddr") == 0)
 		type = -1;
+	else if (r->type == AUDIT_USER_TTY && strcmp(name, "msg") == 0)
+		type = AUPARSE_TYPE_ESCAPED;
 	else if (strcmp(name, "acct") == 0) {
 		if (val[0] == '"')
 			type = AUPARSE_TYPE_ESCAPED;
@@ -949,7 +1079,10 @@
 			break; 
 		case AUPARSE_TYPE_LIST:
 			out = print_list(val);
-			break; 
+			break;
+		case AUPARSE_TYPE_TTY_DATA:
+			out = print_tty_data(val);
+			break;
 		case AUPARSE_TYPE_UNCLASSIFIED:
 		default: {
 			char *out2;
Index: auparse/typetab.h
===================================================================
--- auparse/typetab.h	(revision 108)
+++ auparse/typetab.h	(working copy)
@@ -69,3 +69,4 @@
 _S(AUPARSE_TYPE_A1,		"a1"		)
 _S(AUPARSE_TYPE_A2,		"a2"		)
 _S(AUPARSE_TYPE_SIGNAL,		"sig"		)
+_S(AUPARSE_TYPE_TTY_DATA,	"data"		)
Index: auparse/interpret.h
===================================================================
--- auparse/interpret.h	(revision 108)
+++ auparse/interpret.h	(working copy)
@@ -37,7 +37,7 @@
 const char *interpret(const rnode *r);
 void aulookup_destroy_uid_list(void);
 void aulookup_destroy_gid_list(void);
-const char *au_unescape(char *buf);
+char *au_unescape(char *buf);
 
 /* Make these hidden to prevent conflicts */
 hidden_proto(lookup_type);
Index: auparse/tty_named_keys.h
===================================================================
--- auparse/tty_named_keys.h	(revision 0)
+++ auparse/tty_named_keys.h	(revision 0)
@@ -0,0 +1,71 @@
+/* tty_named_keys.h --
+ * Copyright 2008 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors:
+ *      Miloslav Trmač <mitr@redhat.com>
+ */
+
+E("\x01", "^A")
+E("\x02", "^B")
+E("\x03", "^C")
+E("\x04", "^D")
+E("\x05", "^E")
+E("\x06", "^F")
+E("\x07", "^G")
+E("\x08", "^H")
+E("\t", "tab")
+E("\n", "nl")
+E("\x0B", "^K")
+E("\x0C", "^L")
+E("\r", "ret")
+E("\x0E", "^N")
+E("\x0F", "^O")
+E("\x10", "^P")
+E("\x11", "^Q")
+E("\x12", "^R")
+E("\x13", "^S")
+E("\x14", "^T")
+E("\x15", "^U")
+E("\x16", "^V")
+E("\x17", "^W")
+E("\x18", "^X")
+E("\x19", "^Y")
+E("\x1A", "^Z")
+/* \x1B handled only after all other escape sequences */
+E("\x7F", "backspace")
+
+E("\x1B[A", "up")
+E("\x1B[B", "down")
+E("\x1B[C", "right")
+E("\x1B[D", "left")
+
+E("\x1B""OP", "F1")
+E("\x1B""OQ", "F2")
+E("\x1B""OR", "F3")
+E("\x1B""OS", "F4")
+E("\x1B[15~", "F5")
+E("\x1B[17~", "F6")
+E("\x1B[18~", "F7")
+E("\x1B[19~", "F8")
+E("\x1B[20~", "F9")
+E("\x1B[21~", "F10")
+E("\x1B[23~", "F11")
+E("\x1B[24~", "F12")
+
+E("\x1B", "esc")
+E("\x7F", "backspace")
Index: auparse/Makefile.am
===================================================================
--- auparse/Makefile.am	(revision 108)
+++ auparse/Makefile.am	(working copy)
@@ -32,7 +32,7 @@
 	auparse.c auditd-config.c message.c data_buf.c auparse-defs.h	\
 	data_buf.h nvlist.h auparse.h ellist.h				\
 	internal.h nvpair.h rnode.h interpret.h				\
-	private.h expression.c expression.h
+	private.h expression.c expression.h tty_named_keys.h
 nodist_libauparse_la_SOURCES = $(BUILT_SOURCES)
 
 libauparse_la_LIBADD = ${top_builddir}/lib/libaudit.la
