Windowsのコマンドプロンプト上でUnicode文字を扱う

Windowsのコマンドプロンプト上でUnicode文字を扱う

- t tk の投稿
返信数: 5
Windows/W32TeXで、upTeXのファイル名に関する
QA:54205, QA:54206, QA:54207
について気になったので調べています。
Unicodeがらみの話題になりそうなので、TeX Q & AのBBSではなく、こちらのフォーラムに来ました。詳しい方、お付き合いをお願いします。

問題の "欲.tex" は、Shift_JIS(CP932)で2バイト目が7Eになっているのが
引っかかっている原因のようです。
この問題は、upTeXだけでなくluatexでも同様のようです。
「ファイル名はASCIIの範囲にするのが推奨」という現状も分かりますが、いつまで経っても解決しないのももどかしいので何とかしたい、という思いはあります。さらに、どうせならCP932の範囲ではなくて、Unicodeの範囲で扱えるようにしておきたいです。"♥.tex" とか。

Shift_JISの"欲"のように、従来のマルチバイトで2バイト目が7E(ASCIIの"~")になり得るような文字コードは、日本のShift_JISを含めて
↓のものがあるようです。
日本語 CodePage 932 Shift_JISのMS版
簡体中国語 CodePage 936 GBKのMS版
繁体中国語 CodePage 950 Big5のMS版
(韓国の CodePage 949 はマルチバイト文字に7Eを使用していない)
これらの言語版の Windows + W32TeX の luatex でも問題発生すると予測できます。日本ローカルの話でもないしupTeX限定の話でもなさそうなので、きちんと対処できれば国際的な開発の場でも取り入れてもらえるんじゃないか、という期待が持てます。

Windowsでは、C標準のfopenはANSI版(従来のマルチバイト)なのでUnicode文字列を扱えませんが、NT系列でファイル名はUTF-16LEで管理されていて適切にコーディングすれば、Unicodeのファイル名を扱えるようになるはずです。
コーディングのポイントはANSI版でなくてUnicode版でよければ、
(1) コマンドラインの読み込みに GetCommandLineW() を使う
(2) ファイルのopenに wfopen() を使う
でよいみたいです。

ANSIのfopenのwchar_t版でも _wfopen() でUnicodeのファイル名を扱えるそうです。参考: 奥村先生のblogの↓。
http://oku.edu.mie-u.ac.jp/~okumura/blog/node/2236

コマンドラインの文字コードをいろいろ想定するならかなり手の込んだことをやらないといけないようです。↓
ruby_dev Mailing list の 35339, 35343, 35345, 35347, 35348あたり
http://blade.nagaokaut.ac.jp/ruby/ruby-dev/35201-35400.shtml

要改造箇所は、
[1] コマンドラインでのUnicode文字列の読み込み
[2] ファイルのopen
[3] kpathsearchのUnicode対応
あたりでしょうか? 他に注意すべき点はありますか?

[1][2]は、Windows NT系列限定なら GetCommandLineW(), wopen() の使用で悩みませんが、「Windows 95/98/Me系列ならば従来のマルチバイトで動作する」のような両立を一つのバイナリーで実現することは可能なのでしょうか? あるいはもう95/98/Meを見捨てる選択もありかと思いますがどうなのでしょうか?
[3]は、kpathsearchの設定ファイルや文字コードなどの想定をUTF-8限定にしてしまえば、すぐに動きそうな気がしますが、そういう想定をしてしまうことに問題はありますか? Windows以外のプラットフォームでも、UTF-8決め打ちにしたら迷惑な環境はあるのでしょうか? 「pTeXでkpathsearchを使うときにShift_JIS/EUCで動いていたのに…」みたいな副作用があるのでしょうか?

漠然とした質問ですが、議論にお付き合いの程、よろしくお願いします。
t tk への返信

Re: Windowsのコマンドプロンプト上でUnicode文字を扱う

- Akira Kakuto の投稿
問題をよく理解していませんが、♥.tex も 欲.tex も
UTF-8 でファイル名を与えておけば、何の問題もありませんでした。
現状のターミナルでは UTF-8 で入力する方法がわからないので、
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *fn = "♥.tex";
char *fn2 = "欲.tex";
FILE *f;
char cmd[512];

f = fopen(fn, "wb");
fprintf(f, "This is a test.\n");
fprintf(f, "\\bye\n");
fclose(f);
strcpy(cmd, "uptex ");
strcat(cmd, fn);
system(cmd);
f = fopen(fn2, "wb");
fprintf(f, "This is a test.\n");
fprintf(f, "\\bye\n");
fclose(f);
strcpy(cmd, "uptex ");
strcat(cmd, fn2);
system(cmd);
return 0;
}
としましたが、♥.dvi, 欲.dvi ができています。

Akira Kakuto への返信

Re: Windowsのコマンドプロンプト上でUnicode文字を扱う

- Akira Kakuto の投稿
> 問題をよく理解していませんが

どうもとんちんかんなことを書いたようです。
忘れてください ^^;

Akira Kakuto への返信

Re: Windowsのコマンドプロンプト上でUnicode文字を扱う

- t tk の投稿
角藤先生、
お付き合いくださりありがとうございます。
コマンドプロンプトへは、コピペでUnicode文字を入力できます。

現状のW32TeXでは、当然ながらCP932外のUnicode文字を含む
kpsewhich ♥.tex
とか、
kpsewhich 한글.tex
とか、
kpsewhich 尾骶骨.tex
などは見つかりません。
また、CP932に含まれる
kpsewhich 髙.tex

kpsewhich 鄧.tex
などはたとえJIS第1,2水準外であっても見つかります。

Windows APIにはANSI版とワイド文字版があり、従来のものは通常ANSI版でありレガシーエンコーディングの範囲(日本語WindowsならCP932)が利用可。
Unicodeを扱う場合にはワイド文字版のAPIを使う必要があり、特に何もしないとWindows 95/98/Meでは動きませんが、UNICOWS.LIBというのをリンクしておけばWindows 95系(要UNICOWS.DLL)とWindows NT系の両方で動くそうです。
Wikipedia:Microsoft Layer for Unicode
単純にUnicodeが通るようにするのは、関連するAPIをUnicodeに置き換え、要りそうなところにUTF-16LEとUTF-8の変換を入れれば可能だと思っています。

↓参考になったところ
UNICODEプログラムの作り方(プログラム内部編)
UNICODEプログラムの作り方(補足編)
"Microsoft Layer for Unicode"のバグ疑惑

これはあくまで私の身勝手な案なのですが、kpathsearchをUnicode系(xetex, luatex, uptex)では、Unicode/UTF-8ベースで処理できるように、改造することは出来ないでしょうか?
おそらくbinaryを従来系とUnicode系で分けるのはがんばれば出来ますが、仕様上悩ましいのは texmf.cnf で今までレガシーエンコーディング(日本ならCP932)が動いていたはずのところをUTF-8と混在させるわけにもいかないだろう、ということです。
t tk への返信

Re: Windowsのコマンドプロンプト上でUnicode文字を扱う

- t tk の投稿
関連する内容で、コマンドプロンプト cmd.exe の文字コードをutf-8にする方法があることを知りました。
chcp 65001
で検索するといろいろ引っかかります。

Gauche の 2009/11/22 Windowsサポート のあたりや、そこから Windowsコンソール のあたりが参考になりました。
いろいろ奥が深そうなので、もう少し勉強します複雑な
t tk への返信

Re: Windowsのコマンドプロンプト上でUnicode文字を扱う

- t tk の投稿
もう4年前の投稿になりますが、そのころに出来たらよいなと考えていたことが大体実現でき、TeX Liveにも入れていただき、W32TeXにも採用していただきました。

結局、仕様はこうなりました。

texmf.cnf で、 command_line_encoding なる変数を用意する。
command_line_encoding で、"utf-8" または "utf8" を指定されていると、該当プログラムは、コマンドラインの文字列をUnicodeで解釈し、UTF-8の文字列として内部に取り込む。ファイルのopenなどは、Unicodeで行う。
command_line_encoding で、"" を指定、またはcommand_line_encodingの指定が全く無いと、該当プログラムは、コマンドラインの文字列を従来の文字コード(日本語WinodowsではCP932(Shift_JIS))で解釈し、従来の文字コードの文字列で内部に取り込む。

up(la)tex, dvipdfmx,dvips では、command_line_encoding=utf-8 と指定しておけば
uplatex 尾骶骨.tex
dvipdfmx 尾骶骨.dvi
kpsewhich -progname=uplatex 尾骶骨.tex
uplatex ♥.tex
dvips ♥.dvi
kpsewhich -progname=uplatex ♥.tex
などがうまく動く。
command_line_encoding.dvipdfmx=utf-8
などの書き方でプログラムごとにコマンドラインの文字コードの切り替えが出来る。
この動作に対応したプログラムは、(e)uptex, xetex, dvips, dvipdfmx, xdvipdfmx, kpsewhichなど。
( 残念ながら luatex では、luaコードにトラブルが出るとのことで不採用でした )

ls-Rの中の文字コードは何も配慮していないので、マクロやフォントのファイル名はASCIIの範囲にとどめておかないと、kpathsearchで見つけることが出来なくなる。

従来の動作も可能にしたまま、うまく拡張することが出来たと思っています。
Windows 95,98,Me系は全く考慮していませんが、XPですらサポート切れの時代なのでもういいでしょう。

以下は、開発に興味がある人へのご参考です。
改造は、大体以下のとおりでした。
コマンドライン、コンソールの読み書きに ReadConsoleW(), WriteConsoleW(), GetCommandLineW(), CommandLineToArgvW() などを使用する。
fopen(), popen(), spawnvp()の代わりに_wfopen(), _wpopen(), _wspawnvp()などを使用する。
command_line_encoding の設定状況に応じて、文字コードを読み替えつつ動作させるラッパー関数をkpathsearchライブラリに用意する。
アプリケーション側で使用する関数をラッパー関数に置き換える。

私はTeXLiveをWindows 7上、MinGW32でコンパイルしてテストしました。普通はlibtoolを使ったlinkが行われるようですが、その場合コマンドライン上のUnicodeが上手く渡されませんでした。ライブラリを直にlinkさせると上手く動きました。
このあたりは、私はよく分かっていません。

もう少し凝ると、「英語(非日本語)版Windows上でp(la)texを使う場合にCP932の範囲の漢字ファイル名を使う」「日本語版Windows上でp(la)texを使う場合に、内部コードをEUCにしつつ漢字ファイル名を使う」みたいなことも出来そうなのですが、需要も少ないでしょうし、ソースコードが若干複雑化するので自重しています。