transcode.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   transcode.c -
00004 
00005   $Author: naruse $
00006   created at: Tue Oct 30 16:10:22 JST 2007
00007 
00008   Copyright (C) 2007 Martin Duerst
00009 
00010 **********************************************************************/
00011 
00012 #include "ruby/ruby.h"
00013 #include "ruby/encoding.h"
00014 #include "internal.h"
00015 #include "transcode_data.h"
00016 #include <ctype.h>
00017 
00018 #define ENABLE_ECONV_NEWLINE_OPTION 1
00019 
00020 /* VALUE rb_cEncoding = rb_define_class("Encoding", rb_cObject); */
00021 VALUE rb_eUndefinedConversionError;
00022 VALUE rb_eInvalidByteSequenceError;
00023 VALUE rb_eConverterNotFoundError;
00024 
00025 VALUE rb_cEncodingConverter;
00026 
00027 static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback, sym_aref;
00028 static VALUE sym_xml, sym_text, sym_attr;
00029 static VALUE sym_universal_newline;
00030 static VALUE sym_crlf_newline;
00031 static VALUE sym_cr_newline;
00032 #ifdef ENABLE_ECONV_NEWLINE_OPTION
00033 static VALUE sym_newline, sym_universal, sym_crlf, sym_cr, sym_lf;
00034 #endif
00035 static VALUE sym_partial_input;
00036 
00037 static VALUE sym_invalid_byte_sequence;
00038 static VALUE sym_undefined_conversion;
00039 static VALUE sym_destination_buffer_full;
00040 static VALUE sym_source_buffer_empty;
00041 static VALUE sym_finished;
00042 static VALUE sym_after_output;
00043 static VALUE sym_incomplete_input;
00044 
00045 static unsigned char *
00046 allocate_converted_string(const char *sname, const char *dname,
00047         const unsigned char *str, size_t len,
00048         unsigned char *caller_dst_buf, size_t caller_dst_bufsize,
00049         size_t *dst_len_ptr);
00050 
00051 /* dynamic structure, one per conversion (similar to iconv_t) */
00052 /* may carry conversion state (e.g. for iso-2022-jp) */
00053 typedef struct rb_transcoding {
00054     const rb_transcoder *transcoder;
00055 
00056     int flags;
00057 
00058     int resume_position;
00059     unsigned int next_table;
00060     VALUE next_info;
00061     unsigned char next_byte;
00062     unsigned int output_index;
00063 
00064     ssize_t recognized_len; /* already interpreted */
00065     ssize_t readagain_len; /* not yet interpreted */
00066     union {
00067         unsigned char ary[8]; /* max_input <= sizeof(ary) */
00068         unsigned char *ptr; /* length: max_input */
00069     } readbuf; /* recognized_len + readagain_len used */
00070 
00071     ssize_t writebuf_off;
00072     ssize_t writebuf_len;
00073     union {
00074         unsigned char ary[8]; /* max_output <= sizeof(ary) */
00075         unsigned char *ptr; /* length: max_output */
00076     } writebuf;
00077 
00078     union rb_transcoding_state_t { /* opaque data for stateful encoding */
00079         void *ptr;
00080         char ary[sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*)];
00081         double dummy_for_alignment;
00082     } state;
00083 } rb_transcoding;
00084 #define TRANSCODING_READBUF(tc) \
00085     ((tc)->transcoder->max_input <= (int)sizeof((tc)->readbuf.ary) ? \
00086      (tc)->readbuf.ary : \
00087      (tc)->readbuf.ptr)
00088 #define TRANSCODING_WRITEBUF(tc) \
00089     ((tc)->transcoder->max_output <= (int)sizeof((tc)->writebuf.ary) ? \
00090      (tc)->writebuf.ary : \
00091      (tc)->writebuf.ptr)
00092 #define TRANSCODING_WRITEBUF_SIZE(tc) \
00093     ((tc)->transcoder->max_output <= (int)sizeof((tc)->writebuf.ary) ? \
00094      sizeof((tc)->writebuf.ary) : \
00095      (size_t)(tc)->transcoder->max_output)
00096 #define TRANSCODING_STATE_EMBED_MAX ((int)sizeof(union rb_transcoding_state_t))
00097 #define TRANSCODING_STATE(tc) \
00098     ((tc)->transcoder->state_size <= (int)sizeof((tc)->state) ? \
00099      (tc)->state.ary : \
00100      (tc)->state.ptr)
00101 
00102 typedef struct {
00103     struct rb_transcoding *tc;
00104     unsigned char *out_buf_start;
00105     unsigned char *out_data_start;
00106     unsigned char *out_data_end;
00107     unsigned char *out_buf_end;
00108     rb_econv_result_t last_result;
00109 } rb_econv_elem_t;
00110 
00111 struct rb_econv_t {
00112     int flags;
00113     const char *source_encoding_name;
00114     const char *destination_encoding_name;
00115 
00116     int started;
00117 
00118     const unsigned char *replacement_str;
00119     size_t replacement_len;
00120     const char *replacement_enc;
00121     int replacement_allocated;
00122 
00123     unsigned char *in_buf_start;
00124     unsigned char *in_data_start;
00125     unsigned char *in_data_end;
00126     unsigned char *in_buf_end;
00127     rb_econv_elem_t *elems;
00128     int num_allocated;
00129     int num_trans;
00130     int num_finished;
00131     struct rb_transcoding *last_tc;
00132 
00133     /* last error */
00134     struct {
00135         rb_econv_result_t result;
00136         struct rb_transcoding *error_tc;
00137         const char *source_encoding;
00138         const char *destination_encoding;
00139         const unsigned char *error_bytes_start;
00140         size_t error_bytes_len;
00141         size_t readagain_len;
00142     } last_error;
00143 
00144     /* The following fields are only for Encoding::Converter.
00145      * rb_econv_open set them NULL. */
00146     rb_encoding *source_encoding;
00147     rb_encoding *destination_encoding;
00148 };
00149 
00150 /*
00151  *  Dispatch data and logic
00152  */
00153 
00154 #define DECORATOR_P(sname, dname) (*(sname) == '\0')
00155 
00156 typedef struct {
00157     const char *sname;
00158     const char *dname;
00159     const char *lib; /* null means means no need to load a library */
00160     const rb_transcoder *transcoder;
00161 } transcoder_entry_t;
00162 
00163 static st_table *transcoder_table;
00164 
00165 static transcoder_entry_t *
00166 make_transcoder_entry(const char *sname, const char *dname)
00167 {
00168     st_data_t val;
00169     st_table *table2;
00170 
00171     if (!st_lookup(transcoder_table, (st_data_t)sname, &val)) {
00172         val = (st_data_t)st_init_strcasetable();
00173         st_add_direct(transcoder_table, (st_data_t)sname, val);
00174     }
00175     table2 = (st_table *)val;
00176     if (!st_lookup(table2, (st_data_t)dname, &val)) {
00177         transcoder_entry_t *entry = ALLOC(transcoder_entry_t);
00178         entry->sname = sname;
00179         entry->dname = dname;
00180         entry->lib = NULL;
00181         entry->transcoder = NULL;
00182         val = (st_data_t)entry;
00183         st_add_direct(table2, (st_data_t)dname, val);
00184     }
00185     return (transcoder_entry_t *)val;
00186 }
00187 
00188 static transcoder_entry_t *
00189 get_transcoder_entry(const char *sname, const char *dname)
00190 {
00191     st_data_t val;
00192     st_table *table2;
00193 
00194     if (!st_lookup(transcoder_table, (st_data_t)sname, &val)) {
00195         return NULL;
00196     }
00197     table2 = (st_table *)val;
00198     if (!st_lookup(table2, (st_data_t)dname, &val)) {
00199         return NULL;
00200     }
00201     return (transcoder_entry_t *)val;
00202 }
00203 
00204 void
00205 rb_register_transcoder(const rb_transcoder *tr)
00206 {
00207     const char *const sname = tr->src_encoding;
00208     const char *const dname = tr->dst_encoding;
00209 
00210     transcoder_entry_t *entry;
00211 
00212     entry = make_transcoder_entry(sname, dname);
00213     if (entry->transcoder) {
00214         rb_raise(rb_eArgError, "transcoder from %s to %s has been already registered",
00215                  sname, dname);
00216     }
00217 
00218     entry->transcoder = tr;
00219 }
00220 
00221 static void
00222 declare_transcoder(const char *sname, const char *dname, const char *lib)
00223 {
00224     transcoder_entry_t *entry;
00225 
00226     entry = make_transcoder_entry(sname, dname);
00227     entry->lib = lib;
00228 }
00229 
00230 static const char transcoder_lib_prefix[] = "enc/trans/";
00231 
00232 void
00233 rb_declare_transcoder(const char *enc1, const char *enc2, const char *lib)
00234 {
00235     if (!lib) {
00236         rb_raise(rb_eArgError, "invalid library name - (null)");
00237     }
00238     declare_transcoder(enc1, enc2, lib);
00239 }
00240 
00241 #define encoding_equal(enc1, enc2) (STRCASECMP((enc1), (enc2)) == 0)
00242 
00243 typedef struct search_path_queue_tag {
00244     struct search_path_queue_tag *next;
00245     const char *enc;
00246 } search_path_queue_t;
00247 
00248 typedef struct {
00249     st_table *visited;
00250     search_path_queue_t *queue;
00251     search_path_queue_t **queue_last_ptr;
00252     const char *base_enc;
00253 } search_path_bfs_t;
00254 
00255 static int
00256 transcode_search_path_i(st_data_t key, st_data_t val, st_data_t arg)
00257 {
00258     const char *dname = (const char *)key;
00259     search_path_bfs_t *bfs = (search_path_bfs_t *)arg;
00260     search_path_queue_t *q;
00261 
00262     if (st_lookup(bfs->visited, (st_data_t)dname, &val)) {
00263         return ST_CONTINUE;
00264     }
00265 
00266     q = ALLOC(search_path_queue_t);
00267     q->enc = dname;
00268     q->next = NULL;
00269     *bfs->queue_last_ptr = q;
00270     bfs->queue_last_ptr = &q->next;
00271 
00272     st_add_direct(bfs->visited, (st_data_t)dname, (st_data_t)bfs->base_enc);
00273     return ST_CONTINUE;
00274 }
00275 
00276 static int
00277 transcode_search_path(const char *sname, const char *dname,
00278     void (*callback)(const char *sname, const char *dname, int depth, void *arg),
00279     void *arg)
00280 {
00281     search_path_bfs_t bfs;
00282     search_path_queue_t *q;
00283     st_data_t val;
00284     st_table *table2;
00285     int found;
00286     int pathlen = -1;
00287 
00288     if (encoding_equal(sname, dname))
00289         return -1;
00290 
00291     q = ALLOC(search_path_queue_t);
00292     q->enc = sname;
00293     q->next = NULL;
00294     bfs.queue_last_ptr = &q->next;
00295     bfs.queue = q;
00296 
00297     bfs.visited = st_init_strcasetable();
00298     st_add_direct(bfs.visited, (st_data_t)sname, (st_data_t)NULL);
00299 
00300     while (bfs.queue) {
00301         q = bfs.queue;
00302         bfs.queue = q->next;
00303         if (!bfs.queue)
00304             bfs.queue_last_ptr = &bfs.queue;
00305 
00306         if (!st_lookup(transcoder_table, (st_data_t)q->enc, &val)) {
00307             xfree(q);
00308             continue;
00309         }
00310         table2 = (st_table *)val;
00311 
00312         if (st_lookup(table2, (st_data_t)dname, &val)) {
00313             st_add_direct(bfs.visited, (st_data_t)dname, (st_data_t)q->enc);
00314             xfree(q);
00315             found = 1;
00316             goto cleanup;
00317         }
00318 
00319         bfs.base_enc = q->enc;
00320         st_foreach(table2, transcode_search_path_i, (st_data_t)&bfs);
00321         bfs.base_enc = NULL;
00322 
00323         xfree(q);
00324     }
00325     found = 0;
00326 
00327   cleanup:
00328     while (bfs.queue) {
00329         q = bfs.queue;
00330         bfs.queue = q->next;
00331         xfree(q);
00332     }
00333 
00334     if (found) {
00335         const char *enc = dname;
00336         int depth;
00337         pathlen = 0;
00338         while (1) {
00339             st_lookup(bfs.visited, (st_data_t)enc, &val);
00340             if (!val)
00341                 break;
00342             pathlen++;
00343             enc = (const char *)val;
00344         }
00345         depth = pathlen;
00346         enc = dname;
00347         while (1) {
00348             st_lookup(bfs.visited, (st_data_t)enc, &val);
00349             if (!val)
00350                 break;
00351             callback((const char *)val, enc, --depth, arg);
00352             enc = (const char *)val;
00353         }
00354     }
00355 
00356     st_free_table(bfs.visited);
00357 
00358     return pathlen; /* is -1 if not found */
00359 }
00360 
00361 static const rb_transcoder *
00362 load_transcoder_entry(transcoder_entry_t *entry)
00363 {
00364     if (entry->transcoder)
00365         return entry->transcoder;
00366 
00367     if (entry->lib) {
00368         const char *const lib = entry->lib;
00369         const size_t len = strlen(lib);
00370         const size_t total_len = sizeof(transcoder_lib_prefix) - 1 + len;
00371         const VALUE fn = rb_str_new(0, total_len);
00372         char *const path = RSTRING_PTR(fn);
00373         const int safe = rb_safe_level();
00374 
00375         entry->lib = NULL;
00376 
00377         memcpy(path, transcoder_lib_prefix, sizeof(transcoder_lib_prefix) - 1);
00378         memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len);
00379         rb_str_set_len(fn, total_len);
00380         FL_UNSET(fn, FL_TAINT);
00381         OBJ_FREEZE(fn);
00382         if (!rb_require_safe(fn, safe > 3 ? 3 : safe))
00383             return NULL;
00384     }
00385 
00386     if (entry->transcoder)
00387         return entry->transcoder;
00388 
00389     return NULL;
00390 }
00391 
00392 static const char*
00393 get_replacement_character(const char *encname, size_t *len_ret, const char **repl_encname_ptr)
00394 {
00395     if (encoding_equal(encname, "UTF-8")) {
00396         *len_ret = 3;
00397         *repl_encname_ptr = "UTF-8";
00398         return "\xEF\xBF\xBD";
00399     }
00400     else {
00401         *len_ret = 1;
00402         *repl_encname_ptr = "US-ASCII";
00403         return "?";
00404     }
00405 }
00406 
00407 /*
00408  *  Transcoding engine logic
00409  */
00410 
00411 static const unsigned char *
00412 transcode_char_start(rb_transcoding *tc,
00413                          const unsigned char *in_start,
00414                          const unsigned char *inchar_start,
00415                          const unsigned char *in_p,
00416                          size_t *char_len_ptr)
00417 {
00418     const unsigned char *ptr;
00419     if (inchar_start - in_start < tc->recognized_len) {
00420         MEMCPY(TRANSCODING_READBUF(tc) + tc->recognized_len,
00421                inchar_start, unsigned char, in_p - inchar_start);
00422         ptr = TRANSCODING_READBUF(tc);
00423     }
00424     else {
00425         ptr = inchar_start - tc->recognized_len;
00426     }
00427     *char_len_ptr = tc->recognized_len + (in_p - inchar_start);
00428     return ptr;
00429 }
00430 
00431 static rb_econv_result_t
00432 transcode_restartable0(const unsigned char **in_pos, unsigned char **out_pos,
00433                       const unsigned char *in_stop, unsigned char *out_stop,
00434                       rb_transcoding *tc,
00435                       const int opt)
00436 {
00437     const rb_transcoder *tr = tc->transcoder;
00438     int unitlen = tr->input_unit_length;
00439     ssize_t readagain_len = 0;
00440 
00441     const unsigned char *inchar_start;
00442     const unsigned char *in_p;
00443 
00444     unsigned char *out_p;
00445 
00446     in_p = inchar_start = *in_pos;
00447 
00448     out_p = *out_pos;
00449 
00450 #define SUSPEND(ret, num) \
00451     do { \
00452         tc->resume_position = (num); \
00453         if (0 < in_p - inchar_start) \
00454             MEMMOVE(TRANSCODING_READBUF(tc)+tc->recognized_len, \
00455                    inchar_start, unsigned char, in_p - inchar_start); \
00456         *in_pos = in_p; \
00457         *out_pos = out_p; \
00458         tc->recognized_len += in_p - inchar_start; \
00459         if (readagain_len) { \
00460             tc->recognized_len -= readagain_len; \
00461             tc->readagain_len = readagain_len; \
00462         } \
00463         return (ret); \
00464         resume_label ## num:; \
00465     } while (0)
00466 #define SUSPEND_OBUF(num) \
00467     do { \
00468         while (out_stop - out_p < 1) { SUSPEND(econv_destination_buffer_full, num); } \
00469     } while (0)
00470 
00471 #define SUSPEND_AFTER_OUTPUT(num) \
00472     if ((opt & ECONV_AFTER_OUTPUT) && *out_pos != out_p) { \
00473         SUSPEND(econv_after_output, num); \
00474     }
00475 
00476 #define next_table (tc->next_table)
00477 #define next_info (tc->next_info)
00478 #define next_byte (tc->next_byte)
00479 #define writebuf_len (tc->writebuf_len)
00480 #define writebuf_off (tc->writebuf_off)
00481 
00482     switch (tc->resume_position) {
00483       case 0: break;
00484       case 1: goto resume_label1;
00485       case 2: goto resume_label2;
00486       case 3: goto resume_label3;
00487       case 4: goto resume_label4;
00488       case 5: goto resume_label5;
00489       case 6: goto resume_label6;
00490       case 7: goto resume_label7;
00491       case 8: goto resume_label8;
00492       case 9: goto resume_label9;
00493       case 10: goto resume_label10;
00494       case 11: goto resume_label11;
00495       case 12: goto resume_label12;
00496       case 13: goto resume_label13;
00497       case 14: goto resume_label14;
00498       case 15: goto resume_label15;
00499       case 16: goto resume_label16;
00500       case 17: goto resume_label17;
00501       case 18: goto resume_label18;
00502       case 19: goto resume_label19;
00503       case 20: goto resume_label20;
00504       case 21: goto resume_label21;
00505       case 22: goto resume_label22;
00506       case 23: goto resume_label23;
00507       case 24: goto resume_label24;
00508       case 25: goto resume_label25;
00509       case 26: goto resume_label26;
00510       case 27: goto resume_label27;
00511       case 28: goto resume_label28;
00512       case 29: goto resume_label29;
00513       case 30: goto resume_label30;
00514       case 31: goto resume_label31;
00515       case 32: goto resume_label32;
00516       case 33: goto resume_label33;
00517       case 34: goto resume_label34;
00518     }
00519 
00520     while (1) {
00521         inchar_start = in_p;
00522         tc->recognized_len = 0;
00523         next_table = tr->conv_tree_start;
00524 
00525         SUSPEND_AFTER_OUTPUT(24);
00526 
00527         if (in_stop <= in_p) {
00528             if (!(opt & ECONV_PARTIAL_INPUT))
00529                 break;
00530             SUSPEND(econv_source_buffer_empty, 7);
00531             continue;
00532         }
00533 
00534 #define BYTE_ADDR(index) (tr->byte_array + (index))
00535 #define WORD_ADDR(index) (tr->word_array + INFO2WORDINDEX(index))
00536 #define BL_BASE BYTE_ADDR(BYTE_LOOKUP_BASE(WORD_ADDR(next_table)))
00537 #define BL_INFO WORD_ADDR(BYTE_LOOKUP_INFO(WORD_ADDR(next_table)))
00538 #define BL_MIN_BYTE     (BL_BASE[0])
00539 #define BL_MAX_BYTE     (BL_BASE[1])
00540 #define BL_OFFSET(byte) (BL_BASE[2+(byte)-BL_MIN_BYTE])
00541 #define BL_ACTION(byte) (BL_INFO[BL_OFFSET((byte))])
00542 
00543         next_byte = (unsigned char)*in_p++;
00544       follow_byte:
00545         if (next_byte < BL_MIN_BYTE || BL_MAX_BYTE < next_byte)
00546             next_info = INVALID;
00547         else {
00548             next_info = (VALUE)BL_ACTION(next_byte);
00549         }
00550       follow_info:
00551         switch (next_info & 0x1F) {
00552           case NOMAP:
00553             {
00554                 const unsigned char *p = inchar_start;
00555                 writebuf_off = 0;
00556                 while (p < in_p) {
00557                     TRANSCODING_WRITEBUF(tc)[writebuf_off++] = (unsigned char)*p++;
00558                 }
00559                 writebuf_len = writebuf_off;
00560                 writebuf_off = 0;
00561                 while (writebuf_off < writebuf_len) {
00562                     SUSPEND_OBUF(3);
00563                     *out_p++ = TRANSCODING_WRITEBUF(tc)[writebuf_off++];
00564                 }
00565             }
00566             continue;
00567           case 0x00: case 0x04: case 0x08: case 0x0C:
00568           case 0x10: case 0x14: case 0x18: case 0x1C:
00569             SUSPEND_AFTER_OUTPUT(25);
00570             while (in_p >= in_stop) {
00571                 if (!(opt & ECONV_PARTIAL_INPUT))
00572                     goto incomplete;
00573                 SUSPEND(econv_source_buffer_empty, 5);
00574             }
00575             next_byte = (unsigned char)*in_p++;
00576             next_table = (unsigned int)next_info;
00577             goto follow_byte;
00578           case ZERObt: /* drop input */
00579             continue;
00580           case ONEbt:
00581             SUSPEND_OBUF(9); *out_p++ = getBT1(next_info);
00582             continue;
00583           case TWObt:
00584             SUSPEND_OBUF(10); *out_p++ = getBT1(next_info);
00585             SUSPEND_OBUF(21); *out_p++ = getBT2(next_info);
00586             continue;
00587           case THREEbt:
00588             SUSPEND_OBUF(11); *out_p++ = getBT1(next_info);
00589             SUSPEND_OBUF(15); *out_p++ = getBT2(next_info);
00590             SUSPEND_OBUF(16); *out_p++ = getBT3(next_info);
00591             continue;
00592           case FOURbt:
00593             SUSPEND_OBUF(12); *out_p++ = getBT0(next_info);
00594             SUSPEND_OBUF(17); *out_p++ = getBT1(next_info);
00595             SUSPEND_OBUF(18); *out_p++ = getBT2(next_info);
00596             SUSPEND_OBUF(19); *out_p++ = getBT3(next_info);
00597             continue;
00598           case GB4bt:
00599             SUSPEND_OBUF(29); *out_p++ = getGB4bt0(next_info);
00600             SUSPEND_OBUF(30); *out_p++ = getGB4bt1(next_info);
00601             SUSPEND_OBUF(31); *out_p++ = getGB4bt2(next_info);
00602             SUSPEND_OBUF(32); *out_p++ = getGB4bt3(next_info);
00603             continue;
00604           case STR1:
00605             tc->output_index = 0;
00606             while (tc->output_index < STR1_LENGTH(BYTE_ADDR(STR1_BYTEINDEX(next_info)))) {
00607                 SUSPEND_OBUF(28); *out_p++ = BYTE_ADDR(STR1_BYTEINDEX(next_info))[1+tc->output_index];
00608                 tc->output_index++;
00609             }
00610             continue;
00611           case FUNii:
00612             next_info = (VALUE)(*tr->func_ii)(TRANSCODING_STATE(tc), next_info);
00613             goto follow_info;
00614           case FUNsi:
00615             {
00616                 const unsigned char *char_start;
00617                 size_t char_len;
00618                 char_start = transcode_char_start(tc, *in_pos, inchar_start, in_p, &char_len);
00619                 next_info = (VALUE)(*tr->func_si)(TRANSCODING_STATE(tc), char_start, (size_t)char_len);
00620                 goto follow_info;
00621             }
00622           case FUNio:
00623             SUSPEND_OBUF(13);
00624             if (tr->max_output <= out_stop - out_p)
00625                 out_p += tr->func_io(TRANSCODING_STATE(tc),
00626                     next_info, out_p, out_stop - out_p);
00627             else {
00628                 writebuf_len = tr->func_io(TRANSCODING_STATE(tc),
00629                     next_info,
00630                     TRANSCODING_WRITEBUF(tc), TRANSCODING_WRITEBUF_SIZE(tc));
00631                 writebuf_off = 0;
00632                 while (writebuf_off < writebuf_len) {
00633                     SUSPEND_OBUF(20);
00634                     *out_p++ = TRANSCODING_WRITEBUF(tc)[writebuf_off++];
00635                 }
00636             }
00637             break;
00638           case FUNso:
00639             {
00640                 const unsigned char *char_start;
00641                 size_t char_len;
00642                 SUSPEND_OBUF(14);
00643                 if (tr->max_output <= out_stop - out_p) {
00644                     char_start = transcode_char_start(tc, *in_pos, inchar_start, in_p, &char_len);
00645                     out_p += tr->func_so(TRANSCODING_STATE(tc),
00646                         char_start, (size_t)char_len,
00647                         out_p, out_stop - out_p);
00648                 }
00649                 else {
00650                     char_start = transcode_char_start(tc, *in_pos, inchar_start, in_p, &char_len);
00651                     writebuf_len = tr->func_so(TRANSCODING_STATE(tc),
00652                         char_start, (size_t)char_len,
00653                         TRANSCODING_WRITEBUF(tc), TRANSCODING_WRITEBUF_SIZE(tc));
00654                     writebuf_off = 0;
00655                     while (writebuf_off < writebuf_len) {
00656                         SUSPEND_OBUF(22);
00657                         *out_p++ = TRANSCODING_WRITEBUF(tc)[writebuf_off++];
00658                     }
00659                 }
00660                 break;
00661             }
00662       case FUNsio:
00663             {
00664                 const unsigned char *char_start;
00665                 size_t char_len;
00666                 SUSPEND_OBUF(33);
00667                 if (tr->max_output <= out_stop - out_p) {
00668                     char_start = transcode_char_start(tc, *in_pos, inchar_start, in_p, &char_len);
00669                     out_p += tr->func_sio(TRANSCODING_STATE(tc),
00670                         char_start, (size_t)char_len, next_info,
00671                         out_p, out_stop - out_p);
00672                 }
00673                 else {
00674                     char_start = transcode_char_start(tc, *in_pos, inchar_start, in_p, &char_len);
00675                     writebuf_len = tr->func_sio(TRANSCODING_STATE(tc),
00676                         char_start, (size_t)char_len, next_info,
00677                         TRANSCODING_WRITEBUF(tc), TRANSCODING_WRITEBUF_SIZE(tc));
00678                     writebuf_off = 0;
00679                     while (writebuf_off < writebuf_len) {
00680                         SUSPEND_OBUF(34);
00681                         *out_p++ = TRANSCODING_WRITEBUF(tc)[writebuf_off++];
00682                     }
00683                 }
00684                 break;
00685             }
00686           case INVALID:
00687             if (tc->recognized_len + (in_p - inchar_start) <= unitlen) {
00688                 if (tc->recognized_len + (in_p - inchar_start) < unitlen)
00689                     SUSPEND_AFTER_OUTPUT(26);
00690                 while ((opt & ECONV_PARTIAL_INPUT) && tc->recognized_len + (in_stop - inchar_start) < unitlen) {
00691                     in_p = in_stop;
00692                     SUSPEND(econv_source_buffer_empty, 8);
00693                 }
00694                 if (tc->recognized_len + (in_stop - inchar_start) <= unitlen) {
00695                     in_p = in_stop;
00696                 }
00697                 else {
00698                     in_p = inchar_start + (unitlen - tc->recognized_len);
00699                 }
00700             }
00701             else {
00702                 ssize_t invalid_len; /* including the last byte which causes invalid */
00703                 ssize_t discard_len;
00704                 invalid_len = tc->recognized_len + (in_p - inchar_start);
00705                 discard_len = ((invalid_len - 1) / unitlen) * unitlen;
00706                 readagain_len = invalid_len - discard_len;
00707             }
00708             goto invalid;
00709           case UNDEF:
00710             goto undef;
00711           default:
00712             rb_raise(rb_eRuntimeError, "unknown transcoding instruction");
00713         }
00714         continue;
00715 
00716       invalid:
00717         SUSPEND(econv_invalid_byte_sequence, 1);
00718         continue;
00719 
00720       incomplete:
00721         SUSPEND(econv_incomplete_input, 27);
00722         continue;
00723 
00724       undef:
00725         SUSPEND(econv_undefined_conversion, 2);
00726         continue;
00727     }
00728 
00729     /* cleanup */
00730     if (tr->finish_func) {
00731         SUSPEND_OBUF(4);
00732         if (tr->max_output <= out_stop - out_p) {
00733             out_p += tr->finish_func(TRANSCODING_STATE(tc),
00734                 out_p, out_stop - out_p);
00735         }
00736         else {
00737             writebuf_len = tr->finish_func(TRANSCODING_STATE(tc),
00738                 TRANSCODING_WRITEBUF(tc), TRANSCODING_WRITEBUF_SIZE(tc));
00739             writebuf_off = 0;
00740             while (writebuf_off < writebuf_len) {
00741                 SUSPEND_OBUF(23);
00742                 *out_p++ = TRANSCODING_WRITEBUF(tc)[writebuf_off++];
00743             }
00744         }
00745     }
00746     while (1)
00747         SUSPEND(econv_finished, 6);
00748 #undef SUSPEND
00749 #undef next_table
00750 #undef next_info
00751 #undef next_byte
00752 #undef writebuf_len
00753 #undef writebuf_off
00754 }
00755 
00756 static rb_econv_result_t
00757 transcode_restartable(const unsigned char **in_pos, unsigned char **out_pos,
00758                       const unsigned char *in_stop, unsigned char *out_stop,
00759                       rb_transcoding *tc,
00760                       const int opt)
00761 {
00762     if (tc->readagain_len) {
00763         unsigned char *readagain_buf = ALLOCA_N(unsigned char, tc->readagain_len);
00764         const unsigned char *readagain_pos = readagain_buf;
00765         const unsigned char *readagain_stop = readagain_buf + tc->readagain_len;
00766         rb_econv_result_t res;
00767 
00768         MEMCPY(readagain_buf, TRANSCODING_READBUF(tc) + tc->recognized_len,
00769                unsigned char, tc->readagain_len);
00770         tc->readagain_len = 0;
00771         res = transcode_restartable0(&readagain_pos, out_pos, readagain_stop, out_stop, tc, opt|ECONV_PARTIAL_INPUT);
00772         if (res != econv_source_buffer_empty) {
00773             MEMCPY(TRANSCODING_READBUF(tc) + tc->recognized_len + tc->readagain_len,
00774                    readagain_pos, unsigned char, readagain_stop - readagain_pos);
00775             tc->readagain_len += readagain_stop - readagain_pos;
00776             return res;
00777         }
00778     }
00779     return transcode_restartable0(in_pos, out_pos, in_stop, out_stop, tc, opt);
00780 }
00781 
00782 static rb_transcoding *
00783 rb_transcoding_open_by_transcoder(const rb_transcoder *tr, int flags)
00784 {
00785     rb_transcoding *tc;
00786 
00787     tc = ALLOC(rb_transcoding);
00788     tc->transcoder = tr;
00789     tc->flags = flags;
00790     if (TRANSCODING_STATE_EMBED_MAX < tr->state_size)
00791         tc->state.ptr = xmalloc(tr->state_size);
00792     if (tr->state_init_func) {
00793         (tr->state_init_func)(TRANSCODING_STATE(tc)); /* xxx: check return value */
00794     }
00795     tc->resume_position = 0;
00796     tc->recognized_len = 0;
00797     tc->readagain_len = 0;
00798     tc->writebuf_len = 0;
00799     tc->writebuf_off = 0;
00800     if ((int)sizeof(tc->readbuf.ary) < tr->max_input) {
00801         tc->readbuf.ptr = xmalloc(tr->max_input);
00802     }
00803     if ((int)sizeof(tc->writebuf.ary) < tr->max_output) {
00804         tc->writebuf.ptr = xmalloc(tr->max_output);
00805     }
00806     return tc;
00807 }
00808 
00809 static rb_econv_result_t
00810 rb_transcoding_convert(rb_transcoding *tc,
00811   const unsigned char **input_ptr, const unsigned char *input_stop,
00812   unsigned char **output_ptr, unsigned char *output_stop,
00813   int flags)
00814 {
00815     return transcode_restartable(
00816                 input_ptr, output_ptr,
00817                 input_stop, output_stop,
00818                 tc, flags);
00819 }
00820 
00821 static void
00822 rb_transcoding_close(rb_transcoding *tc)
00823 {
00824     const rb_transcoder *tr = tc->transcoder;
00825     if (tr->state_fini_func) {
00826         (tr->state_fini_func)(TRANSCODING_STATE(tc)); /* check return value? */
00827     }
00828     if (TRANSCODING_STATE_EMBED_MAX < tr->state_size)
00829         xfree(tc->state.ptr);
00830     if ((int)sizeof(tc->readbuf.ary) < tr->max_input)
00831         xfree(tc->readbuf.ptr);
00832     if ((int)sizeof(tc->writebuf.ary) < tr->max_output)
00833         xfree(tc->writebuf.ptr);
00834     xfree(tc);
00835 }
00836 
00837 static size_t
00838 rb_transcoding_memsize(rb_transcoding *tc)
00839 {
00840     size_t size = sizeof(rb_transcoding);
00841     const rb_transcoder *tr = tc->transcoder;
00842 
00843     if (TRANSCODING_STATE_EMBED_MAX < tr->state_size) {
00844         size += tr->state_size;
00845     }
00846     if ((int)sizeof(tc->readbuf.ary) < tr->max_input) {
00847         size += tr->max_input;
00848     }
00849     if ((int)sizeof(tc->writebuf.ary) < tr->max_output) {
00850         size += tr->max_output;
00851     }
00852     return size;
00853 }
00854 
00855 static rb_econv_t *
00856 rb_econv_alloc(int n_hint)
00857 {
00858     rb_econv_t *ec;
00859 
00860     if (n_hint <= 0)
00861         n_hint = 1;
00862 
00863     ec = ALLOC(rb_econv_t);
00864     ec->flags = 0;
00865     ec->source_encoding_name = NULL;
00866     ec->destination_encoding_name = NULL;
00867     ec->started = 0;
00868     ec->replacement_str = NULL;
00869     ec->replacement_len = 0;
00870     ec->replacement_enc = NULL;
00871     ec->replacement_allocated = 0;
00872     ec->in_buf_start = NULL;
00873     ec->in_data_start = NULL;
00874     ec->in_data_end = NULL;
00875     ec->in_buf_end = NULL;
00876     ec->num_allocated = n_hint;
00877     ec->num_trans = 0;
00878     ec->elems = ALLOC_N(rb_econv_elem_t, ec->num_allocated);
00879     ec->num_finished = 0;
00880     ec->last_tc = NULL;
00881     ec->last_error.result = econv_source_buffer_empty;
00882     ec->last_error.error_tc = NULL;
00883     ec->last_error.source_encoding = NULL;
00884     ec->last_error.destination_encoding = NULL;
00885     ec->last_error.error_bytes_start = NULL;
00886     ec->last_error.error_bytes_len = 0;
00887     ec->last_error.readagain_len = 0;
00888     ec->source_encoding = NULL;
00889     ec->destination_encoding = NULL;
00890     return ec;
00891 }
00892 
00893 static int
00894 rb_econv_add_transcoder_at(rb_econv_t *ec, const rb_transcoder *tr, int i)
00895 {
00896     int n, j;
00897     int bufsize = 4096;
00898     unsigned char *p;
00899 
00900     if (ec->num_trans == ec->num_allocated) {
00901         n = ec->num_allocated * 2;
00902         REALLOC_N(ec->elems, rb_econv_elem_t, n);
00903         ec->num_allocated = n;
00904     }
00905 
00906     p = xmalloc(bufsize);
00907 
00908     MEMMOVE(ec->elems+i+1, ec->elems+i, rb_econv_elem_t, ec->num_trans-i);
00909 
00910     ec->elems[i].tc = rb_transcoding_open_by_transcoder(tr, 0);
00911     ec->elems[i].out_buf_start = p;
00912     ec->elems[i].out_buf_end = p + bufsize;
00913     ec->elems[i].out_data_start = p;
00914     ec->elems[i].out_data_end = p;
00915     ec->elems[i].last_result = econv_source_buffer_empty;
00916 
00917     ec->num_trans++;
00918 
00919     if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding))
00920         for (j = ec->num_trans-1; i <= j; j--) {
00921             rb_transcoding *tc = ec->elems[j].tc;
00922             const rb_transcoder *tr2 = tc->transcoder;
00923             if (!DECORATOR_P(tr2->src_encoding, tr2->dst_encoding)) {
00924                 ec->last_tc = tc;
00925                 break;
00926             }
00927         }
00928 
00929     return 0;
00930 }
00931 
00932 static rb_econv_t *
00933 rb_econv_open_by_transcoder_entries(int n, transcoder_entry_t **entries)
00934 {
00935     rb_econv_t *ec;
00936     int i, ret;
00937 
00938     for (i = 0; i < n; i++) {
00939         const rb_transcoder *tr;
00940         tr = load_transcoder_entry(entries[i]);
00941         if (!tr)
00942             return NULL;
00943     }
00944 
00945     ec = rb_econv_alloc(n);
00946 
00947     for (i = 0; i < n; i++) {
00948         const rb_transcoder *tr = load_transcoder_entry(entries[i]);
00949         ret = rb_econv_add_transcoder_at(ec, tr, ec->num_trans);
00950         if (ret == -1) {
00951             rb_econv_close(ec);
00952             return NULL;
00953         }
00954     }
00955 
00956     return ec;
00957 }
00958 
00959 struct trans_open_t {
00960     transcoder_entry_t **entries;
00961     int num_additional;
00962 };
00963 
00964 static void
00965 trans_open_i(const char *sname, const char *dname, int depth, void *arg)
00966 {
00967     struct trans_open_t *toarg = arg;
00968 
00969     if (!toarg->entries) {
00970         toarg->entries = ALLOC_N(transcoder_entry_t *, depth+1+toarg->num_additional);
00971     }
00972     toarg->entries[depth] = get_transcoder_entry(sname, dname);
00973 }
00974 
00975 static rb_econv_t *
00976 rb_econv_open0(const char *sname, const char *dname, int ecflags)
00977 {
00978     transcoder_entry_t **entries = NULL;
00979     int num_trans;
00980     rb_econv_t *ec;
00981 
00982     int sidx, didx;
00983 
00984     if (*sname) {
00985         sidx = rb_enc_find_index(sname);
00986         if (0 <= sidx) {
00987             rb_enc_from_index(sidx);
00988         }
00989     }
00990 
00991     if (*dname) {
00992         didx = rb_enc_find_index(dname);
00993         if (0 <= didx) {
00994             rb_enc_from_index(didx);
00995         }
00996     }
00997 
00998     if (*sname == '\0' && *dname == '\0') {
00999         num_trans = 0;
01000         entries = NULL;
01001     }
01002     else {
01003         struct trans_open_t toarg;
01004         toarg.entries = NULL;
01005         toarg.num_additional = 0;
01006         num_trans = transcode_search_path(sname, dname, trans_open_i, (void *)&toarg);
01007         entries = toarg.entries;
01008         if (num_trans < 0) {
01009             xfree(entries);
01010             return NULL;
01011         }
01012     }
01013 
01014     ec = rb_econv_open_by_transcoder_entries(num_trans, entries);
01015     xfree(entries);
01016     if (!ec)
01017         return NULL;
01018 
01019     ec->flags = ecflags;
01020     ec->source_encoding_name = sname;
01021     ec->destination_encoding_name = dname;
01022 
01023     return ec;
01024 }
01025 
01026 #define MAX_ECFLAGS_DECORATORS 32
01027 
01028 static int
01029 decorator_names(int ecflags, const char **decorators_ret)
01030 {
01031     int num_decorators;
01032 
01033     switch (ecflags & ECONV_NEWLINE_DECORATOR_MASK) {
01034       case ECONV_UNIVERSAL_NEWLINE_DECORATOR:
01035       case ECONV_CRLF_NEWLINE_DECORATOR:
01036       case ECONV_CR_NEWLINE_DECORATOR:
01037       case 0:
01038         break;
01039       default:
01040         return -1;
01041     }
01042 
01043     if ((ecflags & ECONV_XML_TEXT_DECORATOR) &&
01044         (ecflags & ECONV_XML_ATTR_CONTENT_DECORATOR))
01045         return -1;
01046 
01047     num_decorators = 0;
01048 
01049     if (ecflags & ECONV_XML_TEXT_DECORATOR)
01050         decorators_ret[num_decorators++] = "xml_text_escape";
01051     if (ecflags & ECONV_XML_ATTR_CONTENT_DECORATOR)
01052         decorators_ret[num_decorators++] = "xml_attr_content_escape";
01053     if (ecflags & ECONV_XML_ATTR_QUOTE_DECORATOR)
01054         decorators_ret[num_decorators++] = "xml_attr_quote";
01055 
01056     if (ecflags & ECONV_CRLF_NEWLINE_DECORATOR)
01057         decorators_ret[num_decorators++] = "crlf_newline";
01058     if (ecflags & ECONV_CR_NEWLINE_DECORATOR)
01059         decorators_ret[num_decorators++] = "cr_newline";
01060     if (ecflags & ECONV_UNIVERSAL_NEWLINE_DECORATOR)
01061         decorators_ret[num_decorators++] = "universal_newline";
01062 
01063     return num_decorators;
01064 }
01065 
01066 rb_econv_t *
01067 rb_econv_open(const char *sname, const char *dname, int ecflags)
01068 {
01069     rb_econv_t *ec;
01070     int num_decorators;
01071     const char *decorators[MAX_ECFLAGS_DECORATORS];
01072     int i;
01073 
01074     num_decorators = decorator_names(ecflags, decorators);
01075     if (num_decorators == -1)
01076         return NULL;
01077 
01078     ec = rb_econv_open0(sname, dname, ecflags & ECONV_ERROR_HANDLER_MASK);
01079     if (!ec)
01080         return NULL;
01081 
01082     for (i = 0; i < num_decorators; i++)
01083         if (rb_econv_decorate_at_last(ec, decorators[i]) == -1) {
01084             rb_econv_close(ec);
01085             return NULL;
01086         }
01087 
01088     ec->flags |= ecflags & ~ECONV_ERROR_HANDLER_MASK;
01089 
01090     return ec;
01091 }
01092 
01093 static int
01094 trans_sweep(rb_econv_t *ec,
01095     const unsigned char **input_ptr, const unsigned char *input_stop,
01096     unsigned char **output_ptr, unsigned char *output_stop,
01097     int flags,
01098     int start)
01099 {
01100     int try;
01101     int i, f;
01102 
01103     const unsigned char **ipp, *is, *iold;
01104     unsigned char **opp, *os, *oold;
01105     rb_econv_result_t res;
01106 
01107     try = 1;
01108     while (try) {
01109         try = 0;
01110         for (i = start; i < ec->num_trans; i++) {
01111             rb_econv_elem_t *te = &ec->elems[i];
01112 
01113             if (i == 0) {
01114                 ipp = input_ptr;
01115                 is = input_stop;
01116             }
01117             else {
01118                 rb_econv_elem_t *prev_te = &ec->elems[i-1];
01119                 ipp = (const unsigned char **)&prev_te->out_data_start;
01120                 is = prev_te->out_data_end;
01121             }
01122 
01123             if (i == ec->num_trans-1) {
01124                 opp = output_ptr;
01125                 os = output_stop;
01126             }
01127             else {
01128                 if (te->out_buf_start != te->out_data_start) {
01129                     ssize_t len = te->out_data_end - te->out_data_start;
01130                     ssize_t off = te->out_data_start - te->out_buf_start;
01131                     MEMMOVE(te->out_buf_start, te->out_data_start, unsigned char, len);
01132                     te->out_data_start = te->out_buf_start;
01133                     te->out_data_end -= off;
01134                 }
01135                 opp = &te->out_data_end;
01136                 os = te->out_buf_end;
01137             }
01138 
01139             f = flags;
01140             if (ec->num_finished != i)
01141                 f |= ECONV_PARTIAL_INPUT;
01142             if (i == 0 && (flags & ECONV_AFTER_OUTPUT)) {
01143                 start = 1;
01144                 flags &= ~ECONV_AFTER_OUTPUT;
01145             }
01146             if (i != 0)
01147                 f &= ~ECONV_AFTER_OUTPUT;
01148             iold = *ipp;
01149             oold = *opp;
01150             te->last_result = res = rb_transcoding_convert(te->tc, ipp, is, opp, os, f);
01151             if (iold != *ipp || oold != *opp)
01152                 try = 1;
01153 
01154             switch (res) {
01155               case econv_invalid_byte_sequence:
01156               case econv_incomplete_input:
01157               case econv_undefined_conversion:
01158               case econv_after_output:
01159                 return i;
01160 
01161               case econv_destination_buffer_full:
01162               case econv_source_buffer_empty:
01163                 break;
01164 
01165               case econv_finished:
01166                 ec->num_finished = i+1;
01167                 break;
01168             }
01169         }
01170     }
01171     return -1;
01172 }
01173 
01174 static rb_econv_result_t
01175 rb_trans_conv(rb_econv_t *ec,
01176     const unsigned char **input_ptr, const unsigned char *input_stop,
01177     unsigned char **output_ptr, unsigned char *output_stop,
01178     int flags,
01179     int *result_position_ptr)
01180 {
01181     int i;
01182     int needreport_index;
01183     int sweep_start;
01184 
01185     unsigned char empty_buf;
01186     unsigned char *empty_ptr = &empty_buf;
01187 
01188     if (!input_ptr) {
01189         input_ptr = (const unsigned char **)&empty_ptr;
01190         input_stop = empty_ptr;
01191     }
01192 
01193     if (!output_ptr) {
01194         output_ptr = &empty_ptr;
01195         output_stop = empty_ptr;
01196     }
01197 
01198     if (ec->elems[0].last_result == econv_after_output)
01199         ec->elems[0].last_result = econv_source_buffer_empty;
01200 
01201     needreport_index = -1;
01202     for (i = ec->num_trans-1; 0 <= i; i--) {
01203         switch (ec->elems[i].last_result) {
01204           case econv_invalid_byte_sequence:
01205           case econv_incomplete_input:
01206           case econv_undefined_conversion:
01207           case econv_after_output:
01208           case econv_finished:
01209             sweep_start = i+1;
01210             needreport_index = i;
01211             goto found_needreport;
01212 
01213           case econv_destination_buffer_full:
01214           case econv_source_buffer_empty:
01215             break;
01216 
01217           default:
01218             rb_bug("unexpected transcode last result");
01219         }
01220     }
01221 
01222     /* /^[sd]+$/ is confirmed.  but actually /^s*d*$/. */
01223 
01224     if (ec->elems[ec->num_trans-1].last_result == econv_destination_buffer_full &&
01225         (flags & ECONV_AFTER_OUTPUT)) {
01226         rb_econv_result_t res;
01227 
01228         res = rb_trans_conv(ec, NULL, NULL, output_ptr, output_stop,
01229                 (flags & ~ECONV_AFTER_OUTPUT)|ECONV_PARTIAL_INPUT,
01230                 result_position_ptr);
01231 
01232         if (res == econv_source_buffer_empty)
01233             return econv_after_output;
01234         return res;
01235     }
01236 
01237     sweep_start = 0;
01238 
01239   found_needreport:
01240 
01241     do {
01242         needreport_index = trans_sweep(ec, input_ptr, input_stop, output_ptr, output_stop, flags, sweep_start);
01243         sweep_start = needreport_index + 1;
01244     } while (needreport_index != -1 && needreport_index != ec->num_trans-1);
01245 
01246     for (i = ec->num_trans-1; 0 <= i; i--) {
01247         if (ec->elems[i].last_result != econv_source_buffer_empty) {
01248             rb_econv_result_t res = ec->elems[i].last_result;
01249             if (res == econv_invalid_byte_sequence ||
01250                 res == econv_incomplete_input ||
01251                 res == econv_undefined_conversion ||
01252                 res == econv_after_output) {
01253                 ec->elems[i].last_result = econv_source_buffer_empty;
01254             }
01255             if (result_position_ptr)
01256                 *result_position_ptr = i;
01257             return res;
01258         }
01259     }
01260     if (result_position_ptr)
01261         *result_position_ptr = -1;
01262     return econv_source_buffer_empty;
01263 }
01264 
01265 static rb_econv_result_t
01266 rb_econv_convert0(rb_econv_t *ec,
01267     const unsigned char **input_ptr, const unsigned char *input_stop,
01268     unsigned char **output_ptr, unsigned char *output_stop,
01269     int flags)
01270 {
01271     rb_econv_result_t res;
01272     int result_position;
01273     int has_output = 0;
01274 
01275     memset(&ec->last_error, 0, sizeof(ec->last_error));
01276 
01277     if (ec->num_trans == 0) {
01278         size_t len;
01279         if (ec->in_buf_start && ec->in_data_start != ec->in_data_end) {
01280             if (output_stop - *output_ptr < ec->in_data_end - ec->in_data_start) {
01281                 len = output_stop - *output_ptr;
01282                 memcpy(*output_ptr, ec->in_data_start, len);
01283                 *output_ptr = output_stop;
01284                 ec->in_data_start += len;
01285                 res = econv_destination_buffer_full;
01286                 goto gotresult;
01287             }
01288             len = ec->in_data_end - ec->in_data_start;
01289             memcpy(*output_ptr, ec->in_data_start, len);
01290             *output_ptr += len;
01291             ec->in_data_start = ec->in_data_end = ec->in_buf_start;
01292             if (flags & ECONV_AFTER_OUTPUT) {
01293                 res = econv_after_output;
01294                 goto gotresult;
01295             }
01296         }
01297         if (output_stop - *output_ptr < input_stop - *input_ptr) {
01298             len = output_stop - *output_ptr;
01299         }
01300         else {
01301             len = input_stop - *input_ptr;
01302         }
01303         if (0 < len && (flags & ECONV_AFTER_OUTPUT)) {
01304             *(*output_ptr)++ = *(*input_ptr)++;
01305             res = econv_after_output;
01306             goto gotresult;
01307         }
01308         memcpy(*output_ptr, *input_ptr, len);
01309         *output_ptr += len;
01310         *input_ptr += len;
01311         if (*input_ptr != input_stop)
01312             res = econv_destination_buffer_full;
01313         else if (flags & ECONV_PARTIAL_INPUT)
01314             res = econv_source_buffer_empty;
01315         else
01316             res = econv_finished;
01317         goto gotresult;
01318     }
01319 
01320     if (ec->elems[ec->num_trans-1].out_data_start) {
01321         unsigned char *data_start = ec->elems[ec->num_trans-1].out_data_start;
01322         unsigned char *data_end = ec->elems[ec->num_trans-1].out_data_end;
01323         if (data_start != data_end) {
01324             size_t len;
01325             if (output_stop - *output_ptr < data_end - data_start) {
01326                 len = output_stop - *output_ptr;
01327                 memcpy(*output_ptr, data_start, len);
01328                 *output_ptr = output_stop;
01329                 ec->elems[ec->num_trans-1].out_data_start += len;
01330                 res = econv_destination_buffer_full;
01331                 goto gotresult;
01332             }
01333             len = data_end - data_start;
01334             memcpy(*output_ptr, data_start, len);
01335             *output_ptr += len;
01336             ec->elems[ec->num_trans-1].out_data_start =
01337                 ec->elems[ec->num_trans-1].out_data_end =
01338                 ec->elems[ec->num_trans-1].out_buf_start;
01339             has_output = 1;
01340         }
01341     }
01342 
01343     if (ec->in_buf_start &&
01344         ec->in_data_start != ec->in_data_end) {
01345         res = rb_trans_conv(ec, (const unsigned char **)&ec->in_data_start, ec->in_data_end, output_ptr, output_stop,
01346                 (flags&~ECONV_AFTER_OUTPUT)|ECONV_PARTIAL_INPUT, &result_position);
01347         if (res != econv_source_buffer_empty)
01348             goto gotresult;
01349     }
01350 
01351     if (has_output &&
01352         (flags & ECONV_AFTER_OUTPUT) &&
01353         *input_ptr != input_stop) {
01354         input_stop = *input_ptr;
01355         res = rb_trans_conv(ec, input_ptr, input_stop, output_ptr, output_stop, flags, &result_position);
01356         if (res == econv_source_buffer_empty)
01357             res = econv_after_output;
01358     }
01359     else if ((flags & ECONV_AFTER_OUTPUT) ||
01360         ec->num_trans == 1) {
01361         res = rb_trans_conv(ec, input_ptr, input_stop, output_ptr, output_stop, flags, &result_position);
01362     }
01363     else {
01364         flags |= ECONV_AFTER_OUTPUT;
01365         do {
01366             res = rb_trans_conv(ec, input_ptr, input_stop, output_ptr, output_stop, flags, &result_position);
01367         } while (res == econv_after_output);
01368     }
01369 
01370   gotresult:
01371     ec->last_error.result = res;
01372     if (res == econv_invalid_byte_sequence ||
01373         res == econv_incomplete_input ||
01374         res == econv_undefined_conversion) {
01375         rb_transcoding *error_tc = ec->elems[result_position].tc;
01376         ec->last_error.error_tc = error_tc;
01377         ec->last_error.source_encoding = error_tc->transcoder->src_encoding;
01378         ec->last_error.destination_encoding = error_tc->transcoder->dst_encoding;
01379         ec->last_error.error_bytes_start = TRANSCODING_READBUF(error_tc);
01380         ec->last_error.error_bytes_len = error_tc->recognized_len;
01381         ec->last_error.readagain_len = error_tc->readagain_len;
01382     }
01383 
01384     return res;
01385 }
01386 
01387 static int output_replacement_character(rb_econv_t *ec);
01388 
01389 static int
01390 output_hex_charref(rb_econv_t *ec)
01391 {
01392     int ret;
01393     unsigned char utfbuf[1024];
01394     const unsigned char *utf;
01395     size_t utf_len;
01396     int utf_allocated = 0;
01397     char charef_buf[16];
01398     const unsigned char *p;
01399 
01400     if (encoding_equal(ec->last_error.source_encoding, "UTF-32BE")) {
01401         utf = ec->last_error.error_bytes_start;
01402         utf_len = ec->last_error.error_bytes_len;
01403     }
01404     else {
01405         utf = allocate_converted_string(ec->last_error.source_encoding, "UTF-32BE",
01406                 ec->last_error.error_bytes_start, ec->last_error.error_bytes_len,
01407                 utfbuf, sizeof(utfbuf),
01408                 &utf_len);
01409         if (!utf)
01410             return -1;
01411         if (utf != utfbuf && utf != ec->last_error.error_bytes_start)
01412             utf_allocated = 1;
01413     }
01414 
01415     if (utf_len % 4 != 0)
01416         goto fail;
01417 
01418     p = utf;
01419     while (4 <= utf_len) {
01420         unsigned int u = 0;
01421         u += p[0] << 24;
01422         u += p[1] << 16;
01423         u += p[2] << 8;
01424         u += p[3];
01425         snprintf(charef_buf, sizeof(charef_buf), "&#x%X;", u);
01426 
01427         ret = rb_econv_insert_output(ec, (unsigned char *)charef_buf, strlen(charef_buf), "US-ASCII");
01428         if (ret == -1)
01429             goto fail;
01430 
01431         p += 4;
01432         utf_len -= 4;
01433     }
01434 
01435     if (utf_allocated)
01436         xfree((void *)utf);
01437     return 0;
01438 
01439   fail:
01440     if (utf_allocated)
01441         xfree((void *)utf);
01442     return -1;
01443 }
01444 
01445 rb_econv_result_t
01446 rb_econv_convert(rb_econv_t *ec,
01447     const unsigned char **input_ptr, const unsigned char *input_stop,
01448     unsigned char **output_ptr, unsigned char *output_stop,
01449     int flags)
01450 {
01451     rb_econv_result_t ret;
01452 
01453     unsigned char empty_buf;
01454     unsigned char *empty_ptr = &empty_buf;
01455 
01456     ec->started = 1;
01457 
01458     if (!input_ptr) {
01459         input_ptr = (const unsigned char **)&empty_ptr;
01460         input_stop = empty_ptr;
01461     }
01462 
01463     if (!output_ptr) {
01464         output_ptr = &empty_ptr;
01465         output_stop = empty_ptr;
01466     }
01467 
01468   resume:
01469     ret = rb_econv_convert0(ec, input_ptr, input_stop, output_ptr, output_stop, flags);
01470 
01471     if (ret == econv_invalid_byte_sequence ||
01472         ret == econv_incomplete_input) {
01473         /* deal with invalid byte sequence */
01474         /* todo: add more alternative behaviors */
01475         switch (ec->flags & ECONV_INVALID_MASK) {
01476           case ECONV_INVALID_REPLACE:
01477             if (output_replacement_character(ec) == 0)
01478                 goto resume;
01479         }
01480     }
01481 
01482     if (ret == econv_undefined_conversion) {
01483         /* valid character in source encoding
01484          * but no related character(s) in destination encoding */
01485         /* todo: add more alternative behaviors */
01486         switch (ec->flags & ECONV_UNDEF_MASK) {
01487           case ECONV_UNDEF_REPLACE:
01488             if (output_replacement_character(ec) == 0)
01489                 goto resume;
01490             break;
01491 
01492           case ECONV_UNDEF_HEX_CHARREF:
01493             if (output_hex_charref(ec) == 0)
01494                 goto resume;
01495             break;
01496         }
01497     }
01498 
01499     return ret;
01500 }
01501 
01502 const char *
01503 rb_econv_encoding_to_insert_output(rb_econv_t *ec)
01504 {
01505     rb_transcoding *tc = ec->last_tc;
01506     const rb_transcoder *tr;
01507 
01508     if (tc == NULL)
01509         return "";
01510 
01511     tr = tc->transcoder;
01512 
01513     if (tr->asciicompat_type == asciicompat_encoder)
01514         return tr->src_encoding;
01515     return tr->dst_encoding;
01516 }
01517 
01518 static unsigned char *
01519 allocate_converted_string(const char *sname, const char *dname,
01520         const unsigned char *str, size_t len,
01521         unsigned char *caller_dst_buf, size_t caller_dst_bufsize,
01522         size_t *dst_len_ptr)
01523 {
01524     unsigned char *dst_str;
01525     size_t dst_len;
01526     size_t dst_bufsize;
01527 
01528     rb_econv_t *ec;
01529     rb_econv_result_t res;
01530 
01531     const unsigned char *sp;
01532     unsigned char *dp;
01533 
01534     if (caller_dst_buf)
01535         dst_bufsize = caller_dst_bufsize;
01536     else if (len == 0)
01537         dst_bufsize = 1;
01538     else
01539         dst_bufsize = len;
01540 
01541     ec = rb_econv_open(sname, dname, 0);
01542     if (ec == NULL)
01543         return NULL;
01544     if (caller_dst_buf)
01545         dst_str = caller_dst_buf;
01546     else
01547         dst_str = xmalloc(dst_bufsize);
01548     dst_len = 0;
01549     sp = str;
01550     dp = dst_str+dst_len;
01551     res = rb_econv_convert(ec, &sp, str+len, &dp, dst_str+dst_bufsize, 0);
01552     dst_len = dp - dst_str;
01553     while (res == econv_destination_buffer_full) {
01554         if (SIZE_MAX/2 < dst_bufsize) {
01555             goto fail;
01556         }
01557         dst_bufsize *= 2;
01558         if (dst_str == caller_dst_buf) {
01559             unsigned char *tmp;
01560             tmp = xmalloc(dst_bufsize);
01561             memcpy(tmp, dst_str, dst_bufsize/2);
01562             dst_str = tmp;
01563         }
01564         else {
01565             dst_str = xrealloc(dst_str, dst_bufsize);
01566         }
01567         dp = dst_str+dst_len;
01568         res = rb_econv_convert(ec, &sp, str+len, &dp, dst_str+dst_bufsize, 0);
01569         dst_len = dp - dst_str;
01570     }
01571     if (res != econv_finished) {
01572         goto fail;
01573     }
01574     rb_econv_close(ec);
01575     *dst_len_ptr = dst_len;
01576     return dst_str;
01577 
01578   fail:
01579     if (dst_str != caller_dst_buf)
01580         xfree(dst_str);
01581     rb_econv_close(ec);
01582     return NULL;
01583 }
01584 
01585 /* result: 0:success -1:failure */
01586 int
01587 rb_econv_insert_output(rb_econv_t *ec,
01588     const unsigned char *str, size_t len, const char *str_encoding)
01589 {
01590     const char *insert_encoding = rb_econv_encoding_to_insert_output(ec);
01591     unsigned char insert_buf[4096];
01592     const unsigned char *insert_str = NULL;
01593     size_t insert_len;
01594 
01595     int last_trans_index;
01596     rb_transcoding *tc;
01597 
01598     unsigned char **buf_start_p;
01599     unsigned char **data_start_p;
01600     unsigned char **data_end_p;
01601     unsigned char **buf_end_p;
01602 
01603     size_t need;
01604 
01605     ec->started = 1;
01606 
01607     if (len == 0)
01608         return 0;
01609 
01610     if (encoding_equal(insert_encoding, str_encoding)) {
01611         insert_str = str;
01612         insert_len = len;
01613     }
01614     else {
01615         insert_str = allocate_converted_string(str_encoding, insert_encoding,
01616                 str, len, insert_buf, sizeof(insert_buf), &insert_len);
01617         if (insert_str == NULL)
01618             return -1;
01619     }
01620 
01621     need = insert_len;
01622 
01623     last_trans_index = ec->num_trans-1;
01624     if (ec->num_trans == 0) {
01625         tc = NULL;
01626         buf_start_p = &ec->in_buf_start;
01627         data_start_p = &ec->in_data_start;
01628         data_end_p = &ec->in_data_end;
01629         buf_end_p = &ec->in_buf_end;
01630     }
01631     else if (ec->elems[last_trans_index].tc->transcoder->asciicompat_type == asciicompat_encoder) {
01632         tc = ec->elems[last_trans_index].tc;
01633         need += tc->readagain_len;
01634         if (need < insert_len)
01635             goto fail;
01636         if (last_trans_index == 0) {
01637             buf_start_p = &ec->in_buf_start;
01638             data_start_p = &ec->in_data_start;
01639             data_end_p = &ec->in_data_end;
01640             buf_end_p = &ec->in_buf_end;
01641         }
01642         else {
01643             rb_econv_elem_t *ee = &ec->elems[last_trans_index-1];
01644             buf_start_p = &ee->out_buf_start;
01645             data_start_p = &ee->out_data_start;
01646             data_end_p = &ee->out_data_end;
01647             buf_end_p = &ee->out_buf_end;
01648         }
01649     }
01650     else {
01651         rb_econv_elem_t *ee = &ec->elems[last_trans_index];
01652         buf_start_p = &ee->out_buf_start;
01653         data_start_p = &ee->out_data_start;
01654         data_end_p = &ee->out_data_end;
01655         buf_end_p = &ee->out_buf_end;
01656         tc = ec->elems[last_trans_index].tc;
01657     }
01658 
01659     if (*buf_start_p == NULL) {
01660         unsigned char *buf = xmalloc(need);
01661         *buf_start_p = buf;
01662         *data_start_p = buf;
01663         *data_end_p = buf;
01664         *buf_end_p = buf+need;
01665     }
01666     else if ((size_t)(*buf_end_p - *data_end_p) < need) {
01667         MEMMOVE(*buf_start_p, *data_start_p, unsigned char, *data_end_p - *data_start_p);
01668         *data_end_p = *buf_start_p + (*data_end_p - *data_start_p);
01669         *data_start_p = *buf_start_p;
01670         if ((size_t)(*buf_end_p - *data_end_p) < need) {
01671             unsigned char *buf;
01672             size_t s = (*data_end_p - *buf_start_p) + need;
01673             if (s < need)
01674                 goto fail;
01675             buf = xrealloc(*buf_start_p, s);
01676             *data_start_p = buf;
01677             *data_end_p = buf + (*data_end_p - *buf_start_p);
01678             *buf_start_p = buf;
01679             *buf_end_p = buf + s;
01680         }
01681     }
01682 
01683     memcpy(*data_end_p, insert_str, insert_len);
01684     *data_end_p += insert_len;
01685     if (tc && tc->transcoder->asciicompat_type == asciicompat_encoder) {
01686         memcpy(*data_end_p, TRANSCODING_READBUF(tc)+tc->recognized_len, tc->readagain_len);
01687         *data_end_p += tc->readagain_len;
01688         tc->readagain_len = 0;
01689     }
01690 
01691     if (insert_str != str && insert_str != insert_buf)
01692         xfree((void*)insert_str);
01693     return 0;
01694 
01695   fail:
01696     if (insert_str != str && insert_str != insert_buf)
01697         xfree((void*)insert_str);
01698     return -1;
01699 }
01700 
01701 void
01702 rb_econv_close(rb_econv_t *ec)
01703 {
01704     int i;
01705 
01706     if (ec->replacement_allocated) {
01707         xfree((void *)ec->replacement_str);
01708     }
01709     for (i = 0; i < ec->num_trans; i++) {
01710         rb_transcoding_close(ec->elems[i].tc);
01711         if (ec->elems[i].out_buf_start)
01712             xfree(ec->elems[i].out_buf_start);
01713     }
01714     xfree(ec->in_buf_start);
01715     xfree(ec->elems);
01716     xfree(ec);
01717 }
01718 
01719 size_t
01720 rb_econv_memsize(rb_econv_t *ec)
01721 {
01722     size_t size = sizeof(rb_econv_t);
01723     int i;
01724 
01725     if (ec->replacement_allocated) {
01726         size += ec->replacement_len;
01727     }
01728     for (i = 0; i < ec->num_trans; i++) {
01729         size += rb_transcoding_memsize(ec->elems[i].tc);
01730 
01731         if (ec->elems[i].out_buf_start) {
01732             size += ec->elems[i].out_buf_end - ec->elems[i].out_buf_start;
01733         }
01734     }
01735     size += ec->in_buf_end - ec->in_buf_start;
01736     size += sizeof(rb_econv_elem_t) * ec->num_allocated;
01737 
01738     return size;
01739 }
01740 
01741 int
01742 rb_econv_putbackable(rb_econv_t *ec)
01743 {
01744     if (ec->num_trans == 0)
01745         return 0;
01746 #if SIZEOF_SIZE_T > SIZEOF_INT
01747     if (ec->elems[0].tc->readagain_len > INT_MAX) return INT_MAX;
01748 #endif
01749     return (int)ec->elems[0].tc->readagain_len;
01750 }
01751 
01752 void
01753 rb_econv_putback(rb_econv_t *ec, unsigned char *p, int n)
01754 {
01755     rb_transcoding *tc;
01756     if (ec->num_trans == 0 || n == 0)
01757         return;
01758     tc = ec->elems[0].tc;
01759     memcpy(p, TRANSCODING_READBUF(tc) + tc->recognized_len + tc->readagain_len - n, n);
01760     tc->readagain_len -= n;
01761 }
01762 
01763 struct asciicompat_encoding_t {
01764     const char *ascii_compat_name;
01765     const char *ascii_incompat_name;
01766 };
01767 
01768 static int
01769 asciicompat_encoding_i(st_data_t key, st_data_t val, st_data_t arg)
01770 {
01771     struct asciicompat_encoding_t *data = (struct asciicompat_encoding_t *)arg;
01772     transcoder_entry_t *entry = (transcoder_entry_t *)val;
01773     const rb_transcoder *tr;
01774 
01775     if (DECORATOR_P(entry->sname, entry->dname))
01776         return ST_CONTINUE;
01777     tr = load_transcoder_entry(entry);
01778     if (tr && tr->asciicompat_type == asciicompat_decoder) {
01779         data->ascii_compat_name = tr->dst_encoding;
01780         return ST_STOP;
01781     }
01782     return ST_CONTINUE;
01783 }
01784 
01785 const char *
01786 rb_econv_asciicompat_encoding(const char *ascii_incompat_name)
01787 {
01788     st_data_t v;
01789     st_table *table2;
01790     struct asciicompat_encoding_t data;
01791 
01792     if (!st_lookup(transcoder_table, (st_data_t)ascii_incompat_name, &v))
01793         return NULL;
01794     table2 = (st_table *)v;
01795 
01796     /*
01797      * Assumption:
01798      * There is at most one transcoder for
01799      * converting from ASCII incompatible encoding.
01800      *
01801      * For ISO-2022-JP, there is ISO-2022-JP -> stateless-ISO-2022-JP and no others.
01802      */
01803     if (table2->num_entries != 1)
01804         return NULL;
01805 
01806     data.ascii_incompat_name = ascii_incompat_name;
01807     data.ascii_compat_name = NULL;
01808     st_foreach(table2, asciicompat_encoding_i, (st_data_t)&data);
01809     return data.ascii_compat_name;
01810 }
01811 
01812 VALUE
01813 rb_econv_append(rb_econv_t *ec, const char *ss, long len, VALUE dst, int flags)
01814 {
01815     unsigned const char *sp, *se;
01816     unsigned char *ds, *dp, *de;
01817     rb_econv_result_t res;
01818     int max_output;
01819 
01820     if (NIL_P(dst)) {
01821         dst = rb_str_buf_new(len);
01822         if (ec->destination_encoding)
01823             rb_enc_associate(dst, ec->destination_encoding);
01824     }
01825 
01826     if (ec->last_tc)
01827         max_output = ec->last_tc->transcoder->max_output;
01828     else
01829         max_output = 1;
01830 
01831     do {
01832         long dlen = RSTRING_LEN(dst);
01833         if (rb_str_capacity(dst) - dlen < (size_t)len + max_output) {
01834             unsigned long new_capa = (unsigned long)dlen + len + max_output;
01835             if (LONG_MAX < new_capa)
01836                 rb_raise(rb_eArgError, "too long string");
01837             rb_str_resize(dst, new_capa);
01838             rb_str_set_len(dst, dlen);
01839         }
01840         sp = (const unsigned char *)ss;
01841         se = sp + len;
01842         ds = (unsigned char *)RSTRING_PTR(dst);
01843         de = ds + rb_str_capacity(dst);
01844         dp = ds += dlen;
01845         res = rb_econv_convert(ec, &sp, se, &dp, de, flags);
01846         len -= (const char *)sp - ss;
01847         ss  = (const char *)sp;
01848         rb_str_set_len(dst, dlen + (dp - ds));
01849         rb_econv_check_error(ec);
01850     } while (res == econv_destination_buffer_full);
01851 
01852     return dst;
01853 }
01854 
01855 VALUE
01856 rb_econv_substr_append(rb_econv_t *ec, VALUE src, long off, long len, VALUE dst, int flags)
01857 {
01858     src = rb_str_new_frozen(src);
01859     dst = rb_econv_append(ec, RSTRING_PTR(src) + off, len, dst, flags);
01860     RB_GC_GUARD(src);
01861     return dst;
01862 }
01863 
01864 VALUE
01865 rb_econv_str_append(rb_econv_t *ec, VALUE src, VALUE dst, int flags)
01866 {
01867     return rb_econv_substr_append(ec, src, 0, RSTRING_LEN(src), dst, flags);
01868 }
01869 
01870 VALUE
01871 rb_econv_substr_convert(rb_econv_t *ec, VALUE src, long byteoff, long bytesize, int flags)
01872 {
01873     return rb_econv_substr_append(ec, src, byteoff, bytesize, Qnil, flags);
01874 }
01875 
01876 VALUE
01877 rb_econv_str_convert(rb_econv_t *ec, VALUE src, int flags)
01878 {
01879     return rb_econv_substr_append(ec, src, 0, RSTRING_LEN(src), Qnil, flags);
01880 }
01881 
01882 static int
01883 rb_econv_add_converter(rb_econv_t *ec, const char *sname, const char *dname, int n)
01884 {
01885     transcoder_entry_t *entry;
01886     const rb_transcoder *tr;
01887 
01888     if (ec->started != 0)
01889         return -1;
01890 
01891     entry = get_transcoder_entry(sname, dname);
01892     if (!entry)
01893         return -1;
01894 
01895     tr = load_transcoder_entry(entry);
01896     if (!tr) return -1;
01897 
01898     return rb_econv_add_transcoder_at(ec, tr, n);
01899 }
01900 
01901 static int
01902 rb_econv_decorate_at(rb_econv_t *ec, const char *decorator_name, int n)
01903 {
01904     return rb_econv_add_converter(ec, "", decorator_name, n);
01905 }
01906 
01907 int
01908 rb_econv_decorate_at_first(rb_econv_t *ec, const char *decorator_name)
01909 {
01910     const rb_transcoder *tr;
01911 
01912     if (ec->num_trans == 0)
01913         return rb_econv_decorate_at(ec, decorator_name, 0);
01914 
01915     tr = ec->elems[0].tc->transcoder;
01916 
01917     if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding) &&
01918         tr->asciicompat_type == asciicompat_decoder)
01919         return rb_econv_decorate_at(ec, decorator_name, 1);
01920 
01921     return rb_econv_decorate_at(ec, decorator_name, 0);
01922 }
01923 
01924 int
01925 rb_econv_decorate_at_last(rb_econv_t *ec, const char *decorator_name)
01926 {
01927     const rb_transcoder *tr;
01928 
01929     if (ec->num_trans == 0)
01930         return rb_econv_decorate_at(ec, decorator_name, 0);
01931 
01932     tr = ec->elems[ec->num_trans-1].tc->transcoder;
01933 
01934     if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding) &&
01935         tr->asciicompat_type == asciicompat_encoder)
01936         return rb_econv_decorate_at(ec, decorator_name, ec->num_trans-1);
01937 
01938     return rb_econv_decorate_at(ec, decorator_name, ec->num_trans);
01939 }
01940 
01941 void
01942 rb_econv_binmode(rb_econv_t *ec)
01943 {
01944     const char *dname = 0;
01945 
01946     switch (ec->flags & ECONV_NEWLINE_DECORATOR_MASK) {
01947       case ECONV_UNIVERSAL_NEWLINE_DECORATOR:
01948         dname = "universal_newline";
01949         break;
01950       case ECONV_CRLF_NEWLINE_DECORATOR:
01951         dname = "crlf_newline";
01952         break;
01953       case ECONV_CR_NEWLINE_DECORATOR:
01954         dname = "cr_newline";
01955         break;
01956     }
01957 
01958     if (dname) {
01959         const rb_transcoder *transcoder = get_transcoder_entry("", dname)->transcoder;
01960         int num_trans = ec->num_trans;
01961         int i, j = 0;
01962 
01963         for (i=0; i < num_trans; i++) {
01964             if (transcoder == ec->elems[i].tc->transcoder) {
01965                 rb_transcoding_close(ec->elems[i].tc);
01966                 xfree(ec->elems[i].out_buf_start);
01967                 ec->num_trans--;
01968             }
01969             else
01970                 ec->elems[j++] = ec->elems[i];
01971         }
01972     }
01973 
01974     ec->flags &= ~ECONV_NEWLINE_DECORATOR_MASK;
01975 }
01976 
01977 static VALUE
01978 econv_description(const char *sname, const char *dname, int ecflags, VALUE mesg)
01979 {
01980     int has_description = 0;
01981 
01982     if (NIL_P(mesg))
01983         mesg = rb_str_new(NULL, 0);
01984 
01985     if (*sname != '\0' || *dname != '\0') {
01986         if (*sname == '\0')
01987             rb_str_cat2(mesg, dname);
01988         else if (*dname == '\0')
01989             rb_str_cat2(mesg, sname);
01990         else
01991             rb_str_catf(mesg, "%s to %s", sname, dname);
01992         has_description = 1;
01993     }
01994 
01995     if (ecflags & (ECONV_NEWLINE_DECORATOR_MASK|
01996                    ECONV_XML_TEXT_DECORATOR|
01997                    ECONV_XML_ATTR_CONTENT_DECORATOR|
01998                    ECONV_XML_ATTR_QUOTE_DECORATOR)) {
01999         const char *pre = "";
02000         if (has_description)
02001             rb_str_cat2(mesg, " with ");
02002         if (ecflags & ECONV_UNIVERSAL_NEWLINE_DECORATOR)  {
02003             rb_str_cat2(mesg, pre); pre = ",";
02004             rb_str_cat2(mesg, "universal_newline");
02005         }
02006         if (ecflags & ECONV_CRLF_NEWLINE_DECORATOR) {
02007             rb_str_cat2(mesg, pre); pre = ",";
02008             rb_str_cat2(mesg, "crlf_newline");
02009         }
02010         if (ecflags & ECONV_CR_NEWLINE_DECORATOR) {
02011             rb_str_cat2(mesg, pre); pre = ",";
02012             rb_str_cat2(mesg, "cr_newline");
02013         }
02014         if (ecflags & ECONV_XML_TEXT_DECORATOR) {
02015             rb_str_cat2(mesg, pre); pre = ",";
02016             rb_str_cat2(mesg, "xml_text");
02017         }
02018         if (ecflags & ECONV_XML_ATTR_CONTENT_DECORATOR) {
02019             rb_str_cat2(mesg, pre); pre = ",";
02020             rb_str_cat2(mesg, "xml_attr_content");
02021         }
02022         if (ecflags & ECONV_XML_ATTR_QUOTE_DECORATOR) {
02023             rb_str_cat2(mesg, pre); pre = ",";
02024             rb_str_cat2(mesg, "xml_attr_quote");
02025         }
02026         has_description = 1;
02027     }
02028     if (!has_description) {
02029         rb_str_cat2(mesg, "no-conversion");
02030     }
02031 
02032     return mesg;
02033 }
02034 
02035 VALUE
02036 rb_econv_open_exc(const char *sname, const char *dname, int ecflags)
02037 {
02038     VALUE mesg, exc;
02039     mesg = rb_str_new_cstr("code converter not found (");
02040     econv_description(sname, dname, ecflags, mesg);
02041     rb_str_cat2(mesg, ")");
02042     exc = rb_exc_new3(rb_eConverterNotFoundError, mesg);
02043     return exc;
02044 }
02045 
02046 static VALUE
02047 make_econv_exception(rb_econv_t *ec)
02048 {
02049     VALUE mesg, exc;
02050     if (ec->last_error.result == econv_invalid_byte_sequence ||
02051         ec->last_error.result == econv_incomplete_input) {
02052         const char *err = (const char *)ec->last_error.error_bytes_start;
02053         size_t error_len = ec->last_error.error_bytes_len;
02054         VALUE bytes = rb_str_new(err, error_len);
02055         VALUE dumped = rb_str_dump(bytes);
02056         size_t readagain_len = ec->last_error.readagain_len;
02057         VALUE bytes2 = Qnil;
02058         VALUE dumped2;
02059         int idx;
02060         if (ec->last_error.result == econv_incomplete_input) {
02061             mesg = rb_sprintf("incomplete %s on %s",
02062                     StringValueCStr(dumped),
02063                     ec->last_error.source_encoding);
02064         }
02065         else if (readagain_len) {
02066             bytes2 = rb_str_new(err+error_len, readagain_len);
02067             dumped2 = rb_str_dump(bytes2);
02068             mesg = rb_sprintf("%s followed by %s on %s",
02069                     StringValueCStr(dumped),
02070                     StringValueCStr(dumped2),
02071                     ec->last_error.source_encoding);
02072         }
02073         else {
02074             mesg = rb_sprintf("%s on %s",
02075                     StringValueCStr(dumped),
02076                     ec->last_error.source_encoding);
02077         }
02078 
02079         exc = rb_exc_new3(rb_eInvalidByteSequenceError, mesg);
02080         rb_ivar_set(exc, rb_intern("error_bytes"), bytes);
02081         rb_ivar_set(exc, rb_intern("readagain_bytes"), bytes2);
02082         rb_ivar_set(exc, rb_intern("incomplete_input"), ec->last_error.result == econv_incomplete_input ? Qtrue : Qfalse);
02083 
02084       set_encs:
02085         rb_ivar_set(exc, rb_intern("source_encoding_name"), rb_str_new2(ec->last_error.source_encoding));
02086         rb_ivar_set(exc, rb_intern("destination_encoding_name"), rb_str_new2(ec->last_error.destination_encoding));
02087         idx = rb_enc_find_index(ec->last_error.source_encoding);
02088         if (0 <= idx)
02089             rb_ivar_set(exc, rb_intern("source_encoding"), rb_enc_from_encoding(rb_enc_from_index(idx)));
02090         idx = rb_enc_find_index(ec->last_error.destination_encoding);
02091         if (0 <= idx)
02092             rb_ivar_set(exc, rb_intern("destination_encoding"), rb_enc_from_encoding(rb_enc_from_index(idx)));
02093         return exc;
02094     }
02095     if (ec->last_error.result == econv_undefined_conversion) {
02096         VALUE bytes = rb_str_new((const char *)ec->last_error.error_bytes_start,
02097                                  ec->last_error.error_bytes_len);
02098         VALUE dumped = Qnil;
02099         int idx;
02100         if (strcmp(ec->last_error.source_encoding, "UTF-8") == 0) {
02101             rb_encoding *utf8 = rb_utf8_encoding();
02102             const char *start, *end;
02103             int n;
02104             start = (const char *)ec->last_error.error_bytes_start;
02105             end = start + ec->last_error.error_bytes_len;
02106             n = rb_enc_precise_mbclen(start, end, utf8);
02107             if (MBCLEN_CHARFOUND_P(n) &&
02108                 (size_t)MBCLEN_CHARFOUND_LEN(n) == ec->last_error.error_bytes_len) {
02109                 unsigned int cc = rb_enc_mbc_to_codepoint(start, end, utf8);
02110                 dumped = rb_sprintf("U+%04X", cc);
02111             }
02112         }
02113         if (dumped == Qnil)
02114             dumped = rb_str_dump(bytes);
02115         if (strcmp(ec->last_error.source_encoding,
02116                    ec->source_encoding_name) == 0 &&
02117             strcmp(ec->last_error.destination_encoding,
02118                    ec->destination_encoding_name) == 0) {
02119             mesg = rb_sprintf("%s from %s to %s",
02120                     StringValueCStr(dumped),
02121                     ec->last_error.source_encoding,
02122                     ec->last_error.destination_encoding);
02123         }
02124         else {
02125             int i;
02126             mesg = rb_sprintf("%s to %s in conversion from %s",
02127                     StringValueCStr(dumped),
02128                     ec->last_error.destination_encoding,
02129                     ec->source_encoding_name);
02130             for (i = 0; i < ec->num_trans; i++) {
02131                 const rb_transcoder *tr = ec->elems[i].tc->transcoder;
02132                 if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding))
02133                     rb_str_catf(mesg, " to %s",
02134                                 ec->elems[i].tc->transcoder->dst_encoding);
02135             }
02136         }
02137         exc = rb_exc_new3(rb_eUndefinedConversionError, mesg);
02138         idx = rb_enc_find_index(ec->last_error.source_encoding);
02139         if (0 <= idx)
02140             rb_enc_associate_index(bytes, idx);
02141         rb_ivar_set(exc, rb_intern("error_char"), bytes);
02142         goto set_encs;
02143     }
02144     return Qnil;
02145 }
02146 
02147 static void
02148 more_output_buffer(
02149         VALUE destination,
02150         unsigned char *(*resize_destination)(VALUE, size_t, size_t),
02151         int max_output,
02152         unsigned char **out_start_ptr,
02153         unsigned char **out_pos,
02154         unsigned char **out_stop_ptr)
02155 {
02156     size_t len = (*out_pos - *out_start_ptr);
02157     size_t new_len = (len + max_output) * 2;
02158     *out_start_ptr = resize_destination(destination, len, new_len);
02159     *out_pos = *out_start_ptr + len;
02160     *out_stop_ptr = *out_start_ptr + new_len;
02161 }
02162 
02163 static int
02164 make_replacement(rb_econv_t *ec)
02165 {
02166     rb_transcoding *tc;
02167     const rb_transcoder *tr;
02168     const unsigned char *replacement;
02169     const char *repl_enc;
02170     const char *ins_enc;
02171     size_t len;
02172 
02173     if (ec->replacement_str)
02174         return 0;
02175 
02176     ins_enc = rb_econv_encoding_to_insert_output(ec);
02177 
02178     tc = ec->last_tc;
02179     if (*ins_enc) {
02180         tr = tc->transcoder;
02181         rb_enc_find(tr->dst_encoding);
02182         replacement = (const unsigned char *)get_replacement_character(ins_enc, &len, &repl_enc);
02183     }
02184     else {
02185         replacement = (unsigned char *)"?";
02186         len = 1;
02187         repl_enc = "";
02188     }
02189 
02190     ec->replacement_str = replacement;
02191     ec->replacement_len = len;
02192     ec->replacement_enc = repl_enc;
02193     ec->replacement_allocated = 0;
02194     return 0;
02195 }
02196 
02197 int
02198 rb_econv_set_replacement(rb_econv_t *ec,
02199     const unsigned char *str, size_t len, const char *encname)
02200 {
02201     unsigned char *str2;
02202     size_t len2;
02203     const char *encname2;
02204 
02205     encname2 = rb_econv_encoding_to_insert_output(ec);
02206 
02207     if (encoding_equal(encname, encname2)) {
02208         str2 = xmalloc(len);
02209         MEMCPY(str2, str, unsigned char, len); /* xxx: str may be invalid */
02210         len2 = len;
02211         encname2 = encname;
02212     }
02213     else {
02214         str2 = allocate_converted_string(encname, encname2, str, len, NULL, 0, &len2);
02215         if (!str2)
02216             return -1;
02217     }
02218 
02219     if (ec->replacement_allocated) {
02220         xfree((void *)ec->replacement_str);
02221     }
02222     ec->replacement_allocated = 1;
02223     ec->replacement_str = str2;
02224     ec->replacement_len = len2;
02225     ec->replacement_enc = encname2;
02226     return 0;
02227 }
02228 
02229 static int
02230 output_replacement_character(rb_econv_t *ec)
02231 {
02232     int ret;
02233 
02234     if (make_replacement(ec) == -1)
02235         return -1;
02236 
02237     ret = rb_econv_insert_output(ec, ec->replacement_str, ec->replacement_len, ec->replacement_enc);
02238     if (ret == -1)
02239         return -1;
02240 
02241     return 0;
02242 }
02243 
02244 #if 1
02245 #define hash_fallback rb_hash_aref
02246 
02247 static VALUE
02248 proc_fallback(VALUE fallback, VALUE c)
02249 {
02250     return rb_proc_call(fallback, rb_ary_new4(1, &c));
02251 }
02252 
02253 static VALUE
02254 method_fallback(VALUE fallback, VALUE c)
02255 {
02256     return rb_method_call(1, &c, fallback);
02257 }
02258 
02259 static VALUE
02260 aref_fallback(VALUE fallback, VALUE c)
02261 {
02262     return rb_funcall3(fallback, sym_aref, 1, &c);
02263 }
02264 
02265 static void
02266 transcode_loop(const unsigned char **in_pos, unsigned char **out_pos,
02267                const unsigned char *in_stop, unsigned char *out_stop,
02268                VALUE destination,
02269                unsigned char *(*resize_destination)(VALUE, size_t, size_t),
02270                const char *src_encoding,
02271                const char *dst_encoding,
02272                int ecflags,
02273                VALUE ecopts)
02274 {
02275     rb_econv_t *ec;
02276     rb_transcoding *last_tc;
02277     rb_econv_result_t ret;
02278     unsigned char *out_start = *out_pos;
02279     int max_output;
02280     VALUE exc;
02281     VALUE fallback = Qnil;
02282     VALUE (*fallback_func)(VALUE, VALUE) = 0;
02283 
02284     ec = rb_econv_open_opts(src_encoding, dst_encoding, ecflags, ecopts);
02285     if (!ec)
02286         rb_exc_raise(rb_econv_open_exc(src_encoding, dst_encoding, ecflags));
02287 
02288     if (!NIL_P(ecopts) && RB_TYPE_P(ecopts, T_HASH)) {
02289         fallback = rb_hash_aref(ecopts, sym_fallback);
02290         if (RB_TYPE_P(fallback, T_HASH)) {
02291             fallback_func = hash_fallback;
02292         }
02293         else if (rb_obj_is_proc(fallback)) {
02294             fallback_func = proc_fallback;
02295         }
02296         else if (rb_obj_is_method(fallback)) {
02297             fallback_func = method_fallback;
02298         }
02299         else {
02300             fallback_func = aref_fallback;
02301         }
02302     }
02303     last_tc = ec->last_tc;
02304     max_output = last_tc ? last_tc->transcoder->max_output : 1;
02305 
02306   resume:
02307     ret = rb_econv_convert(ec, in_pos, in_stop, out_pos, out_stop, 0);
02308 
02309     if (!NIL_P(fallback) && ret == econv_undefined_conversion) {
02310         VALUE rep = rb_enc_str_new(
02311                 (const char *)ec->last_error.error_bytes_start,
02312                 ec->last_error.error_bytes_len,
02313                 rb_enc_find(ec->last_error.source_encoding));
02314         rep = (*fallback_func)(fallback, rep);
02315         if (rep != Qundef && !NIL_P(rep)) {
02316             StringValue(rep);
02317             ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(rep),
02318                     RSTRING_LEN(rep), rb_enc_name(rb_enc_get(rep)));
02319             if ((int)ret == -1) {
02320                 rb_raise(rb_eArgError, "too big fallback string");
02321             }
02322             goto resume;
02323         }
02324     }
02325 
02326     if (ret == econv_invalid_byte_sequence ||
02327         ret == econv_incomplete_input ||
02328         ret == econv_undefined_conversion) {
02329         exc = make_econv_exception(ec);
02330         rb_econv_close(ec);
02331         rb_exc_raise(exc);
02332     }
02333 
02334     if (ret == econv_destination_buffer_full) {
02335         more_output_buffer(destination, resize_destination, max_output, &out_start, out_pos, &out_stop);
02336         goto resume;
02337     }
02338 
02339     rb_econv_close(ec);
02340     return;
02341 }
02342 #else
02343 /* sample transcode_loop implementation in byte-by-byte stream style */
02344 static void
02345 transcode_loop(const unsigned char **in_pos, unsigned char **out_pos,
02346                const unsigned char *in_stop, unsigned char *out_stop,
02347                VALUE destination,
02348                unsigned char *(*resize_destination)(VALUE, size_t, size_t),
02349                const char *src_encoding,
02350                const char *dst_encoding,
02351                int ecflags,
02352                VALUE ecopts)
02353 {
02354     rb_econv_t *ec;
02355     rb_transcoding *last_tc;
02356     rb_econv_result_t ret;
02357     unsigned char *out_start = *out_pos;
02358     const unsigned char *ptr;
02359     int max_output;
02360     VALUE exc;
02361 
02362     ec = rb_econv_open_opts(src_encoding, dst_encoding, ecflags, ecopts);
02363     if (!ec)
02364         rb_exc_raise(rb_econv_open_exc(src_encoding, dst_encoding, ecflags));
02365 
02366     last_tc = ec->last_tc;
02367     max_output = last_tc ? last_tc->transcoder->max_output : 1;
02368 
02369     ret = econv_source_buffer_empty;
02370     ptr = *in_pos;
02371     while (ret != econv_finished) {
02372         unsigned char input_byte;
02373         const unsigned char *p = &input_byte;
02374 
02375         if (ret == econv_source_buffer_empty) {
02376             if (ptr < in_stop) {
02377                 input_byte = *ptr;
02378                 ret = rb_econv_convert(ec, &p, p+1, out_pos, out_stop, ECONV_PARTIAL_INPUT);
02379             }
02380             else {
02381                 ret = rb_econv_convert(ec, NULL, NULL, out_pos, out_stop, 0);
02382             }
02383         }
02384         else {
02385             ret = rb_econv_convert(ec, NULL, NULL, out_pos, out_stop, ECONV_PARTIAL_INPUT);
02386         }
02387         if (&input_byte != p)
02388             ptr += p - &input_byte;
02389         switch (ret) {
02390           case econv_invalid_byte_sequence:
02391           case econv_incomplete_input:
02392           case econv_undefined_conversion:
02393             exc = make_econv_exception(ec);
02394             rb_econv_close(ec);
02395             rb_exc_raise(exc);
02396             break;
02397 
02398           case econv_destination_buffer_full:
02399             more_output_buffer(destination, resize_destination, max_output, &out_start, out_pos, &out_stop);
02400             break;
02401 
02402           case econv_source_buffer_empty:
02403             break;
02404 
02405           case econv_finished:
02406             break;
02407         }
02408     }
02409     rb_econv_close(ec);
02410     *in_pos = in_stop;
02411     return;
02412 }
02413 #endif
02414 
02415 
02416 /*
02417  *  String-specific code
02418  */
02419 
02420 static unsigned char *
02421 str_transcoding_resize(VALUE destination, size_t len, size_t new_len)
02422 {
02423     rb_str_resize(destination, new_len);
02424     return (unsigned char *)RSTRING_PTR(destination);
02425 }
02426 
02427 static int
02428 econv_opts(VALUE opt, int ecflags)
02429 {
02430     VALUE v;
02431 
02432     v = rb_hash_aref(opt, sym_invalid);
02433     if (NIL_P(v)) {
02434     }
02435     else if (v==sym_replace) {
02436         ecflags |= ECONV_INVALID_REPLACE;
02437     }
02438     else {
02439         rb_raise(rb_eArgError, "unknown value for invalid character option");
02440     }
02441 
02442     v = rb_hash_aref(opt, sym_undef);
02443     if (NIL_P(v)) {
02444     }
02445     else if (v==sym_replace) {
02446         ecflags |= ECONV_UNDEF_REPLACE;
02447     }
02448     else {
02449         rb_raise(rb_eArgError, "unknown value for undefined character option");
02450     }
02451 
02452     v = rb_hash_aref(opt, sym_replace);
02453     if (!NIL_P(v) && !(ecflags & ECONV_INVALID_REPLACE)) {
02454         ecflags |= ECONV_UNDEF_REPLACE;
02455     }
02456 
02457     v = rb_hash_aref(opt, sym_xml);
02458     if (!NIL_P(v)) {
02459         if (v==sym_text) {
02460             ecflags |= ECONV_XML_TEXT_DECORATOR|ECONV_UNDEF_HEX_CHARREF;
02461         }
02462         else if (v==sym_attr) {
02463             ecflags |= ECONV_XML_ATTR_CONTENT_DECORATOR|ECONV_XML_ATTR_QUOTE_DECORATOR|ECONV_UNDEF_HEX_CHARREF;
02464         }
02465         else if (RB_TYPE_P(v, T_SYMBOL)) {
02466             rb_raise(rb_eArgError, "unexpected value for xml option: %s", rb_id2name(SYM2ID(v)));
02467         }
02468         else {
02469             rb_raise(rb_eArgError, "unexpected value for xml option");
02470         }
02471     }
02472 
02473 #ifdef ENABLE_ECONV_NEWLINE_OPTION
02474     v = rb_hash_aref(opt, sym_newline);
02475     if (!NIL_P(v)) {
02476         ecflags &= ~ECONV_NEWLINE_DECORATOR_MASK;
02477         if (v == sym_universal) {
02478             ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
02479         }
02480         else if (v == sym_crlf) {
02481             ecflags |= ECONV_CRLF_NEWLINE_DECORATOR;
02482         }
02483         else if (v == sym_cr) {
02484             ecflags |= ECONV_CR_NEWLINE_DECORATOR;
02485         }
02486         else if (v == sym_lf) {
02487             /* ecflags |= ECONV_LF_NEWLINE_DECORATOR; */
02488         }
02489         else if (SYMBOL_P(v)) {
02490             rb_raise(rb_eArgError, "unexpected value for newline option: %s",
02491                      rb_id2name(SYM2ID(v)));
02492         }
02493         else {
02494             rb_raise(rb_eArgError, "unexpected value for newline option");
02495         }
02496     }
02497     else
02498 #endif
02499     {
02500         int setflags = 0, newlineflag = 0;
02501 
02502         v = rb_hash_aref(opt, sym_universal_newline);
02503         if (RTEST(v))
02504             setflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
02505         newlineflag |= !NIL_P(v);
02506 
02507         v = rb_hash_aref(opt, sym_crlf_newline);
02508         if (RTEST(v))
02509             setflags |= ECONV_CRLF_NEWLINE_DECORATOR;
02510         newlineflag |= !NIL_P(v);
02511 
02512         v = rb_hash_aref(opt, sym_cr_newline);
02513         if (RTEST(v))
02514             setflags |= ECONV_CR_NEWLINE_DECORATOR;
02515         newlineflag |= !NIL_P(v);
02516 
02517         if (newlineflag) {
02518             ecflags &= ~ECONV_NEWLINE_DECORATOR_MASK;
02519             ecflags |= setflags;
02520         }
02521     }
02522 
02523     return ecflags;
02524 }
02525 
02526 int
02527 rb_econv_prepare_options(VALUE opthash, VALUE *opts, int ecflags)
02528 {
02529     VALUE newhash = Qnil;
02530     VALUE v;
02531 
02532     if (NIL_P(opthash)) {
02533         *opts = Qnil;
02534         return ecflags;
02535     }
02536     ecflags = econv_opts(opthash, ecflags);
02537 
02538     v = rb_hash_aref(opthash, sym_replace);
02539     if (!NIL_P(v)) {
02540         StringValue(v);
02541         if (rb_enc_str_coderange(v) == ENC_CODERANGE_BROKEN) {
02542             VALUE dumped = rb_str_dump(v);
02543             rb_raise(rb_eArgError, "replacement string is broken: %s as %s",
02544                      StringValueCStr(dumped),
02545                      rb_enc_name(rb_enc_get(v)));
02546         }
02547         v = rb_str_new_frozen(v);
02548         newhash = rb_hash_new();
02549         rb_hash_aset(newhash, sym_replace, v);
02550     }
02551 
02552     v = rb_hash_aref(opthash, sym_fallback);
02553     if (!NIL_P(v)) {
02554         VALUE h = rb_check_hash_type(v);
02555         if (NIL_P(h)
02556             ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, sym_aref))
02557             : (v = h, 1)) {
02558             if (NIL_P(newhash))
02559                 newhash = rb_hash_new();
02560             rb_hash_aset(newhash, sym_fallback, v);
02561         }
02562     }
02563 
02564     if (!NIL_P(newhash))
02565         rb_hash_freeze(newhash);
02566     *opts = newhash;
02567 
02568     return ecflags;
02569 }
02570 
02571 int
02572 rb_econv_prepare_opts(VALUE opthash, VALUE *opts)
02573 {
02574     return rb_econv_prepare_options(opthash, opts, 0);
02575 }
02576 
02577 rb_econv_t *
02578 rb_econv_open_opts(const char *source_encoding, const char *destination_encoding, int ecflags, VALUE opthash)
02579 {
02580     rb_econv_t *ec;
02581     VALUE replacement;
02582 
02583     if (NIL_P(opthash)) {
02584         replacement = Qnil;
02585     }
02586     else {
02587         if (!RB_TYPE_P(opthash, T_HASH) || !OBJ_FROZEN(opthash))
02588             rb_bug("rb_econv_open_opts called with invalid opthash");
02589         replacement = rb_hash_aref(opthash, sym_replace);
02590     }
02591 
02592     ec = rb_econv_open(source_encoding, destination_encoding, ecflags);
02593     if (!ec)
02594         return ec;
02595 
02596     if (!NIL_P(replacement)) {
02597         int ret;
02598         rb_encoding *enc = rb_enc_get(replacement);
02599 
02600         ret = rb_econv_set_replacement(ec,
02601                 (const unsigned char *)RSTRING_PTR(replacement),
02602                 RSTRING_LEN(replacement),
02603                 rb_enc_name(enc));
02604         if (ret == -1) {
02605             rb_econv_close(ec);
02606             return NULL;
02607         }
02608     }
02609     return ec;
02610 }
02611 
02612 static int
02613 enc_arg(volatile VALUE *arg, const char **name_p, rb_encoding **enc_p)
02614 {
02615     rb_encoding *enc;
02616     const char *n;
02617     int encidx;
02618     VALUE encval;
02619 
02620     if (((encidx = rb_to_encoding_index(encval = *arg)) < 0) ||
02621         !(enc = rb_enc_from_index(encidx))) {
02622         enc = NULL;
02623         encidx = 0;
02624         n = StringValueCStr(*arg);
02625     }
02626     else {
02627         n = rb_enc_name(enc);
02628     }
02629 
02630     *name_p = n;
02631     *enc_p = enc;
02632 
02633     return encidx;
02634 }
02635 
02636 static int
02637 str_transcode_enc_args(VALUE str, volatile VALUE *arg1, volatile VALUE *arg2,
02638         const char **sname_p, rb_encoding **senc_p,
02639         const char **dname_p, rb_encoding **denc_p)
02640 {
02641     rb_encoding *senc, *denc;
02642     const char *sname, *dname;
02643     int sencidx, dencidx;
02644 
02645     dencidx = enc_arg(arg1, &dname, &denc);
02646 
02647     if (NIL_P(*arg2)) {
02648         sencidx = rb_enc_get_index(str);
02649         senc = rb_enc_from_index(sencidx);
02650         sname = rb_enc_name(senc);
02651     }
02652     else {
02653         sencidx = enc_arg(arg2, &sname, &senc);
02654     }
02655 
02656     *sname_p = sname;
02657     *senc_p = senc;
02658     *dname_p = dname;
02659     *denc_p = denc;
02660     return dencidx;
02661 }
02662 
02663 static int
02664 str_transcode0(int argc, VALUE *argv, VALUE *self, int ecflags, VALUE ecopts)
02665 {
02666     VALUE dest;
02667     VALUE str = *self;
02668     volatile VALUE arg1, arg2;
02669     long blen, slen;
02670     unsigned char *buf, *bp, *sp;
02671     const unsigned char *fromp;
02672     rb_encoding *senc, *denc;
02673     const char *sname, *dname;
02674     int dencidx;
02675     int explicitly_invalid_replace = TRUE;
02676 
02677     rb_check_arity(argc, 0, 2);
02678 
02679     if (argc == 0) {
02680         arg1 = rb_enc_default_internal();
02681         if (NIL_P(arg1)) {
02682             if (!ecflags) return -1;
02683             arg1 = rb_obj_encoding(str);
02684         }
02685         if (!(ecflags & ECONV_INVALID_MASK)) {
02686             explicitly_invalid_replace = FALSE;
02687         }
02688         ecflags |= ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE;
02689     }
02690     else {
02691         arg1 = argv[0];
02692     }
02693     arg2 = argc<=1 ? Qnil : argv[1];
02694     dencidx = str_transcode_enc_args(str, &arg1, &arg2, &sname, &senc, &dname, &denc);
02695 
02696     if ((ecflags & (ECONV_NEWLINE_DECORATOR_MASK|
02697                     ECONV_XML_TEXT_DECORATOR|
02698                     ECONV_XML_ATTR_CONTENT_DECORATOR|
02699                     ECONV_XML_ATTR_QUOTE_DECORATOR)) == 0) {
02700         if (senc && senc == denc) {
02701             if ((ecflags & ECONV_INVALID_MASK) && explicitly_invalid_replace) {
02702                 VALUE rep = Qnil;
02703                 if (!NIL_P(ecopts)) {
02704                     rep = rb_hash_aref(ecopts, sym_replace);
02705                 }
02706                 dest = rb_str_scrub(str, rep);
02707                 if (NIL_P(dest)) dest = str;
02708                 *self = dest;
02709                 return dencidx;
02710             }
02711             return NIL_P(arg2) ? -1 : dencidx;
02712         }
02713         if (senc && denc && rb_enc_asciicompat(senc) && rb_enc_asciicompat(denc)) {
02714             if (rb_enc_str_coderange(str) == ENC_CODERANGE_7BIT) {
02715                 return dencidx;
02716             }
02717         }
02718         if (encoding_equal(sname, dname)) {
02719             return NIL_P(arg2) ? -1 : dencidx;
02720         }
02721     }
02722     else {
02723         if (encoding_equal(sname, dname)) {
02724             sname = "";
02725             dname = "";
02726         }
02727     }
02728 
02729     fromp = sp = (unsigned char *)RSTRING_PTR(str);
02730     slen = RSTRING_LEN(str);
02731     blen = slen + 30; /* len + margin */
02732     dest = rb_str_tmp_new(blen);
02733     bp = (unsigned char *)RSTRING_PTR(dest);
02734 
02735     transcode_loop(&fromp, &bp, (sp+slen), (bp+blen), dest, str_transcoding_resize, sname, dname, ecflags, ecopts);
02736     if (fromp != sp+slen) {
02737         rb_raise(rb_eArgError, "not fully converted, %"PRIdPTRDIFF" bytes left", sp+slen-fromp);
02738     }
02739     buf = (unsigned char *)RSTRING_PTR(dest);
02740     *bp = '\0';
02741     rb_str_set_len(dest, bp - buf);
02742 
02743     /* set encoding */
02744     if (!denc) {
02745         dencidx = rb_define_dummy_encoding(dname);
02746     }
02747     *self = dest;
02748 
02749     return dencidx;
02750 }
02751 
02752 static int
02753 str_transcode(int argc, VALUE *argv, VALUE *self)
02754 {
02755     VALUE opt;
02756     int ecflags = 0;
02757     VALUE ecopts = Qnil;
02758 
02759     argc = rb_scan_args(argc, argv, "02:", NULL, NULL, &opt);
02760     if (!NIL_P(opt)) {
02761         ecflags = rb_econv_prepare_opts(opt, &ecopts);
02762     }
02763     return str_transcode0(argc, argv, self, ecflags, ecopts);
02764 }
02765 
02766 static inline VALUE
02767 str_encode_associate(VALUE str, int encidx)
02768 {
02769     int cr = 0;
02770 
02771     rb_enc_associate_index(str, encidx);
02772 
02773     /* transcoded string never be broken. */
02774     if (rb_enc_asciicompat(rb_enc_from_index(encidx))) {
02775         rb_str_coderange_scan_restartable(RSTRING_PTR(str), RSTRING_END(str), 0, &cr);
02776     }
02777     else {
02778         cr = ENC_CODERANGE_VALID;
02779     }
02780     ENC_CODERANGE_SET(str, cr);
02781     return str;
02782 }
02783 
02784 /*
02785  *  call-seq:
02786  *     str.encode!(encoding [, options] )   -> str
02787  *     str.encode!(dst_encoding, src_encoding [, options] )   -> str
02788  *
02789  *  The first form transcodes the contents of <i>str</i> from
02790  *  str.encoding to +encoding+.
02791  *  The second form transcodes the contents of <i>str</i> from
02792  *  src_encoding to dst_encoding.
02793  *  The options Hash gives details for conversion. See String#encode
02794  *  for details.
02795  *  Returns the string even if no changes were made.
02796  */
02797 
02798 static VALUE
02799 str_encode_bang(int argc, VALUE *argv, VALUE str)
02800 {
02801     VALUE newstr;
02802     int encidx;
02803 
02804     rb_check_frozen(str);
02805 
02806     newstr = str;
02807     encidx = str_transcode(argc, argv, &newstr);
02808 
02809     if (encidx < 0) return str;
02810     if (newstr == str) {
02811         rb_enc_associate_index(str, encidx);
02812         return str;
02813     }
02814     rb_str_shared_replace(str, newstr);
02815     return str_encode_associate(str, encidx);
02816 }
02817 
02818 static VALUE encoded_dup(VALUE newstr, VALUE str, int encidx);
02819 
02820 /*
02821  *  call-seq:
02822  *     str.encode(encoding [, options] )   -> str
02823  *     str.encode(dst_encoding, src_encoding [, options] )   -> str
02824  *     str.encode([options])   -> str
02825  *
02826  *  The first form returns a copy of +str+ transcoded
02827  *  to encoding +encoding+.
02828  *  The second form returns a copy of +str+ transcoded
02829  *  from src_encoding to dst_encoding.
02830  *  The last form returns a copy of +str+ transcoded to
02831  *  <tt>Encoding.default_internal</tt>.
02832  *
02833  *  By default, the first and second form raise
02834  *  Encoding::UndefinedConversionError for characters that are
02835  *  undefined in the destination encoding, and
02836  *  Encoding::InvalidByteSequenceError for invalid byte sequences
02837  *  in the source encoding. The last form by default does not raise
02838  *  exceptions but uses replacement strings.
02839  *
02840  *  The +options+ Hash gives details for conversion and can have the following
02841  *  keys:
02842  *
02843  *  :invalid ::
02844  *    If the value is +:replace+, #encode replaces invalid byte sequences in
02845  *    +str+ with the replacement character.  The default is to raise the
02846  *    Encoding::InvalidByteSequenceError exception
02847  *  :undef ::
02848  *    If the value is +:replace+, #encode replaces characters which are
02849  *    undefined in the destination encoding with the replacement character.
02850  *    The default is to raise the Encoding::UndefinedConversionError.
02851  *  :replace ::
02852  *    Sets the replacement string to the given value. The default replacement
02853  *    string is "\uFFFD" for Unicode encoding forms, and "?" otherwise.
02854  *  :fallback ::
02855  *    Sets the replacement string by the given object for undefined
02856  *    character.  The object should be a Hash, a Proc, a Method, or an
02857  *    object which has [] method.
02858  *    Its key is an undefined character encoded in the source encoding
02859  *    of current transcoder. Its value can be any encoding until it
02860  *    can be converted into the destination encoding of the transcoder.
02861  *  :xml ::
02862  *    The value must be +:text+ or +:attr+.
02863  *    If the value is +:text+ #encode replaces undefined characters with their
02864  *    (upper-case hexadecimal) numeric character references. '&', '<', and '>'
02865  *    are converted to "&amp;", "&lt;", and "&gt;", respectively.
02866  *    If the value is +:attr+, #encode also quotes the replacement result
02867  *    (using '"'), and replaces '"' with "&quot;".
02868  *  :cr_newline ::
02869  *    Replaces LF ("\n") with CR ("\r") if value is true.
02870  *  :crlf_newline ::
02871  *    Replaces LF ("\n") with CRLF ("\r\n") if value is true.
02872  *  :universal_newline ::
02873  *    Replaces CRLF ("\r\n") and CR ("\r") with LF ("\n") if value is true.
02874  */
02875 
02876 static VALUE
02877 str_encode(int argc, VALUE *argv, VALUE str)
02878 {
02879     VALUE newstr = str;
02880     int encidx = str_transcode(argc, argv, &newstr);
02881     return encoded_dup(newstr, str, encidx);
02882 }
02883 
02884 VALUE
02885 rb_str_encode(VALUE str, VALUE to, int ecflags, VALUE ecopts)
02886 {
02887     int argc = 1;
02888     VALUE *argv = &to;
02889     VALUE newstr = str;
02890     int encidx = str_transcode0(argc, argv, &newstr, ecflags, ecopts);
02891     return encoded_dup(newstr, str, encidx);
02892 }
02893 
02894 static VALUE
02895 encoded_dup(VALUE newstr, VALUE str, int encidx)
02896 {
02897     if (encidx < 0) return rb_str_dup(str);
02898     if (newstr == str) {
02899         newstr = rb_str_dup(str);
02900         rb_enc_associate_index(newstr, encidx);
02901         return newstr;
02902     }
02903     else {
02904         RBASIC_SET_CLASS(newstr, rb_obj_class(str));
02905     }
02906     return str_encode_associate(newstr, encidx);
02907 }
02908 
02909 static void
02910 econv_free(void *ptr)
02911 {
02912     rb_econv_t *ec = ptr;
02913     rb_econv_close(ec);
02914 }
02915 
02916 static size_t
02917 econv_memsize(const void *ptr)
02918 {
02919     return ptr ? sizeof(rb_econv_t) : 0;
02920 }
02921 
02922 static const rb_data_type_t econv_data_type = {
02923     "econv",
02924     {NULL, econv_free, econv_memsize,},
02925     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
02926 };
02927 
02928 static VALUE
02929 econv_s_allocate(VALUE klass)
02930 {
02931     return TypedData_Wrap_Struct(klass, &econv_data_type, NULL);
02932 }
02933 
02934 static rb_encoding *
02935 make_dummy_encoding(const char *name)
02936 {
02937     rb_encoding *enc;
02938     int idx;
02939     idx = rb_define_dummy_encoding(name);
02940     enc = rb_enc_from_index(idx);
02941     return enc;
02942 }
02943 
02944 static rb_encoding *
02945 make_encoding(const char *name)
02946 {
02947     rb_encoding *enc;
02948     enc = rb_enc_find(name);
02949     if (!enc)
02950         enc = make_dummy_encoding(name);
02951     return enc;
02952 }
02953 
02954 static VALUE
02955 make_encobj(const char *name)
02956 {
02957     return rb_enc_from_encoding(make_encoding(name));
02958 }
02959 
02960 /*
02961  * call-seq:
02962  *   Encoding::Converter.asciicompat_encoding(string) -> encoding or nil
02963  *   Encoding::Converter.asciicompat_encoding(encoding) -> encoding or nil
02964  *
02965  * Returns the corresponding ASCII compatible encoding.
02966  *
02967  * Returns nil if the argument is an ASCII compatible encoding.
02968  *
02969  * "corresponding ASCII compatible encoding" is an ASCII compatible encoding which
02970  * can represents exactly the same characters as the given ASCII incompatible encoding.
02971  * So, no conversion undefined error occurs when converting between the two encodings.
02972  *
02973  *   Encoding::Converter.asciicompat_encoding("ISO-2022-JP") #=> #<Encoding:stateless-ISO-2022-JP>
02974  *   Encoding::Converter.asciicompat_encoding("UTF-16BE") #=> #<Encoding:UTF-8>
02975  *   Encoding::Converter.asciicompat_encoding("UTF-8") #=> nil
02976  *
02977  */
02978 static VALUE
02979 econv_s_asciicompat_encoding(VALUE klass, VALUE arg)
02980 {
02981     const char *arg_name, *result_name;
02982     rb_encoding *arg_enc, *result_enc;
02983 
02984     enc_arg(&arg, &arg_name, &arg_enc);
02985 
02986     result_name = rb_econv_asciicompat_encoding(arg_name);
02987 
02988     if (result_name == NULL)
02989         return Qnil;
02990 
02991     result_enc = make_encoding(result_name);
02992 
02993     return rb_enc_from_encoding(result_enc);
02994 }
02995 
02996 static void
02997 econv_args(int argc, VALUE *argv,
02998     volatile VALUE *snamev_p, volatile VALUE *dnamev_p,
02999     const char **sname_p, const char **dname_p,
03000     rb_encoding **senc_p, rb_encoding **denc_p,
03001     int *ecflags_p,
03002     VALUE *ecopts_p)
03003 {
03004     VALUE opt, flags_v, ecopts;
03005     int sidx, didx;
03006     const char *sname, *dname;
03007     rb_encoding *senc, *denc;
03008     int ecflags;
03009 
03010     argc = rb_scan_args(argc, argv, "21:", snamev_p, dnamev_p, &flags_v, &opt);
03011 
03012     if (!NIL_P(flags_v)) {
03013         if (!NIL_P(opt)) {
03014             rb_error_arity(argc + 1, 2, 3);
03015         }
03016         ecflags = NUM2INT(rb_to_int(flags_v));
03017         ecopts = Qnil;
03018     }
03019     else if (!NIL_P(opt)) {
03020         ecflags = rb_econv_prepare_opts(opt, &ecopts);
03021     }
03022     else {
03023         ecflags = 0;
03024         ecopts = Qnil;
03025     }
03026 
03027     senc = NULL;
03028     sidx = rb_to_encoding_index(*snamev_p);
03029     if (0 <= sidx) {
03030         senc = rb_enc_from_index(sidx);
03031     }
03032     else {
03033         StringValue(*snamev_p);
03034     }
03035 
03036     denc = NULL;
03037     didx = rb_to_encoding_index(*dnamev_p);
03038     if (0 <= didx) {
03039         denc = rb_enc_from_index(didx);
03040     }
03041     else {
03042         StringValue(*dnamev_p);
03043     }
03044 
03045     sname = senc ? rb_enc_name(senc) : StringValueCStr(*snamev_p);
03046     dname = denc ? rb_enc_name(denc) : StringValueCStr(*dnamev_p);
03047 
03048     *sname_p = sname;
03049     *dname_p = dname;
03050     *senc_p = senc;
03051     *denc_p = denc;
03052     *ecflags_p = ecflags;
03053     *ecopts_p = ecopts;
03054 }
03055 
03056 static int
03057 decorate_convpath(VALUE convpath, int ecflags)
03058 {
03059     int num_decorators;
03060     const char *decorators[MAX_ECFLAGS_DECORATORS];
03061     int i;
03062     int n, len;
03063 
03064     num_decorators = decorator_names(ecflags, decorators);
03065     if (num_decorators == -1)
03066         return -1;
03067 
03068     len = n = RARRAY_LENINT(convpath);
03069     if (n != 0) {
03070         VALUE pair = RARRAY_AREF(convpath, n-1);
03071         if (RB_TYPE_P(pair, T_ARRAY)) {
03072             const char *sname = rb_enc_name(rb_to_encoding(RARRAY_AREF(pair, 0)));
03073             const char *dname = rb_enc_name(rb_to_encoding(RARRAY_AREF(pair, 1)));
03074             transcoder_entry_t *entry = get_transcoder_entry(sname, dname);
03075             const rb_transcoder *tr = load_transcoder_entry(entry);
03076             if (!tr)
03077                 return -1;
03078             if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding) &&
03079                     tr->asciicompat_type == asciicompat_encoder) {
03080                 n--;
03081                 rb_ary_store(convpath, len + num_decorators - 1, pair);
03082             }
03083         }
03084         else {
03085             rb_ary_store(convpath, len + num_decorators - 1, pair);
03086         }
03087     }
03088 
03089     for (i = 0; i < num_decorators; i++)
03090         rb_ary_store(convpath, n + i, rb_str_new_cstr(decorators[i]));
03091 
03092     return 0;
03093 }
03094 
03095 static void
03096 search_convpath_i(const char *sname, const char *dname, int depth, void *arg)
03097 {
03098     VALUE *ary_p = arg;
03099     VALUE v;
03100 
03101     if (*ary_p == Qnil) {
03102         *ary_p = rb_ary_new();
03103     }
03104 
03105     if (DECORATOR_P(sname, dname)) {
03106         v = rb_str_new_cstr(dname);
03107     }
03108     else {
03109         v = rb_assoc_new(make_encobj(sname), make_encobj(dname));
03110     }
03111     rb_ary_store(*ary_p, depth, v);
03112 }
03113 
03114 /*
03115  * call-seq:
03116  *   Encoding::Converter.search_convpath(source_encoding, destination_encoding)         -> ary
03117  *   Encoding::Converter.search_convpath(source_encoding, destination_encoding, opt)    -> ary
03118  *
03119  *  Returns a conversion path.
03120  *
03121  *   p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP")
03122  *   #=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
03123  *   #    [#<Encoding:UTF-8>, #<Encoding:EUC-JP>]]
03124  *
03125  *   p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP", universal_newline: true)
03126  *   or
03127  *   p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP", newline: :universal)
03128  *   #=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
03129  *   #    [#<Encoding:UTF-8>, #<Encoding:EUC-JP>],
03130  *   #    "universal_newline"]
03131  *
03132  *   p Encoding::Converter.search_convpath("ISO-8859-1", "UTF-32BE", universal_newline: true)
03133  *   or
03134  *   p Encoding::Converter.search_convpath("ISO-8859-1", "UTF-32BE", newline: :universal)
03135  *   #=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
03136  *   #    "universal_newline",
03137  *   #    [#<Encoding:UTF-8>, #<Encoding:UTF-32BE>]]
03138  */
03139 static VALUE
03140 econv_s_search_convpath(int argc, VALUE *argv, VALUE klass)
03141 {
03142     volatile VALUE snamev, dnamev;
03143     const char *sname, *dname;
03144     rb_encoding *senc, *denc;
03145     int ecflags;
03146     VALUE ecopts;
03147     VALUE convpath;
03148 
03149     econv_args(argc, argv, &snamev, &dnamev, &sname, &dname, &senc, &denc, &ecflags, &ecopts);
03150 
03151     convpath = Qnil;
03152     transcode_search_path(sname, dname, search_convpath_i, &convpath);
03153 
03154     if (NIL_P(convpath))
03155         rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags));
03156 
03157     if (decorate_convpath(convpath, ecflags) == -1)
03158         rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags));
03159 
03160     return convpath;
03161 }
03162 
03163 /*
03164  * Check the existence of a conversion path.
03165  * Returns the number of converters in the conversion path.
03166  * result: >=0:success -1:failure
03167  */
03168 int
03169 rb_econv_has_convpath_p(const char* from_encoding, const char* to_encoding)
03170 {
03171     VALUE convpath = Qnil;
03172     transcode_search_path(from_encoding, to_encoding, search_convpath_i,
03173                           &convpath);
03174     return RTEST(convpath);
03175 }
03176 
03177 struct rb_econv_init_by_convpath_t {
03178     rb_econv_t *ec;
03179     int index;
03180     int ret;
03181 };
03182 
03183 static void
03184 rb_econv_init_by_convpath_i(const char *sname, const char *dname, int depth, void *arg)
03185 {
03186     struct rb_econv_init_by_convpath_t *a = (struct rb_econv_init_by_convpath_t *)arg;
03187     int ret;
03188 
03189     if (a->ret == -1)
03190         return;
03191 
03192     ret = rb_econv_add_converter(a->ec, sname, dname, a->index);
03193 
03194     a->ret = ret;
03195     return;
03196 }
03197 
03198 static rb_econv_t *
03199 rb_econv_init_by_convpath(VALUE self, VALUE convpath,
03200     const char **sname_p, const char **dname_p,
03201     rb_encoding **senc_p, rb_encoding**denc_p)
03202 {
03203     rb_econv_t *ec;
03204     long i;
03205     int ret, first=1;
03206     VALUE elt;
03207     rb_encoding *senc = 0, *denc = 0;
03208     const char *sname, *dname;
03209 
03210     ec = rb_econv_alloc(RARRAY_LENINT(convpath));
03211     DATA_PTR(self) = ec;
03212 
03213     for (i = 0; i < RARRAY_LEN(convpath); i++) {
03214         volatile VALUE snamev, dnamev;
03215         VALUE pair;
03216         elt = rb_ary_entry(convpath, i);
03217         if (!NIL_P(pair = rb_check_array_type(elt))) {
03218             if (RARRAY_LEN(pair) != 2)
03219                 rb_raise(rb_eArgError, "not a 2-element array in convpath");
03220             snamev = rb_ary_entry(pair, 0);
03221             enc_arg(&snamev, &sname, &senc);
03222             dnamev = rb_ary_entry(pair, 1);
03223             enc_arg(&dnamev, &dname, &denc);
03224         }
03225         else {
03226             sname = "";
03227             dname = StringValueCStr(elt);
03228         }
03229         if (DECORATOR_P(sname, dname)) {
03230             ret = rb_econv_add_converter(ec, sname, dname, ec->num_trans);
03231             if (ret == -1)
03232                 rb_raise(rb_eArgError, "decoration failed: %s", dname);
03233         }
03234         else {
03235             int j = ec->num_trans;
03236             struct rb_econv_init_by_convpath_t arg;
03237             arg.ec = ec;
03238             arg.index = ec->num_trans;
03239             arg.ret = 0;
03240             ret = transcode_search_path(sname, dname, rb_econv_init_by_convpath_i, &arg);
03241             if (ret == -1 || arg.ret == -1)
03242                 rb_raise(rb_eArgError, "adding conversion failed: %s to %s", sname, dname);
03243             if (first) {
03244                 first = 0;
03245                 *senc_p = senc;
03246                 *sname_p = ec->elems[j].tc->transcoder->src_encoding;
03247             }
03248             *denc_p = denc;
03249             *dname_p = ec->elems[ec->num_trans-1].tc->transcoder->dst_encoding;
03250         }
03251     }
03252 
03253     if (first) {
03254       *senc_p = NULL;
03255       *denc_p = NULL;
03256       *sname_p = "";
03257       *dname_p = "";
03258     }
03259 
03260     ec->source_encoding_name = *sname_p;
03261     ec->destination_encoding_name = *dname_p;
03262 
03263     return ec;
03264 }
03265 
03266 /*
03267  * call-seq:
03268  *   Encoding::Converter.new(source_encoding, destination_encoding)
03269  *   Encoding::Converter.new(source_encoding, destination_encoding, opt)
03270  *   Encoding::Converter.new(convpath)
03271  *
03272  * possible options elements:
03273  *   hash form:
03274  *     :invalid => nil            # raise error on invalid byte sequence (default)
03275  *     :invalid => :replace       # replace invalid byte sequence
03276  *     :undef => nil              # raise error on undefined conversion (default)
03277  *     :undef => :replace         # replace undefined conversion
03278  *     :replace => string         # replacement string ("?" or "\uFFFD" if not specified)
03279  *     :newline => :universal     # decorator for converting CRLF and CR to LF
03280  *     :newline => :crlf          # decorator for converting LF to CRLF
03281  *     :newline => :cr            # decorator for converting LF to CR
03282  *     :universal_newline => true # decorator for converting CRLF and CR to LF
03283  *     :crlf_newline => true      # decorator for converting LF to CRLF
03284  *     :cr_newline => true        # decorator for converting LF to CR
03285  *     :xml => :text              # escape as XML CharData.
03286  *     :xml => :attr              # escape as XML AttValue
03287  *   integer form:
03288  *     Encoding::Converter::INVALID_REPLACE
03289  *     Encoding::Converter::UNDEF_REPLACE
03290  *     Encoding::Converter::UNDEF_HEX_CHARREF
03291  *     Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR
03292  *     Encoding::Converter::CRLF_NEWLINE_DECORATOR
03293  *     Encoding::Converter::CR_NEWLINE_DECORATOR
03294  *     Encoding::Converter::XML_TEXT_DECORATOR
03295  *     Encoding::Converter::XML_ATTR_CONTENT_DECORATOR
03296  *     Encoding::Converter::XML_ATTR_QUOTE_DECORATOR
03297  *
03298  * Encoding::Converter.new creates an instance of Encoding::Converter.
03299  *
03300  * Source_encoding and destination_encoding should be a string or
03301  * Encoding object.
03302  *
03303  * opt should be nil, a hash or an integer.
03304  *
03305  * convpath should be an array.
03306  * convpath may contain
03307  * - two-element arrays which contain encodings or encoding names, or
03308  * - strings representing decorator names.
03309  *
03310  * Encoding::Converter.new optionally takes an option.
03311  * The option should be a hash or an integer.
03312  * The option hash can contain :invalid => nil, etc.
03313  * The option integer should be logical-or of constants such as
03314  * Encoding::Converter::INVALID_REPLACE, etc.
03315  *
03316  * [:invalid => nil]
03317  *   Raise error on invalid byte sequence.  This is a default behavior.
03318  * [:invalid => :replace]
03319  *   Replace invalid byte sequence by replacement string.
03320  * [:undef => nil]
03321  *   Raise an error if a character in source_encoding is not defined in destination_encoding.
03322  *   This is a default behavior.
03323  * [:undef => :replace]
03324  *   Replace undefined character in destination_encoding with replacement string.
03325  * [:replace => string]
03326  *   Specify the replacement string.
03327  *   If not specified, "\uFFFD" is used for Unicode encodings and "?" for others.
03328  * [:universal_newline => true]
03329  *   Convert CRLF and CR to LF.
03330  * [:crlf_newline => true]
03331  *   Convert LF to CRLF.
03332  * [:cr_newline => true]
03333  *   Convert LF to CR.
03334  * [:xml => :text]
03335  *   Escape as XML CharData.
03336  *   This form can be used as a HTML 4.0 #PCDATA.
03337  *   - '&' -> '&amp;'
03338  *   - '<' -> '&lt;'
03339  *   - '>' -> '&gt;'
03340  *   - undefined characters in destination_encoding -> hexadecimal CharRef such as &#xHH;
03341  * [:xml => :attr]
03342  *   Escape as XML AttValue.
03343  *   The converted result is quoted as "...".
03344  *   This form can be used as a HTML 4.0 attribute value.
03345  *   - '&' -> '&amp;'
03346  *   - '<' -> '&lt;'
03347  *   - '>' -> '&gt;'
03348  *   - '"' -> '&quot;'
03349  *   - undefined characters in destination_encoding -> hexadecimal CharRef such as &#xHH;
03350  *
03351  * Examples:
03352  *   # UTF-16BE to UTF-8
03353  *   ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
03354  *
03355  *   # Usually, decorators such as newline conversion are inserted last.
03356  *   ec = Encoding::Converter.new("UTF-16BE", "UTF-8", :universal_newline => true)
03357  *   p ec.convpath #=> [[#<Encoding:UTF-16BE>, #<Encoding:UTF-8>],
03358  *                 #    "universal_newline"]
03359  *
03360  *   # But, if the last encoding is ASCII incompatible,
03361  *   # decorators are inserted before the last conversion.
03362  *   ec = Encoding::Converter.new("UTF-8", "UTF-16BE", :crlf_newline => true)
03363  *   p ec.convpath #=> ["crlf_newline",
03364  *                 #    [#<Encoding:UTF-8>, #<Encoding:UTF-16BE>]]
03365  *
03366  *   # Conversion path can be specified directly.
03367  *   ec = Encoding::Converter.new(["universal_newline", ["EUC-JP", "UTF-8"], ["UTF-8", "UTF-16BE"]])
03368  *   p ec.convpath #=> ["universal_newline",
03369  *                 #    [#<Encoding:EUC-JP>, #<Encoding:UTF-8>],
03370  *                 #    [#<Encoding:UTF-8>, #<Encoding:UTF-16BE>]]
03371  */
03372 static VALUE
03373 econv_init(int argc, VALUE *argv, VALUE self)
03374 {
03375     VALUE ecopts;
03376     volatile VALUE snamev, dnamev;
03377     const char *sname, *dname;
03378     rb_encoding *senc, *denc;
03379     rb_econv_t *ec;
03380     int ecflags;
03381     VALUE convpath;
03382 
03383     if (rb_check_typeddata(self, &econv_data_type)) {
03384         rb_raise(rb_eTypeError, "already initialized");
03385     }
03386 
03387     if (argc == 1 && !NIL_P(convpath = rb_check_array_type(argv[0]))) {
03388         ec = rb_econv_init_by_convpath(self, convpath, &sname, &dname, &senc, &denc);
03389         ecflags = 0;
03390         ecopts = Qnil;
03391     }
03392     else {
03393         econv_args(argc, argv, &snamev, &dnamev, &sname, &dname, &senc, &denc, &ecflags, &ecopts);
03394         ec = rb_econv_open_opts(sname, dname, ecflags, ecopts);
03395     }
03396 
03397     if (!ec) {
03398         rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags));
03399     }
03400 
03401     if (!DECORATOR_P(sname, dname)) {
03402         if (!senc)
03403             senc = make_dummy_encoding(sname);
03404         if (!denc)
03405             denc = make_dummy_encoding(dname);
03406     }
03407 
03408     ec->source_encoding = senc;
03409     ec->destination_encoding = denc;
03410 
03411     DATA_PTR(self) = ec;
03412 
03413     return self;
03414 }
03415 
03416 /*
03417  * call-seq:
03418  *   ec.inspect         -> string
03419  *
03420  * Returns a printable version of <i>ec</i>
03421  *
03422  *   ec = Encoding::Converter.new("iso-8859-1", "utf-8")
03423  *   puts ec.inspect    #=> #<Encoding::Converter: ISO-8859-1 to UTF-8>
03424  *
03425  */
03426 static VALUE
03427 econv_inspect(VALUE self)
03428 {
03429     const char *cname = rb_obj_classname(self);
03430     rb_econv_t *ec;
03431 
03432     TypedData_Get_Struct(self, rb_econv_t, &econv_data_type, ec);
03433     if (!ec)
03434         return rb_sprintf("#<%s: uninitialized>", cname);
03435     else {
03436         const char *sname = ec->source_encoding_name;
03437         const char *dname = ec->destination_encoding_name;
03438         VALUE str;
03439         str = rb_sprintf("#<%s: ", cname);
03440         econv_description(sname, dname, ec->flags, str);
03441         rb_str_cat2(str, ">");
03442         return str;
03443     }
03444 }
03445 
03446 static rb_econv_t *
03447 check_econv(VALUE self)
03448 {
03449     rb_econv_t *ec;
03450 
03451     TypedData_Get_Struct(self, rb_econv_t, &econv_data_type, ec);
03452     if (!ec) {
03453         rb_raise(rb_eTypeError, "uninitialized encoding converter");
03454     }
03455     return ec;
03456 }
03457 
03458 /*
03459  * call-seq:
03460  *   ec.source_encoding -> encoding
03461  *
03462  * Returns the source encoding as an Encoding object.
03463  */
03464 static VALUE
03465 econv_source_encoding(VALUE self)
03466 {
03467     rb_econv_t *ec = check_econv(self);
03468     if (!ec->source_encoding)
03469         return Qnil;
03470     return rb_enc_from_encoding(ec->source_encoding);
03471 }
03472 
03473 /*
03474  * call-seq:
03475  *   ec.destination_encoding -> encoding
03476  *
03477  * Returns the destination encoding as an Encoding object.
03478  */
03479 static VALUE
03480 econv_destination_encoding(VALUE self)
03481 {
03482     rb_econv_t *ec = check_econv(self);
03483     if (!ec->destination_encoding)
03484         return Qnil;
03485     return rb_enc_from_encoding(ec->destination_encoding);
03486 }
03487 
03488 /*
03489  * call-seq:
03490  *   ec.convpath        -> ary
03491  *
03492  * Returns the conversion path of ec.
03493  *
03494  * The result is an array of conversions.
03495  *
03496  *   ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP", crlf_newline: true)
03497  *   p ec.convpath
03498  *   #=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
03499  *   #    [#<Encoding:UTF-8>, #<Encoding:EUC-JP>],
03500  *   #    "crlf_newline"]
03501  *
03502  * Each element of the array is a pair of encodings or a string.
03503  * A pair means an encoding conversion.
03504  * A string means a decorator.
03505  *
03506  * In the above example, [#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>] means
03507  * a converter from ISO-8859-1 to UTF-8.
03508  * "crlf_newline" means newline converter from LF to CRLF.
03509  */
03510 static VALUE
03511 econv_convpath(VALUE self)
03512 {
03513     rb_econv_t *ec = check_econv(self);
03514     VALUE result;
03515     int i;
03516 
03517     result = rb_ary_new();
03518     for (i = 0; i < ec->num_trans; i++) {
03519         const rb_transcoder *tr = ec->elems[i].tc->transcoder;
03520         VALUE v;
03521         if (DECORATOR_P(tr->src_encoding, tr->dst_encoding))
03522             v = rb_str_new_cstr(tr->dst_encoding);
03523         else
03524             v = rb_assoc_new(make_encobj(tr->src_encoding), make_encobj(tr->dst_encoding));
03525         rb_ary_push(result, v);
03526     }
03527     return result;
03528 }
03529 
03530 /*
03531  * call-seq:
03532  *   ec == other        -> true or false
03533  */
03534 static VALUE
03535 econv_equal(VALUE self, VALUE other)
03536 {
03537     rb_econv_t *ec1 = check_econv(self);
03538     rb_econv_t *ec2;
03539     int i;
03540 
03541     if (!rb_typeddata_is_kind_of(other, &econv_data_type)) {
03542         return Qnil;
03543     }
03544     ec2 = DATA_PTR(other);
03545     if (!ec2) return Qfalse;
03546     if (ec1->source_encoding_name != ec2->source_encoding_name &&
03547         strcmp(ec1->source_encoding_name, ec2->source_encoding_name))
03548         return Qfalse;
03549     if (ec1->destination_encoding_name != ec2->destination_encoding_name &&
03550         strcmp(ec1->destination_encoding_name, ec2->destination_encoding_name))
03551         return Qfalse;
03552     if (ec1->flags != ec2->flags) return Qfalse;
03553     if (ec1->replacement_enc != ec2->replacement_enc &&
03554         strcmp(ec1->replacement_enc, ec2->replacement_enc))
03555         return Qfalse;
03556     if (ec1->replacement_len != ec2->replacement_len) return Qfalse;
03557     if (ec1->replacement_str != ec2->replacement_str &&
03558         memcmp(ec1->replacement_str, ec2->replacement_str, ec2->replacement_len))
03559         return Qfalse;
03560 
03561     if (ec1->num_trans != ec2->num_trans) return Qfalse;
03562     for (i = 0; i < ec1->num_trans; i++) {
03563         if (ec1->elems[i].tc->transcoder != ec2->elems[i].tc->transcoder)
03564             return Qfalse;
03565     }
03566     return Qtrue;
03567 }
03568 
03569 static VALUE
03570 econv_result_to_symbol(rb_econv_result_t res)
03571 {
03572     switch (res) {
03573       case econv_invalid_byte_sequence: return sym_invalid_byte_sequence;
03574       case econv_incomplete_input: return sym_incomplete_input;
03575       case econv_undefined_conversion: return sym_undefined_conversion;
03576       case econv_destination_buffer_full: return sym_destination_buffer_full;
03577       case econv_source_buffer_empty: return sym_source_buffer_empty;
03578       case econv_finished: return sym_finished;
03579       case econv_after_output: return sym_after_output;
03580       default: return INT2NUM(res); /* should not be reached */
03581     }
03582 }
03583 
03584 /*
03585  * call-seq:
03586  *   ec.primitive_convert(source_buffer, destination_buffer) -> symbol
03587  *   ec.primitive_convert(source_buffer, destination_buffer, destination_byteoffset) -> symbol
03588  *   ec.primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize) -> symbol
03589  *   ec.primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize, opt) -> symbol
03590  *
03591  * possible opt elements:
03592  *   hash form:
03593  *     :partial_input => true           # source buffer may be part of larger source
03594  *     :after_output => true            # stop conversion after output before input
03595  *   integer form:
03596  *     Encoding::Converter::PARTIAL_INPUT
03597  *     Encoding::Converter::AFTER_OUTPUT
03598  *
03599  * possible results:
03600  *    :invalid_byte_sequence
03601  *    :incomplete_input
03602  *    :undefined_conversion
03603  *    :after_output
03604  *    :destination_buffer_full
03605  *    :source_buffer_empty
03606  *    :finished
03607  *
03608  * primitive_convert converts source_buffer into destination_buffer.
03609  *
03610  * source_buffer should be a string or nil.
03611  * nil means an empty string.
03612  *
03613  * destination_buffer should be a string.
03614  *
03615  * destination_byteoffset should be an integer or nil.
03616  * nil means the end of destination_buffer.
03617  * If it is omitted, nil is assumed.
03618  *
03619  * destination_bytesize should be an integer or nil.
03620  * nil means unlimited.
03621  * If it is omitted, nil is assumed.
03622  *
03623  * opt should be nil, a hash or an integer.
03624  * nil means no flags.
03625  * If it is omitted, nil is assumed.
03626  *
03627  * primitive_convert converts the content of source_buffer from beginning
03628  * and store the result into destination_buffer.
03629  *
03630  * destination_byteoffset and destination_bytesize specify the region which
03631  * the converted result is stored.
03632  * destination_byteoffset specifies the start position in destination_buffer in bytes.
03633  * If destination_byteoffset is nil,
03634  * destination_buffer.bytesize is used for appending the result.
03635  * destination_bytesize specifies maximum number of bytes.
03636  * If destination_bytesize is nil,
03637  * destination size is unlimited.
03638  * After conversion, destination_buffer is resized to
03639  * destination_byteoffset + actually produced number of bytes.
03640  * Also destination_buffer's encoding is set to destination_encoding.
03641  *
03642  * primitive_convert drops the converted part of source_buffer.
03643  * the dropped part is converted in destination_buffer or
03644  * buffered in Encoding::Converter object.
03645  *
03646  * primitive_convert stops conversion when one of following condition met.
03647  * - invalid byte sequence found in source buffer (:invalid_byte_sequence)
03648  *   +primitive_errinfo+ and +last_error+ methods returns the detail of the error.
03649  * - unexpected end of source buffer (:incomplete_input)
03650  *   this occur only when :partial_input is not specified.
03651  *   +primitive_errinfo+ and +last_error+ methods returns the detail of the error.
03652  * - character not representable in output encoding (:undefined_conversion)
03653  *   +primitive_errinfo+ and +last_error+ methods returns the detail of the error.
03654  * - after some output is generated, before input is done (:after_output)
03655  *   this occur only when :after_output is specified.
03656  * - destination buffer is full (:destination_buffer_full)
03657  *   this occur only when destination_bytesize is non-nil.
03658  * - source buffer is empty (:source_buffer_empty)
03659  *   this occur only when :partial_input is specified.
03660  * - conversion is finished (:finished)
03661  *
03662  * example:
03663  *   ec = Encoding::Converter.new("UTF-8", "UTF-16BE")
03664  *   ret = ec.primitive_convert(src="pi", dst="", nil, 100)
03665  *   p [ret, src, dst] #=> [:finished, "", "\x00p\x00i"]
03666  *
03667  *   ec = Encoding::Converter.new("UTF-8", "UTF-16BE")
03668  *   ret = ec.primitive_convert(src="pi", dst="", nil, 1)
03669  *   p [ret, src, dst] #=> [:destination_buffer_full, "i", "\x00"]
03670  *   ret = ec.primitive_convert(src, dst="", nil, 1)
03671  *   p [ret, src, dst] #=> [:destination_buffer_full, "", "p"]
03672  *   ret = ec.primitive_convert(src, dst="", nil, 1)
03673  *   p [ret, src, dst] #=> [:destination_buffer_full, "", "\x00"]
03674  *   ret = ec.primitive_convert(src, dst="", nil, 1)
03675  *   p [ret, src, dst] #=> [:finished, "", "i"]
03676  *
03677  */
03678 static VALUE
03679 econv_primitive_convert(int argc, VALUE *argv, VALUE self)
03680 {
03681     VALUE input, output, output_byteoffset_v, output_bytesize_v, opt, flags_v;
03682     rb_econv_t *ec = check_econv(self);
03683     rb_econv_result_t res;
03684     const unsigned char *ip, *is;
03685     unsigned char *op, *os;
03686     long output_byteoffset, output_bytesize;
03687     unsigned long output_byteend;
03688     int flags;
03689 
03690     argc = rb_scan_args(argc, argv, "23:", &input, &output, &output_byteoffset_v, &output_bytesize_v, &flags_v, &opt);
03691 
03692     if (NIL_P(output_byteoffset_v))
03693         output_byteoffset = 0; /* dummy */
03694     else
03695         output_byteoffset = NUM2LONG(output_byteoffset_v);
03696 
03697     if (NIL_P(output_bytesize_v))
03698         output_bytesize = 0; /* dummy */
03699     else
03700         output_bytesize = NUM2LONG(output_bytesize_v);
03701 
03702     if (!NIL_P(flags_v)) {
03703         if (!NIL_P(opt)) {
03704             rb_error_arity(argc + 1, 2, 5);
03705         }
03706         flags = NUM2INT(rb_to_int(flags_v));
03707     }
03708     else if (!NIL_P(opt)) {
03709         VALUE v;
03710         flags = 0;
03711         v = rb_hash_aref(opt, sym_partial_input);
03712         if (RTEST(v))
03713             flags |= ECONV_PARTIAL_INPUT;
03714         v = rb_hash_aref(opt, sym_after_output);
03715         if (RTEST(v))
03716             flags |= ECONV_AFTER_OUTPUT;
03717     }
03718     else {
03719         flags = 0;
03720     }
03721 
03722     StringValue(output);
03723     if (!NIL_P(input))
03724         StringValue(input);
03725     rb_str_modify(output);
03726 
03727     if (NIL_P(output_bytesize_v)) {
03728         output_bytesize = RSTRING_EMBED_LEN_MAX;
03729         if (!NIL_P(input) && output_bytesize < RSTRING_LEN(input))
03730             output_bytesize = RSTRING_LEN(input);
03731     }
03732 
03733   retry:
03734 
03735     if (NIL_P(output_byteoffset_v))
03736         output_byteoffset = RSTRING_LEN(output);
03737 
03738     if (output_byteoffset < 0)
03739         rb_raise(rb_eArgError, "negative output_byteoffset");
03740 
03741     if (RSTRING_LEN(output) < output_byteoffset)
03742         rb_raise(rb_eArgError, "output_byteoffset too big");
03743 
03744     if (output_bytesize < 0)
03745         rb_raise(rb_eArgError, "negative output_bytesize");
03746 
03747     output_byteend = (unsigned long)output_byteoffset +
03748                      (unsigned long)output_bytesize;
03749 
03750     if (output_byteend < (unsigned long)output_byteoffset ||
03751         LONG_MAX < output_byteend)
03752         rb_raise(rb_eArgError, "output_byteoffset+output_bytesize too big");
03753 
03754     if (rb_str_capacity(output) < output_byteend)
03755         rb_str_resize(output, output_byteend);
03756 
03757     if (NIL_P(input)) {
03758         ip = is = NULL;
03759     }
03760     else {
03761         ip = (const unsigned char *)RSTRING_PTR(input);
03762         is = ip + RSTRING_LEN(input);
03763     }
03764 
03765     op = (unsigned char *)RSTRING_PTR(output) + output_byteoffset;
03766     os = op + output_bytesize;
03767 
03768     res = rb_econv_convert(ec, &ip, is, &op, os, flags);
03769     rb_str_set_len(output, op-(unsigned char *)RSTRING_PTR(output));
03770     if (!NIL_P(input))
03771         rb_str_drop_bytes(input, ip - (unsigned char *)RSTRING_PTR(input));
03772 
03773     if (NIL_P(output_bytesize_v) && res == econv_destination_buffer_full) {
03774         if (LONG_MAX / 2 < output_bytesize)
03775             rb_raise(rb_eArgError, "too long conversion result");
03776         output_bytesize *= 2;
03777         output_byteoffset_v = Qnil;
03778         goto retry;
03779     }
03780 
03781     if (ec->destination_encoding) {
03782         rb_enc_associate(output, ec->destination_encoding);
03783     }
03784 
03785     return econv_result_to_symbol(res);
03786 }
03787 
03788 /*
03789  * call-seq:
03790  *   ec.convert(source_string) -> destination_string
03791  *
03792  * Convert source_string and return destination_string.
03793  *
03794  * source_string is assumed as a part of source.
03795  * i.e.  :partial_input=>true is specified internally.
03796  * finish method should be used last.
03797  *
03798  *   ec = Encoding::Converter.new("utf-8", "euc-jp")
03799  *   puts ec.convert("\u3042").dump     #=> "\xA4\xA2"
03800  *   puts ec.finish.dump                #=> ""
03801  *
03802  *   ec = Encoding::Converter.new("euc-jp", "utf-8")
03803  *   puts ec.convert("\xA4").dump       #=> ""
03804  *   puts ec.convert("\xA2").dump       #=> "\xE3\x81\x82"
03805  *   puts ec.finish.dump                #=> ""
03806  *
03807  *   ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
03808  *   puts ec.convert("\xE3").dump       #=> "".force_encoding("ISO-2022-JP")
03809  *   puts ec.convert("\x81").dump       #=> "".force_encoding("ISO-2022-JP")
03810  *   puts ec.convert("\x82").dump       #=> "\e$B$\"".force_encoding("ISO-2022-JP")
03811  *   puts ec.finish.dump                #=> "\e(B".force_encoding("ISO-2022-JP")
03812  *
03813  * If a conversion error occur,
03814  * Encoding::UndefinedConversionError or
03815  * Encoding::InvalidByteSequenceError is raised.
03816  * Encoding::Converter#convert doesn't supply methods to recover or restart
03817  * from these exceptions.
03818  * When you want to handle these conversion errors,
03819  * use Encoding::Converter#primitive_convert.
03820  *
03821  */
03822 static VALUE
03823 econv_convert(VALUE self, VALUE source_string)
03824 {
03825     VALUE ret, dst;
03826     VALUE av[5];
03827     int ac;
03828     rb_econv_t *ec = check_econv(self);
03829 
03830     StringValue(source_string);
03831 
03832     dst = rb_str_new(NULL, 0);
03833 
03834     av[0] = rb_str_dup(source_string);
03835     av[1] = dst;
03836     av[2] = Qnil;
03837     av[3] = Qnil;
03838     av[4] = INT2NUM(ECONV_PARTIAL_INPUT);
03839     ac = 5;
03840 
03841     ret = econv_primitive_convert(ac, av, self);
03842 
03843     if (ret == sym_invalid_byte_sequence ||
03844         ret == sym_undefined_conversion ||
03845         ret == sym_incomplete_input) {
03846         VALUE exc = make_econv_exception(ec);
03847         rb_exc_raise(exc);
03848     }
03849 
03850     if (ret == sym_finished) {
03851         rb_raise(rb_eArgError, "converter already finished");
03852     }
03853 
03854     if (ret != sym_source_buffer_empty) {
03855         rb_bug("unexpected result of econv_primitive_convert");
03856     }
03857 
03858     return dst;
03859 }
03860 
03861 /*
03862  * call-seq:
03863  *   ec.finish -> string
03864  *
03865  * Finishes the converter.
03866  * It returns the last part of the converted string.
03867  *
03868  *   ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
03869  *   p ec.convert("\u3042")     #=> "\e$B$\""
03870  *   p ec.finish                #=> "\e(B"
03871  */
03872 static VALUE
03873 econv_finish(VALUE self)
03874 {
03875     VALUE ret, dst;
03876     VALUE av[5];
03877     int ac;
03878     rb_econv_t *ec = check_econv(self);
03879 
03880     dst = rb_str_new(NULL, 0);
03881 
03882     av[0] = Qnil;
03883     av[1] = dst;
03884     av[2] = Qnil;
03885     av[3] = Qnil;
03886     av[4] = INT2FIX(0);
03887     ac = 5;
03888 
03889     ret = econv_primitive_convert(ac, av, self);
03890 
03891     if (ret == sym_invalid_byte_sequence ||
03892         ret == sym_undefined_conversion ||
03893         ret == sym_incomplete_input) {
03894         VALUE exc = make_econv_exception(ec);
03895         rb_exc_raise(exc);
03896     }
03897 
03898     if (ret != sym_finished) {
03899         rb_bug("unexpected result of econv_primitive_convert");
03900     }
03901 
03902     return dst;
03903 }
03904 
03905 /*
03906  * call-seq:
03907  *   ec.primitive_errinfo -> array
03908  *
03909  * primitive_errinfo returns important information regarding the last error
03910  * as a 5-element array:
03911  *
03912  *   [result, enc1, enc2, error_bytes, readagain_bytes]
03913  *
03914  * result is the last result of primitive_convert.
03915  *
03916  * Other elements are only meaningful when result is
03917  * :invalid_byte_sequence, :incomplete_input or :undefined_conversion.
03918  *
03919  * enc1 and enc2 indicate a conversion step as a pair of strings.
03920  * For example, a converter from EUC-JP to ISO-8859-1 converts
03921  * a string as follows: EUC-JP -> UTF-8 -> ISO-8859-1.
03922  * So [enc1, enc2] is either ["EUC-JP", "UTF-8"] or ["UTF-8", "ISO-8859-1"].
03923  *
03924  * error_bytes and readagain_bytes indicate the byte sequences which caused the error.
03925  * error_bytes is discarded portion.
03926  * readagain_bytes is buffered portion which is read again on next conversion.
03927  *
03928  * Example:
03929  *
03930  *   # \xff is invalid as EUC-JP.
03931  *   ec = Encoding::Converter.new("EUC-JP", "Shift_JIS")
03932  *   ec.primitive_convert(src="\xff", dst="", nil, 10)
03933  *   p ec.primitive_errinfo
03934  *   #=> [:invalid_byte_sequence, "EUC-JP", "UTF-8", "\xFF", ""]
03935  *
03936  *   # HIRAGANA LETTER A (\xa4\xa2 in EUC-JP) is not representable in ISO-8859-1.
03937  *   # Since this error is occur in UTF-8 to ISO-8859-1 conversion,
03938  *   # error_bytes is HIRAGANA LETTER A in UTF-8 (\xE3\x81\x82).
03939  *   ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
03940  *   ec.primitive_convert(src="\xa4\xa2", dst="", nil, 10)
03941  *   p ec.primitive_errinfo
03942  *   #=> [:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE3\x81\x82", ""]
03943  *
03944  *   # partial character is invalid
03945  *   ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
03946  *   ec.primitive_convert(src="\xa4", dst="", nil, 10)
03947  *   p ec.primitive_errinfo
03948  *   #=> [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""]
03949  *
03950  *   # Encoding::Converter::PARTIAL_INPUT prevents invalid errors by
03951  *   # partial characters.
03952  *   ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
03953  *   ec.primitive_convert(src="\xa4", dst="", nil, 10, Encoding::Converter::PARTIAL_INPUT)
03954  *   p ec.primitive_errinfo
03955  *   #=> [:source_buffer_empty, nil, nil, nil, nil]
03956  *
03957  *   # \xd8\x00\x00@ is invalid as UTF-16BE because
03958  *   # no low surrogate after high surrogate (\xd8\x00).
03959  *   # It is detected by 3rd byte (\00) which is part of next character.
03960  *   # So the high surrogate (\xd8\x00) is discarded and
03961  *   # the 3rd byte is read again later.
03962  *   # Since the byte is buffered in ec, it is dropped from src.
03963  *   ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
03964  *   ec.primitive_convert(src="\xd8\x00\x00@", dst="", nil, 10)
03965  *   p ec.primitive_errinfo
03966  *   #=> [:invalid_byte_sequence, "UTF-16BE", "UTF-8", "\xD8\x00", "\x00"]
03967  *   p src
03968  *   #=> "@"
03969  *
03970  *   # Similar to UTF-16BE, \x00\xd8@\x00 is invalid as UTF-16LE.
03971  *   # The problem is detected by 4th byte.
03972  *   ec = Encoding::Converter.new("UTF-16LE", "UTF-8")
03973  *   ec.primitive_convert(src="\x00\xd8@\x00", dst="", nil, 10)
03974  *   p ec.primitive_errinfo
03975  *   #=> [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "@\x00"]
03976  *   p src
03977  *   #=> ""
03978  *
03979  */
03980 static VALUE
03981 econv_primitive_errinfo(VALUE self)
03982 {
03983     rb_econv_t *ec = check_econv(self);
03984 
03985     VALUE ary;
03986 
03987     ary = rb_ary_new2(5);
03988 
03989     rb_ary_store(ary, 0, econv_result_to_symbol(ec->last_error.result));
03990     rb_ary_store(ary, 4, Qnil);
03991 
03992     if (ec->last_error.source_encoding)
03993         rb_ary_store(ary, 1, rb_str_new2(ec->last_error.source_encoding));
03994 
03995     if (ec->last_error.destination_encoding)
03996         rb_ary_store(ary, 2, rb_str_new2(ec->last_error.destination_encoding));
03997 
03998     if (ec->last_error.error_bytes_start) {
03999         rb_ary_store(ary, 3, rb_str_new((const char *)ec->last_error.error_bytes_start, ec->last_error.error_bytes_len));
04000         rb_ary_store(ary, 4, rb_str_new((const char *)ec->last_error.error_bytes_start + ec->last_error.error_bytes_len, ec->last_error.readagain_len));
04001     }
04002 
04003     return ary;
04004 }
04005 
04006 /*
04007  * call-seq:
04008  *   ec.insert_output(string) -> nil
04009  *
04010  * Inserts string into the encoding converter.
04011  * The string will be converted to the destination encoding and
04012  * output on later conversions.
04013  *
04014  * If the destination encoding is stateful,
04015  * string is converted according to the state and the state is updated.
04016  *
04017  * This method should be used only when a conversion error occurs.
04018  *
04019  *  ec = Encoding::Converter.new("utf-8", "iso-8859-1")
04020  *  src = "HIRAGANA LETTER A is \u{3042}."
04021  *  dst = ""
04022  *  p ec.primitive_convert(src, dst)    #=> :undefined_conversion
04023  *  puts "[#{dst.dump}, #{src.dump}]"   #=> ["HIRAGANA LETTER A is ", "."]
04024  *  ec.insert_output("<err>")
04025  *  p ec.primitive_convert(src, dst)    #=> :finished
04026  *  puts "[#{dst.dump}, #{src.dump}]"   #=> ["HIRAGANA LETTER A is <err>.", ""]
04027  *
04028  *  ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
04029  *  src = "\u{306F 3041 3068 2661 3002}" # U+2661 is not representable in iso-2022-jp
04030  *  dst = ""
04031  *  p ec.primitive_convert(src, dst)    #=> :undefined_conversion
04032  *  puts "[#{dst.dump}, #{src.dump}]"   #=> ["\e$B$O$!$H".force_encoding("ISO-2022-JP"), "\xE3\x80\x82"]
04033  *  ec.insert_output "?"                # state change required to output "?".
04034  *  p ec.primitive_convert(src, dst)    #=> :finished
04035  *  puts "[#{dst.dump}, #{src.dump}]"   #=> ["\e$B$O$!$H\e(B?\e$B!#\e(B".force_encoding("ISO-2022-JP"), ""]
04036  *
04037  */
04038 static VALUE
04039 econv_insert_output(VALUE self, VALUE string)
04040 {
04041     const char *insert_enc;
04042 
04043     int ret;
04044 
04045     rb_econv_t *ec = check_econv(self);
04046 
04047     StringValue(string);
04048     insert_enc = rb_econv_encoding_to_insert_output(ec);
04049     string = rb_str_encode(string, rb_enc_from_encoding(rb_enc_find(insert_enc)), 0, Qnil);
04050 
04051     ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(string), RSTRING_LEN(string), insert_enc);
04052     if (ret == -1) {
04053         rb_raise(rb_eArgError, "too big string");
04054     }
04055 
04056     return Qnil;
04057 }
04058 
04059 /*
04060  * call-seq
04061  *   ec.putback                    -> string
04062  *   ec.putback(max_numbytes)      -> string
04063  *
04064  * Put back the bytes which will be converted.
04065  *
04066  * The bytes are caused by invalid_byte_sequence error.
04067  * When invalid_byte_sequence error, some bytes are discarded and
04068  * some bytes are buffered to be converted later.
04069  * The latter bytes can be put back.
04070  * It can be observed by
04071  * Encoding::InvalidByteSequenceError#readagain_bytes and
04072  * Encoding::Converter#primitive_errinfo.
04073  *
04074  *   ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
04075  *   src = "\x00\xd8\x61\x00"
04076  *   dst = ""
04077  *   p ec.primitive_convert(src, dst)   #=> :invalid_byte_sequence
04078  *   p ec.primitive_errinfo     #=> [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
04079  *   p ec.putback               #=> "a\x00"
04080  *   p ec.putback               #=> ""          # no more bytes to put back
04081  *
04082  */
04083 static VALUE
04084 econv_putback(int argc, VALUE *argv, VALUE self)
04085 {
04086     rb_econv_t *ec = check_econv(self);
04087     int n;
04088     int putbackable;
04089     VALUE str, max;
04090 
04091     rb_scan_args(argc, argv, "01", &max);
04092 
04093     if (NIL_P(max))
04094         n = rb_econv_putbackable(ec);
04095     else {
04096         n = NUM2INT(max);
04097         putbackable = rb_econv_putbackable(ec);
04098         if (putbackable < n)
04099             n = putbackable;
04100     }
04101 
04102     str = rb_str_new(NULL, n);
04103     rb_econv_putback(ec, (unsigned char *)RSTRING_PTR(str), n);
04104 
04105     if (ec->source_encoding) {
04106         rb_enc_associate(str, ec->source_encoding);
04107     }
04108 
04109     return str;
04110 }
04111 
04112 /*
04113  * call-seq:
04114  *   ec.last_error -> exception or nil
04115  *
04116  * Returns an exception object for the last conversion.
04117  * Returns nil if the last conversion did not produce an error.
04118  *
04119  * "error" means that
04120  * Encoding::InvalidByteSequenceError and Encoding::UndefinedConversionError for
04121  * Encoding::Converter#convert and
04122  * :invalid_byte_sequence, :incomplete_input and :undefined_conversion for
04123  * Encoding::Converter#primitive_convert.
04124  *
04125  *  ec = Encoding::Converter.new("utf-8", "iso-8859-1")
04126  *  p ec.primitive_convert(src="\xf1abcd", dst="")       #=> :invalid_byte_sequence
04127  *  p ec.last_error      #=> #<Encoding::InvalidByteSequenceError: "\xF1" followed by "a" on UTF-8>
04128  *  p ec.primitive_convert(src, dst, nil, 1)             #=> :destination_buffer_full
04129  *  p ec.last_error      #=> nil
04130  *
04131  */
04132 static VALUE
04133 econv_last_error(VALUE self)
04134 {
04135     rb_econv_t *ec = check_econv(self);
04136     VALUE exc;
04137 
04138     exc = make_econv_exception(ec);
04139     if (NIL_P(exc))
04140         return Qnil;
04141     return exc;
04142 }
04143 
04144 /*
04145  * call-seq:
04146  *   ec.replacement -> string
04147  *
04148  * Returns the replacement string.
04149  *
04150  *  ec = Encoding::Converter.new("euc-jp", "us-ascii")
04151  *  p ec.replacement    #=> "?"
04152  *
04153  *  ec = Encoding::Converter.new("euc-jp", "utf-8")
04154  *  p ec.replacement    #=> "\uFFFD"
04155  */
04156 static VALUE
04157 econv_get_replacement(VALUE self)
04158 {
04159     rb_econv_t *ec = check_econv(self);
04160     int ret;
04161     rb_encoding *enc;
04162 
04163     ret = make_replacement(ec);
04164     if (ret == -1) {
04165         rb_raise(rb_eUndefinedConversionError, "replacement character setup failed");
04166     }
04167 
04168     enc = rb_enc_find(ec->replacement_enc);
04169     return rb_enc_str_new((const char *)ec->replacement_str, (long)ec->replacement_len, enc);
04170 }
04171 
04172 /*
04173  * call-seq:
04174  *   ec.replacement = string
04175  *
04176  * Sets the replacement string.
04177  *
04178  *  ec = Encoding::Converter.new("utf-8", "us-ascii", :undef => :replace)
04179  *  ec.replacement = "<undef>"
04180  *  p ec.convert("a \u3042 b")      #=> "a <undef> b"
04181  */
04182 static VALUE
04183 econv_set_replacement(VALUE self, VALUE arg)
04184 {
04185     rb_econv_t *ec = check_econv(self);
04186     VALUE string = arg;
04187     int ret;
04188     rb_encoding *enc;
04189 
04190     StringValue(string);
04191     enc = rb_enc_get(string);
04192 
04193     ret = rb_econv_set_replacement(ec,
04194             (const unsigned char *)RSTRING_PTR(string),
04195             RSTRING_LEN(string),
04196             rb_enc_name(enc));
04197 
04198     if (ret == -1) {
04199         /* xxx: rb_eInvalidByteSequenceError? */
04200         rb_raise(rb_eUndefinedConversionError, "replacement character setup failed");
04201     }
04202 
04203     return arg;
04204 }
04205 
04206 VALUE
04207 rb_econv_make_exception(rb_econv_t *ec)
04208 {
04209     return make_econv_exception(ec);
04210 }
04211 
04212 void
04213 rb_econv_check_error(rb_econv_t *ec)
04214 {
04215     VALUE exc;
04216 
04217     exc = make_econv_exception(ec);
04218     if (NIL_P(exc))
04219         return;
04220     rb_exc_raise(exc);
04221 }
04222 
04223 /*
04224  * call-seq:
04225  *   ecerr.source_encoding_name         -> string
04226  *
04227  * Returns the source encoding name as a string.
04228  */
04229 static VALUE
04230 ecerr_source_encoding_name(VALUE self)
04231 {
04232     return rb_attr_get(self, rb_intern("source_encoding_name"));
04233 }
04234 
04235 /*
04236  * call-seq:
04237  *   ecerr.source_encoding              -> encoding
04238  *
04239  * Returns the source encoding as an encoding object.
04240  *
04241  * Note that the result may not be equal to the source encoding of
04242  * the encoding converter if the conversion has multiple steps.
04243  *
04244  *  ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") # ISO-8859-1 -> UTF-8 -> EUC-JP
04245  *  begin
04246  *    ec.convert("\xa0") # NO-BREAK SPACE, which is available in UTF-8 but not in EUC-JP.
04247  *  rescue Encoding::UndefinedConversionError
04248  *    p $!.source_encoding              #=> #<Encoding:UTF-8>
04249  *    p $!.destination_encoding         #=> #<Encoding:EUC-JP>
04250  *    p $!.source_encoding_name         #=> "UTF-8"
04251  *    p $!.destination_encoding_name    #=> "EUC-JP"
04252  *  end
04253  *
04254  */
04255 static VALUE
04256 ecerr_source_encoding(VALUE self)
04257 {
04258     return rb_attr_get(self, rb_intern("source_encoding"));
04259 }
04260 
04261 /*
04262  * call-seq:
04263  *   ecerr.destination_encoding_name         -> string
04264  *
04265  * Returns the destination encoding name as a string.
04266  */
04267 static VALUE
04268 ecerr_destination_encoding_name(VALUE self)
04269 {
04270     return rb_attr_get(self, rb_intern("destination_encoding_name"));
04271 }
04272 
04273 /*
04274  * call-seq:
04275  *   ecerr.destination_encoding         -> string
04276  *
04277  * Returns the destination encoding as an encoding object.
04278  */
04279 static VALUE
04280 ecerr_destination_encoding(VALUE self)
04281 {
04282     return rb_attr_get(self, rb_intern("destination_encoding"));
04283 }
04284 
04285 /*
04286  * call-seq:
04287  *   ecerr.error_char         -> string
04288  *
04289  * Returns the one-character string which cause Encoding::UndefinedConversionError.
04290  *
04291  *  ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP")
04292  *  begin
04293  *    ec.convert("\xa0")
04294  *  rescue Encoding::UndefinedConversionError
04295  *    puts $!.error_char.dump   #=> "\xC2\xA0"
04296  *    p $!.error_char.encoding  #=> #<Encoding:UTF-8>
04297  *  end
04298  *
04299  */
04300 static VALUE
04301 ecerr_error_char(VALUE self)
04302 {
04303     return rb_attr_get(self, rb_intern("error_char"));
04304 }
04305 
04306 /*
04307  * call-seq:
04308  *   ecerr.error_bytes         -> string
04309  *
04310  * Returns the discarded bytes when Encoding::InvalidByteSequenceError occurs.
04311  *
04312  *  ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
04313  *  begin
04314  *    ec.convert("abc\xA1\xFFdef")
04315  *  rescue Encoding::InvalidByteSequenceError
04316  *    p $!      #=> #<Encoding::InvalidByteSequenceError: "\xA1" followed by "\xFF" on EUC-JP>
04317  *    puts $!.error_bytes.dump          #=> "\xA1"
04318  *    puts $!.readagain_bytes.dump      #=> "\xFF"
04319  *  end
04320  */
04321 static VALUE
04322 ecerr_error_bytes(VALUE self)
04323 {
04324     return rb_attr_get(self, rb_intern("error_bytes"));
04325 }
04326 
04327 /*
04328  * call-seq:
04329  *   ecerr.readagain_bytes         -> string
04330  *
04331  * Returns the bytes to be read again when Encoding::InvalidByteSequenceError occurs.
04332  */
04333 static VALUE
04334 ecerr_readagain_bytes(VALUE self)
04335 {
04336     return rb_attr_get(self, rb_intern("readagain_bytes"));
04337 }
04338 
04339 /*
04340  * call-seq:
04341  *   ecerr.incomplete_input?         -> true or false
04342  *
04343  * Returns true if the invalid byte sequence error is caused by
04344  * premature end of string.
04345  *
04346  *  ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
04347  *
04348  *  begin
04349  *    ec.convert("abc\xA1z")
04350  *  rescue Encoding::InvalidByteSequenceError
04351  *    p $!      #=> #<Encoding::InvalidByteSequenceError: "\xA1" followed by "z" on EUC-JP>
04352  *    p $!.incomplete_input?    #=> false
04353  *  end
04354  *
04355  *  begin
04356  *    ec.convert("abc\xA1")
04357  *    ec.finish
04358  *  rescue Encoding::InvalidByteSequenceError
04359  *    p $!      #=> #<Encoding::InvalidByteSequenceError: incomplete "\xA1" on EUC-JP>
04360  *    p $!.incomplete_input?    #=> true
04361  *  end
04362  */
04363 static VALUE
04364 ecerr_incomplete_input(VALUE self)
04365 {
04366     return rb_attr_get(self, rb_intern("incomplete_input"));
04367 }
04368 
04369 /*
04370  *  Document-class: Encoding::UndefinedConversionError
04371  *
04372  *  Raised by Encoding and String methods when a transcoding operation
04373  *  fails.
04374  */
04375 
04376 /*
04377  *  Document-class: Encoding::InvalidByteSequenceError
04378  *
04379  *  Raised by Encoding and String methods when the string being
04380  *  transcoded contains a byte invalid for the either the source or
04381  *  target encoding.
04382  */
04383 
04384 /*
04385  *  Document-class: Encoding::ConverterNotFoundError
04386  *
04387  *  Raised by transcoding methods when a named encoding does not
04388  *  correspond with a known converter.
04389  */
04390 
04391 void
04392 Init_transcode(void)
04393 {
04394     rb_eUndefinedConversionError = rb_define_class_under(rb_cEncoding, "UndefinedConversionError", rb_eEncodingError);
04395     rb_eInvalidByteSequenceError = rb_define_class_under(rb_cEncoding, "InvalidByteSequenceError", rb_eEncodingError);
04396     rb_eConverterNotFoundError = rb_define_class_under(rb_cEncoding, "ConverterNotFoundError", rb_eEncodingError);
04397 
04398     transcoder_table = st_init_strcasetable();
04399 
04400     sym_invalid = ID2SYM(rb_intern("invalid"));
04401     sym_undef = ID2SYM(rb_intern("undef"));
04402     sym_replace = ID2SYM(rb_intern("replace"));
04403     sym_fallback = ID2SYM(rb_intern("fallback"));
04404     sym_aref = ID2SYM(rb_intern("[]"));
04405     sym_xml = ID2SYM(rb_intern("xml"));
04406     sym_text = ID2SYM(rb_intern("text"));
04407     sym_attr = ID2SYM(rb_intern("attr"));
04408 
04409     sym_invalid_byte_sequence = ID2SYM(rb_intern("invalid_byte_sequence"));
04410     sym_undefined_conversion = ID2SYM(rb_intern("undefined_conversion"));
04411     sym_destination_buffer_full = ID2SYM(rb_intern("destination_buffer_full"));
04412     sym_source_buffer_empty = ID2SYM(rb_intern("source_buffer_empty"));
04413     sym_finished = ID2SYM(rb_intern("finished"));
04414     sym_after_output = ID2SYM(rb_intern("after_output"));
04415     sym_incomplete_input = ID2SYM(rb_intern("incomplete_input"));
04416     sym_universal_newline = ID2SYM(rb_intern("universal_newline"));
04417     sym_crlf_newline = ID2SYM(rb_intern("crlf_newline"));
04418     sym_cr_newline = ID2SYM(rb_intern("cr_newline"));
04419     sym_partial_input = ID2SYM(rb_intern("partial_input"));
04420 
04421 #ifdef ENABLE_ECONV_NEWLINE_OPTION
04422     sym_newline = ID2SYM(rb_intern("newline"));
04423     sym_universal = ID2SYM(rb_intern("universal"));
04424     sym_crlf = ID2SYM(rb_intern("crlf"));
04425     sym_cr = ID2SYM(rb_intern("cr"));
04426     sym_lf = ID2SYM(rb_intern("lf"));
04427 #endif
04428 
04429     rb_define_method(rb_cString, "encode", str_encode, -1);
04430     rb_define_method(rb_cString, "encode!", str_encode_bang, -1);
04431 
04432     rb_cEncodingConverter = rb_define_class_under(rb_cEncoding, "Converter", rb_cData);
04433     rb_define_alloc_func(rb_cEncodingConverter, econv_s_allocate);
04434     rb_define_singleton_method(rb_cEncodingConverter, "asciicompat_encoding", econv_s_asciicompat_encoding, 1);
04435     rb_define_singleton_method(rb_cEncodingConverter, "search_convpath", econv_s_search_convpath, -1);
04436     rb_define_method(rb_cEncodingConverter, "initialize", econv_init, -1);
04437     rb_define_method(rb_cEncodingConverter, "inspect", econv_inspect, 0);
04438     rb_define_method(rb_cEncodingConverter, "convpath", econv_convpath, 0);
04439     rb_define_method(rb_cEncodingConverter, "source_encoding", econv_source_encoding, 0);
04440     rb_define_method(rb_cEncodingConverter, "destination_encoding", econv_destination_encoding, 0);
04441     rb_define_method(rb_cEncodingConverter, "primitive_convert", econv_primitive_convert, -1);
04442     rb_define_method(rb_cEncodingConverter, "convert", econv_convert, 1);
04443     rb_define_method(rb_cEncodingConverter, "finish", econv_finish, 0);
04444     rb_define_method(rb_cEncodingConverter, "primitive_errinfo", econv_primitive_errinfo, 0);
04445     rb_define_method(rb_cEncodingConverter, "insert_output", econv_insert_output, 1);
04446     rb_define_method(rb_cEncodingConverter, "putback", econv_putback, -1);
04447     rb_define_method(rb_cEncodingConverter, "last_error", econv_last_error, 0);
04448     rb_define_method(rb_cEncodingConverter, "replacement", econv_get_replacement, 0);
04449     rb_define_method(rb_cEncodingConverter, "replacement=", econv_set_replacement, 1);
04450     rb_define_method(rb_cEncodingConverter, "==", econv_equal, 1);
04451 
04452     /* Document-const: INVALID_MASK
04453      *
04454      * Mask for invalid byte sequences
04455      */
04456     rb_define_const(rb_cEncodingConverter, "INVALID_MASK", INT2FIX(ECONV_INVALID_MASK));
04457 
04458     /* Document-const: INVALID_REPLACE
04459      *
04460      * Replace invalid byte sequences
04461      */
04462     rb_define_const(rb_cEncodingConverter, "INVALID_REPLACE", INT2FIX(ECONV_INVALID_REPLACE));
04463 
04464     /* Document-const: UNDEF_MASK
04465      *
04466      * Mask for a valid character in the source encoding but no related
04467      * character(s) in destination encoding.
04468      */
04469     rb_define_const(rb_cEncodingConverter, "UNDEF_MASK", INT2FIX(ECONV_UNDEF_MASK));
04470 
04471     /* Document-const: UNDEF_REPLACE
04472      *
04473      * Replace byte sequences that are undefined in the destination encoding.
04474      */
04475     rb_define_const(rb_cEncodingConverter, "UNDEF_REPLACE", INT2FIX(ECONV_UNDEF_REPLACE));
04476 
04477     /* Document-const: UNDEF_HEX_CHARREF
04478      *
04479      * Replace byte sequences that are undefined in the destination encoding
04480      * with an XML hexadecimal character reference.  This is valid for XML
04481      * conversion.
04482      */
04483     rb_define_const(rb_cEncodingConverter, "UNDEF_HEX_CHARREF", INT2FIX(ECONV_UNDEF_HEX_CHARREF));
04484 
04485     /* Document-const: PARTIAL_INPUT
04486      *
04487      * Indicates the source may be part of a larger string.  See
04488      * primitive_convert for an example.
04489      */
04490     rb_define_const(rb_cEncodingConverter, "PARTIAL_INPUT", INT2FIX(ECONV_PARTIAL_INPUT));
04491 
04492     /* Document-const: AFTER_OUTPUT
04493      *
04494      * Stop converting after some output is complete but before all of the
04495      * input was consumed.  See primitive_convert for an example.
04496      */
04497     rb_define_const(rb_cEncodingConverter, "AFTER_OUTPUT", INT2FIX(ECONV_AFTER_OUTPUT));
04498 
04499     /* Document-const: UNIVERSAL_NEWLINE_DECORATOR
04500      *
04501      * Decorator for converting CRLF and CR to LF
04502      */
04503     rb_define_const(rb_cEncodingConverter, "UNIVERSAL_NEWLINE_DECORATOR", INT2FIX(ECONV_UNIVERSAL_NEWLINE_DECORATOR));
04504 
04505     /* Document-const: CRLF_NEWLINE_DECORATOR
04506      *
04507      * Decorator for converting LF to CRLF
04508      */
04509     rb_define_const(rb_cEncodingConverter, "CRLF_NEWLINE_DECORATOR", INT2FIX(ECONV_CRLF_NEWLINE_DECORATOR));
04510 
04511     /* Document-const: CR_NEWLINE_DECORATOR
04512      *
04513      * Decorator for converting LF to CR
04514      */
04515     rb_define_const(rb_cEncodingConverter, "CR_NEWLINE_DECORATOR", INT2FIX(ECONV_CR_NEWLINE_DECORATOR));
04516 
04517     /* Document-const: XML_TEXT_DECORATOR
04518      *
04519      * Escape as XML CharData
04520      */
04521     rb_define_const(rb_cEncodingConverter, "XML_TEXT_DECORATOR", INT2FIX(ECONV_XML_TEXT_DECORATOR));
04522 
04523     /* Document-const: XML_ATTR_CONTENT_DECORATOR
04524      *
04525      * Escape as XML AttValue
04526      */
04527     rb_define_const(rb_cEncodingConverter, "XML_ATTR_CONTENT_DECORATOR", INT2FIX(ECONV_XML_ATTR_CONTENT_DECORATOR));
04528 
04529     /* Document-const: XML_ATTR_QUOTE_DECORATOR
04530      *
04531      * Escape as XML AttValue
04532      */
04533     rb_define_const(rb_cEncodingConverter, "XML_ATTR_QUOTE_DECORATOR", INT2FIX(ECONV_XML_ATTR_QUOTE_DECORATOR));
04534 
04535     rb_define_method(rb_eUndefinedConversionError, "source_encoding_name", ecerr_source_encoding_name, 0);
04536     rb_define_method(rb_eUndefinedConversionError, "destination_encoding_name", ecerr_destination_encoding_name, 0);
04537     rb_define_method(rb_eUndefinedConversionError, "source_encoding", ecerr_source_encoding, 0);
04538     rb_define_method(rb_eUndefinedConversionError, "destination_encoding", ecerr_destination_encoding, 0);
04539     rb_define_method(rb_eUndefinedConversionError, "error_char", ecerr_error_char, 0);
04540 
04541     rb_define_method(rb_eInvalidByteSequenceError, "source_encoding_name", ecerr_source_encoding_name, 0);
04542     rb_define_method(rb_eInvalidByteSequenceError, "destination_encoding_name", ecerr_destination_encoding_name, 0);
04543     rb_define_method(rb_eInvalidByteSequenceError, "source_encoding", ecerr_source_encoding, 0);
04544     rb_define_method(rb_eInvalidByteSequenceError, "destination_encoding", ecerr_destination_encoding, 0);
04545     rb_define_method(rb_eInvalidByteSequenceError, "error_bytes", ecerr_error_bytes, 0);
04546     rb_define_method(rb_eInvalidByteSequenceError, "readagain_bytes", ecerr_readagain_bytes, 0);
04547     rb_define_method(rb_eInvalidByteSequenceError, "incomplete_input?", ecerr_incomplete_input, 0);
04548 
04549     Init_newline();
04550 }
04551 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7