rubysl-iconv 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+