rubysl-iconv 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b996f2c507adb4ba7d797f82968121e4f4878430
4
+ data.tar.gz: 88c863f3933a15ce5b24e212a61cf39cc55b8214
5
+ SHA512:
6
+ metadata.gz: d353a1064d680991c841d91120fb8acb6749f2c081a29b37263bd92e11581e15f6dba715a0b665fbffbc7564e15624ffb8035bf05cae6a460422a53ee21c3b12
7
+ data.tar.gz: 0243ca3bcaf43e5a44a5f54f7fcfb8475e6183dd928e785ebb488eb1cc2586a8fc58058f64e9c68c6393858958b5465bd9d46750689f8d27d41c476fb88d8860
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.8.7
7
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-iconv.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Iconv
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-iconv'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-iconv
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,51 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("iconv")
4
+
5
+ conf = File.exist?(File.join($srcdir, "config.charset"))
6
+ conf = with_config("config-charset", enable_config("config-charset", conf))
7
+
8
+ if have_func("iconv", "iconv.h") or
9
+ have_library("iconv", "iconv", "iconv.h")
10
+ if checking_for("const of iconv() 2nd argument") do
11
+ create_tmpsrc(cpp_include("iconv.h") + "---> iconv(cd,0,0,0,0) <---")
12
+ src = xpopen(cpp_command("")) {|f|f.read}
13
+ if !(func = src[/^--->\s*(\w+).*\s*<---/, 1])
14
+ Logging::message "iconv function name not found"
15
+ false
16
+ elsif !(second = src[%r"\b#{func}\s*\(.*?,(.*?),.*?\)\s*;"m, 1])
17
+ Logging::message "prototype for #{func}() not found"
18
+ false
19
+ else
20
+ Logging::message $&+"\n"
21
+ /\bconst\b/ =~ second
22
+ end
23
+ end
24
+ $defs.push('-DICONV_INPTR_CONST')
25
+ end
26
+ if conf
27
+ prefix = '$(srcdir)'
28
+ prefix = $nmake ? "{#{prefix}}" : "#{prefix}/"
29
+ if $extout
30
+ wrapper = "$(RUBYARCHDIR)/iconv.rb"
31
+ else
32
+ wrapper = "./iconv.rb"
33
+ $INSTALLFILES = [[wrapper, "$(RUBYARCHDIR)"]]
34
+ end
35
+ if String === conf
36
+ require 'uri'
37
+ scheme = URI.parse(conf).scheme
38
+ else
39
+ conf = "$(srcdir)/config.charset"
40
+ end
41
+ $cleanfiles << wrapper
42
+ end
43
+ create_makefile("iconv/iconv")
44
+ if conf
45
+ open("Makefile", "a") do |mf|
46
+ mf.print("\nall: #{wrapper}\n\n#{wrapper}: #{prefix}charset_alias.rb")
47
+ mf.print(" ", conf) unless scheme
48
+ mf.print("\n\t$(RUBY) $(srcdir)/charset_alias.rb #{conf} $@\n")
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,927 @@
1
+ /* -*- mode:c; c-file-style:"ruby" -*- */
2
+ /**********************************************************************
3
+
4
+ iconv.c -
5
+
6
+ $Author$
7
+ $Date$
8
+ created at: Wed Dec 1 20:28:09 JST 1999
9
+
10
+ All the files in this distribution are covered under the Ruby's
11
+ license (see the file COPYING).
12
+
13
+ Documentation by Yukihiro Matsumoto and Gavin Sinclair.
14
+
15
+ **********************************************************************/
16
+
17
+ #include "ruby.h"
18
+ #include <errno.h>
19
+ #include <iconv.h>
20
+ #include <assert.h>
21
+ #include "st.h"
22
+ #include "intern.h"
23
+
24
+ /*
25
+ * Document-class: Iconv
26
+ *
27
+ * == Summary
28
+ *
29
+ * Ruby extension for charset conversion.
30
+ *
31
+ * == Abstract
32
+ *
33
+ * Iconv is a wrapper class for the UNIX 95 <tt>iconv()</tt> function family,
34
+ * which translates string between various encoding systems.
35
+ *
36
+ * See Open Group's on-line documents for more details.
37
+ * * <tt>iconv.h</tt>: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.h.html
38
+ * * <tt>iconv_open()</tt>: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_open.html
39
+ * * <tt>iconv()</tt>: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html
40
+ * * <tt>iconv_close()</tt>: http://www.opengroup.org/onlinepubs/007908799/xsh/iconv_close.html
41
+ *
42
+ * Which coding systems are available is platform-dependent.
43
+ *
44
+ * == Examples
45
+ *
46
+ * 1. Simple conversion between two charsets.
47
+ *
48
+ * converted_text = Iconv.conv('iso-8859-15', 'utf-8', text)
49
+ *
50
+ * 2. Instantiate a new Iconv and use method Iconv#iconv.
51
+ *
52
+ * cd = Iconv.new(to, from)
53
+ * begin
54
+ * input.each { |s| output << cd.iconv(s) }
55
+ * output << cd.iconv(nil) # Don't forget this!
56
+ * ensure
57
+ * cd.close
58
+ * end
59
+ *
60
+ * 3. Invoke Iconv.open with a block.
61
+ *
62
+ * Iconv.open(to, from) do |cd|
63
+ * input.each { |s| output << cd.iconv(s) }
64
+ * output << cd.iconv(nil)
65
+ * end
66
+ *
67
+ * 4. Shorthand for (3).
68
+ *
69
+ * Iconv.iconv(to, from, *input.to_a)
70
+ */
71
+
72
+ /* Invalid value for iconv_t is -1 but 0 for VALUE, I hope VALUE is
73
+ big enough to keep iconv_t */
74
+ #define VALUE2ICONV(v) ((iconv_t)((VALUE)(v) ^ -1))
75
+ #define ICONV2VALUE(c) ((VALUE)(c) ^ -1)
76
+
77
+ struct iconv_env_t
78
+ {
79
+ iconv_t cd;
80
+ int argc;
81
+ VALUE *argv;
82
+ VALUE ret;
83
+ VALUE (*append)_((VALUE, VALUE));
84
+ };
85
+
86
+ static VALUE rb_eIconvInvalidEncoding;
87
+ static VALUE rb_eIconvFailure;
88
+ static VALUE rb_eIconvIllegalSeq;
89
+ static VALUE rb_eIconvInvalidChar;
90
+ static VALUE rb_eIconvOutOfRange;
91
+ static VALUE rb_eIconvBrokenLibrary;
92
+
93
+ static ID rb_success, rb_failed;
94
+ static VALUE iconv_fail _((VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, const char *mesg));
95
+ static VALUE iconv_fail_retry _((VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, const char *mesg));
96
+ static VALUE iconv_failure_initialize _((VALUE error, VALUE mesg, VALUE success, VALUE failed));
97
+ static VALUE iconv_failure_success _((VALUE self));
98
+ static VALUE iconv_failure_failed _((VALUE self));
99
+
100
+ static iconv_t iconv_create _((VALUE to, VALUE from));
101
+ static void iconv_dfree _((void *cd));
102
+ static VALUE iconv_free _((VALUE cd));
103
+ static VALUE iconv_try _((iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen));
104
+ static VALUE rb_str_derive _((VALUE str, const char* ptr, int len));
105
+ static VALUE iconv_convert _((iconv_t cd, VALUE str, long start, long length, struct iconv_env_t* env));
106
+ static VALUE iconv_s_allocate _((VALUE klass));
107
+ static VALUE iconv_initialize _((VALUE self, VALUE to, VALUE from));
108
+ static VALUE iconv_s_open _((VALUE self, VALUE to, VALUE from));
109
+ static VALUE iconv_s_convert _((struct iconv_env_t* env));
110
+ static VALUE iconv_s_iconv _((int argc, VALUE *argv, VALUE self));
111
+ static VALUE iconv_init_state _((VALUE cd));
112
+ static VALUE iconv_finish _((VALUE self));
113
+ static VALUE iconv_iconv _((int argc, VALUE *argv, VALUE self));
114
+
115
+ static VALUE charset_map;
116
+
117
+ /*
118
+ * Document-method: charset_map
119
+ * call-seq: Iconv.charset_map
120
+ *
121
+ * Returns the map from canonical name to system dependent name.
122
+ */
123
+ static VALUE charset_map_get _((void))
124
+ {
125
+ return charset_map;
126
+ }
127
+
128
+ static char *
129
+ map_charset(VALUE *code)
130
+ {
131
+ VALUE val = StringValue(*code);
132
+
133
+ if (RHASH_SIZE(charset_map)) {
134
+ VALUE key = rb_funcall2(val, rb_intern("downcase"), 0, 0);
135
+ StringValuePtr(key);
136
+ VALUE data = rb_hash_aref(charset_map, key);
137
+ if (data != Qnil) {
138
+ *code = data;
139
+ }
140
+ }
141
+ return StringValuePtr(*code);
142
+ }
143
+
144
+ NORETURN(static void rb_iconv_sys_fail(const char *s));
145
+ static void
146
+ rb_iconv_sys_fail(const char *s)
147
+ {
148
+ if (errno == 0) {
149
+ rb_exc_raise(iconv_fail(rb_eIconvBrokenLibrary, Qnil, Qnil, NULL, s));
150
+ }
151
+ rb_sys_fail(s);
152
+ }
153
+
154
+ #define rb_sys_fail(s) rb_iconv_sys_fail(s)
155
+
156
+ static iconv_t
157
+ iconv_create
158
+ #ifdef HAVE_PROTOTYPES
159
+ (VALUE to, VALUE from)
160
+ #else /* HAVE_PROTOTYPES */
161
+ (to, from)
162
+ VALUE to;
163
+ VALUE from;
164
+ #endif /* HAVE_PROTOTYPES */
165
+ {
166
+ const char* tocode = map_charset(&to);
167
+ const char* fromcode = map_charset(&from);
168
+
169
+ iconv_t cd = iconv_open(tocode, fromcode);
170
+
171
+ if (cd == (iconv_t)-1) {
172
+ switch (errno) {
173
+ case EMFILE:
174
+ case ENFILE:
175
+ case ENOMEM:
176
+ rb_gc();
177
+ cd = iconv_open(tocode, fromcode);
178
+ }
179
+ if (cd == (iconv_t)-1) {
180
+ int inval = errno == EINVAL;
181
+ const char *s = inval ? "invalid encoding " : "iconv";
182
+ volatile VALUE msg = rb_str_new(0, strlen(s) + RSTRING(to)->len +
183
+ RSTRING(from)->len + 8);
184
+
185
+ sprintf(RSTRING(msg)->ptr, "%s(\"%s\", \"%s\")",
186
+ s, RSTRING(to)->ptr, RSTRING(from)->ptr);
187
+ s = RSTRING(msg)->ptr;
188
+ RSTRING(msg)->len = strlen(s);
189
+ if (!inval) rb_sys_fail(s);
190
+ rb_exc_raise(iconv_fail(rb_eIconvInvalidEncoding, Qnil,
191
+ rb_ary_new3(2, to, from), NULL, s));
192
+ }
193
+ }
194
+
195
+ return cd;
196
+ }
197
+
198
+ static void
199
+ iconv_dfree
200
+ #ifdef HAVE_PROTOTYPES
201
+ (void *cd)
202
+ #else /* HAVE_PROTOTYPES */
203
+ (cd)
204
+ void *cd;
205
+ #endif /* HAVE_PROTOTYPES */
206
+ {
207
+ iconv_close(VALUE2ICONV(cd));
208
+ }
209
+
210
+ #define ICONV_FREE iconv_dfree
211
+
212
+ static VALUE
213
+ iconv_free
214
+ #ifdef HAVE_PROTOTYPES
215
+ (VALUE cd)
216
+ #else /* HAVE_PROTOTYPES */
217
+ (cd)
218
+ VALUE cd;
219
+ #endif /* HAVE_PROTOTYPES */
220
+ {
221
+ if (cd && iconv_close(VALUE2ICONV(cd)) == -1)
222
+ rb_sys_fail("iconv_close");
223
+ return Qnil;
224
+ }
225
+
226
+ static VALUE
227
+ check_iconv
228
+ #ifdef HAVE_PROTOTYPES
229
+ (VALUE obj)
230
+ #else /* HAVE_PROTOTYPES */
231
+ (obj)
232
+ VALUE obj;
233
+ #endif /* HAVE_PROTOTYPES */
234
+ {
235
+ Check_Type(obj, T_DATA);
236
+ if (RDATA(obj)->dfree != ICONV_FREE) {
237
+ rb_raise(rb_eArgError, "Iconv expected (%s)", rb_class2name(CLASS_OF(obj)));
238
+ }
239
+ return (VALUE)DATA_PTR(obj);
240
+ }
241
+
242
+ static VALUE
243
+ iconv_try
244
+ #ifdef HAVE_PROTOTYPES
245
+ (iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen)
246
+ #else /* HAVE_PROTOTYPES */
247
+ (cd, inptr, inlen, outptr, outlen)
248
+ iconv_t cd;
249
+ const char **inptr;
250
+ size_t *inlen;
251
+ char **outptr;
252
+ size_t *outlen;
253
+ #endif /* HAVE_PROTOTYPES */
254
+ {
255
+ #ifdef ICONV_INPTR_CONST
256
+ #define ICONV_INPTR_CAST
257
+ #else
258
+ #define ICONV_INPTR_CAST (char **)
259
+ #endif
260
+ size_t ret;
261
+
262
+ errno = 0;
263
+ ret = iconv(cd, ICONV_INPTR_CAST inptr, inlen, outptr, outlen);
264
+ if (ret == (size_t)-1) {
265
+ if (!*inlen)
266
+ return Qfalse;
267
+ switch (errno) {
268
+ case E2BIG:
269
+ /* try the left in next loop */
270
+ break;
271
+ case EILSEQ:
272
+ return rb_eIconvIllegalSeq;
273
+ case EINVAL:
274
+ return rb_eIconvInvalidChar;
275
+ case 0:
276
+ return rb_eIconvBrokenLibrary;
277
+ default:
278
+ rb_sys_fail("iconv");
279
+ }
280
+ }
281
+ else if (*inlen > 0) {
282
+ /* something goes wrong */
283
+ return rb_eIconvIllegalSeq;
284
+ }
285
+ else if (ret) {
286
+ return Qnil; /* conversion */
287
+ }
288
+ return Qfalse;
289
+ }
290
+
291
+ #define FAILED_MAXLEN 16
292
+
293
+ static VALUE iconv_failure_initialize
294
+ #ifdef HAVE_PROTOTYPES
295
+ (VALUE error, VALUE mesg, VALUE success, VALUE failed)
296
+ #else /* HAVE_PROTOTYPES */
297
+ (error, mesg, success, failed)
298
+ VALUE error, mesg, success, failed;
299
+ #endif /* HAVE_PROTOTYPES */
300
+ {
301
+ rb_call_super(1, &mesg);
302
+ rb_ivar_set(error, rb_success, success);
303
+ rb_ivar_set(error, rb_failed, failed);
304
+ return error;
305
+ }
306
+
307
+ static VALUE
308
+ iconv_fail
309
+ #ifdef HAVE_PROTOTYPES
310
+ (VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, const char *mesg)
311
+ #else /* HAVE_PROTOTYPES */
312
+ (error, success, failed, env, mesg)
313
+ VALUE error, success, failed;
314
+ struct iconv_env_t *env;
315
+ const char *mesg;
316
+ #endif /* HAVE_PROTOTYPES */
317
+ {
318
+ VALUE args[3];
319
+
320
+ if (mesg && *mesg) {
321
+ args[0] = rb_str_new2(mesg);
322
+ }
323
+ else if (TYPE(failed) != T_STRING || RSTRING(failed)->len < FAILED_MAXLEN) {
324
+ args[0] = rb_inspect(failed);
325
+ }
326
+ else {
327
+ args[0] = rb_inspect(rb_str_substr(failed, 0, FAILED_MAXLEN));
328
+ rb_str_cat2(args[0], "...");
329
+ }
330
+ args[1] = success;
331
+ args[2] = failed;
332
+ if (env) {
333
+ args[1] = env->append(rb_obj_dup(env->ret), success);
334
+ if (env->argc > 0) {
335
+ *(env->argv) = failed;
336
+ args[2] = rb_ary_new4(env->argc, env->argv);
337
+ }
338
+ }
339
+ return rb_class_new_instance(3, args, error);
340
+ }
341
+
342
+ static VALUE
343
+ iconv_fail_retry(VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env, const char *mesg)
344
+ {
345
+ error = iconv_fail(error, success, failed, env, mesg);
346
+ if (!rb_block_given_p()) rb_exc_raise(error);
347
+ rb_set_errinfo(error);
348
+ return rb_yield(failed);
349
+ }
350
+
351
+ static VALUE
352
+ rb_str_derive
353
+ #ifdef HAVE_PROTOTYPES
354
+ (VALUE str, const char* ptr, int len)
355
+ #else /* HAVE_PROTOTYPES */
356
+ (str, ptr, len)
357
+ VALUE str;
358
+ const char *ptr;
359
+ int len;
360
+ #endif /* HAVE_PROTOTYPES */
361
+ {
362
+ VALUE ret;
363
+
364
+ if (NIL_P(str))
365
+ return rb_str_new(ptr, len);
366
+ if (RSTRING(str)->ptr == ptr && RSTRING(str)->len == len)
367
+ return str;
368
+ if (RSTRING(str)->ptr + RSTRING(str)->len == ptr + len)
369
+ ret = rb_str_substr(str, ptr - RSTRING(str)->ptr, len);
370
+ else
371
+ ret = rb_str_new(ptr, len);
372
+ OBJ_INFECT(ret, str);
373
+ return ret;
374
+ }
375
+
376
+ static VALUE
377
+ iconv_convert
378
+ #ifdef HAVE_PROTOTYPES
379
+ (iconv_t cd, VALUE str, long start, long length, struct iconv_env_t* env)
380
+ #else /* HAVE_PROTOTYPES */
381
+ (cd, str, start, length, env)
382
+ iconv_t cd;
383
+ VALUE str;
384
+ long start;
385
+ long length;
386
+ struct iconv_env_t *env;
387
+ #endif /* HAVE_PROTOTYPES */
388
+ {
389
+ VALUE ret = Qfalse;
390
+ VALUE error = Qfalse;
391
+ VALUE rescue;
392
+ const char *inptr, *instart;
393
+ size_t inlen;
394
+ /* I believe ONE CHARACTER never exceed this. */
395
+ char buffer[BUFSIZ];
396
+ char *outptr;
397
+ size_t outlen;
398
+
399
+ if (cd == (iconv_t)-1)
400
+ rb_raise(rb_eArgError, "closed iconv");
401
+
402
+ if (NIL_P(str)) {
403
+ /* Reset output pointer or something. */
404
+ inptr = "";
405
+ inlen = 0;
406
+ outptr = buffer;
407
+ outlen = sizeof(buffer);
408
+ error = iconv_try(cd, &inptr, &inlen, &outptr, &outlen);
409
+ if (RTEST(error)) {
410
+ unsigned int i;
411
+ rescue = iconv_fail_retry(error, Qnil, Qnil, env, 0);
412
+ if (TYPE(rescue) == T_ARRAY) {
413
+ str = RARRAY_LEN(rescue) > 0 ? rb_ary_entry(rescue, 0) : Qnil;
414
+ }
415
+ if (FIXNUM_P(str) && (i = FIX2INT(str)) <= 0xff) {
416
+ char c = i;
417
+ str = rb_str_new(&c, 1);
418
+ }
419
+ else if (!NIL_P(str)) {
420
+ StringValue(str);
421
+ }
422
+ }
423
+
424
+ inptr = NULL;
425
+ length = 0;
426
+ }
427
+ else {
428
+ int slen;
429
+
430
+ StringValue(str);
431
+ slen = RSTRING(str)->len;
432
+ inptr = RSTRING(str)->ptr;
433
+
434
+ inptr += start;
435
+ if (length < 0 || length > start + slen)
436
+ length = slen - start;
437
+ }
438
+ instart = inptr;
439
+ inlen = length;
440
+
441
+ do {
442
+ char errmsg[50];
443
+ const char *tmpstart = inptr;
444
+ outptr = buffer;
445
+ outlen = sizeof(buffer);
446
+
447
+ errmsg[0] = 0;
448
+ error = iconv_try(cd, &inptr, &inlen, &outptr, &outlen);
449
+
450
+ if (0 <= outlen && outlen <= sizeof(buffer)) {
451
+ outlen = sizeof(buffer) - outlen;
452
+ if (NIL_P(error) || /* something converted */
453
+ outlen > inptr - tmpstart || /* input can't contain output */
454
+ (outlen < inptr - tmpstart && inlen > 0) || /* something skipped */
455
+ memcmp(buffer, tmpstart, outlen)) /* something differs */
456
+ {
457
+ if (NIL_P(str)) {
458
+ ret = rb_str_new(buffer, outlen);
459
+ }
460
+ else {
461
+ if (ret) {
462
+ ret = rb_str_buf_cat(ret, instart, tmpstart - instart);
463
+ }
464
+ else {
465
+ ret = rb_str_new(instart, tmpstart - instart);
466
+ OBJ_INFECT(ret, str);
467
+ }
468
+ ret = rb_str_buf_cat(ret, buffer, outlen);
469
+ instart = inptr;
470
+ }
471
+ }
472
+ else if (!inlen) {
473
+ inptr = tmpstart + outlen;
474
+ }
475
+ }
476
+ else {
477
+ /* Some iconv() have a bug, return *outlen out of range */
478
+ sprintf(errmsg, "bug?(output length = %ld)", (long)(sizeof(buffer) - outlen));
479
+ error = rb_eIconvOutOfRange;
480
+ }
481
+
482
+ if (RTEST(error)) {
483
+ long len = 0;
484
+
485
+ if (!ret)
486
+ ret = rb_str_derive(str, instart, inptr - instart);
487
+ else if (inptr > instart)
488
+ rb_str_cat(ret, instart, inptr - instart);
489
+ str = rb_str_derive(str, inptr, inlen);
490
+ rescue = iconv_fail_retry(error, ret, str, env, errmsg);
491
+ if (TYPE(rescue) == T_ARRAY) {
492
+ if ((len = RARRAY_LEN(rescue)) > 0)
493
+ rb_str_concat(ret, rb_ary_entry(rescue, 0));
494
+ if (len > 1 && !NIL_P(str = rb_ary_entry(rescue, 1))) {
495
+ StringValue(str);
496
+ inlen = length = RSTRING(str)->len;
497
+ instart = inptr = RSTRING(str)->ptr;
498
+ continue;
499
+ }
500
+ }
501
+ else if (!NIL_P(rescue)) {
502
+ rb_str_concat(ret, rescue);
503
+ }
504
+ break;
505
+ }
506
+ } while (inlen > 0);
507
+
508
+ if (!ret)
509
+ ret = rb_str_derive(str, instart, inptr - instart);
510
+ else if (inptr > instart)
511
+ rb_str_cat(ret, instart, inptr - instart);
512
+ return ret;
513
+ }
514
+
515
+ static VALUE
516
+ iconv_s_allocate
517
+ #ifdef HAVE_PROTOTYPES
518
+ (VALUE klass)
519
+ #else /* HAVE_PROTOTYPES */
520
+ (klass)
521
+ VALUE klass;
522
+ #endif /* HAVE_PROTOTYPES */
523
+ {
524
+ return Data_Wrap_Struct(klass, 0, ICONV_FREE, 0);
525
+ }
526
+
527
+ /*
528
+ * Document-method: new
529
+ * call-seq: Iconv.new(to, from)
530
+ *
531
+ * Creates new code converter from a coding-system designated with +from+
532
+ * to another one designated with +to+.
533
+ *
534
+ * === Parameters
535
+ *
536
+ * +to+:: encoding name for destination
537
+ * +from+:: encoding name for source
538
+ *
539
+ * === Exceptions
540
+ *
541
+ * TypeError:: if +to+ or +from+ aren't String
542
+ * InvalidEncoding:: if designated converter couldn't find out
543
+ * SystemCallError:: if <tt>iconv_open(3)</tt> fails
544
+ */
545
+ static VALUE
546
+ iconv_initialize
547
+ #ifdef HAVE_PROTOTYPES
548
+ (VALUE self, VALUE to, VALUE from)
549
+ #else /* HAVE_PROTOTYPES */
550
+ (self, to, from)
551
+ VALUE self;
552
+ VALUE to;
553
+ VALUE from;
554
+ #endif /* HAVE_PROTOTYPES */
555
+ {
556
+ iconv_free(check_iconv(self));
557
+ DATA_PTR(self) = NULL;
558
+ DATA_PTR(self) = (void *)ICONV2VALUE(iconv_create(to, from));
559
+ return self;
560
+ }
561
+
562
+ /*
563
+ * Document-method: open
564
+ * call-seq: Iconv.open(to, from) { |iconv| ... }
565
+ *
566
+ * Equivalent to Iconv.new except that when it is called with a block, it
567
+ * yields with the new instance and closes it, and returns the result which
568
+ * returned from the block.
569
+ */
570
+ static VALUE
571
+ iconv_s_open
572
+ #ifdef HAVE_PROTOTYPES
573
+ (VALUE self, VALUE to, VALUE from)
574
+ #else /* HAVE_PROTOTYPES */
575
+ (self, to, from)
576
+ VALUE self;
577
+ VALUE to;
578
+ VALUE from;
579
+ #endif /* HAVE_PROTOTYPES */
580
+ {
581
+ VALUE cd = ICONV2VALUE(iconv_create(to, from));
582
+
583
+ self = Data_Wrap_Struct(self, NULL, ICONV_FREE, (void *)cd);
584
+ if (rb_block_given_p()) {
585
+ return rb_ensure(rb_yield, self, (VALUE(*)())iconv_finish, self);
586
+ }
587
+ else {
588
+ return self;
589
+ }
590
+ }
591
+
592
+ static VALUE
593
+ iconv_s_convert
594
+ #ifdef HAVE_PROTOTYPES
595
+ (struct iconv_env_t* env)
596
+ #else /* HAVE_PROTOTYPES */
597
+ (env)
598
+ struct iconv_env_t *env;
599
+ #endif /* HAVE_PROTOTYPES */
600
+ {
601
+ VALUE last = 0;
602
+
603
+ for (; env->argc > 0; --env->argc, ++env->argv) {
604
+ VALUE s = iconv_convert(env->cd, last = *(env->argv), 0, -1, env);
605
+ env->append(env->ret, s);
606
+ }
607
+
608
+ if (!NIL_P(last)) {
609
+ VALUE s = iconv_convert(env->cd, Qnil, 0, 0, env);
610
+ if (RSTRING(s)->len)
611
+ env->append(env->ret, s);
612
+ }
613
+
614
+ return env->ret;
615
+ }
616
+
617
+ /*
618
+ * Document-method: Iconv::iconv
619
+ * call-seq: Iconv.iconv(to, from, *strs)
620
+ *
621
+ * Shorthand for
622
+ * Iconv.open(to, from) { |cd|
623
+ * (strs + [nil]).collect { |s| cd.iconv(s) }
624
+ * }
625
+ *
626
+ * === Parameters
627
+ *
628
+ * <tt>to, from</tt>:: see Iconv.new
629
+ * <tt>strs</tt>:: strings to be converted
630
+ *
631
+ * === Exceptions
632
+ *
633
+ * Exceptions thrown by Iconv.new, Iconv.open and Iconv#iconv.
634
+ */
635
+ static VALUE
636
+ iconv_s_iconv
637
+ #ifdef HAVE_PROTOTYPES
638
+ (int argc, VALUE *argv, VALUE self)
639
+ #else /* HAVE_PROTOTYPES */
640
+ (argc, argv, self)
641
+ int argc;
642
+ VALUE *argv;
643
+ VALUE self;
644
+ #endif /* HAVE_PROTOTYPES */
645
+ {
646
+ struct iconv_env_t arg;
647
+
648
+ if (argc < 2) /* needs `to' and `from' arguments at least */
649
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 2);
650
+
651
+ arg.argc = argc -= 2;
652
+ arg.argv = argv + 2;
653
+ arg.append = rb_ary_push;
654
+ arg.ret = rb_ary_new2(argc);
655
+ arg.cd = iconv_create(argv[0], argv[1]);
656
+ return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd));
657
+ }
658
+
659
+ /*
660
+ * Document-method: Iconv::conv
661
+ * call-seq: Iconv.conv(to, from, str)
662
+ *
663
+ * Shorthand for
664
+ * Iconv.iconv(to, from, str).join
665
+ * See Iconv.iconv.
666
+ */
667
+ static VALUE
668
+ iconv_s_conv
669
+ #ifdef HAVE_PROTOTYPES
670
+ (VALUE self, VALUE to, VALUE from, VALUE str)
671
+ #else /* HAVE_PROTOTYPES */
672
+ (self, to, from, str)
673
+ VALUE self, to, from, str;
674
+ #endif /* HAVE_PROTOTYPES */
675
+ {
676
+ struct iconv_env_t arg;
677
+
678
+ arg.argc = 1;
679
+ arg.argv = &str;
680
+ arg.append = rb_str_append;
681
+ arg.ret = rb_str_new(0, 0);
682
+ arg.cd = iconv_create(to, from);
683
+ return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd));
684
+ }
685
+
686
+ /*
687
+ * Document-method: close
688
+ *
689
+ * Finishes conversion.
690
+ *
691
+ * After calling this, calling Iconv#iconv will cause an exception, but
692
+ * multiple calls of #close are guaranteed to end successfully.
693
+ *
694
+ * Returns a string containing the byte sequence to change the output buffer to
695
+ * its initial shift state.
696
+ */
697
+ static VALUE
698
+ iconv_init_state
699
+ #ifdef HAVE_PROTOTYPES
700
+ (VALUE cd)
701
+ #else /* HAVE_PROTOTYPES */
702
+ (cd)
703
+ VALUE cd;
704
+ #endif /* HAVE_PROTOTYPES */
705
+ {
706
+ return iconv_convert(VALUE2ICONV(cd), Qnil, 0, 0, NULL);
707
+ }
708
+
709
+ static VALUE
710
+ iconv_finish
711
+ #ifdef HAVE_PROTOTYPES
712
+ (VALUE self)
713
+ #else /* HAVE_PROTOTYPES */
714
+ (self)
715
+ VALUE self;
716
+ #endif /* HAVE_PROTOTYPES */
717
+ {
718
+ VALUE cd = check_iconv(self);
719
+
720
+ if (!cd) return Qnil;
721
+ DATA_PTR(self) = NULL;
722
+
723
+ return rb_ensure(iconv_init_state, cd, iconv_free, cd);
724
+ }
725
+
726
+ /*
727
+ * Document-method: Iconv#iconv
728
+ * call-seq: iconv(str, start=0, length=-1)
729
+ *
730
+ * Converts string and returns the result.
731
+ * * If +str+ is a String, converts <tt>str[start, length]</tt> and returns the converted string.
732
+ * * If +str+ is +nil+, places converter itself into initial shift state and
733
+ * just returns a string containing the byte sequence to change the output
734
+ * buffer to its initial shift state.
735
+ * * Otherwise, raises an exception.
736
+ *
737
+ * === Parameters
738
+ *
739
+ * str:: string to be converted, or nil
740
+ * start:: starting offset
741
+ * length:: conversion length; nil or -1 means whole the string from start
742
+ *
743
+ * === Exceptions
744
+ *
745
+ * * IconvIllegalSequence
746
+ * * IconvInvalidCharacter
747
+ * * IconvOutOfRange
748
+ *
749
+ * === Examples
750
+ *
751
+ * See the Iconv documentation.
752
+ */
753
+ static VALUE
754
+ iconv_iconv
755
+ #ifdef HAVE_PROTOTYPES
756
+ (int argc, VALUE *argv, VALUE self)
757
+ #else /* HAVE_PROTOTYPES */
758
+ (argc, argv, self)
759
+ int argc;
760
+ VALUE *argv;
761
+ VALUE self;
762
+ #endif /* HAVE_PROTOTYPES */
763
+ {
764
+ VALUE str, n1, n2;
765
+ VALUE cd = check_iconv(self);
766
+ long start = 0, length = 0, slen = 0;
767
+
768
+ rb_scan_args(argc, argv, "12", &str, &n1, &n2);
769
+ if (!NIL_P(str)) slen = RSTRING_LEN(StringValue(str));
770
+ if (argc != 2 || !RTEST(rb_range_beg_len(n1, &start, &length, slen, 0))) {
771
+ if (NIL_P(n1) || ((start = NUM2LONG(n1)) < 0 ? (start += slen) >= 0 : start < slen)) {
772
+ if (NIL_P(n2)) {
773
+ length = -1;
774
+ }
775
+ else if ((length = NUM2LONG(n2)) >= slen - start) {
776
+ length = slen - start;
777
+ }
778
+ }
779
+ }
780
+
781
+ return iconv_convert(VALUE2ICONV(cd), str, start, length, NULL);
782
+ }
783
+
784
+ /*
785
+ * Document-class: Iconv::Failure
786
+ *
787
+ * Base attributes for Iconv exceptions.
788
+ */
789
+
790
+ /*
791
+ * Document-method: success
792
+ * call-seq: success
793
+ *
794
+ * Returns string(s) translated successfully until the exception occurred.
795
+ * * In the case of failure occurred within Iconv.iconv, returned
796
+ * value is an array of strings translated successfully preceding
797
+ * failure and the last element is string on the way.
798
+ */
799
+ static VALUE
800
+ iconv_failure_success
801
+ #ifdef HAVE_PROTOTYPES
802
+ (VALUE self)
803
+ #else /* HAVE_PROTOTYPES */
804
+ (self)
805
+ VALUE self;
806
+ #endif /* HAVE_PROTOTYPES */
807
+ {
808
+ return rb_attr_get(self, rb_success);
809
+ }
810
+
811
+ /*
812
+ * Document-method: failed
813
+ * call-seq: failed
814
+ *
815
+ * Returns substring of the original string passed to Iconv that starts at the
816
+ * character caused the exception.
817
+ */
818
+ static VALUE
819
+ iconv_failure_failed
820
+ #ifdef HAVE_PROTOTYPES
821
+ (VALUE self)
822
+ #else /* HAVE_PROTOTYPES */
823
+ (self)
824
+ VALUE self;
825
+ #endif /* HAVE_PROTOTYPES */
826
+ {
827
+ return rb_attr_get(self, rb_failed);
828
+ }
829
+
830
+ /*
831
+ * Document-method: inspect
832
+ * call-seq: inspect
833
+ *
834
+ * Returns inspected string like as: #<_class_: _success_, _failed_>
835
+ */
836
+ static VALUE
837
+ iconv_failure_inspect
838
+ #ifdef HAVE_PROTOTYPES
839
+ (VALUE self)
840
+ #else /* HAVE_PROTOTYPES */
841
+ (self)
842
+ VALUE self;
843
+ #endif /* HAVE_PROTOTYPES */
844
+ {
845
+ const char *cname = rb_class2name(CLASS_OF(self));
846
+ VALUE success = rb_attr_get(self, rb_success);
847
+ VALUE failed = rb_attr_get(self, rb_failed);
848
+ VALUE str = rb_str_buf_cat2(rb_str_new2("#<"), cname);
849
+ str = rb_str_buf_cat(str, ": ", 2);
850
+ str = rb_str_buf_append(str, rb_inspect(success));
851
+ str = rb_str_buf_cat(str, ", ", 2);
852
+ str = rb_str_buf_append(str, rb_inspect(failed));
853
+ return rb_str_buf_cat(str, ">", 1);
854
+ }
855
+
856
+ /*
857
+ * Document-class: Iconv::InvalidEncoding
858
+ *
859
+ * Requested coding-system is not available on this system.
860
+ */
861
+
862
+ /*
863
+ * Document-class: Iconv::IllegalSequence
864
+ *
865
+ * Input conversion stopped due to an input byte that does not belong to
866
+ * the input codeset, or the output codeset does not contain the
867
+ * character.
868
+ */
869
+
870
+ /*
871
+ * Document-class: Iconv::InvalidCharacter
872
+ *
873
+ * Input conversion stopped due to an incomplete character or shift
874
+ * sequence at the end of the input buffer.
875
+ */
876
+
877
+ /*
878
+ * Document-class: Iconv::OutOfRange
879
+ *
880
+ * Iconv library internal error. Must not occur.
881
+ */
882
+
883
+ /*
884
+ * Document-class: Iconv::BrokenLibrary
885
+ *
886
+ * Detected a bug of underlying iconv(3) libray.
887
+ * * returns an error without setting errno properly
888
+ */
889
+
890
+ void
891
+ Init_iconv _((void))
892
+ {
893
+ VALUE rb_cIconv = rb_define_class("Iconv", rb_cData);
894
+
895
+ rb_define_alloc_func(rb_cIconv, iconv_s_allocate);
896
+ rb_define_singleton_method(rb_cIconv, "open", iconv_s_open, 2);
897
+ rb_define_singleton_method(rb_cIconv, "iconv", iconv_s_iconv, -1);
898
+ rb_define_singleton_method(rb_cIconv, "conv", iconv_s_conv, 3);
899
+ rb_define_method(rb_cIconv, "initialize", iconv_initialize, 2);
900
+ rb_define_method(rb_cIconv, "close", iconv_finish, 0);
901
+ rb_define_method(rb_cIconv, "iconv", iconv_iconv, -1);
902
+
903
+ rb_eIconvFailure = rb_define_module_under(rb_cIconv, "Failure");
904
+ rb_define_method(rb_eIconvFailure, "initialize", iconv_failure_initialize, 3);
905
+ rb_define_method(rb_eIconvFailure, "success", iconv_failure_success, 0);
906
+ rb_define_method(rb_eIconvFailure, "failed", iconv_failure_failed, 0);
907
+ rb_define_method(rb_eIconvFailure, "inspect", iconv_failure_inspect, 0);
908
+
909
+ rb_eIconvInvalidEncoding = rb_define_class_under(rb_cIconv, "InvalidEncoding", rb_eArgError);
910
+ rb_eIconvIllegalSeq = rb_define_class_under(rb_cIconv, "IllegalSequence", rb_eArgError);
911
+ rb_eIconvInvalidChar = rb_define_class_under(rb_cIconv, "InvalidCharacter", rb_eArgError);
912
+ rb_eIconvOutOfRange = rb_define_class_under(rb_cIconv, "OutOfRange", rb_eRuntimeError);
913
+ rb_eIconvBrokenLibrary = rb_define_class_under(rb_cIconv, "BrokenLibrary", rb_eRuntimeError);
914
+ rb_include_module(rb_eIconvInvalidEncoding, rb_eIconvFailure);
915
+ rb_include_module(rb_eIconvIllegalSeq, rb_eIconvFailure);
916
+ rb_include_module(rb_eIconvInvalidChar, rb_eIconvFailure);
917
+ rb_include_module(rb_eIconvOutOfRange, rb_eIconvFailure);
918
+ rb_include_module(rb_eIconvBrokenLibrary, rb_eIconvFailure);
919
+
920
+ rb_success = rb_intern("success");
921
+ rb_failed = rb_intern("failed");
922
+
923
+ rb_gc_register_address(&charset_map);
924
+ charset_map = rb_hash_new();
925
+ rb_define_singleton_method(rb_cIconv, "charset_map", charset_map_get, 0);
926
+ }
927
+