○[NetBSD] GNU libiconv emulation
joerg氏に今何の機能が足りてなくて、何のアプリで困ってるのか再確認するメール投げた。
忘れたぜwwwってちょ、おまwwwwwヒドスwwwww
xmllint/xmlprocで問題があった鴨って、ちょwwwPyXMLはiconv(3)使ってなくね?
以下おさらいの意味で、GNU libiconvとCitrus iconvの挙動の違い。
(A) GNU libiconvとCitrus iconvは、変換を行うbyte sequenceが不正
(mbrtowc(3)に食わせるとEILSEQを返すような場合ね)である時は
同じ動作をします、ま当然ですけどね、フフン。
[test.c]
#include <assert.h>
#include <errno.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int
main(void)
{
iconv_t cd;
char buf[BUFSIZ];
char *s, *in, *out;
size_t n, nin, nout, ret;
s = "ABCD\xa0"; /* ABCD + illegal */
n = strlen(s);
cd = iconv_open("US-ASCII", "UTF-8");
if (cd == (iconv_t)-1)
abort();
in = s;
nin = n;
out = &buf[0];
nout = sizeof(buf);
errno = 0;
ret = iconv(cd, (const char **)&in, &nin, &out, &nout);
printf("ret: %d\n", ret);
printf("%s\n", strerror(errno));
printf("in - converted:[%.*s]\n", n - nin, s);
printf("in - illegal:[%.*s]\n", nin, in);
printf("nin:[%zd]\n", nin);
printf("out:[%.*s]\n", sizeof(buf) - nout, buf);
printf("nout:[%zd]\n", nout);
return 0;
}
* GNU libiconvの場合(A-1)
$ CFLAGS="-I/usr/pkg/include" \
> LDFLAGS="-L/usr/pkg/lib -liconv -Wl,-rpath=/usr/pkg/lib" make test
$ nm test | grep iconv
U libiconv
U libiconv_open
$ ldd test
test:
-liconv.2 => /usr/pkg/lib/libiconv.so.2
-lc.12 => /usr/lib/libc.so.12
$ ./test
ret: -1
Illegal byte sequence
in - converted:[ABCD]
in - illegal:[\xa0]
nin:[1]
out:[ABCD]
nout:[1020]
* Citrus iconvの場合(A-2)
$ rm -f test && make test
$ nm test | grep iconv
U iconv
U iconv_open
$ ldd test
test:
-lc.12 => /usr/lib/libc.so.12
$ ./test
ret: -1
Illegal byte sequence
in - converted:[ABCD]
in - illegal:[\xa0]
nin:[1]
out:[ABCD]
nout:[1020]
inの先頭は不正なバイト列を指し、戻り値は(size_t)-1で
errnoにはEILSEQがセットされます。
(B)しかし一方で
- 変換元としては正常なbyte sequence
- 変換先の文字集合に対応する文字が存在しない
ケースでは、両者は異なる動作をします。
[test.c.diff]
--- test.c.orig 2007-09-22 04:45:32.000000000 +0900
+++ test.c 2007-09-22 04:45:19.000000000 +0900
@@ -13,7 +13,7 @@
char *s, *in, *out;
size_t n, nin, nout, ret;
- s = "ABCD\xa0"; /* ABCD + illegal */
+ s = "\xc2\xa0""ABCD"; /* U+00A0 + ABCD */
n = strlen(s);
cd = iconv_open("US-ASCII", "UTF-8");
* GNU libiconvの場合(B-1)
$ patch -p0 < test.c.diff
$ CFLAGS="-I/usr/pkg/include" \
> LDFLAGS="-L/usr/pkg/lib -liconv -Wl,-rpath=/usr/pkg/lib" make test
$ ./test
ret: -1
Illegal byte sequence
in - converted:[]
in - illegal:[\xc2\xa0ABCD]
nin:[6]
out:[]
nout:[1024]
* Citrus iconvの場合(B-2)
$ rm -f test && make test
$ ./test
ret: 1
Undefined error: 0
in - converted:[\xc2\xa0ABCD]
in - illegal:[]
nin:[0]
out:[?ABCD]
nout:[1019]
GNU libiconvは不正なbyte sequenceに出会ったときと同じ動作をします。
一方Citrus iconvは?や〓といった代替文字に置換して
戻り値として不可逆変換な文字数を返します。
IEEE Std 1003.1-2001には、このケースについて
``If iconv() encounters a character in the input buffer that is valid,
but for which an identical character does not exist in the target codeset,
iconv() shall perform an implementation-defined conversion on this character.''
と書かれていて、実装依存でいいから何かしら変換をしろとあります。
つまり、GNU libiconvが勝手に変換を止めてしまうのは規格違反の疑いがあります。
しかし、libxml2などではこのGNU libiconvの規格違反な動作に依存した
コーディングがあるらしく、
このスレッドで議論になってます。
一方の当事者、Bruno Haible氏も登場しますな。
私の投げたpatchを適用してみましょう。
[test.c.diff]
--- test.c.orig 2007-09-22 04:53:35.000000000 +0900
+++ test.c 2007-09-22 04:52:12.000000000 +0900
@@ -11,7 +11,7 @@
iconv_t cd;
char buf[BUFSIZ];
char *s, *in, *out;
- size_t n, nin, nout, ret;
+ size_t n, nin, nout, ret, dummy;
s = "\xc2\xa0""ABCD"; /* U+00A0 + ABCD */
n = strlen(s);
@@ -26,7 +26,8 @@
nout = sizeof(buf);
errno = 0;
- ret = iconv(cd, (const char **)&in, &nin, &out, &nout);
+ ret = __iconv(cd, (const char **)&in, &nin, &out, &nout,
+ __ICONV_F_STOP_NO_CORRESPONDING_CHAR, &dummy);
printf("ret: %d\n", ret);
printf("%s\n", strerror(errno));
printf("in - converted:[%.*s]\n", n - nin, s);
* Citrus iconv(C-1)
$ cd /usr/src
$ patch -p0 -E -l < ~/patch-iconv
$ cd lib/libc
$ make depend all install
$ cd ../i18n_module/iconv_std
$ make depend all install
$ cd
$ patch -p0 < test.c.diff
$ make test
$ ./test
ret: -1
Illegal byte sequence
in - converted:[]
in - illegal:[\xc2\xa0ABCD]
nin:[6]
out:[]
nout:[1024]
GNU libiconvの挙動をemulateし、C-1とB-1の結果が同じになりました。
しかしまあ、こんなpatchを下手にcommitして将来縛られたくないので絶賛放置中。
余談ですが、iconv(3)の戻り値の不可逆変換された文字数は
(A)のケースと絡むと信用できないので注意が必要。
[test.c.diff]
--- test.c.orig 2007-09-22 04:53:35.000000000 +0900
+++ test.c 2007-09-22 13:05:09.000000000 +0900
@@ -13,7 +13,7 @@
char *s, *in, *out;
size_t n, nin, nout, ret;
- s = "\xc2\xa0""ABCD"; /* U+00A0 + ABCD */
+ s = "\xc2\xa0\xa0"; /* U+00A0 + illegal */
n = strlen(s);
cd = iconv_open("US-ASCII", "UTF-8");
* Citrus iconv(D-1)
$ make test
$ ./test
ret: -1
Illegal byte sequence
in - converted:[\xc2\x0]
in - illegal:[\xa0]
nin:[1]
out:[?]
nout:[1023]
戻り値として不正な変換が行われたことを表す(size_t)-1が返り
不可逆変換された文字数1は失われてしまいます。
まあ普通はEILSEQだったらそこで終了なはずなので
失われてもいいという判断なのですが、どうも無理矢理keep goingしたがる
アプリが世の中多いようで。