Win32 Unicode console output

Tietew です。

VS2008 でUnicode文字列のコンソール出力ができたのでとりあえずなパッチを投
稿します。VC++6 等、msvcrt.dll の環境で使えるかどうか不明です。

こんな感じ:

D:\Projects\ruby\trunk\win32\dist\usr\bin>chcp
現在のコード ページ: 932
D:\Projects\ruby\trunk\win32\dist\usr\bin>ruby -ve ‘puts
“\xE4\xBD\xA0\xE5\xA5\xBD!”.force_encoding(“UTF-8”)’
ruby 1.9.0 (2008-06-22 revision 17533) [i386-mswin32_90]
ä½ å¥½!

ã€Œä½ ã€ã¯cp932に存在しない文字。

rb_write_error2 のパッチが無理矢理っぽいのは、最初 IO#write を通そうとし
たら無限ループにはまってしまったので(バグ?)、とりあえず回避したもので
す。

Index: io.c

— io.c (revision 17534)
+++ io.c (working copy)
@@ -4952,6 +4952,11 @@
rb_write_error2(const char *mesg, long len)
{
if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
+#ifdef WIN32

  • LPWSTR wmesg = ALLOCA_N(WCHAR, len + 16);

  • len = MultiByteToWideChar(CP_ACP, 0, mesg, len, wmesg, len + 16) *
    sizeof(WCHAR);

  • mesg = (const char *)wmesg;
    +#endif
    fwrite(mesg, sizeof(char), len, stderr);
    }
    else {
    Index: ruby.c
    ===================================================================
    — ruby.c (revision 17534)
    +++ ruby.c (working copy)
    @@ -22,6 +22,7 @@
    #include “ruby/ruby.h”
    #include “ruby/node.h”
    #include “ruby/encoding.h”
    +#include “ruby/io.h”
    #include “eval_intern.h”
    #include “dln.h”
    #include <stdio.h>
    @@ -1096,6 +1097,23 @@
    enc = lenc;
    }
    rb_enc_set_default_external(rb_enc_from_encoding(enc));
    +#ifdef WIN32

  • {

  • int utf16le = rb_enc_find_index(“UTF-16LE”);

  • if(utf16le > 0) {

  •  rb_encoding *enc = rb_enc_from_index(utf16le);
    
  •  rb_io_t *fptr;
    
  •  /* stdout */
    
  •  GetOpenFile(rb_stdout, fptr);
    
  •  _setmode(fptr->fd, _O_WTEXT);
    
  •  fptr->enc = enc;
    
  •  /* stderr */
    
  •  GetOpenFile(rb_stderr, fptr);
    
  •  _setmode(fptr->fd, _O_WTEXT);
    
  •  fptr->enc = enc;
    
  • }

  • }
    +#endif

    rb_set_safe_level_force(safe);
    if (opt->e_script) {

成瀬です。

Tietew wrote:

ruby 1.9.0 (2008-06-22 revision 17533) [i386-mswin32_90]
ä½ å¥½!

ã€Œä½ ã€ã¯cp932に存在しない文字。

rb_write_error2 のパッチが無理矢理っぽいのは、最初 IO#write を通そうとし
たら無限ループにはまってしまったので(バグ?)、とりあえず回避したもので
す。

実装をどうするかはさておき、とりあえず、
コンソール (正確にはそのような変換を入れていいケース) のみで
変換をするにはどうするのがよいか、というのが課題かと思います。

Window のコンソールに関しては、この自動変換は問題がないと思うのですが、
他の OS の他のインターフェイスではどうかとかはまたあらためて。

とりあえず each を使ってしまう初心者の木村です。

On Sun, 22 Jun 2008 17:09:51 +0900
“NARUSE, Yui” [email protected] wrote:

成瀬です。

Tietew wrote:

Tietew です。

VS2008 でUnicode文字列のコンソール出力ができたのでとりあえずなパッチを投
稿します。VC++6 等、msvcrt.dll の環境で使えるかどうか不明です。
(ç•¥)
実装をどうするかはさておき、とりあえず、
コンソール (正確にはそのような変換を入れていいケース) のみで
変換をするにはどうするのがよいか、というのが課題かと思います。

Window のコンソールに関しては、この自動変換は問題がないと思うのですが、
他の OS の他のインターフェイスではどうかとかはまたあらためて。

あんまり #ifdef を増やしたり実行時の条件判定を増やさない方がいいとは
思うんですが、とりあえずコンソールに出すということを重視するなら、
Windowsに関してはfwriteに投げないで、Win32API(WriteConsoleW)を直接呼ぶとか、
あるいはリダイレクトしているいないで変換するかどうか決めた方がいいかなという
気はします。

ファイルにリダイレクトしているときは変換されない方がいいんじゃないかと。
って

コンソール (正確にはそのような変換を入れていいケース) のみで
変換をする

ですね。

#あーWriteConsoleWって 9xは使えない?

成瀬です。

KIMURA Koichi wrote:

VS2008 でUnicode文字列のコンソール出力ができたのでとりあえずなパッチを投
思うんですが、とりあえずコンソールに出すということを重視するなら、
Windowsに関してはfwriteに投げないで、Win32API(WriteConsoleW)を直接呼ぶとか、
あるいはリダイレクトしているいないで変換するかどうか決めた方がいいかなという
気はします。

GetConsole を呼んで、コンソールか調べてから、WriteConsoleW を呼べばいいっぽいですね。

#あーWriteConsoleWって 9xは使えない?

Win2k 以降のようですね。
9x まだ切っちゃダメかなぁ。

e$B$3$s$K$A$O!"$J$+$`$ie(B(e$B$&e(B)e$B$G$9!#e(B

In message “[ruby-dev:35206] Re: Win32 Unicode console output”
on Jun.23,2008 23:36:51, [email protected] wrote:
| GetConsole e$B$r8F$s$G!"%3%s%=!<%k$+D4$Y$F$+$i!"e(BWriteConsoleW e$B$r8F$Y$P$$$$$C$]$$$G$9$M!#e(B

GetConsole()e$B$G$o$+$k$N$+$J$“e(B…
e$B$J$*e(Bwin32/win32.ce$B$K$Oe(Bis_console()e$B$H$$$&$N$,$”$k$N$G$=$NJU$OBge(B
e$B>fIW$G$9!#$?$V$s!#e(B

| > #e$B$"!<e(BWriteConsoleWe$B$C$Fe(B 9xe$B$O;H$($J$$e(B?
|
| Win2k e$B0J9_$N$h$&$G$9$M!#e(B
| 9x e$B$^$@@Z$C$A$c%@%a$+$J$!!#e(B

mswin32e$B%]!<%H%a%s%F%J$N0U8~$H$7$F$O!“<B9T4D6-$H$7$F$Ne(BWin9xe$B$Oe(B
e$B@Z$j$?$/$J$$$J$”!“$C$F$H$3$m$J$s$G$9$,!”$I$&$;C/$b%P%0%l%]!<e(B
e$B%H$N0l$D$b4s1[$5$J$$$N$G5$IU$+$J$$4V$K@Z$l$A$c$C$F$b$$$$$+$be(B
e$B$7$l$^$;$s!#e(B

e$B$=$l$G$O!#e(B

On Tue, 24 Jun 2008 00:20:12 +0900
In article [email protected]
[[ruby-dev:35209] Re: Win32 Unicode console output]
“U.Nakamura” [email protected] wrote:

| GetConsole e$B$r8F$s$G!"%3%s%=!<%k$+D4$Y$F$+$i!"e(BWriteConsoleW e$B$r8F$Y$P$$$$$C$]$$$G$9$M!#e(B

GetConsole()e$B$G$o$+$k$N$+$J$“e(B…
e$B$J$*e(Bwin32/win32.ce$B$K$Oe(Bis_console()e$B$H$$$&$N$,$”$k$N$G$=$NJU$OBge(B

MSVCR90 e$B$K$h$k$H!"e(BGetConsoleMode e$B$r8F$Y$P$$$$$_$?$$$G$9!#e(B
e$B%b!<%ICM$OL5;k$7$F!"JVCM$,e(B true e$B$J$i%3%s%=!<%k!“e(Bfalse
(e$B%(%i!<e(B) e$B$J$i$=$le(B
e$B0J30!#e(B
e$B$3$l$J$ie(B SOCKET e$B$,EO$5$l$F$b$”$s$7$s$8$c$J$$$G$7$g$&$+!#e(B


    /*
     * Note that in case the handle belongs to Console, write file 

will
* generate garbage output. For user to print these characters
* correctly, we will need to print ANSI.
*
* Also note that in case of printing to Console, we still have
to
* convert the characters to console codepage.
*/

    if (_isatty(fh) && (_osfile(fh) & FTEXT))
    {
        DWORD dwMode;
        _ptiddata ptd = _getptd();
        isCLocale = (ptd->ptlocinfo->lc_handle[LC_CTYPE] == 

_CLOCALEHANDLE);
toConsole = GetConsoleMode((HANDLE)_osfhnd(fh), &dwMode);
}

On Mon, 23 Jun 2008 15:02:21 +0900
In article [email protected]
[[ruby-dev:35203] Re: Win32 Unicode console output]
KIMURA Koichi [email protected] wrote:

#あーWriteConsoleWって 9xは使えない?

リンクだけはできた気がします。
呼ぶと、GetLastError() が ERROR_CALL_NOT_IMPLEMENTED を返します。

少なくとも Win98 SE の KERNEL32.DLL がエクスポートしていることは確認。

— MSVCR90 の実装:

/***
*_putwch_nolock() - _putwch() core routine (locked version)
*
*Purpose:

  •   Core _putwch() routine; assumes stream is already locked.
    
  •   [See _putwch() above for more info.]
    

*Entry: [See _putwch()]
*
*Exit: [See _putwch()]
*
*Exceptions:
*
*******************************************************************************/

wint_t __cdecl _putwch_nolock (
wchar_t ch
)
{

int size, num_written;
static int use_w = 2;
char mbc[MB_LEN_MAX +1];
if ( use_w)
{
    if (_confh == -2)
        __initconout();

    /* write character to console file handle */

    if (_confh == -1)
        return WEOF;
    else if ( !WriteConsoleW( (HANDLE)_confh,
                              (LPVOID)&ch,
                              1,
                              &num_written,
                              NULL )
              )
    {
        if ( use_w == 2 && GetLastError() == 

ERROR_CALL_NOT_IMPLEMENTED)
use_w = 0;
else
return WEOF;
} else
use_w = 1;
}

if ( use_w == 0)
{
    size = WideCharToMultiByte(
                               GetConsoleOutputCP(),
                               0,
                               (LPWSTR)&ch, 1,
                               mbc,
                               MB_LEN_MAX,
                               NULL,
                               NULL
                               );
    if ( (_confh == -1) || !WriteConsole( (HANDLE)_confh,
                                          (LPVOID)mbc,
                                          size,
                                          &num_written,
                                          NULL )
       )
            /* return error indicator */
            return WEOF;
}
return ch;

}

artone$B$G$9!#e(B

ruby19 -e’puts"\u{3042 2603}"’

e$B$G!"@c%@%k%^$,[email protected]$G$9!#e(B

e$B!V$"!W$H@c$@$k$^$G$9$h$M!)e(B

Windows Vista 64
(e$B$?$@$7e(BRubye$B$Oe(Bx86e$BHG$Ne(B1.9.0-2e$B!K!“e(BVisual C++ 6
SP5e$B$GF0:n$7e(B
e$B$^$7$?!#e(B
e$B$?$@$7!”<!$N%Q%C%A$,I,MW$G$7$?!#O3$l$F$k$@$1$@$H;W$$$^$9$1$I!#e(B

diff -u -p encoding.c~ encoding.c
*** encoding.c~ Tue Jun 17 06:28:03 2008
— encoding.c Mon Jun 30 23:00:50 2008
*************** rb_usascii_encindex(void)
*** 946,951 ****
— 946,957 ----
return ENCINDEX_US_ASCII;
}

  • int
  • rb_utf8_encindex(void)
  • {
  • return ENCINDEX_UTF_8;
    
  • }
  • rb_encoding *
    rb_locale_encoding(void)
    {

e$B@.@%$G$9!#e(B

arton wrote:

ruby19 -e’puts"\u{3042 2603}"’

e$B$G!"@c%@%k%^$,[email protected]$G$9!#e(B

e$B!V$"!W$H@c$@$k$^$G$9$h$M!)e(B

e$B$=$&$G$9$M!"!V$"e(B[e$B@c%@%k%^e(B]e$B!W$G$9!#e(B

Windows Vista 64 (e$B$?$@$7e(BRubye$B$Oe(Bx86e$BHG$Ne(B1.9.0-2e$B!K!"e(BVisual C++ 6 SP5e$B$GF0:n$7e(B
e$B$^$7$?!#e(B

e$BJs9p$"$j$,$H$&$4$6$$$^$9!#e(B

Vista64 e$B$G$bBg>fIW$@$H!"$"$HCN$j$?$$$N$Oe(B Win9x e$B$G$9$+$M!#e(B

e$B$?$@$7!"<!$N%Q%C%A$,I,MW$G$7$?!#O3$l$F$k$@$1$@$H;W$$$^$9$1$I!#e(B

rb_utf8_encindex e$B$NDI2C$O!"e(B[ruby-dev:35270]
e$B$ND>A0$K%3%_%C%H$7$?!"e(B
r17724 e$B$G2?5$$KF~$C$F$$$k$N$G$9!J$)e(B

成瀬です。

  • GetConsole でコンソールへの出力かどうか判別
  • Win2k 以降では、Unicode 系エンコーディングの文字列は、
    WriteConsoleW を用いてコンソールに Unicode で書き出す
    を行うようにしてみました。

ruby19 -e’puts"\u{3042 2603}"’

で、雪ダルマが出れば成功です。

手元の WinXP + MSYS + MinGW32 では意図したとおりに動いているようです。

他の環境のお持ちの方は試していただけると幸いです。

On Tue, 1 Jul 2008 03:06:31 +0900
In article [email protected]
[[ruby-dev:35283] Re: Win32 Unicode console output]
“NARUSE, Yui” [email protected] wrote:

e$B$^$7$?!#e(B

e$BJs9p$"$j$,$H$&$4$6$$$^$9!#e(B

Vista 32bit + VS2008 e$B$G$OF0:n$7$?$s$G$9$,!"e(B

C:.…> ruby -e’puts “\u{3042 2603}”.encode(“utf-16le”)’
e$B$H$+$d$k$H2?$b=P$^$;$s$M!#e(B
e$B$9$G$K%o%$%IJ8;zNs$K$J$C$F$$$k$N$re(B MultiByteToWideChar
e$B$K$+$1$F$b%5%]!<e(B
e$B%H$7$F$$$J$$$_$?$$$G$9!#e(B

e$B$"$H!"EvA3$G$9$,e(B

C:.…>ruby -e’puts “e$B$”$$$&$($*e(B".encode(“EUC-JP”)’
e$B!“!V!”!“!”%r!“%#!”%'e(B

e$B$3$s$J$N$b$@$a$G$9$M!#$d$O$j$3$3$Oe(B trascode
e$B$N;EAH$_$r;H$C$?J}$,$h$$$N$Ge(B
e$B$O$J$$$+$H;W$$$^$9!#e(B(e$B:#$N$H$3$mA4J8;z%;%C%H$Oe(B UTF-16LE
e$B$KJQ49$G$-$^$9$he(B
e$B$M!)e(B)

e$B$"$H$OF~NO7O!“e(BReadConsoleInputW() e$B$He(B GetCommandLineW()
e$B$+!De(B
-EUTF-8
e$B$H$+$G5/F0$5$l$?>l9g$K8B$C$F;HMQ$9$k!”$H$+@)8f$7$J$$$HBLL$G$9$he(B
e$B$M$d$C$Q!#e(B

Vista64 e$B$G$bBg>fIW$@$H!“$”$HCN$j$?$$$N$Oe(B Win9x e$B$G$9$+$M!#e(B

Win98SE e$B$G<B9T$7$h$&$H$7$?$N$G$9$,!"e(BVS2008
e$B$G%3%s%Q%$%k$7$?$ie(B

e$B%U%!%$%k$K$O?7$7$$%P!<%8%g%s$Ne(B Windows e$B$,I,MW$G$9!#e(B
Windows e$B$r%"%C%W%0%l!<%I$7$F$/$@$5$$!#e(B

e$B$H$+8@$o$l$F<B9T$G$-$^$;$s$G$7$?!#$C$F$+$=$s$J%(%i!<$r=P$95!G=$"$C$?$s$@!#e(B

VC++6 e$B$I$3$K;EIq$C$?$s$@$C$1!D!De(B

e$B@.@%$G$9!#e(B

Tietew wrote:

C:…> ruby -e’puts “\u{3042 2603}”.encode(“utf-16le”)’
e$B$H$+$d$k$H2?$b=P$^$;$s$M!#e(B
e$B$9$G$K%o%$%IJ8;zNs$K$J$C$F$$$k$N$re(B MultiByteToWideChar e$B$K$+$1$F$b%5%]!<e(B
e$B%H$7$F$$$J$$$_$?$$$G$9!#e(B

e$B$`!"$=$&$J$N$G$9$+!#e(B

e$B$"$H!"EvA3$G$9$,e(B

C:…>ruby -e’puts “e$B$”$$$&$($*e(B".encode(“EUC-JP”)’
e$B!"!V!"!"!"%r!"%#!"%'e(B

e$B$3$s$J$N$b$@$a$G$9$M!#$d$O$j$3$3$Oe(B trascode e$B$N;EAH$_$r;H$C$?J}$,$h$$$N$Ge(B
e$B$O$J$$$+$H;W$$$^$9!#e(B

e$B$3$l$O;EMM$N$D$b$j$@$C$?$N$G$9$,!"$&!<$s!"$I$&$J$s$@$m$&!#e(B
Windows
e$B$K8B$C$F$O!“0J2<$,$G$-$A$c$&$N$b%”%j$+$J$!!"$H$b;W$$;O$a$F$-$^$7$?!#e(B
e$BB>$N4D6-$H$N7s$M9g$$$r9M$($k$HLB$$$b$"$j$^$9$,!#e(B

ruby19 -e’print"\u{2603}";print"\xA9".force_encoding(“iso-8859-1”);
print"\xa4\xa2\xa4\xa4".force_encoding
(“eucjp”)’
[e$B@c%@%k%^e(B][Copyright]e$B$"$$e(B

(e$B:#$N$H$3$mA4J8;z%;%C%H$Oe(B UTF-16LE e$B$KJQ49$G$-$^$9$h$M!)e(B)

e$B$$$(!“JQ49$G$-$k$b$N$r?t$($?$[$&$,Aa$/$F!“e(B
ISO 8859 e$B%7%j!<%:$HF|K\8l!“4Z9q8l!“e(BUTF-8, UTF-{16,32}{BE,LE}
e$B$@$1$G$9!#e(B
e$BCf9q7O!“BfOQ7O!”%?%$!”%&%/%i%$%J$”$?$j$OL5M}$G$9$M!”$”$He(B UTF-7
e$B$b!#e(B
e$B%a%8%c!<$J$H$3$m$@$He(B ASCII-8BIT e$B$,L5M}$G$9!#e(B

e$B$"$H$OF~NO7O!“e(BReadConsoleInputW() e$B$He(B GetCommandLineW() e$B$+!De(B
-EUTF-8 e$B$H$+$G5/F0$5$l$?>l9g$K8B$C$F;HMQ$9$k!”$H$+@)8f$7$J$$$HBLL$G$9$he(B
e$B$M$d$C$Q!#e(B

e$B$^$5$+!"e(BWindows e$B$N%3%^%s%I%W%m%s%W%H$G!“e(B
locale e$B$H0[$J$kJ8;zNs$r@8$GBG$DM&<T$O$$$J$$$H;W$&$s$G$9$,!“e(B
ruby19 -EEUC-JP -e’puts”\xa4\xa2”’ > foo.txt
e$B$N$h$&$K%(%9%1!<%W$7$F$@$H$J$$$H$b8@$$@Z$l$^$;$s$+$M$’!#e(B

Vista64 e$B$G$bBg>fIW$@$H!"$"$HCN$j$?$$$N$Oe(B Win9x e$B$G$9$+$M!#e(B

Win98SE e$B$G<B9T$7$h$&$H$7$?$N$G$9$,!"e(BVS2008 e$B$G%3%s%Q%$%k$7$?$ie(B

e$B%U%!%$%k$K$O?7$7$$%P!<%8%g%s$Ne(B Windows e$B$,I,MW$G$9!#e(B
Windows e$B$r%"%C%W%0%l!<%I$7$F$/$@$5$$!#e(B

e$B$H$+8@$o$l$F<B9T$G$-$^$;$s$G$7$?!#$C$F$+$=$s$J%(%i!<$r=P$95!G=$"$C$?$s$@!#e(B

VC++6 e$B$I$3$K;EIq$C$?$s$@$C$1!D!De(B

VC++6 e$B$G:n$k$HF0$/$N$G$9$+$M$’!&!&!&!)e(B

e$B0J2<!"e(Btranscode e$B$rMQ$$$?%Q%C%A$G$9!#e(B

Index: include/ruby/encoding.h

— include/ruby/encoding.h (revision 17818)
+++ include/ruby/encoding.h (working copy)
@@ -192,6 +192,7 @@
return ENC_DUMMY_P(ENC_FROM_ENCODING(enc));
}

+int rb_transcode_convertible(const char* from_encoding, const char*
to_encoding);
VALUE rb_str_transcode(VALUE str, VALUE to);

#endif /* RUBY_ENCODING_H */
Index: include/ruby/win32.h

— include/ruby/win32.h (revision 17818)
+++ include/ruby/win32.h (working copy)
@@ -547,6 +547,7 @@
int rb_w32_fclose(FILE*);
size_t rb_w32_read(int, void *, size_t);
size_t rb_w32_write(int, const void *, size_t);
+long rb_console_write_unicode(unsigned long, int);
int rb_w32_utime(const char *, const struct utimbuf *);
int WINAPI rb_w32_Sleep(unsigned long msec);
int rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD
timeout);
Index: io.c

— io.c (revision 17818)
+++ io.c (working copy)
@@ -14,6 +14,7 @@
#include “ruby/ruby.h”
#include “ruby/io.h”
#include “ruby/signal.h”
+#include “ruby/win32.h”
#include “vm_core.h”
#include <ctype.h>
#include <errno.h>
@@ -689,6 +690,11 @@
{
long len, n, r, l, offset = 0;

+#ifdef _WIN32

  • len = rb_console_write_unicode(str, fptr->fd);
  • if (len >= 0) return len;
    +#endif
  • /*
    * If an external encoding was specified and it differs from
    * the strings encoding then we must transcode before writing.
    Index: transcode.c
    ===================================================================
    — transcode.c (revision 17818)
    +++ transcode.c (working copy)
    @@ -119,6 +119,14 @@
    return (rb_transcoder *)val;
    }

+/*

    • experimental.
  • /
    +int
    +rb_transcode_convertible(const char
    from_encoding, const char*
    to_encoding)
    +{
  • return transcode_dispatch(from_encoding, to_encoding) ? TRUE :
    FALSE;
    +}

/*

  • Transcoding engine logic
    Index: win32/win32.c
    ===================================================================
    — win32/win32.c (revision 17818)
    +++ win32/win32.c (working copy)
    @@ -12,6 +12,7 @@

#include “ruby/ruby.h”
#include “ruby/signal.h”
+#include “ruby/encoding.h”
#include “dln.h”
#include <fcntl.h>
#include <process.h>
@@ -3933,6 +3934,28 @@
return rb_w32_send(fd, buf, size, 0);
}

+long
+rb_console_write_unicode(VALUE str, int fd)
+{

  • static int disable;
  • HANDLE handle;
  • DWORD dwMode, reslen;
  • if (disable) return -1L;
  • handle = (HANDLE)_osfhnd(fd);
  • if (!GetConsoleMode(handle, &dwMode))
  • return -1L;
  • str = rb_str_transcode(str, rb_str_new2(“UTF-16LE”));
  • if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str),
    RSTRING_LEN(str)/2, &reslen, NULL)) {
  • if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
  •  disable = TRUE;
    
  • return -1L;
  • }
  • return (long)reslen;
    +}

static int
unixtime_to_filetime(time_t time, FILETIME *ft)
{

On Thu, 3 Jul 2008 00:08:45 +0900
In article [email protected]
[[ruby-dev:35328] Re: Win32 Unicode console output]
“NARUSE, Yui” [email protected] wrote:

e$B$3$s$J$N$b$@$a$G$9$M!#$d$O$j$3$3$Oe(B trascode e$B$N;EAH$_$r;H$C$?J}$,$h$$$N$Ge(B
e$B$O$J$$$+$H;W$$$^$9!#e(B

e$B$3$l$O;EMM$N$D$b$j$@$C$?$N$G$9$,!“$&!<$s!”$I$&$J$s$@$m$&!#e(B
Windows e$B$K8B$C$F$O!“0J2<$,$G$-$A$c$&$N$b%”%j$+$J$!!“$H$b;W$$;O$a$F$-$^$7$?!#e(B
e$BB>$N4D6-$H$N7s$M9g$$$r9M$($k$HLB$$$b$”$j$^$9$,!#e(B

Windowse$B$N%3%^%s%IAk$C$FFC<l$G$9$+$i$M$(!#e(B

ruby19 -e’print"\u{2603}“;print”\xA9".force_encoding(“iso-8859-1”);
print"\xa4\xa2\xa4\xa4".force_encoding
(“eucjp”)’
[e$B@c%@%k%^e(B][Copyright]e$B$"$$e(B

(e$B:#$N$H$3$mA4J8;z%;%C%H$Oe(B UTF-16LE e$B$KJQ49$G$-$^$9$h$M!)e(B)

e$B$$$(!“JQ49$G$-$k$b$N$r?t$($?$[$&$,Aa$/$F!“e(B
ISO 8859 e$B%7%j!<%:$HF|K\8l!“4Z9q8l!“e(BUTF-8, UTF-{16,32}{BE,LE} e$B$@$1$G$9!#e(B
e$BCf9q7O!“BfOQ7O!”%?%$!”%&%/%i%$%J$”$?$j$OL5M}$G$9$M!”$”$He(B UTF-7 e$B$b!#e(B

e$B$$$d!“8=>u$N<BAu$G!”$H$$$&$3$H$G$O$J$/$F!“%(%s%3!<%G%#%s%0$N;EMM>e!”$G$9!#e(B

e$B%a%8%c!<$J$H$3$m$@$He(B ASCII-8BIT e$B$,L5M}$G$9!#e(B

e$B$=$l$O!"!D!De(BLocalee$B$K$9$k$7$+$J$$$N$+$J!#e(B

e$B$"$H$OF~NO7O!“e(BReadConsoleInputW() e$B$He(B GetCommandLineW() e$B$+!De(B
-EUTF-8 e$B$H$+$G5/F0$5$l$?>l9g$K8B$C$F;HMQ$9$k!”$H$+@)8f$7$J$$$HBLL$G$9$he(B
e$B$M$d$C$Q!#e(B

e$B$^$5$+!"e(BWindows e$B$N%3%^%s%I%W%m%s%W%H$G!"e(B
locale e$B$H0[$J$kJ8;zNs$r@8$GBG$DM&<T$O$$$J$$$H;W$&$s$G$9$,!"e(B

e$B$$$d!"e(BCreateProcessW() e$B$H$+e(B ShellExecuteW()
e$B$H$$$&6/<T$,5o$^$9$+$i!#e(B

Exerb e$B$+2?$+$Ge(B exe
e$B2=$5$l$?%9%/%j%W%H$G!“4XO”$E$1$5$l$?%U%!%$%k$r3+$/!“e(B
e$B$J$s$F%7%A%e%(!<%7%g%s$r9M$($k$H!”%(%/%9%W%m!<%i$,e(B non-Locale
e$B$N%U%!%$%ke(B
e$BL>$rEO$7$F$/$k$J$s$F2DG=@-$b=<J,$"$j$^$9!#e(B

e$B$3$s$K$A$O!"$J$+$`$ie(B(e$B$&e(B)e$B$G$9!#e(B

In message “[ruby-dev:35334] Re: Win32 Unicode console output”
on Jul.03,2008 12:13:17, [email protected] wrote:

e$B$J$s$F%7%A%e%(!<%7%g%s$r9M$($k$H!“%(%/%9%W%m!<%i$,e(B non-Locale e$B$N%U%!%$%ke(B
e$BL>$rEO$7$F$/$k$J$s$F2DG=@-$b=<J,$”$j$^$9!#e(B

e$B$H$j$“$($:!”<!$Oe(BGetCommandLineW()e$B$@$m$&!"$H;W$C$F%3!<%I$r8+$Je(B
e$B$,$i9M$($?$N$G$9$,!"Ev$?$jA0$G$9$,%3%^%s%I%i%$%s0z?t$N2r<a$,e(B
e$B=*$o$k$^$Ge(Bdefault_externale$B$O3NDj$G$-$J$$$N$G$7$?!#e(B

e$B0lEYe(BGetCommandLineA()e$B$G2r<a$7$?8e!“e(Bdefault_externale$B$,7h$^$C$?e(B
e$B$i$d$j$J$*$9$N$+$J$”!#$G$-$k$s$@$m$&$+!#e(B

e$B$=$l$G$O!#e(B

e$B@.@%$G$9!#e(B

e$B%Q%C%A$r4V0c$($^$7$?!"@5$7$/$O0J2<$N$H$*$j$G$9!#e(B

Index: include/ruby/encoding.h

— include/ruby/encoding.h (revision 17818)
+++ include/ruby/encoding.h (working copy)
@@ -192,6 +192,7 @@
return ENC_DUMMY_P(ENC_FROM_ENCODING(enc));
}

+int rb_transcode_convertible(const char* from_encoding, const char*
to_encoding);
VALUE rb_str_transcode(VALUE str, VALUE to);

#endif /* RUBY_ENCODING_H */
Index: include/ruby/win32.h

— include/ruby/win32.h (revision 17818)
+++ include/ruby/win32.h (working copy)
@@ -547,6 +547,7 @@
int rb_w32_fclose(FILE*);
size_t rb_w32_read(int, void *, size_t);
size_t rb_w32_write(int, const void *, size_t);
+long rb_console_write_unicode(unsigned long, int);
int rb_w32_utime(const char *, const struct utimbuf *);
int WINAPI rb_w32_Sleep(unsigned long msec);
int rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD
timeout);
Index: io.c

— io.c (revision 17818)
+++ io.c (working copy)
@@ -14,6 +14,7 @@
#include “ruby/ruby.h”
#include “ruby/io.h”
#include “ruby/signal.h”
+#include “ruby/win32.h”
#include “vm_core.h”
#include <ctype.h>
#include <errno.h>
@@ -689,6 +690,11 @@
{
long len, n, r, l, offset = 0;

+#ifdef _WIN32

  • len = rb_console_write_unicode(str, fptr->fd);
  • if (len >= 0) return len;
    +#endif
  • /*
    * If an external encoding was specified and it differs from
    * the strings encoding then we must transcode before writing.
    Index: transcode.c
    ===================================================================
    — transcode.c (revision 17818)
    +++ transcode.c (working copy)
    @@ -119,6 +119,14 @@
    return (rb_transcoder *)val;
    }

+/*

    • experimental.
  • /
    +int
    +rb_transcode_convertible(const char
    from_encoding, const char*
    to_encoding)
    +{
  • return transcode_dispatch(from_encoding, to_encoding) ? TRUE :
    FALSE;
    +}

/*

  • Transcoding engine logic
    Index: win32/win32.c
    ===================================================================
    — win32/win32.c (revision 17818)
    +++ win32/win32.c (working copy)
    @@ -12,6 +12,7 @@

#include “ruby/ruby.h”
#include “ruby/signal.h”
+#include “ruby/encoding.h”
#include “dln.h”
#include <fcntl.h>
#include <process.h>
@@ -3933,6 +3934,28 @@
return rb_w32_send(fd, buf, size, 0);
}

+long
+rb_console_write_unicode(VALUE str, int fd)
+{

  • static int disable;
  • HANDLE handle;
  • DWORD dwMode, reslen;
  • if (disable) return -1L;
  • handle = (HANDLE)_osfhnd(fd);
  • if (!GetConsoleMode(handle, &dwMode) ||
  • !rb_transcode_convertible(rb_enc_name(rb_enc_get(str)), “UTF-16LE”))
  • return -1L;
  • str = rb_str_transcode(str, rb_str_new2(“UTF-16LE”));
  • if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str),
    RSTRING_LEN(str)/2, &reslen, NULL)) {
  • if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
  •  disable = TRUE;
    
  • return -1L;
  • }
  • return (long)reslen;
    +}

static int
unixtime_to_filetime(time_t time, FILETIME *ft)
{

e$B@.@%$G$9!#e(B

U.Nakamura wrote:

Exerb e$B$+2?$+$Ge(B exe e$B2=$5$l$?%9%/%j%W%H$G!“4XO”$E$1$5$l$?%U%!%$%k$r3+$/!“e(B
e$B$J$s$F%7%A%e%(!<%7%g%s$r9M$($k$H!”%(%/%9%W%m!<%i$,e(B non-Locale e$B$N%U%!%$%ke(B
e$BL>$rEO$7$F$/$k$J$s$F2DG=@-$b=<J,$"$j$^$9!#e(B

e$B$H$j$"$($:!"<!$Oe(BGetCommandLineW()e$B$@$m$&!"$H;W$C$F%3!<%I$r8+$Je(B
e$B$,$i9M$($?$N$G$9$,!"Ev$?$jA0$G$9$,%3%^%s%I%i%$%s0z?t$N2r<a$,e(B
e$B=*$o$k$^$Ge(Bdefault_externale$B$O3NDj$G$-$J$$$N$G$7$?!#e(B

e$B0lEYe(BGetCommandLineA()e$B$G2r<a$7$?8e!“e(Bdefault_externale$B$,7h$^$C$?e(B
e$B$i$d$j$J$*$9$N$+$J$”!#$G$-$k$s$@$m$&$+!#e(B

GetCommandLineW() e$B$G2r<a$7$F!"e(Bdefault_external e$B$Ke(B
e$BJQ49$9$l$P$$$$$s$8$c$J$$$G$9$+$M!#e(B

GetCommandLineA() e$B$G$Oe(B Windows e$B$,$d$C$F$$$k$3$H$r!“e(B
Ruby e$B$NB&$G$d$k$h$&$KJQ$($k$@$1$+$J!”$H!#e(B

e$B$3$s$K$A$O!"$J$+$`$ie(B(e$B$&e(B)e$B$G$9!#e(B

In message “[ruby-dev:35343] Re: Win32 Unicode console output”
on Jul.04,2008 01:15:04, [email protected] wrote:
| > e$B$H$j$“$($:!”<!$Oe(BGetCommandLineW()e$B$@$m$&!"$H;W$C$F%3!<%I$r8+$Je(B
| > e$B$,$i9M$($?$N$G$9$,!“Ev$?$jA0$G$9$,%3%^%s%I%i%$%s0z?t$N2r<a$,e(B
| > e$B=$o$k$^$Ge(Bdefault_externale$B$O3NDj$G$-$J$$$N$G$7$?!#e(B
| >
| > e$B0lEYe(BGetCommandLineA()e$B$G2r<a$7$?8e!"e(Bdefault_externale$B$,7h$^$C$?e(B
| > e$B$i$d$j$J$
$9$N$+$J$”!#$G$-$k$s$@$m$&$+!#e(B
|
| GetCommandLineW() e$B$G2r<a$7$F!"e(Bdefault_external e$B$Ke(B
| e$BJQ49$9$l$P$$$$$s$8$c$J$$$G$9$+$M!#e(B
|
| GetCommandLineA() e$B$G$Oe(B Windows e$B$,$d$C$F$$$k$3$H$r!“e(B
| Ruby e$B$NB&$G$d$k$h$&$KJQ$($k$@$1$+$J!”$H!#e(B

e$B$(!<$H!“e(BWindowse$B$H$$$&$+e(BCe$B%9%?!<%H%”%C%W%k!<%A%s$,:n@.$7$F$/$le(B
e$B$ke(Bargc,argve$B$Oe(Bmswin32(e$B$J$Ie(B)e$B$G$O;H$C$F$^$;$s!#e(B
e$B$D$^$j!“8=>u4{$Ke(BGetCommandLineA()e$B$G<hF@$7$?FbMF$r<+A0$G2r<a$7e(B
e$B$F$$$^$9!#e(B
e$B>$7$/$O!“e(Bwin32/win32.ce$B$Ne(Brb_w32_sysinit()e$B$H!”$=$3$+$i8F$P$l$ke(B
rb_w32_cmdvector()e$B$r;2>H$7$F$$$?$@$1$k$H$o$+$k$+$H!#e(B
e$B$G!”$3$N:]$Ke(Brubye$B$,;}$C$F$$$ke(Bglobbinge$B%k!<%A%s72$rMxMQ$7$F%o%$e(B
e$B%k%I%+!<%IN`$NE83+$r9T$C$F$$$^$9!#e(B

e$B0J>e$+$i!“$5$7$”$?$C$F$=$S$($F$$$kJI$O!"e(B
(1)
e$B$I$N%?%$%_%s%0$Ge(BGetCommandLineW()e$B$+$i<h$C$F$-$?e(Bargc,argv
e$B$X$N:9$7BX$($r9T$&$+$NH=CG!#e(B
(2) rb_w32_cmdvector()e$B$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B
(3) globbinge$B%k!<%A%s72$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B
e$B$H$$$&$3$H$K$J$k$+$H;W$$$^$9!#e(B

e$B$:$C$HL$r$=$`$1$F$$$?$N$G$9$,!";W$$$N$[$+Aa$/e(Bglobbinge$B$NB?8@e(B
e$B8lBP1~$NI,MW@-$,L$NA0$K=P$F$-$^$7$?$M$(e(B…

e$B$=$l$G$O!#e(B

e$B@.@%$G$9!#e(B

U.Nakamura wrote:

|
e$B>$7$/$O!“e(Bwin32/win32.ce$B$Ne(Brb_w32_sysinit()e$B$H!”$=$3$+$i8F$P$l$ke(B
rb_w32_cmdvector()e$B$r;2>H$7$F$$$?$@$1$k$H$o$+$k$+$H!#e(B
e$B$G!"$3$N:]$Ke(Brubye$B$,;}$C$F$$$ke(Bglobbinge$B%k!<%A%s72$rMxMQ$7$F%o%$e(B
e$B%k%I%+!<%IN`$NE83+$r9T$C$F$$$^$9!#e(B

e$B0J>e$+$i!"$5$7$"$?$C$F$=$S$($F$$$kJI$O!"e(B
(1) e$B$I$N%?%$%_%s%0$Ge(BGetCommandLineW()e$B$+$i<h$C$F$-$?e(Bargc,argv
e$B$X$N:9$7BX$($r9T$&$+$NH=CG!#e(B

e$B$3$3$O$b$C$HC1=c$K!"e(B

  1. GetCommandLineW() e$B$Ge(B LPCWSTR e$B$r<h$C$F$/$k!#e(B
  2. WideCharToMultibyte e$B$G$R$H$^$:e(B locale e$B$KJQ49e(B
  3. locale e$B$KJQ49$7$?J8;zNs$r:#$^$G$I$*$j2r<ae(B
  4. default_external e$B$,e(B locale e$B$@$C$?$i$*$7$^$$e(B
  5. e$B0c$C$?$i!"e(BLPCWSTR e$B$re(B default_external
    e$B$KJQ49$7$J$*$9e(B
  6. e$B$=$l$r2r<ae(B
    (e$B2r<aIt$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~$,I,MWe(B)
  7. e$B$*$7$^$$e(B
    e$B$H$$$&$3$H$G$O!#e(B

(2) rb_w32_cmdvector()e$B$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B

e$B$3$l$Oe(B CharNext() e$B$@$1$G$9$+$M!&!&!&!)e(B

(3) globbinge$B%k!<%A%s72$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B

e$B$3$A$i$Oe(B Next(), Inc(), Compare() e$B$G$7$g$&$+!#e(B
downcase e$B$H$+$b$J$N$+!&!&!&!)e(B
e$B$"$H!"A4It$Ke(B encoding e$BEO$9$h$&$K$7$J$$$H$$$1$^$;$s$M!#e(B

e$B$:$C$HL$r$=$`$1$F$$$?$N$G$9$,!";W$$$N$[$+Aa$/e(Bglobbinge$B$NB?8@e(B
e$B8lBP1~$NI,MW@-$,L$NA0$K=P$F$-$^$7$?$M$(e(B…

e$B$d$O$j$d$D$H$O@o$o$M$P$J$i$J$$1?L?$J$N$+!&!&!&!#e(B

e$B$3$s$K$A$O!"$J$+$`$ie(B(e$B$&e(B)e$B$G$9!#e(B

In message “[ruby-dev:35347] Re: Win32 Unicode console output”
on Jul.04,2008 02:41:47, [email protected] wrote:
| > (1) e$B$I$N%?%$%_%s%0$Ge(BGetCommandLineW()e$B$+$i<h$C$F$-$?e(Bargc,argv
| > e$B$X$N:9$7BX$($r9T$&$+$NH=CG!#e(B
|
| e$B$3$3$O$b$C$HC1=c$K!"e(B
| 1. GetCommandLineW() e$B$Ge(B LPCWSTR e$B$r<h$C$F$/$k!#e(B
| 2. WideCharToMultibyte e$B$G$R$H$^$:e(B locale e$B$KJQ49e(B

e$B$3$3$GJQ49$9$k$J$i:#$^$G$I$*$je(BGetCommandLineA()e$B$NJ}$,$$$$$h$&e(B
e$B$J5$$,$7$^$9!#e(B

| 3. locale e$B$KJQ49$7$?J8;zNs$r:#$^$G$I$$j2r<ae(B
| 4. default_external e$B$,e(B locale e$B$@$C$?$i$
$7$^$$e(B
| 5. e$B0c$C$?$i!"e(BLPCWSTR e$B$re(B default_external e$B$KJQ49$7$J$$9e(B
| 6. e$B$=$l$r2r<ae(B (e$B2r<aIt$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~$,I,MWe(B)
| 7. e$B$
$7$^$$e(B
| e$B$H$$$&$3$H$G$O!#e(B

e$B$=$l$O$=$N$H$*$j$J$s$G$9$1$Ie(B…

ruby_set_argv()e$B$N8F$S=P$7$re(Bdefault_externale$B3NDj$N8e$m$K$:$i$7e(B
e$B$F!“$=$ND>A0$/$i$$$Ge(B5e$B!Ae(B6e$B$r$d$l$P$$$$$N$+$J$”!#e(B

| > (2) rb_w32_cmdvector()e$B$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B
|
| e$B$3$l$Oe(B CharNext() e$B$@$1$G$9$+$M!&!&!&!)e(B

e$B$@$1$@$H$$$$$J$“!”$H$$$&46?($G$9!#e(B

| > (3) globbinge$B%k!<%A%s72$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~!#e(B
|
| e$B$3$A$i$Oe(B Next(), Inc(), Compare() e$B$G$7$g$&$+!#e(B
| downcase e$B$H$+$b$J$N$+!&!&!&!)e(B
| e$B$"$H!"A4It$Ke(B encoding e$BEO$9$h$&$K$7$J$$$H$$$1$^$;$s$M!#e(B

e$BI]$$$N$G$h$/8+$F$J$$$N$G$9$,!"$?$V$s$=$NJU$j$K$J$k$+$H;W$$$^e(B
e$B$9!#e(B

| > e$B$:$C$HL$r$=$`$1$F$$$?$N$G$9$,!";W$$$N$[$+Aa$/e(Bglobbinge$B$NB?8@e(B
| > e$B8lBP1~$NI,MW@-$,L$NA0$K=P$F$-$^$7$?$M$(e(B…
|
| e$B$d$O$j$d$D$H$O@o$o$M$P$J$i$J$$1?L?$J$N$+!&!&!&!#e(B

fnmatche$B$H$+$b$“$k$7!”$I!<$7$^$7$g$&$+$M!#e(B

e$BKh7n%j%j!<%9$,$“$k$s$@$+$i!”$=$N$I$l$+$rL\I8$H$7$F@_Dj$7$?J}e(B
e$B$,$$$$$N$+$J!#e(B
e$B$(!<$H!“e(B9/25e$B$K=P$ke(B1.9.0-5e$B$Ge(Bfeature
freezee$B$i$7$$$N$G!“e(B
win32-unicode-teste$B%V%i%s%A$G$N<B83$Oe(B9e$B7n>e=$/$i$$$r$a$I$K40N;e(B
e$B$7$Fe(Btrunke$B$K;}$C$F$3$i$l$k$h$&$K$7$?$$!”$H$$$&$3$H$K$7$^$7$g$&e(B
e$B$+!#$”$i!“e(B2e$B%v7n$7$+$”$j$^$;$s$J!#e(B
e$B$G!“$=$l$^$G$K$G$-$?$3$H$,e(B1.9.1e$B$KF~$k!”$G$-$J$+$C$?$3$H$Oe(B1.9.2
e$B$K8~$1$Fe(Bwin32-unicode-teste$B%V%i%s%A$G<B83$r7QB3$9$k!"$H!#e(B

e$B$=$7$Fe(B1.9.1e$B$N%j%j!<%9$K8~$1$?:n6H$K?4$r<h$i$l$F!"@Q$_;D$7$,e(B

e$B$“$k$3$H$rK:$l$?$^$^$K$J$k!”$H$$$&$"$j$,$A$JE83+$X!#e(B

e$B$=$l$G$O!#e(B

e$B$3$s$K$A$O!"$J$+$`$ie(B(e$B$&e(B)e$B$G$9!#e(B

In message “[ruby-dev:35348] Re: Win32 Unicode console output”
on Jul.04,2008 03:17:09, [email protected] wrote:

| 3. locale e$B$KJQ49$7$?J8;zNs$r:#$^$G$I$$j2r<ae(B
| 4. default_external e$B$,e(B locale e$B$@$C$?$i$
$7$^$$e(B
| 5. e$B0c$C$?$i!"e(BLPCWSTR e$B$re(B default_external e$B$KJQ49$7$J$$9e(B
| 6. e$B$=$l$r2r<ae(B (e$B2r<aIt$NHs%3!<%I%Z!<%8%(%s%3!<%G%#%s%0BP1~$,I,MWe(B)
| 7. e$B$
$7$^$$e(B

e$B$H$j$"$($:!"e(B6e$B0J30$O:n$C$Fe(Bwin32-uncode-teste$B$KF~$l$F$_$^$7$?!#e(B
e$BK\Ev$Oe(B-ee$B0J30$N%9%$%C%AN$bBP1~$7$J$$$H$$$1$J$$$N$G$9$,!"e(B-Ie$B$Ne(B e$B:F@_Dj$H$+9M$($k$H;$K$=$&$J$N$G$=$3$O>JN,$7$^$7$?!#e(B

e$B$=$l$G$O!#e(B