From d6574e8c0fe1dfd118a94debfca17890c9196092 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 22 Jul 2014 15:04:28 -0400 Subject: [PATCH] dos2unix a bunch of files For some reason these files would get marked as modified just after a fresh clone on Linux --- src/mod/applications/mod_stress/FFTReal.cpp | 1230 ++--- src/mod/asr_tts/mod_cepstral/WinReadme.txt | 6 +- .../mod_gsmopen/gsmopen_protocol.c | 10 +- .../include/ctb-0.16/win32/getopt.h | 34 +- .../include/ctb-0.16/win32/gpib-32.h | 828 ++-- src/mod/endpoints/mod_gsmopen/win_iconv.c | 3972 ++++++++--------- .../endpoints/mod_gsmopen/win_iconv/iconv.h | 10 +- .../endpoints/mod_gsmopen/win_iconv/mlang.h | 108 +- .../mod_gsmopen/win_iconv/readme.txt | 6 +- .../mod_gsmopen/win_iconv/win_iconv.c | 3972 ++++++++--------- .../mod_gsmopen/win_iconv/win_iconv_test.c | 522 +-- src/mod/endpoints/mod_opal/mod_opal.cpp | 3034 ++++++------- src/mod/endpoints/mod_opal/mod_opal.h | 662 +-- .../endpoints/mod_rtmp/libamf/amf-cmake.h.in | 26 +- src/mod/languages/mod_perl/perlibs.h.in | 2 +- src/mod/say/mod_say_fa/mod_say_fa.c | 204 +- src/mod/say/mod_say_pt/mod_say_pt.c | 1082 ++--- 17 files changed, 7854 insertions(+), 7854 deletions(-) diff --git a/src/mod/applications/mod_stress/FFTReal.cpp b/src/mod/applications/mod_stress/FFTReal.cpp index e1a77b465e..3c82fc0752 100644 --- a/src/mod/applications/mod_stress/FFTReal.cpp +++ b/src/mod/applications/mod_stress/FFTReal.cpp @@ -1,615 +1,615 @@ -/***************************************************************************** -* * -* DIGITAL SIGNAL PROCESSING TOOLS * -* Version 1.03, 2001/06/15 * -* (c) 1999 - Laurent de Soras * -* * -* FFTReal.cpp * -* Fourier transformation of real number arrays. * -* Portable ISO C++ * -* * -* Tab = 3 * -*****************************************************************************/ - - - -/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ - -#include "FFTReal.h" - -#include -#include - - - -#if defined (_MSC_VER) -#pragma pack (push, 8) -#endif // _MSC_VER - - - -/*\\\ PUBLIC MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ - - - -/*==========================================================================*/ -/* Name: Constructor */ -/* Input parameters: */ -/* - length: length of the array on which we want to do a FFT. */ -/* Range: power of 2 only, > 0. */ -/* Throws: std::bad_alloc, anything */ -/*==========================================================================*/ - -FFTReal::FFTReal (const long length) -: _length (length) -, _nbr_bits (int (floor (log (length) / log (2) + 0.5))) -, _bit_rev_lut (int (floor (log (length) / log (2) + 0.5))) -, _trigo_lut (int (floor (log (length) / log (2) + 0.5))) -, _sqrt2_2 (flt_t (sqrt (2) * 0.5)) -{ - assert ((1L << _nbr_bits) == length); - - _buffer_ptr = 0; - if (_nbr_bits > 2) - { - _buffer_ptr = new flt_t [_length]; - } -} - - - -/*==========================================================================*/ -/* Name: Destructor */ -/*==========================================================================*/ - -FFTReal::~FFTReal (void) -{ - delete [] _buffer_ptr; - _buffer_ptr = 0; -} - - - -/*==========================================================================*/ -/* Name: do_fft */ -/* Description: Compute the FFT of the array. */ -/* Input parameters: */ -/* - x: pointer on the source array (time). */ -/* Output parameters: */ -/* - f: pointer on the destination array (frequencies). */ -/* f [0...length(x)/2] = real values, */ -/* f [length(x)/2+1...length(x)-1] = imaginary values of */ -/* coefficents 1...length(x)/2-1. */ -/* Throws: Nothing */ -/*==========================================================================*/ - -void FFTReal::do_fft (flt_t f [], const flt_t x []) const -{ - -/*______________________________________________ - * - * General case - *______________________________________________ - */ - - if (_nbr_bits > 2) - { - flt_t * sf; - flt_t * df; - - if (_nbr_bits & 1) - { - df = _buffer_ptr; - sf = f; - } - else - { - df = f; - sf = _buffer_ptr; - } - - /* Do the transformation in several pass */ - { - int pass; - long nbr_coef; - long h_nbr_coef; - long d_nbr_coef; - long coef_index; - - /* First and second pass at once */ - { - const long * const bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); - coef_index = 0; - do - { - const long rev_index_0 = bit_rev_lut_ptr [coef_index]; - const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1]; - const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2]; - const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3]; - - flt_t * const df2 = df + coef_index; - df2 [1] = x [rev_index_0] - x [rev_index_1]; - df2 [3] = x [rev_index_2] - x [rev_index_3]; - - const flt_t sf_0 = x [rev_index_0] + x [rev_index_1]; - const flt_t sf_2 = x [rev_index_2] + x [rev_index_3]; - - df2 [0] = sf_0 + sf_2; - df2 [2] = sf_0 - sf_2; - - coef_index += 4; - } - while (coef_index < _length); - } - - /* Third pass */ - { - coef_index = 0; - const flt_t sqrt2_2 = _sqrt2_2; - do - { - flt_t v; - - sf [coef_index] = df [coef_index] + df [coef_index + 4]; - sf [coef_index + 4] = df [coef_index] - df [coef_index + 4]; - sf [coef_index + 2] = df [coef_index + 2]; - sf [coef_index + 6] = df [coef_index + 6]; - - v = (df [coef_index + 5] - df [coef_index + 7]) * sqrt2_2; - sf [coef_index + 1] = df [coef_index + 1] + v; - sf [coef_index + 3] = df [coef_index + 1] - v; - - v = (df [coef_index + 5] + df [coef_index + 7]) * sqrt2_2; - sf [coef_index + 5] = v + df [coef_index + 3]; - sf [coef_index + 7] = v - df [coef_index + 3]; - - coef_index += 8; - } - while (coef_index < _length); - } - - /* Next pass */ - for (pass = 3; pass < _nbr_bits; ++pass) - { - coef_index = 0; - nbr_coef = 1 << pass; - h_nbr_coef = nbr_coef >> 1; - d_nbr_coef = nbr_coef << 1; - const flt_t * const cos_ptr = _trigo_lut.get_ptr (pass); - do - { - long i; - const flt_t * const sf1r = sf + coef_index; - const flt_t * const sf2r = sf1r + nbr_coef; - flt_t * const dfr = df + coef_index; - flt_t * const dfi = dfr + nbr_coef; - - /* Extreme coefficients are always real */ - dfr [0] = sf1r [0] + sf2r [0]; - dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = - dfr [h_nbr_coef] = sf1r [h_nbr_coef]; - dfi [h_nbr_coef] = sf2r [h_nbr_coef]; - - /* Others are conjugate complex numbers */ - const flt_t * const sf1i = sf1r + h_nbr_coef; - const flt_t * const sf2i = sf1i + nbr_coef; - for (i = 1; i < h_nbr_coef; ++ i) - { - const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); - const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); - flt_t v; - - v = sf2r [i] * c - sf2i [i] * s; - dfr [i] = sf1r [i] + v; - dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = - - v = sf2r [i] * s + sf2i [i] * c; - dfi [i] = v + sf1i [i]; - dfi [nbr_coef - i] = v - sf1i [i]; - } - - coef_index += d_nbr_coef; - } - while (coef_index < _length); - - /* Prepare to the next pass */ - { - flt_t * const temp_ptr = df; - df = sf; - sf = temp_ptr; - } - } - } - } - -/*______________________________________________ - * - * Special cases - *______________________________________________ - */ - - /* 4-point FFT */ - else if (_nbr_bits == 2) - { - f [1] = x [0] - x [2]; - f [3] = x [1] - x [3]; - - const flt_t b_0 = x [0] + x [2]; - const flt_t b_2 = x [1] + x [3]; - - f [0] = b_0 + b_2; - f [2] = b_0 - b_2; - } - - /* 2-point FFT */ - else if (_nbr_bits == 1) - { - f [0] = x [0] + x [1]; - f [1] = x [0] - x [1]; - } - - /* 1-point FFT */ - else - { - f [0] = x [0]; - } -} - - - -/*==========================================================================*/ -/* Name: do_ifft */ -/* Description: Compute the inverse FFT of the array. Notice that */ -/* IFFT (FFT (x)) = x * length (x). Data must be */ -/* post-scaled. */ -/* Input parameters: */ -/* - f: pointer on the source array (frequencies). */ -/* f [0...length(x)/2] = real values, */ -/* f [length(x)/2+1...length(x)] = imaginary values of */ -/* coefficents 1...length(x)-1. */ -/* Output parameters: */ -/* - x: pointer on the destination array (time). */ -/* Throws: Nothing */ -/*==========================================================================*/ - -void FFTReal::do_ifft (const flt_t f [], flt_t x []) const -{ - -/*______________________________________________ - * - * General case - *______________________________________________ - */ - - if (_nbr_bits > 2) - { - flt_t * sf = const_cast (f); - flt_t * df; - flt_t * df_temp; - - if (_nbr_bits & 1) - { - df = _buffer_ptr; - df_temp = x; - } - else - { - df = x; - df_temp = _buffer_ptr; - } - - /* Do the transformation in several pass */ - { - int pass; - long nbr_coef; - long h_nbr_coef; - long d_nbr_coef; - long coef_index; - - /* First pass */ - for (pass = _nbr_bits - 1; pass >= 3; --pass) - { - coef_index = 0; - nbr_coef = 1 << pass; - h_nbr_coef = nbr_coef >> 1; - d_nbr_coef = nbr_coef << 1; - const flt_t *const cos_ptr = _trigo_lut.get_ptr (pass); - do - { - long i; - const flt_t * const sfr = sf + coef_index; - const flt_t * const sfi = sfr + nbr_coef; - flt_t * const df1r = df + coef_index; - flt_t * const df2r = df1r + nbr_coef; - - /* Extreme coefficients are always real */ - df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] - df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] - df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; - df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; - - /* Others are conjugate complex numbers */ - flt_t * const df1i = df1r + h_nbr_coef; - flt_t * const df2i = df1i + nbr_coef; - for (i = 1; i < h_nbr_coef; ++ i) - { - df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] - df1i [i] = sfi [i] - sfi [nbr_coef - i]; - - const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); - const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); - const flt_t vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] - const flt_t vi = sfi [i] + sfi [nbr_coef - i]; - - df2r [i] = vr * c + vi * s; - df2i [i] = vi * c - vr * s; - } - - coef_index += d_nbr_coef; - } - while (coef_index < _length); - - /* Prepare to the next pass */ - if (pass < _nbr_bits - 1) - { - flt_t * const temp_ptr = df; - df = sf; - sf = temp_ptr; - } - else - { - sf = df; - df = df_temp; - } - } - - /* Antepenultimate pass */ - { - const flt_t sqrt2_2 = _sqrt2_2; - coef_index = 0; - do - { - df [coef_index] = sf [coef_index] + sf [coef_index + 4]; - df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; - df [coef_index + 2] = sf [coef_index + 2] * 2; - df [coef_index + 6] = sf [coef_index + 6] * 2; - - df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3]; - df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7]; - - const flt_t vr = sf [coef_index + 1] - sf [coef_index + 3]; - const flt_t vi = sf [coef_index + 5] + sf [coef_index + 7]; - - df [coef_index + 5] = (vr + vi) * sqrt2_2; - df [coef_index + 7] = (vi - vr) * sqrt2_2; - - coef_index += 8; - } - while (coef_index < _length); - } - - /* Penultimate and last pass at once */ - { - coef_index = 0; - const long * bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); - const flt_t * sf2 = df; - do - { - { - const flt_t b_0 = sf2 [0] + sf2 [2]; - const flt_t b_2 = sf2 [0] - sf2 [2]; - const flt_t b_1 = sf2 [1] * 2; - const flt_t b_3 = sf2 [3] * 2; - - x [bit_rev_lut_ptr [0]] = b_0 + b_1; - x [bit_rev_lut_ptr [1]] = b_0 - b_1; - x [bit_rev_lut_ptr [2]] = b_2 + b_3; - x [bit_rev_lut_ptr [3]] = b_2 - b_3; - } - { - const flt_t b_0 = sf2 [4] + sf2 [6]; - const flt_t b_2 = sf2 [4] - sf2 [6]; - const flt_t b_1 = sf2 [5] * 2; - const flt_t b_3 = sf2 [7] * 2; - - x [bit_rev_lut_ptr [4]] = b_0 + b_1; - x [bit_rev_lut_ptr [5]] = b_0 - b_1; - x [bit_rev_lut_ptr [6]] = b_2 + b_3; - x [bit_rev_lut_ptr [7]] = b_2 - b_3; - } - - sf2 += 8; - coef_index += 8; - bit_rev_lut_ptr += 8; - } - while (coef_index < _length); - } - } - } - -/*______________________________________________ - * - * Special cases - *______________________________________________ - */ - - /* 4-point IFFT */ - else if (_nbr_bits == 2) - { - const flt_t b_0 = f [0] + f [2]; - const flt_t b_2 = f [0] - f [2]; - - x [0] = b_0 + f [1] * 2; - x [2] = b_0 - f [1] * 2; - x [1] = b_2 + f [3] * 2; - x [3] = b_2 - f [3] * 2; - } - - /* 2-point IFFT */ - else if (_nbr_bits == 1) - { - x [0] = f [0] + f [1]; - x [1] = f [0] - f [1]; - } - - /* 1-point IFFT */ - else - { - x [0] = f [0]; - } -} - - - -/*==========================================================================*/ -/* Name: rescale */ -/* Description: Scale an array by divide each element by its length. */ -/* This function should be called after FFT + IFFT. */ -/* Input/Output parameters: */ -/* - x: pointer on array to rescale (time or frequency). */ -/* Throws: Nothing */ -/*==========================================================================*/ - -void FFTReal::rescale (flt_t x []) const -{ - const flt_t mul = flt_t (1.0 / _length); - long i = _length - 1; - - do - { - x [i] *= mul; - --i; - } - while (i >= 0); -} - - - -/*\\\ NESTED CLASS MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ - - - -/*==========================================================================*/ -/* Name: Constructor */ -/* Input parameters: */ -/* - nbr_bits: number of bits of the array on which we want to do a */ -/* FFT. Range: > 0 */ -/* Throws: std::bad_alloc */ -/*==========================================================================*/ - -FFTReal::BitReversedLUT::BitReversedLUT (const int nbr_bits) -{ - long length; - long cnt; - long br_index; - long bit; - - length = 1L << nbr_bits; - _ptr = new long [length]; - - br_index = 0; - _ptr [0] = 0; - for (cnt = 1; cnt < length; ++cnt) - { - /* ++br_index (bit reversed) */ - bit = length >> 1; - while (((br_index ^= bit) & bit) == 0) - { - bit >>= 1; - } - - _ptr [cnt] = br_index; - } -} - - - -/*==========================================================================*/ -/* Name: Destructor */ -/*==========================================================================*/ - -FFTReal::BitReversedLUT::~BitReversedLUT (void) -{ - delete [] _ptr; - _ptr = 0; -} - - - -/*==========================================================================*/ -/* Name: Constructor */ -/* Input parameters: */ -/* - nbr_bits: number of bits of the array on which we want to do a */ -/* FFT. Range: > 0 */ -/* Throws: std::bad_alloc, anything */ -/*==========================================================================*/ - -FFTReal::TrigoLUT::TrigoLUT (const int nbr_bits) -{ - long total_len; - - _ptr = 0; - if (nbr_bits > 3) - { - total_len = (1L << (nbr_bits - 1)) - 4; - _ptr = new flt_t [total_len]; - - const double PI = atan (1) * 4; - for (int level = 3; level < nbr_bits; ++level) - { - const long level_len = 1L << (level - 1); - flt_t * const level_ptr = const_cast (get_ptr (level)); - const double mul = PI / (level_len << 1); - - for (long i = 0; i < level_len; ++ i) - { - level_ptr [i] = (flt_t) cos (i * mul); - } - } - } -} - - - -/*==========================================================================*/ -/* Name: Destructor */ -/*==========================================================================*/ - -FFTReal::TrigoLUT::~TrigoLUT (void) -{ - delete [] _ptr; - _ptr = 0; -} - - - -#if defined (_MSC_VER) -#pragma pack (pop) -#endif // _MSC_VER - - - -/***************************************************************************** - - LEGAL - - Source code may be freely used for any purpose, including commercial - applications. Programs must display in their "About" dialog-box (or - documentation) a text telling they use these routines by Laurent de Soras. - Modified source code can be distributed, but modifications must be clearly - indicated. - - CONTACT - - Laurent de Soras - 92 avenue Albert 1er - 92500 Rueil-Malmaison - France - - ldesoras@club-internet.fr - -*****************************************************************************/ - - - -/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ +/***************************************************************************** +* * +* DIGITAL SIGNAL PROCESSING TOOLS * +* Version 1.03, 2001/06/15 * +* (c) 1999 - Laurent de Soras * +* * +* FFTReal.cpp * +* Fourier transformation of real number arrays. * +* Portable ISO C++ * +* * +* Tab = 3 * +*****************************************************************************/ + + + +/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +#include "FFTReal.h" + +#include +#include + + + +#if defined (_MSC_VER) +#pragma pack (push, 8) +#endif // _MSC_VER + + + +/*\\\ PUBLIC MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - length: length of the array on which we want to do a FFT. */ +/* Range: power of 2 only, > 0. */ +/* Throws: std::bad_alloc, anything */ +/*==========================================================================*/ + +FFTReal::FFTReal (const long length) +: _length (length) +, _nbr_bits (int (floor (log (length) / log (2) + 0.5))) +, _bit_rev_lut (int (floor (log (length) / log (2) + 0.5))) +, _trigo_lut (int (floor (log (length) / log (2) + 0.5))) +, _sqrt2_2 (flt_t (sqrt (2) * 0.5)) +{ + assert ((1L << _nbr_bits) == length); + + _buffer_ptr = 0; + if (_nbr_bits > 2) + { + _buffer_ptr = new flt_t [_length]; + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::~FFTReal (void) +{ + delete [] _buffer_ptr; + _buffer_ptr = 0; +} + + + +/*==========================================================================*/ +/* Name: do_fft */ +/* Description: Compute the FFT of the array. */ +/* Input parameters: */ +/* - x: pointer on the source array (time). */ +/* Output parameters: */ +/* - f: pointer on the destination array (frequencies). */ +/* f [0...length(x)/2] = real values, */ +/* f [length(x)/2+1...length(x)-1] = imaginary values of */ +/* coefficents 1...length(x)/2-1. */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::do_fft (flt_t f [], const flt_t x []) const +{ + +/*______________________________________________ + * + * General case + *______________________________________________ + */ + + if (_nbr_bits > 2) + { + flt_t * sf; + flt_t * df; + + if (_nbr_bits & 1) + { + df = _buffer_ptr; + sf = f; + } + else + { + df = f; + sf = _buffer_ptr; + } + + /* Do the transformation in several pass */ + { + int pass; + long nbr_coef; + long h_nbr_coef; + long d_nbr_coef; + long coef_index; + + /* First and second pass at once */ + { + const long * const bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); + coef_index = 0; + do + { + const long rev_index_0 = bit_rev_lut_ptr [coef_index]; + const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1]; + const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2]; + const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3]; + + flt_t * const df2 = df + coef_index; + df2 [1] = x [rev_index_0] - x [rev_index_1]; + df2 [3] = x [rev_index_2] - x [rev_index_3]; + + const flt_t sf_0 = x [rev_index_0] + x [rev_index_1]; + const flt_t sf_2 = x [rev_index_2] + x [rev_index_3]; + + df2 [0] = sf_0 + sf_2; + df2 [2] = sf_0 - sf_2; + + coef_index += 4; + } + while (coef_index < _length); + } + + /* Third pass */ + { + coef_index = 0; + const flt_t sqrt2_2 = _sqrt2_2; + do + { + flt_t v; + + sf [coef_index] = df [coef_index] + df [coef_index + 4]; + sf [coef_index + 4] = df [coef_index] - df [coef_index + 4]; + sf [coef_index + 2] = df [coef_index + 2]; + sf [coef_index + 6] = df [coef_index + 6]; + + v = (df [coef_index + 5] - df [coef_index + 7]) * sqrt2_2; + sf [coef_index + 1] = df [coef_index + 1] + v; + sf [coef_index + 3] = df [coef_index + 1] - v; + + v = (df [coef_index + 5] + df [coef_index + 7]) * sqrt2_2; + sf [coef_index + 5] = v + df [coef_index + 3]; + sf [coef_index + 7] = v - df [coef_index + 3]; + + coef_index += 8; + } + while (coef_index < _length); + } + + /* Next pass */ + for (pass = 3; pass < _nbr_bits; ++pass) + { + coef_index = 0; + nbr_coef = 1 << pass; + h_nbr_coef = nbr_coef >> 1; + d_nbr_coef = nbr_coef << 1; + const flt_t * const cos_ptr = _trigo_lut.get_ptr (pass); + do + { + long i; + const flt_t * const sf1r = sf + coef_index; + const flt_t * const sf2r = sf1r + nbr_coef; + flt_t * const dfr = df + coef_index; + flt_t * const dfi = dfr + nbr_coef; + + /* Extreme coefficients are always real */ + dfr [0] = sf1r [0] + sf2r [0]; + dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = + dfr [h_nbr_coef] = sf1r [h_nbr_coef]; + dfi [h_nbr_coef] = sf2r [h_nbr_coef]; + + /* Others are conjugate complex numbers */ + const flt_t * const sf1i = sf1r + h_nbr_coef; + const flt_t * const sf2i = sf1i + nbr_coef; + for (i = 1; i < h_nbr_coef; ++ i) + { + const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); + const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); + flt_t v; + + v = sf2r [i] * c - sf2i [i] * s; + dfr [i] = sf1r [i] + v; + dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = + + v = sf2r [i] * s + sf2i [i] * c; + dfi [i] = v + sf1i [i]; + dfi [nbr_coef - i] = v - sf1i [i]; + } + + coef_index += d_nbr_coef; + } + while (coef_index < _length); + + /* Prepare to the next pass */ + { + flt_t * const temp_ptr = df; + df = sf; + sf = temp_ptr; + } + } + } + } + +/*______________________________________________ + * + * Special cases + *______________________________________________ + */ + + /* 4-point FFT */ + else if (_nbr_bits == 2) + { + f [1] = x [0] - x [2]; + f [3] = x [1] - x [3]; + + const flt_t b_0 = x [0] + x [2]; + const flt_t b_2 = x [1] + x [3]; + + f [0] = b_0 + b_2; + f [2] = b_0 - b_2; + } + + /* 2-point FFT */ + else if (_nbr_bits == 1) + { + f [0] = x [0] + x [1]; + f [1] = x [0] - x [1]; + } + + /* 1-point FFT */ + else + { + f [0] = x [0]; + } +} + + + +/*==========================================================================*/ +/* Name: do_ifft */ +/* Description: Compute the inverse FFT of the array. Notice that */ +/* IFFT (FFT (x)) = x * length (x). Data must be */ +/* post-scaled. */ +/* Input parameters: */ +/* - f: pointer on the source array (frequencies). */ +/* f [0...length(x)/2] = real values, */ +/* f [length(x)/2+1...length(x)] = imaginary values of */ +/* coefficents 1...length(x)-1. */ +/* Output parameters: */ +/* - x: pointer on the destination array (time). */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::do_ifft (const flt_t f [], flt_t x []) const +{ + +/*______________________________________________ + * + * General case + *______________________________________________ + */ + + if (_nbr_bits > 2) + { + flt_t * sf = const_cast (f); + flt_t * df; + flt_t * df_temp; + + if (_nbr_bits & 1) + { + df = _buffer_ptr; + df_temp = x; + } + else + { + df = x; + df_temp = _buffer_ptr; + } + + /* Do the transformation in several pass */ + { + int pass; + long nbr_coef; + long h_nbr_coef; + long d_nbr_coef; + long coef_index; + + /* First pass */ + for (pass = _nbr_bits - 1; pass >= 3; --pass) + { + coef_index = 0; + nbr_coef = 1 << pass; + h_nbr_coef = nbr_coef >> 1; + d_nbr_coef = nbr_coef << 1; + const flt_t *const cos_ptr = _trigo_lut.get_ptr (pass); + do + { + long i; + const flt_t * const sfr = sf + coef_index; + const flt_t * const sfi = sfr + nbr_coef; + flt_t * const df1r = df + coef_index; + flt_t * const df2r = df1r + nbr_coef; + + /* Extreme coefficients are always real */ + df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] + df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] + df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; + df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; + + /* Others are conjugate complex numbers */ + flt_t * const df1i = df1r + h_nbr_coef; + flt_t * const df2i = df1i + nbr_coef; + for (i = 1; i < h_nbr_coef; ++ i) + { + df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] + df1i [i] = sfi [i] - sfi [nbr_coef - i]; + + const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); + const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); + const flt_t vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] + const flt_t vi = sfi [i] + sfi [nbr_coef - i]; + + df2r [i] = vr * c + vi * s; + df2i [i] = vi * c - vr * s; + } + + coef_index += d_nbr_coef; + } + while (coef_index < _length); + + /* Prepare to the next pass */ + if (pass < _nbr_bits - 1) + { + flt_t * const temp_ptr = df; + df = sf; + sf = temp_ptr; + } + else + { + sf = df; + df = df_temp; + } + } + + /* Antepenultimate pass */ + { + const flt_t sqrt2_2 = _sqrt2_2; + coef_index = 0; + do + { + df [coef_index] = sf [coef_index] + sf [coef_index + 4]; + df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; + df [coef_index + 2] = sf [coef_index + 2] * 2; + df [coef_index + 6] = sf [coef_index + 6] * 2; + + df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3]; + df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7]; + + const flt_t vr = sf [coef_index + 1] - sf [coef_index + 3]; + const flt_t vi = sf [coef_index + 5] + sf [coef_index + 7]; + + df [coef_index + 5] = (vr + vi) * sqrt2_2; + df [coef_index + 7] = (vi - vr) * sqrt2_2; + + coef_index += 8; + } + while (coef_index < _length); + } + + /* Penultimate and last pass at once */ + { + coef_index = 0; + const long * bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); + const flt_t * sf2 = df; + do + { + { + const flt_t b_0 = sf2 [0] + sf2 [2]; + const flt_t b_2 = sf2 [0] - sf2 [2]; + const flt_t b_1 = sf2 [1] * 2; + const flt_t b_3 = sf2 [3] * 2; + + x [bit_rev_lut_ptr [0]] = b_0 + b_1; + x [bit_rev_lut_ptr [1]] = b_0 - b_1; + x [bit_rev_lut_ptr [2]] = b_2 + b_3; + x [bit_rev_lut_ptr [3]] = b_2 - b_3; + } + { + const flt_t b_0 = sf2 [4] + sf2 [6]; + const flt_t b_2 = sf2 [4] - sf2 [6]; + const flt_t b_1 = sf2 [5] * 2; + const flt_t b_3 = sf2 [7] * 2; + + x [bit_rev_lut_ptr [4]] = b_0 + b_1; + x [bit_rev_lut_ptr [5]] = b_0 - b_1; + x [bit_rev_lut_ptr [6]] = b_2 + b_3; + x [bit_rev_lut_ptr [7]] = b_2 - b_3; + } + + sf2 += 8; + coef_index += 8; + bit_rev_lut_ptr += 8; + } + while (coef_index < _length); + } + } + } + +/*______________________________________________ + * + * Special cases + *______________________________________________ + */ + + /* 4-point IFFT */ + else if (_nbr_bits == 2) + { + const flt_t b_0 = f [0] + f [2]; + const flt_t b_2 = f [0] - f [2]; + + x [0] = b_0 + f [1] * 2; + x [2] = b_0 - f [1] * 2; + x [1] = b_2 + f [3] * 2; + x [3] = b_2 - f [3] * 2; + } + + /* 2-point IFFT */ + else if (_nbr_bits == 1) + { + x [0] = f [0] + f [1]; + x [1] = f [0] - f [1]; + } + + /* 1-point IFFT */ + else + { + x [0] = f [0]; + } +} + + + +/*==========================================================================*/ +/* Name: rescale */ +/* Description: Scale an array by divide each element by its length. */ +/* This function should be called after FFT + IFFT. */ +/* Input/Output parameters: */ +/* - x: pointer on array to rescale (time or frequency). */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::rescale (flt_t x []) const +{ + const flt_t mul = flt_t (1.0 / _length); + long i = _length - 1; + + do + { + x [i] *= mul; + --i; + } + while (i >= 0); +} + + + +/*\\\ NESTED CLASS MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - nbr_bits: number of bits of the array on which we want to do a */ +/* FFT. Range: > 0 */ +/* Throws: std::bad_alloc */ +/*==========================================================================*/ + +FFTReal::BitReversedLUT::BitReversedLUT (const int nbr_bits) +{ + long length; + long cnt; + long br_index; + long bit; + + length = 1L << nbr_bits; + _ptr = new long [length]; + + br_index = 0; + _ptr [0] = 0; + for (cnt = 1; cnt < length; ++cnt) + { + /* ++br_index (bit reversed) */ + bit = length >> 1; + while (((br_index ^= bit) & bit) == 0) + { + bit >>= 1; + } + + _ptr [cnt] = br_index; + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::BitReversedLUT::~BitReversedLUT (void) +{ + delete [] _ptr; + _ptr = 0; +} + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - nbr_bits: number of bits of the array on which we want to do a */ +/* FFT. Range: > 0 */ +/* Throws: std::bad_alloc, anything */ +/*==========================================================================*/ + +FFTReal::TrigoLUT::TrigoLUT (const int nbr_bits) +{ + long total_len; + + _ptr = 0; + if (nbr_bits > 3) + { + total_len = (1L << (nbr_bits - 1)) - 4; + _ptr = new flt_t [total_len]; + + const double PI = atan (1) * 4; + for (int level = 3; level < nbr_bits; ++level) + { + const long level_len = 1L << (level - 1); + flt_t * const level_ptr = const_cast (get_ptr (level)); + const double mul = PI / (level_len << 1); + + for (long i = 0; i < level_len; ++ i) + { + level_ptr [i] = (flt_t) cos (i * mul); + } + } + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::TrigoLUT::~TrigoLUT (void) +{ + delete [] _ptr; + _ptr = 0; +} + + + +#if defined (_MSC_VER) +#pragma pack (pop) +#endif // _MSC_VER + + + +/***************************************************************************** + + LEGAL + + Source code may be freely used for any purpose, including commercial + applications. Programs must display in their "About" dialog-box (or + documentation) a text telling they use these routines by Laurent de Soras. + Modified source code can be distributed, but modifications must be clearly + indicated. + + CONTACT + + Laurent de Soras + 92 avenue Albert 1er + 92500 Rueil-Malmaison + France + + ldesoras@club-internet.fr + +*****************************************************************************/ + + + +/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/src/mod/asr_tts/mod_cepstral/WinReadme.txt b/src/mod/asr_tts/mod_cepstral/WinReadme.txt index 56b4e79a39..50ca93d3f8 100644 --- a/src/mod/asr_tts/mod_cepstral/WinReadme.txt +++ b/src/mod/asr_tts/mod_cepstral/WinReadme.txt @@ -1,4 +1,4 @@ -The Cepstral SDK for Windows should be placed in c:\dev\cepstral -ex. C:\dev\cepstral\sdk\include -This SDK can be obtained from http://cepstral.com/ +The Cepstral SDK for Windows should be placed in c:\dev\cepstral +ex. C:\dev\cepstral\sdk\include +This SDK can be obtained from http://cepstral.com/ If you want a prebuilt version you may download one from http://files.freeswitch.org/windows/installer/ \ No newline at end of file diff --git a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.c b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.c index d8eb66aecc..5a97ffa873 100644 --- a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.c +++ b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.c @@ -537,11 +537,11 @@ int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed) NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); #if 0 - size = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, 0); - UserName=(wchar_t*)GlobalAlloc(GME­ M_ZEROINIT, size); - ret = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, size); - if(ret == 0) - getError(GetLastError()); + size = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, 0); + UserName=(wchar_t*)GlobalAlloc(GME­ M_ZEROINIT, size); + ret = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, size); + if(ret == 0) + getError(GetLastError()); #endif //0 return (-1); } diff --git a/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/getopt.h b/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/getopt.h index afa50d4567..d2a909fd73 100644 --- a/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/getopt.h +++ b/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/getopt.h @@ -1,17 +1,17 @@ -#ifndef LIBCTB_WIN32_GETOPT_H_INCLUDED_ -#define LIBCTB_WIN32_GETOPT_H_INCLUDED_ - -///////////////////////////////////////////////////////////////////////////// -// Name: win32/getopt.h -// Purpose: -// Author: Joachim Buermann -// Copyright: (c) 2010 Joachim Buermann -// Licence: wxWindows licence -///////////////////////////////////////////////////////////////////////////// - -extern char* optarg; -extern int optind; - -int getopt(int argc, char* argv[], char* optstring); - -#endif +#ifndef LIBCTB_WIN32_GETOPT_H_INCLUDED_ +#define LIBCTB_WIN32_GETOPT_H_INCLUDED_ + +///////////////////////////////////////////////////////////////////////////// +// Name: win32/getopt.h +// Purpose: +// Author: Joachim Buermann +// Copyright: (c) 2010 Joachim Buermann +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +extern char* optarg; +extern int optind; + +int getopt(int argc, char* argv[], char* optstring); + +#endif diff --git a/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/gpib-32.h b/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/gpib-32.h index 627b30acfa..307c6ce001 100644 --- a/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/gpib-32.h +++ b/src/mod/endpoints/mod_gsmopen/libctb-0.16/include/ctb-0.16/win32/gpib-32.h @@ -1,414 +1,414 @@ -/* - * - * - * Win32 include file - * for accessing the 32-bit GPIB DLL (gpib-32.dll) - * - * - * Contains user variables (ibsta, iberr, ibcnt, ibcntl), - * function prototypes and useful defined constants for - * calling NI-488 and NI-488.2 routines from a Microsoft - * C/C++ Win32 application. - * - * - * Copyright 1998 National Instruments Corporation - * - */ - -#ifndef DECL_32_H // ensure we are only included once -#define DECL_32_H - -#include "windows.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/***************************************************************************/ -/* HANDY CONSTANTS FOR USE BY APPLICATION PROGRAMS ... */ -/***************************************************************************/ -#define UNL 0x3f /* GPIB unlisten command */ -#define UNT 0x5f /* GPIB untalk command */ -#define GTL 0x01 /* GPIB go to local */ -#define SDC 0x04 /* GPIB selected device clear */ -#define PPC 0x05 /* GPIB parallel poll configure */ -#define GET 0x08 /* GPIB group execute trigger */ -#define TCT 0x09 /* GPIB take control */ -#define LLO 0x11 /* GPIB local lock out */ -#define DCL 0x14 /* GPIB device clear */ -#define PPU 0.16 /* GPIB parallel poll unconfigure */ -#define SPE 0x18 /* GPIB serial poll enable */ -#define SPD 0x19 /* GPIB serial poll disable */ -#define PPE 0x60 /* GPIB parallel poll enable */ -#define PPD 0x70 /* GPIB parallel poll disable */ - -/* GPIB status bit vector : */ -/* global variable ibsta and wait mask */ - -#define ERR (1<<15) /* Error detected */ -#define TIMO (1<<14) /* Timeout */ -#define END (1<<13) /* EOI or EOS detected */ -#define SRQI (1<<12) /* SRQ detected by CIC */ -#define RQS (1<<11) /* Device needs service */ -#define CMPL (1<<8) /* I/O completed */ -#define LOK (1<<7) /* Local lockout state */ -#define REM (1<<6) /* Remote state */ -#define CIC (1<<5) /* Controller-in-Charge */ -#define ATN (1<<4) /* Attention asserted */ -#define TACS (1<<3) /* Talker active */ -#define LACS (1<<2) /* Listener active */ -#define DTAS (1<<1) /* Device trigger state */ -#define DCAS (1<<0) /* Device clear state */ - -/* Error messages returned in global variable iberr */ - -#define EDVR 0 /* System error */ -#define ECIC 1 /* Function requires GPIB board to be CIC */ -#define ENOL 2 /* Write function detected no Listeners */ -#define EADR 3 /* Interface board not addressed correctly*/ -#define EARG 4 /* Invalid argument to function call */ -#define ESAC 5 /* Function requires GPIB board to be SAC */ -#define EABO 6 /* I/O operation aborted */ -#define ENEB 7 /* Non-existent interface board */ -#define EDMA 8 /* Error performing DMA */ -#define EOIP 10 /* I/O operation started before previous */ - /* operation completed */ -#define ECAP 11 /* No capability for intended operation */ -#define EFSO 12 /* File system operation error */ -#define EBUS 14 /* Command error during device call */ -#define ESTB 15 /* Serial poll status byte lost */ -#define ESRQ 16 /* SRQ remains asserted */ -#define ETAB 20 /* The return buffer is full. */ -#define ELCK 21 /* Address or board is locked. */ - -/* EOS mode bits */ - -#define BIN (1<<12) /* Eight bit compare */ -#define XEOS (1<<11) /* Send END with EOS byte */ -#define REOS (1<<10) /* Terminate read on EOS */ - -/* Timeout values and meanings */ - -#define TNONE 0 /* Infinite timeout (disabled) */ -#define T10us 1 /* Timeout of 10 us (ideal) */ -#define T30us 2 /* Timeout of 30 us (ideal) */ -#define T100us 3 /* Timeout of 100 us (ideal) */ -#define T300us 4 /* Timeout of 300 us (ideal) */ -#define T1ms 5 /* Timeout of 1 ms (ideal) */ -#define T3ms 6 /* Timeout of 3 ms (ideal) */ -#define T10ms 7 /* Timeout of 10 ms (ideal) */ -#define T30ms 8 /* Timeout of 30 ms (ideal) */ -#define T100ms 9 /* Timeout of 100 ms (ideal) */ -#define T300ms 10 /* Timeout of 300 ms (ideal) */ -#define T1s 11 /* Timeout of 1 s (ideal) */ -#define T3s 12 /* Timeout of 3 s (ideal) */ -#define T10s 13 /* Timeout of 10 s (ideal) */ -#define T30s 14 /* Timeout of 30 s (ideal) */ -#define T100s 15 /* Timeout of 100 s (ideal) */ -#define T300s 16 /* Timeout of 300 s (ideal) */ -#define T1000s 17 /* Timeout of 1000 s (ideal) */ - - -/* IBLN Constants */ -#define NO_SAD 0 -#define ALL_SAD -1 - - -/* The following constants are used for the second parameter of the - * ibconfig function. They are the "option" selection codes. - */ -#define IbcPAD 0x0001 /* Primary Address */ -#define IbcSAD 0x0002 /* Secondary Address */ -#define IbcTMO 0x0003 /* Timeout Value */ -#define IbcEOT 0x0004 /* Send EOI with last data byte? */ -#define IbcPPC 0x0005 /* Parallel Poll Configure */ -#define IbcREADDR 0x0006 /* Repeat Addressing */ -#define IbcAUTOPOLL 0x0007 /* Disable Auto Serial Polling */ -#define IbcCICPROT 0x0008 /* Use the CIC Protocol? */ -#define IbcIRQ 0x0009 /* Use PIO for I/O */ -#define IbcSC 0x000A /* Board is System Controller? */ -#define IbcSRE 0x000B /* Assert SRE on device calls? */ -#define IbcEOSrd 0x000C /* Terminate reads on EOS */ -#define IbcEOSwrt 0x000D /* Send EOI with EOS character */ -#define IbcEOScmp 0x000E /* Use 7 or 8-bit EOS compare */ -#define IbcEOSchar 0x000F /* The EOS character. */ -#define IbcPP2 0x0010 /* Use Parallel Poll Mode 2. */ -#define IbcTIMING 0x0011 /* NORMAL, HIGH, or VERY_HIGH timing. */ -#define IbcDMA 0x0012 /* Use DMA for I/O */ -#define IbcReadAdjust 0x0013 /* Swap bytes during an ibrd. */ -#define IbcWriteAdjust 0x014 /* Swap bytes during an ibwrt. */ -#define IbcSendLLO 0x0017 /* Enable/disable the sending of LLO. */ -#define IbcSPollTime 0x0018 /* Set the timeout value for serial polls. */ -#define IbcPPollTime 0x0019 /* Set the parallel poll length period. */ -#define IbcEndBitIsNormal 0x001A /* Remove EOS from END bit of IBSTA. */ -#define IbcUnAddr 0x001B /* Enable/disable device unaddressing. */ -#define IbcSignalNumber 0x001C /* Set UNIX signal number - unsupported */ -#define IbcBlockIfLocked 0x001D /* Enable/disable blocking for locked boards/devices */ -#define IbcHSCableLength 0x001F /* Length of cable specified for high speed timing.*/ -#define IbcIst 0x0020 /* Set the IST bit. */ -#define IbcRsv 0x0021 /* Set the RSV byte. */ - -/* - * Constants that can be used (in addition to the ibconfig constants) - * when calling the ibask() function. - */ - -#define IbaPAD IbcPAD -#define IbaSAD IbcSAD -#define IbaTMO IbcTMO -#define IbaEOT IbcEOT -#define IbaPPC IbcPPC -#define IbaREADDR IbcREADDR -#define IbaAUTOPOLL IbcAUTOPOLL -#define IbaCICPROT IbcCICPROT -#define IbaIRQ IbcIRQ -#define IbaSC IbcSC -#define IbaSRE IbcSRE -#define IbaEOSrd IbcEOSrd -#define IbaEOSwrt IbcEOSwrt -#define IbaEOScmp IbcEOScmp -#define IbaEOSchar IbcEOSchar -#define IbaPP2 IbcPP2 -#define IbaTIMING IbcTIMING -#define IbaDMA IbcDMA -#define IbaReadAdjust IbcReadAdjust -#define IbaWriteAdjust IbcWriteAdjust -#define IbaSendLLO IbcSendLLO -#define IbaSPollTime IbcSPollTime -#define IbaPPollTime IbcPPollTime -#define IbaEndBitIsNormal IbcEndBitIsNormal -#define IbaUnAddr IbcUnAddr -#define IbaSignalNumber IbcSignalNumber -#define IbaBlockIfLocked IbcBlockIfLocked -#define IbaHSCableLength IbcHSCableLength -#define IbaIst IbcIst -#define IbaRsv IbcRsv - -#define IbaBNA 0x0200 /* A device's access board. */ - - -/* Values used by the Send 488.2 command. */ -#define NULLend 0x00 /* Do nothing at the end of a transfer.*/ -#define NLend 0x01 /* Send NL with EOI after a transfer. */ -#define DABend 0x02 /* Send EOI with the last DAB. */ - -/* Value used by the 488.2 Receive command. - */ -#define STOPend 0x0100 - - -/* Address type (for 488.2 calls) */ - -typedef short Addr4882_t; /* System dependent: must be 16 bits */ - -/* - * This macro can be used to easily create an entry in address list - * that is required by many of the 488.2 functions. The primary address goes in the - * lower 8-bits and the secondary address goes in the upper 8-bits. - */ -#define MakeAddr(pad, sad) ((Addr4882_t)(((pad)&0xFF) | ((sad)<<8))) - -/* - * This value is used to terminate an address list. It should be - * assigned to the last entry. - */ -#ifndef NOADDR -#define NOADDR (Addr4882_t)((unsigned short)0xFFFF) -#endif - -/* - * The following two macros are used to "break apart" an address list - * entry. They take an unsigned integer and return either the primary - * or secondary address stored in the integer. - */ -#define GetPAD(val) ((val) & 0xFF) -#define GetSAD(val) (((val) >> 8) & 0xFF) - -/* iblines constants */ - -#define ValidEOI (short)0x0080 -#define ValidATN (short)0x0040 -#define ValidSRQ (short)0x0020 -#define ValidREN (short)0x0010 -#define ValidIFC (short)0x0008 -#define ValidNRFD (short)0x0004 -#define ValidNDAC (short)0x0002 -#define ValidDAV (short)0x0001 -#define BusEOI (short)0x8000 -#define BusATN (short)0x4000 -#define BusSRQ (short)0x2000 -#define BusREN (short)0x1000 -#define BusIFC (short)0x0800 -#define BusNRFD (short)0x0400 -#define BusNDAC (short)0x0200 -#define BusDAV (short)0x0100 - -/**** - **** typedef for ibnotify callback **** - ****/ -typedef int (__stdcall * GpibNotifyCallback_t)(int, int, int, long, PVOID); - -#define IBNOTIFY_REARM_FAILED 0xE00A003F - - -/*************************************************************************/ -/* */ -/* iblockx and ibunlockx definitions --- for use with GPIB-ENET only !! */ -/* */ -/*************************************************************************/ -#define TIMMEDIATE -1 -#define TINFINITE -2 -#define MAX_LOCKSHARENAME_LENGTH 64 - -#if defined(UNICODE) - #define iblockx iblockxW -#else - #define iblockx iblockxA -#endif - -extern int __stdcall iblockxA (int ud, int LockWaitTime, PCHAR LockShareName); -extern int __stdcall iblockxW (int ud, int LockWaitTime, PWCHAR LockShareName); -extern int __stdcall ibunlockx (int ud); - - -/***************************************************************************/ -/* IBSTA, IBERR, IBCNT, IBCNTL and FUNCTION PROTOTYPES */ -/* ( only included if not accessing the 32-bit DLL directly ) */ -/***************************************************************************/ -#if !defined(GPIB_DIRECT_ACCESS) - -/* - * Set up access to the user variables (ibsta, iberr, ibcnt, ibcntl). - * These are declared and exported by the 32-bit DLL. Separate copies - * exist for each process that accesses the DLL. They are shared by - * multiple threads of a single process. - */ - -extern int ibsta; -extern int iberr; -extern int ibcnt; -extern long ibcntl; - - -#if defined(UNICODE) - #define ibbna ibbnaW - #define ibfind ibfindW - #define ibrdf ibrdfW - #define ibwrtf ibwrtfW -#else - #define ibbna ibbnaA - #define ibfind ibfindA - #define ibrdf ibrdfA - #define ibwrtf ibwrtfA -#endif - -/* - * Extern 32-bit GPIB DLL functions - */ - -/* NI-488 Function Prototypes */ -extern int __stdcall ibfindA (LPCSTR udname); -extern int __stdcall ibbnaA (int ud, LPCSTR udname); -extern int __stdcall ibrdfA (int ud, LPCSTR filename); -extern int __stdcall ibwrtfA (int ud, LPCSTR filename); - -extern int __stdcall ibfindW (LPCWSTR udname); -extern int __stdcall ibbnaW (int ud, LPCWSTR udname); -extern int __stdcall ibrdfW (int ud, LPCWSTR filename); -extern int __stdcall ibwrtfW (int ud, LPCWSTR filename); - -extern int __stdcall ibask (int ud, int option, PINT v); -extern int __stdcall ibcac (int ud, int v); -extern int __stdcall ibclr (int ud); -extern int __stdcall ibcmd (int ud, PVOID buf, long cnt); -extern int __stdcall ibcmda (int ud, PVOID buf, long cnt); -extern int __stdcall ibconfig (int ud, int option, int v); -extern int __stdcall ibdev (int boardID, int pad, int sad, int tmo, int eot, int eos); -extern int __stdcall ibdiag (int ud, PVOID buf, long cnt); -extern int __stdcall ibdma (int ud, int v); -extern int __stdcall ibeos (int ud, int v); -extern int __stdcall ibeot (int ud, int v); -extern int __stdcall ibgts (int ud, int v); -extern int __stdcall ibist (int ud, int v); -extern int __stdcall iblines (int ud, PSHORT result); -extern int __stdcall ibln (int ud, int pad, int sad, PSHORT listen); -extern int __stdcall ibloc (int ud); -extern int __stdcall ibnotify (int ud, int mask, GpibNotifyCallback_t Callback, PVOID RefData); -extern int __stdcall ibonl (int ud, int v); -extern int __stdcall ibpad (int ud, int v); -extern int __stdcall ibpct (int ud); -extern int __stdcall ibpoke (int ud, long option, long v); -extern int __stdcall ibppc (int ud, int v); -extern int __stdcall ibrd (int ud, PVOID buf, long cnt); -extern int __stdcall ibrda (int ud, PVOID buf, long cnt); -extern int __stdcall ibrpp (int ud, PCHAR ppr); -extern int __stdcall ibrsc (int ud, int v); -extern int __stdcall ibrsp (int ud, PCHAR spr); -extern int __stdcall ibrsv (int ud, int v); -extern int __stdcall ibsad (int ud, int v); -extern int __stdcall ibsic (int ud); -extern int __stdcall ibsre (int ud, int v); -extern int __stdcall ibstop (int ud); -extern int __stdcall ibtmo (int ud, int v); -extern int __stdcall ibtrg (int ud); -extern int __stdcall ibwait (int ud, int mask); -extern int __stdcall ibwrt (int ud, PVOID buf, long cnt); -extern int __stdcall ibwrta (int ud, PVOID buf, long cnt); - -// GPIB-ENET only functions to support locking across machines -extern int __stdcall iblock (int ud); -extern int __stdcall ibunlock (int ud); - -/**************************************************************************/ -/* Functions to access Thread-Specific copies of the GPIB global vars */ - -extern int __stdcall ThreadIbsta (void); -extern int __stdcall ThreadIberr (void); -extern int __stdcall ThreadIbcnt (void); -extern long __stdcall ThreadIbcntl (void); - - -/**************************************************************************/ -/* NI-488.2 Function Prototypes */ - -extern void __stdcall AllSpoll (int boardID, Addr4882_t * addrlist, PSHORT results); -extern void __stdcall DevClear (int boardID, Addr4882_t addr); -extern void __stdcall DevClearList (int boardID, Addr4882_t * addrlist); -extern void __stdcall EnableLocal (int boardID, Addr4882_t * addrlist); -extern void __stdcall EnableRemote (int boardID, Addr4882_t * addrlist); -extern void __stdcall FindLstn (int boardID, Addr4882_t * addrlist, Addr4882_t * results, int limit); -extern void __stdcall FindRQS (int boardID, Addr4882_t * addrlist, PSHORT dev_stat); -extern void __stdcall PPoll (int boardID, PSHORT result); -extern void __stdcall PPollConfig (int boardID, Addr4882_t addr, int dataLine, int lineSense); -extern void __stdcall PPollUnconfig (int boardID, Addr4882_t * addrlist); -extern void __stdcall PassControl (int boardID, Addr4882_t addr); -extern void __stdcall RcvRespMsg (int boardID, PVOID buffer, long cnt, int Termination); -extern void __stdcall ReadStatusByte(int boardID, Addr4882_t addr, PSHORT result); -extern void __stdcall Receive (int boardID, Addr4882_t addr, PVOID buffer, long cnt, int Termination); -extern void __stdcall ReceiveSetup (int boardID, Addr4882_t addr); -extern void __stdcall ResetSys (int boardID, Addr4882_t * addrlist); -extern void __stdcall Send (int boardID, Addr4882_t addr, PVOID databuf, long datacnt, int eotMode); -extern void __stdcall SendCmds (int boardID, PVOID buffer, long cnt); -extern void __stdcall SendDataBytes (int boardID, PVOID buffer, long cnt, int eot_mode); -extern void __stdcall SendIFC (int boardID); -extern void __stdcall SendLLO (int boardID); -extern void __stdcall SendList (int boardID, Addr4882_t * addrlist, PVOID databuf, long datacnt, int eotMode); -extern void __stdcall SendSetup (int boardID, Addr4882_t * addrlist); -extern void __stdcall SetRWLS (int boardID, Addr4882_t * addrlist); -extern void __stdcall TestSRQ (int boardID, PSHORT result); -extern void __stdcall TestSys (int boardID, Addr4882_t * addrlist, PSHORT results); -extern void __stdcall Trigger (int boardID, Addr4882_t addr); -extern void __stdcall TriggerList (int boardID, Addr4882_t * addrlist); -extern void __stdcall WaitSRQ (int boardID, PSHORT result); - -#endif - - -#ifdef __cplusplus -} -#endif - - -#endif // DECL_32_H - +/* + * + * + * Win32 include file + * for accessing the 32-bit GPIB DLL (gpib-32.dll) + * + * + * Contains user variables (ibsta, iberr, ibcnt, ibcntl), + * function prototypes and useful defined constants for + * calling NI-488 and NI-488.2 routines from a Microsoft + * C/C++ Win32 application. + * + * + * Copyright 1998 National Instruments Corporation + * + */ + +#ifndef DECL_32_H // ensure we are only included once +#define DECL_32_H + +#include "windows.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************************************************/ +/* HANDY CONSTANTS FOR USE BY APPLICATION PROGRAMS ... */ +/***************************************************************************/ +#define UNL 0x3f /* GPIB unlisten command */ +#define UNT 0x5f /* GPIB untalk command */ +#define GTL 0x01 /* GPIB go to local */ +#define SDC 0x04 /* GPIB selected device clear */ +#define PPC 0x05 /* GPIB parallel poll configure */ +#define GET 0x08 /* GPIB group execute trigger */ +#define TCT 0x09 /* GPIB take control */ +#define LLO 0x11 /* GPIB local lock out */ +#define DCL 0x14 /* GPIB device clear */ +#define PPU 0.16 /* GPIB parallel poll unconfigure */ +#define SPE 0x18 /* GPIB serial poll enable */ +#define SPD 0x19 /* GPIB serial poll disable */ +#define PPE 0x60 /* GPIB parallel poll enable */ +#define PPD 0x70 /* GPIB parallel poll disable */ + +/* GPIB status bit vector : */ +/* global variable ibsta and wait mask */ + +#define ERR (1<<15) /* Error detected */ +#define TIMO (1<<14) /* Timeout */ +#define END (1<<13) /* EOI or EOS detected */ +#define SRQI (1<<12) /* SRQ detected by CIC */ +#define RQS (1<<11) /* Device needs service */ +#define CMPL (1<<8) /* I/O completed */ +#define LOK (1<<7) /* Local lockout state */ +#define REM (1<<6) /* Remote state */ +#define CIC (1<<5) /* Controller-in-Charge */ +#define ATN (1<<4) /* Attention asserted */ +#define TACS (1<<3) /* Talker active */ +#define LACS (1<<2) /* Listener active */ +#define DTAS (1<<1) /* Device trigger state */ +#define DCAS (1<<0) /* Device clear state */ + +/* Error messages returned in global variable iberr */ + +#define EDVR 0 /* System error */ +#define ECIC 1 /* Function requires GPIB board to be CIC */ +#define ENOL 2 /* Write function detected no Listeners */ +#define EADR 3 /* Interface board not addressed correctly*/ +#define EARG 4 /* Invalid argument to function call */ +#define ESAC 5 /* Function requires GPIB board to be SAC */ +#define EABO 6 /* I/O operation aborted */ +#define ENEB 7 /* Non-existent interface board */ +#define EDMA 8 /* Error performing DMA */ +#define EOIP 10 /* I/O operation started before previous */ + /* operation completed */ +#define ECAP 11 /* No capability for intended operation */ +#define EFSO 12 /* File system operation error */ +#define EBUS 14 /* Command error during device call */ +#define ESTB 15 /* Serial poll status byte lost */ +#define ESRQ 16 /* SRQ remains asserted */ +#define ETAB 20 /* The return buffer is full. */ +#define ELCK 21 /* Address or board is locked. */ + +/* EOS mode bits */ + +#define BIN (1<<12) /* Eight bit compare */ +#define XEOS (1<<11) /* Send END with EOS byte */ +#define REOS (1<<10) /* Terminate read on EOS */ + +/* Timeout values and meanings */ + +#define TNONE 0 /* Infinite timeout (disabled) */ +#define T10us 1 /* Timeout of 10 us (ideal) */ +#define T30us 2 /* Timeout of 30 us (ideal) */ +#define T100us 3 /* Timeout of 100 us (ideal) */ +#define T300us 4 /* Timeout of 300 us (ideal) */ +#define T1ms 5 /* Timeout of 1 ms (ideal) */ +#define T3ms 6 /* Timeout of 3 ms (ideal) */ +#define T10ms 7 /* Timeout of 10 ms (ideal) */ +#define T30ms 8 /* Timeout of 30 ms (ideal) */ +#define T100ms 9 /* Timeout of 100 ms (ideal) */ +#define T300ms 10 /* Timeout of 300 ms (ideal) */ +#define T1s 11 /* Timeout of 1 s (ideal) */ +#define T3s 12 /* Timeout of 3 s (ideal) */ +#define T10s 13 /* Timeout of 10 s (ideal) */ +#define T30s 14 /* Timeout of 30 s (ideal) */ +#define T100s 15 /* Timeout of 100 s (ideal) */ +#define T300s 16 /* Timeout of 300 s (ideal) */ +#define T1000s 17 /* Timeout of 1000 s (ideal) */ + + +/* IBLN Constants */ +#define NO_SAD 0 +#define ALL_SAD -1 + + +/* The following constants are used for the second parameter of the + * ibconfig function. They are the "option" selection codes. + */ +#define IbcPAD 0x0001 /* Primary Address */ +#define IbcSAD 0x0002 /* Secondary Address */ +#define IbcTMO 0x0003 /* Timeout Value */ +#define IbcEOT 0x0004 /* Send EOI with last data byte? */ +#define IbcPPC 0x0005 /* Parallel Poll Configure */ +#define IbcREADDR 0x0006 /* Repeat Addressing */ +#define IbcAUTOPOLL 0x0007 /* Disable Auto Serial Polling */ +#define IbcCICPROT 0x0008 /* Use the CIC Protocol? */ +#define IbcIRQ 0x0009 /* Use PIO for I/O */ +#define IbcSC 0x000A /* Board is System Controller? */ +#define IbcSRE 0x000B /* Assert SRE on device calls? */ +#define IbcEOSrd 0x000C /* Terminate reads on EOS */ +#define IbcEOSwrt 0x000D /* Send EOI with EOS character */ +#define IbcEOScmp 0x000E /* Use 7 or 8-bit EOS compare */ +#define IbcEOSchar 0x000F /* The EOS character. */ +#define IbcPP2 0x0010 /* Use Parallel Poll Mode 2. */ +#define IbcTIMING 0x0011 /* NORMAL, HIGH, or VERY_HIGH timing. */ +#define IbcDMA 0x0012 /* Use DMA for I/O */ +#define IbcReadAdjust 0x0013 /* Swap bytes during an ibrd. */ +#define IbcWriteAdjust 0x014 /* Swap bytes during an ibwrt. */ +#define IbcSendLLO 0x0017 /* Enable/disable the sending of LLO. */ +#define IbcSPollTime 0x0018 /* Set the timeout value for serial polls. */ +#define IbcPPollTime 0x0019 /* Set the parallel poll length period. */ +#define IbcEndBitIsNormal 0x001A /* Remove EOS from END bit of IBSTA. */ +#define IbcUnAddr 0x001B /* Enable/disable device unaddressing. */ +#define IbcSignalNumber 0x001C /* Set UNIX signal number - unsupported */ +#define IbcBlockIfLocked 0x001D /* Enable/disable blocking for locked boards/devices */ +#define IbcHSCableLength 0x001F /* Length of cable specified for high speed timing.*/ +#define IbcIst 0x0020 /* Set the IST bit. */ +#define IbcRsv 0x0021 /* Set the RSV byte. */ + +/* + * Constants that can be used (in addition to the ibconfig constants) + * when calling the ibask() function. + */ + +#define IbaPAD IbcPAD +#define IbaSAD IbcSAD +#define IbaTMO IbcTMO +#define IbaEOT IbcEOT +#define IbaPPC IbcPPC +#define IbaREADDR IbcREADDR +#define IbaAUTOPOLL IbcAUTOPOLL +#define IbaCICPROT IbcCICPROT +#define IbaIRQ IbcIRQ +#define IbaSC IbcSC +#define IbaSRE IbcSRE +#define IbaEOSrd IbcEOSrd +#define IbaEOSwrt IbcEOSwrt +#define IbaEOScmp IbcEOScmp +#define IbaEOSchar IbcEOSchar +#define IbaPP2 IbcPP2 +#define IbaTIMING IbcTIMING +#define IbaDMA IbcDMA +#define IbaReadAdjust IbcReadAdjust +#define IbaWriteAdjust IbcWriteAdjust +#define IbaSendLLO IbcSendLLO +#define IbaSPollTime IbcSPollTime +#define IbaPPollTime IbcPPollTime +#define IbaEndBitIsNormal IbcEndBitIsNormal +#define IbaUnAddr IbcUnAddr +#define IbaSignalNumber IbcSignalNumber +#define IbaBlockIfLocked IbcBlockIfLocked +#define IbaHSCableLength IbcHSCableLength +#define IbaIst IbcIst +#define IbaRsv IbcRsv + +#define IbaBNA 0x0200 /* A device's access board. */ + + +/* Values used by the Send 488.2 command. */ +#define NULLend 0x00 /* Do nothing at the end of a transfer.*/ +#define NLend 0x01 /* Send NL with EOI after a transfer. */ +#define DABend 0x02 /* Send EOI with the last DAB. */ + +/* Value used by the 488.2 Receive command. + */ +#define STOPend 0x0100 + + +/* Address type (for 488.2 calls) */ + +typedef short Addr4882_t; /* System dependent: must be 16 bits */ + +/* + * This macro can be used to easily create an entry in address list + * that is required by many of the 488.2 functions. The primary address goes in the + * lower 8-bits and the secondary address goes in the upper 8-bits. + */ +#define MakeAddr(pad, sad) ((Addr4882_t)(((pad)&0xFF) | ((sad)<<8))) + +/* + * This value is used to terminate an address list. It should be + * assigned to the last entry. + */ +#ifndef NOADDR +#define NOADDR (Addr4882_t)((unsigned short)0xFFFF) +#endif + +/* + * The following two macros are used to "break apart" an address list + * entry. They take an unsigned integer and return either the primary + * or secondary address stored in the integer. + */ +#define GetPAD(val) ((val) & 0xFF) +#define GetSAD(val) (((val) >> 8) & 0xFF) + +/* iblines constants */ + +#define ValidEOI (short)0x0080 +#define ValidATN (short)0x0040 +#define ValidSRQ (short)0x0020 +#define ValidREN (short)0x0010 +#define ValidIFC (short)0x0008 +#define ValidNRFD (short)0x0004 +#define ValidNDAC (short)0x0002 +#define ValidDAV (short)0x0001 +#define BusEOI (short)0x8000 +#define BusATN (short)0x4000 +#define BusSRQ (short)0x2000 +#define BusREN (short)0x1000 +#define BusIFC (short)0x0800 +#define BusNRFD (short)0x0400 +#define BusNDAC (short)0x0200 +#define BusDAV (short)0x0100 + +/**** + **** typedef for ibnotify callback **** + ****/ +typedef int (__stdcall * GpibNotifyCallback_t)(int, int, int, long, PVOID); + +#define IBNOTIFY_REARM_FAILED 0xE00A003F + + +/*************************************************************************/ +/* */ +/* iblockx and ibunlockx definitions --- for use with GPIB-ENET only !! */ +/* */ +/*************************************************************************/ +#define TIMMEDIATE -1 +#define TINFINITE -2 +#define MAX_LOCKSHARENAME_LENGTH 64 + +#if defined(UNICODE) + #define iblockx iblockxW +#else + #define iblockx iblockxA +#endif + +extern int __stdcall iblockxA (int ud, int LockWaitTime, PCHAR LockShareName); +extern int __stdcall iblockxW (int ud, int LockWaitTime, PWCHAR LockShareName); +extern int __stdcall ibunlockx (int ud); + + +/***************************************************************************/ +/* IBSTA, IBERR, IBCNT, IBCNTL and FUNCTION PROTOTYPES */ +/* ( only included if not accessing the 32-bit DLL directly ) */ +/***************************************************************************/ +#if !defined(GPIB_DIRECT_ACCESS) + +/* + * Set up access to the user variables (ibsta, iberr, ibcnt, ibcntl). + * These are declared and exported by the 32-bit DLL. Separate copies + * exist for each process that accesses the DLL. They are shared by + * multiple threads of a single process. + */ + +extern int ibsta; +extern int iberr; +extern int ibcnt; +extern long ibcntl; + + +#if defined(UNICODE) + #define ibbna ibbnaW + #define ibfind ibfindW + #define ibrdf ibrdfW + #define ibwrtf ibwrtfW +#else + #define ibbna ibbnaA + #define ibfind ibfindA + #define ibrdf ibrdfA + #define ibwrtf ibwrtfA +#endif + +/* + * Extern 32-bit GPIB DLL functions + */ + +/* NI-488 Function Prototypes */ +extern int __stdcall ibfindA (LPCSTR udname); +extern int __stdcall ibbnaA (int ud, LPCSTR udname); +extern int __stdcall ibrdfA (int ud, LPCSTR filename); +extern int __stdcall ibwrtfA (int ud, LPCSTR filename); + +extern int __stdcall ibfindW (LPCWSTR udname); +extern int __stdcall ibbnaW (int ud, LPCWSTR udname); +extern int __stdcall ibrdfW (int ud, LPCWSTR filename); +extern int __stdcall ibwrtfW (int ud, LPCWSTR filename); + +extern int __stdcall ibask (int ud, int option, PINT v); +extern int __stdcall ibcac (int ud, int v); +extern int __stdcall ibclr (int ud); +extern int __stdcall ibcmd (int ud, PVOID buf, long cnt); +extern int __stdcall ibcmda (int ud, PVOID buf, long cnt); +extern int __stdcall ibconfig (int ud, int option, int v); +extern int __stdcall ibdev (int boardID, int pad, int sad, int tmo, int eot, int eos); +extern int __stdcall ibdiag (int ud, PVOID buf, long cnt); +extern int __stdcall ibdma (int ud, int v); +extern int __stdcall ibeos (int ud, int v); +extern int __stdcall ibeot (int ud, int v); +extern int __stdcall ibgts (int ud, int v); +extern int __stdcall ibist (int ud, int v); +extern int __stdcall iblines (int ud, PSHORT result); +extern int __stdcall ibln (int ud, int pad, int sad, PSHORT listen); +extern int __stdcall ibloc (int ud); +extern int __stdcall ibnotify (int ud, int mask, GpibNotifyCallback_t Callback, PVOID RefData); +extern int __stdcall ibonl (int ud, int v); +extern int __stdcall ibpad (int ud, int v); +extern int __stdcall ibpct (int ud); +extern int __stdcall ibpoke (int ud, long option, long v); +extern int __stdcall ibppc (int ud, int v); +extern int __stdcall ibrd (int ud, PVOID buf, long cnt); +extern int __stdcall ibrda (int ud, PVOID buf, long cnt); +extern int __stdcall ibrpp (int ud, PCHAR ppr); +extern int __stdcall ibrsc (int ud, int v); +extern int __stdcall ibrsp (int ud, PCHAR spr); +extern int __stdcall ibrsv (int ud, int v); +extern int __stdcall ibsad (int ud, int v); +extern int __stdcall ibsic (int ud); +extern int __stdcall ibsre (int ud, int v); +extern int __stdcall ibstop (int ud); +extern int __stdcall ibtmo (int ud, int v); +extern int __stdcall ibtrg (int ud); +extern int __stdcall ibwait (int ud, int mask); +extern int __stdcall ibwrt (int ud, PVOID buf, long cnt); +extern int __stdcall ibwrta (int ud, PVOID buf, long cnt); + +// GPIB-ENET only functions to support locking across machines +extern int __stdcall iblock (int ud); +extern int __stdcall ibunlock (int ud); + +/**************************************************************************/ +/* Functions to access Thread-Specific copies of the GPIB global vars */ + +extern int __stdcall ThreadIbsta (void); +extern int __stdcall ThreadIberr (void); +extern int __stdcall ThreadIbcnt (void); +extern long __stdcall ThreadIbcntl (void); + + +/**************************************************************************/ +/* NI-488.2 Function Prototypes */ + +extern void __stdcall AllSpoll (int boardID, Addr4882_t * addrlist, PSHORT results); +extern void __stdcall DevClear (int boardID, Addr4882_t addr); +extern void __stdcall DevClearList (int boardID, Addr4882_t * addrlist); +extern void __stdcall EnableLocal (int boardID, Addr4882_t * addrlist); +extern void __stdcall EnableRemote (int boardID, Addr4882_t * addrlist); +extern void __stdcall FindLstn (int boardID, Addr4882_t * addrlist, Addr4882_t * results, int limit); +extern void __stdcall FindRQS (int boardID, Addr4882_t * addrlist, PSHORT dev_stat); +extern void __stdcall PPoll (int boardID, PSHORT result); +extern void __stdcall PPollConfig (int boardID, Addr4882_t addr, int dataLine, int lineSense); +extern void __stdcall PPollUnconfig (int boardID, Addr4882_t * addrlist); +extern void __stdcall PassControl (int boardID, Addr4882_t addr); +extern void __stdcall RcvRespMsg (int boardID, PVOID buffer, long cnt, int Termination); +extern void __stdcall ReadStatusByte(int boardID, Addr4882_t addr, PSHORT result); +extern void __stdcall Receive (int boardID, Addr4882_t addr, PVOID buffer, long cnt, int Termination); +extern void __stdcall ReceiveSetup (int boardID, Addr4882_t addr); +extern void __stdcall ResetSys (int boardID, Addr4882_t * addrlist); +extern void __stdcall Send (int boardID, Addr4882_t addr, PVOID databuf, long datacnt, int eotMode); +extern void __stdcall SendCmds (int boardID, PVOID buffer, long cnt); +extern void __stdcall SendDataBytes (int boardID, PVOID buffer, long cnt, int eot_mode); +extern void __stdcall SendIFC (int boardID); +extern void __stdcall SendLLO (int boardID); +extern void __stdcall SendList (int boardID, Addr4882_t * addrlist, PVOID databuf, long datacnt, int eotMode); +extern void __stdcall SendSetup (int boardID, Addr4882_t * addrlist); +extern void __stdcall SetRWLS (int boardID, Addr4882_t * addrlist); +extern void __stdcall TestSRQ (int boardID, PSHORT result); +extern void __stdcall TestSys (int boardID, Addr4882_t * addrlist, PSHORT results); +extern void __stdcall Trigger (int boardID, Addr4882_t addr); +extern void __stdcall TriggerList (int boardID, Addr4882_t * addrlist); +extern void __stdcall WaitSRQ (int boardID, PSHORT result); + +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif // DECL_32_H + diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv.c b/src/mod/endpoints/mod_gsmopen/win_iconv.c index 4eb5433196..e60f714889 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv.c +++ b/src/mod/endpoints/mod_gsmopen/win_iconv.c @@ -1,1986 +1,1986 @@ -/* - * iconv library using Win32 API to conversion. - * - * This file is placed in the public domain. - * - * Last Change: 2009-07-06 - * - * ENVIRONMENT VARIABLE: - * WINICONV_LIBICONV_DLL - * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If - * loading the DLL or iconv_open() failed, falls back to internal - * conversion. If a few DLL are specified as comma separated list, - * the first loadable DLL is used. The DLL should have iconv_open(), - * iconv_close() and iconv(). Or libiconv_open(), libiconv_close() - * and libiconv(). - * (only available when USE_LIBICONV_DLL is defined at compile time) - * - * Win32 API does not support strict encoding conversion for some - * codepage. And MLang function drop or replace invalid bytes and does - * not return useful error status as iconv. This implementation cannot - * be used for encoding validation purpose. - */ - -/* for WC_NO_BEST_FIT_CHARS */ -#ifndef WINVER -# define WINVER 0x0500 -#endif - -#include -#include -#include -#include - -#if 0 -# define MAKE_EXE -# define MAKE_DLL -# define USE_LIBICONV_DLL -#endif - -#if !defined(DEFAULT_LIBICONV_DLL) -# define DEFAULT_LIBICONV_DLL "" -#endif - -#define MB_CHAR_MAX 16 - -#define UNICODE_MODE_BOM_DONE 1 -#define UNICODE_MODE_SWAPPED 2 - -#define FLAG_USE_BOM 1 -#define FLAG_TRANSLIT 2 /* //TRANSLIT */ -#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */ - -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; - -typedef void* iconv_t; - -iconv_t iconv_open(const char *tocode, const char *fromcode); -int iconv_close(iconv_t cd); -size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); - -/* libiconv interface for vim */ -#if defined(MAKE_DLL) -int -iconvctl (iconv_t cd, int request, void* argument) -{ - /* not supported */ - return 0; -} -#endif - -typedef struct compat_t compat_t; -typedef struct csconv_t csconv_t; -typedef struct rec_iconv_t rec_iconv_t; - -typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode); -typedef int (*f_iconv_close)(iconv_t cd); -typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); -typedef int* (*f_errno)(void); -typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); -typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); - -#define COMPAT_IN 1 -#define COMPAT_OUT 2 - -/* unicode mapping for compatibility with other conversion table. */ -struct compat_t { - uint in; - uint out; - uint flag; -}; - -struct csconv_t { - int codepage; - int flags; - f_mbtowc mbtowc; - f_wctomb wctomb; - f_mblen mblen; - f_flush flush; - DWORD mode; - compat_t *compat; -}; - -struct rec_iconv_t { - iconv_t cd; - f_iconv_close iconv_close; - f_iconv iconv; - f_errno _errno; - csconv_t from; - csconv_t to; -#if defined(USE_LIBICONV_DLL) - HMODULE hlibiconv; -#endif -}; - -static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); -static int win_iconv_close(iconv_t cd); -static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); - -static int load_mlang(); -static int make_csconv(const char *name, csconv_t *cv); -static int name_to_codepage(const char *name); -static uint utf16_to_ucs4(const ushort *wbuf); -static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); -static int mbtowc_flags(int codepage); -static int must_use_null_useddefaultchar(int codepage); -static char *strrstr(const char *str, const char *token); -static char *xstrndup(const char *s, size_t n); -static int seterror(int err); - -#if defined(USE_LIBICONV_DLL) -static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); -static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size); -static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname); - -static HMODULE hwiniconv; -#endif - -static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); - -static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); - -static struct { - int codepage; - const char *name; -} codepage_alias[] = { - {65001, "CP65001"}, - {65001, "UTF8"}, - {65001, "UTF-8"}, - - {1200, "CP1200"}, - {1200, "UTF16LE"}, - {1200, "UTF-16LE"}, - {1200, "UCS-2LE"}, - - {1201, "CP1201"}, - {1201, "UTF16BE"}, - {1201, "UTF-16BE"}, - {1201, "UCS-2BE"}, - {1201, "unicodeFFFE"}, - - {12000, "CP12000"}, - {12000, "UTF32LE"}, - {12000, "UTF-32LE"}, - - {12001, "CP12001"}, - {12001, "UTF32BE"}, - {12001, "UTF-32BE"}, - -#ifndef GLIB_COMPILATION - /* - * Default is big endian. - * See rfc2781 4.3 Interpreting text labelled as UTF-16. - */ - {1201, "UTF16"}, - {1201, "UTF-16"}, - {12001, "UTF32"}, - {12001, "UTF-32"}, -#else - /* Default is little endian, because the platform is */ - {1200, "UTF16"}, - {1200, "UTF-16"}, - {1200, "UCS-2"}, - {12000, "UTF32"}, - {12000, "UTF-32"}, -#endif - - /* copy from libiconv `iconv -l` */ - /* !IsValidCodePage(367) */ - {20127, "ANSI_X3.4-1968"}, - {20127, "ANSI_X3.4-1986"}, - {20127, "ASCII"}, - {20127, "CP367"}, - {20127, "IBM367"}, - {20127, "ISO-IR-6"}, - {20127, "ISO646-US"}, - {20127, "ISO_646.IRV:1991"}, - {20127, "US"}, - {20127, "US-ASCII"}, - {20127, "CSASCII"}, - - /* !IsValidCodePage(819) */ - {1252, "CP819"}, - {1252, "IBM819"}, - {28591, "ISO-8859-1"}, - {28591, "ISO-IR-100"}, - {28591, "ISO8859-1"}, - {28591, "ISO_8859-1"}, - {28591, "ISO_8859-1:1987"}, - {28591, "L1"}, - {28591, "LATIN1"}, - {28591, "CSISOLATIN1"}, - - {1250, "CP1250"}, - {1250, "MS-EE"}, - {1250, "WINDOWS-1250"}, - - {1251, "CP1251"}, - {1251, "MS-CYRL"}, - {1251, "WINDOWS-1251"}, - - {1252, "CP1252"}, - {1252, "MS-ANSI"}, - {1252, "WINDOWS-1252"}, - - {1253, "CP1253"}, - {1253, "MS-GREEK"}, - {1253, "WINDOWS-1253"}, - - {1254, "CP1254"}, - {1254, "MS-TURK"}, - {1254, "WINDOWS-1254"}, - - {1255, "CP1255"}, - {1255, "MS-HEBR"}, - {1255, "WINDOWS-1255"}, - - {1256, "CP1256"}, - {1256, "MS-ARAB"}, - {1256, "WINDOWS-1256"}, - - {1257, "CP1257"}, - {1257, "WINBALTRIM"}, - {1257, "WINDOWS-1257"}, - - {1258, "CP1258"}, - {1258, "WINDOWS-1258"}, - - {850, "850"}, - {850, "CP850"}, - {850, "IBM850"}, - {850, "CSPC850MULTILINGUAL"}, - - /* !IsValidCodePage(862) */ - {862, "862"}, - {862, "CP862"}, - {862, "IBM862"}, - {862, "CSPC862LATINHEBREW"}, - - {866, "866"}, - {866, "CP866"}, - {866, "IBM866"}, - {866, "CSIBM866"}, - - /* !IsValidCodePage(154) */ - {154, "CP154"}, - {154, "CYRILLIC-ASIAN"}, - {154, "PT154"}, - {154, "PTCP154"}, - {154, "CSPTCP154"}, - - /* !IsValidCodePage(1133) */ - {1133, "CP1133"}, - {1133, "IBM-CP1133"}, - - {874, "CP874"}, - {874, "WINDOWS-874"}, - - /* !IsValidCodePage(51932) */ - {51932, "CP51932"}, - {51932, "MS51932"}, - {51932, "WINDOWS-51932"}, - {51932, "EUC-JP"}, - - {932, "CP932"}, - {932, "MS932"}, - {932, "SHIFFT_JIS"}, - {932, "SHIFFT_JIS-MS"}, - {932, "SJIS"}, - {932, "SJIS-MS"}, - {932, "SJIS-OPEN"}, - {932, "SJIS-WIN"}, - {932, "WINDOWS-31J"}, - {932, "WINDOWS-932"}, - {932, "CSWINDOWS31J"}, - - {50221, "CP50221"}, - {50221, "ISO-2022-JP"}, - {50221, "ISO-2022-JP-MS"}, - {50221, "ISO2022-JP"}, - {50221, "ISO2022-JP-MS"}, - {50221, "MS50221"}, - {50221, "WINDOWS-50221"}, - - {936, "CP936"}, - {936, "GBK"}, - {936, "MS936"}, - {936, "WINDOWS-936"}, - - {950, "CP950"}, - {950, "BIG5"}, - - {949, "CP949"}, - {949, "UHC"}, - {949, "EUC-KR"}, - - {1361, "CP1361"}, - {1361, "JOHAB"}, - - {437, "437"}, - {437, "CP437"}, - {437, "IBM437"}, - {437, "CSPC8CODEPAGE437"}, - - {737, "CP737"}, - - {775, "CP775"}, - {775, "IBM775"}, - {775, "CSPC775BALTIC"}, - - {852, "852"}, - {852, "CP852"}, - {852, "IBM852"}, - {852, "CSPCP852"}, - - /* !IsValidCodePage(853) */ - {853, "CP853"}, - - {855, "855"}, - {855, "CP855"}, - {855, "IBM855"}, - {855, "CSIBM855"}, - - {857, "857"}, - {857, "CP857"}, - {857, "IBM857"}, - {857, "CSIBM857"}, - - /* !IsValidCodePage(858) */ - {858, "CP858"}, - - {860, "860"}, - {860, "CP860"}, - {860, "IBM860"}, - {860, "CSIBM860"}, - - {861, "861"}, - {861, "CP-IS"}, - {861, "CP861"}, - {861, "IBM861"}, - {861, "CSIBM861"}, - - {863, "863"}, - {863, "CP863"}, - {863, "IBM863"}, - {863, "CSIBM863"}, - - {864, "CP864"}, - {864, "IBM864"}, - {864, "CSIBM864"}, - - {865, "865"}, - {865, "CP865"}, - {865, "IBM865"}, - {865, "CSIBM865"}, - - {869, "869"}, - {869, "CP-GR"}, - {869, "CP869"}, - {869, "IBM869"}, - {869, "CSIBM869"}, - - /* !IsValidCodePage(1152) */ - {1125, "CP1125"}, - - /* - * Code Page Identifiers - * http://msdn2.microsoft.com/en-us/library/ms776446.aspx - */ - {37, "IBM037"}, /* IBM EBCDIC US-Canada */ - {437, "IBM437"}, /* OEM United States */ - {500, "IBM500"}, /* IBM EBCDIC International */ - {708, "ASMO-708"}, /* Arabic (ASMO 708) */ - /* 709 Arabic (ASMO-449+, BCON V4) */ - /* 710 Arabic - Transparent Arabic */ - {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */ - {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */ - {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */ - {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */ - {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */ - {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */ - {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */ - {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */ - {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */ - {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */ - {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */ - {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */ - {864, "IBM864"}, /* OEM Arabic; Arabic (864) */ - {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */ - {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */ - {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */ - {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ - {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ - {875, "cp875"}, /* IBM EBCDIC Greek Modern */ - {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ - {932, "shift-jis"}, /* alternative name for it */ - {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ - {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */ - {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ - {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */ - {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */ - {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ - {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ - {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ - {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ - {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ - {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ - {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ - {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ - {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ - {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ - {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */ - {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */ - {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */ - {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */ - {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */ - {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */ - {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */ - {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */ - {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ - {1361, "Johab"}, /* Korean (Johab) */ - {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */ - {10001, "x-mac-japanese"}, /* Japanese (Mac) */ - {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ - {10003, "x-mac-korean"}, /* Korean (Mac) */ - {10004, "x-mac-arabic"}, /* Arabic (Mac) */ - {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */ - {10006, "x-mac-greek"}, /* Greek (Mac) */ - {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */ - {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ - {10010, "x-mac-romanian"}, /* Romanian (Mac) */ - {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */ - {10021, "x-mac-thai"}, /* Thai (Mac) */ - {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */ - {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */ - {10081, "x-mac-turkish"}, /* Turkish (Mac) */ - {10082, "x-mac-croatian"}, /* Croatian (Mac) */ - {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */ - {20001, "x-cp20001"}, /* TCA Taiwan */ - {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */ - {20003, "x-cp20003"}, /* IBM5550 Taiwan */ - {20004, "x-cp20004"}, /* TeleText Taiwan */ - {20005, "x-cp20005"}, /* Wang Taiwan */ - {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ - {20106, "x-IA5-German"}, /* IA5 German (7-bit) */ - {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */ - {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */ - {20127, "us-ascii"}, /* US-ASCII (7-bit) */ - {20261, "x-cp20261"}, /* T.61 */ - {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */ - {20273, "IBM273"}, /* IBM EBCDIC Germany */ - {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */ - {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */ - {20280, "IBM280"}, /* IBM EBCDIC Italy */ - {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */ - {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */ - {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */ - {20297, "IBM297"}, /* IBM EBCDIC France */ - {20420, "IBM420"}, /* IBM EBCDIC Arabic */ - {20423, "IBM423"}, /* IBM EBCDIC Greek */ - {20424, "IBM424"}, /* IBM EBCDIC Hebrew */ - {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */ - {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */ - {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */ - {20871, "IBM871"}, /* IBM EBCDIC Icelandic */ - {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */ - {20905, "IBM905"}, /* IBM EBCDIC Turkish */ - {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ - {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */ - {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ - {20949, "x-cp20949"}, /* Korean Wansung */ - {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ - /* 21027 (deprecated) */ - {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ - {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ - {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ - {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ - {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ - {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */ - {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */ - {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */ - {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */ - {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */ - {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */ - {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */ - {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */ - {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */ - {28597, "iso8859-7"}, /* ISO 8859-7 Greek */ - {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ - {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ - {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */ - {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */ - {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */ - {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */ - {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */ - {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */ - {29001, "x-Europa"}, /* Europa 3 */ - {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ - {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ - {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ - {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ - {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ - {50225, "iso-2022-kr"}, /* ISO 2022 Korean */ - {50225, "iso2022-kr"}, /* ISO 2022 Korean */ - {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ - /* 50229 ISO 2022 Traditional Chinese */ - /* 50930 EBCDIC Japanese (Katakana) Extended */ - /* 50931 EBCDIC US-Canada and Japanese */ - /* 50933 EBCDIC Korean Extended and Korean */ - /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ - /* 50936 EBCDIC Simplified Chinese */ - /* 50937 EBCDIC US-Canada and Traditional Chinese */ - /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ - {51932, "euc-jp"}, /* EUC Japanese */ - {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */ - {51949, "euc-kr"}, /* EUC Korean */ - /* 51950 EUC Traditional Chinese */ - {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ - {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ - {57002, "x-iscii-de"}, /* ISCII Devanagari */ - {57003, "x-iscii-be"}, /* ISCII Bengali */ - {57004, "x-iscii-ta"}, /* ISCII Tamil */ - {57005, "x-iscii-te"}, /* ISCII Telugu */ - {57006, "x-iscii-as"}, /* ISCII Assamese */ - {57007, "x-iscii-or"}, /* ISCII Oriya */ - {57008, "x-iscii-ka"}, /* ISCII Kannada */ - {57009, "x-iscii-ma"}, /* ISCII Malayalam */ - {57010, "x-iscii-gu"}, /* ISCII Gujarati */ - {57011, "x-iscii-pa"}, /* ISCII Punjabi */ - - {0, NULL} -}; - -/* - * SJIS SHIFTJIS table CP932 table - * ---- --------------------------- -------------------------------- - * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS - * 7E U+203E OVERLINE U+007E TILDE - * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR - * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS - * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE - * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO - * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS - * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN - * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN - * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN - * - * EUC-JP and ISO-2022-JP should be compatible with CP932. - * - * Kernel and MLang have different Unicode mapping table. Make sure - * which API is used. - */ -static compat_t cp932_compat[] = { - {0x00A5, 0x005C, COMPAT_OUT}, - {0x203E, 0x007E, COMPAT_OUT}, - {0x2014, 0x2015, COMPAT_OUT}, - {0x301C, 0xFF5E, COMPAT_OUT}, - {0x2016, 0x2225, COMPAT_OUT}, - {0x2212, 0xFF0D, COMPAT_OUT}, - {0x00A2, 0xFFE0, COMPAT_OUT}, - {0x00A3, 0xFFE1, COMPAT_OUT}, - {0x00AC, 0xFFE2, COMPAT_OUT}, - {0, 0, 0} -}; - -static compat_t cp20932_compat[] = { - {0x00A5, 0x005C, COMPAT_OUT}, - {0x203E, 0x007E, COMPAT_OUT}, - {0x2014, 0x2015, COMPAT_OUT}, - {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, - {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, - {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, - {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, - {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, - {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, - {0, 0, 0} -}; - -static compat_t *cp51932_compat = cp932_compat; - -/* cp20932_compat for kernel. cp932_compat for mlang. */ -static compat_t *cp5022x_compat = cp932_compat; - -typedef HRESULT (WINAPI *CONVERTINETSTRING)( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - DWORD dwDstEncoding, - LPCSTR lpSrcStr, - LPINT lpnSrcSize, - LPBYTE lpDstStr, - LPINT lpnDstSize -); -typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - LPCSTR lpSrcStr, - LPINT lpnMultiCharCount, - LPWSTR lpDstStr, - LPINT lpnWideCharCount -); -typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( - LPDWORD lpdwMode, - DWORD dwEncoding, - LPCWSTR lpSrcStr, - LPINT lpnWideCharCount, - LPSTR lpDstStr, - LPINT lpnMultiCharCount -); -typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)( - DWORD dwSrcEncoding, - DWORD dwDstEncoding -); -typedef HRESULT (WINAPI *LCIDTORFC1766A)( - LCID Locale, - LPSTR pszRfc1766, - int nChar -); -typedef HRESULT (WINAPI *LCIDTORFC1766W)( - LCID Locale, - LPWSTR pszRfc1766, - int nChar -); -typedef HRESULT (WINAPI *RFC1766TOLCIDA)( - LCID *pLocale, - LPSTR pszRfc1766 -); -typedef HRESULT (WINAPI *RFC1766TOLCIDW)( - LCID *pLocale, - LPWSTR pszRfc1766 -); -static CONVERTINETSTRING ConvertINetString; -static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; -static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; -static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable; -static LCIDTORFC1766A LcidToRfc1766A; -static RFC1766TOLCIDA Rfc1766ToLcidA; - -static int -load_mlang() -{ - HMODULE h; - if (ConvertINetString != NULL) - return TRUE; - h = LoadLibrary("mlang.dll"); - if (!h) - return FALSE; - ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString"); - ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode"); - ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte"); - IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable"); - LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A"); - Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA"); - return TRUE; -} - -iconv_t -iconv_open(const char *tocode, const char *fromcode) -{ - rec_iconv_t *cd; - - cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t)); - if (cd == NULL) - return (iconv_t)(-1); - -#if defined(USE_LIBICONV_DLL) - errno = 0; - if (libiconv_iconv_open(cd, tocode, fromcode)) - return (iconv_t)cd; -#endif - - /* reset the errno to prevent reporting wrong error code. - * 0 for unsorted error. */ - errno = 0; - if (win_iconv_open(cd, tocode, fromcode)) - return (iconv_t)cd; - - free(cd); - - return (iconv_t)(-1); -} - -int -iconv_close(iconv_t _cd) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - int r = cd->iconv_close(cd->cd); - int e = *(cd->_errno()); -#if defined(USE_LIBICONV_DLL) - if (cd->hlibiconv != NULL) - FreeLibrary(cd->hlibiconv); -#endif - free(cd); - errno = e; - return r; -} - -size_t -iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft); - errno = *(cd->_errno()); - return r; -} - -static int -win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) -{ - if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to)) - return FALSE; - cd->iconv_close = win_iconv_close; - cd->iconv = win_iconv; - cd->_errno = _errno; - cd->cd = (iconv_t)cd; - return TRUE; -} - -static int -win_iconv_close(iconv_t cd) -{ - return 0; -} - -static size_t -win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ - int insize; - int outsize; - int wsize; - DWORD frommode; - DWORD tomode; - uint wc; - compat_t *cp; - int i; - - if (inbuf == NULL || *inbuf == NULL) - { - if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) - { - tomode = cd->to.mode; - outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); - if (outsize == -1) - { - cd->to.mode = tomode; - return (size_t)(-1); - } - *outbuf += outsize; - *outbytesleft -= outsize; - } - cd->from.mode = 0; - cd->to.mode = 0; - return 0; - } - - while (*inbytesleft != 0) - { - frommode = cd->from.mode; - tomode = cd->to.mode; - wsize = MB_CHAR_MAX; - - insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); - if (insize == -1) - { - cd->from.mode = frommode; - return (size_t)(-1); - } - - if (wsize == 0) - { - *inbuf += insize; - *inbytesleft -= insize; - continue; - } - - if (cd->from.compat != NULL) - { - wc = utf16_to_ucs4(wbuf); - cp = cd->from.compat; - for (i = 0; cp[i].in != 0; ++i) - { - if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) - { - ucs4_to_utf16(cp[i].in, wbuf, &wsize); - break; - } - } - } - - if (cd->to.compat != NULL) - { - wc = utf16_to_ucs4(wbuf); - cp = cd->to.compat; - for (i = 0; cp[i].in != 0; ++i) - { - if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) - { - ucs4_to_utf16(cp[i].out, wbuf, &wsize); - break; - } - } - } - - outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); - if (outsize == -1) - { - cd->from.mode = frommode; - cd->to.mode = tomode; - return (size_t)(-1); - } - - *inbuf += insize; - *outbuf += outsize; - *inbytesleft -= insize; - *outbytesleft -= outsize; - } - - return 0; -} - -static int -make_csconv(const char *_name, csconv_t *cv) -{ - CPINFOEX cpinfoex; - int use_compat = TRUE; - int flag = 0; - char *name; - char *p; - - name = xstrndup(_name, strlen(_name)); - if (name == NULL) - return FALSE; - - /* check for option "enc_name//opt1//opt2" */ - while ((p = strrstr(name, "//")) != NULL) - { - if (_stricmp(p + 2, "nocompat") == 0) - use_compat = FALSE; - else if (_stricmp(p + 2, "translit") == 0) - flag |= FLAG_TRANSLIT; - else if (_stricmp(p + 2, "ignore") == 0) - flag |= FLAG_IGNORE; - *p = 0; - } - - cv->mode = 0; - cv->flags = flag; - cv->mblen = NULL; - cv->flush = NULL; - cv->compat = NULL; - cv->codepage = name_to_codepage(name); - if (cv->codepage == 1200 || cv->codepage == 1201) - { - cv->mbtowc = utf16_mbtowc; - cv->wctomb = utf16_wctomb; - if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0) - cv->flags |= FLAG_USE_BOM; - } - else if (cv->codepage == 12000 || cv->codepage == 12001) - { - cv->mbtowc = utf32_mbtowc; - cv->wctomb = utf32_wctomb; - if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0) - cv->flags |= FLAG_USE_BOM; - } - else if (cv->codepage == 65001) - { - cv->mbtowc = kernel_mbtowc; - cv->wctomb = kernel_wctomb; - cv->mblen = utf8_mblen; - } - else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang()) - { - cv->mbtowc = iso2022jp_mbtowc; - cv->wctomb = iso2022jp_wctomb; - cv->flush = iso2022jp_flush; - } - else if (cv->codepage == 51932 && load_mlang()) - { - cv->mbtowc = mlang_mbtowc; - cv->wctomb = mlang_wctomb; - cv->mblen = eucjp_mblen; - } - else if (IsValidCodePage(cv->codepage) - && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0) - { - cv->mbtowc = kernel_mbtowc; - cv->wctomb = kernel_wctomb; - if (cpinfoex.MaxCharSize == 1) - cv->mblen = sbcs_mblen; - else if (cpinfoex.MaxCharSize == 2) - cv->mblen = dbcs_mblen; - else - cv->mblen = mbcs_mblen; - } - else - { - /* not supported */ - free(name); - errno = EINVAL; - return FALSE; - } - - if (use_compat) - { - switch (cv->codepage) - { - case 932: cv->compat = cp932_compat; break; - case 20932: cv->compat = cp20932_compat; break; - case 51932: cv->compat = cp51932_compat; break; - case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break; - } - } - - free(name); - - return TRUE; -} - -static int -name_to_codepage(const char *name) -{ - int i; - - if (*name == '\0' || - strcmp(name, "char") == 0) - return GetACP(); - else if (strcmp(name, "wchar_t") == 0) - return 1200; - else if (_strnicmp(name, "cp", 2) == 0) - return atoi(name + 2); /* CP123 */ - else if ('0' <= name[0] && name[0] <= '9') - return atoi(name); /* 123 */ - else if (_strnicmp(name, "xx", 2) == 0) - return atoi(name + 2); /* XX123 for debug */ - - for (i = 0; codepage_alias[i].name != NULL; ++i) - if (_stricmp(name, codepage_alias[i].name) == 0) - return codepage_alias[i].codepage; - return -1; -} - -/* - * http://www.faqs.org/rfcs/rfc2781.html - */ -static uint -utf16_to_ucs4(const ushort *wbuf) -{ - uint wc = wbuf[0]; - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; - return wc; -} - -static void -ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) -{ - if (wc < 0x10000) - { - wbuf[0] = wc; - *wbufsize = 1; - } - else - { - wc -= 0x10000; - wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); - wbuf[1] = 0xDC00 | (wc & 0x3FF); - *wbufsize = 2; - } -} - -/* - * Check if codepage is one of those for which the dwFlags parameter - * to MultiByteToWideChar() must be zero. Return zero or - * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows - * Server 2003 R2 claims that also codepage 65001 is one of these, but - * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave - * out 65001 (UTF-8), and that indeed seems to be the case on XP, it - * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting - * from UTF-8. - */ -static int -mbtowc_flags(int codepage) -{ - return (codepage == 50220 || codepage == 50221 || - codepage == 50222 || codepage == 50225 || - codepage == 50227 || codepage == 50229 || - codepage == 52936 || codepage == 54936 || - (codepage >= 57002 && codepage <= 57011) || - codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; -} - -/* - * Check if codepage is one those for which the lpUsedDefaultChar - * parameter to WideCharToMultiByte() must be NULL. The docs in - * Platform SDK for for Windows Server 2003 R2 claims that this is the - * list below, while the MSDN docs for MSVS2008 claim that it is only - * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform - * SDK seems to be correct, at least for XP. - */ -static int -must_use_null_useddefaultchar(int codepage) -{ - return (codepage == 65000 || codepage == 65001 || - codepage == 50220 || codepage == 50221 || - codepage == 50222 || codepage == 50225 || - codepage == 50227 || codepage == 50229 || - codepage == 52936 || codepage == 54936 || - (codepage >= 57002 && codepage <= 57011) || - codepage == 42); -} - -static char * -strrstr(const char *str, const char *token) -{ - int len = strlen(token); - const char *p = str + strlen(str); - - while (str <= --p) - if (p[0] == token[0] && strncmp(p, token, len) == 0) - return (char *)p; - return NULL; -} - -static char * -xstrndup(const char *s, size_t n) -{ - char *p; - - p = (char *)malloc(n + 1); - if (p == NULL) - return NULL; - memcpy(p, s, n); - p[n] = '\0'; - return p; -} - -static int -seterror(int err) -{ - errno = err; - return -1; -} - -#if defined(USE_LIBICONV_DLL) -static int -libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) -{ - HMODULE hlibiconv = NULL; - HMODULE hmsvcrt = NULL; - char *dllname; - const char *p; - const char *e; - f_iconv_open _iconv_open; - - /* - * always try to load dll, so that we can switch dll in runtime. - */ - - /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */ - p = getenv("WINICONV_LIBICONV_DLL"); - if (p == NULL) - p = DEFAULT_LIBICONV_DLL; - /* parse comma separated value */ - for ( ; *p != 0; p = (*e == ',') ? e + 1 : e) - { - e = strchr(p, ','); - if (p == e) - continue; - else if (e == NULL) - e = p + strlen(p); - dllname = xstrndup(p, e - p); - if (dllname == NULL) - return FALSE; - hlibiconv = LoadLibrary(dllname); - free(dllname); - if (hlibiconv != NULL) - { - if (hlibiconv == hwiniconv) - { - FreeLibrary(hlibiconv); - hlibiconv = NULL; - continue; - } - break; - } - } - - if (hlibiconv == NULL) - goto failed; - - hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno"); - if (hmsvcrt == NULL) - goto failed; - - _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open"); - if (_iconv_open == NULL) - _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open"); - cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close"); - if (cd->iconv_close == NULL) - cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close"); - cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv"); - if (cd->iconv == NULL) - cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv"); - cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno"); - if (_iconv_open == NULL || cd->iconv_close == NULL - || cd->iconv == NULL || cd->_errno == NULL) - goto failed; - - cd->cd = _iconv_open(tocode, fromcode); - if (cd->cd == (iconv_t)(-1)) - goto failed; - - cd->hlibiconv = hlibiconv; - return TRUE; - -failed: - if (hlibiconv != NULL) - FreeLibrary(hlibiconv); - /* do not free hmsvcrt which is obtained by GetModuleHandle() */ - return FALSE; -} - -/* - * Reference: - * http://forums.belution.com/ja/vc/000/234/78s.shtml - * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html - * - * The formal way is - * imagehlp.h or dbghelp.h - * imagehlp.lib or dbghelp.lib - * ImageDirectoryEntryToData() - */ -#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base)) -#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew)) -static PVOID -MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size) -{ - /* TODO: MappedAsImage? */ - PIMAGE_DATA_DIRECTORY p; - p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry; - if (p->VirtualAddress == 0) { - *Size = 0; - return NULL; - } - *Size = p->Size; - return (PVOID)((LPBYTE)Base + p->VirtualAddress); -} - -static HMODULE -find_imported_module_by_funcname(HMODULE hModule, const char *funcname) -{ - DWORD Base; - ULONG Size; - PIMAGE_IMPORT_DESCRIPTOR Imp; - PIMAGE_THUNK_DATA Name; /* Import Name Table */ - PIMAGE_IMPORT_BY_NAME ImpName; - - Base = (DWORD)hModule; - Imp = MyImageDirectoryEntryToData( - (LPVOID)Base, - TRUE, - IMAGE_DIRECTORY_ENTRY_IMPORT, - &Size); - if (Imp == NULL) - return NULL; - for ( ; Imp->OriginalFirstThunk != 0; ++Imp) - { - Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk); - for ( ; Name->u1.Ordinal != 0; ++Name) - { - if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal)) - { - ImpName = (PIMAGE_IMPORT_BY_NAME) - (Base + (DWORD)Name->u1.AddressOfData); - if (strcmp((char *)ImpName->Name, funcname) == 0) - return GetModuleHandle((char *)(Base + Imp->Name)); - } - } - } - return NULL; -} -#endif - -static int -sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - return 1; -} - -static int -dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; - if (bufsize < len) - return seterror(EINVAL); - return len; -} - -static int -mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = 0; - - if (cv->codepage == 54936) { - if (buf[0] <= 0x7F) len = 1; - else if (buf[0] >= 0x81 && buf[0] <= 0xFE && - bufsize >= 2 && - ((buf[1] >= 0x40 && buf[1] <= 0x7E) || - (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2; - else if (buf[0] >= 0x81 && buf[0] <= 0xFE && - bufsize >= 4 && - buf[1] >= 0x30 && buf[1] <= 0x39) len = 4; - else - return seterror(EINVAL); - return len; - } - else - return seterror(EINVAL); -} - -static int -utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = 0; - - if (buf[0] < 0x80) len = 1; - else if ((buf[0] & 0xE0) == 0xC0) len = 2; - else if ((buf[0] & 0xF0) == 0xE0) len = 3; - else if ((buf[0] & 0xF8) == 0xF0) len = 4; - else if ((buf[0] & 0xFC) == 0xF8) len = 5; - else if ((buf[0] & 0xFE) == 0xFC) len = 6; - - if (len == 0) - return seterror(EILSEQ); - else if (bufsize < len) - return seterror(EINVAL); - return len; -} - -static int -eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - if (buf[0] < 0x80) /* ASCII */ - return 1; - else if (buf[0] == 0x8E) /* JIS X 0201 */ - { - if (bufsize < 2) - return seterror(EINVAL); - else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) - return seterror(EILSEQ); - return 2; - } - else if (buf[0] == 0x8F) /* JIS X 0212 */ - { - if (bufsize < 3) - return seterror(EINVAL); - else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) - || !(0xA1 <= buf[2] && buf[2] <= 0xFE)) - return seterror(EILSEQ); - return 3; - } - else /* JIS X 0208 */ - { - if (bufsize < 2) - return seterror(EINVAL); - else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) - || !(0xA1 <= buf[1] && buf[1] <= 0xFE)) - return seterror(EILSEQ); - return 2; - } -} - -static int -kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int len; - - len = cv->mblen(cv, buf, bufsize); - if (len == -1) - return -1; - *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), - (const char *)buf, len, (wchar_t *)wbuf, *wbufsize); - if (*wbufsize == 0) - return seterror(EILSEQ); - return len; -} - -static int -kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - BOOL usedDefaultChar = 0; - BOOL *p = NULL; - int flags = 0; - int len; - - if (bufsize == 0) - return seterror(E2BIG); - if (!must_use_null_useddefaultchar(cv->codepage)) - { - p = &usedDefaultChar; -#ifdef WC_NO_BEST_FIT_CHARS - if (!(cv->flags & FLAG_TRANSLIT)) - flags |= WC_NO_BEST_FIT_CHARS; -#endif - } - len = WideCharToMultiByte(cv->codepage, flags, - (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); - if (len == 0) - { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) - return seterror(E2BIG); - return seterror(EILSEQ); - } - else if (usedDefaultChar) - return seterror(EILSEQ); - else if (cv->mblen(cv, buf, len) != len) /* validate result */ - return seterror(EILSEQ); - return len; -} - -/* - * It seems that the mode (cv->mode) is fixnum. - * For example, when converting iso-2022-jp(cp50221) to unicode: - * in ascii sequence: mode=0xC42C0000 - * in jisx0208 sequence: mode=0xC42C0001 - * "C42C" is same for each convert session. - * It should be: ((codepage-1)<<16)|state - */ -static int -mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int len; - int insize; - HRESULT hr; - - len = cv->mblen(cv, buf, bufsize); - if (len == -1) - return -1; - insize = len; - hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, - (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); - if (hr != S_OK || insize != len) - return seterror(EILSEQ); - return len; -} - -static int -mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ - int tmpsize = MB_CHAR_MAX; - int insize = wbufsize; - HRESULT hr; - - hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, - (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); - if (hr != S_OK || insize != wbufsize) - return seterror(EILSEQ); - else if (bufsize < tmpsize) - return seterror(E2BIG); - else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) - return seterror(EILSEQ); - memcpy(buf, tmpbuf, tmpsize); - return tmpsize; -} - -static int -utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int codepage = cv->codepage; - - /* swap endian: 1200 <-> 1201 */ - if (cv->mode & UNICODE_MODE_SWAPPED) - codepage ^= 1; - - if (bufsize < 2) - return seterror(EINVAL); - if (codepage == 1200) /* little endian */ - wbuf[0] = (buf[1] << 8) | buf[0]; - else if (codepage == 1201) /* big endian */ - wbuf[0] = (buf[0] << 8) | buf[1]; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - cv->mode |= UNICODE_MODE_BOM_DONE; - if (wbuf[0] == 0xFFFE) - { - cv->mode |= UNICODE_MODE_SWAPPED; - *wbufsize = 0; - return 2; - } - else if (wbuf[0] == 0xFEFF) - { - *wbufsize = 0; - return 2; - } - } - - if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) - return seterror(EILSEQ); - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - { - if (bufsize < 4) - return seterror(EINVAL); - if (codepage == 1200) /* little endian */ - wbuf[1] = (buf[3] << 8) | buf[2]; - else if (codepage == 1201) /* big endian */ - wbuf[1] = (buf[2] << 8) | buf[3]; - if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) - return seterror(EILSEQ); - *wbufsize = 2; - return 4; - } - *wbufsize = 1; - return 2; -} - -static int -utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - int r; - - cv->mode |= UNICODE_MODE_BOM_DONE; - if (bufsize < 2) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - memcpy(buf, "\xFF\xFE", 2); - else if (cv->codepage == 1201) /* big endian */ - memcpy(buf, "\xFE\xFF", 2); - - r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); - if (r == -1) - return -1; - return r + 2; - } - - if (bufsize < 2) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - { - buf[0] = (wbuf[0] & 0x00FF); - buf[1] = (wbuf[0] & 0xFF00) >> 8; - } - else if (cv->codepage == 1201) /* big endian */ - { - buf[0] = (wbuf[0] & 0xFF00) >> 8; - buf[1] = (wbuf[0] & 0x00FF); - } - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - { - if (bufsize < 4) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - { - buf[2] = (wbuf[1] & 0x00FF); - buf[3] = (wbuf[1] & 0xFF00) >> 8; - } - else if (cv->codepage == 1201) /* big endian */ - { - buf[2] = (wbuf[1] & 0xFF00) >> 8; - buf[3] = (wbuf[1] & 0x00FF); - } - return 4; - } - return 2; -} - -static int -utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int codepage = cv->codepage; - uint wc = 0; - - /* swap endian: 12000 <-> 12001 */ - if (cv->mode & UNICODE_MODE_SWAPPED) - codepage ^= 1; - - if (bufsize < 4) - return seterror(EINVAL); - if (codepage == 12000) /* little endian */ - wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; - else if (codepage == 12001) /* big endian */ - wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - cv->mode |= UNICODE_MODE_BOM_DONE; - if (wc == 0xFFFE0000) - { - cv->mode |= UNICODE_MODE_SWAPPED; - *wbufsize = 0; - return 4; - } - else if (wc == 0x0000FEFF) - { - *wbufsize = 0; - return 4; - } - } - - if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) - return seterror(EILSEQ); - ucs4_to_utf16(wc, wbuf, wbufsize); - return 4; -} - -static int -utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - uint wc; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - int r; - - cv->mode |= UNICODE_MODE_BOM_DONE; - if (bufsize < 4) - return seterror(E2BIG); - if (cv->codepage == 12000) /* little endian */ - memcpy(buf, "\xFF\xFE\x00\x00", 4); - else if (cv->codepage == 12001) /* big endian */ - memcpy(buf, "\x00\x00\xFE\xFF", 4); - - r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); - if (r == -1) - return -1; - return r + 4; - } - - if (bufsize < 4) - return seterror(E2BIG); - wc = utf16_to_ucs4(wbuf); - if (cv->codepage == 12000) /* little endian */ - { - buf[0] = wc & 0x000000FF; - buf[1] = (wc & 0x0000FF00) >> 8; - buf[2] = (wc & 0x00FF0000) >> 16; - buf[3] = (wc & 0xFF000000) >> 24; - } - else if (cv->codepage == 12001) /* big endian */ - { - buf[0] = (wc & 0xFF000000) >> 24; - buf[1] = (wc & 0x00FF0000) >> 16; - buf[2] = (wc & 0x0000FF00) >> 8; - buf[3] = wc & 0x000000FF; - } - return 4; -} - -/* - * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) - * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow - * 1 byte Kana) - * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte - * Kana - SO/SI) - * - * MultiByteToWideChar() and WideCharToMultiByte() behave differently - * depending on Windows version. On XP, WideCharToMultiByte() doesn't - * terminate result sequence with ascii escape. But Vista does. - * Use MLang instead. - */ - -#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) -#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) -#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) - -#define ISO2022_SI 0 -#define ISO2022_SO 1 - -/* shift in */ -static const char iso2022_SI_seq[] = "\x0F"; -/* shift out */ -static const char iso2022_SO_seq[] = "\x0E"; - -typedef struct iso2022_esc_t iso2022_esc_t; -struct iso2022_esc_t { - const char *esc; - int esc_len; - int len; - int cs; -}; - -#define ISO2022JP_CS_ASCII 0 -#define ISO2022JP_CS_JISX0201_ROMAN 1 -#define ISO2022JP_CS_JISX0201_KANA 2 -#define ISO2022JP_CS_JISX0208_1978 3 -#define ISO2022JP_CS_JISX0208_1983 4 -#define ISO2022JP_CS_JISX0212 5 - -static iso2022_esc_t iso2022jp_esc[] = { - {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, - {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, - {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, - {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ - {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, - {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, - {NULL, 0, 0, 0} -}; - -static int -iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - char tmp[MB_CHAR_MAX]; - int insize; - HRESULT hr; - DWORD dummy = 0; - int len; - int esc_len; - int cs; - int shift; - int i; - - if (buf[0] == 0x1B) - { - for (i = 0; iesc[i].esc != NULL; ++i) - { - esc_len = iesc[i].esc_len; - if (bufsize < esc_len) - { - if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) - return seterror(EINVAL); - } - else - { - if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) - { - cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); - *wbufsize = 0; - return esc_len; - } - } - } - /* not supported escape sequence */ - return seterror(EILSEQ); - } - else if (buf[0] == iso2022_SO_seq[0]) - { - cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); - *wbufsize = 0; - return 1; - } - else if (buf[0] == iso2022_SI_seq[0]) - { - cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); - *wbufsize = 0; - return 1; - } - - cs = ISO2022_MODE_CS(cv->mode); - shift = ISO2022_MODE_SHIFT(cv->mode); - - /* reset the mode for informal sequence */ - if (buf[0] < 0x20) - { - cs = ISO2022JP_CS_ASCII; - shift = ISO2022_SI; - } - - len = iesc[cs].len; - if (bufsize < len) - return seterror(EINVAL); - for (i = 0; i < len; ++i) - if (!(buf[i] < 0x80)) - return seterror(EILSEQ); - esc_len = iesc[cs].esc_len; - memcpy(tmp, iesc[cs].esc, esc_len); - if (shift == ISO2022_SO) - { - memcpy(tmp + esc_len, iso2022_SO_seq, 1); - esc_len += 1; - } - memcpy(tmp + esc_len, buf, len); - - if ((cv->codepage == 50220 || cv->codepage == 50221 - || cv->codepage == 50222) && shift == ISO2022_SO) - { - /* XXX: shift-out cannot be used for mbtowc (both kernel and - * mlang) */ - esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; - memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); - memcpy(tmp + esc_len, buf, len); - } - - insize = len + esc_len; - hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, - (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); - if (hr != S_OK || insize != len + esc_len) - return seterror(EILSEQ); - - /* Check for conversion error. Assuming defaultChar is 0x3F. */ - /* ascii should be converted from ascii */ - if (wbuf[0] == buf[0] - && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) - return seterror(EILSEQ); - - /* reset the mode for informal sequence */ - if (cv->mode != ISO2022_MODE(cs, shift)) - cv->mode = ISO2022_MODE(cs, shift); - - return len; -} - -static int -iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - char tmp[MB_CHAR_MAX]; - int tmpsize = MB_CHAR_MAX; - int insize = wbufsize; - HRESULT hr; - DWORD dummy = 0; - int len; - int esc_len = 0; - int cs = 0; - int shift; - int i; - - /* - * MultiByte = [escape sequence] + character + [escape sequence] - * - * Whether trailing escape sequence is added depends on which API is - * used (kernel or MLang, and its version). - */ - hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, - (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); - if (hr != S_OK || insize != wbufsize) - return seterror(EILSEQ); - else if (bufsize < tmpsize) - return seterror(E2BIG); - - if (tmpsize == 1) - { - cs = ISO2022JP_CS_ASCII; - esc_len = 0; - } - else - { - for (i = 1; iesc[i].esc != NULL; ++i) - { - esc_len = iesc[i].esc_len; - if (strncmp(tmp, iesc[i].esc, esc_len) == 0) - { - cs = iesc[i].cs; - break; - } - } - if (iesc[i].esc == NULL) - /* not supported escape sequence */ - return seterror(EILSEQ); - } - - shift = ISO2022_SI; - if (tmp[esc_len] == iso2022_SO_seq[0]) - { - shift = ISO2022_SO; - esc_len += 1; - } - - len = iesc[cs].len; - - /* Check for converting error. Assuming defaultChar is 0x3F. */ - /* ascii should be converted from ascii */ - if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) - return seterror(EILSEQ); - else if (tmpsize < esc_len + len) - return seterror(EILSEQ); - - if (cv->mode == ISO2022_MODE(cs, shift)) - { - /* remove escape sequence */ - if (esc_len != 0) - memmove(tmp, tmp + esc_len, len); - esc_len = 0; - } - else - { - if (cs == ISO2022JP_CS_ASCII) - { - esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; - memmove(tmp + esc_len, tmp, len); - memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); - } - if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) - { - /* shift-in before changing to other mode */ - memmove(tmp + 1, tmp, len + esc_len); - memcpy(tmp, iso2022_SI_seq, 1); - esc_len += 1; - } - } - - if (bufsize < len + esc_len) - return seterror(E2BIG); - memcpy(buf, tmp, len + esc_len); - cv->mode = ISO2022_MODE(cs, shift); - return len + esc_len; -} - -static int -iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - int esc_len; - - if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) - { - esc_len = 0; - if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) - esc_len += 1; - if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) - esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; - if (bufsize < esc_len) - return seterror(E2BIG); - - esc_len = 0; - if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) - { - memcpy(buf, iso2022_SI_seq, 1); - esc_len += 1; - } - if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) - { - memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, - iesc[ISO2022JP_CS_ASCII].esc_len); - esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; - } - return esc_len; - } - return 0; -} - -#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) -BOOL WINAPI -DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) -{ - switch( fdwReason ) - { - case DLL_PROCESS_ATTACH: - hwiniconv = (HMODULE)hinstDLL; - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} -#endif - -#if defined(MAKE_EXE) -#include -#include -#include -int -main(int argc, char **argv) -{ - char *fromcode = NULL; - char *tocode = NULL; - int i; - char inbuf[BUFSIZ]; - char outbuf[BUFSIZ]; - const char *pin; - char *pout; - size_t inbytesleft; - size_t outbytesleft; - size_t rest = 0; - iconv_t cd; - size_t r; - FILE *in = stdin; - - _setmode(_fileno(stdin), _O_BINARY); - _setmode(_fileno(stdout), _O_BINARY); - - for (i = 1; i < argc; ++i) - { - if (strcmp(argv[i], "-l") == 0) - { - for (i = 0; codepage_alias[i].name != NULL; ++i) - printf("%s\n", codepage_alias[i].name); - return 0; - } - - if (strcmp(argv[i], "-f") == 0) - fromcode = argv[++i]; - else if (strcmp(argv[i], "-t") == 0) - tocode = argv[++i]; - else - { - in = fopen(argv[i], "rb"); - if (in == NULL) - { - fprintf(stderr, "cannot open %s\n", argv[i]); - return 1; - } - break; - } - } - - if (fromcode == NULL || tocode == NULL) - { - printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]); - return 0; - } - - cd = iconv_open(tocode, fromcode); - if (cd == (iconv_t)(-1)) - { - perror("iconv_open error"); - return 1; - } - - while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0 - || rest != 0) - { - inbytesleft += rest; - pin = inbuf; - pout = outbuf; - outbytesleft = sizeof(outbuf); - r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); - fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); - if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in))) - { - perror("conversion error"); - return 1; - } - memmove(inbuf, pin, inbytesleft); - rest = inbytesleft; - } - pout = outbuf; - outbytesleft = sizeof(outbuf); - r = iconv(cd, NULL, NULL, &pout, &outbytesleft); - fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); - if (r == (size_t)(-1)) - { - perror("conversion error"); - return 1; - } - - iconv_close(cd); - - return 0; -} -#endif - +/* + * iconv library using Win32 API to conversion. + * + * This file is placed in the public domain. + * + * Last Change: 2009-07-06 + * + * ENVIRONMENT VARIABLE: + * WINICONV_LIBICONV_DLL + * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If + * loading the DLL or iconv_open() failed, falls back to internal + * conversion. If a few DLL are specified as comma separated list, + * the first loadable DLL is used. The DLL should have iconv_open(), + * iconv_close() and iconv(). Or libiconv_open(), libiconv_close() + * and libiconv(). + * (only available when USE_LIBICONV_DLL is defined at compile time) + * + * Win32 API does not support strict encoding conversion for some + * codepage. And MLang function drop or replace invalid bytes and does + * not return useful error status as iconv. This implementation cannot + * be used for encoding validation purpose. + */ + +/* for WC_NO_BEST_FIT_CHARS */ +#ifndef WINVER +# define WINVER 0x0500 +#endif + +#include +#include +#include +#include + +#if 0 +# define MAKE_EXE +# define MAKE_DLL +# define USE_LIBICONV_DLL +#endif + +#if !defined(DEFAULT_LIBICONV_DLL) +# define DEFAULT_LIBICONV_DLL "" +#endif + +#define MB_CHAR_MAX 16 + +#define UNICODE_MODE_BOM_DONE 1 +#define UNICODE_MODE_SWAPPED 2 + +#define FLAG_USE_BOM 1 +#define FLAG_TRANSLIT 2 /* //TRANSLIT */ +#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */ + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; + +typedef void* iconv_t; + +iconv_t iconv_open(const char *tocode, const char *fromcode); +int iconv_close(iconv_t cd); +size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); + +/* libiconv interface for vim */ +#if defined(MAKE_DLL) +int +iconvctl (iconv_t cd, int request, void* argument) +{ + /* not supported */ + return 0; +} +#endif + +typedef struct compat_t compat_t; +typedef struct csconv_t csconv_t; +typedef struct rec_iconv_t rec_iconv_t; + +typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode); +typedef int (*f_iconv_close)(iconv_t cd); +typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +typedef int* (*f_errno)(void); +typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); +typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); + +#define COMPAT_IN 1 +#define COMPAT_OUT 2 + +/* unicode mapping for compatibility with other conversion table. */ +struct compat_t { + uint in; + uint out; + uint flag; +}; + +struct csconv_t { + int codepage; + int flags; + f_mbtowc mbtowc; + f_wctomb wctomb; + f_mblen mblen; + f_flush flush; + DWORD mode; + compat_t *compat; +}; + +struct rec_iconv_t { + iconv_t cd; + f_iconv_close iconv_close; + f_iconv iconv; + f_errno _errno; + csconv_t from; + csconv_t to; +#if defined(USE_LIBICONV_DLL) + HMODULE hlibiconv; +#endif +}; + +static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); +static int win_iconv_close(iconv_t cd); +static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); + +static int load_mlang(); +static int make_csconv(const char *name, csconv_t *cv); +static int name_to_codepage(const char *name); +static uint utf16_to_ucs4(const ushort *wbuf); +static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); +static int mbtowc_flags(int codepage); +static int must_use_null_useddefaultchar(int codepage); +static char *strrstr(const char *str, const char *token); +static char *xstrndup(const char *s, size_t n); +static int seterror(int err); + +#if defined(USE_LIBICONV_DLL) +static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); +static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size); +static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname); + +static HMODULE hwiniconv; +#endif + +static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); + +static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); + +static struct { + int codepage; + const char *name; +} codepage_alias[] = { + {65001, "CP65001"}, + {65001, "UTF8"}, + {65001, "UTF-8"}, + + {1200, "CP1200"}, + {1200, "UTF16LE"}, + {1200, "UTF-16LE"}, + {1200, "UCS-2LE"}, + + {1201, "CP1201"}, + {1201, "UTF16BE"}, + {1201, "UTF-16BE"}, + {1201, "UCS-2BE"}, + {1201, "unicodeFFFE"}, + + {12000, "CP12000"}, + {12000, "UTF32LE"}, + {12000, "UTF-32LE"}, + + {12001, "CP12001"}, + {12001, "UTF32BE"}, + {12001, "UTF-32BE"}, + +#ifndef GLIB_COMPILATION + /* + * Default is big endian. + * See rfc2781 4.3 Interpreting text labelled as UTF-16. + */ + {1201, "UTF16"}, + {1201, "UTF-16"}, + {12001, "UTF32"}, + {12001, "UTF-32"}, +#else + /* Default is little endian, because the platform is */ + {1200, "UTF16"}, + {1200, "UTF-16"}, + {1200, "UCS-2"}, + {12000, "UTF32"}, + {12000, "UTF-32"}, +#endif + + /* copy from libiconv `iconv -l` */ + /* !IsValidCodePage(367) */ + {20127, "ANSI_X3.4-1968"}, + {20127, "ANSI_X3.4-1986"}, + {20127, "ASCII"}, + {20127, "CP367"}, + {20127, "IBM367"}, + {20127, "ISO-IR-6"}, + {20127, "ISO646-US"}, + {20127, "ISO_646.IRV:1991"}, + {20127, "US"}, + {20127, "US-ASCII"}, + {20127, "CSASCII"}, + + /* !IsValidCodePage(819) */ + {1252, "CP819"}, + {1252, "IBM819"}, + {28591, "ISO-8859-1"}, + {28591, "ISO-IR-100"}, + {28591, "ISO8859-1"}, + {28591, "ISO_8859-1"}, + {28591, "ISO_8859-1:1987"}, + {28591, "L1"}, + {28591, "LATIN1"}, + {28591, "CSISOLATIN1"}, + + {1250, "CP1250"}, + {1250, "MS-EE"}, + {1250, "WINDOWS-1250"}, + + {1251, "CP1251"}, + {1251, "MS-CYRL"}, + {1251, "WINDOWS-1251"}, + + {1252, "CP1252"}, + {1252, "MS-ANSI"}, + {1252, "WINDOWS-1252"}, + + {1253, "CP1253"}, + {1253, "MS-GREEK"}, + {1253, "WINDOWS-1253"}, + + {1254, "CP1254"}, + {1254, "MS-TURK"}, + {1254, "WINDOWS-1254"}, + + {1255, "CP1255"}, + {1255, "MS-HEBR"}, + {1255, "WINDOWS-1255"}, + + {1256, "CP1256"}, + {1256, "MS-ARAB"}, + {1256, "WINDOWS-1256"}, + + {1257, "CP1257"}, + {1257, "WINBALTRIM"}, + {1257, "WINDOWS-1257"}, + + {1258, "CP1258"}, + {1258, "WINDOWS-1258"}, + + {850, "850"}, + {850, "CP850"}, + {850, "IBM850"}, + {850, "CSPC850MULTILINGUAL"}, + + /* !IsValidCodePage(862) */ + {862, "862"}, + {862, "CP862"}, + {862, "IBM862"}, + {862, "CSPC862LATINHEBREW"}, + + {866, "866"}, + {866, "CP866"}, + {866, "IBM866"}, + {866, "CSIBM866"}, + + /* !IsValidCodePage(154) */ + {154, "CP154"}, + {154, "CYRILLIC-ASIAN"}, + {154, "PT154"}, + {154, "PTCP154"}, + {154, "CSPTCP154"}, + + /* !IsValidCodePage(1133) */ + {1133, "CP1133"}, + {1133, "IBM-CP1133"}, + + {874, "CP874"}, + {874, "WINDOWS-874"}, + + /* !IsValidCodePage(51932) */ + {51932, "CP51932"}, + {51932, "MS51932"}, + {51932, "WINDOWS-51932"}, + {51932, "EUC-JP"}, + + {932, "CP932"}, + {932, "MS932"}, + {932, "SHIFFT_JIS"}, + {932, "SHIFFT_JIS-MS"}, + {932, "SJIS"}, + {932, "SJIS-MS"}, + {932, "SJIS-OPEN"}, + {932, "SJIS-WIN"}, + {932, "WINDOWS-31J"}, + {932, "WINDOWS-932"}, + {932, "CSWINDOWS31J"}, + + {50221, "CP50221"}, + {50221, "ISO-2022-JP"}, + {50221, "ISO-2022-JP-MS"}, + {50221, "ISO2022-JP"}, + {50221, "ISO2022-JP-MS"}, + {50221, "MS50221"}, + {50221, "WINDOWS-50221"}, + + {936, "CP936"}, + {936, "GBK"}, + {936, "MS936"}, + {936, "WINDOWS-936"}, + + {950, "CP950"}, + {950, "BIG5"}, + + {949, "CP949"}, + {949, "UHC"}, + {949, "EUC-KR"}, + + {1361, "CP1361"}, + {1361, "JOHAB"}, + + {437, "437"}, + {437, "CP437"}, + {437, "IBM437"}, + {437, "CSPC8CODEPAGE437"}, + + {737, "CP737"}, + + {775, "CP775"}, + {775, "IBM775"}, + {775, "CSPC775BALTIC"}, + + {852, "852"}, + {852, "CP852"}, + {852, "IBM852"}, + {852, "CSPCP852"}, + + /* !IsValidCodePage(853) */ + {853, "CP853"}, + + {855, "855"}, + {855, "CP855"}, + {855, "IBM855"}, + {855, "CSIBM855"}, + + {857, "857"}, + {857, "CP857"}, + {857, "IBM857"}, + {857, "CSIBM857"}, + + /* !IsValidCodePage(858) */ + {858, "CP858"}, + + {860, "860"}, + {860, "CP860"}, + {860, "IBM860"}, + {860, "CSIBM860"}, + + {861, "861"}, + {861, "CP-IS"}, + {861, "CP861"}, + {861, "IBM861"}, + {861, "CSIBM861"}, + + {863, "863"}, + {863, "CP863"}, + {863, "IBM863"}, + {863, "CSIBM863"}, + + {864, "CP864"}, + {864, "IBM864"}, + {864, "CSIBM864"}, + + {865, "865"}, + {865, "CP865"}, + {865, "IBM865"}, + {865, "CSIBM865"}, + + {869, "869"}, + {869, "CP-GR"}, + {869, "CP869"}, + {869, "IBM869"}, + {869, "CSIBM869"}, + + /* !IsValidCodePage(1152) */ + {1125, "CP1125"}, + + /* + * Code Page Identifiers + * http://msdn2.microsoft.com/en-us/library/ms776446.aspx + */ + {37, "IBM037"}, /* IBM EBCDIC US-Canada */ + {437, "IBM437"}, /* OEM United States */ + {500, "IBM500"}, /* IBM EBCDIC International */ + {708, "ASMO-708"}, /* Arabic (ASMO 708) */ + /* 709 Arabic (ASMO-449+, BCON V4) */ + /* 710 Arabic - Transparent Arabic */ + {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */ + {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */ + {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */ + {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */ + {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */ + {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */ + {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */ + {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */ + {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */ + {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */ + {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */ + {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */ + {864, "IBM864"}, /* OEM Arabic; Arabic (864) */ + {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */ + {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */ + {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */ + {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ + {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ + {875, "cp875"}, /* IBM EBCDIC Greek Modern */ + {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ + {932, "shift-jis"}, /* alternative name for it */ + {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ + {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */ + {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ + {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */ + {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */ + {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ + {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ + {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ + {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ + {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ + {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ + {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ + {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ + {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ + {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ + {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */ + {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */ + {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */ + {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */ + {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */ + {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */ + {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */ + {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */ + {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ + {1361, "Johab"}, /* Korean (Johab) */ + {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */ + {10001, "x-mac-japanese"}, /* Japanese (Mac) */ + {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ + {10003, "x-mac-korean"}, /* Korean (Mac) */ + {10004, "x-mac-arabic"}, /* Arabic (Mac) */ + {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */ + {10006, "x-mac-greek"}, /* Greek (Mac) */ + {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */ + {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ + {10010, "x-mac-romanian"}, /* Romanian (Mac) */ + {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */ + {10021, "x-mac-thai"}, /* Thai (Mac) */ + {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */ + {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */ + {10081, "x-mac-turkish"}, /* Turkish (Mac) */ + {10082, "x-mac-croatian"}, /* Croatian (Mac) */ + {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */ + {20001, "x-cp20001"}, /* TCA Taiwan */ + {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */ + {20003, "x-cp20003"}, /* IBM5550 Taiwan */ + {20004, "x-cp20004"}, /* TeleText Taiwan */ + {20005, "x-cp20005"}, /* Wang Taiwan */ + {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ + {20106, "x-IA5-German"}, /* IA5 German (7-bit) */ + {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */ + {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */ + {20127, "us-ascii"}, /* US-ASCII (7-bit) */ + {20261, "x-cp20261"}, /* T.61 */ + {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */ + {20273, "IBM273"}, /* IBM EBCDIC Germany */ + {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */ + {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */ + {20280, "IBM280"}, /* IBM EBCDIC Italy */ + {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */ + {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */ + {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */ + {20297, "IBM297"}, /* IBM EBCDIC France */ + {20420, "IBM420"}, /* IBM EBCDIC Arabic */ + {20423, "IBM423"}, /* IBM EBCDIC Greek */ + {20424, "IBM424"}, /* IBM EBCDIC Hebrew */ + {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */ + {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */ + {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */ + {20871, "IBM871"}, /* IBM EBCDIC Icelandic */ + {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */ + {20905, "IBM905"}, /* IBM EBCDIC Turkish */ + {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ + {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */ + {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ + {20949, "x-cp20949"}, /* Korean Wansung */ + {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ + /* 21027 (deprecated) */ + {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ + {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ + {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ + {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ + {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ + {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */ + {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */ + {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */ + {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */ + {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */ + {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */ + {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */ + {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */ + {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */ + {28597, "iso8859-7"}, /* ISO 8859-7 Greek */ + {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ + {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ + {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */ + {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */ + {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */ + {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */ + {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */ + {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */ + {29001, "x-Europa"}, /* Europa 3 */ + {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ + {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ + {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ + {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ + {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ + {50225, "iso-2022-kr"}, /* ISO 2022 Korean */ + {50225, "iso2022-kr"}, /* ISO 2022 Korean */ + {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ + /* 50229 ISO 2022 Traditional Chinese */ + /* 50930 EBCDIC Japanese (Katakana) Extended */ + /* 50931 EBCDIC US-Canada and Japanese */ + /* 50933 EBCDIC Korean Extended and Korean */ + /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ + /* 50936 EBCDIC Simplified Chinese */ + /* 50937 EBCDIC US-Canada and Traditional Chinese */ + /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ + {51932, "euc-jp"}, /* EUC Japanese */ + {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */ + {51949, "euc-kr"}, /* EUC Korean */ + /* 51950 EUC Traditional Chinese */ + {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ + {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ + {57002, "x-iscii-de"}, /* ISCII Devanagari */ + {57003, "x-iscii-be"}, /* ISCII Bengali */ + {57004, "x-iscii-ta"}, /* ISCII Tamil */ + {57005, "x-iscii-te"}, /* ISCII Telugu */ + {57006, "x-iscii-as"}, /* ISCII Assamese */ + {57007, "x-iscii-or"}, /* ISCII Oriya */ + {57008, "x-iscii-ka"}, /* ISCII Kannada */ + {57009, "x-iscii-ma"}, /* ISCII Malayalam */ + {57010, "x-iscii-gu"}, /* ISCII Gujarati */ + {57011, "x-iscii-pa"}, /* ISCII Punjabi */ + + {0, NULL} +}; + +/* + * SJIS SHIFTJIS table CP932 table + * ---- --------------------------- -------------------------------- + * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS + * 7E U+203E OVERLINE U+007E TILDE + * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR + * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS + * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE + * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO + * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS + * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN + * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN + * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN + * + * EUC-JP and ISO-2022-JP should be compatible with CP932. + * + * Kernel and MLang have different Unicode mapping table. Make sure + * which API is used. + */ +static compat_t cp932_compat[] = { + {0x00A5, 0x005C, COMPAT_OUT}, + {0x203E, 0x007E, COMPAT_OUT}, + {0x2014, 0x2015, COMPAT_OUT}, + {0x301C, 0xFF5E, COMPAT_OUT}, + {0x2016, 0x2225, COMPAT_OUT}, + {0x2212, 0xFF0D, COMPAT_OUT}, + {0x00A2, 0xFFE0, COMPAT_OUT}, + {0x00A3, 0xFFE1, COMPAT_OUT}, + {0x00AC, 0xFFE2, COMPAT_OUT}, + {0, 0, 0} +}; + +static compat_t cp20932_compat[] = { + {0x00A5, 0x005C, COMPAT_OUT}, + {0x203E, 0x007E, COMPAT_OUT}, + {0x2014, 0x2015, COMPAT_OUT}, + {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, + {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, + {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, + {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, + {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, + {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, + {0, 0, 0} +}; + +static compat_t *cp51932_compat = cp932_compat; + +/* cp20932_compat for kernel. cp932_compat for mlang. */ +static compat_t *cp5022x_compat = cp932_compat; + +typedef HRESULT (WINAPI *CONVERTINETSTRING)( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + DWORD dwDstEncoding, + LPCSTR lpSrcStr, + LPINT lpnSrcSize, + LPBYTE lpDstStr, + LPINT lpnDstSize +); +typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + LPCSTR lpSrcStr, + LPINT lpnMultiCharCount, + LPWSTR lpDstStr, + LPINT lpnWideCharCount +); +typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( + LPDWORD lpdwMode, + DWORD dwEncoding, + LPCWSTR lpSrcStr, + LPINT lpnWideCharCount, + LPSTR lpDstStr, + LPINT lpnMultiCharCount +); +typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)( + DWORD dwSrcEncoding, + DWORD dwDstEncoding +); +typedef HRESULT (WINAPI *LCIDTORFC1766A)( + LCID Locale, + LPSTR pszRfc1766, + int nChar +); +typedef HRESULT (WINAPI *LCIDTORFC1766W)( + LCID Locale, + LPWSTR pszRfc1766, + int nChar +); +typedef HRESULT (WINAPI *RFC1766TOLCIDA)( + LCID *pLocale, + LPSTR pszRfc1766 +); +typedef HRESULT (WINAPI *RFC1766TOLCIDW)( + LCID *pLocale, + LPWSTR pszRfc1766 +); +static CONVERTINETSTRING ConvertINetString; +static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; +static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; +static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable; +static LCIDTORFC1766A LcidToRfc1766A; +static RFC1766TOLCIDA Rfc1766ToLcidA; + +static int +load_mlang() +{ + HMODULE h; + if (ConvertINetString != NULL) + return TRUE; + h = LoadLibrary("mlang.dll"); + if (!h) + return FALSE; + ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString"); + ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode"); + ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte"); + IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable"); + LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A"); + Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA"); + return TRUE; +} + +iconv_t +iconv_open(const char *tocode, const char *fromcode) +{ + rec_iconv_t *cd; + + cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t)); + if (cd == NULL) + return (iconv_t)(-1); + +#if defined(USE_LIBICONV_DLL) + errno = 0; + if (libiconv_iconv_open(cd, tocode, fromcode)) + return (iconv_t)cd; +#endif + + /* reset the errno to prevent reporting wrong error code. + * 0 for unsorted error. */ + errno = 0; + if (win_iconv_open(cd, tocode, fromcode)) + return (iconv_t)cd; + + free(cd); + + return (iconv_t)(-1); +} + +int +iconv_close(iconv_t _cd) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + int r = cd->iconv_close(cd->cd); + int e = *(cd->_errno()); +#if defined(USE_LIBICONV_DLL) + if (cd->hlibiconv != NULL) + FreeLibrary(cd->hlibiconv); +#endif + free(cd); + errno = e; + return r; +} + +size_t +iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft); + errno = *(cd->_errno()); + return r; +} + +static int +win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) +{ + if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to)) + return FALSE; + cd->iconv_close = win_iconv_close; + cd->iconv = win_iconv; + cd->_errno = _errno; + cd->cd = (iconv_t)cd; + return TRUE; +} + +static int +win_iconv_close(iconv_t cd) +{ + return 0; +} + +static size_t +win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ + int insize; + int outsize; + int wsize; + DWORD frommode; + DWORD tomode; + uint wc; + compat_t *cp; + int i; + + if (inbuf == NULL || *inbuf == NULL) + { + if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) + { + tomode = cd->to.mode; + outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); + if (outsize == -1) + { + cd->to.mode = tomode; + return (size_t)(-1); + } + *outbuf += outsize; + *outbytesleft -= outsize; + } + cd->from.mode = 0; + cd->to.mode = 0; + return 0; + } + + while (*inbytesleft != 0) + { + frommode = cd->from.mode; + tomode = cd->to.mode; + wsize = MB_CHAR_MAX; + + insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); + if (insize == -1) + { + cd->from.mode = frommode; + return (size_t)(-1); + } + + if (wsize == 0) + { + *inbuf += insize; + *inbytesleft -= insize; + continue; + } + + if (cd->from.compat != NULL) + { + wc = utf16_to_ucs4(wbuf); + cp = cd->from.compat; + for (i = 0; cp[i].in != 0; ++i) + { + if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) + { + ucs4_to_utf16(cp[i].in, wbuf, &wsize); + break; + } + } + } + + if (cd->to.compat != NULL) + { + wc = utf16_to_ucs4(wbuf); + cp = cd->to.compat; + for (i = 0; cp[i].in != 0; ++i) + { + if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) + { + ucs4_to_utf16(cp[i].out, wbuf, &wsize); + break; + } + } + } + + outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); + if (outsize == -1) + { + cd->from.mode = frommode; + cd->to.mode = tomode; + return (size_t)(-1); + } + + *inbuf += insize; + *outbuf += outsize; + *inbytesleft -= insize; + *outbytesleft -= outsize; + } + + return 0; +} + +static int +make_csconv(const char *_name, csconv_t *cv) +{ + CPINFOEX cpinfoex; + int use_compat = TRUE; + int flag = 0; + char *name; + char *p; + + name = xstrndup(_name, strlen(_name)); + if (name == NULL) + return FALSE; + + /* check for option "enc_name//opt1//opt2" */ + while ((p = strrstr(name, "//")) != NULL) + { + if (_stricmp(p + 2, "nocompat") == 0) + use_compat = FALSE; + else if (_stricmp(p + 2, "translit") == 0) + flag |= FLAG_TRANSLIT; + else if (_stricmp(p + 2, "ignore") == 0) + flag |= FLAG_IGNORE; + *p = 0; + } + + cv->mode = 0; + cv->flags = flag; + cv->mblen = NULL; + cv->flush = NULL; + cv->compat = NULL; + cv->codepage = name_to_codepage(name); + if (cv->codepage == 1200 || cv->codepage == 1201) + { + cv->mbtowc = utf16_mbtowc; + cv->wctomb = utf16_wctomb; + if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0) + cv->flags |= FLAG_USE_BOM; + } + else if (cv->codepage == 12000 || cv->codepage == 12001) + { + cv->mbtowc = utf32_mbtowc; + cv->wctomb = utf32_wctomb; + if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0) + cv->flags |= FLAG_USE_BOM; + } + else if (cv->codepage == 65001) + { + cv->mbtowc = kernel_mbtowc; + cv->wctomb = kernel_wctomb; + cv->mblen = utf8_mblen; + } + else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang()) + { + cv->mbtowc = iso2022jp_mbtowc; + cv->wctomb = iso2022jp_wctomb; + cv->flush = iso2022jp_flush; + } + else if (cv->codepage == 51932 && load_mlang()) + { + cv->mbtowc = mlang_mbtowc; + cv->wctomb = mlang_wctomb; + cv->mblen = eucjp_mblen; + } + else if (IsValidCodePage(cv->codepage) + && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0) + { + cv->mbtowc = kernel_mbtowc; + cv->wctomb = kernel_wctomb; + if (cpinfoex.MaxCharSize == 1) + cv->mblen = sbcs_mblen; + else if (cpinfoex.MaxCharSize == 2) + cv->mblen = dbcs_mblen; + else + cv->mblen = mbcs_mblen; + } + else + { + /* not supported */ + free(name); + errno = EINVAL; + return FALSE; + } + + if (use_compat) + { + switch (cv->codepage) + { + case 932: cv->compat = cp932_compat; break; + case 20932: cv->compat = cp20932_compat; break; + case 51932: cv->compat = cp51932_compat; break; + case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break; + } + } + + free(name); + + return TRUE; +} + +static int +name_to_codepage(const char *name) +{ + int i; + + if (*name == '\0' || + strcmp(name, "char") == 0) + return GetACP(); + else if (strcmp(name, "wchar_t") == 0) + return 1200; + else if (_strnicmp(name, "cp", 2) == 0) + return atoi(name + 2); /* CP123 */ + else if ('0' <= name[0] && name[0] <= '9') + return atoi(name); /* 123 */ + else if (_strnicmp(name, "xx", 2) == 0) + return atoi(name + 2); /* XX123 for debug */ + + for (i = 0; codepage_alias[i].name != NULL; ++i) + if (_stricmp(name, codepage_alias[i].name) == 0) + return codepage_alias[i].codepage; + return -1; +} + +/* + * http://www.faqs.org/rfcs/rfc2781.html + */ +static uint +utf16_to_ucs4(const ushort *wbuf) +{ + uint wc = wbuf[0]; + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; + return wc; +} + +static void +ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) +{ + if (wc < 0x10000) + { + wbuf[0] = wc; + *wbufsize = 1; + } + else + { + wc -= 0x10000; + wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); + wbuf[1] = 0xDC00 | (wc & 0x3FF); + *wbufsize = 2; + } +} + +/* + * Check if codepage is one of those for which the dwFlags parameter + * to MultiByteToWideChar() must be zero. Return zero or + * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows + * Server 2003 R2 claims that also codepage 65001 is one of these, but + * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave + * out 65001 (UTF-8), and that indeed seems to be the case on XP, it + * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting + * from UTF-8. + */ +static int +mbtowc_flags(int codepage) +{ + return (codepage == 50220 || codepage == 50221 || + codepage == 50222 || codepage == 50225 || + codepage == 50227 || codepage == 50229 || + codepage == 52936 || codepage == 54936 || + (codepage >= 57002 && codepage <= 57011) || + codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; +} + +/* + * Check if codepage is one those for which the lpUsedDefaultChar + * parameter to WideCharToMultiByte() must be NULL. The docs in + * Platform SDK for for Windows Server 2003 R2 claims that this is the + * list below, while the MSDN docs for MSVS2008 claim that it is only + * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform + * SDK seems to be correct, at least for XP. + */ +static int +must_use_null_useddefaultchar(int codepage) +{ + return (codepage == 65000 || codepage == 65001 || + codepage == 50220 || codepage == 50221 || + codepage == 50222 || codepage == 50225 || + codepage == 50227 || codepage == 50229 || + codepage == 52936 || codepage == 54936 || + (codepage >= 57002 && codepage <= 57011) || + codepage == 42); +} + +static char * +strrstr(const char *str, const char *token) +{ + int len = strlen(token); + const char *p = str + strlen(str); + + while (str <= --p) + if (p[0] == token[0] && strncmp(p, token, len) == 0) + return (char *)p; + return NULL; +} + +static char * +xstrndup(const char *s, size_t n) +{ + char *p; + + p = (char *)malloc(n + 1); + if (p == NULL) + return NULL; + memcpy(p, s, n); + p[n] = '\0'; + return p; +} + +static int +seterror(int err) +{ + errno = err; + return -1; +} + +#if defined(USE_LIBICONV_DLL) +static int +libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) +{ + HMODULE hlibiconv = NULL; + HMODULE hmsvcrt = NULL; + char *dllname; + const char *p; + const char *e; + f_iconv_open _iconv_open; + + /* + * always try to load dll, so that we can switch dll in runtime. + */ + + /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */ + p = getenv("WINICONV_LIBICONV_DLL"); + if (p == NULL) + p = DEFAULT_LIBICONV_DLL; + /* parse comma separated value */ + for ( ; *p != 0; p = (*e == ',') ? e + 1 : e) + { + e = strchr(p, ','); + if (p == e) + continue; + else if (e == NULL) + e = p + strlen(p); + dllname = xstrndup(p, e - p); + if (dllname == NULL) + return FALSE; + hlibiconv = LoadLibrary(dllname); + free(dllname); + if (hlibiconv != NULL) + { + if (hlibiconv == hwiniconv) + { + FreeLibrary(hlibiconv); + hlibiconv = NULL; + continue; + } + break; + } + } + + if (hlibiconv == NULL) + goto failed; + + hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno"); + if (hmsvcrt == NULL) + goto failed; + + _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open"); + if (_iconv_open == NULL) + _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open"); + cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close"); + if (cd->iconv_close == NULL) + cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close"); + cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv"); + if (cd->iconv == NULL) + cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv"); + cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno"); + if (_iconv_open == NULL || cd->iconv_close == NULL + || cd->iconv == NULL || cd->_errno == NULL) + goto failed; + + cd->cd = _iconv_open(tocode, fromcode); + if (cd->cd == (iconv_t)(-1)) + goto failed; + + cd->hlibiconv = hlibiconv; + return TRUE; + +failed: + if (hlibiconv != NULL) + FreeLibrary(hlibiconv); + /* do not free hmsvcrt which is obtained by GetModuleHandle() */ + return FALSE; +} + +/* + * Reference: + * http://forums.belution.com/ja/vc/000/234/78s.shtml + * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html + * + * The formal way is + * imagehlp.h or dbghelp.h + * imagehlp.lib or dbghelp.lib + * ImageDirectoryEntryToData() + */ +#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base)) +#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew)) +static PVOID +MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size) +{ + /* TODO: MappedAsImage? */ + PIMAGE_DATA_DIRECTORY p; + p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry; + if (p->VirtualAddress == 0) { + *Size = 0; + return NULL; + } + *Size = p->Size; + return (PVOID)((LPBYTE)Base + p->VirtualAddress); +} + +static HMODULE +find_imported_module_by_funcname(HMODULE hModule, const char *funcname) +{ + DWORD Base; + ULONG Size; + PIMAGE_IMPORT_DESCRIPTOR Imp; + PIMAGE_THUNK_DATA Name; /* Import Name Table */ + PIMAGE_IMPORT_BY_NAME ImpName; + + Base = (DWORD)hModule; + Imp = MyImageDirectoryEntryToData( + (LPVOID)Base, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + if (Imp == NULL) + return NULL; + for ( ; Imp->OriginalFirstThunk != 0; ++Imp) + { + Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk); + for ( ; Name->u1.Ordinal != 0; ++Name) + { + if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal)) + { + ImpName = (PIMAGE_IMPORT_BY_NAME) + (Base + (DWORD)Name->u1.AddressOfData); + if (strcmp((char *)ImpName->Name, funcname) == 0) + return GetModuleHandle((char *)(Base + Imp->Name)); + } + } + } + return NULL; +} +#endif + +static int +sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + return 1; +} + +static int +dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; + if (bufsize < len) + return seterror(EINVAL); + return len; +} + +static int +mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = 0; + + if (cv->codepage == 54936) { + if (buf[0] <= 0x7F) len = 1; + else if (buf[0] >= 0x81 && buf[0] <= 0xFE && + bufsize >= 2 && + ((buf[1] >= 0x40 && buf[1] <= 0x7E) || + (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2; + else if (buf[0] >= 0x81 && buf[0] <= 0xFE && + bufsize >= 4 && + buf[1] >= 0x30 && buf[1] <= 0x39) len = 4; + else + return seterror(EINVAL); + return len; + } + else + return seterror(EINVAL); +} + +static int +utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = 0; + + if (buf[0] < 0x80) len = 1; + else if ((buf[0] & 0xE0) == 0xC0) len = 2; + else if ((buf[0] & 0xF0) == 0xE0) len = 3; + else if ((buf[0] & 0xF8) == 0xF0) len = 4; + else if ((buf[0] & 0xFC) == 0xF8) len = 5; + else if ((buf[0] & 0xFE) == 0xFC) len = 6; + + if (len == 0) + return seterror(EILSEQ); + else if (bufsize < len) + return seterror(EINVAL); + return len; +} + +static int +eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + if (buf[0] < 0x80) /* ASCII */ + return 1; + else if (buf[0] == 0x8E) /* JIS X 0201 */ + { + if (bufsize < 2) + return seterror(EINVAL); + else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) + return seterror(EILSEQ); + return 2; + } + else if (buf[0] == 0x8F) /* JIS X 0212 */ + { + if (bufsize < 3) + return seterror(EINVAL); + else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) + || !(0xA1 <= buf[2] && buf[2] <= 0xFE)) + return seterror(EILSEQ); + return 3; + } + else /* JIS X 0208 */ + { + if (bufsize < 2) + return seterror(EINVAL); + else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) + || !(0xA1 <= buf[1] && buf[1] <= 0xFE)) + return seterror(EILSEQ); + return 2; + } +} + +static int +kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int len; + + len = cv->mblen(cv, buf, bufsize); + if (len == -1) + return -1; + *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), + (const char *)buf, len, (wchar_t *)wbuf, *wbufsize); + if (*wbufsize == 0) + return seterror(EILSEQ); + return len; +} + +static int +kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + BOOL usedDefaultChar = 0; + BOOL *p = NULL; + int flags = 0; + int len; + + if (bufsize == 0) + return seterror(E2BIG); + if (!must_use_null_useddefaultchar(cv->codepage)) + { + p = &usedDefaultChar; +#ifdef WC_NO_BEST_FIT_CHARS + if (!(cv->flags & FLAG_TRANSLIT)) + flags |= WC_NO_BEST_FIT_CHARS; +#endif + } + len = WideCharToMultiByte(cv->codepage, flags, + (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); + if (len == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + return seterror(E2BIG); + return seterror(EILSEQ); + } + else if (usedDefaultChar) + return seterror(EILSEQ); + else if (cv->mblen(cv, buf, len) != len) /* validate result */ + return seterror(EILSEQ); + return len; +} + +/* + * It seems that the mode (cv->mode) is fixnum. + * For example, when converting iso-2022-jp(cp50221) to unicode: + * in ascii sequence: mode=0xC42C0000 + * in jisx0208 sequence: mode=0xC42C0001 + * "C42C" is same for each convert session. + * It should be: ((codepage-1)<<16)|state + */ +static int +mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int len; + int insize; + HRESULT hr; + + len = cv->mblen(cv, buf, bufsize); + if (len == -1) + return -1; + insize = len; + hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, + (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len) + return seterror(EILSEQ); + return len; +} + +static int +mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + + hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) + return seterror(EILSEQ); + memcpy(buf, tmpbuf, tmpsize); + return tmpsize; +} + +static int +utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + + /* swap endian: 1200 <-> 1201 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 2) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[0] = (buf[1] << 8) | buf[0]; + else if (codepage == 1201) /* big endian */ + wbuf[0] = (buf[0] << 8) | buf[1]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wbuf[0] == 0xFFFE) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 2; + } + else if (wbuf[0] == 0xFEFF) + { + *wbufsize = 0; + return 2; + } + } + + if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) + return seterror(EILSEQ); + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[1] = (buf[3] << 8) | buf[2]; + else if (codepage == 1201) /* big endian */ + wbuf[1] = (buf[2] << 8) | buf[3]; + if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) + return seterror(EILSEQ); + *wbufsize = 2; + return 4; + } + *wbufsize = 1; + return 2; +} + +static int +utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + memcpy(buf, "\xFF\xFE", 2); + else if (cv->codepage == 1201) /* big endian */ + memcpy(buf, "\xFE\xFF", 2); + + r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); + if (r == -1) + return -1; + return r + 2; + } + + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[0] = (wbuf[0] & 0x00FF); + buf[1] = (wbuf[0] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[0] = (wbuf[0] & 0xFF00) >> 8; + buf[1] = (wbuf[0] & 0x00FF); + } + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[2] = (wbuf[1] & 0x00FF); + buf[3] = (wbuf[1] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[2] = (wbuf[1] & 0xFF00) >> 8; + buf[3] = (wbuf[1] & 0x00FF); + } + return 4; + } + return 2; +} + +static int +utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + uint wc = 0; + + /* swap endian: 12000 <-> 12001 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 12000) /* little endian */ + wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + else if (codepage == 12001) /* big endian */ + wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wc == 0xFFFE0000) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 4; + } + else if (wc == 0x0000FEFF) + { + *wbufsize = 0; + return 4; + } + } + + if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) + return seterror(EILSEQ); + ucs4_to_utf16(wc, wbuf, wbufsize); + return 4; +} + +static int +utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + uint wc; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 12000) /* little endian */ + memcpy(buf, "\xFF\xFE\x00\x00", 4); + else if (cv->codepage == 12001) /* big endian */ + memcpy(buf, "\x00\x00\xFE\xFF", 4); + + r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); + if (r == -1) + return -1; + return r + 4; + } + + if (bufsize < 4) + return seterror(E2BIG); + wc = utf16_to_ucs4(wbuf); + if (cv->codepage == 12000) /* little endian */ + { + buf[0] = wc & 0x000000FF; + buf[1] = (wc & 0x0000FF00) >> 8; + buf[2] = (wc & 0x00FF0000) >> 16; + buf[3] = (wc & 0xFF000000) >> 24; + } + else if (cv->codepage == 12001) /* big endian */ + { + buf[0] = (wc & 0xFF000000) >> 24; + buf[1] = (wc & 0x00FF0000) >> 16; + buf[2] = (wc & 0x0000FF00) >> 8; + buf[3] = wc & 0x000000FF; + } + return 4; +} + +/* + * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) + * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow + * 1 byte Kana) + * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte + * Kana - SO/SI) + * + * MultiByteToWideChar() and WideCharToMultiByte() behave differently + * depending on Windows version. On XP, WideCharToMultiByte() doesn't + * terminate result sequence with ascii escape. But Vista does. + * Use MLang instead. + */ + +#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) +#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) +#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) + +#define ISO2022_SI 0 +#define ISO2022_SO 1 + +/* shift in */ +static const char iso2022_SI_seq[] = "\x0F"; +/* shift out */ +static const char iso2022_SO_seq[] = "\x0E"; + +typedef struct iso2022_esc_t iso2022_esc_t; +struct iso2022_esc_t { + const char *esc; + int esc_len; + int len; + int cs; +}; + +#define ISO2022JP_CS_ASCII 0 +#define ISO2022JP_CS_JISX0201_ROMAN 1 +#define ISO2022JP_CS_JISX0201_KANA 2 +#define ISO2022JP_CS_JISX0208_1978 3 +#define ISO2022JP_CS_JISX0208_1983 4 +#define ISO2022JP_CS_JISX0212 5 + +static iso2022_esc_t iso2022jp_esc[] = { + {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, + {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, + {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, + {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ + {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, + {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, + {NULL, 0, 0, 0} +}; + +static int +iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int insize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len; + int cs; + int shift; + int i; + + if (buf[0] == 0x1B) + { + for (i = 0; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (bufsize < esc_len) + { + if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) + return seterror(EINVAL); + } + else + { + if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) + { + cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); + *wbufsize = 0; + return esc_len; + } + } + } + /* not supported escape sequence */ + return seterror(EILSEQ); + } + else if (buf[0] == iso2022_SO_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); + *wbufsize = 0; + return 1; + } + else if (buf[0] == iso2022_SI_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); + *wbufsize = 0; + return 1; + } + + cs = ISO2022_MODE_CS(cv->mode); + shift = ISO2022_MODE_SHIFT(cv->mode); + + /* reset the mode for informal sequence */ + if (buf[0] < 0x20) + { + cs = ISO2022JP_CS_ASCII; + shift = ISO2022_SI; + } + + len = iesc[cs].len; + if (bufsize < len) + return seterror(EINVAL); + for (i = 0; i < len; ++i) + if (!(buf[i] < 0x80)) + return seterror(EILSEQ); + esc_len = iesc[cs].esc_len; + memcpy(tmp, iesc[cs].esc, esc_len); + if (shift == ISO2022_SO) + { + memcpy(tmp + esc_len, iso2022_SO_seq, 1); + esc_len += 1; + } + memcpy(tmp + esc_len, buf, len); + + if ((cv->codepage == 50220 || cv->codepage == 50221 + || cv->codepage == 50222) && shift == ISO2022_SO) + { + /* XXX: shift-out cannot be used for mbtowc (both kernel and + * mlang) */ + esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; + memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); + memcpy(tmp + esc_len, buf, len); + } + + insize = len + esc_len; + hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, + (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len + esc_len) + return seterror(EILSEQ); + + /* Check for conversion error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (wbuf[0] == buf[0] + && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + return seterror(EILSEQ); + + /* reset the mode for informal sequence */ + if (cv->mode != ISO2022_MODE(cs, shift)) + cv->mode = ISO2022_MODE(cs, shift); + + return len; +} + +static int +iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len = 0; + int cs = 0; + int shift; + int i; + + /* + * MultiByte = [escape sequence] + character + [escape sequence] + * + * Whether trailing escape sequence is added depends on which API is + * used (kernel or MLang, and its version). + */ + hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + + if (tmpsize == 1) + { + cs = ISO2022JP_CS_ASCII; + esc_len = 0; + } + else + { + for (i = 1; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (strncmp(tmp, iesc[i].esc, esc_len) == 0) + { + cs = iesc[i].cs; + break; + } + } + if (iesc[i].esc == NULL) + /* not supported escape sequence */ + return seterror(EILSEQ); + } + + shift = ISO2022_SI; + if (tmp[esc_len] == iso2022_SO_seq[0]) + { + shift = ISO2022_SO; + esc_len += 1; + } + + len = iesc[cs].len; + + /* Check for converting error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) + return seterror(EILSEQ); + else if (tmpsize < esc_len + len) + return seterror(EILSEQ); + + if (cv->mode == ISO2022_MODE(cs, shift)) + { + /* remove escape sequence */ + if (esc_len != 0) + memmove(tmp, tmp + esc_len, len); + esc_len = 0; + } + else + { + if (cs == ISO2022JP_CS_ASCII) + { + esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; + memmove(tmp + esc_len, tmp, len); + memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); + } + if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) + { + /* shift-in before changing to other mode */ + memmove(tmp + 1, tmp, len + esc_len); + memcpy(tmp, iso2022_SI_seq, 1); + esc_len += 1; + } + } + + if (bufsize < len + esc_len) + return seterror(E2BIG); + memcpy(buf, tmp, len + esc_len); + cv->mode = ISO2022_MODE(cs, shift); + return len + esc_len; +} + +static int +iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + int esc_len; + + if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + { + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + esc_len += 1; + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + if (bufsize < esc_len) + return seterror(E2BIG); + + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + { + memcpy(buf, iso2022_SI_seq, 1); + esc_len += 1; + } + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + { + memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, + iesc[ISO2022JP_CS_ASCII].esc_len); + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + } + return esc_len; + } + return 0; +} + +#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) +BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) +{ + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + hwiniconv = (HMODULE)hinstDLL; + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +#if defined(MAKE_EXE) +#include +#include +#include +int +main(int argc, char **argv) +{ + char *fromcode = NULL; + char *tocode = NULL; + int i; + char inbuf[BUFSIZ]; + char outbuf[BUFSIZ]; + const char *pin; + char *pout; + size_t inbytesleft; + size_t outbytesleft; + size_t rest = 0; + iconv_t cd; + size_t r; + FILE *in = stdin; + + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + + for (i = 1; i < argc; ++i) + { + if (strcmp(argv[i], "-l") == 0) + { + for (i = 0; codepage_alias[i].name != NULL; ++i) + printf("%s\n", codepage_alias[i].name); + return 0; + } + + if (strcmp(argv[i], "-f") == 0) + fromcode = argv[++i]; + else if (strcmp(argv[i], "-t") == 0) + tocode = argv[++i]; + else + { + in = fopen(argv[i], "rb"); + if (in == NULL) + { + fprintf(stderr, "cannot open %s\n", argv[i]); + return 1; + } + break; + } + } + + if (fromcode == NULL || tocode == NULL) + { + printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]); + return 0; + } + + cd = iconv_open(tocode, fromcode); + if (cd == (iconv_t)(-1)) + { + perror("iconv_open error"); + return 1; + } + + while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0 + || rest != 0) + { + inbytesleft += rest; + pin = inbuf; + pout = outbuf; + outbytesleft = sizeof(outbuf); + r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); + fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); + if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in))) + { + perror("conversion error"); + return 1; + } + memmove(inbuf, pin, inbytesleft); + rest = inbytesleft; + } + pout = outbuf; + outbytesleft = sizeof(outbuf); + r = iconv(cd, NULL, NULL, &pout, &outbytesleft); + fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); + if (r == (size_t)(-1)) + { + perror("conversion error"); + return 1; + } + + iconv_close(cd); + + return 0; +} +#endif + diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv/iconv.h b/src/mod/endpoints/mod_gsmopen/win_iconv/iconv.h index 7c941fe435..455be3b6b7 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv/iconv.h +++ b/src/mod/endpoints/mod_gsmopen/win_iconv/iconv.h @@ -1,5 +1,5 @@ -#include -typedef void* iconv_t; -iconv_t iconv_open(const char *tocode, const char *fromcode); -int iconv_close(iconv_t cd); -size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +#include +typedef void* iconv_t; +iconv_t iconv_open(const char *tocode, const char *fromcode); +int iconv_close(iconv_t cd); +size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv/mlang.h b/src/mod/endpoints/mod_gsmopen/win_iconv/mlang.h index 525f484e98..5cbf779c92 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv/mlang.h +++ b/src/mod/endpoints/mod_gsmopen/win_iconv/mlang.h @@ -1,54 +1,54 @@ -HRESULT WINAPI ConvertINetString( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - DWORD dwDstEncoding, - LPCSTR lpSrcStr, - LPINT lpnSrcSize, - LPBYTE lpDstStr, - LPINT lpnDstSize -); - -HRESULT WINAPI ConvertINetMultiByteToUnicode( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - LPCSTR lpSrcStr, - LPINT lpnMultiCharCount, - LPWSTR lpDstStr, - LPINT lpnWideCharCount -); - -HRESULT WINAPI ConvertINetUnicodeToMultiByte( - LPDWORD lpdwMode, - DWORD dwEncoding, - LPCWSTR lpSrcStr, - LPINT lpnWideCharCount, - LPSTR lpDstStr, - LPINT lpnMultiCharCount -); - -HRESULT WINAPI IsConvertINetStringAvailable( - DWORD dwSrcEncoding, - DWORD dwDstEncoding -); - -HRESULT WINAPI LcidToRfc1766A( - LCID Locale, - LPSTR pszRfc1766, - int nChar -); - -HRESULT WINAPI LcidToRfc1766W( - LCID Locale, - LPWSTR pszRfc1766, - int nChar -); - -HRESULT WINAPI Rfc1766ToLcidA( - LCID *pLocale, - LPSTR pszRfc1766 -); - -HRESULT WINAPI Rfc1766ToLcidW( - LCID *pLocale, - LPWSTR pszRfc1766 -); +HRESULT WINAPI ConvertINetString( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + DWORD dwDstEncoding, + LPCSTR lpSrcStr, + LPINT lpnSrcSize, + LPBYTE lpDstStr, + LPINT lpnDstSize +); + +HRESULT WINAPI ConvertINetMultiByteToUnicode( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + LPCSTR lpSrcStr, + LPINT lpnMultiCharCount, + LPWSTR lpDstStr, + LPINT lpnWideCharCount +); + +HRESULT WINAPI ConvertINetUnicodeToMultiByte( + LPDWORD lpdwMode, + DWORD dwEncoding, + LPCWSTR lpSrcStr, + LPINT lpnWideCharCount, + LPSTR lpDstStr, + LPINT lpnMultiCharCount +); + +HRESULT WINAPI IsConvertINetStringAvailable( + DWORD dwSrcEncoding, + DWORD dwDstEncoding +); + +HRESULT WINAPI LcidToRfc1766A( + LCID Locale, + LPSTR pszRfc1766, + int nChar +); + +HRESULT WINAPI LcidToRfc1766W( + LCID Locale, + LPWSTR pszRfc1766, + int nChar +); + +HRESULT WINAPI Rfc1766ToLcidA( + LCID *pLocale, + LPSTR pszRfc1766 +); + +HRESULT WINAPI Rfc1766ToLcidW( + LCID *pLocale, + LPWSTR pszRfc1766 +); diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv/readme.txt b/src/mod/endpoints/mod_gsmopen/win_iconv/readme.txt index c4fb7d692b..31f423dc75 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv/readme.txt +++ b/src/mod/endpoints/mod_gsmopen/win_iconv/readme.txt @@ -1,3 +1,3 @@ -win_iconv is a iconv library using Win32 API to conversion. -win_iconv is placed in the public domain. -Yukihiro Nakadaira +win_iconv is a iconv library using Win32 API to conversion. +win_iconv is placed in the public domain. +Yukihiro Nakadaira diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv.c b/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv.c index 94c8d4a1e7..7a3eda3e10 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv.c +++ b/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv.c @@ -1,1986 +1,1986 @@ -/* - * iconv library using Win32 API to conversion. - * - * This file is placed in the public domain. - * - * Last Change: 2009-07-06 - * - * ENVIRONMENT VARIABLE: - * WINICONV_LIBICONV_DLL - * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If - * loading the DLL or iconv_open() failed, falls back to internal - * conversion. If a few DLL are specified as comma separated list, - * the first loadable DLL is used. The DLL should have iconv_open(), - * iconv_close() and iconv(). Or libiconv_open(), libiconv_close() - * and libiconv(). - * (only available when USE_LIBICONV_DLL is defined at compile time) - * - * Win32 API does not support strict encoding conversion for some - * codepage. And MLang function drop or replace invalid bytes and does - * not return useful error status as iconv. This implementation cannot - * be used for encoding validation purpose. - */ - -/* for WC_NO_BEST_FIT_CHARS */ -#ifndef WINVER -# define WINVER 0x0500 -#endif - -#include -#include -#include -#include - -#if 0 -# define MAKE_EXE -# define MAKE_DLL -# define USE_LIBICONV_DLL -#endif - -#if !defined(DEFAULT_LIBICONV_DLL) -# define DEFAULT_LIBICONV_DLL "" -#endif - -#define MB_CHAR_MAX 16 - -#define UNICODE_MODE_BOM_DONE 1 -#define UNICODE_MODE_SWAPPED 2 - -#define FLAG_USE_BOM 1 -#define FLAG_TRANSLIT 2 /* //TRANSLIT */ -#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */ - -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; - -typedef void* iconv_t; - -iconv_t iconv_open(const char *tocode, const char *fromcode); -int iconv_close(iconv_t cd); -size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); - -/* libiconv interface for vim */ -#if defined(MAKE_DLL) -int -iconvctl (iconv_t cd, int request, void* argument) -{ - /* not supported */ - return 0; -} -#endif - -typedef struct compat_t compat_t; -typedef struct csconv_t csconv_t; -typedef struct rec_iconv_t rec_iconv_t; - -typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode); -typedef int (*f_iconv_close)(iconv_t cd); -typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); -typedef int* (*f_errno)(void); -typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); -typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); - -#define COMPAT_IN 1 -#define COMPAT_OUT 2 - -/* unicode mapping for compatibility with other conversion table. */ -struct compat_t { - uint in; - uint out; - uint flag; -}; - -struct csconv_t { - int codepage; - int flags; - f_mbtowc mbtowc; - f_wctomb wctomb; - f_mblen mblen; - f_flush flush; - DWORD mode; - compat_t *compat; -}; - -struct rec_iconv_t { - iconv_t cd; - f_iconv_close iconv_close; - f_iconv iconv; - f_errno _errno; - csconv_t from; - csconv_t to; -#if defined(USE_LIBICONV_DLL) - HMODULE hlibiconv; -#endif -}; - -static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); -static int win_iconv_close(iconv_t cd); -static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); - -static int load_mlang(); -static int make_csconv(const char *name, csconv_t *cv); -static int name_to_codepage(const char *name); -static uint utf16_to_ucs4(const ushort *wbuf); -static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); -static int mbtowc_flags(int codepage); -static int must_use_null_useddefaultchar(int codepage); -static char *strrstr(const char *str, const char *token); -static char *xstrndup(const char *s, size_t n); -static int seterror(int err); - -#if defined(USE_LIBICONV_DLL) -static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); -static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size); -static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname); - -static HMODULE hwiniconv; -#endif - -static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); -static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); - -static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); -static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); -static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); - -static struct { - int codepage; - const char *name; -} codepage_alias[] = { - {65001, "CP65001"}, - {65001, "UTF8"}, - {65001, "UTF-8"}, - - {1200, "CP1200"}, - {1200, "UTF16LE"}, - {1200, "UTF-16LE"}, - {1200, "UCS-2LE"}, - - {1201, "CP1201"}, - {1201, "UTF16BE"}, - {1201, "UTF-16BE"}, - {1201, "UCS-2BE"}, - {1201, "unicodeFFFE"}, - - {12000, "CP12000"}, - {12000, "UTF32LE"}, - {12000, "UTF-32LE"}, - - {12001, "CP12001"}, - {12001, "UTF32BE"}, - {12001, "UTF-32BE"}, - -#ifndef GLIB_COMPILATION - /* - * Default is big endian. - * See rfc2781 4.3 Interpreting text labelled as UTF-16. - */ - {1201, "UTF16"}, - {1201, "UTF-16"}, - {12001, "UTF32"}, - {12001, "UTF-32"}, -#else - /* Default is little endian, because the platform is */ - {1200, "UTF16"}, - {1200, "UTF-16"}, - {1200, "UCS-2"}, - {12000, "UTF32"}, - {12000, "UTF-32"}, -#endif - - /* copy from libiconv `iconv -l` */ - /* !IsValidCodePage(367) */ - {20127, "ANSI_X3.4-1968"}, - {20127, "ANSI_X3.4-1986"}, - {20127, "ASCII"}, - {20127, "CP367"}, - {20127, "IBM367"}, - {20127, "ISO-IR-6"}, - {20127, "ISO646-US"}, - {20127, "ISO_646.IRV:1991"}, - {20127, "US"}, - {20127, "US-ASCII"}, - {20127, "CSASCII"}, - - /* !IsValidCodePage(819) */ - {1252, "CP819"}, - {1252, "IBM819"}, - {28591, "ISO-8859-1"}, - {28591, "ISO-IR-100"}, - {28591, "ISO8859-1"}, - {28591, "ISO_8859-1"}, - {28591, "ISO_8859-1:1987"}, - {28591, "L1"}, - {28591, "LATIN1"}, - {28591, "CSISOLATIN1"}, - - {1250, "CP1250"}, - {1250, "MS-EE"}, - {1250, "WINDOWS-1250"}, - - {1251, "CP1251"}, - {1251, "MS-CYRL"}, - {1251, "WINDOWS-1251"}, - - {1252, "CP1252"}, - {1252, "MS-ANSI"}, - {1252, "WINDOWS-1252"}, - - {1253, "CP1253"}, - {1253, "MS-GREEK"}, - {1253, "WINDOWS-1253"}, - - {1254, "CP1254"}, - {1254, "MS-TURK"}, - {1254, "WINDOWS-1254"}, - - {1255, "CP1255"}, - {1255, "MS-HEBR"}, - {1255, "WINDOWS-1255"}, - - {1256, "CP1256"}, - {1256, "MS-ARAB"}, - {1256, "WINDOWS-1256"}, - - {1257, "CP1257"}, - {1257, "WINBALTRIM"}, - {1257, "WINDOWS-1257"}, - - {1258, "CP1258"}, - {1258, "WINDOWS-1258"}, - - {850, "850"}, - {850, "CP850"}, - {850, "IBM850"}, - {850, "CSPC850MULTILINGUAL"}, - - /* !IsValidCodePage(862) */ - {862, "862"}, - {862, "CP862"}, - {862, "IBM862"}, - {862, "CSPC862LATINHEBREW"}, - - {866, "866"}, - {866, "CP866"}, - {866, "IBM866"}, - {866, "CSIBM866"}, - - /* !IsValidCodePage(154) */ - {154, "CP154"}, - {154, "CYRILLIC-ASIAN"}, - {154, "PT154"}, - {154, "PTCP154"}, - {154, "CSPTCP154"}, - - /* !IsValidCodePage(1133) */ - {1133, "CP1133"}, - {1133, "IBM-CP1133"}, - - {874, "CP874"}, - {874, "WINDOWS-874"}, - - /* !IsValidCodePage(51932) */ - {51932, "CP51932"}, - {51932, "MS51932"}, - {51932, "WINDOWS-51932"}, - {51932, "EUC-JP"}, - - {932, "CP932"}, - {932, "MS932"}, - {932, "SHIFFT_JIS"}, - {932, "SHIFFT_JIS-MS"}, - {932, "SJIS"}, - {932, "SJIS-MS"}, - {932, "SJIS-OPEN"}, - {932, "SJIS-WIN"}, - {932, "WINDOWS-31J"}, - {932, "WINDOWS-932"}, - {932, "CSWINDOWS31J"}, - - {50221, "CP50221"}, - {50221, "ISO-2022-JP"}, - {50221, "ISO-2022-JP-MS"}, - {50221, "ISO2022-JP"}, - {50221, "ISO2022-JP-MS"}, - {50221, "MS50221"}, - {50221, "WINDOWS-50221"}, - - {936, "CP936"}, - {936, "GBK"}, - {936, "MS936"}, - {936, "WINDOWS-936"}, - - {950, "CP950"}, - {950, "BIG5"}, - - {949, "CP949"}, - {949, "UHC"}, - {949, "EUC-KR"}, - - {1361, "CP1361"}, - {1361, "JOHAB"}, - - {437, "437"}, - {437, "CP437"}, - {437, "IBM437"}, - {437, "CSPC8CODEPAGE437"}, - - {737, "CP737"}, - - {775, "CP775"}, - {775, "IBM775"}, - {775, "CSPC775BALTIC"}, - - {852, "852"}, - {852, "CP852"}, - {852, "IBM852"}, - {852, "CSPCP852"}, - - /* !IsValidCodePage(853) */ - {853, "CP853"}, - - {855, "855"}, - {855, "CP855"}, - {855, "IBM855"}, - {855, "CSIBM855"}, - - {857, "857"}, - {857, "CP857"}, - {857, "IBM857"}, - {857, "CSIBM857"}, - - /* !IsValidCodePage(858) */ - {858, "CP858"}, - - {860, "860"}, - {860, "CP860"}, - {860, "IBM860"}, - {860, "CSIBM860"}, - - {861, "861"}, - {861, "CP-IS"}, - {861, "CP861"}, - {861, "IBM861"}, - {861, "CSIBM861"}, - - {863, "863"}, - {863, "CP863"}, - {863, "IBM863"}, - {863, "CSIBM863"}, - - {864, "CP864"}, - {864, "IBM864"}, - {864, "CSIBM864"}, - - {865, "865"}, - {865, "CP865"}, - {865, "IBM865"}, - {865, "CSIBM865"}, - - {869, "869"}, - {869, "CP-GR"}, - {869, "CP869"}, - {869, "IBM869"}, - {869, "CSIBM869"}, - - /* !IsValidCodePage(1152) */ - {1125, "CP1125"}, - - /* - * Code Page Identifiers - * http://msdn2.microsoft.com/en-us/library/ms776446.aspx - */ - {37, "IBM037"}, /* IBM EBCDIC US-Canada */ - {437, "IBM437"}, /* OEM United States */ - {500, "IBM500"}, /* IBM EBCDIC International */ - {708, "ASMO-708"}, /* Arabic (ASMO 708) */ - /* 709 Arabic (ASMO-449+, BCON V4) */ - /* 710 Arabic - Transparent Arabic */ - {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */ - {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */ - {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */ - {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */ - {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */ - {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */ - {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */ - {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */ - {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */ - {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */ - {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */ - {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */ - {864, "IBM864"}, /* OEM Arabic; Arabic (864) */ - {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */ - {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */ - {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */ - {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ - {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ - {875, "cp875"}, /* IBM EBCDIC Greek Modern */ - {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ - {932, "shift-jis"}, /* alternative name for it */ - {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ - {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */ - {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ - {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */ - {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */ - {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ - {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ - {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ - {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ - {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ - {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ - {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ - {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ - {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ - {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ - {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */ - {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */ - {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */ - {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */ - {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */ - {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */ - {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */ - {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */ - {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ - {1361, "Johab"}, /* Korean (Johab) */ - {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */ - {10001, "x-mac-japanese"}, /* Japanese (Mac) */ - {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ - {10003, "x-mac-korean"}, /* Korean (Mac) */ - {10004, "x-mac-arabic"}, /* Arabic (Mac) */ - {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */ - {10006, "x-mac-greek"}, /* Greek (Mac) */ - {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */ - {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ - {10010, "x-mac-romanian"}, /* Romanian (Mac) */ - {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */ - {10021, "x-mac-thai"}, /* Thai (Mac) */ - {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */ - {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */ - {10081, "x-mac-turkish"}, /* Turkish (Mac) */ - {10082, "x-mac-croatian"}, /* Croatian (Mac) */ - {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */ - {20001, "x-cp20001"}, /* TCA Taiwan */ - {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */ - {20003, "x-cp20003"}, /* IBM5550 Taiwan */ - {20004, "x-cp20004"}, /* TeleText Taiwan */ - {20005, "x-cp20005"}, /* Wang Taiwan */ - {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ - {20106, "x-IA5-German"}, /* IA5 German (7-bit) */ - {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */ - {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */ - {20127, "us-ascii"}, /* US-ASCII (7-bit) */ - {20261, "x-cp20261"}, /* T.61 */ - {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */ - {20273, "IBM273"}, /* IBM EBCDIC Germany */ - {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */ - {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */ - {20280, "IBM280"}, /* IBM EBCDIC Italy */ - {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */ - {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */ - {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */ - {20297, "IBM297"}, /* IBM EBCDIC France */ - {20420, "IBM420"}, /* IBM EBCDIC Arabic */ - {20423, "IBM423"}, /* IBM EBCDIC Greek */ - {20424, "IBM424"}, /* IBM EBCDIC Hebrew */ - {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */ - {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */ - {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */ - {20871, "IBM871"}, /* IBM EBCDIC Icelandic */ - {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */ - {20905, "IBM905"}, /* IBM EBCDIC Turkish */ - {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ - {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */ - {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ - {20949, "x-cp20949"}, /* Korean Wansung */ - {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ - /* 21027 (deprecated) */ - {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ - {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ - {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ - {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ - {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ - {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */ - {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */ - {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */ - {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */ - {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */ - {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */ - {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */ - {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */ - {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */ - {28597, "iso8859-7"}, /* ISO 8859-7 Greek */ - {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ - {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ - {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */ - {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */ - {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */ - {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */ - {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */ - {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */ - {29001, "x-Europa"}, /* Europa 3 */ - {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ - {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ - {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ - {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ - {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ - {50225, "iso-2022-kr"}, /* ISO 2022 Korean */ - {50225, "iso2022-kr"}, /* ISO 2022 Korean */ - {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ - /* 50229 ISO 2022 Traditional Chinese */ - /* 50930 EBCDIC Japanese (Katakana) Extended */ - /* 50931 EBCDIC US-Canada and Japanese */ - /* 50933 EBCDIC Korean Extended and Korean */ - /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ - /* 50936 EBCDIC Simplified Chinese */ - /* 50937 EBCDIC US-Canada and Traditional Chinese */ - /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ - {51932, "euc-jp"}, /* EUC Japanese */ - {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */ - {51949, "euc-kr"}, /* EUC Korean */ - /* 51950 EUC Traditional Chinese */ - {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ - {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ - {57002, "x-iscii-de"}, /* ISCII Devanagari */ - {57003, "x-iscii-be"}, /* ISCII Bengali */ - {57004, "x-iscii-ta"}, /* ISCII Tamil */ - {57005, "x-iscii-te"}, /* ISCII Telugu */ - {57006, "x-iscii-as"}, /* ISCII Assamese */ - {57007, "x-iscii-or"}, /* ISCII Oriya */ - {57008, "x-iscii-ka"}, /* ISCII Kannada */ - {57009, "x-iscii-ma"}, /* ISCII Malayalam */ - {57010, "x-iscii-gu"}, /* ISCII Gujarati */ - {57011, "x-iscii-pa"}, /* ISCII Punjabi */ - - {0, NULL} -}; - -/* - * SJIS SHIFTJIS table CP932 table - * ---- --------------------------- -------------------------------- - * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS - * 7E U+203E OVERLINE U+007E TILDE - * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR - * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS - * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE - * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO - * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS - * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN - * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN - * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN - * - * EUC-JP and ISO-2022-JP should be compatible with CP932. - * - * Kernel and MLang have different Unicode mapping table. Make sure - * which API is used. - */ -static compat_t cp932_compat[] = { - {0x00A5, 0x005C, COMPAT_OUT}, - {0x203E, 0x007E, COMPAT_OUT}, - {0x2014, 0x2015, COMPAT_OUT}, - {0x301C, 0xFF5E, COMPAT_OUT}, - {0x2016, 0x2225, COMPAT_OUT}, - {0x2212, 0xFF0D, COMPAT_OUT}, - {0x00A2, 0xFFE0, COMPAT_OUT}, - {0x00A3, 0xFFE1, COMPAT_OUT}, - {0x00AC, 0xFFE2, COMPAT_OUT}, - {0, 0, 0} -}; - -static compat_t cp20932_compat[] = { - {0x00A5, 0x005C, COMPAT_OUT}, - {0x203E, 0x007E, COMPAT_OUT}, - {0x2014, 0x2015, COMPAT_OUT}, - {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, - {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, - {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, - {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, - {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, - {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, - {0, 0, 0} -}; - -static compat_t *cp51932_compat = cp932_compat; - -/* cp20932_compat for kernel. cp932_compat for mlang. */ -static compat_t *cp5022x_compat = cp932_compat; - -typedef HRESULT (WINAPI *CONVERTINETSTRING)( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - DWORD dwDstEncoding, - LPCSTR lpSrcStr, - LPINT lpnSrcSize, - LPBYTE lpDstStr, - LPINT lpnDstSize -); -typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( - LPDWORD lpdwMode, - DWORD dwSrcEncoding, - LPCSTR lpSrcStr, - LPINT lpnMultiCharCount, - LPWSTR lpDstStr, - LPINT lpnWideCharCount -); -typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( - LPDWORD lpdwMode, - DWORD dwEncoding, - LPCWSTR lpSrcStr, - LPINT lpnWideCharCount, - LPSTR lpDstStr, - LPINT lpnMultiCharCount -); -typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)( - DWORD dwSrcEncoding, - DWORD dwDstEncoding -); -typedef HRESULT (WINAPI *LCIDTORFC1766A)( - LCID Locale, - LPSTR pszRfc1766, - int nChar -); -typedef HRESULT (WINAPI *LCIDTORFC1766W)( - LCID Locale, - LPWSTR pszRfc1766, - int nChar -); -typedef HRESULT (WINAPI *RFC1766TOLCIDA)( - LCID *pLocale, - LPSTR pszRfc1766 -); -typedef HRESULT (WINAPI *RFC1766TOLCIDW)( - LCID *pLocale, - LPWSTR pszRfc1766 -); -static CONVERTINETSTRING ConvertINetString; -static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; -static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; -static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable; -static LCIDTORFC1766A LcidToRfc1766A; -static RFC1766TOLCIDA Rfc1766ToLcidA; - -static int -load_mlang() -{ - HMODULE h; - if (ConvertINetString != NULL) - return TRUE; - h = LoadLibrary("mlang.dll"); - if (!h) - return FALSE; - ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString"); - ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode"); - ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte"); - IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable"); - LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A"); - Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA"); - return TRUE; -} - -iconv_t -iconv_open(const char *tocode, const char *fromcode) -{ - rec_iconv_t *cd; - - cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t)); - if (cd == NULL) - return (iconv_t)(-1); - -#if defined(USE_LIBICONV_DLL) - errno = 0; - if (libiconv_iconv_open(cd, tocode, fromcode)) - return (iconv_t)cd; -#endif - - /* reset the errno to prevent reporting wrong error code. - * 0 for unsorted error. */ - errno = 0; - if (win_iconv_open(cd, tocode, fromcode)) - return (iconv_t)cd; - - free(cd); - - return (iconv_t)(-1); -} - -int -iconv_close(iconv_t _cd) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - int r = cd->iconv_close(cd->cd); - int e = *(cd->_errno()); -#if defined(USE_LIBICONV_DLL) - if (cd->hlibiconv != NULL) - FreeLibrary(cd->hlibiconv); -#endif - free(cd); - errno = e; - return r; -} - -size_t -iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft); - errno = *(cd->_errno()); - return r; -} - -static int -win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) -{ - if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to)) - return FALSE; - cd->iconv_close = win_iconv_close; - cd->iconv = win_iconv; - cd->_errno = _errno; - cd->cd = (iconv_t)cd; - return TRUE; -} - -static int -win_iconv_close(iconv_t cd) -{ - return 0; -} - -static size_t -win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) -{ - rec_iconv_t *cd = (rec_iconv_t *)_cd; - ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ - int insize; - int outsize; - int wsize; - DWORD frommode; - DWORD tomode; - uint wc; - compat_t *cp; - int i; - - if (inbuf == NULL || *inbuf == NULL) - { - if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) - { - tomode = cd->to.mode; - outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); - if (outsize == -1) - { - cd->to.mode = tomode; - return (size_t)(-1); - } - *outbuf += outsize; - *outbytesleft -= outsize; - } - cd->from.mode = 0; - cd->to.mode = 0; - return 0; - } - - while (*inbytesleft != 0) - { - frommode = cd->from.mode; - tomode = cd->to.mode; - wsize = MB_CHAR_MAX; - - insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); - if (insize == -1) - { - cd->from.mode = frommode; - return (size_t)(-1); - } - - if (wsize == 0) - { - *inbuf += insize; - *inbytesleft -= insize; - continue; - } - - if (cd->from.compat != NULL) - { - wc = utf16_to_ucs4(wbuf); - cp = cd->from.compat; - for (i = 0; cp[i].in != 0; ++i) - { - if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) - { - ucs4_to_utf16(cp[i].in, wbuf, &wsize); - break; - } - } - } - - if (cd->to.compat != NULL) - { - wc = utf16_to_ucs4(wbuf); - cp = cd->to.compat; - for (i = 0; cp[i].in != 0; ++i) - { - if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) - { - ucs4_to_utf16(cp[i].out, wbuf, &wsize); - break; - } - } - } - - outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); - if (outsize == -1) - { - cd->from.mode = frommode; - cd->to.mode = tomode; - return (size_t)(-1); - } - - *inbuf += insize; - *outbuf += outsize; - *inbytesleft -= insize; - *outbytesleft -= outsize; - } - - return 0; -} - -static int -make_csconv(const char *_name, csconv_t *cv) -{ - CPINFOEX cpinfoex; - int use_compat = TRUE; - int flag = 0; - char *name; - char *p; - - name = xstrndup(_name, strlen(_name)); - if (name == NULL) - return FALSE; - - /* check for option "enc_name//opt1//opt2" */ - while ((p = strrstr(name, "//")) != NULL) - { - if (_stricmp(p + 2, "nocompat") == 0) - use_compat = FALSE; - else if (_stricmp(p + 2, "translit") == 0) - flag |= FLAG_TRANSLIT; - else if (_stricmp(p + 2, "ignore") == 0) - flag |= FLAG_IGNORE; - *p = 0; - } - - cv->mode = 0; - cv->flags = flag; - cv->mblen = NULL; - cv->flush = NULL; - cv->compat = NULL; - cv->codepage = name_to_codepage(name); - if (cv->codepage == 1200 || cv->codepage == 1201) - { - cv->mbtowc = utf16_mbtowc; - cv->wctomb = utf16_wctomb; - if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0) - cv->flags |= FLAG_USE_BOM; - } - else if (cv->codepage == 12000 || cv->codepage == 12001) - { - cv->mbtowc = utf32_mbtowc; - cv->wctomb = utf32_wctomb; - if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0) - cv->flags |= FLAG_USE_BOM; - } - else if (cv->codepage == 65001) - { - cv->mbtowc = kernel_mbtowc; - cv->wctomb = kernel_wctomb; - cv->mblen = utf8_mblen; - } - else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang()) - { - cv->mbtowc = iso2022jp_mbtowc; - cv->wctomb = iso2022jp_wctomb; - cv->flush = iso2022jp_flush; - } - else if (cv->codepage == 51932 && load_mlang()) - { - cv->mbtowc = mlang_mbtowc; - cv->wctomb = mlang_wctomb; - cv->mblen = eucjp_mblen; - } - else if (IsValidCodePage(cv->codepage) - && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0) - { - cv->mbtowc = kernel_mbtowc; - cv->wctomb = kernel_wctomb; - if (cpinfoex.MaxCharSize == 1) - cv->mblen = sbcs_mblen; - else if (cpinfoex.MaxCharSize == 2) - cv->mblen = dbcs_mblen; - else - cv->mblen = mbcs_mblen; - } - else - { - /* not supported */ - free(name); - errno = EINVAL; - return FALSE; - } - - if (use_compat) - { - switch (cv->codepage) - { - case 932: cv->compat = cp932_compat; break; - case 20932: cv->compat = cp20932_compat; break; - case 51932: cv->compat = cp51932_compat; break; - case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break; - } - } - - free(name); - - return TRUE; -} - -static int -name_to_codepage(const char *name) -{ - int i; - - if (*name == '\0' || - strcmp(name, "char") == 0) - return GetACP(); - else if (strcmp(name, "wchar_t") == 0) - return 1200; - else if (_strnicmp(name, "cp", 2) == 0) - return atoi(name + 2); /* CP123 */ - else if ('0' <= name[0] && name[0] <= '9') - return atoi(name); /* 123 */ - else if (_strnicmp(name, "xx", 2) == 0) - return atoi(name + 2); /* XX123 for debug */ - - for (i = 0; codepage_alias[i].name != NULL; ++i) - if (_stricmp(name, codepage_alias[i].name) == 0) - return codepage_alias[i].codepage; - return -1; -} - -/* - * http://www.faqs.org/rfcs/rfc2781.html - */ -static uint -utf16_to_ucs4(const ushort *wbuf) -{ - uint wc = wbuf[0]; - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; - return wc; -} - -static void -ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) -{ - if (wc < 0x10000) - { - wbuf[0] = wc; - *wbufsize = 1; - } - else - { - wc -= 0x10000; - wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); - wbuf[1] = 0xDC00 | (wc & 0x3FF); - *wbufsize = 2; - } -} - -/* - * Check if codepage is one of those for which the dwFlags parameter - * to MultiByteToWideChar() must be zero. Return zero or - * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows - * Server 2003 R2 claims that also codepage 65001 is one of these, but - * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave - * out 65001 (UTF-8), and that indeed seems to be the case on XP, it - * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting - * from UTF-8. - */ -static int -mbtowc_flags(int codepage) -{ - return (codepage == 50220 || codepage == 50221 || - codepage == 50222 || codepage == 50225 || - codepage == 50227 || codepage == 50229 || - codepage == 52936 || codepage == 54936 || - (codepage >= 57002 && codepage <= 57011) || - codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; -} - -/* - * Check if codepage is one those for which the lpUsedDefaultChar - * parameter to WideCharToMultiByte() must be NULL. The docs in - * Platform SDK for for Windows Server 2003 R2 claims that this is the - * list below, while the MSDN docs for MSVS2008 claim that it is only - * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform - * SDK seems to be correct, at least for XP. - */ -static int -must_use_null_useddefaultchar(int codepage) -{ - return (codepage == 65000 || codepage == 65001 || - codepage == 50220 || codepage == 50221 || - codepage == 50222 || codepage == 50225 || - codepage == 50227 || codepage == 50229 || - codepage == 52936 || codepage == 54936 || - (codepage >= 57002 && codepage <= 57011) || - codepage == 42); -} - -static char * -strrstr(const char *str, const char *token) -{ - int len = strlen(token); - const char *p = str + strlen(str); - - while (str <= --p) - if (p[0] == token[0] && strncmp(p, token, len) == 0) - return (char *)p; - return NULL; -} - -static char * -xstrndup(const char *s, size_t n) -{ - char *p; - - p = (char *)malloc(n + 1); - if (p == NULL) - return NULL; - memcpy(p, s, n); - p[n] = '\0'; - return p; -} - -static int -seterror(int err) -{ - errno = err; - return -1; -} - -#if defined(USE_LIBICONV_DLL) -static int -libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) -{ - HMODULE hlibiconv = NULL; - HMODULE hmsvcrt = NULL; - char *dllname; - const char *p; - const char *e; - f_iconv_open _iconv_open; - - /* - * always try to load dll, so that we can switch dll in runtime. - */ - - /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */ - p = getenv("WINICONV_LIBICONV_DLL"); - if (p == NULL) - p = DEFAULT_LIBICONV_DLL; - /* parse comma separated value */ - for ( ; *p != 0; p = (*e == ',') ? e + 1 : e) - { - e = strchr(p, ','); - if (p == e) - continue; - else if (e == NULL) - e = p + strlen(p); - dllname = xstrndup(p, e - p); - if (dllname == NULL) - return FALSE; - hlibiconv = LoadLibrary(dllname); - free(dllname); - if (hlibiconv != NULL) - { - if (hlibiconv == hwiniconv) - { - FreeLibrary(hlibiconv); - hlibiconv = NULL; - continue; - } - break; - } - } - - if (hlibiconv == NULL) - goto failed; - - hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno"); - if (hmsvcrt == NULL) - goto failed; - - _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open"); - if (_iconv_open == NULL) - _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open"); - cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close"); - if (cd->iconv_close == NULL) - cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close"); - cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv"); - if (cd->iconv == NULL) - cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv"); - cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno"); - if (_iconv_open == NULL || cd->iconv_close == NULL - || cd->iconv == NULL || cd->_errno == NULL) - goto failed; - - cd->cd = _iconv_open(tocode, fromcode); - if (cd->cd == (iconv_t)(-1)) - goto failed; - - cd->hlibiconv = hlibiconv; - return TRUE; - -failed: - if (hlibiconv != NULL) - FreeLibrary(hlibiconv); - /* do not free hmsvcrt which is obtained by GetModuleHandle() */ - return FALSE; -} - -/* - * Reference: - * http://forums.belution.com/ja/vc/000/234/78s.shtml - * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html - * - * The formal way is - * imagehlp.h or dbghelp.h - * imagehlp.lib or dbghelp.lib - * ImageDirectoryEntryToData() - */ -#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base)) -#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew)) -static PVOID -MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size) -{ - /* TODO: MappedAsImage? */ - PIMAGE_DATA_DIRECTORY p; - p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry; - if (p->VirtualAddress == 0) { - *Size = 0; - return NULL; - } - *Size = p->Size; - return (PVOID)((LPBYTE)Base + p->VirtualAddress); -} - -static HMODULE -find_imported_module_by_funcname(HMODULE hModule, const char *funcname) -{ - DWORD Base; - ULONG Size; - PIMAGE_IMPORT_DESCRIPTOR Imp; - PIMAGE_THUNK_DATA Name; /* Import Name Table */ - PIMAGE_IMPORT_BY_NAME ImpName; - - Base = (DWORD)hModule; - Imp = MyImageDirectoryEntryToData( - (LPVOID)Base, - TRUE, - IMAGE_DIRECTORY_ENTRY_IMPORT, - &Size); - if (Imp == NULL) - return NULL; - for ( ; Imp->OriginalFirstThunk != 0; ++Imp) - { - Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk); - for ( ; Name->u1.Ordinal != 0; ++Name) - { - if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal)) - { - ImpName = (PIMAGE_IMPORT_BY_NAME) - (Base + (DWORD)Name->u1.AddressOfData); - if (strcmp((char *)ImpName->Name, funcname) == 0) - return GetModuleHandle((char *)(Base + Imp->Name)); - } - } - } - return NULL; -} -#endif - -static int -sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - return 1; -} - -static int -dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; - if (bufsize < len) - return seterror(EINVAL); - return len; -} - -static int -mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = 0; - - if (cv->codepage == 54936) { - if (buf[0] <= 0x7F) len = 1; - else if (buf[0] >= 0x81 && buf[0] <= 0xFE && - bufsize >= 2 && - ((buf[1] >= 0x40 && buf[1] <= 0x7E) || - (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2; - else if (buf[0] >= 0x81 && buf[0] <= 0xFE && - bufsize >= 4 && - buf[1] >= 0x30 && buf[1] <= 0x39) len = 4; - else - return seterror(EINVAL); - return len; - } - else - return seterror(EINVAL); -} - -static int -utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - int len = 0; - - if (buf[0] < 0x80) len = 1; - else if ((buf[0] & 0xE0) == 0xC0) len = 2; - else if ((buf[0] & 0xF0) == 0xE0) len = 3; - else if ((buf[0] & 0xF8) == 0xF0) len = 4; - else if ((buf[0] & 0xFC) == 0xF8) len = 5; - else if ((buf[0] & 0xFE) == 0xFC) len = 6; - - if (len == 0) - return seterror(EILSEQ); - else if (bufsize < len) - return seterror(EINVAL); - return len; -} - -static int -eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize) -{ - if (buf[0] < 0x80) /* ASCII */ - return 1; - else if (buf[0] == 0x8E) /* JIS X 0201 */ - { - if (bufsize < 2) - return seterror(EINVAL); - else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) - return seterror(EILSEQ); - return 2; - } - else if (buf[0] == 0x8F) /* JIS X 0212 */ - { - if (bufsize < 3) - return seterror(EINVAL); - else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) - || !(0xA1 <= buf[2] && buf[2] <= 0xFE)) - return seterror(EILSEQ); - return 3; - } - else /* JIS X 0208 */ - { - if (bufsize < 2) - return seterror(EINVAL); - else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) - || !(0xA1 <= buf[1] && buf[1] <= 0xFE)) - return seterror(EILSEQ); - return 2; - } -} - -static int -kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int len; - - len = cv->mblen(cv, buf, bufsize); - if (len == -1) - return -1; - *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), - (const char *)buf, len, (wchar_t *)wbuf, *wbufsize); - if (*wbufsize == 0) - return seterror(EILSEQ); - return len; -} - -static int -kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - BOOL usedDefaultChar = 0; - BOOL *p = NULL; - int flags = 0; - int len; - - if (bufsize == 0) - return seterror(E2BIG); - if (!must_use_null_useddefaultchar(cv->codepage)) - { - p = &usedDefaultChar; -#ifdef WC_NO_BEST_FIT_CHARS - if (!(cv->flags & FLAG_TRANSLIT)) - flags |= WC_NO_BEST_FIT_CHARS; -#endif - } - len = WideCharToMultiByte(cv->codepage, flags, - (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); - if (len == 0) - { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) - return seterror(E2BIG); - return seterror(EILSEQ); - } - else if (usedDefaultChar) - return seterror(EILSEQ); - else if (cv->mblen(cv, buf, len) != len) /* validate result */ - return seterror(EILSEQ); - return len; -} - -/* - * It seems that the mode (cv->mode) is fixnum. - * For example, when converting iso-2022-jp(cp50221) to unicode: - * in ascii sequence: mode=0xC42C0000 - * in jisx0208 sequence: mode=0xC42C0001 - * "C42C" is same for each convert session. - * It should be: ((codepage-1)<<16)|state - */ -static int -mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int len; - int insize; - HRESULT hr; - - len = cv->mblen(cv, buf, bufsize); - if (len == -1) - return -1; - insize = len; - hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, - (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); - if (hr != S_OK || insize != len) - return seterror(EILSEQ); - return len; -} - -static int -mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ - int tmpsize = MB_CHAR_MAX; - int insize = wbufsize; - HRESULT hr; - - hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, - (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); - if (hr != S_OK || insize != wbufsize) - return seterror(EILSEQ); - else if (bufsize < tmpsize) - return seterror(E2BIG); - else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) - return seterror(EILSEQ); - memcpy(buf, tmpbuf, tmpsize); - return tmpsize; -} - -static int -utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int codepage = cv->codepage; - - /* swap endian: 1200 <-> 1201 */ - if (cv->mode & UNICODE_MODE_SWAPPED) - codepage ^= 1; - - if (bufsize < 2) - return seterror(EINVAL); - if (codepage == 1200) /* little endian */ - wbuf[0] = (buf[1] << 8) | buf[0]; - else if (codepage == 1201) /* big endian */ - wbuf[0] = (buf[0] << 8) | buf[1]; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - cv->mode |= UNICODE_MODE_BOM_DONE; - if (wbuf[0] == 0xFFFE) - { - cv->mode |= UNICODE_MODE_SWAPPED; - *wbufsize = 0; - return 2; - } - else if (wbuf[0] == 0xFEFF) - { - *wbufsize = 0; - return 2; - } - } - - if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) - return seterror(EILSEQ); - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - { - if (bufsize < 4) - return seterror(EINVAL); - if (codepage == 1200) /* little endian */ - wbuf[1] = (buf[3] << 8) | buf[2]; - else if (codepage == 1201) /* big endian */ - wbuf[1] = (buf[2] << 8) | buf[3]; - if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) - return seterror(EILSEQ); - *wbufsize = 2; - return 4; - } - *wbufsize = 1; - return 2; -} - -static int -utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - int r; - - cv->mode |= UNICODE_MODE_BOM_DONE; - if (bufsize < 2) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - memcpy(buf, "\xFF\xFE", 2); - else if (cv->codepage == 1201) /* big endian */ - memcpy(buf, "\xFE\xFF", 2); - - r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); - if (r == -1) - return -1; - return r + 2; - } - - if (bufsize < 2) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - { - buf[0] = (wbuf[0] & 0x00FF); - buf[1] = (wbuf[0] & 0xFF00) >> 8; - } - else if (cv->codepage == 1201) /* big endian */ - { - buf[0] = (wbuf[0] & 0xFF00) >> 8; - buf[1] = (wbuf[0] & 0x00FF); - } - if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) - { - if (bufsize < 4) - return seterror(E2BIG); - if (cv->codepage == 1200) /* little endian */ - { - buf[2] = (wbuf[1] & 0x00FF); - buf[3] = (wbuf[1] & 0xFF00) >> 8; - } - else if (cv->codepage == 1201) /* big endian */ - { - buf[2] = (wbuf[1] & 0xFF00) >> 8; - buf[3] = (wbuf[1] & 0x00FF); - } - return 4; - } - return 2; -} - -static int -utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - int codepage = cv->codepage; - uint wc; - - /* swap endian: 12000 <-> 12001 */ - if (cv->mode & UNICODE_MODE_SWAPPED) - codepage ^= 1; - - if (bufsize < 4) - return seterror(EINVAL); - if (codepage == 12000) /* little endian */ - wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; - else if (codepage == 12001) /* big endian */ - wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - cv->mode |= UNICODE_MODE_BOM_DONE; - if (wc == 0xFFFE0000) - { - cv->mode |= UNICODE_MODE_SWAPPED; - *wbufsize = 0; - return 4; - } - else if (wc == 0x0000FEFF) - { - *wbufsize = 0; - return 4; - } - } - - if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) - return seterror(EILSEQ); - ucs4_to_utf16(wc, wbuf, wbufsize); - return 4; -} - -static int -utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - uint wc; - - if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) - { - int r; - - cv->mode |= UNICODE_MODE_BOM_DONE; - if (bufsize < 4) - return seterror(E2BIG); - if (cv->codepage == 12000) /* little endian */ - memcpy(buf, "\xFF\xFE\x00\x00", 4); - else if (cv->codepage == 12001) /* big endian */ - memcpy(buf, "\x00\x00\xFE\xFF", 4); - - r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); - if (r == -1) - return -1; - return r + 4; - } - - if (bufsize < 4) - return seterror(E2BIG); - wc = utf16_to_ucs4(wbuf); - if (cv->codepage == 12000) /* little endian */ - { - buf[0] = wc & 0x000000FF; - buf[1] = (wc & 0x0000FF00) >> 8; - buf[2] = (wc & 0x00FF0000) >> 16; - buf[3] = (wc & 0xFF000000) >> 24; - } - else if (cv->codepage == 12001) /* big endian */ - { - buf[0] = (wc & 0xFF000000) >> 24; - buf[1] = (wc & 0x00FF0000) >> 16; - buf[2] = (wc & 0x0000FF00) >> 8; - buf[3] = wc & 0x000000FF; - } - return 4; -} - -/* - * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) - * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow - * 1 byte Kana) - * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte - * Kana - SO/SI) - * - * MultiByteToWideChar() and WideCharToMultiByte() behave differently - * depending on Windows version. On XP, WideCharToMultiByte() doesn't - * terminate result sequence with ascii escape. But Vista does. - * Use MLang instead. - */ - -#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) -#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) -#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) - -#define ISO2022_SI 0 -#define ISO2022_SO 1 - -/* shift in */ -static const char iso2022_SI_seq[] = "\x0F"; -/* shift out */ -static const char iso2022_SO_seq[] = "\x0E"; - -typedef struct iso2022_esc_t iso2022_esc_t; -struct iso2022_esc_t { - const char *esc; - int esc_len; - int len; - int cs; -}; - -#define ISO2022JP_CS_ASCII 0 -#define ISO2022JP_CS_JISX0201_ROMAN 1 -#define ISO2022JP_CS_JISX0201_KANA 2 -#define ISO2022JP_CS_JISX0208_1978 3 -#define ISO2022JP_CS_JISX0208_1983 4 -#define ISO2022JP_CS_JISX0212 5 - -static iso2022_esc_t iso2022jp_esc[] = { - {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, - {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, - {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, - {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ - {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, - {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, - {NULL, 0, 0, 0} -}; - -static int -iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - char tmp[MB_CHAR_MAX]; - int insize; - HRESULT hr; - DWORD dummy = 0; - int len; - int esc_len; - int cs; - int shift; - int i; - - if (buf[0] == 0x1B) - { - for (i = 0; iesc[i].esc != NULL; ++i) - { - esc_len = iesc[i].esc_len; - if (bufsize < esc_len) - { - if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) - return seterror(EINVAL); - } - else - { - if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) - { - cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); - *wbufsize = 0; - return esc_len; - } - } - } - /* not supported escape sequence */ - return seterror(EILSEQ); - } - else if (buf[0] == iso2022_SO_seq[0]) - { - cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); - *wbufsize = 0; - return 1; - } - else if (buf[0] == iso2022_SI_seq[0]) - { - cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); - *wbufsize = 0; - return 1; - } - - cs = ISO2022_MODE_CS(cv->mode); - shift = ISO2022_MODE_SHIFT(cv->mode); - - /* reset the mode for informal sequence */ - if (buf[0] < 0x20) - { - cs = ISO2022JP_CS_ASCII; - shift = ISO2022_SI; - } - - len = iesc[cs].len; - if (bufsize < len) - return seterror(EINVAL); - for (i = 0; i < len; ++i) - if (!(buf[i] < 0x80)) - return seterror(EILSEQ); - esc_len = iesc[cs].esc_len; - memcpy(tmp, iesc[cs].esc, esc_len); - if (shift == ISO2022_SO) - { - memcpy(tmp + esc_len, iso2022_SO_seq, 1); - esc_len += 1; - } - memcpy(tmp + esc_len, buf, len); - - if ((cv->codepage == 50220 || cv->codepage == 50221 - || cv->codepage == 50222) && shift == ISO2022_SO) - { - /* XXX: shift-out cannot be used for mbtowc (both kernel and - * mlang) */ - esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; - memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); - memcpy(tmp + esc_len, buf, len); - } - - insize = len + esc_len; - hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, - (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); - if (hr != S_OK || insize != len + esc_len) - return seterror(EILSEQ); - - /* Check for conversion error. Assuming defaultChar is 0x3F. */ - /* ascii should be converted from ascii */ - if (wbuf[0] == buf[0] - && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) - return seterror(EILSEQ); - - /* reset the mode for informal sequence */ - if (cv->mode != ISO2022_MODE(cs, shift)) - cv->mode = ISO2022_MODE(cs, shift); - - return len; -} - -static int -iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - char tmp[MB_CHAR_MAX]; - int tmpsize = MB_CHAR_MAX; - int insize = wbufsize; - HRESULT hr; - DWORD dummy = 0; - int len; - int esc_len; - int cs; - int shift; - int i; - - /* - * MultiByte = [escape sequence] + character + [escape sequence] - * - * Whether trailing escape sequence is added depends on which API is - * used (kernel or MLang, and its version). - */ - hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, - (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); - if (hr != S_OK || insize != wbufsize) - return seterror(EILSEQ); - else if (bufsize < tmpsize) - return seterror(E2BIG); - - if (tmpsize == 1) - { - cs = ISO2022JP_CS_ASCII; - esc_len = 0; - } - else - { - for (i = 1; iesc[i].esc != NULL; ++i) - { - esc_len = iesc[i].esc_len; - if (strncmp(tmp, iesc[i].esc, esc_len) == 0) - { - cs = iesc[i].cs; - break; - } - } - if (iesc[i].esc == NULL) - /* not supported escape sequence */ - return seterror(EILSEQ); - } - - shift = ISO2022_SI; - if (tmp[esc_len] == iso2022_SO_seq[0]) - { - shift = ISO2022_SO; - esc_len += 1; - } - - len = iesc[cs].len; - - /* Check for converting error. Assuming defaultChar is 0x3F. */ - /* ascii should be converted from ascii */ - if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) - return seterror(EILSEQ); - else if (tmpsize < esc_len + len) - return seterror(EILSEQ); - - if (cv->mode == ISO2022_MODE(cs, shift)) - { - /* remove escape sequence */ - if (esc_len != 0) - memmove(tmp, tmp + esc_len, len); - esc_len = 0; - } - else - { - if (cs == ISO2022JP_CS_ASCII) - { - esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; - memmove(tmp + esc_len, tmp, len); - memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); - } - if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) - { - /* shift-in before changing to other mode */ - memmove(tmp + 1, tmp, len + esc_len); - memcpy(tmp, iso2022_SI_seq, 1); - esc_len += 1; - } - } - - if (bufsize < len + esc_len) - return seterror(E2BIG); - memcpy(buf, tmp, len + esc_len); - cv->mode = ISO2022_MODE(cs, shift); - return len + esc_len; -} - -static int -iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) -{ - iso2022_esc_t *iesc = iso2022jp_esc; - int esc_len; - - if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) - { - esc_len = 0; - if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) - esc_len += 1; - if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) - esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; - if (bufsize < esc_len) - return seterror(E2BIG); - - esc_len = 0; - if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) - { - memcpy(buf, iso2022_SI_seq, 1); - esc_len += 1; - } - if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) - { - memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, - iesc[ISO2022JP_CS_ASCII].esc_len); - esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; - } - return esc_len; - } - return 0; -} - -#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) -BOOL WINAPI -DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) -{ - switch( fdwReason ) - { - case DLL_PROCESS_ATTACH: - hwiniconv = (HMODULE)hinstDLL; - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} -#endif - -#if defined(MAKE_EXE) -#include -#include -#include -int -main(int argc, char **argv) -{ - char *fromcode = NULL; - char *tocode = NULL; - int i; - char inbuf[BUFSIZ]; - char outbuf[BUFSIZ]; - const char *pin; - char *pout; - size_t inbytesleft; - size_t outbytesleft; - size_t rest = 0; - iconv_t cd; - size_t r; - FILE *in = stdin; - - _setmode(_fileno(stdin), _O_BINARY); - _setmode(_fileno(stdout), _O_BINARY); - - for (i = 1; i < argc; ++i) - { - if (strcmp(argv[i], "-l") == 0) - { - for (i = 0; codepage_alias[i].name != NULL; ++i) - printf("%s\n", codepage_alias[i].name); - return 0; - } - - if (strcmp(argv[i], "-f") == 0) - fromcode = argv[++i]; - else if (strcmp(argv[i], "-t") == 0) - tocode = argv[++i]; - else - { - in = fopen(argv[i], "rb"); - if (in == NULL) - { - fprintf(stderr, "cannot open %s\n", argv[i]); - return 1; - } - break; - } - } - - if (fromcode == NULL || tocode == NULL) - { - printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]); - return 0; - } - - cd = iconv_open(tocode, fromcode); - if (cd == (iconv_t)(-1)) - { - perror("iconv_open error"); - return 1; - } - - while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0 - || rest != 0) - { - inbytesleft += rest; - pin = inbuf; - pout = outbuf; - outbytesleft = sizeof(outbuf); - r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); - fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); - if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in))) - { - perror("conversion error"); - return 1; - } - memmove(inbuf, pin, inbytesleft); - rest = inbytesleft; - } - pout = outbuf; - outbytesleft = sizeof(outbuf); - r = iconv(cd, NULL, NULL, &pout, &outbytesleft); - fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); - if (r == (size_t)(-1)) - { - perror("conversion error"); - return 1; - } - - iconv_close(cd); - - return 0; -} -#endif - +/* + * iconv library using Win32 API to conversion. + * + * This file is placed in the public domain. + * + * Last Change: 2009-07-06 + * + * ENVIRONMENT VARIABLE: + * WINICONV_LIBICONV_DLL + * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If + * loading the DLL or iconv_open() failed, falls back to internal + * conversion. If a few DLL are specified as comma separated list, + * the first loadable DLL is used. The DLL should have iconv_open(), + * iconv_close() and iconv(). Or libiconv_open(), libiconv_close() + * and libiconv(). + * (only available when USE_LIBICONV_DLL is defined at compile time) + * + * Win32 API does not support strict encoding conversion for some + * codepage. And MLang function drop or replace invalid bytes and does + * not return useful error status as iconv. This implementation cannot + * be used for encoding validation purpose. + */ + +/* for WC_NO_BEST_FIT_CHARS */ +#ifndef WINVER +# define WINVER 0x0500 +#endif + +#include +#include +#include +#include + +#if 0 +# define MAKE_EXE +# define MAKE_DLL +# define USE_LIBICONV_DLL +#endif + +#if !defined(DEFAULT_LIBICONV_DLL) +# define DEFAULT_LIBICONV_DLL "" +#endif + +#define MB_CHAR_MAX 16 + +#define UNICODE_MODE_BOM_DONE 1 +#define UNICODE_MODE_SWAPPED 2 + +#define FLAG_USE_BOM 1 +#define FLAG_TRANSLIT 2 /* //TRANSLIT */ +#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */ + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; + +typedef void* iconv_t; + +iconv_t iconv_open(const char *tocode, const char *fromcode); +int iconv_close(iconv_t cd); +size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); + +/* libiconv interface for vim */ +#if defined(MAKE_DLL) +int +iconvctl (iconv_t cd, int request, void* argument) +{ + /* not supported */ + return 0; +} +#endif + +typedef struct compat_t compat_t; +typedef struct csconv_t csconv_t; +typedef struct rec_iconv_t rec_iconv_t; + +typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode); +typedef int (*f_iconv_close)(iconv_t cd); +typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); +typedef int* (*f_errno)(void); +typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); +typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); + +#define COMPAT_IN 1 +#define COMPAT_OUT 2 + +/* unicode mapping for compatibility with other conversion table. */ +struct compat_t { + uint in; + uint out; + uint flag; +}; + +struct csconv_t { + int codepage; + int flags; + f_mbtowc mbtowc; + f_wctomb wctomb; + f_mblen mblen; + f_flush flush; + DWORD mode; + compat_t *compat; +}; + +struct rec_iconv_t { + iconv_t cd; + f_iconv_close iconv_close; + f_iconv iconv; + f_errno _errno; + csconv_t from; + csconv_t to; +#if defined(USE_LIBICONV_DLL) + HMODULE hlibiconv; +#endif +}; + +static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); +static int win_iconv_close(iconv_t cd); +static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); + +static int load_mlang(); +static int make_csconv(const char *name, csconv_t *cv); +static int name_to_codepage(const char *name); +static uint utf16_to_ucs4(const ushort *wbuf); +static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); +static int mbtowc_flags(int codepage); +static int must_use_null_useddefaultchar(int codepage); +static char *strrstr(const char *str, const char *token); +static char *xstrndup(const char *s, size_t n); +static int seterror(int err); + +#if defined(USE_LIBICONV_DLL) +static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode); +static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size); +static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname); + +static HMODULE hwiniconv; +#endif + +static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); +static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); + +static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); +static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); +static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); + +static struct { + int codepage; + const char *name; +} codepage_alias[] = { + {65001, "CP65001"}, + {65001, "UTF8"}, + {65001, "UTF-8"}, + + {1200, "CP1200"}, + {1200, "UTF16LE"}, + {1200, "UTF-16LE"}, + {1200, "UCS-2LE"}, + + {1201, "CP1201"}, + {1201, "UTF16BE"}, + {1201, "UTF-16BE"}, + {1201, "UCS-2BE"}, + {1201, "unicodeFFFE"}, + + {12000, "CP12000"}, + {12000, "UTF32LE"}, + {12000, "UTF-32LE"}, + + {12001, "CP12001"}, + {12001, "UTF32BE"}, + {12001, "UTF-32BE"}, + +#ifndef GLIB_COMPILATION + /* + * Default is big endian. + * See rfc2781 4.3 Interpreting text labelled as UTF-16. + */ + {1201, "UTF16"}, + {1201, "UTF-16"}, + {12001, "UTF32"}, + {12001, "UTF-32"}, +#else + /* Default is little endian, because the platform is */ + {1200, "UTF16"}, + {1200, "UTF-16"}, + {1200, "UCS-2"}, + {12000, "UTF32"}, + {12000, "UTF-32"}, +#endif + + /* copy from libiconv `iconv -l` */ + /* !IsValidCodePage(367) */ + {20127, "ANSI_X3.4-1968"}, + {20127, "ANSI_X3.4-1986"}, + {20127, "ASCII"}, + {20127, "CP367"}, + {20127, "IBM367"}, + {20127, "ISO-IR-6"}, + {20127, "ISO646-US"}, + {20127, "ISO_646.IRV:1991"}, + {20127, "US"}, + {20127, "US-ASCII"}, + {20127, "CSASCII"}, + + /* !IsValidCodePage(819) */ + {1252, "CP819"}, + {1252, "IBM819"}, + {28591, "ISO-8859-1"}, + {28591, "ISO-IR-100"}, + {28591, "ISO8859-1"}, + {28591, "ISO_8859-1"}, + {28591, "ISO_8859-1:1987"}, + {28591, "L1"}, + {28591, "LATIN1"}, + {28591, "CSISOLATIN1"}, + + {1250, "CP1250"}, + {1250, "MS-EE"}, + {1250, "WINDOWS-1250"}, + + {1251, "CP1251"}, + {1251, "MS-CYRL"}, + {1251, "WINDOWS-1251"}, + + {1252, "CP1252"}, + {1252, "MS-ANSI"}, + {1252, "WINDOWS-1252"}, + + {1253, "CP1253"}, + {1253, "MS-GREEK"}, + {1253, "WINDOWS-1253"}, + + {1254, "CP1254"}, + {1254, "MS-TURK"}, + {1254, "WINDOWS-1254"}, + + {1255, "CP1255"}, + {1255, "MS-HEBR"}, + {1255, "WINDOWS-1255"}, + + {1256, "CP1256"}, + {1256, "MS-ARAB"}, + {1256, "WINDOWS-1256"}, + + {1257, "CP1257"}, + {1257, "WINBALTRIM"}, + {1257, "WINDOWS-1257"}, + + {1258, "CP1258"}, + {1258, "WINDOWS-1258"}, + + {850, "850"}, + {850, "CP850"}, + {850, "IBM850"}, + {850, "CSPC850MULTILINGUAL"}, + + /* !IsValidCodePage(862) */ + {862, "862"}, + {862, "CP862"}, + {862, "IBM862"}, + {862, "CSPC862LATINHEBREW"}, + + {866, "866"}, + {866, "CP866"}, + {866, "IBM866"}, + {866, "CSIBM866"}, + + /* !IsValidCodePage(154) */ + {154, "CP154"}, + {154, "CYRILLIC-ASIAN"}, + {154, "PT154"}, + {154, "PTCP154"}, + {154, "CSPTCP154"}, + + /* !IsValidCodePage(1133) */ + {1133, "CP1133"}, + {1133, "IBM-CP1133"}, + + {874, "CP874"}, + {874, "WINDOWS-874"}, + + /* !IsValidCodePage(51932) */ + {51932, "CP51932"}, + {51932, "MS51932"}, + {51932, "WINDOWS-51932"}, + {51932, "EUC-JP"}, + + {932, "CP932"}, + {932, "MS932"}, + {932, "SHIFFT_JIS"}, + {932, "SHIFFT_JIS-MS"}, + {932, "SJIS"}, + {932, "SJIS-MS"}, + {932, "SJIS-OPEN"}, + {932, "SJIS-WIN"}, + {932, "WINDOWS-31J"}, + {932, "WINDOWS-932"}, + {932, "CSWINDOWS31J"}, + + {50221, "CP50221"}, + {50221, "ISO-2022-JP"}, + {50221, "ISO-2022-JP-MS"}, + {50221, "ISO2022-JP"}, + {50221, "ISO2022-JP-MS"}, + {50221, "MS50221"}, + {50221, "WINDOWS-50221"}, + + {936, "CP936"}, + {936, "GBK"}, + {936, "MS936"}, + {936, "WINDOWS-936"}, + + {950, "CP950"}, + {950, "BIG5"}, + + {949, "CP949"}, + {949, "UHC"}, + {949, "EUC-KR"}, + + {1361, "CP1361"}, + {1361, "JOHAB"}, + + {437, "437"}, + {437, "CP437"}, + {437, "IBM437"}, + {437, "CSPC8CODEPAGE437"}, + + {737, "CP737"}, + + {775, "CP775"}, + {775, "IBM775"}, + {775, "CSPC775BALTIC"}, + + {852, "852"}, + {852, "CP852"}, + {852, "IBM852"}, + {852, "CSPCP852"}, + + /* !IsValidCodePage(853) */ + {853, "CP853"}, + + {855, "855"}, + {855, "CP855"}, + {855, "IBM855"}, + {855, "CSIBM855"}, + + {857, "857"}, + {857, "CP857"}, + {857, "IBM857"}, + {857, "CSIBM857"}, + + /* !IsValidCodePage(858) */ + {858, "CP858"}, + + {860, "860"}, + {860, "CP860"}, + {860, "IBM860"}, + {860, "CSIBM860"}, + + {861, "861"}, + {861, "CP-IS"}, + {861, "CP861"}, + {861, "IBM861"}, + {861, "CSIBM861"}, + + {863, "863"}, + {863, "CP863"}, + {863, "IBM863"}, + {863, "CSIBM863"}, + + {864, "CP864"}, + {864, "IBM864"}, + {864, "CSIBM864"}, + + {865, "865"}, + {865, "CP865"}, + {865, "IBM865"}, + {865, "CSIBM865"}, + + {869, "869"}, + {869, "CP-GR"}, + {869, "CP869"}, + {869, "IBM869"}, + {869, "CSIBM869"}, + + /* !IsValidCodePage(1152) */ + {1125, "CP1125"}, + + /* + * Code Page Identifiers + * http://msdn2.microsoft.com/en-us/library/ms776446.aspx + */ + {37, "IBM037"}, /* IBM EBCDIC US-Canada */ + {437, "IBM437"}, /* OEM United States */ + {500, "IBM500"}, /* IBM EBCDIC International */ + {708, "ASMO-708"}, /* Arabic (ASMO 708) */ + /* 709 Arabic (ASMO-449+, BCON V4) */ + /* 710 Arabic - Transparent Arabic */ + {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */ + {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */ + {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */ + {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */ + {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */ + {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */ + {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */ + {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */ + {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */ + {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */ + {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */ + {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */ + {864, "IBM864"}, /* OEM Arabic; Arabic (864) */ + {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */ + {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */ + {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */ + {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ + {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ + {875, "cp875"}, /* IBM EBCDIC Greek Modern */ + {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ + {932, "shift-jis"}, /* alternative name for it */ + {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ + {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */ + {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ + {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */ + {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */ + {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ + {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ + {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ + {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ + {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ + {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ + {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ + {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ + {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ + {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ + {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */ + {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */ + {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */ + {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */ + {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */ + {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */ + {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */ + {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */ + {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ + {1361, "Johab"}, /* Korean (Johab) */ + {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */ + {10001, "x-mac-japanese"}, /* Japanese (Mac) */ + {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ + {10003, "x-mac-korean"}, /* Korean (Mac) */ + {10004, "x-mac-arabic"}, /* Arabic (Mac) */ + {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */ + {10006, "x-mac-greek"}, /* Greek (Mac) */ + {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */ + {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ + {10010, "x-mac-romanian"}, /* Romanian (Mac) */ + {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */ + {10021, "x-mac-thai"}, /* Thai (Mac) */ + {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */ + {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */ + {10081, "x-mac-turkish"}, /* Turkish (Mac) */ + {10082, "x-mac-croatian"}, /* Croatian (Mac) */ + {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */ + {20001, "x-cp20001"}, /* TCA Taiwan */ + {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */ + {20003, "x-cp20003"}, /* IBM5550 Taiwan */ + {20004, "x-cp20004"}, /* TeleText Taiwan */ + {20005, "x-cp20005"}, /* Wang Taiwan */ + {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ + {20106, "x-IA5-German"}, /* IA5 German (7-bit) */ + {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */ + {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */ + {20127, "us-ascii"}, /* US-ASCII (7-bit) */ + {20261, "x-cp20261"}, /* T.61 */ + {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */ + {20273, "IBM273"}, /* IBM EBCDIC Germany */ + {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */ + {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */ + {20280, "IBM280"}, /* IBM EBCDIC Italy */ + {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */ + {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */ + {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */ + {20297, "IBM297"}, /* IBM EBCDIC France */ + {20420, "IBM420"}, /* IBM EBCDIC Arabic */ + {20423, "IBM423"}, /* IBM EBCDIC Greek */ + {20424, "IBM424"}, /* IBM EBCDIC Hebrew */ + {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */ + {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */ + {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */ + {20871, "IBM871"}, /* IBM EBCDIC Icelandic */ + {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */ + {20905, "IBM905"}, /* IBM EBCDIC Turkish */ + {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ + {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */ + {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ + {20949, "x-cp20949"}, /* Korean Wansung */ + {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ + /* 21027 (deprecated) */ + {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ + {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ + {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */ + {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ + {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */ + {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */ + {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */ + {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */ + {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */ + {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */ + {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */ + {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */ + {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */ + {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */ + {28597, "iso8859-7"}, /* ISO 8859-7 Greek */ + {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ + {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ + {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */ + {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */ + {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */ + {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */ + {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */ + {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */ + {29001, "x-Europa"}, /* Europa 3 */ + {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ + {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ + {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ + {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ + {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ + {50225, "iso-2022-kr"}, /* ISO 2022 Korean */ + {50225, "iso2022-kr"}, /* ISO 2022 Korean */ + {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ + /* 50229 ISO 2022 Traditional Chinese */ + /* 50930 EBCDIC Japanese (Katakana) Extended */ + /* 50931 EBCDIC US-Canada and Japanese */ + /* 50933 EBCDIC Korean Extended and Korean */ + /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ + /* 50936 EBCDIC Simplified Chinese */ + /* 50937 EBCDIC US-Canada and Traditional Chinese */ + /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ + {51932, "euc-jp"}, /* EUC Japanese */ + {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */ + {51949, "euc-kr"}, /* EUC Korean */ + /* 51950 EUC Traditional Chinese */ + {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ + {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ + {57002, "x-iscii-de"}, /* ISCII Devanagari */ + {57003, "x-iscii-be"}, /* ISCII Bengali */ + {57004, "x-iscii-ta"}, /* ISCII Tamil */ + {57005, "x-iscii-te"}, /* ISCII Telugu */ + {57006, "x-iscii-as"}, /* ISCII Assamese */ + {57007, "x-iscii-or"}, /* ISCII Oriya */ + {57008, "x-iscii-ka"}, /* ISCII Kannada */ + {57009, "x-iscii-ma"}, /* ISCII Malayalam */ + {57010, "x-iscii-gu"}, /* ISCII Gujarati */ + {57011, "x-iscii-pa"}, /* ISCII Punjabi */ + + {0, NULL} +}; + +/* + * SJIS SHIFTJIS table CP932 table + * ---- --------------------------- -------------------------------- + * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS + * 7E U+203E OVERLINE U+007E TILDE + * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR + * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS + * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE + * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO + * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS + * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN + * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN + * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN + * + * EUC-JP and ISO-2022-JP should be compatible with CP932. + * + * Kernel and MLang have different Unicode mapping table. Make sure + * which API is used. + */ +static compat_t cp932_compat[] = { + {0x00A5, 0x005C, COMPAT_OUT}, + {0x203E, 0x007E, COMPAT_OUT}, + {0x2014, 0x2015, COMPAT_OUT}, + {0x301C, 0xFF5E, COMPAT_OUT}, + {0x2016, 0x2225, COMPAT_OUT}, + {0x2212, 0xFF0D, COMPAT_OUT}, + {0x00A2, 0xFFE0, COMPAT_OUT}, + {0x00A3, 0xFFE1, COMPAT_OUT}, + {0x00AC, 0xFFE2, COMPAT_OUT}, + {0, 0, 0} +}; + +static compat_t cp20932_compat[] = { + {0x00A5, 0x005C, COMPAT_OUT}, + {0x203E, 0x007E, COMPAT_OUT}, + {0x2014, 0x2015, COMPAT_OUT}, + {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, + {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, + {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, + {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, + {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, + {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, + {0, 0, 0} +}; + +static compat_t *cp51932_compat = cp932_compat; + +/* cp20932_compat for kernel. cp932_compat for mlang. */ +static compat_t *cp5022x_compat = cp932_compat; + +typedef HRESULT (WINAPI *CONVERTINETSTRING)( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + DWORD dwDstEncoding, + LPCSTR lpSrcStr, + LPINT lpnSrcSize, + LPBYTE lpDstStr, + LPINT lpnDstSize +); +typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + LPCSTR lpSrcStr, + LPINT lpnMultiCharCount, + LPWSTR lpDstStr, + LPINT lpnWideCharCount +); +typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( + LPDWORD lpdwMode, + DWORD dwEncoding, + LPCWSTR lpSrcStr, + LPINT lpnWideCharCount, + LPSTR lpDstStr, + LPINT lpnMultiCharCount +); +typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)( + DWORD dwSrcEncoding, + DWORD dwDstEncoding +); +typedef HRESULT (WINAPI *LCIDTORFC1766A)( + LCID Locale, + LPSTR pszRfc1766, + int nChar +); +typedef HRESULT (WINAPI *LCIDTORFC1766W)( + LCID Locale, + LPWSTR pszRfc1766, + int nChar +); +typedef HRESULT (WINAPI *RFC1766TOLCIDA)( + LCID *pLocale, + LPSTR pszRfc1766 +); +typedef HRESULT (WINAPI *RFC1766TOLCIDW)( + LCID *pLocale, + LPWSTR pszRfc1766 +); +static CONVERTINETSTRING ConvertINetString; +static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; +static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; +static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable; +static LCIDTORFC1766A LcidToRfc1766A; +static RFC1766TOLCIDA Rfc1766ToLcidA; + +static int +load_mlang() +{ + HMODULE h; + if (ConvertINetString != NULL) + return TRUE; + h = LoadLibrary("mlang.dll"); + if (!h) + return FALSE; + ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString"); + ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode"); + ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte"); + IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable"); + LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A"); + Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA"); + return TRUE; +} + +iconv_t +iconv_open(const char *tocode, const char *fromcode) +{ + rec_iconv_t *cd; + + cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t)); + if (cd == NULL) + return (iconv_t)(-1); + +#if defined(USE_LIBICONV_DLL) + errno = 0; + if (libiconv_iconv_open(cd, tocode, fromcode)) + return (iconv_t)cd; +#endif + + /* reset the errno to prevent reporting wrong error code. + * 0 for unsorted error. */ + errno = 0; + if (win_iconv_open(cd, tocode, fromcode)) + return (iconv_t)cd; + + free(cd); + + return (iconv_t)(-1); +} + +int +iconv_close(iconv_t _cd) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + int r = cd->iconv_close(cd->cd); + int e = *(cd->_errno()); +#if defined(USE_LIBICONV_DLL) + if (cd->hlibiconv != NULL) + FreeLibrary(cd->hlibiconv); +#endif + free(cd); + errno = e; + return r; +} + +size_t +iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft); + errno = *(cd->_errno()); + return r; +} + +static int +win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) +{ + if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to)) + return FALSE; + cd->iconv_close = win_iconv_close; + cd->iconv = win_iconv; + cd->_errno = _errno; + cd->cd = (iconv_t)cd; + return TRUE; +} + +static int +win_iconv_close(iconv_t cd) +{ + return 0; +} + +static size_t +win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) +{ + rec_iconv_t *cd = (rec_iconv_t *)_cd; + ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ + int insize; + int outsize; + int wsize; + DWORD frommode; + DWORD tomode; + uint wc; + compat_t *cp; + int i; + + if (inbuf == NULL || *inbuf == NULL) + { + if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) + { + tomode = cd->to.mode; + outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); + if (outsize == -1) + { + cd->to.mode = tomode; + return (size_t)(-1); + } + *outbuf += outsize; + *outbytesleft -= outsize; + } + cd->from.mode = 0; + cd->to.mode = 0; + return 0; + } + + while (*inbytesleft != 0) + { + frommode = cd->from.mode; + tomode = cd->to.mode; + wsize = MB_CHAR_MAX; + + insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); + if (insize == -1) + { + cd->from.mode = frommode; + return (size_t)(-1); + } + + if (wsize == 0) + { + *inbuf += insize; + *inbytesleft -= insize; + continue; + } + + if (cd->from.compat != NULL) + { + wc = utf16_to_ucs4(wbuf); + cp = cd->from.compat; + for (i = 0; cp[i].in != 0; ++i) + { + if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) + { + ucs4_to_utf16(cp[i].in, wbuf, &wsize); + break; + } + } + } + + if (cd->to.compat != NULL) + { + wc = utf16_to_ucs4(wbuf); + cp = cd->to.compat; + for (i = 0; cp[i].in != 0; ++i) + { + if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) + { + ucs4_to_utf16(cp[i].out, wbuf, &wsize); + break; + } + } + } + + outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); + if (outsize == -1) + { + cd->from.mode = frommode; + cd->to.mode = tomode; + return (size_t)(-1); + } + + *inbuf += insize; + *outbuf += outsize; + *inbytesleft -= insize; + *outbytesleft -= outsize; + } + + return 0; +} + +static int +make_csconv(const char *_name, csconv_t *cv) +{ + CPINFOEX cpinfoex; + int use_compat = TRUE; + int flag = 0; + char *name; + char *p; + + name = xstrndup(_name, strlen(_name)); + if (name == NULL) + return FALSE; + + /* check for option "enc_name//opt1//opt2" */ + while ((p = strrstr(name, "//")) != NULL) + { + if (_stricmp(p + 2, "nocompat") == 0) + use_compat = FALSE; + else if (_stricmp(p + 2, "translit") == 0) + flag |= FLAG_TRANSLIT; + else if (_stricmp(p + 2, "ignore") == 0) + flag |= FLAG_IGNORE; + *p = 0; + } + + cv->mode = 0; + cv->flags = flag; + cv->mblen = NULL; + cv->flush = NULL; + cv->compat = NULL; + cv->codepage = name_to_codepage(name); + if (cv->codepage == 1200 || cv->codepage == 1201) + { + cv->mbtowc = utf16_mbtowc; + cv->wctomb = utf16_wctomb; + if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0) + cv->flags |= FLAG_USE_BOM; + } + else if (cv->codepage == 12000 || cv->codepage == 12001) + { + cv->mbtowc = utf32_mbtowc; + cv->wctomb = utf32_wctomb; + if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0) + cv->flags |= FLAG_USE_BOM; + } + else if (cv->codepage == 65001) + { + cv->mbtowc = kernel_mbtowc; + cv->wctomb = kernel_wctomb; + cv->mblen = utf8_mblen; + } + else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang()) + { + cv->mbtowc = iso2022jp_mbtowc; + cv->wctomb = iso2022jp_wctomb; + cv->flush = iso2022jp_flush; + } + else if (cv->codepage == 51932 && load_mlang()) + { + cv->mbtowc = mlang_mbtowc; + cv->wctomb = mlang_wctomb; + cv->mblen = eucjp_mblen; + } + else if (IsValidCodePage(cv->codepage) + && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0) + { + cv->mbtowc = kernel_mbtowc; + cv->wctomb = kernel_wctomb; + if (cpinfoex.MaxCharSize == 1) + cv->mblen = sbcs_mblen; + else if (cpinfoex.MaxCharSize == 2) + cv->mblen = dbcs_mblen; + else + cv->mblen = mbcs_mblen; + } + else + { + /* not supported */ + free(name); + errno = EINVAL; + return FALSE; + } + + if (use_compat) + { + switch (cv->codepage) + { + case 932: cv->compat = cp932_compat; break; + case 20932: cv->compat = cp20932_compat; break; + case 51932: cv->compat = cp51932_compat; break; + case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break; + } + } + + free(name); + + return TRUE; +} + +static int +name_to_codepage(const char *name) +{ + int i; + + if (*name == '\0' || + strcmp(name, "char") == 0) + return GetACP(); + else if (strcmp(name, "wchar_t") == 0) + return 1200; + else if (_strnicmp(name, "cp", 2) == 0) + return atoi(name + 2); /* CP123 */ + else if ('0' <= name[0] && name[0] <= '9') + return atoi(name); /* 123 */ + else if (_strnicmp(name, "xx", 2) == 0) + return atoi(name + 2); /* XX123 for debug */ + + for (i = 0; codepage_alias[i].name != NULL; ++i) + if (_stricmp(name, codepage_alias[i].name) == 0) + return codepage_alias[i].codepage; + return -1; +} + +/* + * http://www.faqs.org/rfcs/rfc2781.html + */ +static uint +utf16_to_ucs4(const ushort *wbuf) +{ + uint wc = wbuf[0]; + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; + return wc; +} + +static void +ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) +{ + if (wc < 0x10000) + { + wbuf[0] = wc; + *wbufsize = 1; + } + else + { + wc -= 0x10000; + wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); + wbuf[1] = 0xDC00 | (wc & 0x3FF); + *wbufsize = 2; + } +} + +/* + * Check if codepage is one of those for which the dwFlags parameter + * to MultiByteToWideChar() must be zero. Return zero or + * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows + * Server 2003 R2 claims that also codepage 65001 is one of these, but + * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave + * out 65001 (UTF-8), and that indeed seems to be the case on XP, it + * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting + * from UTF-8. + */ +static int +mbtowc_flags(int codepage) +{ + return (codepage == 50220 || codepage == 50221 || + codepage == 50222 || codepage == 50225 || + codepage == 50227 || codepage == 50229 || + codepage == 52936 || codepage == 54936 || + (codepage >= 57002 && codepage <= 57011) || + codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; +} + +/* + * Check if codepage is one those for which the lpUsedDefaultChar + * parameter to WideCharToMultiByte() must be NULL. The docs in + * Platform SDK for for Windows Server 2003 R2 claims that this is the + * list below, while the MSDN docs for MSVS2008 claim that it is only + * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform + * SDK seems to be correct, at least for XP. + */ +static int +must_use_null_useddefaultchar(int codepage) +{ + return (codepage == 65000 || codepage == 65001 || + codepage == 50220 || codepage == 50221 || + codepage == 50222 || codepage == 50225 || + codepage == 50227 || codepage == 50229 || + codepage == 52936 || codepage == 54936 || + (codepage >= 57002 && codepage <= 57011) || + codepage == 42); +} + +static char * +strrstr(const char *str, const char *token) +{ + int len = strlen(token); + const char *p = str + strlen(str); + + while (str <= --p) + if (p[0] == token[0] && strncmp(p, token, len) == 0) + return (char *)p; + return NULL; +} + +static char * +xstrndup(const char *s, size_t n) +{ + char *p; + + p = (char *)malloc(n + 1); + if (p == NULL) + return NULL; + memcpy(p, s, n); + p[n] = '\0'; + return p; +} + +static int +seterror(int err) +{ + errno = err; + return -1; +} + +#if defined(USE_LIBICONV_DLL) +static int +libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode) +{ + HMODULE hlibiconv = NULL; + HMODULE hmsvcrt = NULL; + char *dllname; + const char *p; + const char *e; + f_iconv_open _iconv_open; + + /* + * always try to load dll, so that we can switch dll in runtime. + */ + + /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */ + p = getenv("WINICONV_LIBICONV_DLL"); + if (p == NULL) + p = DEFAULT_LIBICONV_DLL; + /* parse comma separated value */ + for ( ; *p != 0; p = (*e == ',') ? e + 1 : e) + { + e = strchr(p, ','); + if (p == e) + continue; + else if (e == NULL) + e = p + strlen(p); + dllname = xstrndup(p, e - p); + if (dllname == NULL) + return FALSE; + hlibiconv = LoadLibrary(dllname); + free(dllname); + if (hlibiconv != NULL) + { + if (hlibiconv == hwiniconv) + { + FreeLibrary(hlibiconv); + hlibiconv = NULL; + continue; + } + break; + } + } + + if (hlibiconv == NULL) + goto failed; + + hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno"); + if (hmsvcrt == NULL) + goto failed; + + _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open"); + if (_iconv_open == NULL) + _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open"); + cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close"); + if (cd->iconv_close == NULL) + cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close"); + cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv"); + if (cd->iconv == NULL) + cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv"); + cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno"); + if (_iconv_open == NULL || cd->iconv_close == NULL + || cd->iconv == NULL || cd->_errno == NULL) + goto failed; + + cd->cd = _iconv_open(tocode, fromcode); + if (cd->cd == (iconv_t)(-1)) + goto failed; + + cd->hlibiconv = hlibiconv; + return TRUE; + +failed: + if (hlibiconv != NULL) + FreeLibrary(hlibiconv); + /* do not free hmsvcrt which is obtained by GetModuleHandle() */ + return FALSE; +} + +/* + * Reference: + * http://forums.belution.com/ja/vc/000/234/78s.shtml + * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html + * + * The formal way is + * imagehlp.h or dbghelp.h + * imagehlp.lib or dbghelp.lib + * ImageDirectoryEntryToData() + */ +#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base)) +#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew)) +static PVOID +MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size) +{ + /* TODO: MappedAsImage? */ + PIMAGE_DATA_DIRECTORY p; + p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry; + if (p->VirtualAddress == 0) { + *Size = 0; + return NULL; + } + *Size = p->Size; + return (PVOID)((LPBYTE)Base + p->VirtualAddress); +} + +static HMODULE +find_imported_module_by_funcname(HMODULE hModule, const char *funcname) +{ + DWORD Base; + ULONG Size; + PIMAGE_IMPORT_DESCRIPTOR Imp; + PIMAGE_THUNK_DATA Name; /* Import Name Table */ + PIMAGE_IMPORT_BY_NAME ImpName; + + Base = (DWORD)hModule; + Imp = MyImageDirectoryEntryToData( + (LPVOID)Base, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + if (Imp == NULL) + return NULL; + for ( ; Imp->OriginalFirstThunk != 0; ++Imp) + { + Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk); + for ( ; Name->u1.Ordinal != 0; ++Name) + { + if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal)) + { + ImpName = (PIMAGE_IMPORT_BY_NAME) + (Base + (DWORD)Name->u1.AddressOfData); + if (strcmp((char *)ImpName->Name, funcname) == 0) + return GetModuleHandle((char *)(Base + Imp->Name)); + } + } + } + return NULL; +} +#endif + +static int +sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + return 1; +} + +static int +dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; + if (bufsize < len) + return seterror(EINVAL); + return len; +} + +static int +mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = 0; + + if (cv->codepage == 54936) { + if (buf[0] <= 0x7F) len = 1; + else if (buf[0] >= 0x81 && buf[0] <= 0xFE && + bufsize >= 2 && + ((buf[1] >= 0x40 && buf[1] <= 0x7E) || + (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2; + else if (buf[0] >= 0x81 && buf[0] <= 0xFE && + bufsize >= 4 && + buf[1] >= 0x30 && buf[1] <= 0x39) len = 4; + else + return seterror(EINVAL); + return len; + } + else + return seterror(EINVAL); +} + +static int +utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + int len = 0; + + if (buf[0] < 0x80) len = 1; + else if ((buf[0] & 0xE0) == 0xC0) len = 2; + else if ((buf[0] & 0xF0) == 0xE0) len = 3; + else if ((buf[0] & 0xF8) == 0xF0) len = 4; + else if ((buf[0] & 0xFC) == 0xF8) len = 5; + else if ((buf[0] & 0xFE) == 0xFC) len = 6; + + if (len == 0) + return seterror(EILSEQ); + else if (bufsize < len) + return seterror(EINVAL); + return len; +} + +static int +eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize) +{ + if (buf[0] < 0x80) /* ASCII */ + return 1; + else if (buf[0] == 0x8E) /* JIS X 0201 */ + { + if (bufsize < 2) + return seterror(EINVAL); + else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) + return seterror(EILSEQ); + return 2; + } + else if (buf[0] == 0x8F) /* JIS X 0212 */ + { + if (bufsize < 3) + return seterror(EINVAL); + else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) + || !(0xA1 <= buf[2] && buf[2] <= 0xFE)) + return seterror(EILSEQ); + return 3; + } + else /* JIS X 0208 */ + { + if (bufsize < 2) + return seterror(EINVAL); + else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) + || !(0xA1 <= buf[1] && buf[1] <= 0xFE)) + return seterror(EILSEQ); + return 2; + } +} + +static int +kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int len; + + len = cv->mblen(cv, buf, bufsize); + if (len == -1) + return -1; + *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), + (const char *)buf, len, (wchar_t *)wbuf, *wbufsize); + if (*wbufsize == 0) + return seterror(EILSEQ); + return len; +} + +static int +kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + BOOL usedDefaultChar = 0; + BOOL *p = NULL; + int flags = 0; + int len; + + if (bufsize == 0) + return seterror(E2BIG); + if (!must_use_null_useddefaultchar(cv->codepage)) + { + p = &usedDefaultChar; +#ifdef WC_NO_BEST_FIT_CHARS + if (!(cv->flags & FLAG_TRANSLIT)) + flags |= WC_NO_BEST_FIT_CHARS; +#endif + } + len = WideCharToMultiByte(cv->codepage, flags, + (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); + if (len == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + return seterror(E2BIG); + return seterror(EILSEQ); + } + else if (usedDefaultChar) + return seterror(EILSEQ); + else if (cv->mblen(cv, buf, len) != len) /* validate result */ + return seterror(EILSEQ); + return len; +} + +/* + * It seems that the mode (cv->mode) is fixnum. + * For example, when converting iso-2022-jp(cp50221) to unicode: + * in ascii sequence: mode=0xC42C0000 + * in jisx0208 sequence: mode=0xC42C0001 + * "C42C" is same for each convert session. + * It should be: ((codepage-1)<<16)|state + */ +static int +mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int len; + int insize; + HRESULT hr; + + len = cv->mblen(cv, buf, bufsize); + if (len == -1) + return -1; + insize = len; + hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, + (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len) + return seterror(EILSEQ); + return len; +} + +static int +mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + + hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) + return seterror(EILSEQ); + memcpy(buf, tmpbuf, tmpsize); + return tmpsize; +} + +static int +utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + + /* swap endian: 1200 <-> 1201 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 2) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[0] = (buf[1] << 8) | buf[0]; + else if (codepage == 1201) /* big endian */ + wbuf[0] = (buf[0] << 8) | buf[1]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wbuf[0] == 0xFFFE) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 2; + } + else if (wbuf[0] == 0xFEFF) + { + *wbufsize = 0; + return 2; + } + } + + if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) + return seterror(EILSEQ); + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[1] = (buf[3] << 8) | buf[2]; + else if (codepage == 1201) /* big endian */ + wbuf[1] = (buf[2] << 8) | buf[3]; + if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) + return seterror(EILSEQ); + *wbufsize = 2; + return 4; + } + *wbufsize = 1; + return 2; +} + +static int +utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + memcpy(buf, "\xFF\xFE", 2); + else if (cv->codepage == 1201) /* big endian */ + memcpy(buf, "\xFE\xFF", 2); + + r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); + if (r == -1) + return -1; + return r + 2; + } + + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[0] = (wbuf[0] & 0x00FF); + buf[1] = (wbuf[0] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[0] = (wbuf[0] & 0xFF00) >> 8; + buf[1] = (wbuf[0] & 0x00FF); + } + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[2] = (wbuf[1] & 0x00FF); + buf[3] = (wbuf[1] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[2] = (wbuf[1] & 0xFF00) >> 8; + buf[3] = (wbuf[1] & 0x00FF); + } + return 4; + } + return 2; +} + +static int +utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + uint wc; + + /* swap endian: 12000 <-> 12001 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 12000) /* little endian */ + wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + else if (codepage == 12001) /* big endian */ + wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wc == 0xFFFE0000) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 4; + } + else if (wc == 0x0000FEFF) + { + *wbufsize = 0; + return 4; + } + } + + if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) + return seterror(EILSEQ); + ucs4_to_utf16(wc, wbuf, wbufsize); + return 4; +} + +static int +utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + uint wc; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 12000) /* little endian */ + memcpy(buf, "\xFF\xFE\x00\x00", 4); + else if (cv->codepage == 12001) /* big endian */ + memcpy(buf, "\x00\x00\xFE\xFF", 4); + + r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); + if (r == -1) + return -1; + return r + 4; + } + + if (bufsize < 4) + return seterror(E2BIG); + wc = utf16_to_ucs4(wbuf); + if (cv->codepage == 12000) /* little endian */ + { + buf[0] = wc & 0x000000FF; + buf[1] = (wc & 0x0000FF00) >> 8; + buf[2] = (wc & 0x00FF0000) >> 16; + buf[3] = (wc & 0xFF000000) >> 24; + } + else if (cv->codepage == 12001) /* big endian */ + { + buf[0] = (wc & 0xFF000000) >> 24; + buf[1] = (wc & 0x00FF0000) >> 16; + buf[2] = (wc & 0x0000FF00) >> 8; + buf[3] = wc & 0x000000FF; + } + return 4; +} + +/* + * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) + * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow + * 1 byte Kana) + * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte + * Kana - SO/SI) + * + * MultiByteToWideChar() and WideCharToMultiByte() behave differently + * depending on Windows version. On XP, WideCharToMultiByte() doesn't + * terminate result sequence with ascii escape. But Vista does. + * Use MLang instead. + */ + +#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) +#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) +#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) + +#define ISO2022_SI 0 +#define ISO2022_SO 1 + +/* shift in */ +static const char iso2022_SI_seq[] = "\x0F"; +/* shift out */ +static const char iso2022_SO_seq[] = "\x0E"; + +typedef struct iso2022_esc_t iso2022_esc_t; +struct iso2022_esc_t { + const char *esc; + int esc_len; + int len; + int cs; +}; + +#define ISO2022JP_CS_ASCII 0 +#define ISO2022JP_CS_JISX0201_ROMAN 1 +#define ISO2022JP_CS_JISX0201_KANA 2 +#define ISO2022JP_CS_JISX0208_1978 3 +#define ISO2022JP_CS_JISX0208_1983 4 +#define ISO2022JP_CS_JISX0212 5 + +static iso2022_esc_t iso2022jp_esc[] = { + {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, + {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, + {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, + {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ + {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, + {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, + {NULL, 0, 0, 0} +}; + +static int +iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int insize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len; + int cs; + int shift; + int i; + + if (buf[0] == 0x1B) + { + for (i = 0; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (bufsize < esc_len) + { + if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) + return seterror(EINVAL); + } + else + { + if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) + { + cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); + *wbufsize = 0; + return esc_len; + } + } + } + /* not supported escape sequence */ + return seterror(EILSEQ); + } + else if (buf[0] == iso2022_SO_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); + *wbufsize = 0; + return 1; + } + else if (buf[0] == iso2022_SI_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); + *wbufsize = 0; + return 1; + } + + cs = ISO2022_MODE_CS(cv->mode); + shift = ISO2022_MODE_SHIFT(cv->mode); + + /* reset the mode for informal sequence */ + if (buf[0] < 0x20) + { + cs = ISO2022JP_CS_ASCII; + shift = ISO2022_SI; + } + + len = iesc[cs].len; + if (bufsize < len) + return seterror(EINVAL); + for (i = 0; i < len; ++i) + if (!(buf[i] < 0x80)) + return seterror(EILSEQ); + esc_len = iesc[cs].esc_len; + memcpy(tmp, iesc[cs].esc, esc_len); + if (shift == ISO2022_SO) + { + memcpy(tmp + esc_len, iso2022_SO_seq, 1); + esc_len += 1; + } + memcpy(tmp + esc_len, buf, len); + + if ((cv->codepage == 50220 || cv->codepage == 50221 + || cv->codepage == 50222) && shift == ISO2022_SO) + { + /* XXX: shift-out cannot be used for mbtowc (both kernel and + * mlang) */ + esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; + memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); + memcpy(tmp + esc_len, buf, len); + } + + insize = len + esc_len; + hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, + (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len + esc_len) + return seterror(EILSEQ); + + /* Check for conversion error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (wbuf[0] == buf[0] + && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + return seterror(EILSEQ); + + /* reset the mode for informal sequence */ + if (cv->mode != ISO2022_MODE(cs, shift)) + cv->mode = ISO2022_MODE(cs, shift); + + return len; +} + +static int +iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len; + int cs; + int shift; + int i; + + /* + * MultiByte = [escape sequence] + character + [escape sequence] + * + * Whether trailing escape sequence is added depends on which API is + * used (kernel or MLang, and its version). + */ + hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + + if (tmpsize == 1) + { + cs = ISO2022JP_CS_ASCII; + esc_len = 0; + } + else + { + for (i = 1; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (strncmp(tmp, iesc[i].esc, esc_len) == 0) + { + cs = iesc[i].cs; + break; + } + } + if (iesc[i].esc == NULL) + /* not supported escape sequence */ + return seterror(EILSEQ); + } + + shift = ISO2022_SI; + if (tmp[esc_len] == iso2022_SO_seq[0]) + { + shift = ISO2022_SO; + esc_len += 1; + } + + len = iesc[cs].len; + + /* Check for converting error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) + return seterror(EILSEQ); + else if (tmpsize < esc_len + len) + return seterror(EILSEQ); + + if (cv->mode == ISO2022_MODE(cs, shift)) + { + /* remove escape sequence */ + if (esc_len != 0) + memmove(tmp, tmp + esc_len, len); + esc_len = 0; + } + else + { + if (cs == ISO2022JP_CS_ASCII) + { + esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; + memmove(tmp + esc_len, tmp, len); + memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); + } + if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) + { + /* shift-in before changing to other mode */ + memmove(tmp + 1, tmp, len + esc_len); + memcpy(tmp, iso2022_SI_seq, 1); + esc_len += 1; + } + } + + if (bufsize < len + esc_len) + return seterror(E2BIG); + memcpy(buf, tmp, len + esc_len); + cv->mode = ISO2022_MODE(cs, shift); + return len + esc_len; +} + +static int +iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + int esc_len; + + if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + { + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + esc_len += 1; + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + if (bufsize < esc_len) + return seterror(E2BIG); + + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + { + memcpy(buf, iso2022_SI_seq, 1); + esc_len += 1; + } + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + { + memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, + iesc[ISO2022JP_CS_ASCII].esc_len); + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + } + return esc_len; + } + return 0; +} + +#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) +BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) +{ + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + hwiniconv = (HMODULE)hinstDLL; + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +#if defined(MAKE_EXE) +#include +#include +#include +int +main(int argc, char **argv) +{ + char *fromcode = NULL; + char *tocode = NULL; + int i; + char inbuf[BUFSIZ]; + char outbuf[BUFSIZ]; + const char *pin; + char *pout; + size_t inbytesleft; + size_t outbytesleft; + size_t rest = 0; + iconv_t cd; + size_t r; + FILE *in = stdin; + + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + + for (i = 1; i < argc; ++i) + { + if (strcmp(argv[i], "-l") == 0) + { + for (i = 0; codepage_alias[i].name != NULL; ++i) + printf("%s\n", codepage_alias[i].name); + return 0; + } + + if (strcmp(argv[i], "-f") == 0) + fromcode = argv[++i]; + else if (strcmp(argv[i], "-t") == 0) + tocode = argv[++i]; + else + { + in = fopen(argv[i], "rb"); + if (in == NULL) + { + fprintf(stderr, "cannot open %s\n", argv[i]); + return 1; + } + break; + } + } + + if (fromcode == NULL || tocode == NULL) + { + printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]); + return 0; + } + + cd = iconv_open(tocode, fromcode); + if (cd == (iconv_t)(-1)) + { + perror("iconv_open error"); + return 1; + } + + while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0 + || rest != 0) + { + inbytesleft += rest; + pin = inbuf; + pout = outbuf; + outbytesleft = sizeof(outbuf); + r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); + fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); + if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in))) + { + perror("conversion error"); + return 1; + } + memmove(inbuf, pin, inbytesleft); + rest = inbytesleft; + } + pout = outbuf; + outbytesleft = sizeof(outbuf); + r = iconv(cd, NULL, NULL, &pout, &outbytesleft); + fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout); + if (r == (size_t)(-1)) + { + perror("conversion error"); + return 1; + } + + iconv_close(cd); + + return 0; +} +#endif + diff --git a/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv_test.c b/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv_test.c index 25b5574fa6..b2aafa4a57 100644 --- a/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv_test.c +++ b/src/mod/endpoints/mod_gsmopen/win_iconv/win_iconv_test.c @@ -1,261 +1,261 @@ - -#include "win_iconv.c" -#include - -const char * -tohex(const char *str, int size) -{ - static char buf[BUFSIZ]; - char *pbuf = buf; - int i; - buf[0] = 0; - for (i = 0; i < size; ++i) - pbuf += sprintf(pbuf, "%02X", str[i] & 0xFF); - return buf; -} - -const char * -errstr(int errcode) -{ - static char buf[BUFSIZ]; - switch (errcode) - { - case 0: return "NOERROR"; - case EINVAL: return "EINVAL"; - case EILSEQ: return "EILSEQ"; - case E2BIG: return "E2BIG"; - } - sprintf(buf, "%d\n", errcode); - return buf; -} - -#ifdef USE_LIBICONV_DLL -int use_dll; - -int -setdll(const char *dllpath) -{ - char buf[BUFSIZ]; - rec_iconv_t cd; - - sprintf(buf, "WINICONV_LIBICONV_DLL=%s", dllpath); - putenv(buf); - if (libiconv_iconv_open(&cd, "ascii", "ascii")) - { - FreeLibrary(cd.hlibiconv); - use_dll = TRUE; - return TRUE; - } - use_dll = FALSE; - return FALSE; -} -#endif - -/* - * We can test the codepage that is installed in the system. - */ -int -check_enc(const char *encname, int codepage) -{ - iconv_t cd; - int cp; - cd = iconv_open("utf-8", encname); - if (cd == (iconv_t)(-1)) - { - printf("%s(%d) IS NOT SUPPORTED: SKIP THE TEST\n", encname, codepage); - return FALSE; - } - cp = ((rec_iconv_t *)cd)->from.codepage; - if (cp != codepage) - { - printf("%s(%d) ALIAS IS MAPPED TO DIFFERENT CODEPAGE (%d)\n", encname, codepage, cp); - exit(1); - } - iconv_close(cd); - return TRUE; -} - -int use_dll; - -void -test(const char *from, const char *fromstr, int fromsize, const char *to, const char *tostr, int tosize, int errcode, int bufsize, int line) -{ - char outbuf[BUFSIZ]; - const char *pin; - char *pout; - size_t inbytesleft; - size_t outbytesleft; - iconv_t cd; - size_t r; - char dllpath[_MAX_PATH]; - - cd = iconv_open(to, from); - if (cd == (iconv_t)(-1)) - { - printf("%s -> %s: NG: INVALID ENCODING NAME: line=%d\n", from, to, line); - exit(1); - } - -#ifdef USE_LIBICONV_DLL - if (((rec_iconv_t *)cd)->hlibiconv != NULL) - GetModuleFileName(((rec_iconv_t *)cd)->hlibiconv, dllpath, sizeof(dllpath)); - - if (use_dll && ((rec_iconv_t *)cd)->hlibiconv == NULL) - { - printf("%s: %s -> %s: NG: FAILED TO USE DLL: line=%d\n", dllpath, from, to, line); - exit(1); - } - else if (!use_dll && ((rec_iconv_t *)cd)->hlibiconv != NULL) - { - printf("%s: %s -> %s: NG: DLL IS LOADED UNEXPECTEDLY: line=%d\n", dllpath, from, to, line); - exit(1); - } -#endif - - errno = 0; - - pin = fromstr; - pout = outbuf; - inbytesleft = fromsize; - outbytesleft = bufsize; - r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); - if (r != (size_t)(-1)) - r = iconv(cd, NULL, NULL, &pout, &outbytesleft); - *pout = 0; - -#ifdef USE_LIBICONV_DLL - if (use_dll) - printf("%s: ", dllpath); -#endif - printf("%s(%s) -> ", from, tohex(fromstr, fromsize)); - printf("%s(%s%s%s): ", to, tohex(tostr, tosize), - errcode == 0 ? "" : ":", - errcode == 0 ? "" : errstr(errcode)); - if (strcmp(outbuf, tostr) == 0 && errno == errcode) - printf("OK\n"); - else - { - printf("RESULT(%s:%s): ", tohex(outbuf, sizeof(outbuf) - outbytesleft), - errstr(errno)); - printf("NG: line=%d\n", line); - exit(1); - } -} - -#define STATIC_STRLEN(arr) (sizeof(arr) - 1) - -#define success(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), 0, BUFSIZ, __LINE__) -#define einval(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EINVAL, BUFSIZ, __LINE__) -#define eilseq(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EILSEQ, BUFSIZ, __LINE__) -#define e2big(from, fromstr, to, tostr, bufsize) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), E2BIG, bufsize, __LINE__) - -int -main(int argc, char **argv) -{ -#ifdef USE_LIBICONV_DLL - /* test use of dll if $DEFAULT_LIBICONV_DLL was defined. */ - if (setdll("")) - { - success("ascii", "ABC", "ascii", "ABC"); - success("ascii", "ABC", "utf-16be", "\x00\x41\x00\x42\x00\x43"); - } - else - { - printf("\nDLL TEST IS SKIPPED\n\n"); - } - - setdll("none"); -#endif - - if (check_enc("ascii", 20127)) - { - success("ascii", "ABC", "ascii", "ABC"); - /* MSB is dropped. Hmm... */ - success("ascii", "\x80\xFF", "ascii", "\x00\x7F"); - } - - /* unicode (CP1200 CP1201 CP12000 CP12001 CP65001) */ - if (check_enc("utf-8", 65001) - && check_enc("utf-16be", 1201) && check_enc("utf-16le", 1200) - && check_enc("utf-32be", 12001) && check_enc("utf-32le", 12000) - ) - { - /* Test the BOM behavior - * 1. Remove the BOM when "fromcode" is utf-16 or utf-32. - * 2. Add the BOM when "tocode" is utf-16 or utf-32. */ - success("utf-16", "\xFE\xFF\x01\x02", "utf-16be", "\x01\x02"); - success("utf-16", "\xFF\xFE\x02\x01", "utf-16be", "\x01\x02"); - success("utf-32", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\x01\x02"); - success("utf-32", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\x01\x02"); - success("utf-16", "\xFE\xFF\x00\x01", "utf-8", "\x01"); -#ifndef GLIB_COMPILATION - success("utf-8", "\x01", "utf-16", "\xFE\xFF\x00\x01"); - success("utf-8", "\x01", "utf-32", "\x00\x00\xFE\xFF\x00\x00\x00\x01"); -#else - success("utf-8", "\x01", "utf-16", "\xFF\xFE\x01\x00"); - success("utf-8", "\x01", "utf-32", "\xFF\xFE\x00\x00\x01\x00\x00\x00"); -#endif - - success("utf-16be", "\xFE\xFF\x01\x02", "utf-16be", "\xFE\xFF\x01\x02"); - success("utf-16le", "\xFF\xFE\x02\x01", "utf-16be", "\xFE\xFF\x01\x02"); - success("utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02"); - success("utf-32le", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02"); - success("utf-16be", "\xFE\xFF\x00\x01", "utf-8", "\xEF\xBB\xBF\x01"); - success("utf-8", "\xEF\xBB\xBF\x01", "utf-8", "\xEF\xBB\xBF\x01"); - - success("utf-16be", "\x01\x02", "utf-16le", "\x02\x01"); - success("utf-16le", "\x02\x01", "utf-16be", "\x01\x02"); - success("utf-16be", "\xFE\xFF", "utf-16le", "\xFF\xFE"); - success("utf-16le", "\xFF\xFE", "utf-16be", "\xFE\xFF"); - success("utf-32be", "\x00\x00\x03\x04", "utf-32le", "\x04\x03\x00\x00"); - success("utf-32le", "\x04\x03\x00\x00", "utf-32be", "\x00\x00\x03\x04"); - success("utf-32be", "\x00\x00\xFF\xFF", "utf-16be", "\xFF\xFF"); - success("utf-16be", "\xFF\xFF", "utf-32be", "\x00\x00\xFF\xFF"); - success("utf-32be", "\x00\x01\x00\x00", "utf-16be", "\xD8\x00\xDC\x00"); - success("utf-16be", "\xD8\x00\xDC\x00", "utf-32be", "\x00\x01\x00\x00"); - success("utf-32be", "\x00\x10\xFF\xFF", "utf-16be", "\xDB\xFF\xDF\xFF"); - success("utf-16be", "\xDB\xFF\xDF\xFF", "utf-32be", "\x00\x10\xFF\xFF"); - eilseq("utf-32be", "\x00\x11\x00\x00", "utf-16be", ""); - eilseq("utf-16be", "\xDB\xFF\xE0\x00", "utf-32be", ""); - success("utf-8", "\xE3\x81\x82", "utf-16be", "\x30\x42"); - einval("utf-8", "\xE3", "utf-16be", ""); - } - - /* Japanese (CP932 CP20932 CP50220 CP50221 CP50222 CP51932) */ - if (check_enc("cp932", 932) - && check_enc("cp20932", 20932) && check_enc("euc-jp", 51932) - && check_enc("cp50220", 50220) && check_enc("cp50221", 50221) - && check_enc("cp50222", 50222) && check_enc("iso-2022-jp", 50221)) - { - /* Test the compatibility for each other Japanese codepage. - * And validate the escape sequence handling for iso-2022-jp. */ - success("utf-16be", "\xFF\x5E", "cp932", "\x81\x60"); - success("utf-16be", "\x30\x1C", "cp932", "\x81\x60"); - success("utf-16be", "\xFF\x5E", "cp932//nocompat", "\x81\x60"); - eilseq("utf-16be", "\x30\x1C", "cp932//nocompat", ""); - success("euc-jp", "\xA4\xA2", "utf-16be", "\x30\x42"); - einval("euc-jp", "\xA4\xA2\xA4", "utf-16be", "\x30\x42"); - eilseq("euc-jp", "\xA4\xA2\xFF\xFF", "utf-16be", "\x30\x42"); - success("cp932", "\x81\x60", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42"); - success("UTF-16BE", "\xFF\x5E", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42"); - eilseq("UTF-16BE", "\x30\x1C", "iso-2022-jp//nocompat", ""); - success("UTF-16BE", "\x30\x42\x30\x44", "iso-2022-jp", "\x1B\x24\x42\x24\x22\x24\x24\x1B\x28\x42"); - success("iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42", "UTF-16BE", "\xFF\x5E"); - } - - /* - * test for //translit - * U+FF41 (FULLWIDTH LATIN SMALL LETTER A) <-> U+0062 (LATIN SMALL LETTER A) - */ - eilseq("UTF-16BE", "\xFF\x41", "iso-8859-1", ""); - success("UTF-16BE", "\xFF\x41", "iso-8859-1//translit", "a"); - - /* - * TODO: - * Test for state after iconv() failed. - * Ensure iconv() error is safe and continuable. - */ - - return 0; -} - + +#include "win_iconv.c" +#include + +const char * +tohex(const char *str, int size) +{ + static char buf[BUFSIZ]; + char *pbuf = buf; + int i; + buf[0] = 0; + for (i = 0; i < size; ++i) + pbuf += sprintf(pbuf, "%02X", str[i] & 0xFF); + return buf; +} + +const char * +errstr(int errcode) +{ + static char buf[BUFSIZ]; + switch (errcode) + { + case 0: return "NOERROR"; + case EINVAL: return "EINVAL"; + case EILSEQ: return "EILSEQ"; + case E2BIG: return "E2BIG"; + } + sprintf(buf, "%d\n", errcode); + return buf; +} + +#ifdef USE_LIBICONV_DLL +int use_dll; + +int +setdll(const char *dllpath) +{ + char buf[BUFSIZ]; + rec_iconv_t cd; + + sprintf(buf, "WINICONV_LIBICONV_DLL=%s", dllpath); + putenv(buf); + if (libiconv_iconv_open(&cd, "ascii", "ascii")) + { + FreeLibrary(cd.hlibiconv); + use_dll = TRUE; + return TRUE; + } + use_dll = FALSE; + return FALSE; +} +#endif + +/* + * We can test the codepage that is installed in the system. + */ +int +check_enc(const char *encname, int codepage) +{ + iconv_t cd; + int cp; + cd = iconv_open("utf-8", encname); + if (cd == (iconv_t)(-1)) + { + printf("%s(%d) IS NOT SUPPORTED: SKIP THE TEST\n", encname, codepage); + return FALSE; + } + cp = ((rec_iconv_t *)cd)->from.codepage; + if (cp != codepage) + { + printf("%s(%d) ALIAS IS MAPPED TO DIFFERENT CODEPAGE (%d)\n", encname, codepage, cp); + exit(1); + } + iconv_close(cd); + return TRUE; +} + +int use_dll; + +void +test(const char *from, const char *fromstr, int fromsize, const char *to, const char *tostr, int tosize, int errcode, int bufsize, int line) +{ + char outbuf[BUFSIZ]; + const char *pin; + char *pout; + size_t inbytesleft; + size_t outbytesleft; + iconv_t cd; + size_t r; + char dllpath[_MAX_PATH]; + + cd = iconv_open(to, from); + if (cd == (iconv_t)(-1)) + { + printf("%s -> %s: NG: INVALID ENCODING NAME: line=%d\n", from, to, line); + exit(1); + } + +#ifdef USE_LIBICONV_DLL + if (((rec_iconv_t *)cd)->hlibiconv != NULL) + GetModuleFileName(((rec_iconv_t *)cd)->hlibiconv, dllpath, sizeof(dllpath)); + + if (use_dll && ((rec_iconv_t *)cd)->hlibiconv == NULL) + { + printf("%s: %s -> %s: NG: FAILED TO USE DLL: line=%d\n", dllpath, from, to, line); + exit(1); + } + else if (!use_dll && ((rec_iconv_t *)cd)->hlibiconv != NULL) + { + printf("%s: %s -> %s: NG: DLL IS LOADED UNEXPECTEDLY: line=%d\n", dllpath, from, to, line); + exit(1); + } +#endif + + errno = 0; + + pin = fromstr; + pout = outbuf; + inbytesleft = fromsize; + outbytesleft = bufsize; + r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); + if (r != (size_t)(-1)) + r = iconv(cd, NULL, NULL, &pout, &outbytesleft); + *pout = 0; + +#ifdef USE_LIBICONV_DLL + if (use_dll) + printf("%s: ", dllpath); +#endif + printf("%s(%s) -> ", from, tohex(fromstr, fromsize)); + printf("%s(%s%s%s): ", to, tohex(tostr, tosize), + errcode == 0 ? "" : ":", + errcode == 0 ? "" : errstr(errcode)); + if (strcmp(outbuf, tostr) == 0 && errno == errcode) + printf("OK\n"); + else + { + printf("RESULT(%s:%s): ", tohex(outbuf, sizeof(outbuf) - outbytesleft), + errstr(errno)); + printf("NG: line=%d\n", line); + exit(1); + } +} + +#define STATIC_STRLEN(arr) (sizeof(arr) - 1) + +#define success(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), 0, BUFSIZ, __LINE__) +#define einval(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EINVAL, BUFSIZ, __LINE__) +#define eilseq(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EILSEQ, BUFSIZ, __LINE__) +#define e2big(from, fromstr, to, tostr, bufsize) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), E2BIG, bufsize, __LINE__) + +int +main(int argc, char **argv) +{ +#ifdef USE_LIBICONV_DLL + /* test use of dll if $DEFAULT_LIBICONV_DLL was defined. */ + if (setdll("")) + { + success("ascii", "ABC", "ascii", "ABC"); + success("ascii", "ABC", "utf-16be", "\x00\x41\x00\x42\x00\x43"); + } + else + { + printf("\nDLL TEST IS SKIPPED\n\n"); + } + + setdll("none"); +#endif + + if (check_enc("ascii", 20127)) + { + success("ascii", "ABC", "ascii", "ABC"); + /* MSB is dropped. Hmm... */ + success("ascii", "\x80\xFF", "ascii", "\x00\x7F"); + } + + /* unicode (CP1200 CP1201 CP12000 CP12001 CP65001) */ + if (check_enc("utf-8", 65001) + && check_enc("utf-16be", 1201) && check_enc("utf-16le", 1200) + && check_enc("utf-32be", 12001) && check_enc("utf-32le", 12000) + ) + { + /* Test the BOM behavior + * 1. Remove the BOM when "fromcode" is utf-16 or utf-32. + * 2. Add the BOM when "tocode" is utf-16 or utf-32. */ + success("utf-16", "\xFE\xFF\x01\x02", "utf-16be", "\x01\x02"); + success("utf-16", "\xFF\xFE\x02\x01", "utf-16be", "\x01\x02"); + success("utf-32", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\x01\x02"); + success("utf-32", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\x01\x02"); + success("utf-16", "\xFE\xFF\x00\x01", "utf-8", "\x01"); +#ifndef GLIB_COMPILATION + success("utf-8", "\x01", "utf-16", "\xFE\xFF\x00\x01"); + success("utf-8", "\x01", "utf-32", "\x00\x00\xFE\xFF\x00\x00\x00\x01"); +#else + success("utf-8", "\x01", "utf-16", "\xFF\xFE\x01\x00"); + success("utf-8", "\x01", "utf-32", "\xFF\xFE\x00\x00\x01\x00\x00\x00"); +#endif + + success("utf-16be", "\xFE\xFF\x01\x02", "utf-16be", "\xFE\xFF\x01\x02"); + success("utf-16le", "\xFF\xFE\x02\x01", "utf-16be", "\xFE\xFF\x01\x02"); + success("utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02"); + success("utf-32le", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02"); + success("utf-16be", "\xFE\xFF\x00\x01", "utf-8", "\xEF\xBB\xBF\x01"); + success("utf-8", "\xEF\xBB\xBF\x01", "utf-8", "\xEF\xBB\xBF\x01"); + + success("utf-16be", "\x01\x02", "utf-16le", "\x02\x01"); + success("utf-16le", "\x02\x01", "utf-16be", "\x01\x02"); + success("utf-16be", "\xFE\xFF", "utf-16le", "\xFF\xFE"); + success("utf-16le", "\xFF\xFE", "utf-16be", "\xFE\xFF"); + success("utf-32be", "\x00\x00\x03\x04", "utf-32le", "\x04\x03\x00\x00"); + success("utf-32le", "\x04\x03\x00\x00", "utf-32be", "\x00\x00\x03\x04"); + success("utf-32be", "\x00\x00\xFF\xFF", "utf-16be", "\xFF\xFF"); + success("utf-16be", "\xFF\xFF", "utf-32be", "\x00\x00\xFF\xFF"); + success("utf-32be", "\x00\x01\x00\x00", "utf-16be", "\xD8\x00\xDC\x00"); + success("utf-16be", "\xD8\x00\xDC\x00", "utf-32be", "\x00\x01\x00\x00"); + success("utf-32be", "\x00\x10\xFF\xFF", "utf-16be", "\xDB\xFF\xDF\xFF"); + success("utf-16be", "\xDB\xFF\xDF\xFF", "utf-32be", "\x00\x10\xFF\xFF"); + eilseq("utf-32be", "\x00\x11\x00\x00", "utf-16be", ""); + eilseq("utf-16be", "\xDB\xFF\xE0\x00", "utf-32be", ""); + success("utf-8", "\xE3\x81\x82", "utf-16be", "\x30\x42"); + einval("utf-8", "\xE3", "utf-16be", ""); + } + + /* Japanese (CP932 CP20932 CP50220 CP50221 CP50222 CP51932) */ + if (check_enc("cp932", 932) + && check_enc("cp20932", 20932) && check_enc("euc-jp", 51932) + && check_enc("cp50220", 50220) && check_enc("cp50221", 50221) + && check_enc("cp50222", 50222) && check_enc("iso-2022-jp", 50221)) + { + /* Test the compatibility for each other Japanese codepage. + * And validate the escape sequence handling for iso-2022-jp. */ + success("utf-16be", "\xFF\x5E", "cp932", "\x81\x60"); + success("utf-16be", "\x30\x1C", "cp932", "\x81\x60"); + success("utf-16be", "\xFF\x5E", "cp932//nocompat", "\x81\x60"); + eilseq("utf-16be", "\x30\x1C", "cp932//nocompat", ""); + success("euc-jp", "\xA4\xA2", "utf-16be", "\x30\x42"); + einval("euc-jp", "\xA4\xA2\xA4", "utf-16be", "\x30\x42"); + eilseq("euc-jp", "\xA4\xA2\xFF\xFF", "utf-16be", "\x30\x42"); + success("cp932", "\x81\x60", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42"); + success("UTF-16BE", "\xFF\x5E", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42"); + eilseq("UTF-16BE", "\x30\x1C", "iso-2022-jp//nocompat", ""); + success("UTF-16BE", "\x30\x42\x30\x44", "iso-2022-jp", "\x1B\x24\x42\x24\x22\x24\x24\x1B\x28\x42"); + success("iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42", "UTF-16BE", "\xFF\x5E"); + } + + /* + * test for //translit + * U+FF41 (FULLWIDTH LATIN SMALL LETTER A) <-> U+0062 (LATIN SMALL LETTER A) + */ + eilseq("UTF-16BE", "\xFF\x41", "iso-8859-1", ""); + success("UTF-16BE", "\xFF\x41", "iso-8859-1//translit", "a"); + + /* + * TODO: + * Test for state after iconv() failed. + * Ensure iconv() error is safe and continuable. + */ + + return 0; +} + diff --git a/src/mod/endpoints/mod_opal/mod_opal.cpp b/src/mod/endpoints/mod_opal/mod_opal.cpp index 7d97c29b51..c844df7052 100644 --- a/src/mod/endpoints/mod_opal/mod_opal.cpp +++ b/src/mod/endpoints/mod_opal/mod_opal.cpp @@ -1,1517 +1,1517 @@ -/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / - * Soft-Switch Application - * - * Version: MPL 1.1 - * - * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) - * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * Contributor(s): - * Tuyan Ozipek (tuyanozipek@gmail.com) - * Lukasz Zwierko (lzwierko@gmail.com) - * Robert Jongbloed (robertj@voxlucida.com.au) - * - */ - -#include "mod_opal.h" -#include -#include -#if PTLIB_CHECK_VERSION(2,11,1) -#include -#endif -#include -#include - - -/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a - MAXIMUM audio frames per packet is negotiated, and there is no - requirement for the remote to actually send that many. So, in say GSM, we - negotiate up to 3 frames or 60ms of data and the remote actually sends one - (20ms) frame per packet. Perfectly legal but blows up the media handling - in FS. - - Eventually we will get around to bundling the packets, but not yet. This - compile flag will just force one frame/packet for all audio codecs. - */ -#define IMPLEMENT_MULTI_FAME_AUDIO 0 - - -static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, - switch_event_t *var_event, - switch_caller_profile_t *outbound_profile, - switch_core_session_t **new_session, - switch_memory_pool_t **pool, - switch_originate_flag_t flags, - switch_call_cause_t *cancel_cause); - - -static FSProcess *opal_process = NULL; - - -static PConstString const ModuleName("opal"); -static char const ConfigFile[] = "opal.conf"; -#define FS_PREFIX "fs" - - -static switch_io_routines_t opalfs_io_routines = { - /*.outgoing_channel */ create_outgoing_channel, - /*.read_frame */ FSConnection::read_audio_frame, - /*.write_frame */ FSConnection::write_audio_frame, - /*.kill_channel */ FSConnection::kill_channel, - /*.send_dtmf */ FSConnection::send_dtmf, - /*.receive_message */ FSConnection::receive_message, - /*.receive_event */ FSConnection::receive_event, - /*.state_change */ FSConnection::state_change, - /*.read_video_frame */ FSConnection::read_video_frame, - /*.write_video_frame */ FSConnection::write_video_frame -}; - -static switch_state_handler_table_t opalfs_event_handlers = { - /*.on_init */ FSConnection::on_init, - /*.on_routing */ FSConnection::on_routing, - /*.on_execute */ FSConnection::on_execute, - /*.on_hangup */ FSConnection::on_hangup, - /*.on_exchange_media */ FSConnection::on_exchange_media, - /*.on_soft_execute */ FSConnection::on_soft_execute, - /*.on_consume_media*/ NULL, - /*.on_hibernate*/ NULL, - /*.on_reset*/ NULL, - /*.on_park*/ NULL, - /*.on_reporting*/ NULL, - /*.on_destroy*/ FSConnection::on_destroy -}; - - -SWITCH_BEGIN_EXTERN_C -/*******************************************************************************/ - -SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load); -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown); -SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL); - -SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) -{ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n"); - - /* Prevent the loading of OPAL codecs via "plug ins", this is a directory - full of DLLs that will be loaded automatically. */ - (void)putenv((char *)"PTLIBPLUGINDIR=/no/thanks"); - - - *module_interface = switch_loadable_module_create_module_interface(pool, modname); - if (!*module_interface) { - return SWITCH_STATUS_MEMERR; - } - - opal_process = new FSProcess(); - if (opal_process == NULL) { - return SWITCH_STATUS_MEMERR; - } - - if (opal_process->Initialise(*module_interface)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n"); - //unloading causes a seg in linux - //return SWITCH_STATUS_UNLOAD; - return SWITCH_STATUS_SUCCESS; - } - - delete opal_process; - opal_process = NULL; - return SWITCH_STATUS_FALSE; -} - - -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) -{ - delete opal_process; - opal_process = NULL; - return SWITCH_STATUS_SUCCESS; -} - -SWITCH_END_EXTERN_C -/*******************************************************************************/ - -/////////////////////////////////////////////////////////////////////// - -#if PTRACING - -class FSTrace : public std::ostream -{ -private: - class Buffer : public std::stringbuf - { - virtual int sync() - { - std::string s = str(); - if (s.empty()) - return 0; - - //Due to explicit setting of flags we know exactly what we are getting - #define THREAD_ID_INDEX 2 - #define FILE_NAME_INDEX 3 - #define FILE_LINE_INDEX 4 -#if PTLIB_CHECK_VERSION(2,11,1) - #define CONTEXT_ID_REGEX "([0-9]+|- - - - - - -)\t" - #define LOG_PRINTF_FORMAT "{%s,%s} %s" - #define FULL_TEXT_INDEX 6 -#else - #define CONTEXT_ID_REGEX - #define LOG_PRINTF_FORMAT "{%s} %s" - #define FULL_TEXT_INDEX 5 -#endif - PStringArray fields(7); - static PRegularExpression logRE("^([0-9]+)\t *(.+)\t *([^(]+)\\(([0-9]+)\\)\t"CONTEXT_ID_REGEX"(.*)", - PRegularExpression::Extended); - if (!logRE.Execute(s.c_str(), fields)) { - fields[1] = "4"; - fields[THREAD_ID_INDEX] = "unknown"; - fields[FILE_NAME_INDEX] = __FILE__; - fields[FILE_LINE_INDEX] = __LINE__; - fields[FULL_TEXT_INDEX] = s; - } - - switch_log_level_t level; - switch (fields[1].AsUnsigned()) { - case 0 : - level = SWITCH_LOG_ALERT; - break; - case 1 : - level = SWITCH_LOG_ERROR; - break; - case 2 : - level = SWITCH_LOG_WARNING; - break; - case 3 : - level = SWITCH_LOG_INFO; - break; - default : - level = SWITCH_LOG_DEBUG; - break; - } - - fields[4].Replace("\t", " ", true); -#if PTLIB_CHECK_VERSION(2,11,1) - fields[5].Replace("- - - - - - -", "-"), -#endif - switch_log_printf(SWITCH_CHANNEL_ID_LOG, - fields[FILE_NAME_INDEX], - "PTLib-OPAL", - fields[FILE_LINE_INDEX].AsUnsigned(), - NULL, - level, - LOG_PRINTF_FORMAT, - fields[THREAD_ID_INDEX].GetPointer(), -#if PTLIB_CHECK_VERSION(2,11,1) - fields[5].GetPointer(), -#endif - fields[FULL_TEXT_INDEX].GetPointer()); - - // Reset string - str(std::string()); - return 0; - } - } buffer; - -public: - FSTrace() - : ostream(&buffer) - { - } -}; - -#endif // PTRACING - - -/////////////////////////////////////////////////////////////////////// - -FSProcess::FSProcess() - : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1) - , m_manager(NULL) -{ -} - - -FSProcess::~FSProcess() -{ - delete m_manager; -#if PTRACING - PTrace::SetStream(NULL); // This will delete the FSTrace object -#endif -} - - -bool FSProcess::Initialise(switch_loadable_module_interface_t *iface) -{ - m_manager = new FSManager(); - return m_manager != NULL && m_manager->Initialise(iface); -} - - -/////////////////////////////////////////////////////////////////////// - -FSManager::FSManager() - : m_context("default") - , m_dialplan("XML") -{ - // These are deleted by the OpalManager class, no need to have destructor - m_h323ep = new H323EndPoint(*this); - m_iaxep = new IAX2EndPoint(*this); - m_fsep = new FSEndPoint(*this); -} - - -bool FSManager::Initialise(switch_loadable_module_interface_t *iface) -{ - ReadConfig(false); - - m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE); - m_FreeSwitch->interface_name = ModuleName; - m_FreeSwitch->io_routines = &opalfs_io_routines; - m_FreeSwitch->state_handler = &opalfs_event_handlers; - - silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection; - - if (m_listeners.empty()) { - m_h323ep->StartListener(""); - } else { - for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { - if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) { - PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name); - } - } - } - - AddRouteEntry("h323:.* = "FS_PREFIX":"); // config option for direct routing - AddRouteEntry("iax2:.* = "FS_PREFIX":"); // config option for direct routing - AddRouteEntry(FS_PREFIX":.* = h323:"); // config option for direct routing - - // Make sure all known codecs are instantiated, - // these are ones we know how to translate into H.323 capabilities - GetOpalG728(); - GetOpalG729(); - GetOpalG729A(); - GetOpalG729B(); - GetOpalG729AB(); - GetOpalG7231_6k3(); - GetOpalG7231_5k3(); - GetOpalG7231A_6k3(); - GetOpalG7231A_5k3(); - GetOpalGSM0610(); - GetOpalGSMAMR(); - GetOpaliLBC(); - -#if !IMPLEMENT_MULTI_FAME_AUDIO - OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats(); - for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) { - if (it->GetMediaType() == OpalMediaType::Audio()) { - int ms_per_frame = it->GetFrameTime()/it->GetTimeUnits(); - int frames_in_20_ms = (ms_per_frame+19)/ms_per_frame; - it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), frames_in_20_ms); - it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), frames_in_20_ms); - OpalMediaFormat::SetRegisteredMediaFormat(*it); - PTRACE(4, "mod_opal\tSet " << *it << " to " << frames_in_20_ms << "frames/packet"); - } - } -#endif // IMPLEMENT_MULTI_FAME_AUDIO - - OpalMediaFormat t38 = OpalT38; - t38.SetOptionBoolean("UDPTL-Raw-Mode", true); - OpalMediaFormat::SetRegisteredMediaFormat(t38); - - if (!m_gkAddress.IsEmpty()) { - if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface)) - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n", - (const char *)m_h323ep->GetGatekeeper()->GetName()); - else - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n", - (const char *)m_gkAddress, - (const char *)m_gkIdentifer, - (const char *)m_gkInterface); - } - - return TRUE; -} - - -switch_status_t FSManager::ReadConfig(int reload) -{ - switch_event_t *request_params = NULL; - switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS); - switch_assert(request_params); - switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil("")); - - switch_xml_t cfg; - switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params); - if (xml == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile); - return SWITCH_STATUS_FALSE; - } - - switch_xml_t xmlSettings = switch_xml_child(cfg, "settings"); - if (xmlSettings) { - for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { - PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); - PConstString const val(switch_xml_attr_soft(xmlParam, "value")); - - if (var == "context") { - m_context = val; - } else if (var == "dialplan") { - m_dialplan = val; - } else if (var == "codec-prefs") { - m_codecPrefs = val; - } else if (var == "disable-transcoding") { - m_disableTranscoding = switch_true(val); - } else if (var == "dtmf-type") { - if (val == "string") - m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsString); - else if (val == "signal") - m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsTone); - else if (val == "rfc2833") - m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsRFC2833); - else if (val == "in-band") - m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputInBand); - } else if (var == "jitter-size") { - SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds - } else if (var == "gk-address") { - m_gkAddress = val; - } else if (var == "gk-identifer") { - m_gkIdentifer = val; - } else if (var == "gk-interface") { - m_gkInterface = val; -#if PTRACING - } else if (var == "trace-level") { - unsigned level = val.AsUnsigned(); - if (level > 0) { - PTrace::SetLevel(level); - PTrace::ClearOptions(0xffffffff); // Everything off - PTrace::SetOptions( // Except these - PTrace::TraceLevel|PTrace::FileAndLine|PTrace::Thread -#if PTLIB_CHECK_VERSION(2,11,1) - |PTrace::ContextIdentifier -#endif - ); - PTrace::SetStream(new FSTrace); - } -#endif - } - } - } - - switch_xml_t xmlListeners = switch_xml_child(cfg, "listeners"); - if (xmlListeners != NULL) { - for (switch_xml_t xmlListener = switch_xml_child(xmlListeners, "listener"); xmlListener != NULL; xmlListener = xmlListener->next) { - - m_listeners.push_back(FSListener()); - FSListener & listener = m_listeners.back(); - - listener.m_name = switch_xml_attr_soft(xmlListener, "name"); - if (listener.m_name.IsEmpty()) - listener.m_name = "unnamed"; - - for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { - PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); - PConstString const val(switch_xml_attr_soft(xmlParam, "value")); - if (var == "h323-ip") - listener.m_address = val; - else if (var == "h323-port") - listener.m_port = (uint16_t)val.AsUnsigned(); - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name); - } - } - - switch_event_destroy(&request_params); - - if (xml) - switch_xml_free(xml); - - return SWITCH_STATUS_SUCCESS; -} - - -static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, - switch_event_t *var_event, - switch_caller_profile_t *outbound_profile, - switch_core_session_t **new_session, - switch_memory_pool_t **pool, - switch_originate_flag_t flags, - switch_call_cause_t *cancel_cause) -{ - if (opal_process == NULL) - return SWITCH_CAUSE_CRASH; - - FSConnection::outgoing_params params; - params.var_event = var_event; - params.outbound_profile = outbound_profile; - params.new_session = new_session; - params.pool = pool; - params.flags = flags; - params.cancel_cause = cancel_cause; - params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; - - if (opal_process->GetManager().SetUpCall(FS_PREFIX":", outbound_profile->destination_number, ¶ms) != NULL) - return SWITCH_CAUSE_SUCCESS; - - if (*new_session != NULL) - switch_core_session_destroy(new_session); - return params.fail_cause; -} - - -/////////////////////////////////////////////////////////////////////// - -FSEndPoint::FSEndPoint(FSManager & manager) - : OpalLocalEndPoint(manager, FS_PREFIX) - , m_manager(manager) -{ - PTRACE(4, "mod_opal\tFSEndPoint created."); -} - - -OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions) -{ - return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData); -} - - -/////////////////////////////////////////////////////////////////////// - -FSConnection::FSConnection(OpalCall & call, - FSEndPoint & endpoint, - unsigned options, - OpalConnection::StringOptions* stringOptions, - outgoing_params * params) - : OpalLocalConnection(call, endpoint, NULL, options, stringOptions) - , m_endpoint(endpoint) - , m_fsSession(NULL) - , m_fsChannel(NULL) - , m_flushAudio(false) - , m_udptl(false) -{ - memset(&m_read_timer, 0, sizeof(m_read_timer)); - memset(&m_read_codec, 0, sizeof(m_read_codec)); - memset(&m_write_codec, 0, sizeof(m_write_codec)); - memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer)); - memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec)); - memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec)); - memset(&m_dummy_frame, 0, sizeof(m_dummy_frame)); - m_dummy_frame.flags = SFF_CNG; - - if (params != NULL) { - // If we fail, this is the cause - params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - - if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), - SWITCH_CALL_DIRECTION_OUTBOUND, params->flags, params->pool)) == NULL) { - PTRACE(1, "mod_opal\tCannot create session for outgoing call."); - return; - } - } - else { - if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), - SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) { - PTRACE(1, "mod_opal\tCannot create session for incoming call."); - return; - } - } - - if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) { - switch_core_session_destroy(&m_fsSession); - return; - } - - switch_core_session_set_private(m_fsSession, this); - SafeReference(); // Make sure cannot be deleted until on_destroy() - - if (params != NULL) { - switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile); - switch_channel_set_caller_profile(m_fsChannel, caller_profile); - SetLocalPartyName(caller_profile->caller_id_number); - SetDisplayName(caller_profile->caller_id_name); - - *params->new_session = m_fsSession; - } - - switch_channel_set_state(m_fsChannel, CS_INIT); -} - - -bool FSConnection::OnOutgoingSetUp() -{ - if (m_fsSession == NULL || m_fsChannel == NULL) { - PTRACE(1, "mod_opal\tSession request failed."); - return false; - } - - // Transfer FS caller_id_number & caller_id_name from the FSConnection - // to the protocol connection (e.g. H.323) so gets sent correctly - // in outgoing packets - PSafePtr proto = GetOtherPartyConnection(); - if (proto == NULL) { - PTRACE(1, "mod_opal\tNo protocol connection in call."); - return false; - } - - proto->SetLocalPartyName(GetLocalPartyName()); - proto->SetDisplayName(GetDisplayName()); - - switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL()); - return true; -} - - -bool FSConnection::OnIncoming() -{ - if (m_fsSession == NULL || m_fsChannel == NULL) { - PTRACE(1, "mod_opal\tSession request failed."); - return false; - } - - switch_core_session_add_stream(m_fsSession, NULL); - - PURL url = GetRemotePartyURL(); - switch_caller_profile_t *caller_profile = switch_caller_profile_new( - switch_core_session_get_pool(m_fsSession), - url.GetUserName(), /** username */ - m_endpoint.GetManager().GetDialPlan(), /** dial plan */ - GetRemotePartyName(), /** caller_id_name */ - GetRemotePartyNumber(), /** caller_id_number */ - url.GetHostName(), /** network addr */ - NULL, /** ANI */ - NULL, /** ANI II */ - NULL, /** RDNIS */ - ModuleName, /** source */ - m_endpoint.GetManager().GetContext(), /** set context */ - GetCalledPartyNumber() /** destination_number */ - ); - if (caller_profile == NULL) { - PTRACE(1, "mod_opal\tCould not create caller profile"); - return false; - } - - PTRACE(4, "mod_opal\tCreated switch caller profile:\n" - " username = " << caller_profile->username << "\n" - " dialplan = " << caller_profile->dialplan << "\n" - " caller_id_name = " << caller_profile->caller_id_name << "\n" - " caller_id_number = " << caller_profile->caller_id_number << "\n" - " network_addr = " << caller_profile->network_addr << "\n" - " source = " << caller_profile->source << "\n" - " context = " << caller_profile->context << "\n" - " destination_number= " << caller_profile->destination_number); - switch_channel_set_caller_profile(m_fsChannel, caller_profile); - - switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number); - - if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) { - PTRACE(1, "mod_opal\tCould not launch session thread"); - switch_core_session_destroy(&m_fsSession); - m_fsChannel = NULL; - return false; - } - - return true; -} - - -void FSConnection::OnEstablished() -{ - OpalLocalConnection::OnEstablished(); - - if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_OUTBOUND) { - PTRACE(4, "mod_opal\tOnEstablished for outbound call, checking for media"); - if (GetMediaStream(OpalMediaType::Audio(), true) != NULL && GetMediaStream(OpalMediaType::Audio(), false) != NULL) { - PTRACE(3, "mod_opal\tOnEstablished for outbound call, making call answered"); - switch_channel_mark_answered(m_fsChannel); - } - } -} - - -void FSConnection::OnReleased() -{ - m_rxAudioOpened.Signal(); // Just in case - m_txAudioOpened.Signal(); - - if (m_fsChannel != NULL) { - PTRACE(3, "mod_opal\tHanging up FS side"); - switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931); - } - - OpalLocalConnection::OnReleased(); -} - - -PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia) -{ - if (PAssertNULL(m_fsChannel) == NULL) - return false; - - switch_channel_mark_ring_ready(m_fsChannel); - return OpalLocalConnection::SetAlerting(calleeName, withMedia); -} - - -PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration) -{ - if (PAssertNULL(m_fsChannel) == NULL) - return false; - - switch_dtmf_t dtmf = { tone, duration }; - PTRACE(4, "mod_opal\tSending DTMF to FS: tone=" << tone << ", duration=" << duration); - return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS; -} - - -OpalMediaFormatList FSConnection::GetMediaFormats() const -{ - if (m_switchMediaFormats.IsEmpty()) { - const_cast(this)->SetCodecs(); - } - - return m_switchMediaFormats; -} - - -void FSConnection::SetCodecs() -{ - int numCodecs = 0; - const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; - - PString codec_string = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"); - if (codec_string.IsEmpty()) { - codec_string = switch_channel_get_variable(m_fsChannel, "codec_string"); - if (codec_string.IsEmpty()) { - codec_string = m_endpoint.GetManager().GetCodecPrefs(); - if (codec_string.IsEmpty()) { - numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0])); - for (int i = 0; i < numCodecs; i++) { - if (i > 0) - codec_string += ','; - codec_string += codecs[i]->iananame; - } - PTRACE(4, "mod_opal\tDefault to all loaded codecs=" << codec_string); - } - else { - PTRACE(4, "mod_opal\tSettings codec-prefs=" << codec_string); - } - } - else { - PTRACE(4, "mod_opal\tChannel codec_string=" << codec_string); - } - - PString orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE); - if (!orig_codec.IsEmpty()) { - if (m_endpoint.GetManager().GetDisableTranscoding()) { - codec_string = orig_codec; - PTRACE(4, "mod_opal\tNo transcoding, forced to originator codec=" << orig_codec); - } - else { - codec_string.Splice(orig_codec+',', 0); - PTRACE(4, "mod_opal\tSetting preference to originator codec=" << orig_codec); - } - } - } - else { - PTRACE(4, "mod_opal\tChannel absolute_codec_string=" << codec_string); - } - - { - char *codec_order[SWITCH_MAX_CODECS]; - int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS); - numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last); - } - - for (int i = 0; i < numCodecs; i++) { - const switch_codec_implementation_t *codec = codecs[i]; - - // See if we have a match by PayloadType/rate/name - OpalMediaFormat switchFormat((RTP_DataFrame::PayloadTypes)codec->ianacode, - codec->samples_per_second, - codec->iananame); - if (!switchFormat.IsValid()) { - // See if we have a match by name alone - switchFormat = codec->iananame; - if (!switchFormat.IsValid()) { - PTRACE(2, "mod_opal\tCould not match FS codec " - << codec->iananame << '@' << codec->samples_per_second - << " (pt=" << (unsigned)codec->ianacode << ")" - " to an OPAL media format."); - continue; - } - } - - PTRACE(4, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat); - -#if IMPLEMENT_MULTI_FAME_AUDIO - // Did we match or create a new media format? - if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) { - // Calculate frames per packet, do not use codec->codec_frames_per_packet as that field - // has slightly different semantics when used in streamed codecs such as G.711 - int fpp = codec->samples_per_packet/switchFormat.GetFrameTime(); - - /* Set the frames/packet to maximum of what is in the FS table. The OPAL negotiations will - drop the value from there. This might fail if there are "holes" in the FS table, e.g. - if for some reason G.723.1 has 30ms and 90ms but not 60ms, then the OPAL negotiations - could end up with 60ms and the codec cannot be created. The "holes" are unlikely in - all but streamed codecs such as G.711, where it is theoretically possible for OPAL to - come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these - scenarios sufficiently rare that we can safely ignore them ... for now. */ - - if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) { - switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp); - } - - if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) { - switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp); - } - } -#endif // IMPLEMENT_MULTI_FAME_AUDIO - - m_switchMediaFormats += switchFormat; - } - -#if HAVE_T38 - OpalMediaFormat t38 = OpalT38; - - /* We need to have a T.38 options for TCS, but may be before the - spandsp_mod has set it us. So, if not, we actually give to spandsp_mod. */ - switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, "t38_options"); - if (t38_options == NULL) - SetT38OptionsFromMediaFormat(t38, "_preconfigured_t38_options"); - else { - t38.SetOptionInteger("T38FaxVersion", t38_options->T38FaxVersion); - t38.SetOptionInteger("T38MaxBitRate", t38_options->T38MaxBitRate); - t38.SetOptionBoolean("T38FaxFillBitRemoval", t38_options->T38FaxFillBitRemoval); - t38.SetOptionBoolean("T38FaxTranscodingMMR", t38_options->T38FaxTranscodingMMR); - t38.SetOptionBoolean("T38FaxTranscodingJBIG", t38_options->T38FaxTranscodingJBIG); - t38.SetOptionValue("T38FaxRateManagement", t38_options->T38FaxRateManagement); - t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxBuffer); - t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxDatagram); - t38.SetOptionValue("T38FaxUdpEC", t38_options->T38FaxUdpEC); - } - - m_switchMediaFormats += t38; -#endif // HAVE_T38 -} - - -OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource) -{ - return new FSMediaStream(*this, mediaFormat, sessionID, isSource); -} - - -void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch) -{ - OpalConnection::OnPatchMediaStream(isSource, patch); - - if (PAssertNULL(m_fsChannel) == NULL) - return; - - if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) - return; - - if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { - PTRACE(4, "mod_opal\tOnPatchMediaStream for inbound call, flagging media opened"); - if (isSource) - m_rxAudioOpened.Signal(); - else - m_txAudioOpened.Signal(); - } - else { - PTRACE(4, "mod_opal\tOnPatchMediaStream for outbound call, checking media"); - if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) { - // Have open media in both directions. - if (IsEstablished()) { - PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call answered"); - switch_channel_mark_answered(m_fsChannel); - } - else if (!IsReleased()) { - PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call pre-answered"); - switch_channel_mark_pre_answered(m_fsChannel); - } - } - } -} - - -switch_status_t FSConnection::on_init() -{ - if (PAssertNULL(m_fsChannel) == NULL) - return SWITCH_STATUS_FALSE; - - PTRACE(4, "mod_opal\tStarted routing for connection " << *this); - - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_routing() -{ - if (PAssertNULL(m_fsChannel) == NULL) - return SWITCH_STATUS_FALSE; - - PTRACE(4, "mod_opal\tRouting connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_execute() -{ - if (PAssertNULL(m_fsChannel) == NULL) - return SWITCH_STATUS_FALSE; - - PTRACE(4, "mod_opal\tExecuting connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_destroy() -{ - PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this); - - m_fsChannel = NULL; // Will be destroyed by FS, so don't use it any more. - - switch_core_codec_destroy(&m_read_codec); - switch_core_codec_destroy(&m_write_codec); - switch_core_codec_destroy(&m_vid_read_codec); - switch_core_codec_destroy(&m_vid_write_codec); - switch_core_timer_destroy(&m_read_timer); - switch_core_timer_destroy(&m_vid_read_timer); - - switch_core_session_set_private(m_fsSession, NULL); - SafeDereference(); - - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_hangup() -{ - if (PAssertNULL(m_fsChannel) == NULL) - return SWITCH_STATUS_FALSE; - - /* if this is still here it was our idea to hangup not opal's */ - ClearCallSynchronous(NULL, H323TranslateToCallEndReason( - (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX)); - - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_exchange_media() -{ - PTRACE(4, "mod_opal\tExchanging media on connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::on_soft_execute() -{ - PTRACE(4, "mod_opal\tSoft execute on connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::kill_channel(int sig) -{ - switch (sig) { - case SWITCH_SIG_KILL: - PTRACE(4, "mod_opal\tSignal KILL received on connection " << *this); - m_rxAudioOpened.Signal(); - m_txAudioOpened.Signal(); - CloseMediaStreams(); - break; - - case SWITCH_SIG_BREAK: - PTRACE(4, "mod_opal\tSignal BREAK received on connection " << *this); - break; - - default: - PTRACE(4, "mod_opal\tSignal " << sig << " received on connection " << *this); - break; - } - - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf) -{ - PTRACE(4, "mod_opal\tReceived DTMF from FS: tone=" << dtmf->digit << ", duration=" << dtmf->duration); - OnUserInputTone(dtmf->digit, dtmf->duration); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg) -{ - if (PAssertNULL(m_fsChannel) == NULL) - return SWITCH_STATUS_FALSE; - - switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_RINGING: - case SWITCH_MESSAGE_INDICATE_PROGRESS: - case SWITCH_MESSAGE_INDICATE_ANSWER: - case SWITCH_MESSAGE_INDICATE_DEFLECT: - if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { - switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel); - if (profile != NULL && profile->caller_extension != NULL) - { - PSafePtr other = GetOtherPartyConnection(); - if (other != NULL) { - other->SetLocalPartyName(profile->caller_extension->extension_number); - other->SetDisplayName(profile->caller_extension->extension_name); - } - SetLocalPartyName(profile->caller_extension->extension_number); - SetDisplayName(profile->caller_extension->extension_name); - } - } - else { - return SWITCH_STATUS_FALSE; - } - break; - - default: - break; - } - - switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_BRIDGE: - case SWITCH_MESSAGE_INDICATE_UNBRIDGE: - case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: - m_flushAudio = true; - break; - - case SWITCH_MESSAGE_INDICATE_RINGING: - AlertingIncoming(); - break; - - case SWITCH_MESSAGE_INDICATE_PROGRESS: - AutoStartMediaStreams(); - AlertingIncoming(); - - if (!WaitForMedia()) - return SWITCH_STATUS_FALSE; - - if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { - switch_channel_mark_pre_answered(m_fsChannel); - } - break; - - case SWITCH_MESSAGE_INDICATE_ANSWER: - AcceptIncoming(); - - if (!WaitForMedia()) - return SWITCH_STATUS_FALSE; - - if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) { - switch_channel_mark_answered(m_fsChannel); - } - break; - - case SWITCH_MESSAGE_INDICATE_DEFLECT: - ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection()); - break; - -#if HAVE_T38 - case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA: - { - PTRACE(2, "mod_opal\tRequesting switch to T.38"); - PSafePtr other = GetOtherPartyConnection(); - if (other != NULL && other->SwitchFaxMediaStreams(true)) - switch_channel_set_flag(m_fsChannel, CF_REQ_MEDIA); - else { - PTRACE(1, "mod_opal\tMode change request to T.38 failed"); - } - break; - } - - case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION: - PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_T38_DESCRIPTION"); - break; - - case SWITCH_MESSAGE_INDICATE_UDPTL_MODE: - PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_UDPTL_MODE"); - m_udptl = true; - break; -#endif // HAVE_T38 - - default: - PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this); - } - - return SWITCH_STATUS_SUCCESS; -} - - -bool FSConnection::WaitForMedia() -{ - PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this); - m_rxAudioOpened.Wait(); - m_txAudioOpened.Wait(); - - if (IsReleased()) { - // Call got aborted - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n"); - return false; - } - - PTRACE(3, "mod_opal\tMedia started on connection " << *this); - return true; -} - - -#if HAVE_T38 -void FSConnection::SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname) -{ - switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, varname); - if (t38_options == NULL) - t38_options = (switch_t38_options_t *)switch_core_session_alloc(m_fsSession, sizeof(switch_t38_options_t)); - - PString value; - mediaFormat.GetOptionValue("T38FaxRateManagement", value); - t38_options->T38FaxRateManagement = switch_core_session_strdup(m_fsSession, value); - - mediaFormat.GetOptionValue("T38FaxUdpEC", value); - t38_options->T38FaxUdpEC = switch_core_session_strdup(m_fsSession, value); - - t38_options->T38MaxBitRate = mediaFormat.GetOptionInteger("T38MaxBitRate", 9600); - t38_options->T38FaxMaxBuffer = mediaFormat.GetOptionInteger("T38FaxMaxBuffer", 2000); - t38_options->T38FaxMaxDatagram = mediaFormat.GetOptionInteger("T38FaxMaxDatagram", 528); - - t38_options->T38FaxFillBitRemoval = mediaFormat.GetOptionBoolean("T38FaxFillBitRemoval") ? SWITCH_TRUE : SWITCH_FALSE; - t38_options->T38FaxTranscodingMMR = mediaFormat.GetOptionBoolean("T38FaxTranscodingMMR") ? SWITCH_TRUE : SWITCH_FALSE; - t38_options->T38FaxTranscodingJBIG = mediaFormat.GetOptionBoolean("T38FaxTranscodingJBIG") ? SWITCH_TRUE : SWITCH_FALSE; - - t38_options->T38VendorInfo = switch_core_session_strdup(m_fsSession, mediaFormat.GetOptionString("T38VendorInfo")); - - //t38_options->remote_ip = switch_core_session_strdup(session, mediaFormat.something); - //t38_options->remote_port = mediaFormat.something; - - switch_channel_set_private(m_fsChannel, varname, t38_options); - PTRACE(3, "mod_opal\tSet " << varname); -} - - -void FSConnection::OnSwitchedT38(bool toT38, bool success) -{ - if (toT38 && success && IndicateSwitchedT38()) { - PTRACE(3, "mod_opal\tMode change request to T.38 succeeded"); - } - else { - AbortT38(); - } -} - - -void FSConnection::OnSwitchingT38(bool toT38) -{ - if (toT38 && IndicateSwitchedT38()) { - PTRACE(3, "mod_opal\tMode change request to T.38 started"); - } - else { - AbortT38(); - } -} - - -void FSConnection::AbortT38() -{ - PTRACE(3, "mod_opal\tMode change request to T.38 failed"); - switch_channel_set_private(m_fsChannel, "t38_options", NULL); - switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38); - switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38_REQ); - switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38_FAIL); -} - - -bool FSConnection::IndicateSwitchedT38() -{ - PSafePtr other = GetOtherPartyConnection(); - if (other == NULL) { - PTRACE(3, "mod_opal\tCan't change to T.38, no other connection"); - return false; - } - - OpalMediaFormatList otherFormats = other->GetMediaFormats(); - OpalMediaFormatList::const_iterator t38 = otherFormats.FindFormat(OpalT38); - if (t38 == otherFormats.end()) { - PTRACE(3, "mod_opal\tCan't change to T.38, no remote capability"); - return false; - } - - SetT38OptionsFromMediaFormat(*t38, "t38_options"); - - switch_channel_set_variable(m_fsChannel, "has_t38", "true"); - switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38); - - switch_channel_execute_on(m_fsChannel, "opal_execute_on_t38"); - switch_channel_api_on(m_fsChannel, "opal_api_on_t38"); - return true; -} -#endif // HAVE_T38 - - -switch_status_t FSConnection::receive_event(switch_event_t *event) -{ - PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::state_change() -{ - PTRACE(4, "mod_opal\tState changed on connection " << *this); - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id) -{ - return read_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags); -} - - -switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id) -{ - return write_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags); -} - - -switch_status_t FSConnection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id) -{ - return read_frame(OpalMediaType::Video(), frame, flag); -} - - -switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id) -{ - return write_frame(OpalMediaType::Video(), frame, flag); -} - - -switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags) -{ - if (!ownerCall.IsSwitchingT38()) { - PSafePtr stream = PSafePtrCast (GetMediaStream(mediaType, false)); - if (stream != NULL) - return stream->read_frame(frame, flags); - - PTRACE(2, "mod_opal\tNo stream for read of " << mediaType); - } - - // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS - *frame = &m_dummy_frame; - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags) -{ - // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS - if (ownerCall.IsSwitchingT38()) - return SWITCH_STATUS_SUCCESS; - - PSafePtr stream = PSafePtrCast(GetMediaStream(mediaType, true)); - if (stream != NULL) - return stream->write_frame(frame, flags); - - PTRACE(2, "mod_opal\tNo stream for write of " << mediaType); - return SWITCH_STATUS_SUCCESS; -} - - -/////////////////////////////////////////////////////////////////////// - -FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource) - : OpalMediaStream(conn, mediaFormat, sessionID, isSource) - , m_connection(conn) - , m_switchTimer(NULL) - , m_switchCodec(NULL) - , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE) -{ - memset(&m_readFrame, 0, sizeof(m_readFrame)); -} - - -PBoolean FSMediaStream::Open() -{ - if (IsOpen()) { - return true; - } - - switch_core_session_t *fsSession = m_connection.GetSession(); - switch_channel_t *fsChannel = m_connection.GetChannel(); - if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL) - return false; - - bool isAudio; - OpalMediaType mediaType = mediaFormat.GetMediaType(); - if (mediaType == OpalMediaType::Audio()) - isAudio = true; - else if (mediaType == OpalMediaType::Video()) - isAudio = false; -#if HAVE_T38 - else if (mediaType == OpalMediaType::Fax()) { - m_readFrame.flags = SFF_UDPTL_PACKET|SFF_PROXY_PACKET; - return OpalMediaStream::Open(); - } -#endif - else { - PTRACE(1, "mod_opal\tUnsupported media type: " << mediaType); - return false; - } - - int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits(); - - if (IsSink()) { - m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec; - m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer; - m_readFrame.codec = m_switchCodec; - m_readFrame.rate = mediaFormat.GetClockRate(); - } else { - m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec; - } - - // The following is performed on two different instances of this object. - if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP - mediaFormat.GetClockRate(), ptime, 1, // Channels - SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings - switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { - // Could not select a codecs using negotiated frames/packet, so try using default. - if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP - mediaFormat.GetClockRate(), 0, 1, // Channels - SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings - switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { - PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) - << " cannot initialise " << (IsSink()? "read" : "write") << ' ' - << mediaType << " codec " << mediaFormat << " for connection " << *this); - switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); - return false; - } - PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel) - << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' ' - << mediaType << " codec " << mediaFormat << " for connection " << *this); - } - - if (IsSink()) { - if (isAudio) { - switch_core_session_set_read_codec(fsSession, m_switchCodec); - if (switch_core_timer_init(m_switchTimer, - "soft", - m_switchCodec->implementation->microseconds_per_packet / 1000, - m_switchCodec->implementation->samples_per_packet, - switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { - PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) - << " timer init failed on " << (IsSink()? "read" : "write") << ' ' - << mediaType << " codec " << mediaFormat << " for connection " << *this); - switch_core_codec_destroy(m_switchCodec); - m_switchCodec = NULL; - return false; - } - } else { - switch_core_session_set_video_read_codec(fsSession, m_switchCodec); - switch_channel_set_flag(fsChannel, CF_VIDEO); - } - } else { - if (isAudio) { - switch_core_session_set_write_codec(fsSession, m_switchCodec); - } else { - switch_core_session_set_video_write_codec(fsSession, m_switchCodec); - switch_channel_set_flag(fsChannel, CF_VIDEO); - } - } - - PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel) - << " initialised " << (IsSink()? "read" : "write") << ' ' - << mediaType << " codec " << mediaFormat << " for connection " << *this); - - return OpalMediaStream::Open(); -} - - -void FSMediaStream::InternalClose() -{ -} - - -PBoolean FSMediaStream::IsSynchronous() const -{ - return true; -} - - -PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const -{ - return false; -} - - -int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const -{ - if (!IsOpen()) { - PTRACE(1, "mod_opal\tNot open!"); - return -1; - } - - if (!m_connection.IsChannelReady()) { - PTRACE(1, "mod_opal\tChannel not ready!"); - return -1; - } - - // We make referenced copy of pointer so can't be deleted out from under us - mediaPatch = m_mediaPatch; - if (mediaPatch == NULL) { - /*There is a race here... sometimes we make it here and m_mediaPatch is NULL - if we wait it shows up in 1ms, maybe there is a better way to wait. */ - PTRACE(2, "mod_opal\tPatch not ready!"); - return 1; - } - - return 0; -} - - -switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags) -{ - *frame = &m_readFrame; - m_readFrame.flags |= SFF_CNG; - - PatchPtr mediaPatch; - switch (StartReadWrite(mediaPatch)) { - case -1 : - return SWITCH_STATUS_FALSE; - case 1 : - return SWITCH_STATUS_SUCCESS; - } - - if (m_connection.NeedFlushAudio()) { - mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer - m_readRTP.SetPayloadSize(0); - } else { - if (m_switchCodec != NULL) { - m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet); - } - - if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) { - PTRACE(1, "mod_opal\tread_frame: no source data!"); - return SWITCH_STATUS_FALSE; - } - } - - if (m_switchTimer != NULL) { - switch_core_timer_next(m_switchTimer); - } - - if (m_switchCodec != NULL) { - if (!switch_core_codec_ready(m_switchCodec)) { - PTRACE(1, "mod_opal\tread_frame: codec not ready!"); - return SWITCH_STATUS_FALSE; - } - } - - if (switch_test_flag(&m_readFrame, SFF_UDPTL_PACKET)) { - m_readFrame.flags &= ~SFF_CNG; - m_readFrame.packet = m_readRTP.GetPayloadPtr(); - m_readFrame.packetlen = m_readRTP.GetPayloadSize(); - return SWITCH_STATUS_SUCCESS; - } - - if (switch_test_flag(&m_readFrame, SFF_RAW_RTP)) { - m_readFrame.flags &= ~SFF_CNG; - m_readFrame.packet = m_readRTP.GetPointer(); - m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readRTP.GetPayloadSize(); - return SWITCH_STATUS_SUCCESS; - } - -#if IMPLEMENT_MULTI_FAME_AUDIO - // Repackage frames in incoming packet to agree with what FS expects. - // Not implmented yet!!!!!!!!! - // Cheating and only supporting one frame per packet -#endif - - m_readFrame.buflen = m_readRTP.GetSize(); - m_readFrame.data = m_readRTP.GetPayloadPtr(); - m_readFrame.datalen = m_readRTP.GetPayloadSize(); - m_readFrame.timestamp = m_readRTP.GetTimestamp(); - m_readFrame.seq = m_readRTP.GetSequenceNumber(); - m_readFrame.ssrc = m_readRTP.GetSyncSource(); - m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE; - m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType(); - - if (m_readFrame.datalen > 0 && - m_readFrame.payload != RTP_DataFrame::CN && - m_readFrame.payload != RTP_DataFrame::Cisco_CN) { - m_readFrame.flags &= ~SFF_CNG; - } - - return SWITCH_STATUS_SUCCESS; -} - - -switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags) -{ - PatchPtr mediaPatch; - switch (StartReadWrite(mediaPatch)) { - case -1 : - return SWITCH_STATUS_FALSE; - case 1 : - return SWITCH_STATUS_SUCCESS; - } - - RTP_DataFrame rtp; - if (switch_test_flag(frame, SFF_RAW_RTP)) { - rtp = RTP_DataFrame((const BYTE *)frame->packet, frame->packetlen, false); - } - else if (switch_test_flag(frame, SFF_UDPTL_PACKET)) { - rtp.SetPayloadSize(frame->packetlen); - memcpy(rtp.GetPayloadPtr(), frame->packet, frame->packetlen); - } - else { - rtp.SetPayloadSize(frame->datalen); - memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen); - - rtp.SetPayloadType(mediaFormat.GetPayloadType()); - - /* Not sure what FS is going to give us! - Suspect it depends on the mod on the other side sending it. */ - if (frame->timestamp != 0) - timestamp = frame->timestamp; - else if (frame->samples != 0) - timestamp += frame->samples; - else if (m_switchCodec != NULL) - timestamp += m_switchCodec->implementation->samples_per_packet; - rtp.SetTimestamp(timestamp); - } - - if (mediaPatch->PushFrame(rtp)) - return SWITCH_STATUS_SUCCESS; - - PTRACE(1, "mod_opal\tread_frame: push failed!"); - return SWITCH_STATUS_FALSE; -} - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:nil - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s: - */ +/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / + * Soft-Switch Application + * + * Version: MPL 1.1 + * + * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) + * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Contributor(s): + * Tuyan Ozipek (tuyanozipek@gmail.com) + * Lukasz Zwierko (lzwierko@gmail.com) + * Robert Jongbloed (robertj@voxlucida.com.au) + * + */ + +#include "mod_opal.h" +#include +#include +#if PTLIB_CHECK_VERSION(2,11,1) +#include +#endif +#include +#include + + +/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a + MAXIMUM audio frames per packet is negotiated, and there is no + requirement for the remote to actually send that many. So, in say GSM, we + negotiate up to 3 frames or 60ms of data and the remote actually sends one + (20ms) frame per packet. Perfectly legal but blows up the media handling + in FS. + + Eventually we will get around to bundling the packets, but not yet. This + compile flag will just force one frame/packet for all audio codecs. + */ +#define IMPLEMENT_MULTI_FAME_AUDIO 0 + + +static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause); + + +static FSProcess *opal_process = NULL; + + +static PConstString const ModuleName("opal"); +static char const ConfigFile[] = "opal.conf"; +#define FS_PREFIX "fs" + + +static switch_io_routines_t opalfs_io_routines = { + /*.outgoing_channel */ create_outgoing_channel, + /*.read_frame */ FSConnection::read_audio_frame, + /*.write_frame */ FSConnection::write_audio_frame, + /*.kill_channel */ FSConnection::kill_channel, + /*.send_dtmf */ FSConnection::send_dtmf, + /*.receive_message */ FSConnection::receive_message, + /*.receive_event */ FSConnection::receive_event, + /*.state_change */ FSConnection::state_change, + /*.read_video_frame */ FSConnection::read_video_frame, + /*.write_video_frame */ FSConnection::write_video_frame +}; + +static switch_state_handler_table_t opalfs_event_handlers = { + /*.on_init */ FSConnection::on_init, + /*.on_routing */ FSConnection::on_routing, + /*.on_execute */ FSConnection::on_execute, + /*.on_hangup */ FSConnection::on_hangup, + /*.on_exchange_media */ FSConnection::on_exchange_media, + /*.on_soft_execute */ FSConnection::on_soft_execute, + /*.on_consume_media*/ NULL, + /*.on_hibernate*/ NULL, + /*.on_reset*/ NULL, + /*.on_park*/ NULL, + /*.on_reporting*/ NULL, + /*.on_destroy*/ FSConnection::on_destroy +}; + + +SWITCH_BEGIN_EXTERN_C +/*******************************************************************************/ + +SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown); +SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL); + +SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n"); + + /* Prevent the loading of OPAL codecs via "plug ins", this is a directory + full of DLLs that will be loaded automatically. */ + (void)putenv((char *)"PTLIBPLUGINDIR=/no/thanks"); + + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + if (!*module_interface) { + return SWITCH_STATUS_MEMERR; + } + + opal_process = new FSProcess(); + if (opal_process == NULL) { + return SWITCH_STATUS_MEMERR; + } + + if (opal_process->Initialise(*module_interface)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n"); + //unloading causes a seg in linux + //return SWITCH_STATUS_UNLOAD; + return SWITCH_STATUS_SUCCESS; + } + + delete opal_process; + opal_process = NULL; + return SWITCH_STATUS_FALSE; +} + + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) +{ + delete opal_process; + opal_process = NULL; + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_END_EXTERN_C +/*******************************************************************************/ + +/////////////////////////////////////////////////////////////////////// + +#if PTRACING + +class FSTrace : public std::ostream +{ +private: + class Buffer : public std::stringbuf + { + virtual int sync() + { + std::string s = str(); + if (s.empty()) + return 0; + + //Due to explicit setting of flags we know exactly what we are getting + #define THREAD_ID_INDEX 2 + #define FILE_NAME_INDEX 3 + #define FILE_LINE_INDEX 4 +#if PTLIB_CHECK_VERSION(2,11,1) + #define CONTEXT_ID_REGEX "([0-9]+|- - - - - - -)\t" + #define LOG_PRINTF_FORMAT "{%s,%s} %s" + #define FULL_TEXT_INDEX 6 +#else + #define CONTEXT_ID_REGEX + #define LOG_PRINTF_FORMAT "{%s} %s" + #define FULL_TEXT_INDEX 5 +#endif + PStringArray fields(7); + static PRegularExpression logRE("^([0-9]+)\t *(.+)\t *([^(]+)\\(([0-9]+)\\)\t"CONTEXT_ID_REGEX"(.*)", + PRegularExpression::Extended); + if (!logRE.Execute(s.c_str(), fields)) { + fields[1] = "4"; + fields[THREAD_ID_INDEX] = "unknown"; + fields[FILE_NAME_INDEX] = __FILE__; + fields[FILE_LINE_INDEX] = __LINE__; + fields[FULL_TEXT_INDEX] = s; + } + + switch_log_level_t level; + switch (fields[1].AsUnsigned()) { + case 0 : + level = SWITCH_LOG_ALERT; + break; + case 1 : + level = SWITCH_LOG_ERROR; + break; + case 2 : + level = SWITCH_LOG_WARNING; + break; + case 3 : + level = SWITCH_LOG_INFO; + break; + default : + level = SWITCH_LOG_DEBUG; + break; + } + + fields[4].Replace("\t", " ", true); +#if PTLIB_CHECK_VERSION(2,11,1) + fields[5].Replace("- - - - - - -", "-"), +#endif + switch_log_printf(SWITCH_CHANNEL_ID_LOG, + fields[FILE_NAME_INDEX], + "PTLib-OPAL", + fields[FILE_LINE_INDEX].AsUnsigned(), + NULL, + level, + LOG_PRINTF_FORMAT, + fields[THREAD_ID_INDEX].GetPointer(), +#if PTLIB_CHECK_VERSION(2,11,1) + fields[5].GetPointer(), +#endif + fields[FULL_TEXT_INDEX].GetPointer()); + + // Reset string + str(std::string()); + return 0; + } + } buffer; + +public: + FSTrace() + : ostream(&buffer) + { + } +}; + +#endif // PTRACING + + +/////////////////////////////////////////////////////////////////////// + +FSProcess::FSProcess() + : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1) + , m_manager(NULL) +{ +} + + +FSProcess::~FSProcess() +{ + delete m_manager; +#if PTRACING + PTrace::SetStream(NULL); // This will delete the FSTrace object +#endif +} + + +bool FSProcess::Initialise(switch_loadable_module_interface_t *iface) +{ + m_manager = new FSManager(); + return m_manager != NULL && m_manager->Initialise(iface); +} + + +/////////////////////////////////////////////////////////////////////// + +FSManager::FSManager() + : m_context("default") + , m_dialplan("XML") +{ + // These are deleted by the OpalManager class, no need to have destructor + m_h323ep = new H323EndPoint(*this); + m_iaxep = new IAX2EndPoint(*this); + m_fsep = new FSEndPoint(*this); +} + + +bool FSManager::Initialise(switch_loadable_module_interface_t *iface) +{ + ReadConfig(false); + + m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE); + m_FreeSwitch->interface_name = ModuleName; + m_FreeSwitch->io_routines = &opalfs_io_routines; + m_FreeSwitch->state_handler = &opalfs_event_handlers; + + silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection; + + if (m_listeners.empty()) { + m_h323ep->StartListener(""); + } else { + for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { + if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) { + PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name); + } + } + } + + AddRouteEntry("h323:.* = "FS_PREFIX":"); // config option for direct routing + AddRouteEntry("iax2:.* = "FS_PREFIX":"); // config option for direct routing + AddRouteEntry(FS_PREFIX":.* = h323:"); // config option for direct routing + + // Make sure all known codecs are instantiated, + // these are ones we know how to translate into H.323 capabilities + GetOpalG728(); + GetOpalG729(); + GetOpalG729A(); + GetOpalG729B(); + GetOpalG729AB(); + GetOpalG7231_6k3(); + GetOpalG7231_5k3(); + GetOpalG7231A_6k3(); + GetOpalG7231A_5k3(); + GetOpalGSM0610(); + GetOpalGSMAMR(); + GetOpaliLBC(); + +#if !IMPLEMENT_MULTI_FAME_AUDIO + OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats(); + for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) { + if (it->GetMediaType() == OpalMediaType::Audio()) { + int ms_per_frame = it->GetFrameTime()/it->GetTimeUnits(); + int frames_in_20_ms = (ms_per_frame+19)/ms_per_frame; + it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), frames_in_20_ms); + it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), frames_in_20_ms); + OpalMediaFormat::SetRegisteredMediaFormat(*it); + PTRACE(4, "mod_opal\tSet " << *it << " to " << frames_in_20_ms << "frames/packet"); + } + } +#endif // IMPLEMENT_MULTI_FAME_AUDIO + + OpalMediaFormat t38 = OpalT38; + t38.SetOptionBoolean("UDPTL-Raw-Mode", true); + OpalMediaFormat::SetRegisteredMediaFormat(t38); + + if (!m_gkAddress.IsEmpty()) { + if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface)) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n", + (const char *)m_h323ep->GetGatekeeper()->GetName()); + else + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n", + (const char *)m_gkAddress, + (const char *)m_gkIdentifer, + (const char *)m_gkInterface); + } + + return TRUE; +} + + +switch_status_t FSManager::ReadConfig(int reload) +{ + switch_event_t *request_params = NULL; + switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS); + switch_assert(request_params); + switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil("")); + + switch_xml_t cfg; + switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params); + if (xml == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile); + return SWITCH_STATUS_FALSE; + } + + switch_xml_t xmlSettings = switch_xml_child(cfg, "settings"); + if (xmlSettings) { + for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { + PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); + PConstString const val(switch_xml_attr_soft(xmlParam, "value")); + + if (var == "context") { + m_context = val; + } else if (var == "dialplan") { + m_dialplan = val; + } else if (var == "codec-prefs") { + m_codecPrefs = val; + } else if (var == "disable-transcoding") { + m_disableTranscoding = switch_true(val); + } else if (var == "dtmf-type") { + if (val == "string") + m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsString); + else if (val == "signal") + m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsTone); + else if (val == "rfc2833") + m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsRFC2833); + else if (val == "in-band") + m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputInBand); + } else if (var == "jitter-size") { + SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds + } else if (var == "gk-address") { + m_gkAddress = val; + } else if (var == "gk-identifer") { + m_gkIdentifer = val; + } else if (var == "gk-interface") { + m_gkInterface = val; +#if PTRACING + } else if (var == "trace-level") { + unsigned level = val.AsUnsigned(); + if (level > 0) { + PTrace::SetLevel(level); + PTrace::ClearOptions(0xffffffff); // Everything off + PTrace::SetOptions( // Except these + PTrace::TraceLevel|PTrace::FileAndLine|PTrace::Thread +#if PTLIB_CHECK_VERSION(2,11,1) + |PTrace::ContextIdentifier +#endif + ); + PTrace::SetStream(new FSTrace); + } +#endif + } + } + } + + switch_xml_t xmlListeners = switch_xml_child(cfg, "listeners"); + if (xmlListeners != NULL) { + for (switch_xml_t xmlListener = switch_xml_child(xmlListeners, "listener"); xmlListener != NULL; xmlListener = xmlListener->next) { + + m_listeners.push_back(FSListener()); + FSListener & listener = m_listeners.back(); + + listener.m_name = switch_xml_attr_soft(xmlListener, "name"); + if (listener.m_name.IsEmpty()) + listener.m_name = "unnamed"; + + for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { + PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); + PConstString const val(switch_xml_attr_soft(xmlParam, "value")); + if (var == "h323-ip") + listener.m_address = val; + else if (var == "h323-port") + listener.m_port = (uint16_t)val.AsUnsigned(); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name); + } + } + + switch_event_destroy(&request_params); + + if (xml) + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause) +{ + if (opal_process == NULL) + return SWITCH_CAUSE_CRASH; + + FSConnection::outgoing_params params; + params.var_event = var_event; + params.outbound_profile = outbound_profile; + params.new_session = new_session; + params.pool = pool; + params.flags = flags; + params.cancel_cause = cancel_cause; + params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; + + if (opal_process->GetManager().SetUpCall(FS_PREFIX":", outbound_profile->destination_number, ¶ms) != NULL) + return SWITCH_CAUSE_SUCCESS; + + if (*new_session != NULL) + switch_core_session_destroy(new_session); + return params.fail_cause; +} + + +/////////////////////////////////////////////////////////////////////// + +FSEndPoint::FSEndPoint(FSManager & manager) + : OpalLocalEndPoint(manager, FS_PREFIX) + , m_manager(manager) +{ + PTRACE(4, "mod_opal\tFSEndPoint created."); +} + + +OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions) +{ + return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData); +} + + +/////////////////////////////////////////////////////////////////////// + +FSConnection::FSConnection(OpalCall & call, + FSEndPoint & endpoint, + unsigned options, + OpalConnection::StringOptions* stringOptions, + outgoing_params * params) + : OpalLocalConnection(call, endpoint, NULL, options, stringOptions) + , m_endpoint(endpoint) + , m_fsSession(NULL) + , m_fsChannel(NULL) + , m_flushAudio(false) + , m_udptl(false) +{ + memset(&m_read_timer, 0, sizeof(m_read_timer)); + memset(&m_read_codec, 0, sizeof(m_read_codec)); + memset(&m_write_codec, 0, sizeof(m_write_codec)); + memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer)); + memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec)); + memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec)); + memset(&m_dummy_frame, 0, sizeof(m_dummy_frame)); + m_dummy_frame.flags = SFF_CNG; + + if (params != NULL) { + // If we fail, this is the cause + params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + + if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), + SWITCH_CALL_DIRECTION_OUTBOUND, params->flags, params->pool)) == NULL) { + PTRACE(1, "mod_opal\tCannot create session for outgoing call."); + return; + } + } + else { + if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), + SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) { + PTRACE(1, "mod_opal\tCannot create session for incoming call."); + return; + } + } + + if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) { + switch_core_session_destroy(&m_fsSession); + return; + } + + switch_core_session_set_private(m_fsSession, this); + SafeReference(); // Make sure cannot be deleted until on_destroy() + + if (params != NULL) { + switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile); + switch_channel_set_caller_profile(m_fsChannel, caller_profile); + SetLocalPartyName(caller_profile->caller_id_number); + SetDisplayName(caller_profile->caller_id_name); + + *params->new_session = m_fsSession; + } + + switch_channel_set_state(m_fsChannel, CS_INIT); +} + + +bool FSConnection::OnOutgoingSetUp() +{ + if (m_fsSession == NULL || m_fsChannel == NULL) { + PTRACE(1, "mod_opal\tSession request failed."); + return false; + } + + // Transfer FS caller_id_number & caller_id_name from the FSConnection + // to the protocol connection (e.g. H.323) so gets sent correctly + // in outgoing packets + PSafePtr proto = GetOtherPartyConnection(); + if (proto == NULL) { + PTRACE(1, "mod_opal\tNo protocol connection in call."); + return false; + } + + proto->SetLocalPartyName(GetLocalPartyName()); + proto->SetDisplayName(GetDisplayName()); + + switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL()); + return true; +} + + +bool FSConnection::OnIncoming() +{ + if (m_fsSession == NULL || m_fsChannel == NULL) { + PTRACE(1, "mod_opal\tSession request failed."); + return false; + } + + switch_core_session_add_stream(m_fsSession, NULL); + + PURL url = GetRemotePartyURL(); + switch_caller_profile_t *caller_profile = switch_caller_profile_new( + switch_core_session_get_pool(m_fsSession), + url.GetUserName(), /** username */ + m_endpoint.GetManager().GetDialPlan(), /** dial plan */ + GetRemotePartyName(), /** caller_id_name */ + GetRemotePartyNumber(), /** caller_id_number */ + url.GetHostName(), /** network addr */ + NULL, /** ANI */ + NULL, /** ANI II */ + NULL, /** RDNIS */ + ModuleName, /** source */ + m_endpoint.GetManager().GetContext(), /** set context */ + GetCalledPartyNumber() /** destination_number */ + ); + if (caller_profile == NULL) { + PTRACE(1, "mod_opal\tCould not create caller profile"); + return false; + } + + PTRACE(4, "mod_opal\tCreated switch caller profile:\n" + " username = " << caller_profile->username << "\n" + " dialplan = " << caller_profile->dialplan << "\n" + " caller_id_name = " << caller_profile->caller_id_name << "\n" + " caller_id_number = " << caller_profile->caller_id_number << "\n" + " network_addr = " << caller_profile->network_addr << "\n" + " source = " << caller_profile->source << "\n" + " context = " << caller_profile->context << "\n" + " destination_number= " << caller_profile->destination_number); + switch_channel_set_caller_profile(m_fsChannel, caller_profile); + + switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number); + + if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_opal\tCould not launch session thread"); + switch_core_session_destroy(&m_fsSession); + m_fsChannel = NULL; + return false; + } + + return true; +} + + +void FSConnection::OnEstablished() +{ + OpalLocalConnection::OnEstablished(); + + if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + PTRACE(4, "mod_opal\tOnEstablished for outbound call, checking for media"); + if (GetMediaStream(OpalMediaType::Audio(), true) != NULL && GetMediaStream(OpalMediaType::Audio(), false) != NULL) { + PTRACE(3, "mod_opal\tOnEstablished for outbound call, making call answered"); + switch_channel_mark_answered(m_fsChannel); + } + } +} + + +void FSConnection::OnReleased() +{ + m_rxAudioOpened.Signal(); // Just in case + m_txAudioOpened.Signal(); + + if (m_fsChannel != NULL) { + PTRACE(3, "mod_opal\tHanging up FS side"); + switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931); + } + + OpalLocalConnection::OnReleased(); +} + + +PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia) +{ + if (PAssertNULL(m_fsChannel) == NULL) + return false; + + switch_channel_mark_ring_ready(m_fsChannel); + return OpalLocalConnection::SetAlerting(calleeName, withMedia); +} + + +PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration) +{ + if (PAssertNULL(m_fsChannel) == NULL) + return false; + + switch_dtmf_t dtmf = { tone, duration }; + PTRACE(4, "mod_opal\tSending DTMF to FS: tone=" << tone << ", duration=" << duration); + return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS; +} + + +OpalMediaFormatList FSConnection::GetMediaFormats() const +{ + if (m_switchMediaFormats.IsEmpty()) { + const_cast(this)->SetCodecs(); + } + + return m_switchMediaFormats; +} + + +void FSConnection::SetCodecs() +{ + int numCodecs = 0; + const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; + + PString codec_string = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"); + if (codec_string.IsEmpty()) { + codec_string = switch_channel_get_variable(m_fsChannel, "codec_string"); + if (codec_string.IsEmpty()) { + codec_string = m_endpoint.GetManager().GetCodecPrefs(); + if (codec_string.IsEmpty()) { + numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0])); + for (int i = 0; i < numCodecs; i++) { + if (i > 0) + codec_string += ','; + codec_string += codecs[i]->iananame; + } + PTRACE(4, "mod_opal\tDefault to all loaded codecs=" << codec_string); + } + else { + PTRACE(4, "mod_opal\tSettings codec-prefs=" << codec_string); + } + } + else { + PTRACE(4, "mod_opal\tChannel codec_string=" << codec_string); + } + + PString orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE); + if (!orig_codec.IsEmpty()) { + if (m_endpoint.GetManager().GetDisableTranscoding()) { + codec_string = orig_codec; + PTRACE(4, "mod_opal\tNo transcoding, forced to originator codec=" << orig_codec); + } + else { + codec_string.Splice(orig_codec+',', 0); + PTRACE(4, "mod_opal\tSetting preference to originator codec=" << orig_codec); + } + } + } + else { + PTRACE(4, "mod_opal\tChannel absolute_codec_string=" << codec_string); + } + + { + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS); + numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last); + } + + for (int i = 0; i < numCodecs; i++) { + const switch_codec_implementation_t *codec = codecs[i]; + + // See if we have a match by PayloadType/rate/name + OpalMediaFormat switchFormat((RTP_DataFrame::PayloadTypes)codec->ianacode, + codec->samples_per_second, + codec->iananame); + if (!switchFormat.IsValid()) { + // See if we have a match by name alone + switchFormat = codec->iananame; + if (!switchFormat.IsValid()) { + PTRACE(2, "mod_opal\tCould not match FS codec " + << codec->iananame << '@' << codec->samples_per_second + << " (pt=" << (unsigned)codec->ianacode << ")" + " to an OPAL media format."); + continue; + } + } + + PTRACE(4, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat); + +#if IMPLEMENT_MULTI_FAME_AUDIO + // Did we match or create a new media format? + if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) { + // Calculate frames per packet, do not use codec->codec_frames_per_packet as that field + // has slightly different semantics when used in streamed codecs such as G.711 + int fpp = codec->samples_per_packet/switchFormat.GetFrameTime(); + + /* Set the frames/packet to maximum of what is in the FS table. The OPAL negotiations will + drop the value from there. This might fail if there are "holes" in the FS table, e.g. + if for some reason G.723.1 has 30ms and 90ms but not 60ms, then the OPAL negotiations + could end up with 60ms and the codec cannot be created. The "holes" are unlikely in + all but streamed codecs such as G.711, where it is theoretically possible for OPAL to + come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these + scenarios sufficiently rare that we can safely ignore them ... for now. */ + + if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) { + switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp); + } + + if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) { + switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp); + } + } +#endif // IMPLEMENT_MULTI_FAME_AUDIO + + m_switchMediaFormats += switchFormat; + } + +#if HAVE_T38 + OpalMediaFormat t38 = OpalT38; + + /* We need to have a T.38 options for TCS, but may be before the + spandsp_mod has set it us. So, if not, we actually give to spandsp_mod. */ + switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, "t38_options"); + if (t38_options == NULL) + SetT38OptionsFromMediaFormat(t38, "_preconfigured_t38_options"); + else { + t38.SetOptionInteger("T38FaxVersion", t38_options->T38FaxVersion); + t38.SetOptionInteger("T38MaxBitRate", t38_options->T38MaxBitRate); + t38.SetOptionBoolean("T38FaxFillBitRemoval", t38_options->T38FaxFillBitRemoval); + t38.SetOptionBoolean("T38FaxTranscodingMMR", t38_options->T38FaxTranscodingMMR); + t38.SetOptionBoolean("T38FaxTranscodingJBIG", t38_options->T38FaxTranscodingJBIG); + t38.SetOptionValue("T38FaxRateManagement", t38_options->T38FaxRateManagement); + t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxBuffer); + t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxDatagram); + t38.SetOptionValue("T38FaxUdpEC", t38_options->T38FaxUdpEC); + } + + m_switchMediaFormats += t38; +#endif // HAVE_T38 +} + + +OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource) +{ + return new FSMediaStream(*this, mediaFormat, sessionID, isSource); +} + + +void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch) +{ + OpalConnection::OnPatchMediaStream(isSource, patch); + + if (PAssertNULL(m_fsChannel) == NULL) + return; + + if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) + return; + + if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { + PTRACE(4, "mod_opal\tOnPatchMediaStream for inbound call, flagging media opened"); + if (isSource) + m_rxAudioOpened.Signal(); + else + m_txAudioOpened.Signal(); + } + else { + PTRACE(4, "mod_opal\tOnPatchMediaStream for outbound call, checking media"); + if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) { + // Have open media in both directions. + if (IsEstablished()) { + PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call answered"); + switch_channel_mark_answered(m_fsChannel); + } + else if (!IsReleased()) { + PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call pre-answered"); + switch_channel_mark_pre_answered(m_fsChannel); + } + } + } +} + + +switch_status_t FSConnection::on_init() +{ + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + PTRACE(4, "mod_opal\tStarted routing for connection " << *this); + + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_routing() +{ + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + PTRACE(4, "mod_opal\tRouting connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_execute() +{ + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + PTRACE(4, "mod_opal\tExecuting connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_destroy() +{ + PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this); + + m_fsChannel = NULL; // Will be destroyed by FS, so don't use it any more. + + switch_core_codec_destroy(&m_read_codec); + switch_core_codec_destroy(&m_write_codec); + switch_core_codec_destroy(&m_vid_read_codec); + switch_core_codec_destroy(&m_vid_write_codec); + switch_core_timer_destroy(&m_read_timer); + switch_core_timer_destroy(&m_vid_read_timer); + + switch_core_session_set_private(m_fsSession, NULL); + SafeDereference(); + + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_hangup() +{ + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + /* if this is still here it was our idea to hangup not opal's */ + ClearCallSynchronous(NULL, H323TranslateToCallEndReason( + (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX)); + + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_exchange_media() +{ + PTRACE(4, "mod_opal\tExchanging media on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::on_soft_execute() +{ + PTRACE(4, "mod_opal\tSoft execute on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::kill_channel(int sig) +{ + switch (sig) { + case SWITCH_SIG_KILL: + PTRACE(4, "mod_opal\tSignal KILL received on connection " << *this); + m_rxAudioOpened.Signal(); + m_txAudioOpened.Signal(); + CloseMediaStreams(); + break; + + case SWITCH_SIG_BREAK: + PTRACE(4, "mod_opal\tSignal BREAK received on connection " << *this); + break; + + default: + PTRACE(4, "mod_opal\tSignal " << sig << " received on connection " << *this); + break; + } + + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf) +{ + PTRACE(4, "mod_opal\tReceived DTMF from FS: tone=" << dtmf->digit << ", duration=" << dtmf->duration); + OnUserInputTone(dtmf->digit, dtmf->duration); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg) +{ + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING: + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + case SWITCH_MESSAGE_INDICATE_DEFLECT: + if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { + switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel); + if (profile != NULL && profile->caller_extension != NULL) + { + PSafePtr other = GetOtherPartyConnection(); + if (other != NULL) { + other->SetLocalPartyName(profile->caller_extension->extension_number); + other->SetDisplayName(profile->caller_extension->extension_name); + } + SetLocalPartyName(profile->caller_extension->extension_number); + SetDisplayName(profile->caller_extension->extension_name); + } + } + else { + return SWITCH_STATUS_FALSE; + } + break; + + default: + break; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_BRIDGE: + case SWITCH_MESSAGE_INDICATE_UNBRIDGE: + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + m_flushAudio = true; + break; + + case SWITCH_MESSAGE_INDICATE_RINGING: + AlertingIncoming(); + break; + + case SWITCH_MESSAGE_INDICATE_PROGRESS: + AutoStartMediaStreams(); + AlertingIncoming(); + + if (!WaitForMedia()) + return SWITCH_STATUS_FALSE; + + if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { + switch_channel_mark_pre_answered(m_fsChannel); + } + break; + + case SWITCH_MESSAGE_INDICATE_ANSWER: + AcceptIncoming(); + + if (!WaitForMedia()) + return SWITCH_STATUS_FALSE; + + if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) { + switch_channel_mark_answered(m_fsChannel); + } + break; + + case SWITCH_MESSAGE_INDICATE_DEFLECT: + ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection()); + break; + +#if HAVE_T38 + case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA: + { + PTRACE(2, "mod_opal\tRequesting switch to T.38"); + PSafePtr other = GetOtherPartyConnection(); + if (other != NULL && other->SwitchFaxMediaStreams(true)) + switch_channel_set_flag(m_fsChannel, CF_REQ_MEDIA); + else { + PTRACE(1, "mod_opal\tMode change request to T.38 failed"); + } + break; + } + + case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION: + PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_T38_DESCRIPTION"); + break; + + case SWITCH_MESSAGE_INDICATE_UDPTL_MODE: + PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_UDPTL_MODE"); + m_udptl = true; + break; +#endif // HAVE_T38 + + default: + PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this); + } + + return SWITCH_STATUS_SUCCESS; +} + + +bool FSConnection::WaitForMedia() +{ + PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this); + m_rxAudioOpened.Wait(); + m_txAudioOpened.Wait(); + + if (IsReleased()) { + // Call got aborted + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n"); + return false; + } + + PTRACE(3, "mod_opal\tMedia started on connection " << *this); + return true; +} + + +#if HAVE_T38 +void FSConnection::SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname) +{ + switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, varname); + if (t38_options == NULL) + t38_options = (switch_t38_options_t *)switch_core_session_alloc(m_fsSession, sizeof(switch_t38_options_t)); + + PString value; + mediaFormat.GetOptionValue("T38FaxRateManagement", value); + t38_options->T38FaxRateManagement = switch_core_session_strdup(m_fsSession, value); + + mediaFormat.GetOptionValue("T38FaxUdpEC", value); + t38_options->T38FaxUdpEC = switch_core_session_strdup(m_fsSession, value); + + t38_options->T38MaxBitRate = mediaFormat.GetOptionInteger("T38MaxBitRate", 9600); + t38_options->T38FaxMaxBuffer = mediaFormat.GetOptionInteger("T38FaxMaxBuffer", 2000); + t38_options->T38FaxMaxDatagram = mediaFormat.GetOptionInteger("T38FaxMaxDatagram", 528); + + t38_options->T38FaxFillBitRemoval = mediaFormat.GetOptionBoolean("T38FaxFillBitRemoval") ? SWITCH_TRUE : SWITCH_FALSE; + t38_options->T38FaxTranscodingMMR = mediaFormat.GetOptionBoolean("T38FaxTranscodingMMR") ? SWITCH_TRUE : SWITCH_FALSE; + t38_options->T38FaxTranscodingJBIG = mediaFormat.GetOptionBoolean("T38FaxTranscodingJBIG") ? SWITCH_TRUE : SWITCH_FALSE; + + t38_options->T38VendorInfo = switch_core_session_strdup(m_fsSession, mediaFormat.GetOptionString("T38VendorInfo")); + + //t38_options->remote_ip = switch_core_session_strdup(session, mediaFormat.something); + //t38_options->remote_port = mediaFormat.something; + + switch_channel_set_private(m_fsChannel, varname, t38_options); + PTRACE(3, "mod_opal\tSet " << varname); +} + + +void FSConnection::OnSwitchedT38(bool toT38, bool success) +{ + if (toT38 && success && IndicateSwitchedT38()) { + PTRACE(3, "mod_opal\tMode change request to T.38 succeeded"); + } + else { + AbortT38(); + } +} + + +void FSConnection::OnSwitchingT38(bool toT38) +{ + if (toT38 && IndicateSwitchedT38()) { + PTRACE(3, "mod_opal\tMode change request to T.38 started"); + } + else { + AbortT38(); + } +} + + +void FSConnection::AbortT38() +{ + PTRACE(3, "mod_opal\tMode change request to T.38 failed"); + switch_channel_set_private(m_fsChannel, "t38_options", NULL); + switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38); + switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38_REQ); + switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38_FAIL); +} + + +bool FSConnection::IndicateSwitchedT38() +{ + PSafePtr other = GetOtherPartyConnection(); + if (other == NULL) { + PTRACE(3, "mod_opal\tCan't change to T.38, no other connection"); + return false; + } + + OpalMediaFormatList otherFormats = other->GetMediaFormats(); + OpalMediaFormatList::const_iterator t38 = otherFormats.FindFormat(OpalT38); + if (t38 == otherFormats.end()) { + PTRACE(3, "mod_opal\tCan't change to T.38, no remote capability"); + return false; + } + + SetT38OptionsFromMediaFormat(*t38, "t38_options"); + + switch_channel_set_variable(m_fsChannel, "has_t38", "true"); + switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38); + + switch_channel_execute_on(m_fsChannel, "opal_execute_on_t38"); + switch_channel_api_on(m_fsChannel, "opal_api_on_t38"); + return true; +} +#endif // HAVE_T38 + + +switch_status_t FSConnection::receive_event(switch_event_t *event) +{ + PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::state_change() +{ + PTRACE(4, "mod_opal\tState changed on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id) +{ + return read_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags); +} + + +switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id) +{ + return write_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags); +} + + +switch_status_t FSConnection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id) +{ + return read_frame(OpalMediaType::Video(), frame, flag); +} + + +switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id) +{ + return write_frame(OpalMediaType::Video(), frame, flag); +} + + +switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags) +{ + if (!ownerCall.IsSwitchingT38()) { + PSafePtr stream = PSafePtrCast (GetMediaStream(mediaType, false)); + if (stream != NULL) + return stream->read_frame(frame, flags); + + PTRACE(2, "mod_opal\tNo stream for read of " << mediaType); + } + + // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS + *frame = &m_dummy_frame; + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags) +{ + // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS + if (ownerCall.IsSwitchingT38()) + return SWITCH_STATUS_SUCCESS; + + PSafePtr stream = PSafePtrCast(GetMediaStream(mediaType, true)); + if (stream != NULL) + return stream->write_frame(frame, flags); + + PTRACE(2, "mod_opal\tNo stream for write of " << mediaType); + return SWITCH_STATUS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////// + +FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource) + : OpalMediaStream(conn, mediaFormat, sessionID, isSource) + , m_connection(conn) + , m_switchTimer(NULL) + , m_switchCodec(NULL) + , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE) +{ + memset(&m_readFrame, 0, sizeof(m_readFrame)); +} + + +PBoolean FSMediaStream::Open() +{ + if (IsOpen()) { + return true; + } + + switch_core_session_t *fsSession = m_connection.GetSession(); + switch_channel_t *fsChannel = m_connection.GetChannel(); + if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL) + return false; + + bool isAudio; + OpalMediaType mediaType = mediaFormat.GetMediaType(); + if (mediaType == OpalMediaType::Audio()) + isAudio = true; + else if (mediaType == OpalMediaType::Video()) + isAudio = false; +#if HAVE_T38 + else if (mediaType == OpalMediaType::Fax()) { + m_readFrame.flags = SFF_UDPTL_PACKET|SFF_PROXY_PACKET; + return OpalMediaStream::Open(); + } +#endif + else { + PTRACE(1, "mod_opal\tUnsupported media type: " << mediaType); + return false; + } + + int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits(); + + if (IsSink()) { + m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec; + m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer; + m_readFrame.codec = m_switchCodec; + m_readFrame.rate = mediaFormat.GetClockRate(); + } else { + m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec; + } + + // The following is performed on two different instances of this object. + if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP + mediaFormat.GetClockRate(), ptime, 1, // Channels + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { + // Could not select a codecs using negotiated frames/packet, so try using default. + if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP + mediaFormat.GetClockRate(), 0, 1, // Channels + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " cannot initialise " << (IsSink()? "read" : "write") << ' ' + << mediaType << " codec " << mediaFormat << " for connection " << *this); + switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); + return false; + } + PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' ' + << mediaType << " codec " << mediaFormat << " for connection " << *this); + } + + if (IsSink()) { + if (isAudio) { + switch_core_session_set_read_codec(fsSession, m_switchCodec); + if (switch_core_timer_init(m_switchTimer, + "soft", + m_switchCodec->implementation->microseconds_per_packet / 1000, + m_switchCodec->implementation->samples_per_packet, + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " timer init failed on " << (IsSink()? "read" : "write") << ' ' + << mediaType << " codec " << mediaFormat << " for connection " << *this); + switch_core_codec_destroy(m_switchCodec); + m_switchCodec = NULL; + return false; + } + } else { + switch_core_session_set_video_read_codec(fsSession, m_switchCodec); + switch_channel_set_flag(fsChannel, CF_VIDEO); + } + } else { + if (isAudio) { + switch_core_session_set_write_codec(fsSession, m_switchCodec); + } else { + switch_core_session_set_video_write_codec(fsSession, m_switchCodec); + switch_channel_set_flag(fsChannel, CF_VIDEO); + } + } + + PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " initialised " << (IsSink()? "read" : "write") << ' ' + << mediaType << " codec " << mediaFormat << " for connection " << *this); + + return OpalMediaStream::Open(); +} + + +void FSMediaStream::InternalClose() +{ +} + + +PBoolean FSMediaStream::IsSynchronous() const +{ + return true; +} + + +PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const +{ + return false; +} + + +int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const +{ + if (!IsOpen()) { + PTRACE(1, "mod_opal\tNot open!"); + return -1; + } + + if (!m_connection.IsChannelReady()) { + PTRACE(1, "mod_opal\tChannel not ready!"); + return -1; + } + + // We make referenced copy of pointer so can't be deleted out from under us + mediaPatch = m_mediaPatch; + if (mediaPatch == NULL) { + /*There is a race here... sometimes we make it here and m_mediaPatch is NULL + if we wait it shows up in 1ms, maybe there is a better way to wait. */ + PTRACE(2, "mod_opal\tPatch not ready!"); + return 1; + } + + return 0; +} + + +switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags) +{ + *frame = &m_readFrame; + m_readFrame.flags |= SFF_CNG; + + PatchPtr mediaPatch; + switch (StartReadWrite(mediaPatch)) { + case -1 : + return SWITCH_STATUS_FALSE; + case 1 : + return SWITCH_STATUS_SUCCESS; + } + + if (m_connection.NeedFlushAudio()) { + mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer + m_readRTP.SetPayloadSize(0); + } else { + if (m_switchCodec != NULL) { + m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet); + } + + if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) { + PTRACE(1, "mod_opal\tread_frame: no source data!"); + return SWITCH_STATUS_FALSE; + } + } + + if (m_switchTimer != NULL) { + switch_core_timer_next(m_switchTimer); + } + + if (m_switchCodec != NULL) { + if (!switch_core_codec_ready(m_switchCodec)) { + PTRACE(1, "mod_opal\tread_frame: codec not ready!"); + return SWITCH_STATUS_FALSE; + } + } + + if (switch_test_flag(&m_readFrame, SFF_UDPTL_PACKET)) { + m_readFrame.flags &= ~SFF_CNG; + m_readFrame.packet = m_readRTP.GetPayloadPtr(); + m_readFrame.packetlen = m_readRTP.GetPayloadSize(); + return SWITCH_STATUS_SUCCESS; + } + + if (switch_test_flag(&m_readFrame, SFF_RAW_RTP)) { + m_readFrame.flags &= ~SFF_CNG; + m_readFrame.packet = m_readRTP.GetPointer(); + m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readRTP.GetPayloadSize(); + return SWITCH_STATUS_SUCCESS; + } + +#if IMPLEMENT_MULTI_FAME_AUDIO + // Repackage frames in incoming packet to agree with what FS expects. + // Not implmented yet!!!!!!!!! + // Cheating and only supporting one frame per packet +#endif + + m_readFrame.buflen = m_readRTP.GetSize(); + m_readFrame.data = m_readRTP.GetPayloadPtr(); + m_readFrame.datalen = m_readRTP.GetPayloadSize(); + m_readFrame.timestamp = m_readRTP.GetTimestamp(); + m_readFrame.seq = m_readRTP.GetSequenceNumber(); + m_readFrame.ssrc = m_readRTP.GetSyncSource(); + m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE; + m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType(); + + if (m_readFrame.datalen > 0 && + m_readFrame.payload != RTP_DataFrame::CN && + m_readFrame.payload != RTP_DataFrame::Cisco_CN) { + m_readFrame.flags &= ~SFF_CNG; + } + + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags) +{ + PatchPtr mediaPatch; + switch (StartReadWrite(mediaPatch)) { + case -1 : + return SWITCH_STATUS_FALSE; + case 1 : + return SWITCH_STATUS_SUCCESS; + } + + RTP_DataFrame rtp; + if (switch_test_flag(frame, SFF_RAW_RTP)) { + rtp = RTP_DataFrame((const BYTE *)frame->packet, frame->packetlen, false); + } + else if (switch_test_flag(frame, SFF_UDPTL_PACKET)) { + rtp.SetPayloadSize(frame->packetlen); + memcpy(rtp.GetPayloadPtr(), frame->packet, frame->packetlen); + } + else { + rtp.SetPayloadSize(frame->datalen); + memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen); + + rtp.SetPayloadType(mediaFormat.GetPayloadType()); + + /* Not sure what FS is going to give us! + Suspect it depends on the mod on the other side sending it. */ + if (frame->timestamp != 0) + timestamp = frame->timestamp; + else if (frame->samples != 0) + timestamp += frame->samples; + else if (m_switchCodec != NULL) + timestamp += m_switchCodec->implementation->samples_per_packet; + rtp.SetTimestamp(timestamp); + } + + if (mediaPatch->PushFrame(rtp)) + return SWITCH_STATUS_SUCCESS; + + PTRACE(1, "mod_opal\tread_frame: push failed!"); + return SWITCH_STATUS_FALSE; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s: + */ diff --git a/src/mod/endpoints/mod_opal/mod_opal.h b/src/mod/endpoints/mod_opal/mod_opal.h index 4b0cd62a02..4ee10910ee 100644 --- a/src/mod/endpoints/mod_opal/mod_opal.h +++ b/src/mod/endpoints/mod_opal/mod_opal.h @@ -1,331 +1,331 @@ -/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / - * Soft-Switch Application - * - * Version: MPL 1.1 - * - * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) - * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * Contributor(s): - * Tuyan Ozipek (tuyanozipek@gmail.com) - * Lukasz Zwierko (lzwierko@gmail.com) - * Robert Jongbloed (robertj@voxlucida.com.au) - * - */ - - -#ifndef __FREESWITCH_MOD_OPAL__ -#define __FREESWITCH_MOD_OPAL__ - -#if defined(__GNUC__) && defined(HAVE_VISIBILITY) -#pragma GCC visibility push(default) -#endif - -#include -#include - -#ifndef OPAL_CHECK_VERSION - #define OPAL_CHECK_VERSION(a,b,c) 0 -#endif - -#if !OPAL_CHECK_VERSION(3,12,8) - #error OPAL is too old to use, must be >= 2.12.8 -#endif - -#include -#include -#include - -#if defined(__GNUC__) && defined(HAVE_VISIBILITY) -#pragma GCC visibility pop -#endif - -#undef strcasecmp -#undef strncasecmp - -#include - -#define MODNAME "mod_opal" - -#define HAVE_T38 OPAL_T38_CAPABILITY - - - -class FSEndPoint; -class FSManager; - - -class FSProcess : public PLibraryProcess -{ - PCLASSINFO(FSProcess, PLibraryProcess); - public: - FSProcess(); - ~FSProcess(); - - bool Initialise(switch_loadable_module_interface_t *iface); - - FSManager & GetManager() const - { - return *m_manager; - } - - protected: - FSManager * m_manager; -}; - - -struct FSListener -{ - FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { } - - PString m_name; - PIPSocket::Address m_address; - uint16_t m_port; -}; - - -class FSManager : public OpalManager -{ - PCLASSINFO(FSManager, OpalManager); - - public: - FSManager(); - - bool Initialise(switch_loadable_module_interface_t *iface); - - switch_status_t ReadConfig(int reload); - - switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; } - const PString & GetContext() const { return m_context; } - const PString & GetDialPlan() const { return m_dialplan; } - const PString & GetCodecPrefs() const { return m_codecPrefs; } - bool GetDisableTranscoding() const { return m_disableTranscoding; } - - private: - switch_endpoint_interface_t *m_FreeSwitch; - - H323EndPoint *m_h323ep; - IAX2EndPoint *m_iaxep; - FSEndPoint *m_fsep; - - PString m_context; - PString m_dialplan; - PString m_codecPrefs; - bool m_disableTranscoding; - PString m_gkAddress; - PString m_gkIdentifer; - PString m_gkInterface; - - list m_listeners; -}; - - -class FSEndPoint : public OpalLocalEndPoint -{ - PCLASSINFO(FSEndPoint, OpalLocalEndPoint); - public: - FSEndPoint(FSManager & manager); - - virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions); - - FSManager & GetManager() const { return m_manager; } - - protected: - FSManager & m_manager; -}; - - -class FSConnection; - - -class FSMediaStream : public OpalMediaStream -{ - PCLASSINFO(FSMediaStream, OpalMediaStream); - public: - FSMediaStream( - FSConnection & conn, - const OpalMediaFormat & mediaFormat, ///< Media format for stream - unsigned sessionID, ///< Session number for stream - bool isSource ///< Is a source stream - ); - - virtual PBoolean Open(); - virtual PBoolean IsSynchronous() const; - virtual PBoolean RequiresPatchThread(OpalMediaStream *) const; - - switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags); - switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags); - - protected: - virtual void InternalClose(); - int StartReadWrite(PatchPtr & mediaPatch) const; - - private: - bool CheckPatchAndLock(); - - FSConnection &m_connection; - switch_timer_t *m_switchTimer; - switch_codec_t *m_switchCodec; - switch_frame_t m_readFrame; - RTP_DataFrame m_readRTP; -}; - - -#define DECLARE_CALLBACK0(name) \ - static switch_status_t name(switch_core_session_t *session) { \ - FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ - return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \ - switch_status_t name() - -#define DECLARE_CALLBACK1(name, type1, name1) \ - static switch_status_t name(switch_core_session_t *session, type1 name1) { \ - FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ - return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \ - switch_status_t name(type1 name1) - -#define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \ - static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \ - FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ - return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \ - switch_status_t name(type1 name1, type2 name2, type3 name3) - - - - -class FSConnection : public OpalLocalConnection -{ - PCLASSINFO(FSConnection, OpalLocalConnection) - - public: - struct outgoing_params { - switch_event_t *var_event; - switch_caller_profile_t *outbound_profile; - switch_core_session_t **new_session; - switch_memory_pool_t **pool; - switch_originate_flag_t flags; - switch_call_cause_t *cancel_cause; - switch_call_cause_t fail_cause; - }; - - FSConnection(OpalCall & call, - FSEndPoint & endpoint, - unsigned options, - OpalConnection::StringOptions * stringOptions, - outgoing_params * params); - - virtual bool OnOutgoingSetUp(); - virtual bool OnIncoming(); - virtual void OnEstablished(); - virtual void OnReleased(); - virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia); - virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean); - virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch); - virtual OpalMediaFormatList GetMediaFormats() const; - virtual PBoolean SendUserInputTone(char tone, unsigned duration); -#if HAVE_T38 - virtual void OnSwitchedT38(bool toT38, bool success); - virtual void OnSwitchingT38(bool toT38); -#endif - - DECLARE_CALLBACK0(on_init); - DECLARE_CALLBACK0(on_destroy); - DECLARE_CALLBACK0(on_routing); - DECLARE_CALLBACK0(on_execute); - DECLARE_CALLBACK0(on_hangup); - - DECLARE_CALLBACK0(on_exchange_media); - DECLARE_CALLBACK0(on_soft_execute); - - DECLARE_CALLBACK1(kill_channel, int, sig); - DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf); - DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg); - DECLARE_CALLBACK1(receive_event, switch_event_t *, event); - DECLARE_CALLBACK0(state_change); - DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id); - DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id); - DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id); - DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id); - - __inline switch_core_session_t *GetSession() const - { - return m_fsSession; - } - - __inline switch_channel_t *GetChannel() const - { - return m_fsChannel; - } - - bool IsChannelReady() const - { - return m_fsChannel != NULL && switch_channel_ready(m_fsChannel); - } - - bool NeedFlushAudio() - { - if (!m_flushAudio) - return false; - m_flushAudio = false; - return true; - } - - protected: - void SetCodecs(); - bool WaitForMedia(); -#if HAVE_T38 - void SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname); - bool IndicateSwitchedT38(); - void AbortT38(); -#endif - - switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags); - switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags); - - private: - FSEndPoint &m_endpoint; - switch_core_session_t *m_fsSession; - switch_channel_t *m_fsChannel; - PSyncPoint m_rxAudioOpened; - PSyncPoint m_txAudioOpened; - OpalMediaFormatList m_switchMediaFormats; - - // If FS ever supports more than one audio and one video, this needs to change - switch_timer_t m_read_timer; - switch_codec_t m_read_codec; - switch_codec_t m_write_codec; - - switch_timer_t m_vid_read_timer; - switch_codec_t m_vid_read_codec; - switch_codec_t m_vid_write_codec; - - switch_frame_t m_dummy_frame; - - bool m_flushAudio; - bool m_udptl; - - friend PBoolean FSMediaStream::Open(); -}; - - -#endif /* __FREESWITCH_MOD_OPAL__ */ - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:nil - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s: - */ +/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / + * Soft-Switch Application + * + * Version: MPL 1.1 + * + * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) + * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Contributor(s): + * Tuyan Ozipek (tuyanozipek@gmail.com) + * Lukasz Zwierko (lzwierko@gmail.com) + * Robert Jongbloed (robertj@voxlucida.com.au) + * + */ + + +#ifndef __FREESWITCH_MOD_OPAL__ +#define __FREESWITCH_MOD_OPAL__ + +#if defined(__GNUC__) && defined(HAVE_VISIBILITY) +#pragma GCC visibility push(default) +#endif + +#include +#include + +#ifndef OPAL_CHECK_VERSION + #define OPAL_CHECK_VERSION(a,b,c) 0 +#endif + +#if !OPAL_CHECK_VERSION(3,12,8) + #error OPAL is too old to use, must be >= 2.12.8 +#endif + +#include +#include +#include + +#if defined(__GNUC__) && defined(HAVE_VISIBILITY) +#pragma GCC visibility pop +#endif + +#undef strcasecmp +#undef strncasecmp + +#include + +#define MODNAME "mod_opal" + +#define HAVE_T38 OPAL_T38_CAPABILITY + + + +class FSEndPoint; +class FSManager; + + +class FSProcess : public PLibraryProcess +{ + PCLASSINFO(FSProcess, PLibraryProcess); + public: + FSProcess(); + ~FSProcess(); + + bool Initialise(switch_loadable_module_interface_t *iface); + + FSManager & GetManager() const + { + return *m_manager; + } + + protected: + FSManager * m_manager; +}; + + +struct FSListener +{ + FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { } + + PString m_name; + PIPSocket::Address m_address; + uint16_t m_port; +}; + + +class FSManager : public OpalManager +{ + PCLASSINFO(FSManager, OpalManager); + + public: + FSManager(); + + bool Initialise(switch_loadable_module_interface_t *iface); + + switch_status_t ReadConfig(int reload); + + switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; } + const PString & GetContext() const { return m_context; } + const PString & GetDialPlan() const { return m_dialplan; } + const PString & GetCodecPrefs() const { return m_codecPrefs; } + bool GetDisableTranscoding() const { return m_disableTranscoding; } + + private: + switch_endpoint_interface_t *m_FreeSwitch; + + H323EndPoint *m_h323ep; + IAX2EndPoint *m_iaxep; + FSEndPoint *m_fsep; + + PString m_context; + PString m_dialplan; + PString m_codecPrefs; + bool m_disableTranscoding; + PString m_gkAddress; + PString m_gkIdentifer; + PString m_gkInterface; + + list m_listeners; +}; + + +class FSEndPoint : public OpalLocalEndPoint +{ + PCLASSINFO(FSEndPoint, OpalLocalEndPoint); + public: + FSEndPoint(FSManager & manager); + + virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions); + + FSManager & GetManager() const { return m_manager; } + + protected: + FSManager & m_manager; +}; + + +class FSConnection; + + +class FSMediaStream : public OpalMediaStream +{ + PCLASSINFO(FSMediaStream, OpalMediaStream); + public: + FSMediaStream( + FSConnection & conn, + const OpalMediaFormat & mediaFormat, ///< Media format for stream + unsigned sessionID, ///< Session number for stream + bool isSource ///< Is a source stream + ); + + virtual PBoolean Open(); + virtual PBoolean IsSynchronous() const; + virtual PBoolean RequiresPatchThread(OpalMediaStream *) const; + + switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags); + switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags); + + protected: + virtual void InternalClose(); + int StartReadWrite(PatchPtr & mediaPatch) const; + + private: + bool CheckPatchAndLock(); + + FSConnection &m_connection; + switch_timer_t *m_switchTimer; + switch_codec_t *m_switchCodec; + switch_frame_t m_readFrame; + RTP_DataFrame m_readRTP; +}; + + +#define DECLARE_CALLBACK0(name) \ + static switch_status_t name(switch_core_session_t *session) { \ + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \ + switch_status_t name() + +#define DECLARE_CALLBACK1(name, type1, name1) \ + static switch_status_t name(switch_core_session_t *session, type1 name1) { \ + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \ + switch_status_t name(type1 name1) + +#define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \ + static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \ + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \ + switch_status_t name(type1 name1, type2 name2, type3 name3) + + + + +class FSConnection : public OpalLocalConnection +{ + PCLASSINFO(FSConnection, OpalLocalConnection) + + public: + struct outgoing_params { + switch_event_t *var_event; + switch_caller_profile_t *outbound_profile; + switch_core_session_t **new_session; + switch_memory_pool_t **pool; + switch_originate_flag_t flags; + switch_call_cause_t *cancel_cause; + switch_call_cause_t fail_cause; + }; + + FSConnection(OpalCall & call, + FSEndPoint & endpoint, + unsigned options, + OpalConnection::StringOptions * stringOptions, + outgoing_params * params); + + virtual bool OnOutgoingSetUp(); + virtual bool OnIncoming(); + virtual void OnEstablished(); + virtual void OnReleased(); + virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia); + virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean); + virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch); + virtual OpalMediaFormatList GetMediaFormats() const; + virtual PBoolean SendUserInputTone(char tone, unsigned duration); +#if HAVE_T38 + virtual void OnSwitchedT38(bool toT38, bool success); + virtual void OnSwitchingT38(bool toT38); +#endif + + DECLARE_CALLBACK0(on_init); + DECLARE_CALLBACK0(on_destroy); + DECLARE_CALLBACK0(on_routing); + DECLARE_CALLBACK0(on_execute); + DECLARE_CALLBACK0(on_hangup); + + DECLARE_CALLBACK0(on_exchange_media); + DECLARE_CALLBACK0(on_soft_execute); + + DECLARE_CALLBACK1(kill_channel, int, sig); + DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf); + DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg); + DECLARE_CALLBACK1(receive_event, switch_event_t *, event); + DECLARE_CALLBACK0(state_change); + DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id); + DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id); + DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id); + DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id); + + __inline switch_core_session_t *GetSession() const + { + return m_fsSession; + } + + __inline switch_channel_t *GetChannel() const + { + return m_fsChannel; + } + + bool IsChannelReady() const + { + return m_fsChannel != NULL && switch_channel_ready(m_fsChannel); + } + + bool NeedFlushAudio() + { + if (!m_flushAudio) + return false; + m_flushAudio = false; + return true; + } + + protected: + void SetCodecs(); + bool WaitForMedia(); +#if HAVE_T38 + void SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname); + bool IndicateSwitchedT38(); + void AbortT38(); +#endif + + switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags); + switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags); + + private: + FSEndPoint &m_endpoint; + switch_core_session_t *m_fsSession; + switch_channel_t *m_fsChannel; + PSyncPoint m_rxAudioOpened; + PSyncPoint m_txAudioOpened; + OpalMediaFormatList m_switchMediaFormats; + + // If FS ever supports more than one audio and one video, this needs to change + switch_timer_t m_read_timer; + switch_codec_t m_read_codec; + switch_codec_t m_write_codec; + + switch_timer_t m_vid_read_timer; + switch_codec_t m_vid_read_codec; + switch_codec_t m_vid_write_codec; + + switch_frame_t m_dummy_frame; + + bool m_flushAudio; + bool m_udptl; + + friend PBoolean FSMediaStream::Open(); +}; + + +#endif /* __FREESWITCH_MOD_OPAL__ */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s: + */ diff --git a/src/mod/endpoints/mod_rtmp/libamf/amf-cmake.h.in b/src/mod/endpoints/mod_rtmp/libamf/amf-cmake.h.in index 40ee41139a..327c5f6b6e 100644 --- a/src/mod/endpoints/mod_rtmp/libamf/amf-cmake.h.in +++ b/src/mod/endpoints/mod_rtmp/libamf/amf-cmake.h.in @@ -79,27 +79,27 @@ such a type exists and the standard includes do not define it. */ #cmakedefine uint8_t unsigned char -#ifdef HAVE_INTTYPES_H -#include +#ifdef HAVE_INTTYPES_H +#include #endif #include /* AMF number */ -typedef -#if SIZEOF_FLOAT == 8 -float -#elif SIZEOF_DOUBLE == 8 -double -#elif SIZEOF_LONG_DOUBLE == 8 -long double -#else -uint64_t -#endif +typedef +#if SIZEOF_FLOAT == 8 +float +#elif SIZEOF_DOUBLE == 8 +double +#elif SIZEOF_LONG_DOUBLE == 8 +long double +#else +uint64_t +#endif number64_t; /* custom read/write function type */ -typedef size_t (*read_proc_t)(void * out_buffer, size_t size, void * user_data); +typedef size_t (*read_proc_t)(void * out_buffer, size_t size, void * user_data); typedef size_t (*write_proc_t)(const void * in_buffer, size_t size, void * user_data); #endif /* __AMF_H__ */ diff --git a/src/mod/languages/mod_perl/perlibs.h.in b/src/mod/languages/mod_perl/perlibs.h.in index 6aa25587d3..1575944b4b 100644 --- a/src/mod/languages/mod_perl/perlibs.h.in +++ b/src/mod/languages/mod_perl/perlibs.h.in @@ -1 +1 @@ -#define PERL_LIB "@PERL_LIB@" +#define PERL_LIB "@PERL_LIB@" diff --git a/src/mod/say/mod_say_fa/mod_say_fa.c b/src/mod/say/mod_say_fa/mod_say_fa.c index e665e9e16e..24716fd2e6 100644 --- a/src/mod/say/mod_say_fa/mod_say_fa.c +++ b/src/mod/say/mod_say_fa/mod_say_fa.c @@ -49,9 +49,9 @@ #include #include -void gregorian_to_jalali(/* output */ int *j_y, int *j_m, int *j_d, +void gregorian_to_jalali(/* output */ int *j_y, int *j_m, int *j_d, /* input */ int g_y, int g_m, int g_d); -void jalali_to_gregorian(/* output */ int *g_y, int *g_m, int *g_d, +void jalali_to_gregorian(/* output */ int *g_y, int *g_m, int *g_d, /* input */ int j_y, int j_m, int j_d); SWITCH_MODULE_LOAD_FUNCTION(mod_say_fa_load); @@ -575,108 +575,108 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_say_fa_load) int g_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int j_days_in_month[12] = {31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29}; -void gregorian_to_jalali(int *j_y, int *j_m, int *j_d, - int g_y, int g_m, int g_d) -{ - int gy, gm, gd; - int jy, jm, jd; - long g_day_no, j_day_no; - int j_np; - - int i; - - gy = g_y-1600; - gm = g_m-1; - gd = g_d-1; - - g_day_no = 365*gy+(gy+3)/4-(gy+99)/100+(gy+399)/400; - for (i=0;i1 && ((gy%4==0 && gy%100!=0) || (gy%400==0))) - /* leap and after Feb */ - ++g_day_no; - g_day_no += gd; - - j_day_no = g_day_no-79; - - j_np = j_day_no / 12053; - j_day_no %= 12053; - - jy = 979+33*j_np+4*(j_day_no/1461); - j_day_no %= 1461; - - if (j_day_no >= 366) { - jy += (j_day_no-1)/365; - j_day_no = (j_day_no-1)%365; - } - - for (i = 0; i < 11 && j_day_no >= j_days_in_month[i]; ++i) { - j_day_no -= j_days_in_month[i]; - } - jm = i+1; - jd = j_day_no+1; - *j_y = jy; - *j_m = jm; - *j_d = jd; +void gregorian_to_jalali(int *j_y, int *j_m, int *j_d, + int g_y, int g_m, int g_d) +{ + int gy, gm, gd; + int jy, jm, jd; + long g_day_no, j_day_no; + int j_np; + + int i; + + gy = g_y-1600; + gm = g_m-1; + gd = g_d-1; + + g_day_no = 365*gy+(gy+3)/4-(gy+99)/100+(gy+399)/400; + for (i=0;i1 && ((gy%4==0 && gy%100!=0) || (gy%400==0))) + /* leap and after Feb */ + ++g_day_no; + g_day_no += gd; + + j_day_no = g_day_no-79; + + j_np = j_day_no / 12053; + j_day_no %= 12053; + + jy = 979+33*j_np+4*(j_day_no/1461); + j_day_no %= 1461; + + if (j_day_no >= 366) { + jy += (j_day_no-1)/365; + j_day_no = (j_day_no-1)%365; + } + + for (i = 0; i < 11 && j_day_no >= j_days_in_month[i]; ++i) { + j_day_no -= j_days_in_month[i]; + } + jm = i+1; + jd = j_day_no+1; + *j_y = jy; + *j_m = jm; + *j_d = jd; } -void jalali_to_gregorian(int *g_y, int *g_m, int *g_d, - int j_y, int j_m, int j_d) -{ - int gy, gm, gd; - int jy, jm, jd; - long g_day_no, j_day_no; - int leap; - - int i; - - jy = j_y-979; - jm = j_m-1; - jd = j_d-1; - - j_day_no = 365*jy + (jy/33)*8 + (jy%33+3)/4; - for (i=0; i < jm; ++i) - j_day_no += j_days_in_month[i]; - - j_day_no += jd; - - g_day_no = j_day_no+79; - - gy = 1600 + 400*(g_day_no/146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */ - g_day_no = g_day_no % 146097; - - leap = 1; - if (g_day_no >= 36525) /* 36525 = 365*100 + 100/4 */ - { - g_day_no--; - gy += 100*(g_day_no/36524); /* 36524 = 365*100 + 100/4 - 100/100 */ - g_day_no = g_day_no % 36524; - - if (g_day_no >= 365) - g_day_no++; - else - leap = 0; - } - - gy += 4*(g_day_no/1461); /* 1461 = 365*4 + 4/4 */ - g_day_no %= 1461; - - if (g_day_no >= 366) { - leap = 0; - - g_day_no--; - gy += g_day_no/365; - g_day_no = g_day_no % 365; - } - - for (i = 0; g_day_no >= g_days_in_month[i] + (i == 1 && leap); i++) - g_day_no -= g_days_in_month[i] + (i == 1 && leap); - gm = i+1; - gd = g_day_no+1; - - *g_y = gy; - *g_m = gm; - *g_d = gd; +void jalali_to_gregorian(int *g_y, int *g_m, int *g_d, + int j_y, int j_m, int j_d) +{ + int gy, gm, gd; + int jy, jm, jd; + long g_day_no, j_day_no; + int leap; + + int i; + + jy = j_y-979; + jm = j_m-1; + jd = j_d-1; + + j_day_no = 365*jy + (jy/33)*8 + (jy%33+3)/4; + for (i=0; i < jm; ++i) + j_day_no += j_days_in_month[i]; + + j_day_no += jd; + + g_day_no = j_day_no+79; + + gy = 1600 + 400*(g_day_no/146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */ + g_day_no = g_day_no % 146097; + + leap = 1; + if (g_day_no >= 36525) /* 36525 = 365*100 + 100/4 */ + { + g_day_no--; + gy += 100*(g_day_no/36524); /* 36524 = 365*100 + 100/4 - 100/100 */ + g_day_no = g_day_no % 36524; + + if (g_day_no >= 365) + g_day_no++; + else + leap = 0; + } + + gy += 4*(g_day_no/1461); /* 1461 = 365*4 + 4/4 */ + g_day_no %= 1461; + + if (g_day_no >= 366) { + leap = 0; + + g_day_no--; + gy += g_day_no/365; + g_day_no = g_day_no % 365; + } + + for (i = 0; g_day_no >= g_days_in_month[i] + (i == 1 && leap); i++) + g_day_no -= g_days_in_month[i] + (i == 1 && leap); + gm = i+1; + gd = g_day_no+1; + + *g_y = gy; + *g_m = gm; + *g_d = gd; } /* For Emacs: diff --git a/src/mod/say/mod_say_pt/mod_say_pt.c b/src/mod/say/mod_say_pt/mod_say_pt.c index b1e0db9b52..a0b9d5b863 100644 --- a/src/mod/say/mod_say_pt/mod_say_pt.c +++ b/src/mod/say/mod_say_pt/mod_say_pt.c @@ -1,546 +1,546 @@ -/* - * Copyright (c) 2007-2014, Anthony Minessale II - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The Initial Developer of the Original Code is - * Anthony Minessale II - * Portions created by the Initial Developer are Copyright (C) - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Anthony Minessale II - * Michael B. Murdock - * António Silva - * - * mod_say_pt.c -- Say for Portuguese - * - */ - -#include -#include -#include - -SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load); -SWITCH_MODULE_DEFINITION(mod_say_pt, mod_say_pt_load, NULL, NULL); - -#define say_num(num, meth) { \ - char tmp[80]; \ - switch_status_t tstatus; \ - switch_say_method_t smeth = say_args->method; \ - switch_say_type_t stype = say_args->type; \ - say_args->type = SST_ITEMS; say_args->method = meth; \ - switch_snprintf(tmp, sizeof(tmp), "%u", (unsigned)num); \ - if ((tstatus = \ - pt_say_general_count(session, tmp, say_args, args)) \ - != SWITCH_STATUS_SUCCESS) { \ - return tstatus; \ - } \ - say_args->method = smeth; say_args->type = stype; \ - } \ - - -#define say_file(...) { \ - char tmp[80]; \ - switch_status_t tstatus; \ - switch_snprintf(tmp, sizeof(tmp), __VA_ARGS__); \ - if ((tstatus = \ - switch_ivr_play_file(session, NULL, tmp, args)) \ - != SWITCH_STATUS_SUCCESS){ \ - return tstatus; \ - } \ - if (!switch_channel_ready(switch_core_session_get_channel(session))) { \ - return SWITCH_STATUS_FALSE; \ - }} \ - - -static switch_status_t play_group(switch_say_method_t method, int a, int b, int c, char *what, switch_core_session_t *session, switch_input_args_t *args) -{ - - /*a => 1xx-9xx*/ - if (a) { - switch (a) { - case 1: - if (b || c) { - say_file("digits/hundred.wav"); - } else { - say_file("digits/100.wav"); - } - break; - case 2: - say_file("digits/200.wav"); - break; - case 3: - say_file("digits/300.wav"); - break; - case 5: - say_file("digits/500.wav"); - break; - default: - say_file("digits/%d.wav", a); - say_file("digits/hundreds.wav"); - break; - } - if (b || c) { - say_file("currency/and.wav"); - } - } - /* b => 1x - 9x */ - if (b) { - if (b > 1) { - if (method == SSM_COUNTED) { - say_file("digits/h-%d0.wav", b); - } else { - say_file("digits/%d0.wav", b); - if (c > 0) { - say_file("currency/and.wav"); - } - } - } else { - say_file("digits/%d%d.wav", b, c); - c = 0; - } - } - /* c => 0 - 9*/ - if (c) { - if (method == SSM_COUNTED) { - say_file("digits/h-%d.wav", c); - } else { - say_file("digits/%d.wav", c); - } - } - - if (what && (a || b || c)) { - say_file(what); - } - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t pt_say_general_count(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) -{ - int in; - int x = 0; - int places[9] = { 0 }; - char sbuf[128] = ""; - switch_status_t status; - - if (say_args->method == SSM_ITERATED) { - if ((tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1))) { - char *p; - for (p = tosay; p && *p; p++) { - say_file("digits/%c.wav", *p); - } - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); - return SWITCH_STATUS_GENERR; - } - return SWITCH_STATUS_SUCCESS; - } - - if (!(tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1)) || strlen(tosay) > 9) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); - return SWITCH_STATUS_GENERR; - } - - in = atoi(tosay); - - if (in != 0) { - for (x = 8; x >= 0; x--) { - int num = (int) pow(10, x); - if ((places[(uint32_t) x] = in / num)) { - in -= places[(uint32_t) x] * num; - } - } - - switch (say_args->method) { - case SSM_COUNTED: - case SSM_PRONOUNCED: +/* + * Copyright (c) 2007-2014, Anthony Minessale II + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael B. Murdock + * António Silva + * + * mod_say_pt.c -- Say for Portuguese + * + */ + +#include +#include +#include + +SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load); +SWITCH_MODULE_DEFINITION(mod_say_pt, mod_say_pt_load, NULL, NULL); + +#define say_num(num, meth) { \ + char tmp[80]; \ + switch_status_t tstatus; \ + switch_say_method_t smeth = say_args->method; \ + switch_say_type_t stype = say_args->type; \ + say_args->type = SST_ITEMS; say_args->method = meth; \ + switch_snprintf(tmp, sizeof(tmp), "%u", (unsigned)num); \ + if ((tstatus = \ + pt_say_general_count(session, tmp, say_args, args)) \ + != SWITCH_STATUS_SUCCESS) { \ + return tstatus; \ + } \ + say_args->method = smeth; say_args->type = stype; \ + } \ + + +#define say_file(...) { \ + char tmp[80]; \ + switch_status_t tstatus; \ + switch_snprintf(tmp, sizeof(tmp), __VA_ARGS__); \ + if ((tstatus = \ + switch_ivr_play_file(session, NULL, tmp, args)) \ + != SWITCH_STATUS_SUCCESS){ \ + return tstatus; \ + } \ + if (!switch_channel_ready(switch_core_session_get_channel(session))) { \ + return SWITCH_STATUS_FALSE; \ + }} \ + + +static switch_status_t play_group(switch_say_method_t method, int a, int b, int c, char *what, switch_core_session_t *session, switch_input_args_t *args) +{ + + /*a => 1xx-9xx*/ + if (a) { + switch (a) { + case 1: + if (b || c) { + say_file("digits/hundred.wav"); + } else { + say_file("digits/100.wav"); + } + break; + case 2: + say_file("digits/200.wav"); + break; + case 3: + say_file("digits/300.wav"); + break; + case 5: + say_file("digits/500.wav"); + break; + default: + say_file("digits/%d.wav", a); + say_file("digits/hundreds.wav"); + break; + } + if (b || c) { + say_file("currency/and.wav"); + } + } + /* b => 1x - 9x */ + if (b) { + if (b > 1) { + if (method == SSM_COUNTED) { + say_file("digits/h-%d0.wav", b); + } else { + say_file("digits/%d0.wav", b); + if (c > 0) { + say_file("currency/and.wav"); + } + } + } else { + say_file("digits/%d%d.wav", b, c); + c = 0; + } + } + /* c => 0 - 9*/ + if (c) { + if (method == SSM_COUNTED) { + say_file("digits/h-%d.wav", c); + } else { + say_file("digits/%d.wav", c); + } + } + + if (what && (a || b || c)) { + say_file(what); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t pt_say_general_count(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + int in; + int x = 0; + int places[9] = { 0 }; + char sbuf[128] = ""; + switch_status_t status; + + if (say_args->method == SSM_ITERATED) { + if ((tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1))) { + char *p; + for (p = tosay; p && *p; p++) { + say_file("digits/%c.wav", *p); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + return SWITCH_STATUS_GENERR; + } + return SWITCH_STATUS_SUCCESS; + } + + if (!(tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1)) || strlen(tosay) > 9) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + return SWITCH_STATUS_GENERR; + } + + in = atoi(tosay); + + if (in != 0) { + for (x = 8; x >= 0; x--) { + int num = (int) pow(10, x); + if ((places[(uint32_t) x] = in / num)) { + in -= places[(uint32_t) x] * num; + } + } + + switch (say_args->method) { + case SSM_COUNTED: + case SSM_PRONOUNCED: /* specific case, one million => um milhão */ if (!places[8] && !places[7] && (places[6] == 1)) { say_file("digits/1.wav"); say_file("digits/million.wav"); - } else if ((status = play_group(SSM_PRONOUNCED, places[8], places[7], places[6], "digits/millions.wav", session, args)) != SWITCH_STATUS_SUCCESS) { - return status; - } - if ((status = play_group(SSM_PRONOUNCED, places[5], places[4], places[3], "digits/thousand.wav", session, args)) != SWITCH_STATUS_SUCCESS) { - return status; - } - if ((status = play_group(say_args->method, places[2], places[1], places[0], NULL, session, args)) != SWITCH_STATUS_SUCCESS) { - return status; - } - break; - default: - break; - } - } else { - say_file("digits/0.wav"); - } - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t pt_say_time(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) -{ - int32_t t; - switch_time_t target = 0, target_now = 0; - switch_time_exp_t tm, tm_now; - uint8_t say_date = 0, say_time = 0, say_year = 0, say_month = 0, say_dow = 0, say_day = 0, say_yesterday = 0, say_today = 0; - switch_channel_t *channel = switch_core_session_get_channel(session); - const char *tz = switch_channel_get_variable(channel, "timezone"); - - if (say_args->type == SST_TIME_MEASUREMENT) { - int64_t hours = 0; - int64_t minutes = 0; - int64_t seconds = 0; - int64_t r = 0; - - if (strchr(tosay, ':')) { - char *tme = switch_core_session_strdup(session, tosay); - char *p; - - if ((p = strrchr(tme, ':'))) { - *p++ = '\0'; - seconds = atoi(p); - if ((p = strchr(tme, ':'))) { - *p++ = '\0'; - minutes = atoi(p); - hours = atoi(tme); - } else { - minutes = atoi(tme); - } - } - } else { - if ((seconds = atol(tosay)) <= 0) { - seconds = (int64_t) switch_epoch_time_now(NULL); - } - - if (seconds >= 60) { - minutes = seconds / 60; - r = seconds % 60; - seconds = r; - } - - if (minutes >= 60) { - hours = minutes / 60; - r = minutes % 60; - minutes = r; - } - } - - if (hours) { - say_num(hours, SSM_PRONOUNCED); - if (hours == 1) { - say_file("time/hour.wav"); - } else { - say_file("time/hours.wav"); - } - } else { - say_file("digits/0.wav"); - say_file("time/hours.wav"); - } - - if (minutes) { - say_num(minutes, SSM_PRONOUNCED); - if (minutes == 1) { - say_file("time/minute.wav"); - } else { - say_file("time/minutes.wav"); - } - } else { - say_file("digits/0.wav"); - say_file("time/minutes.wav"); - } - - if (seconds) { - say_num(seconds, SSM_PRONOUNCED); - if (seconds == 1) { - say_file("time/second.wav"); - } else { - say_file("time/seconds.wav"); - } - } else { - say_file("digits/0.wav"); - say_file("time/seconds.wav"); - } - - return SWITCH_STATUS_SUCCESS; - } - - if ((t = atol(tosay)) > 0) { - target = switch_time_make(t, 0); - target_now = switch_micro_time_now(); - } else { - target = switch_micro_time_now(); - target_now = switch_micro_time_now(); - } - - if (tz) { - int check = atoi(tz); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Timezone is [%s]\n", tz); - if (check) { - switch_time_exp_tz(&tm, target, check); - switch_time_exp_tz(&tm_now, target_now, check); - } else { - switch_time_exp_tz_name(tz, &tm, target); - switch_time_exp_tz_name(tz, &tm_now, target_now); - } - } else { - switch_time_exp_lt(&tm, target); - switch_time_exp_lt(&tm_now, target_now); - } - - switch (say_args->type) { - case SST_CURRENT_DATE_TIME: - say_date = say_time = 1; - break; - case SST_CURRENT_DATE: - say_date = 1; - break; - case SST_CURRENT_TIME: - say_time = 1; - break; - case SST_SHORT_DATE_TIME: - say_time = 1; - if (tm.tm_year != tm_now.tm_year) { - say_date = 1; - break; - } - if (tm.tm_yday == tm_now.tm_yday) { - say_today = 1; - break; - } - if (tm.tm_yday == tm_now.tm_yday - 1) { - say_yesterday = 1; - break; - } - if (tm.tm_yday >= tm_now.tm_yday - 5) { - say_dow = 1; - break; - } - if (tm.tm_mon != tm_now.tm_mon) { - say_month = say_day = say_dow = 1; - break; - } - - say_month = say_day = say_dow = 1; - - break; - default: - break; - } - - if (say_today) { - say_file("time/today.wav"); - } - if (say_yesterday) { - say_file("time/yesterday.wav"); - } - if (say_dow) { - say_file("time/day-%d.wav", tm.tm_wday); - } - - if (say_date) { - say_year = say_month = say_day = say_dow = 1; - say_today = say_yesterday = 0; - } - - if (say_month) { - say_file("time/mon-%d.wav", tm.tm_mon); - } - if (say_day) { - say_num(tm.tm_mday, SSM_COUNTED); - } - if (say_year) { - say_num(tm.tm_year + 1900, SSM_PRONOUNCED); - } - - if (say_time) { - int32_t hour = tm.tm_hour, pm = 0; - - if (say_date || say_today || say_yesterday || say_dow) { - if (hour == 1) { - say_file("time/at.wav"); - } else { - say_file("time/ats.wav"); - } - } - - if (hour > 12) { - hour -= 12; - pm = 1; - } else if (hour == 12) { - pm = 1; - } else if (hour == 0) { - hour = 12; - pm = 0; - } - - say_num(hour, SSM_PRONOUNCED); - - if (tm.tm_min) { - say_file("currency/and.wav"); - say_num(tm.tm_min, SSM_PRONOUNCED); - } else { - say_file("time/oclock.wav"); - } - - say_file("time/%s.wav", pm ? "p-m" : "a-m"); - } - - return SWITCH_STATUS_SUCCESS; -} - - -static switch_status_t pt_say_money(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) -{ - char sbuf[16] = ""; /* enough for 999,999,999,999.99 (w/o the commas or leading $) */ - char *dollars = NULL; - char *cents = NULL; - - if (strlen(tosay) > 15 || !switch_strip_nonnumerics(tosay, sbuf, sizeof(sbuf)-1)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); - return SWITCH_STATUS_GENERR; - } - - dollars = sbuf; - - if ((cents = strchr(sbuf, '.'))) { - *cents++ = '\0'; - if (strlen(cents) > 2) { - cents[2] = '\0'; - } - } - - /* If positive sign - skip over" */ - if (sbuf[0] == '+') { - dollars++; - } - - /* If negative say "negative" */ - if (sbuf[0] == '-') { - say_file("currency/negative.wav"); - dollars++; - } - - /* Say dollar amount */ - pt_say_general_count(session, dollars, say_args, args); - if (atoi(dollars) == 1) { - say_file("currency/dollar.wav"); - } else { - say_file("currency/dollars.wav"); - } - - /* Say "and" */ - say_file("currency/and.wav"); - - /* Say cents */ - if (cents) { - pt_say_general_count(session, cents, say_args, args); - if (atoi(cents) == 1) { - say_file("currency/cent.wav"); - } else { - say_file("currency/cents.wav"); - } - } else { - say_file("digits/0.wav"); - say_file("currency/cents.wav"); - } - - return SWITCH_STATUS_SUCCESS; -} - - - -static switch_status_t pt_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) -{ - - switch_say_callback_t say_cb = NULL; - - switch (say_args->type) { - case SST_NUMBER: - case SST_ITEMS: - case SST_PERSONS: - case SST_MESSAGES: - say_cb = pt_say_general_count; - break; - case SST_TIME_MEASUREMENT: - case SST_CURRENT_DATE: - case SST_CURRENT_TIME: - case SST_CURRENT_DATE_TIME: - case SST_SHORT_DATE_TIME: - say_cb = pt_say_time; - break; - case SST_IP_ADDRESS: - return switch_ivr_say_ip(session, tosay, pt_say_general_count, say_args, args); - break; - case SST_NAME_SPELLED: - case SST_NAME_PHONETIC: - return switch_ivr_say_spell(session, tosay, say_args, args); - break; - case SST_CURRENCY: - say_cb = pt_say_money; - break; - default: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown Say type=[%d]\n", say_args->type); - break; - } - - if (say_cb) { - return say_cb(session, tosay, say_args, args); - } - - return SWITCH_STATUS_FALSE; -} - -SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load) -{ - switch_say_interface_t *say_interface; - /* connect my internal structure to the blank pointer passed to me */ - *module_interface = switch_loadable_module_create_module_interface(pool, modname); - say_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE); - say_interface->interface_name = "pt"; - say_interface->say_function = pt_say; - - /* indicate that the module should continue to be loaded */ - return SWITCH_STATUS_SUCCESS; -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + } else if ((status = play_group(SSM_PRONOUNCED, places[8], places[7], places[6], "digits/millions.wav", session, args)) != SWITCH_STATUS_SUCCESS) { + return status; + } + if ((status = play_group(SSM_PRONOUNCED, places[5], places[4], places[3], "digits/thousand.wav", session, args)) != SWITCH_STATUS_SUCCESS) { + return status; + } + if ((status = play_group(say_args->method, places[2], places[1], places[0], NULL, session, args)) != SWITCH_STATUS_SUCCESS) { + return status; + } + break; + default: + break; + } + } else { + say_file("digits/0.wav"); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t pt_say_time(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + int32_t t; + switch_time_t target = 0, target_now = 0; + switch_time_exp_t tm, tm_now; + uint8_t say_date = 0, say_time = 0, say_year = 0, say_month = 0, say_dow = 0, say_day = 0, say_yesterday = 0, say_today = 0; + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *tz = switch_channel_get_variable(channel, "timezone"); + + if (say_args->type == SST_TIME_MEASUREMENT) { + int64_t hours = 0; + int64_t minutes = 0; + int64_t seconds = 0; + int64_t r = 0; + + if (strchr(tosay, ':')) { + char *tme = switch_core_session_strdup(session, tosay); + char *p; + + if ((p = strrchr(tme, ':'))) { + *p++ = '\0'; + seconds = atoi(p); + if ((p = strchr(tme, ':'))) { + *p++ = '\0'; + minutes = atoi(p); + hours = atoi(tme); + } else { + minutes = atoi(tme); + } + } + } else { + if ((seconds = atol(tosay)) <= 0) { + seconds = (int64_t) switch_epoch_time_now(NULL); + } + + if (seconds >= 60) { + minutes = seconds / 60; + r = seconds % 60; + seconds = r; + } + + if (minutes >= 60) { + hours = minutes / 60; + r = minutes % 60; + minutes = r; + } + } + + if (hours) { + say_num(hours, SSM_PRONOUNCED); + if (hours == 1) { + say_file("time/hour.wav"); + } else { + say_file("time/hours.wav"); + } + } else { + say_file("digits/0.wav"); + say_file("time/hours.wav"); + } + + if (minutes) { + say_num(minutes, SSM_PRONOUNCED); + if (minutes == 1) { + say_file("time/minute.wav"); + } else { + say_file("time/minutes.wav"); + } + } else { + say_file("digits/0.wav"); + say_file("time/minutes.wav"); + } + + if (seconds) { + say_num(seconds, SSM_PRONOUNCED); + if (seconds == 1) { + say_file("time/second.wav"); + } else { + say_file("time/seconds.wav"); + } + } else { + say_file("digits/0.wav"); + say_file("time/seconds.wav"); + } + + return SWITCH_STATUS_SUCCESS; + } + + if ((t = atol(tosay)) > 0) { + target = switch_time_make(t, 0); + target_now = switch_micro_time_now(); + } else { + target = switch_micro_time_now(); + target_now = switch_micro_time_now(); + } + + if (tz) { + int check = atoi(tz); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Timezone is [%s]\n", tz); + if (check) { + switch_time_exp_tz(&tm, target, check); + switch_time_exp_tz(&tm_now, target_now, check); + } else { + switch_time_exp_tz_name(tz, &tm, target); + switch_time_exp_tz_name(tz, &tm_now, target_now); + } + } else { + switch_time_exp_lt(&tm, target); + switch_time_exp_lt(&tm_now, target_now); + } + + switch (say_args->type) { + case SST_CURRENT_DATE_TIME: + say_date = say_time = 1; + break; + case SST_CURRENT_DATE: + say_date = 1; + break; + case SST_CURRENT_TIME: + say_time = 1; + break; + case SST_SHORT_DATE_TIME: + say_time = 1; + if (tm.tm_year != tm_now.tm_year) { + say_date = 1; + break; + } + if (tm.tm_yday == tm_now.tm_yday) { + say_today = 1; + break; + } + if (tm.tm_yday == tm_now.tm_yday - 1) { + say_yesterday = 1; + break; + } + if (tm.tm_yday >= tm_now.tm_yday - 5) { + say_dow = 1; + break; + } + if (tm.tm_mon != tm_now.tm_mon) { + say_month = say_day = say_dow = 1; + break; + } + + say_month = say_day = say_dow = 1; + + break; + default: + break; + } + + if (say_today) { + say_file("time/today.wav"); + } + if (say_yesterday) { + say_file("time/yesterday.wav"); + } + if (say_dow) { + say_file("time/day-%d.wav", tm.tm_wday); + } + + if (say_date) { + say_year = say_month = say_day = say_dow = 1; + say_today = say_yesterday = 0; + } + + if (say_month) { + say_file("time/mon-%d.wav", tm.tm_mon); + } + if (say_day) { + say_num(tm.tm_mday, SSM_COUNTED); + } + if (say_year) { + say_num(tm.tm_year + 1900, SSM_PRONOUNCED); + } + + if (say_time) { + int32_t hour = tm.tm_hour, pm = 0; + + if (say_date || say_today || say_yesterday || say_dow) { + if (hour == 1) { + say_file("time/at.wav"); + } else { + say_file("time/ats.wav"); + } + } + + if (hour > 12) { + hour -= 12; + pm = 1; + } else if (hour == 12) { + pm = 1; + } else if (hour == 0) { + hour = 12; + pm = 0; + } + + say_num(hour, SSM_PRONOUNCED); + + if (tm.tm_min) { + say_file("currency/and.wav"); + say_num(tm.tm_min, SSM_PRONOUNCED); + } else { + say_file("time/oclock.wav"); + } + + say_file("time/%s.wav", pm ? "p-m" : "a-m"); + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t pt_say_money(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + char sbuf[16] = ""; /* enough for 999,999,999,999.99 (w/o the commas or leading $) */ + char *dollars = NULL; + char *cents = NULL; + + if (strlen(tosay) > 15 || !switch_strip_nonnumerics(tosay, sbuf, sizeof(sbuf)-1)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + return SWITCH_STATUS_GENERR; + } + + dollars = sbuf; + + if ((cents = strchr(sbuf, '.'))) { + *cents++ = '\0'; + if (strlen(cents) > 2) { + cents[2] = '\0'; + } + } + + /* If positive sign - skip over" */ + if (sbuf[0] == '+') { + dollars++; + } + + /* If negative say "negative" */ + if (sbuf[0] == '-') { + say_file("currency/negative.wav"); + dollars++; + } + + /* Say dollar amount */ + pt_say_general_count(session, dollars, say_args, args); + if (atoi(dollars) == 1) { + say_file("currency/dollar.wav"); + } else { + say_file("currency/dollars.wav"); + } + + /* Say "and" */ + say_file("currency/and.wav"); + + /* Say cents */ + if (cents) { + pt_say_general_count(session, cents, say_args, args); + if (atoi(cents) == 1) { + say_file("currency/cent.wav"); + } else { + say_file("currency/cents.wav"); + } + } else { + say_file("digits/0.wav"); + say_file("currency/cents.wav"); + } + + return SWITCH_STATUS_SUCCESS; +} + + + +static switch_status_t pt_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + + switch_say_callback_t say_cb = NULL; + + switch (say_args->type) { + case SST_NUMBER: + case SST_ITEMS: + case SST_PERSONS: + case SST_MESSAGES: + say_cb = pt_say_general_count; + break; + case SST_TIME_MEASUREMENT: + case SST_CURRENT_DATE: + case SST_CURRENT_TIME: + case SST_CURRENT_DATE_TIME: + case SST_SHORT_DATE_TIME: + say_cb = pt_say_time; + break; + case SST_IP_ADDRESS: + return switch_ivr_say_ip(session, tosay, pt_say_general_count, say_args, args); + break; + case SST_NAME_SPELLED: + case SST_NAME_PHONETIC: + return switch_ivr_say_spell(session, tosay, say_args, args); + break; + case SST_CURRENCY: + say_cb = pt_say_money; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown Say type=[%d]\n", say_args->type); + break; + } + + if (say_cb) { + return say_cb(session, tosay, say_args, args); + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load) +{ + switch_say_interface_t *say_interface; + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + say_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE); + say_interface->interface_name = "pt"; + say_interface->say_function = pt_say; + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */