[PATCH] Allow non-ASCII keys in keymap

Michael Bienia <[email protected]>
Newsgroups gmane.network.tin.devel
Message-ID <[email protected]>
Hello,

attached is a patch which adds support for non-ASCII keys to the keymap
code.

A known problem is the misaligned helppage as expand_ctrl_chars isn't
multibyte-safe yet. Another problem is that tin currently strips the
charset from LANG (or the other values) and as the keymap code doesn't
do any charset conversions it is impossible to use a keymap (with
non-ASCII keys) for a locale which is available in multiple charsets.
A seperate keymap for en_US and en_US.UTF-8 doesn't work but a separate
keymap for de_DE@euro and de_DE.UTF-8 works.

printascii() is now misnamed as the resulting string is not necessarily
ASCII.

Michael
patch-20051019.diff (text/plain, 15.1 KB)
 include/extern.h |    3 ++
 include/keymap.h |   25 ++++++++++++++--
 include/proto.h  |    1 
 src/global.c     |    6 +++-
 src/keymap.c     |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/lang.c       |    3 ++
 src/page.c       |    2 -
 src/prompt.c     |   55 ++++++++++++++++++++++++++++++++----
 8 files changed, 159 insertions(+), 18 deletions(-)
diff -Nurp tin-1.7.10/include/extern.h tin-1.7.10.new/include/extern.h
--- tin-1.7.10/include/extern.h	2005-07-16 15:30:27.000000000 +0200
+++ tin-1.7.10.new/include/extern.h	2005-10-17 21:05:00.382229000 +0200
@@ -828,6 +828,9 @@ extern constext txt_info_postponed[];
 extern constext txt_info_x_conversion_note[];
 extern constext txt_invalid_from[];
 extern constext txt_invalid_sender[];
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	extern constext txt_invalid_multibyte_sequence[];
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 extern constext txt_inverse_off[];
 extern constext txt_inverse_on[];
 extern constext txt_is_mailbox[];
diff -Nurp tin-1.7.10/include/keymap.h tin-1.7.10.new/include/keymap.h
--- tin-1.7.10/include/keymap.h	2005-07-16 13:56:30.000000000 +0200
+++ tin-1.7.10.new/include/keymap.h	2005-10-17 21:09:38.932688000 +0200
@@ -122,9 +122,15 @@
 
 /*
  * Maximum chars (including null byte) needed to print a key name
- * the longest name will probably something like SPACE
+ * A multibyte character can use up to MB_CUR_MAX chars. But as MB_CUR_MAX
+ * can't be used here, use MB_LEN_MAX instead.
+ * Some values for MB_LEN_MAX:
+ * - glibc 2.3.5: 16
+ * - gcc 4.0: 1
+ * - icc 8.0/9.0: 8
+ * Use the largest + 1 to be on the safe side.
  */
-#define MAXKEYLEN 10
+#define MAXKEYLEN 17
 
 /* TODO: permanently move here from tin.h */
 #define ctrl(c)	((c) & 0x1F)
@@ -343,7 +349,11 @@ typedef enum defined_functions t_functio
 
 
 struct keynode {
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wchar_t key;
+#else
 	char key;
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	t_function function;
 };
 
@@ -384,7 +394,6 @@ extern struct keylist select_keys;
 extern struct keylist thread_keys;
 
 
-extern char func_to_key (t_function func, const struct keylist keys);
 extern t_function global_mouse_action(t_function (*left_action) (void), t_function (*right_action) (void));
 extern t_function handle_keypad(
 	t_function (*left_action) (void),
@@ -393,6 +402,14 @@ extern t_function handle_keypad(
 		t_function (*left_action) (void),
 		t_function (*right_action) (void)),
 	const struct keylist keys);
-extern t_function key_to_func (const char key, const struct keylist keys);
 extern t_function prompt_slk_response(t_function default_func, const struct keylist keys, const char *fmt, ...);
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	extern char *printascii(char *buf, wint_t ch);
+	extern wchar_t func_to_key(t_function func, const struct keylist keys);
+	extern t_function key_to_func(const wchar_t key, const struct keylist keys);
+#else
+	extern char *printascii(char *buf, int ch);
+	extern char func_to_key (t_function func, const struct keylist keys);
+	extern t_function key_to_func (const char key, const struct keylist keys);
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 #endif /* !KEYMAP_H */
diff -Nurp tin-1.7.10/include/proto.h tin-1.7.10.new/include/proto.h
--- tin-1.7.10/include/proto.h	2005-07-17 15:30:25.000000000 +0200
+++ tin-1.7.10.new/include/proto.h	2005-10-17 20:41:36.070024000 +0200
@@ -253,7 +253,6 @@ extern void postinit_regexp(void);
 extern void joinpath(char *result, const char *dir, const char *file);
 
 /* keymap.c */
-extern char *printascii(char *buf, int ch);
 extern t_bool read_keymap_file(void);
 extern void free_keymaps(void);
 extern void setup_default_keys(void);
diff -Nurp tin-1.7.10/src/global.c tin-1.7.10.new/src/global.c
--- tin-1.7.10/src/global.c	2005-07-02 15:12:42.000000000 +0200
+++ tin-1.7.10.new/src/global.c	2005-10-17 20:08:11.956659000 +0200
@@ -357,7 +357,11 @@ handle_keypad(
 		t_function (*right_action) (void)),
 	const struct keylist keys)
 {
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wint_t ch = ReadWch();
+#else
 	int ch = ReadCh();
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	t_function func = NOT_ASSIGNED;
 
 	switch (ch) {
@@ -365,7 +369,7 @@ handle_keypad(
 #	ifdef HAVE_KEY_PREFIX
 		case KEY_PREFIX:
 #	endif /* HAVE_KEY_PREFIX */
-			switch (get_arrow_key(ch)) {
+			switch (get_arrow_key((int) ch)) {
 				case KEYMAP_UP:
 					func = GLOBAL_LINE_UP;
 					break;
diff -Nurp tin-1.7.10/src/keymap.c tin-1.7.10.new/src/keymap.c
--- tin-1.7.10/src/keymap.c	2005-08-27 12:34:15.000000000 +0200
+++ tin-1.7.10.new/src/keymap.c	2005-10-17 23:52:08.989071000 +0200
@@ -48,9 +48,13 @@ static void add_default_key(struct keyli
 static void add_global_keys(struct keylist *keys);
 static void free_keylist(struct keylist *keys);
 static void upgrade_keymap_file(char *old);
-static t_bool add_key(struct keylist *keys, const char key, t_function func, t_bool override);
 static t_bool process_keys(t_function func, const char *keys, struct keylist *kl);
 static t_bool process_mapping(char *keyname, char *keys);
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	static t_bool add_key(struct keylist *keys, const wchar_t key, t_function func, t_bool override);
+#else
+	static t_bool add_key(struct keylist *keys, const char key, t_function func, t_bool override);
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 
 struct keylist feed_post_process_keys = { NULL, 0, 0 };
 struct keylist feed_supersede_article_keys = { NULL, 0, 0 };
@@ -86,7 +90,11 @@ struct keylist thread_keys = { NULL, 0, 
  */
 t_function
 key_to_func(
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	const wchar_t key,
+#else
 	const char key,
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	const struct keylist keys)
 {
 	size_t i;
@@ -103,7 +111,11 @@ key_to_func(
 /*
  * lookup the associated key to the specified function
  */
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+wchar_t
+#else
 char
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 func_to_key(
 	t_function func,
 	const struct keylist keys)
@@ -115,7 +127,7 @@ func_to_key(
 			return keys.list[i].key;
 	}
 
-	return '?';
+	return (wchar_t) '?';
 }
 
 
@@ -127,7 +139,11 @@ func_to_key(
 static t_bool
 add_key(
 	struct keylist *keys,
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	const wchar_t key,
+#else
 	const char key,
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	t_function func,
 	t_bool override)
 {
@@ -166,6 +182,10 @@ add_key(
 }
 
 
+/*
+ * FIXME:
+ * as long as we use only ASCII for default keys no need to change 'keys' to wchar_t
+ */
 static void
 add_default_key(
 	struct keylist *key_list,
@@ -178,7 +198,7 @@ add_default_key(
 		return;
 
 	for (; *key != '\0'; key++)
-		add_key(key_list, *key, func, FALSE);
+		add_key(key_list, (wchar_t) *key, func, FALSE);
 }
 
 
@@ -229,26 +249,45 @@ free_keymaps(
 /*
  * Render ch in human readable ASCII
  * Is there no lib function to do this ?
+ * *buf must have a size of at least MAXKEYLEN
  */
 char *
 printascii(
 	char *buf,
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wint_t ch)
+#else
 	int ch)
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 {
 	if (ch == 0)
 		strcpy(buf, _("NULL"));
-	else if (isgraph(ch)) {	/* Regular printables */
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	else if (iswgraph(ch)) {	/* Regular printables */
+		int i;
+		i = wctomb(buf, ch);
+		if (i > 0)
+			buf[i] = '\0';
+		else
+			buf[0] = '\0';
+#else
+	else if (isgraph(ch)) {		/* Regular printables */
 		buf[0] = ch;
 		buf[1] = '\0';
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	} else if (ch == '\t') {	/* TAB */
 		strcpy(buf, _(txt_tab));
 	} else if ((ch == '\n') || (ch == '\r')) {	/* LF, CR */
 		strcpy(buf, _(txt_cr));
 	} else if (ch == ESC) {		/* Escape */
 		strcpy(buf, _(txt_esc));
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	} else if (iswcntrl(ch)) {	/* Control keys */
+#else
 	} else if (iscntrl(ch)) {	/* Control keys */
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 		buf[0] = '^';
-		buf[1] = (ch & 0xFF) + '@';
+		buf[1] = ((int) ch & 0xFF) + '@';
 		buf[2] = '\0';
 	} else if (ch == ' ')		/* SPACE */
 		strcpy(buf, _(txt_space));
@@ -381,7 +420,12 @@ process_keys(
 	struct keylist *kl)
 {
 	char *keydef, *tmp;
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wchar_t *wkeydef;
+	wchar_t key;
+#else
 	char key;
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	t_bool error, ret = TRUE;
 
 	tmp = my_strdup(keys);		/* don't change "keys" */
@@ -389,6 +433,16 @@ process_keys(
 
 	while (keydef != NULL) {
 		error = FALSE;
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+		if ((wkeydef = char2wchar_t(keydef)) == NULL) {
+			wait_message(1, _(txt_invalid_multibyte_sequence));
+			ret = FALSE;
+
+			keydef = strtok(NULL, KEYSEPS);
+			continue;
+		}
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
+		
 		/*
 		 * Parse the key sequence into 'key'
 		 * Special sequences are:
@@ -396,8 +450,13 @@ process_keys(
 		 * TAB -> ^I
 		 * SPACE -> ' '
 		 */
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+		if (wcslen(wkeydef) > 1) {
+			switch (wkeydef[0]) {	/* Only test 1st char - crude but effective */
+#else
 		if (strlen(keydef) > 1) {
 			switch (keydef[0]) {	/* Only test 1st char - crude but effective */
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 				case 'N':
 					key = '\0';
 					break;
@@ -411,8 +470,14 @@ process_keys(
 					break;
 
 				case '^':
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+					/* allow only ^A to ^Z */
+					if (wkeydef[1] >= 'A' && wkeydef[1] <= 'Z') {
+						key = ctrl(wkeydef[1]);
+#else
 					if (isupper((int)(unsigned char) keydef[1])) {
 						key = ctrl(keydef[1]);
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 						break;
 					}
 					/* FALLTHROUGH */
@@ -423,7 +488,11 @@ process_keys(
 					break;
 			}
 		} else {
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+			if (iswdigit(key = wkeydef[0])) {
+#else
 			if (isdigit(key = keydef[0])) {
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 				wait_message(0, _(txt_keymap_invalid_key), keydef);
 				ret = FALSE;
 				error = TRUE;
@@ -433,6 +502,9 @@ process_keys(
 		if (!error)
 			add_key(kl, key, func, TRUE);
 
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+		FreeIfNeeded(wkeydef);
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 		keydef = strtok(NULL, KEYSEPS);
 	}
 	free(tmp);
diff -Nurp tin-1.7.10/src/lang.c tin-1.7.10.new/src/lang.c
--- tin-1.7.10/src/lang.c	2005-06-28 10:31:24.000000000 +0200
+++ tin-1.7.10.new/src/lang.c	2005-10-17 23:52:29.778451000 +0200
@@ -451,6 +451,9 @@ of %s to be configured via a menu.\n\n\
 For more information read the manual page, README, INSTALL, TODO and FTP files.\n\
 Please send bug-reports/comments to %s with the 'R' command.\n");
 constext txt_invalid_from[] = N_("Invalid  From: %s  line. Read the INSTALL file again.");
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	constext txt_invalid_multibyte_sequence[] = N_("Invalid multibyte sequence found\n");
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 constext txt_invalid_sender[] = N_("Invalid  Sender:-header %s");
 constext txt_inverse_off[] = N_("Inverse video disabled");
 constext txt_inverse_on[] = N_("Inverse video enabled");
diff -Nurp tin-1.7.10/src/page.c tin-1.7.10.new/src/page.c
--- tin-1.7.10/src/page.c	2005-07-05 16:23:00.000000000 +0200
+++ tin-1.7.10.new/src/page.c	2005-10-17 23:40:43.397118000 +0200
@@ -1658,7 +1658,7 @@ draw_page_header(
 	cur_pos += strlen(tmp);
 	free(tmp);
 
-	if (note_h->org && cCOLS - cur_pos - 1 >= strlen(_(txt_at_s)) - 2 + 3) {
+	if (note_h->org && cCOLS - cur_pos - 1 >= (int) strlen(_(txt_at_s)) - 2 + 3) {
 		/* we have enough space to print at least " at ..." */
 		snprintf(buf, line_len, _(txt_at_s), note_h->org);
 
diff -Nurp tin-1.7.10/src/prompt.c tin-1.7.10.new/src/prompt.c
--- tin-1.7.10/src/prompt.c	2005-07-02 15:14:16.000000000 +0200
+++ tin-1.7.10.new/src/prompt.c	2005-10-17 23:09:30.940672000 +0200
@@ -163,19 +163,39 @@ prompt_yn(
 {
 	char *keyprompt;
 	char keyno[MAXKEYLEN], keyyes[MAXKEYLEN];
+	int keyyes_len = 0, keyno_len = 0, maxlen;
+	t_function func;
+#if defined (MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wint_t yes, no, prompt_ch, ch;
+	wchar_t *wtmp;
+#else
 	char yes, no, prompt_ch;
 	int ch;
-	size_t maxlen;
-	t_function func;
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 
 /*	fflush(stdin); */		/* Prevent finger trouble from making important decisions */
 
 	yes = func_to_key(PROMPT_YES, prompt_keys);
 	no = func_to_key(PROMPT_NO, prompt_keys);
 
+#if defined (MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	printascii(keyyes, (default_answer ? towupper(yes) : yes));
+	if ((wtmp = char2wchar_t(keyyes))) {
+		keyyes_len = wcswidth(wtmp, wcslen(wtmp));
+		free(wtmp);
+	}
+	printascii(keyno, (!default_answer ? towupper(no) : no));
+	if ((wtmp = char2wchar_t(keyno))) {
+		keyno_len = wcswidth(wtmp, wcslen(wtmp));
+		free(wtmp);
+	}
+#else
 	printascii(keyyes, (default_answer ? toupper(yes) : yes));
 	printascii(keyno, (!default_answer ? toupper(no) : no));
-	maxlen = MAX(strlen(keyyes), strlen(keyno));
+	keyyes_len = (int) strlen(keyyes);
+	keyno_len = (int) strlen(keyno);
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
+	maxlen = MAX(keyyes_len, keyno_len);
 
 	do {
 		prompt_ch = (default_answer ? yes : no);
@@ -185,14 +205,18 @@ prompt_yn(
 			MoveCursor(cLINES, 0);
 			CleartoEOLN();
 		}
-		my_printf("%s (%s/%s) %-*s", prompt, keyyes, keyno, (int) maxlen, keyprompt);
+		my_printf("%s (%s/%s) %-*s", prompt, keyyes, keyno, maxlen, keyprompt);
 		if (!cmd_line)
 			cursoron();
 		my_flush();
 		if (!cmd_line)
-			MoveCursor(cLINES, (int) strlen(prompt) + strlen(keyyes) + strlen(keyno) + 5);
+			MoveCursor(cLINES, (int) strlen(prompt) + keyyes_len + keyno_len + 5);
 
+#if defined (MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+		if (((ch = ReadWch()) == '\n') || (ch == '\r'))
+#else
 		if (((ch = (char) ReadCh()) == '\n') || (ch == '\r'))
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 			ch = prompt_ch;
 
 		switch (ch) {
@@ -200,7 +224,7 @@ prompt_yn(
 #	ifdef HAVE_KEY_PREFIX
 			case KEY_PREFIX:
 #	endif /* HAVE_KEY_PREFIX */
-				switch (get_arrow_key(ch)) {
+				switch (get_arrow_key((int) ch)) {
 					case KEYMAP_UP:
 					case KEYMAP_DOWN:
 						default_answer = bool_not(default_answer);
@@ -613,7 +637,11 @@ prompt_slk_response(
 	...)
 {
 	va_list ap;
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	wchar_t ch;
+#else
 	char ch;
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 	char buf[LEN];
 	t_function func;
 
@@ -622,14 +650,29 @@ prompt_slk_response(
 	va_end(ap);
 
 	prompt_slk_message = my_malloc(strlen(buf) + 2);
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+	{
+		char *tmp;
+		wchar_t wtmp[2] = { '\0', '\0' };
+		wtmp[0] = func_to_key(default_func, keys);
+		tmp = wchar_t2char(wtmp);
+		snprintf(prompt_slk_message, strlen(buf) + 2, "%s%s", buf, tmp);
+		FreeIfNeeded(tmp);
+	}
+#else
 	snprintf(prompt_slk_message, strlen(buf) + 2, "%s%c", buf, func_to_key(default_func, keys));
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 
 	input_context = cPromptSLK;
 
 	do {
 		prompt_slk_redraw();		/* draw the prompt */
 
+#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
+		if ((ch = ReadWch()) == '\r' || ch == '\n')
+#else
 		if ((ch = ReadCh()) == '\r' || ch == '\n')
+#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
 			func = default_func;
 		else
 			func = key_to_func(ch, keys);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.