パスワードを忘れた? アカウント作成
258529 journal

dodaの日記: Emacs 23.2.1 でのカーソルキー動作不良

日記 by doda

Emacs 23.2.1 を PuTTY で使用すしている時、keyboard-coding-system を euc-japan にすると
カーソルキーがまともに動作しないという話を見かけた。
興味があったし、もしかすると Tera Term にも関係するかもしれないと思ったので、
現実逃避がてらに調べてみる事にした。

Emacs は起動時にカーソルキーをアプリケーションモードに設定する。
vt100 互換端末がアプリケーションカーソルモード時に送出するキーシーケンスは、

  • 上: <ESC>OA
  • 下: <ESC>OB
  • 右: <ESC>OC
  • 左: <ESC>OD

となっている。
これらの先頭の 2 文字の <ESC>O というのは、ISO/IEC 2022 の SS3 (Single Shift 3) と同じである為、

  • ISO 2022 系
  • SS3 を使う
  • G3 になんらかの文字セットが指示されている

これらの条件を全て満たすエンコーディングの場合に SS3 と誤認して動作がおかしくなっているようだった。
# これらを全てみたすのは日本語 EUC 系のみ。

アプリケーションカーソルキーのシーケンスを誤認しているという事は vt100 互換端末が全滅だよなと思い
確認してみると、PuTTY の他にも Tera Term, xterm 等でも現象が出た。
とりあえず、PuTTY や Tera Term ではアプリケーションカーソルモードを無視するように設定すれば
問題なく動きそうなのは確認したけれど、Emacs 以外、たとえば less 等がまともに動かなくなる。

アプリケーションカーソルキーのシーケンスが SS3 とバッティングしている事が問題なので、
対処が難しそう(完璧な対処は無理)だと最初は思ったが、EUC では SS3 で G3 を呼び出す先が
GR 領域 (0xA1~0xFE) なので判断は付く事に気が付いた。
ただ、普段は vi を使い Emacs は全く使わないし、対処するのも面倒だったので取り合えず見なかった事にした。
# Tera Term にも影響は出ているけれど、Emacs のバグと言って逃げられるし

…つもりだったけれど、ISO 2022 は大雑把にしか把握していないので、ISO 2022 の勉強の為にいじってみる事にした。
# 将来的に必要になるし

まずは ISO 2022 を読む所から始めようとしたが持っていないので、同等な ECMA-035 を入手した。
で、読み始めたのだが、根性無しなのですぐに挫折。代わりに日本工業標準調査会JIS 検索から JIS X 0202 を閲覧した。
大体は把握していた通り。
SS3 で GR に呼び出した後に GL の文字を使った時の挙動について書かれていないか確認したが、そんな物は無かった。
まあ、取り合えずは不正なコード扱いでいいかと思っておく事にする。

今回は euc-jp 関連のみに影響が出た問題の修正だけれど、出来るだけ汎用的な修正にしたいと思ったので、
まず single shift を使っているエンコーディングを確認。

  • iso-2022-jp-2 -- 7bit, GL 呼び出し(*1)
  • iso-2022-8bit (euc-jp) -- 8bit, GR 呼び出し
  • eucjp-ms -- 8bit, GR 呼び出し
  • euc-jis-2004 -- 8bit, GR 呼び出し
  • iso-2022-cn -- 7bit, GL 呼び出し(*1)
  • iso-2022-cn-ext -- 7bit, GL 呼び出し(*1)

*1: 7bitコードなので GL とは呼ばないが、便宜上 GL 呼び出しとしておく

修正の方針としては、SS2/SS3 の次に来る文字が G2/G3 の呼び出された先の領域内かをチェックする。
本当ならば SS2/SS3 で G2/G3 が GL/GR のどちらに呼び出されるかフラグを持たせた方がいいのだが、
上記のように 7bit -> GL, 8bit -> GR という関係が成り立っているのでこれを使う事にした。
以下が修正内容。

--- src/coding.c.orig    2010-04-04 07:26:13.000000000 +0900
+++ src/coding.c    2010-09-24 16:42:33.000000000 +0900
@@ -3853,8 +3853,14 @@
           else
         charset = CHARSET_FROM_ID (charset_id_2);
           ONE_MORE_BYTE (c1);
-          if (c1 < 0x20 || (c1 >= 0x80 && c1 < 0xA0))
-        goto invalid_code;
+          if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_SEVEN_BITS) {
+        if (c1 < 0x20 || c1 >= 0x80)
+          goto invalid_code;
+          }
+          else {
+        if (c1 < 0xA0)
+          goto invalid_code;
+          }
           break;
 
         case 'O':        /* invocation of single-shift-3 */
@@ -3867,8 +3873,14 @@
           else
         charset = CHARSET_FROM_ID (charset_id_3);
           ONE_MORE_BYTE (c1);
-          if (c1 < 0x20 || (c1 >= 0x80 && c1 < 0xA0))
-        goto invalid_code;
+          if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_SEVEN_BITS) {
+        if (c1 < 0x20 || c1 >= 0x80)
+          goto invalid_code;
+          }
+          else {
+        if (c1 < 0xA0)
+          goto invalid_code;
+          }
           break;
 
         case '0': case '2':    case '3': case '4': /* start composition */

取り合えずこんな修正で動いているっぽい。
SS2/SS3 で GL に呼び出すエンコーディングの場合はこの修正でも対応出来ないが(そもそも完璧に判別する事が無理)、
幸いな事に iso-2022-jp-2, iso-2022-cn は SS2 のみを使用するし、iso-2022-cn-ext は SS3 も利用するが
初期状態では G3 になにも指示されていない為問題が出ない。

懸念事項としては、SS2/SS3 の呼び出し先の判別を 7bit/8bit で行っている事だが、実用上は問題ないのでこのまま。
問題が出るような状況(8bit なのに GL 呼び出しなエンコーディングが追加される等)になったら、
困った人が自分で直してください。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

日々是ハック也 -- あるハードコアバイナリアン

読み込み中...