The Man Who Fell From The Wrong Side Of The Sky:2004年7月30日分

2004/7/30(Fri)

(ry

@ 続 vfwscanf(3)

#include <limits.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
int main(void)
{
        char c[(MB_LEN_MAX * 3)+1], *p;
        wchar_t wc;
        int i;
        mbstate_t st;
        setlocale(LC_CTYPE, "ja_JP.eucJP");
        memset(&c, 0, sizeof(c));
        wscanf(L"%3c %lc", c, &wc);
        for (p = &c[0], i = 0; *p != '\0'; p++, i++)
                printf("c[%d]: 0x%x\n", i, *p & 0xFFU);
        memset(&c, 0, sizeof(c));
        memset(&st, 0, sizeof(st));
        wcrtomb(&c[0], wc, &st);
        printf("wc: %s\n", c);
}

みたいなコード、に"あいうえお"という入力を喰わせると
glibc2/Solaris9/FreeBSD-current全部動作が違いますな...

まずglibc2。
L"%3c"のwidth=3はwchar_t=3の意味で、入力ストリームからは"あいう"が読み込まれる。
引数にはwcrtomb(3)で変換した結果が書き込まれるので、最低でもMB_CUR_MAX * widthのサイズを保証。

c[0]: 0xa4
c[1]: 0xa2
c[2]: 0xa4
c[3]: 0xa4
c[4]: 0xa4
c[5]: 0xa6
wc: え

んでSolaris9。
width=3はchar=3の意味で、入力ストリームからは3byte消費される、
シフト状態は影響を受けない。つまりvfscanfとまったく同じ動きをする。
(MSDNを読む限りVisualC++もこの動作?)

c[0]: 0xa4
c[1]: 0xa2
c[1]: 0xa4
wc: 0xa4

最後にFreeBSD-current。
入力ストリームからはwchar_t単位で読み込まれるが、
width=3はSolarisと同じくchar=3の意味で、
wcrtomb(3)で変換した結果より先頭3byteが引数に書き込まれる。
c[0]: 0xa4
c[1]: 0xa2
c[2]: 0xa4
wc: う
(訂正)
wcrtomb(3)で変換した結果より3byteが引数に書き込まれる。しかしこの例の場合
3byteではshift stateが変換途中なため、ungetwc(3)で書き戻されるので、
結果として2byteのみ引数に書き込まれる(ワケワカラン)。

c[0]: 0xa4
c[1]: 0xa2
wc: い

で、けつねうろんから言うとglibcが正解。
SUSv3にはこうあるし↓

Matches a sequence of wide-characters of the number specified by the field width
(1 if no field width is present in the conversion specification).
If no l (ell) qualifier is present, wide-characters from the input field are converted as if by
repeated calls to the wcrtomb() function, with the conversion state described by an mbstate_t object
initialised to zero before the first wide-character is converted.

それとFreeBSDのvfscanf.cにもいろいろ間違いハッケソ。

@ 私事ですが

検査前は水すら飲めないのは辛いです。