tmux と East Asian Ambiguous Width Character

tmux は、East Asian Ambiguous Width Character に対応していないため、▽や×のような文字が幅1として見做されてしまいます。
これに対応していないと、Emacs を -nw で動かしたときなどで、上記文字を打つと、おかしな事になります。
とりあえず、場当たり的なもので、同じような処理を追加してたり綺麗ではありませんが、patch を作ってみました。
簡単に動作確認した限りでは動いている風だったので、ログ的な意味と、まあ何かの参考になればという事で、ここに貼っておきます。



Ambiguous Width Character のリストは、以下の URL にある一覧から抜き出してきました。

Ambiguous Width Character は、 A とついている文字で、それらについては全部対応したはず……、たぶん。


現状、Ambiguous Width Character を幅1と見做すか、幅2と見做すかを設定可能に作ってません。全部幅2になります。
# まあ、いいかなと。面倒だったのでw


パッチは、tmux 1.1 を対象に作りました。ファイルは、utf8.c しかいじってないです。
テストについては、深く行っていないので、不具合がある可能性がけっこう高いです。使う場合は、そこらへん理解した上でお願いします。

--- ../tmux-1.1-org/utf8.c	2009-10-24 02:21:34.000000000 +0900
+++ ./utf8.c	2010-01-22 21:35:32.000000000 +0900
@@ -193,7 +193,195 @@
 	{ 0x10a05, 0x10a06, 0, NULL, NULL },
 };
 
+/* Random order */
+struct utf8_width_entry utf8_cjkwidth_table[] = {
+	{ 0x02605, 0x02606, 2, NULL, NULL },
+	{ 0x000F7, 0x000FA, 2, NULL, NULL },
+	{ 0x001DC, 0x001DC, 2, NULL, NULL },
+	{ 0x025BC, 0x025BD, 2, NULL, NULL },
+	{ 0x1F190, 0x1F190, 2, NULL, NULL },
+	{ 0x02660, 0x02661, 2, NULL, NULL },
+	{ 0x026E8, 0x026FF, 2, NULL, NULL },
+	{ 0x02020, 0x02022, 2, NULL, NULL },
+	{ 0x000E6, 0x000E6, 2, NULL, NULL },
+	{ 0x0221D, 0x02220, 2, NULL, NULL },
+	{ 0x00113, 0x00113, 2, NULL, NULL },
+	{ 0x003A3, 0x003A9, 2, NULL, NULL },
+	{ 0x00300, 0x0036F, 2, NULL, NULL },
+	{ 0x021B8, 0x021B9, 2, NULL, NULL },
+	{ 0x02170, 0x02179, 2, NULL, NULL },
+	{ 0x00152, 0x00153, 2, NULL, NULL },
+	{ 0x000F2, 0x000F3, 2, NULL, NULL },
+	{ 0x02642, 0x02642, 2, NULL, NULL },
+	{ 0x025C0, 0x025C1, 2, NULL, NULL },
+	{ 0x025EF, 0x025EF, 2, NULL, NULL },
+	{ 0x001D4, 0x001D4, 2, NULL, NULL },
+	{ 0x00261, 0x00261, 2, NULL, NULL },
+	{ 0x0220B, 0x0220B, 2, NULL, NULL },
+	{ 0x021D2, 0x021D2, 2, NULL, NULL },
+	{ 0x02640, 0x02640, 2, NULL, NULL },
+	{ 0x000E8, 0x000EA, 2, NULL, NULL },
+	{ 0x02035, 0x02035, 2, NULL, NULL },
+	{ 0x026BE, 0x026BF, 2, NULL, NULL },
+	{ 0x022BF, 0x022BF, 2, NULL, NULL },
+	{ 0x020AC, 0x020AC, 2, NULL, NULL },
+	{ 0x025C6, 0x025C8, 2, NULL, NULL },
+	{ 0x000AD, 0x000AE, 2, NULL, NULL },
+	{ 0x02103, 0x02103, 2, NULL, NULL },
+	{ 0x002DF, 0x002DF, 2, NULL, NULL },
+	{ 0x02189, 0x02199, 2, NULL, NULL },
+	{ 0xF0000, 0xFFFFD, 2, NULL, NULL },
+	{ 0x0207F, 0x0207F, 2, NULL, NULL },
+	{ 0x0201C, 0x0201D, 2, NULL, NULL },
+	{ 0x00148, 0x0014B, 2, NULL, NULL },
+	{ 0x0FE00, 0x0FE0F, 2, NULL, NULL },
+	{ 0x02116, 0x02116, 2, NULL, NULL },
+	{ 0x025B2, 0x025B3, 2, NULL, NULL },
+	{ 0x02010, 0x02010, 2, NULL, NULL },
+	{ 0x1F131, 0x1F131, 2, NULL, NULL },
+	{ 0x02B55, 0x02B59, 2, NULL, NULL },
+	{ 0x000B0, 0x000B4, 2, NULL, NULL },
+	{ 0x002DD, 0x002DD, 2, NULL, NULL },
+	{ 0x0215B, 0x0215E, 2, NULL, NULL },
+	{ 0x0261C, 0x0261C, 2, NULL, NULL },
+	{ 0x002C9, 0x002CB, 2, NULL, NULL },
+	{ 0x1F142, 0x1F142, 2, NULL, NULL },
+	{ 0x02074, 0x02074, 2, NULL, NULL },
+	{ 0x000BC, 0x000BF, 2, NULL, NULL },
+	{ 0x0013F, 0x00142, 2, NULL, NULL },
+	{ 0x1F157, 0x1F157, 2, NULL, NULL },
+	{ 0x0011B, 0x0011B, 2, NULL, NULL },
+	{ 0x100000, 0x10FFFD, 2, NULL, NULL },
+	{ 0x1F17F, 0x1F17F, 2, NULL, NULL },
+	{ 0x02776, 0x0277F, 2, NULL, NULL },
+	{ 0x02223, 0x02223, 2, NULL, NULL },
+	{ 0x02592, 0x02595, 2, NULL, NULL },
+	{ 0x1F14A, 0x1F14E, 2, NULL, NULL },
+	{ 0x0223C, 0x0223D, 2, NULL, NULL },
+	{ 0x000A7, 0x000A8, 2, NULL, NULL },
+	{ 0x02013, 0x02016, 2, NULL, NULL },
+	{ 0x0212B, 0x0212B, 2, NULL, NULL },
+	{ 0x025B6, 0x025B7, 2, NULL, NULL },
+	{ 0x02609, 0x02609, 2, NULL, NULL },
+	{ 0x00410, 0x0044F, 2, NULL, NULL },
+	{ 0x0226E, 0x0226F, 2, NULL, NULL },
+	{ 0x00401, 0x00401, 2, NULL, NULL },
+	{ 0x00101, 0x00101, 2, NULL, NULL },
+	{ 0x02211, 0x02211, 2, NULL, NULL },
+	{ 0x03248, 0x0324F, 2, NULL, NULL },
+	{ 0x0E000, 0x0F8FF, 2, NULL, NULL },
+	{ 0x0266F, 0x0266F, 2, NULL, NULL },
+	{ 0x0266C, 0x0266D, 2, NULL, NULL },
+	{ 0x000D7, 0x000D8, 2, NULL, NULL },
+	{ 0x1F13D, 0x1F13D, 2, NULL, NULL },
+	{ 0x003B1, 0x003C1, 2, NULL, NULL },
+	{ 0x02614, 0x02615, 2, NULL, NULL },
+	{ 0x0203B, 0x0203B, 2, NULL, NULL },
+	{ 0x02252, 0x02252, 2, NULL, NULL },
+	{ 0x02105, 0x02105, 2, NULL, NULL },
+	{ 0x025E2, 0x025E5, 2, NULL, NULL },
+	{ 0x02160, 0x0216B, 2, NULL, NULL },
+	{ 0x0221A, 0x0221A, 2, NULL, NULL },
+	{ 0x02126, 0x02126, 2, NULL, NULL },
+	{ 0x021E7, 0x021E7, 2, NULL, NULL },
+	{ 0x002D8, 0x002DB, 2, NULL, NULL },
+	{ 0x00126, 0x00127, 2, NULL, NULL },
+	{ 0x1F110, 0x1F12D, 2, NULL, NULL },
+	{ 0x002CD, 0x002CD, 2, NULL, NULL },
+	{ 0x0260E, 0x0260F, 2, NULL, NULL },
+	{ 0x02153, 0x02154, 2, NULL, NULL },
+	{ 0x1F18A, 0x1F18D, 2, NULL, NULL },
+	{ 0x0012B, 0x0012B, 2, NULL, NULL },
+	{ 0x00451, 0x00451, 2, NULL, NULL },
+	{ 0x02460, 0x024E9, 2, NULL, NULL },
+	{ 0x000C6, 0x000C6, 2, NULL, NULL },
+	{ 0x02264, 0x02267, 2, NULL, NULL },
+	{ 0x1F17B, 0x1F17C, 2, NULL, NULL },
+	{ 0x025CB, 0x025CB, 2, NULL, NULL },
+	{ 0x001DA, 0x001DA, 2, NULL, NULL },
+	{ 0x02282, 0x02283, 2, NULL, NULL },
+	{ 0x0016B, 0x0016B, 2, NULL, NULL },
+	{ 0x02024, 0x02027, 2, NULL, NULL },
+	{ 0x001D2, 0x001D2, 2, NULL, NULL },
+	{ 0x025CE, 0x025D1, 2, NULL, NULL },
+	{ 0x002D0, 0x002D0, 2, NULL, NULL },
+	{ 0x025A3, 0x025A9, 2, NULL, NULL },
+	{ 0x02018, 0x02019, 2, NULL, NULL },
+	{ 0x02202, 0x02203, 2, NULL, NULL },
+	{ 0x1F179, 0x1F179, 2, NULL, NULL },
+	{ 0x0222E, 0x0222E, 2, NULL, NULL },
+	{ 0x00138, 0x00138, 2, NULL, NULL },
+	{ 0x000AA, 0x000AA, 2, NULL, NULL },
+	{ 0x02121, 0x02122, 2, NULL, NULL },
+	{ 0x000D0, 0x000D0, 2, NULL, NULL },
+	{ 0x02757, 0x02757, 2, NULL, NULL },
+	{ 0x024EB, 0x0254B, 2, NULL, NULL },
+	{ 0x025A0, 0x025A1, 2, NULL, NULL },
+	{ 0x02032, 0x02033, 2, NULL, NULL },
+	{ 0x02109, 0x02109, 2, NULL, NULL },
+	{ 0x00131, 0x00133, 2, NULL, NULL },
+	{ 0x0220F, 0x0220F, 2, NULL, NULL },
+	{ 0x00166, 0x00167, 2, NULL, NULL },
+	{ 0x026CF, 0x026E1, 2, NULL, NULL },
+	{ 0x02225, 0x02225, 2, NULL, NULL },
+	{ 0x022A5, 0x022A5, 2, NULL, NULL },
+	{ 0x001D8, 0x001D8, 2, NULL, NULL },
+	{ 0x02260, 0x02261, 2, NULL, NULL },
+	{ 0x02215, 0x02215, 2, NULL, NULL },
+	{ 0x02200, 0x02200, 2, NULL, NULL },
+	{ 0x0224C, 0x0224C, 2, NULL, NULL },
+	{ 0x001D6, 0x001D6, 2, NULL, NULL },
+	{ 0x0014D, 0x0014D, 2, NULL, NULL },
+	{ 0x0273D, 0x0273D, 2, NULL, NULL },
+	{ 0x02248, 0x02248, 2, NULL, NULL },
+	{ 0x021D4, 0x021D4, 2, NULL, NULL },
+	{ 0x02207, 0x02208, 2, NULL, NULL },
+	{ 0x003C3, 0x003C9, 2, NULL, NULL },
+	{ 0x0203E, 0x0203E, 2, NULL, NULL },
+	{ 0x001D0, 0x001D0, 2, NULL, NULL },
+	{ 0x002C4, 0x002C4, 2, NULL, NULL },
+	{ 0x00111, 0x00111, 2, NULL, NULL },
+	{ 0x026E3, 0x026E3, 2, NULL, NULL },
+	{ 0x00144, 0x00144, 2, NULL, NULL },
+	{ 0x0261E, 0x0261E, 2, NULL, NULL },
+	{ 0x02227, 0x0222C, 2, NULL, NULL },
+	{ 0xE0100, 0xE01EF, 2, NULL, NULL },
+	{ 0x0226A, 0x0226B, 2, NULL, NULL },
+	{ 0x000F0, 0x000F0, 2, NULL, NULL },
+	{ 0x1F15F, 0x1F15F, 2, NULL, NULL },
+	{ 0x02550, 0x02573, 2, NULL, NULL },
+	{ 0x00391, 0x003A1, 2, NULL, NULL },
+	{ 0x002C7, 0x002C7, 2, NULL, NULL },
+	{ 0x02299, 0x02299, 2, NULL, NULL },
+	{ 0x02663, 0x02665, 2, NULL, NULL },
+	{ 0x000DE, 0x000E1, 2, NULL, NULL },
+	{ 0x02081, 0x02084, 2, NULL, NULL },
+	{ 0x000A4, 0x000A4, 2, NULL, NULL },
+	{ 0x02113, 0x02113, 2, NULL, NULL },
+	{ 0x02312, 0x02312, 2, NULL, NULL },
+	{ 0x000EC, 0x000ED, 2, NULL, NULL },
+	{ 0x02030, 0x02030, 2, NULL, NULL },
+	{ 0x1F100, 0x1F10A, 2, NULL, NULL },
+	{ 0x02295, 0x02295, 2, NULL, NULL },
+	{ 0x02580, 0x0258F, 2, NULL, NULL },
+	{ 0x00251, 0x00251, 2, NULL, NULL },
+	{ 0x1F13F, 0x1F13F, 2, NULL, NULL },
+	{ 0x000B6, 0x000BA, 2, NULL, NULL },
+	{ 0x001CE, 0x001CE, 2, NULL, NULL },
+	{ 0x02667, 0x0266A, 2, NULL, NULL },
+	{ 0x000FE, 0x000FE, 2, NULL, NULL },
+	{ 0x02286, 0x02287, 2, NULL, NULL },
+	{ 0x000FC, 0x000FC, 2, NULL, NULL },
+	{ 0x0FFFD, 0x0FFFD, 2, NULL, NULL },
+	{ 0x0269E, 0x0269F, 2, NULL, NULL },
+	{ 0x02234, 0x02237, 2, NULL, NULL },
+	{ 0x1F146, 0x1F146, 2, NULL, NULL },
+	{ 0x000A1, 0x000A1, 2, NULL, NULL },
+	{ 0x026C4, 0x026CD, 2, NULL, NULL },
+};
+
 struct utf8_width_entry	*utf8_width_root = NULL;
+struct utf8_width_entry	*utf8_cjkwidth_root = NULL;
 
 int	utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *);
 u_int	utf8_combine(const struct utf8_data *);
@@ -286,6 +474,21 @@
 		}
 		*ptr = item;
 	}
+
+	/* for cjk ambiguous width. */
+	for (i = 0; i < nitems(utf8_cjkwidth_table); i++) {
+		item = &utf8_cjkwidth_table[i];
+
+		ptr = &utf8_cjkwidth_root;
+		while (*ptr != NULL) {
+			node = *ptr;
+			if (item->last < node->first)
+				ptr = &(node->left);
+			else if (item->first > node->last)
+				ptr = &(node->right);
+		}
+		*ptr = item;
+	}
 }
 
 /* Combine UTF-8 into 32-bit Unicode. */
@@ -327,6 +530,17 @@
 
 	value = utf8_combine(utf8data);
 
+	/* for cjk width */
+	item = utf8_cjkwidth_root;
+	while (item != NULL) {
+		if (value < item->first)
+			item = item->left;
+		else if (value > item->last)
+			item = item->right;
+		else
+			return (item->width);
+	}
+
 	item = utf8_width_root;
 	while (item != NULL) {
 		if (value < item->first)


そんなこんなで。

更新時刻

  • 2010/01/22/22:00