Darwin のソースコードを入手・改変して、ls コマンドで1文字につき半角文字3つ分として計算される全角文字を半角2つぶんで表示させることに挑戦してみました。
wchar.h の関数を使うのは今回が初めてだったりします。
そのまま make しようとすると membership.h が無い旨のエラーが出るので Libinfo-*/membership.subproj からヘッダファイルをコピーして make します。
diff -u darwin8-file_cmds-ls/ls.c ls-wc/ls.c --- darwin8-file_cmds-ls/ls.c 2004-11-16 11:47:38.000000000 +0900 +++ ls-wc/ls.c 2007-04-06 22:44:26.000000000 +0900 @@ -68,6 +68,7 @@ #include <termcap.h> #include <signal.h> #endif +#include <wchar.h> #ifdef __APPLE__ #include <get_compat.h> #else @@ -630,6 +631,11 @@ bcfile = 0; flags = NULL; for (cur = list, entries = 0; cur; cur = cur->fts_link) { + wchar_t wc; + int mbclen; + int offset; + u_long namewidth; + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { warnx("%s: %s", cur->fts_name, strerror(cur->fts_errno)); @@ -654,8 +660,22 @@ continue; } } - if (cur->fts_namelen > maxlen) - maxlen = cur->fts_namelen; + /* Get wcwidth */ + namewidth = 0; + offset = 0; + mbclen = mbtowc(&wc, cur->fts_name, MB_CUR_MAX); + while (mbclen > 0) { + namewidth += wcwidth(wc); + offset += mbclen; + mbclen = mbtowc(&wc, cur->fts_name + offset, MB_CUR_MAX); + } + if (mbclen != -1) { + if (namewidth > maxlen) + maxlen = namewidth; + } else { + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + } if (f_octal || f_octal_escape) { u_long t = len_octal(cur->fts_name, cur->fts_namelen); Only in ls-wc: memberd_defines.h Only in ls-wc: membership.h Only in ls-wc: membershipPriv.h Only in ls-wc: ntsid.h diff -u darwin8-file_cmds-ls/print.c ls-wc/print.c --- darwin8-file_cmds-ls/print.c 2005-03-11 07:14:12.000000000 +0900 +++ ls-wc/print.c 2007-04-06 22:57:18.000000000 +0900 @@ -69,6 +69,7 @@ #include <termcap.h> #include <signal.h> #endif +#include <wchar.h> #include "ls.h" #include "extern.h" @@ -157,8 +158,24 @@ return prn_octal(name); else if (f_nonprint) return prn_printable(name); - else - return printf("%s", name); + else { + wchar_t wc; + int mbclen, offset, width; + + width = 0; + offset = 0; + mbclen = mbtowc(&wc, name, MB_CUR_MAX); + while (mbclen > 0) { + width += wcwidth(wc); + offset += mbclen; + mbclen = mbtowc(&wc, name + offset, MB_CUR_MAX); + } + if (width > 0 && mbclen != -1) { + (void)printf("%s", name); + return width; + } else + return printf("%s", name); + } } /*
このパッチを当てた ls では (ロケールが ja_JP.UTF-8 なら) ls -v で正しい幅で表示される・・・と思いきや、濁点があるファイル名でズレが発生。Mac OS X のファイルシステムの文字コードは NFKD で正規化された UTF-8 なわけですが、wcwidth() が返す値は合成用の文字でも 0 ではない模様。そもそも上記のコードでは 0 を返されては困るわけですが。
今回は標準 C ライブラリの関数を使いましたが、Markus Kuhn 氏の wcwidth.c もしくは ICU を使ったほうが良さげです。