From 07e08245d3db1caab2cff2864510f99bf2a84ff9 Mon Sep 17 00:00:00 2001 From: nobu Date: Fri, 5 Sep 2008 09:16:34 +0000 Subject: * ext/iconv/iconv.c (iconv_create): strips glibc style option before charset mapping. retris without options if they seemed causing error, and warns. [ruby-dev:36147] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19147 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 6 +++++ ext/iconv/iconv.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 950761a83e..4674d493db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Fri Sep 5 18:16:31 2008 Nobuyoshi Nakada + + * ext/iconv/iconv.c (iconv_create): strips glibc style option before + charset mapping. retris without options if they seemed causing + error, and warns. [ruby-dev:36147] + Fri Sep 5 03:09:48 2008 Koichi Sasada * iseq.c (iseq_data_to_ary): make it static. diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index 0e7302e1ad..14ef9ae380 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -135,6 +135,21 @@ charset_map_get(void) return charset_map; } +static VALUE +strip_glibc_option(VALUE *code) +{ + VALUE val = *code; + const char *ptr = RSTRING_PTR(val), *pend = RSTRING_END(val); + const char *slash = memchr(ptr, '/', pend - ptr); + if (slash && slash < pend - 1 && slash[1] == '/') { + VALUE opt = rb_str_subseq(val, slash - ptr, pend - slash); + val = rb_str_subseq(val, 0, slash - ptr); + *code = val; + return opt; + } + return 0; +} + static char * map_charset(VALUE *code) { @@ -153,29 +168,53 @@ map_charset(VALUE *code) static iconv_t iconv_create(VALUE to, VALUE from, struct rb_iconv_opt_t *opt, int *idx) { + VALUE toopt = strip_glibc_option(&to); + VALUE fromopt = strip_glibc_option(&from); + VALUE toenc = 0, fromenc = 0; const char* tocode = map_charset(&to); const char* fromcode = map_charset(&from); iconv_t cd; + int retry = 0; - if ((*idx = rb_enc_find_index(tocode)) < 0) { - const char *slash = strchr(tocode, '/'); - if (slash && slash[1] == '/') { - VALUE tmp = rb_str_new(tocode, slash - tocode); - *idx = rb_enc_find_index(RSTRING_PTR(tmp)); - } - } + *idx = rb_enc_find_index(tocode); - cd = iconv_open(tocode, fromcode); - if (cd == (iconv_t)-1) { + if (toopt) { + toenc = rb_str_plus(to, toopt); + tocode = RSTRING_PTR(toenc); + } + if (fromopt) { + fromenc = rb_str_plus(from, fromopt); + fromcode = RSTRING_PTR(fromenc); + } + while ((cd = iconv_open(tocode, fromcode)) == (iconv_t)-1) { + int inval = 0; switch (errno) { case EMFILE: case ENFILE: case ENOMEM: - rb_gc(); - cd = iconv_open(tocode, fromcode); + if (!retry++) { + rb_gc(); + continue; + } + break; + case EINVAL: + retry = 0; + inval = 1; + if (toenc) { + tocode = RSTRING_PTR(to); + rb_str_resize(toenc, 0); + toenc = 0; + continue; + } + if (fromenc) { + fromcode = RSTRING_PTR(from); + rb_str_resize(fromenc, 0); + fromenc = 0; + continue; + } + break; } - if (cd == (iconv_t)-1) { - int inval = errno == EINVAL; + { const char *s = inval ? "invalid encoding " : "iconv"; volatile VALUE msg = rb_str_new(0, strlen(s) + RSTRING_LEN(to) + RSTRING_LEN(from) + 8); @@ -190,10 +229,25 @@ iconv_create(VALUE to, VALUE from, struct rb_iconv_opt_t *opt, int *idx) } } + if (toopt || fromopt) { + if (toopt && fromopt && RTEST(rb_str_equal(toopt, fromopt))) { + fromopt = 0; + } + if (toopt && fromopt) { + rb_warning("encoding option isn't portable: %s, %s", + RSTRING_PTR(toopt) + 2, RSTRING_PTR(fromopt) + 2); + } + else { + rb_warning("encoding option isn't portable: %s", + (toopt ? RSTRING_PTR(toopt) : RSTRING_PTR(fromopt)) + 2); + } + } + if (opt) { #ifdef ICONV_SET_TRANSLITERATE if (opt->transliterate != Qundef) { int flag = RTEST(opt->transliterate); + rb_warning("encoding option isn't portable: transliterate"); if (iconvctl(cd, ICONV_SET_TRANSLITERATE, (void *)&flag)) rb_sys_fail("ICONV_SET_TRANSLITERATE"); } @@ -201,6 +255,7 @@ iconv_create(VALUE to, VALUE from, struct rb_iconv_opt_t *opt, int *idx) #ifdef ICONV_SET_DISCARD_ILSEQ if (opt->discard_ilseq != Qundef) { int flag = RTEST(opt->discard_ilseq); + rb_warning("encoding option isn't portable: discard_ilseq"); if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&flag)) rb_sys_fail("ICONV_SET_DISCARD_ILSEQ"); } -- cgit v1.2.3