xtermなどの端末でUTF-8を使っている時にフォント的には全角の記号やギリシャ文字が半角になってしまう時の対処法

追記

http://d.hatena.ne.jp/silenvx/20121015/1350233551
ここに続きを書きました。内容は幅を2にして含んではいけないcharmapをxtermでも正常に扱う方法です

参考にしたサイト

http://d.hatena.ne.jp/macks/20090305/p1

試した環境

os:archlinux
端末:xterm

やり方

まず適当な作業フォルダに移動してから

    % cp /usr/share/i18n/charmaps/UTF-8.gz UTF-8-CJK.gz

このようにUTF-8のcharmapをコピーしてきます

    % gzip -d UTF-8-CJK.gz

解凍します

    % vim UTF-8-CJK

エディタを起動して直接いじります

っと起動してからでなんですが半角になってしまう全角にしたい文字の文字コードを知る必要があります
しかしいちいち調べるのは面倒なので代わりに用意しましたが仕様なんてものは見ずに用意したので何か問題があるかもしれません
それでもいい方は
https://gist.github.com/3804212
ここにアップロードしておいたのでこの中にあるファイルの文字列を
さっきから開いているUTF-8-CJKの下の方(おそらく一番下)にあるWIDTHで始まる行からEND WIDTHで始まる行の間に貼り付けるだけです
貼り終えたら

    % gzip UTF-8-CJK
    # cp UTF-8-CJK.gz /usr/share/i18n/charmaps/

最後にこのcharmapを使えるように

    # vim /etc/locale.gen

ここに普段使っている${LANG} UTF-8-CJKのように書きます
私の場合は

    en_US.UTF-8 UTF-8-CJK

こうなりますが、既にen_US.UTF-8 UTF-8などがあればそれを書き換えます
保存し終えたら

    # locale-gen

後は再起動をするか端末を再起動させれば記号やギリシャ文字が全角の世界が待っているはずです

[追記] この下の文はやらなくても上の方に書いてある追記のリンク先で対応できます

がしかし
ダメだった場合は再起動をしなくてもうまくいったこちらを使ってください
https://gist.github.com/3804065
これらの違いは一番下の方に書いてあります
ただ、これは個人の環境によるものだと思うので後者のgistに貼ってあるものを使うのはやめたほうがいいかも

そもそもどうやって全角にしたい文字リストを作ったのかなど

最初は全角で表示できるようにset ambiwidth=doubleという設定があるvimソースコードからパクろうとしました

vimのくだりは飛ばしても問題ないです
    % wget ftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2

とりあえずソースコードを落とします

    % tar -xf vim-7.3.tar.bz2

それを解凍します

    % find vim73/ -type f -print0|xargs -0 grep -n 'ambiwidth'

まずは全ファイルからambiwidthの文字列があるファイルを探して当たりをつける
標準出力されたものの中で

    vim73/src/option.c:538:    {"ambiwidth",  "ambw",  P_STRING|P_VI_DEF|P_RCLR,
    vim73/src/option.c:5742:    /* 'ambiwidth' */
    vim73/src/option.h:313:EXTERN char_u    *p_ambw;        /* 'ambiwidth' */
    (以下略)

一番怪しそうなsrcディレクトリのファイルを見る(別にページャじゃなくてもいいけどこれが軽い)

    % less vim73/src/option.c

538行目はただの宣言っぽかったので
5742行目を見る
この近くでambiwidth関連で使われてそうな5744行目にある変数、p_ambwで再び検索をする

    % find vim73/ -type f -print0|xargs -0 grep -n 'p_ambw'

すると明らかにそれっぽいソースファイルが見つかるので見る

    % less vim73/src/mbyte.c

1207行目からそれっぽい文字列がたくさん書いてある
その上の1203行目と1204行目に説明らしきコメントが書いてあってどうやら vim73/runtime/tools/unicode.vim で生成されたものらしいので

    % less vim73/runtime/tools/unicode.vim

見に行くとunicode.orgというドメインへのアドレスがいくつか書いてあってここで初めてそのようなリストがあることを知った*1
その中でも一番怪しそうな
http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
を見に行く事にした*2
中を見てみると#以降がコメントで前の16進数が文字コードセミコロン(;)がただの区切りでその後ろの英字は
東アジアの文字幅 - Wikipedia
ここに書いてあるEast_Asian_Width特性の値のようだった*3
ここで試しに半角になってしまうギリシャ文字のアルファ(α)の03B1を見てみると

    03B1;A # GREEK SMALL LETTER ALPHA

このように書かれていて、Aは

    A (Ambiguous; 曖昧) - 文脈によって文字幅が異なる文字。東アジアの組版とそれ以外の組版の両方に
    出現し、東アジアの従来文字コードではいわゆる全角として扱われることがある。ギリシア文字や
    キリル文字など。

    wikipediaから引用:http://ja.wikipedia.org/wiki/%E6%9D%B1%E3%82%A2%E3%82%B8%E3%82%A2%E3%81%AE%E6%96%87%E5%AD%97%E5%B9%85

そういうことらしかった。つまりこのEast_Asian_Width特性の値がAの文字をこの環境では全角ですよって教えてあげればいいみたいなので
さっそくさっきのテキストファイルからファイルをとってcharmapに書ける文字列を出力するシェルスクリプトを書きました

    #!/bin/sh
    FLAG_TIME='0'
    FLAG_START='0'
    PREV_VAL='-2'
    IFS=$'\n'
    for TMP1 in `wget -O - 'http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt'|grep ';A' 2>/dev/null`;do
        TMP2="${TMP1%%\#*}"
        TMP2="${TMP2%;A*}"
        echo "${TMP2}"|grep -v '\.\.' >/dev/null 2>&1
        if [ "${?}" = '0' ];then
            NOW_VAL=`printf '%d\n' 0x"${TMP2%;A*}"`
            if [ "${NOW_VAL}" == `expr "${PREV_VAL}" + 1` ];then
                END_VAL="${TMP2}"
                FLAG_TIME=`expr "${FLAG_TIME}" + 1`
            else
                if [ "${FLAG_START}" != '0' ];then
                    case `echo "${#START_VAL} % 4"|bc` in
                        '0');;
                        '1')START_VAL="000${START_VAL}";;
                        '2')START_VAL="00${START_VAL}";;
                        '3')START_VAL="0${START_VAL}";;
                    esac
                    if [ "${FLAG_TIME}" == '0' ];then
                        echo "<U${START_VAL}> 2"
                    else
                        case `echo "${#END_VAL} % 4"|bc` in
                            '0');;
                            '1')END_VAL="000${END_VAL}";;
                            '2')END_VAL="00${END_VAL}";;
                            '3')END_VAL="0${END_VAL}";;
                        esac
                        echo "<U${START_VAL}>...<U${END_VAL}> 2"
                    fi
                    FLAG_START='0'
                    FLAG_TIME='0'
                fi
                START_VAL="${TMP2}"
                FLAG_START='1'
            fi
            PREV_VAL="${NOW_VAL}"
        else
            TMP3="${TMP2%%.*}"
            TMP4="${TMP2##*.}"
            case `echo "${#TMP3} % 4"|bc` in
                '0');;
                '1')TMP3="000${TMP3}";;
                '2')TMP3="00${TMP3}";;
                '3')TMP3="0${TMP3}";;
            esac
            case `echo "${#TMP4} % 4"|bc` in
                '0');;
                '1')TMP4="000${TMP4}";;
                '2')TMP4="00${TMP4}";;
                '3')TMP4="0${TMP4}";;
            esac
            echo "<U${TMP3}>...<U${TMP4}> 2"
        fi
    done

このシェルスクリプトを動かすにはwgetとbcが必須です
一見、無駄な処理が多いように見えるのは文字コードをまとめる処理をわざわざ書いたからです

注意点としては仕様なんて見ずに書いたことと
一部の文字コードを含めるとなぜか設定が有効にならないことです(端末の再起動でしか確かめていないのでシステムを再起動させた場合はわかりません)

その文字コード

    <U03C3>...<U03C9> 2
    と
    <U24EB>...<U254B> 2

ですが後者の方は設定が無効にならない文字コードも含まれていて、最終的に混ぜちゃいけないのは
最初に書いた

    <U03C3>...<U03C9> 2


後者の中の

    <U2502> 2
    <U251C> 2
    <U2524> 2
    <U252C> 2
    <U2534> 2

この文字コードが入っているとアルファ(α)などが全角になってくれませんでした
そういうわけで最後に貼ってあるgistにはこれらの文字コードが含まれないようにしているのであしからず
ちなみに最初に貼ってある方は削ってないです
本当はコメントアウトして混ぜてもいいと思うのですが、コメントは

    <comment_char>

で自由に変更ができるのでやめておきました

U03C3はシグマみたいですがなぜダメなんだろう…きっとシステムを再起動すればあっても良さそうだけどこの記事を書いているときはわけあって再起動できない…。

*1:ないほうがおかしいんだけども…

*2:後で知ったのですが参考にしたサイトにリンクがありましたね

*3:あっているか知りません