summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-09-05 09:16:34 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-09-05 09:16:34 +0000
commit07e08245d3db1caab2cff2864510f99bf2a84ff9 (patch)
tree61977bb53e76df7726b15d10103586b21b9134be
parentf0bf74a07b98e0c5cf8875b1a4eb258083fd991e (diff)
* 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
-rw-r--r--ChangeLog6
-rw-r--r--ext/iconv/iconv.c81
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 <nobu@ruby-lang.org>
+
+ * 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 <ko1@atdot.net>
* 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");
}