-/*****************************************************************************\r
-* *\r
-* DIGITAL SIGNAL PROCESSING TOOLS *\r
-* Version 1.03, 2001/06/15 *\r
-* (c) 1999 - Laurent de Soras *\r
-* *\r
-* FFTReal.cpp *\r
-* Fourier transformation of real number arrays. *\r
-* Portable ISO C++ *\r
-* *\r
-* Tab = 3 *\r
-*****************************************************************************/\r
-\r
-\r
-\r
-/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/\r
-\r
-#include "FFTReal.h"\r
-\r
-#include <cassert>\r
-#include <cmath>\r
-\r
-\r
-\r
-#if defined (_MSC_VER)\r
-#pragma pack (push, 8)\r
-#endif // _MSC_VER\r
-\r
-\r
-\r
-/*\\\ PUBLIC MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Constructor */\r
-/* Input parameters: */\r
-/* - length: length of the array on which we want to do a FFT. */\r
-/* Range: power of 2 only, > 0. */\r
-/* Throws: std::bad_alloc, anything */\r
-/*==========================================================================*/\r
-\r
-FFTReal::FFTReal (const long length)\r
-: _length (length)\r
-, _nbr_bits (int (floor (log (length) / log (2) + 0.5)))\r
-, _bit_rev_lut (int (floor (log (length) / log (2) + 0.5)))\r
-, _trigo_lut (int (floor (log (length) / log (2) + 0.5)))\r
-, _sqrt2_2 (flt_t (sqrt (2) * 0.5))\r
-{\r
- assert ((1L << _nbr_bits) == length);\r
-\r
- _buffer_ptr = 0;\r
- if (_nbr_bits > 2)\r
- {\r
- _buffer_ptr = new flt_t [_length];\r
- }\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Destructor */\r
-/*==========================================================================*/\r
-\r
-FFTReal::~FFTReal (void)\r
-{\r
- delete [] _buffer_ptr;\r
- _buffer_ptr = 0;\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: do_fft */\r
-/* Description: Compute the FFT of the array. */\r
-/* Input parameters: */\r
-/* - x: pointer on the source array (time). */\r
-/* Output parameters: */\r
-/* - f: pointer on the destination array (frequencies). */\r
-/* f [0...length(x)/2] = real values, */\r
-/* f [length(x)/2+1...length(x)-1] = imaginary values of */\r
-/* coefficents 1...length(x)/2-1. */\r
-/* Throws: Nothing */\r
-/*==========================================================================*/\r
-\r
-void FFTReal::do_fft (flt_t f [], const flt_t x []) const\r
-{\r
-\r
-/*______________________________________________\r
- *\r
- * General case\r
- *______________________________________________\r
- */\r
-\r
- if (_nbr_bits > 2)\r
- {\r
- flt_t * sf;\r
- flt_t * df;\r
-\r
- if (_nbr_bits & 1)\r
- {\r
- df = _buffer_ptr;\r
- sf = f;\r
- }\r
- else\r
- {\r
- df = f;\r
- sf = _buffer_ptr;\r
- }\r
-\r
- /* Do the transformation in several pass */\r
- {\r
- int pass;\r
- long nbr_coef;\r
- long h_nbr_coef;\r
- long d_nbr_coef;\r
- long coef_index;\r
-\r
- /* First and second pass at once */\r
- {\r
- const long * const bit_rev_lut_ptr = _bit_rev_lut.get_ptr ();\r
- coef_index = 0;\r
- do\r
- {\r
- const long rev_index_0 = bit_rev_lut_ptr [coef_index];\r
- const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1];\r
- const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2];\r
- const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3];\r
-\r
- flt_t * const df2 = df + coef_index;\r
- df2 [1] = x [rev_index_0] - x [rev_index_1];\r
- df2 [3] = x [rev_index_2] - x [rev_index_3];\r
-\r
- const flt_t sf_0 = x [rev_index_0] + x [rev_index_1];\r
- const flt_t sf_2 = x [rev_index_2] + x [rev_index_3];\r
-\r
- df2 [0] = sf_0 + sf_2;\r
- df2 [2] = sf_0 - sf_2;\r
- \r
- coef_index += 4;\r
- }\r
- while (coef_index < _length);\r
- }\r
-\r
- /* Third pass */\r
- {\r
- coef_index = 0;\r
- const flt_t sqrt2_2 = _sqrt2_2;\r
- do\r
- {\r
- flt_t v;\r
-\r
- sf [coef_index] = df [coef_index] + df [coef_index + 4];\r
- sf [coef_index + 4] = df [coef_index] - df [coef_index + 4];\r
- sf [coef_index + 2] = df [coef_index + 2];\r
- sf [coef_index + 6] = df [coef_index + 6];\r
-\r
- v = (df [coef_index + 5] - df [coef_index + 7]) * sqrt2_2;\r
- sf [coef_index + 1] = df [coef_index + 1] + v;\r
- sf [coef_index + 3] = df [coef_index + 1] - v;\r
-\r
- v = (df [coef_index + 5] + df [coef_index + 7]) * sqrt2_2;\r
- sf [coef_index + 5] = v + df [coef_index + 3];\r
- sf [coef_index + 7] = v - df [coef_index + 3];\r
-\r
- coef_index += 8;\r
- }\r
- while (coef_index < _length);\r
- }\r
-\r
- /* Next pass */\r
- for (pass = 3; pass < _nbr_bits; ++pass)\r
- {\r
- coef_index = 0;\r
- nbr_coef = 1 << pass;\r
- h_nbr_coef = nbr_coef >> 1;\r
- d_nbr_coef = nbr_coef << 1;\r
- const flt_t * const cos_ptr = _trigo_lut.get_ptr (pass);\r
- do\r
- {\r
- long i;\r
- const flt_t * const sf1r = sf + coef_index;\r
- const flt_t * const sf2r = sf1r + nbr_coef;\r
- flt_t * const dfr = df + coef_index;\r
- flt_t * const dfi = dfr + nbr_coef;\r
-\r
- /* Extreme coefficients are always real */\r
- dfr [0] = sf1r [0] + sf2r [0];\r
- dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] =\r
- dfr [h_nbr_coef] = sf1r [h_nbr_coef];\r
- dfi [h_nbr_coef] = sf2r [h_nbr_coef];\r
-\r
- /* Others are conjugate complex numbers */\r
- const flt_t * const sf1i = sf1r + h_nbr_coef;\r
- const flt_t * const sf2i = sf1i + nbr_coef;\r
- for (i = 1; i < h_nbr_coef; ++ i)\r
- {\r
- const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef);\r
- const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);\r
- flt_t v;\r
-\r
- v = sf2r [i] * c - sf2i [i] * s;\r
- dfr [i] = sf1r [i] + v;\r
- dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] =\r
-\r
- v = sf2r [i] * s + sf2i [i] * c;\r
- dfi [i] = v + sf1i [i];\r
- dfi [nbr_coef - i] = v - sf1i [i];\r
- }\r
-\r
- coef_index += d_nbr_coef;\r
- }\r
- while (coef_index < _length);\r
-\r
- /* Prepare to the next pass */\r
- {\r
- flt_t * const temp_ptr = df;\r
- df = sf;\r
- sf = temp_ptr;\r
- }\r
- }\r
- }\r
- }\r
-\r
-/*______________________________________________\r
- *\r
- * Special cases\r
- *______________________________________________\r
- */\r
-\r
- /* 4-point FFT */\r
- else if (_nbr_bits == 2)\r
- {\r
- f [1] = x [0] - x [2];\r
- f [3] = x [1] - x [3];\r
-\r
- const flt_t b_0 = x [0] + x [2];\r
- const flt_t b_2 = x [1] + x [3];\r
- \r
- f [0] = b_0 + b_2;\r
- f [2] = b_0 - b_2;\r
- }\r
-\r
- /* 2-point FFT */\r
- else if (_nbr_bits == 1)\r
- {\r
- f [0] = x [0] + x [1];\r
- f [1] = x [0] - x [1];\r
- }\r
-\r
- /* 1-point FFT */\r
- else\r
- {\r
- f [0] = x [0];\r
- }\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: do_ifft */\r
-/* Description: Compute the inverse FFT of the array. Notice that */\r
-/* IFFT (FFT (x)) = x * length (x). Data must be */\r
-/* post-scaled. */\r
-/* Input parameters: */\r
-/* - f: pointer on the source array (frequencies). */\r
-/* f [0...length(x)/2] = real values, */\r
-/* f [length(x)/2+1...length(x)] = imaginary values of */\r
-/* coefficents 1...length(x)-1. */\r
-/* Output parameters: */\r
-/* - x: pointer on the destination array (time). */\r
-/* Throws: Nothing */\r
-/*==========================================================================*/\r
-\r
-void FFTReal::do_ifft (const flt_t f [], flt_t x []) const\r
-{\r
-\r
-/*______________________________________________\r
- *\r
- * General case\r
- *______________________________________________\r
- */\r
-\r
- if (_nbr_bits > 2)\r
- {\r
- flt_t * sf = const_cast <flt_t *> (f);\r
- flt_t * df;\r
- flt_t * df_temp;\r
-\r
- if (_nbr_bits & 1)\r
- {\r
- df = _buffer_ptr;\r
- df_temp = x;\r
- }\r
- else\r
- {\r
- df = x;\r
- df_temp = _buffer_ptr;\r
- }\r
-\r
- /* Do the transformation in several pass */\r
- {\r
- int pass;\r
- long nbr_coef;\r
- long h_nbr_coef;\r
- long d_nbr_coef;\r
- long coef_index;\r
-\r
- /* First pass */\r
- for (pass = _nbr_bits - 1; pass >= 3; --pass)\r
- {\r
- coef_index = 0;\r
- nbr_coef = 1 << pass;\r
- h_nbr_coef = nbr_coef >> 1;\r
- d_nbr_coef = nbr_coef << 1;\r
- const flt_t *const cos_ptr = _trigo_lut.get_ptr (pass);\r
- do\r
- {\r
- long i;\r
- const flt_t * const sfr = sf + coef_index;\r
- const flt_t * const sfi = sfr + nbr_coef;\r
- flt_t * const df1r = df + coef_index;\r
- flt_t * const df2r = df1r + nbr_coef;\r
-\r
- /* Extreme coefficients are always real */\r
- df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef]\r
- df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef]\r
- df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2;\r
- df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2;\r
-\r
- /* Others are conjugate complex numbers */\r
- flt_t * const df1i = df1r + h_nbr_coef;\r
- flt_t * const df2i = df1i + nbr_coef;\r
- for (i = 1; i < h_nbr_coef; ++ i)\r
- {\r
- df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i]\r
- df1i [i] = sfi [i] - sfi [nbr_coef - i];\r
-\r
- const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef);\r
- const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);\r
- const flt_t vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i]\r
- const flt_t vi = sfi [i] + sfi [nbr_coef - i];\r
-\r
- df2r [i] = vr * c + vi * s;\r
- df2i [i] = vi * c - vr * s;\r
- }\r
-\r
- coef_index += d_nbr_coef;\r
- }\r
- while (coef_index < _length);\r
-\r
- /* Prepare to the next pass */\r
- if (pass < _nbr_bits - 1)\r
- {\r
- flt_t * const temp_ptr = df;\r
- df = sf;\r
- sf = temp_ptr;\r
- }\r
- else\r
- {\r
- sf = df;\r
- df = df_temp;\r
- }\r
- }\r
-\r
- /* Antepenultimate pass */\r
- {\r
- const flt_t sqrt2_2 = _sqrt2_2;\r
- coef_index = 0;\r
- do\r
- {\r
- df [coef_index] = sf [coef_index] + sf [coef_index + 4];\r
- df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4];\r
- df [coef_index + 2] = sf [coef_index + 2] * 2;\r
- df [coef_index + 6] = sf [coef_index + 6] * 2;\r
-\r
- df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3];\r
- df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7];\r
-\r
- const flt_t vr = sf [coef_index + 1] - sf [coef_index + 3];\r
- const flt_t vi = sf [coef_index + 5] + sf [coef_index + 7];\r
-\r
- df [coef_index + 5] = (vr + vi) * sqrt2_2;\r
- df [coef_index + 7] = (vi - vr) * sqrt2_2;\r
-\r
- coef_index += 8;\r
- }\r
- while (coef_index < _length);\r
- }\r
-\r
- /* Penultimate and last pass at once */\r
- {\r
- coef_index = 0;\r
- const long * bit_rev_lut_ptr = _bit_rev_lut.get_ptr ();\r
- const flt_t * sf2 = df;\r
- do\r
- {\r
- {\r
- const flt_t b_0 = sf2 [0] + sf2 [2];\r
- const flt_t b_2 = sf2 [0] - sf2 [2];\r
- const flt_t b_1 = sf2 [1] * 2;\r
- const flt_t b_3 = sf2 [3] * 2;\r
-\r
- x [bit_rev_lut_ptr [0]] = b_0 + b_1;\r
- x [bit_rev_lut_ptr [1]] = b_0 - b_1;\r
- x [bit_rev_lut_ptr [2]] = b_2 + b_3;\r
- x [bit_rev_lut_ptr [3]] = b_2 - b_3;\r
- }\r
- {\r
- const flt_t b_0 = sf2 [4] + sf2 [6];\r
- const flt_t b_2 = sf2 [4] - sf2 [6];\r
- const flt_t b_1 = sf2 [5] * 2;\r
- const flt_t b_3 = sf2 [7] * 2;\r
-\r
- x [bit_rev_lut_ptr [4]] = b_0 + b_1;\r
- x [bit_rev_lut_ptr [5]] = b_0 - b_1;\r
- x [bit_rev_lut_ptr [6]] = b_2 + b_3;\r
- x [bit_rev_lut_ptr [7]] = b_2 - b_3;\r
- }\r
-\r
- sf2 += 8;\r
- coef_index += 8;\r
- bit_rev_lut_ptr += 8;\r
- }\r
- while (coef_index < _length);\r
- }\r
- }\r
- }\r
-\r
-/*______________________________________________\r
- *\r
- * Special cases\r
- *______________________________________________\r
- */\r
-\r
- /* 4-point IFFT */\r
- else if (_nbr_bits == 2)\r
- {\r
- const flt_t b_0 = f [0] + f [2];\r
- const flt_t b_2 = f [0] - f [2];\r
-\r
- x [0] = b_0 + f [1] * 2;\r
- x [2] = b_0 - f [1] * 2;\r
- x [1] = b_2 + f [3] * 2;\r
- x [3] = b_2 - f [3] * 2;\r
- }\r
-\r
- /* 2-point IFFT */\r
- else if (_nbr_bits == 1)\r
- {\r
- x [0] = f [0] + f [1];\r
- x [1] = f [0] - f [1];\r
- }\r
-\r
- /* 1-point IFFT */\r
- else\r
- {\r
- x [0] = f [0];\r
- }\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: rescale */\r
-/* Description: Scale an array by divide each element by its length. */\r
-/* This function should be called after FFT + IFFT. */\r
-/* Input/Output parameters: */\r
-/* - x: pointer on array to rescale (time or frequency). */\r
-/* Throws: Nothing */\r
-/*==========================================================================*/\r
-\r
-void FFTReal::rescale (flt_t x []) const\r
-{\r
- const flt_t mul = flt_t (1.0 / _length);\r
- long i = _length - 1;\r
-\r
- do\r
- {\r
- x [i] *= mul;\r
- --i;\r
- }\r
- while (i >= 0);\r
-}\r
-\r
-\r
-\r
-/*\\\ NESTED CLASS MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Constructor */\r
-/* Input parameters: */\r
-/* - nbr_bits: number of bits of the array on which we want to do a */\r
-/* FFT. Range: > 0 */\r
-/* Throws: std::bad_alloc */\r
-/*==========================================================================*/\r
-\r
-FFTReal::BitReversedLUT::BitReversedLUT (const int nbr_bits)\r
-{\r
- long length;\r
- long cnt;\r
- long br_index;\r
- long bit;\r
-\r
- length = 1L << nbr_bits;\r
- _ptr = new long [length];\r
-\r
- br_index = 0;\r
- _ptr [0] = 0;\r
- for (cnt = 1; cnt < length; ++cnt)\r
- {\r
- /* ++br_index (bit reversed) */\r
- bit = length >> 1;\r
- while (((br_index ^= bit) & bit) == 0)\r
- {\r
- bit >>= 1;\r
- }\r
-\r
- _ptr [cnt] = br_index;\r
- }\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Destructor */\r
-/*==========================================================================*/\r
-\r
-FFTReal::BitReversedLUT::~BitReversedLUT (void)\r
-{\r
- delete [] _ptr;\r
- _ptr = 0;\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Constructor */\r
-/* Input parameters: */\r
-/* - nbr_bits: number of bits of the array on which we want to do a */\r
-/* FFT. Range: > 0 */\r
-/* Throws: std::bad_alloc, anything */\r
-/*==========================================================================*/\r
-\r
-FFTReal::TrigoLUT::TrigoLUT (const int nbr_bits)\r
-{\r
- long total_len;\r
-\r
- _ptr = 0;\r
- if (nbr_bits > 3)\r
- {\r
- total_len = (1L << (nbr_bits - 1)) - 4;\r
- _ptr = new flt_t [total_len];\r
-\r
- const double PI = atan (1) * 4;\r
- for (int level = 3; level < nbr_bits; ++level)\r
- {\r
- const long level_len = 1L << (level - 1);\r
- flt_t * const level_ptr = const_cast<flt_t *> (get_ptr (level));\r
- const double mul = PI / (level_len << 1);\r
-\r
- for (long i = 0; i < level_len; ++ i)\r
- {\r
- level_ptr [i] = (flt_t) cos (i * mul);\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-/*==========================================================================*/\r
-/* Name: Destructor */\r
-/*==========================================================================*/\r
-\r
-FFTReal::TrigoLUT::~TrigoLUT (void)\r
-{\r
- delete [] _ptr;\r
- _ptr = 0;\r
-}\r
-\r
-\r
-\r
-#if defined (_MSC_VER)\r
-#pragma pack (pop)\r
-#endif // _MSC_VER\r
-\r
-\r
-\r
-/*****************************************************************************\r
-\r
- LEGAL\r
-\r
- Source code may be freely used for any purpose, including commercial\r
- applications. Programs must display in their "About" dialog-box (or\r
- documentation) a text telling they use these routines by Laurent de Soras.\r
- Modified source code can be distributed, but modifications must be clearly\r
- indicated.\r
-\r
- CONTACT\r
-\r
- Laurent de Soras\r
- 92 avenue Albert 1er\r
- 92500 Rueil-Malmaison\r
- France\r
-\r
- ldesoras@club-internet.fr\r
-\r
-*****************************************************************************/\r
-\r
-\r
-\r
-/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/\r
+/*****************************************************************************
+* *
+* 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 <cassert>
+#include <cmath>
+
+
+
+#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 <flt_t *> (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<flt_t *> (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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
-The Cepstral SDK for Windows should be placed in c:\dev\cepstral\r
-ex. C:\dev\cepstral\sdk\include\r
-This SDK can be obtained from http://cepstral.com/\r
+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
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);\r
- UserName=(wchar_t*)GlobalAlloc(GMEÂ M_ZEROINIT, size);\r
- ret = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, size);\r
- if(ret == 0)\r
- getError(GetLastError());\r
+ 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);
}
-#ifndef LIBCTB_WIN32_GETOPT_H_INCLUDED_\r
-#define LIBCTB_WIN32_GETOPT_H_INCLUDED_\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// Name: win32/getopt.h\r
-// Purpose:\r
-// Author: Joachim Buermann\r
-// Copyright: (c) 2010 Joachim Buermann\r
-// Licence: wxWindows licence\r
-/////////////////////////////////////////////////////////////////////////////\r
-\r
-extern char* optarg;\r
-extern int optind;\r
-\r
-int getopt(int argc, char* argv[], char* optstring);\r
-\r
-#endif\r
+#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
-/*\r
- *\r
- *\r
- * Win32 include file\r
- * for accessing the 32-bit GPIB DLL (gpib-32.dll)\r
- *\r
- *\r
- * Contains user variables (ibsta, iberr, ibcnt, ibcntl),\r
- * function prototypes and useful defined constants for\r
- * calling NI-488 and NI-488.2 routines from a Microsoft\r
- * C/C++ Win32 application.\r
- *\r
- *\r
- * Copyright 1998 National Instruments Corporation\r
- *\r
- */\r
-\r
-#ifndef DECL_32_H // ensure we are only included once\r
-#define DECL_32_H\r
-\r
-#include "windows.h"\r
-\r
-#ifdef __cplusplus\r
-extern "C" {\r
-#endif\r
-\r
-\r
-/***************************************************************************/\r
-/* HANDY CONSTANTS FOR USE BY APPLICATION PROGRAMS ... */\r
-/***************************************************************************/\r
-#define UNL 0x3f /* GPIB unlisten command */\r
-#define UNT 0x5f /* GPIB untalk command */\r
-#define GTL 0x01 /* GPIB go to local */\r
-#define SDC 0x04 /* GPIB selected device clear */\r
-#define PPC 0x05 /* GPIB parallel poll configure */\r
-#define GET 0x08 /* GPIB group execute trigger */\r
-#define TCT 0x09 /* GPIB take control */\r
-#define LLO 0x11 /* GPIB local lock out */\r
-#define DCL 0x14 /* GPIB device clear */\r
-#define PPU 0.16 /* GPIB parallel poll unconfigure */\r
-#define SPE 0x18 /* GPIB serial poll enable */\r
-#define SPD 0x19 /* GPIB serial poll disable */\r
-#define PPE 0x60 /* GPIB parallel poll enable */\r
-#define PPD 0x70 /* GPIB parallel poll disable */\r
-\r
-/* GPIB status bit vector : */\r
-/* global variable ibsta and wait mask */\r
-\r
-#define ERR (1<<15) /* Error detected */\r
-#define TIMO (1<<14) /* Timeout */\r
-#define END (1<<13) /* EOI or EOS detected */\r
-#define SRQI (1<<12) /* SRQ detected by CIC */\r
-#define RQS (1<<11) /* Device needs service */\r
-#define CMPL (1<<8) /* I/O completed */\r
-#define LOK (1<<7) /* Local lockout state */\r
-#define REM (1<<6) /* Remote state */\r
-#define CIC (1<<5) /* Controller-in-Charge */\r
-#define ATN (1<<4) /* Attention asserted */\r
-#define TACS (1<<3) /* Talker active */\r
-#define LACS (1<<2) /* Listener active */\r
-#define DTAS (1<<1) /* Device trigger state */\r
-#define DCAS (1<<0) /* Device clear state */\r
-\r
-/* Error messages returned in global variable iberr */\r
-\r
-#define EDVR 0 /* System error */\r
-#define ECIC 1 /* Function requires GPIB board to be CIC */\r
-#define ENOL 2 /* Write function detected no Listeners */\r
-#define EADR 3 /* Interface board not addressed correctly*/\r
-#define EARG 4 /* Invalid argument to function call */\r
-#define ESAC 5 /* Function requires GPIB board to be SAC */\r
-#define EABO 6 /* I/O operation aborted */\r
-#define ENEB 7 /* Non-existent interface board */\r
-#define EDMA 8 /* Error performing DMA */\r
-#define EOIP 10 /* I/O operation started before previous */\r
- /* operation completed */\r
-#define ECAP 11 /* No capability for intended operation */\r
-#define EFSO 12 /* File system operation error */\r
-#define EBUS 14 /* Command error during device call */\r
-#define ESTB 15 /* Serial poll status byte lost */\r
-#define ESRQ 16 /* SRQ remains asserted */\r
-#define ETAB 20 /* The return buffer is full. */\r
-#define ELCK 21 /* Address or board is locked. */\r
-\r
-/* EOS mode bits */\r
-\r
-#define BIN (1<<12) /* Eight bit compare */\r
-#define XEOS (1<<11) /* Send END with EOS byte */\r
-#define REOS (1<<10) /* Terminate read on EOS */\r
-\r
-/* Timeout values and meanings */\r
-\r
-#define TNONE 0 /* Infinite timeout (disabled) */\r
-#define T10us 1 /* Timeout of 10 us (ideal) */\r
-#define T30us 2 /* Timeout of 30 us (ideal) */\r
-#define T100us 3 /* Timeout of 100 us (ideal) */\r
-#define T300us 4 /* Timeout of 300 us (ideal) */\r
-#define T1ms 5 /* Timeout of 1 ms (ideal) */\r
-#define T3ms 6 /* Timeout of 3 ms (ideal) */\r
-#define T10ms 7 /* Timeout of 10 ms (ideal) */\r
-#define T30ms 8 /* Timeout of 30 ms (ideal) */\r
-#define T100ms 9 /* Timeout of 100 ms (ideal) */\r
-#define T300ms 10 /* Timeout of 300 ms (ideal) */\r
-#define T1s 11 /* Timeout of 1 s (ideal) */\r
-#define T3s 12 /* Timeout of 3 s (ideal) */\r
-#define T10s 13 /* Timeout of 10 s (ideal) */\r
-#define T30s 14 /* Timeout of 30 s (ideal) */\r
-#define T100s 15 /* Timeout of 100 s (ideal) */\r
-#define T300s 16 /* Timeout of 300 s (ideal) */\r
-#define T1000s 17 /* Timeout of 1000 s (ideal) */\r
-\r
-\r
-/* IBLN Constants */\r
-#define NO_SAD 0\r
-#define ALL_SAD -1\r
-\r
-\r
-/* The following constants are used for the second parameter of the\r
- * ibconfig function. They are the "option" selection codes.\r
- */\r
-#define IbcPAD 0x0001 /* Primary Address */\r
-#define IbcSAD 0x0002 /* Secondary Address */\r
-#define IbcTMO 0x0003 /* Timeout Value */\r
-#define IbcEOT 0x0004 /* Send EOI with last data byte? */\r
-#define IbcPPC 0x0005 /* Parallel Poll Configure */\r
-#define IbcREADDR 0x0006 /* Repeat Addressing */\r
-#define IbcAUTOPOLL 0x0007 /* Disable Auto Serial Polling */\r
-#define IbcCICPROT 0x0008 /* Use the CIC Protocol? */\r
-#define IbcIRQ 0x0009 /* Use PIO for I/O */\r
-#define IbcSC 0x000A /* Board is System Controller? */\r
-#define IbcSRE 0x000B /* Assert SRE on device calls? */\r
-#define IbcEOSrd 0x000C /* Terminate reads on EOS */\r
-#define IbcEOSwrt 0x000D /* Send EOI with EOS character */\r
-#define IbcEOScmp 0x000E /* Use 7 or 8-bit EOS compare */\r
-#define IbcEOSchar 0x000F /* The EOS character. */\r
-#define IbcPP2 0x0010 /* Use Parallel Poll Mode 2. */\r
-#define IbcTIMING 0x0011 /* NORMAL, HIGH, or VERY_HIGH timing. */\r
-#define IbcDMA 0x0012 /* Use DMA for I/O */\r
-#define IbcReadAdjust 0x0013 /* Swap bytes during an ibrd. */\r
-#define IbcWriteAdjust 0x014 /* Swap bytes during an ibwrt. */\r
-#define IbcSendLLO 0x0017 /* Enable/disable the sending of LLO. */\r
-#define IbcSPollTime 0x0018 /* Set the timeout value for serial polls. */\r
-#define IbcPPollTime 0x0019 /* Set the parallel poll length period. */\r
-#define IbcEndBitIsNormal 0x001A /* Remove EOS from END bit of IBSTA. */\r
-#define IbcUnAddr 0x001B /* Enable/disable device unaddressing. */\r
-#define IbcSignalNumber 0x001C /* Set UNIX signal number - unsupported */\r
-#define IbcBlockIfLocked 0x001D /* Enable/disable blocking for locked boards/devices */\r
-#define IbcHSCableLength 0x001F /* Length of cable specified for high speed timing.*/\r
-#define IbcIst 0x0020 /* Set the IST bit. */\r
-#define IbcRsv 0x0021 /* Set the RSV byte. */\r
-\r
-/*\r
- * Constants that can be used (in addition to the ibconfig constants)\r
- * when calling the ibask() function.\r
- */\r
-\r
-#define IbaPAD IbcPAD\r
-#define IbaSAD IbcSAD\r
-#define IbaTMO IbcTMO\r
-#define IbaEOT IbcEOT\r
-#define IbaPPC IbcPPC\r
-#define IbaREADDR IbcREADDR\r
-#define IbaAUTOPOLL IbcAUTOPOLL\r
-#define IbaCICPROT IbcCICPROT\r
-#define IbaIRQ IbcIRQ\r
-#define IbaSC IbcSC\r
-#define IbaSRE IbcSRE\r
-#define IbaEOSrd IbcEOSrd\r
-#define IbaEOSwrt IbcEOSwrt\r
-#define IbaEOScmp IbcEOScmp\r
-#define IbaEOSchar IbcEOSchar\r
-#define IbaPP2 IbcPP2\r
-#define IbaTIMING IbcTIMING\r
-#define IbaDMA IbcDMA\r
-#define IbaReadAdjust IbcReadAdjust\r
-#define IbaWriteAdjust IbcWriteAdjust\r
-#define IbaSendLLO IbcSendLLO\r
-#define IbaSPollTime IbcSPollTime\r
-#define IbaPPollTime IbcPPollTime\r
-#define IbaEndBitIsNormal IbcEndBitIsNormal\r
-#define IbaUnAddr IbcUnAddr\r
-#define IbaSignalNumber IbcSignalNumber\r
-#define IbaBlockIfLocked IbcBlockIfLocked\r
-#define IbaHSCableLength IbcHSCableLength\r
-#define IbaIst IbcIst\r
-#define IbaRsv IbcRsv\r
-\r
-#define IbaBNA 0x0200 /* A device's access board. */\r
-\r
-\r
-/* Values used by the Send 488.2 command. */\r
-#define NULLend 0x00 /* Do nothing at the end of a transfer.*/\r
-#define NLend 0x01 /* Send NL with EOI after a transfer. */\r
-#define DABend 0x02 /* Send EOI with the last DAB. */\r
-\r
-/* Value used by the 488.2 Receive command.\r
- */\r
-#define STOPend 0x0100\r
-\r
-\r
-/* Address type (for 488.2 calls) */\r
-\r
-typedef short Addr4882_t; /* System dependent: must be 16 bits */\r
-\r
-/*\r
- * This macro can be used to easily create an entry in address list\r
- * that is required by many of the 488.2 functions. The primary address goes in the\r
- * lower 8-bits and the secondary address goes in the upper 8-bits.\r
- */\r
-#define MakeAddr(pad, sad) ((Addr4882_t)(((pad)&0xFF) | ((sad)<<8)))\r
-\r
-/*\r
- * This value is used to terminate an address list. It should be\r
- * assigned to the last entry.\r
- */\r
-#ifndef NOADDR\r
-#define NOADDR (Addr4882_t)((unsigned short)0xFFFF)\r
-#endif\r
-\r
-/*\r
- * The following two macros are used to "break apart" an address list\r
- * entry. They take an unsigned integer and return either the primary\r
- * or secondary address stored in the integer.\r
- */\r
-#define GetPAD(val) ((val) & 0xFF)\r
-#define GetSAD(val) (((val) >> 8) & 0xFF)\r
-\r
-/* iblines constants */\r
-\r
-#define ValidEOI (short)0x0080\r
-#define ValidATN (short)0x0040\r
-#define ValidSRQ (short)0x0020\r
-#define ValidREN (short)0x0010\r
-#define ValidIFC (short)0x0008\r
-#define ValidNRFD (short)0x0004\r
-#define ValidNDAC (short)0x0002\r
-#define ValidDAV (short)0x0001\r
-#define BusEOI (short)0x8000\r
-#define BusATN (short)0x4000\r
-#define BusSRQ (short)0x2000\r
-#define BusREN (short)0x1000\r
-#define BusIFC (short)0x0800\r
-#define BusNRFD (short)0x0400\r
-#define BusNDAC (short)0x0200\r
-#define BusDAV (short)0x0100\r
-\r
-/****\r
- **** typedef for ibnotify callback ****\r
- ****/\r
-typedef int (__stdcall * GpibNotifyCallback_t)(int, int, int, long, PVOID);\r
-\r
-#define IBNOTIFY_REARM_FAILED 0xE00A003F\r
-\r
-\r
-/*************************************************************************/\r
-/* */\r
-/* iblockx and ibunlockx definitions --- for use with GPIB-ENET only !! */\r
-/* */\r
-/*************************************************************************/\r
-#define TIMMEDIATE -1\r
-#define TINFINITE -2\r
-#define MAX_LOCKSHARENAME_LENGTH 64\r
-\r
-#if defined(UNICODE)\r
- #define iblockx iblockxW\r
-#else\r
- #define iblockx iblockxA\r
-#endif\r
-\r
-extern int __stdcall iblockxA (int ud, int LockWaitTime, PCHAR LockShareName);\r
-extern int __stdcall iblockxW (int ud, int LockWaitTime, PWCHAR LockShareName);\r
-extern int __stdcall ibunlockx (int ud);\r
-\r
-\r
-/***************************************************************************/\r
-/* IBSTA, IBERR, IBCNT, IBCNTL and FUNCTION PROTOTYPES */\r
-/* ( only included if not accessing the 32-bit DLL directly ) */\r
-/***************************************************************************/\r
-#if !defined(GPIB_DIRECT_ACCESS)\r
-\r
-/*\r
- * Set up access to the user variables (ibsta, iberr, ibcnt, ibcntl).\r
- * These are declared and exported by the 32-bit DLL. Separate copies\r
- * exist for each process that accesses the DLL. They are shared by\r
- * multiple threads of a single process.\r
- */\r
-\r
-extern int ibsta;\r
-extern int iberr;\r
-extern int ibcnt;\r
-extern long ibcntl;\r
-\r
-\r
-#if defined(UNICODE)\r
- #define ibbna ibbnaW\r
- #define ibfind ibfindW\r
- #define ibrdf ibrdfW\r
- #define ibwrtf ibwrtfW\r
-#else\r
- #define ibbna ibbnaA\r
- #define ibfind ibfindA\r
- #define ibrdf ibrdfA\r
- #define ibwrtf ibwrtfA\r
-#endif\r
-\r
-/*\r
- * Extern 32-bit GPIB DLL functions\r
- */\r
-\r
-/* NI-488 Function Prototypes */\r
-extern int __stdcall ibfindA (LPCSTR udname);\r
-extern int __stdcall ibbnaA (int ud, LPCSTR udname);\r
-extern int __stdcall ibrdfA (int ud, LPCSTR filename);\r
-extern int __stdcall ibwrtfA (int ud, LPCSTR filename);\r
-\r
-extern int __stdcall ibfindW (LPCWSTR udname);\r
-extern int __stdcall ibbnaW (int ud, LPCWSTR udname);\r
-extern int __stdcall ibrdfW (int ud, LPCWSTR filename);\r
-extern int __stdcall ibwrtfW (int ud, LPCWSTR filename);\r
-\r
-extern int __stdcall ibask (int ud, int option, PINT v);\r
-extern int __stdcall ibcac (int ud, int v);\r
-extern int __stdcall ibclr (int ud);\r
-extern int __stdcall ibcmd (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibcmda (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibconfig (int ud, int option, int v);\r
-extern int __stdcall ibdev (int boardID, int pad, int sad, int tmo, int eot, int eos);\r
-extern int __stdcall ibdiag (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibdma (int ud, int v);\r
-extern int __stdcall ibeos (int ud, int v);\r
-extern int __stdcall ibeot (int ud, int v);\r
-extern int __stdcall ibgts (int ud, int v);\r
-extern int __stdcall ibist (int ud, int v);\r
-extern int __stdcall iblines (int ud, PSHORT result);\r
-extern int __stdcall ibln (int ud, int pad, int sad, PSHORT listen);\r
-extern int __stdcall ibloc (int ud);\r
-extern int __stdcall ibnotify (int ud, int mask, GpibNotifyCallback_t Callback, PVOID RefData);\r
-extern int __stdcall ibonl (int ud, int v);\r
-extern int __stdcall ibpad (int ud, int v);\r
-extern int __stdcall ibpct (int ud);\r
-extern int __stdcall ibpoke (int ud, long option, long v);\r
-extern int __stdcall ibppc (int ud, int v);\r
-extern int __stdcall ibrd (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibrda (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibrpp (int ud, PCHAR ppr);\r
-extern int __stdcall ibrsc (int ud, int v);\r
-extern int __stdcall ibrsp (int ud, PCHAR spr);\r
-extern int __stdcall ibrsv (int ud, int v);\r
-extern int __stdcall ibsad (int ud, int v);\r
-extern int __stdcall ibsic (int ud);\r
-extern int __stdcall ibsre (int ud, int v);\r
-extern int __stdcall ibstop (int ud);\r
-extern int __stdcall ibtmo (int ud, int v);\r
-extern int __stdcall ibtrg (int ud);\r
-extern int __stdcall ibwait (int ud, int mask);\r
-extern int __stdcall ibwrt (int ud, PVOID buf, long cnt);\r
-extern int __stdcall ibwrta (int ud, PVOID buf, long cnt);\r
-\r
-// GPIB-ENET only functions to support locking across machines\r
-extern int __stdcall iblock (int ud);\r
-extern int __stdcall ibunlock (int ud);\r
-\r
-/**************************************************************************/\r
-/* Functions to access Thread-Specific copies of the GPIB global vars */\r
-\r
-extern int __stdcall ThreadIbsta (void);\r
-extern int __stdcall ThreadIberr (void);\r
-extern int __stdcall ThreadIbcnt (void);\r
-extern long __stdcall ThreadIbcntl (void);\r
-\r
-\r
-/**************************************************************************/\r
-/* NI-488.2 Function Prototypes */\r
-\r
-extern void __stdcall AllSpoll (int boardID, Addr4882_t * addrlist, PSHORT results);\r
-extern void __stdcall DevClear (int boardID, Addr4882_t addr);\r
-extern void __stdcall DevClearList (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall EnableLocal (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall EnableRemote (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall FindLstn (int boardID, Addr4882_t * addrlist, Addr4882_t * results, int limit);\r
-extern void __stdcall FindRQS (int boardID, Addr4882_t * addrlist, PSHORT dev_stat);\r
-extern void __stdcall PPoll (int boardID, PSHORT result);\r
-extern void __stdcall PPollConfig (int boardID, Addr4882_t addr, int dataLine, int lineSense);\r
-extern void __stdcall PPollUnconfig (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall PassControl (int boardID, Addr4882_t addr);\r
-extern void __stdcall RcvRespMsg (int boardID, PVOID buffer, long cnt, int Termination);\r
-extern void __stdcall ReadStatusByte(int boardID, Addr4882_t addr, PSHORT result);\r
-extern void __stdcall Receive (int boardID, Addr4882_t addr, PVOID buffer, long cnt, int Termination);\r
-extern void __stdcall ReceiveSetup (int boardID, Addr4882_t addr);\r
-extern void __stdcall ResetSys (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall Send (int boardID, Addr4882_t addr, PVOID databuf, long datacnt, int eotMode);\r
-extern void __stdcall SendCmds (int boardID, PVOID buffer, long cnt);\r
-extern void __stdcall SendDataBytes (int boardID, PVOID buffer, long cnt, int eot_mode);\r
-extern void __stdcall SendIFC (int boardID);\r
-extern void __stdcall SendLLO (int boardID);\r
-extern void __stdcall SendList (int boardID, Addr4882_t * addrlist, PVOID databuf, long datacnt, int eotMode);\r
-extern void __stdcall SendSetup (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall SetRWLS (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall TestSRQ (int boardID, PSHORT result);\r
-extern void __stdcall TestSys (int boardID, Addr4882_t * addrlist, PSHORT results);\r
-extern void __stdcall Trigger (int boardID, Addr4882_t addr);\r
-extern void __stdcall TriggerList (int boardID, Addr4882_t * addrlist);\r
-extern void __stdcall WaitSRQ (int boardID, PSHORT result);\r
-\r
-#endif\r
-\r
-\r
-#ifdef __cplusplus\r
-}\r
-#endif\r
-\r
-\r
-#endif // DECL_32_H\r
-\r
+/*
+ *
+ *
+ * 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
+
-/*\r
- * iconv library using Win32 API to conversion.\r
- *\r
- * This file is placed in the public domain.\r
- *\r
- * Last Change: 2009-07-06\r
- *\r
- * ENVIRONMENT VARIABLE:\r
- * WINICONV_LIBICONV_DLL\r
- * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If\r
- * loading the DLL or iconv_open() failed, falls back to internal\r
- * conversion. If a few DLL are specified as comma separated list,\r
- * the first loadable DLL is used. The DLL should have iconv_open(),\r
- * iconv_close() and iconv(). Or libiconv_open(), libiconv_close()\r
- * and libiconv().\r
- * (only available when USE_LIBICONV_DLL is defined at compile time)\r
- *\r
- * Win32 API does not support strict encoding conversion for some\r
- * codepage. And MLang function drop or replace invalid bytes and does\r
- * not return useful error status as iconv. This implementation cannot\r
- * be used for encoding validation purpose.\r
- */\r
-\r
-/* for WC_NO_BEST_FIT_CHARS */\r
-#ifndef WINVER\r
-# define WINVER 0x0500\r
-#endif\r
-\r
-#include <windows.h>\r
-#include <errno.h>\r
-#include <string.h>\r
-#include <stdlib.h>\r
-\r
-#if 0\r
-# define MAKE_EXE\r
-# define MAKE_DLL\r
-# define USE_LIBICONV_DLL\r
-#endif\r
-\r
-#if !defined(DEFAULT_LIBICONV_DLL)\r
-# define DEFAULT_LIBICONV_DLL ""\r
-#endif\r
-\r
-#define MB_CHAR_MAX 16\r
-\r
-#define UNICODE_MODE_BOM_DONE 1\r
-#define UNICODE_MODE_SWAPPED 2\r
-\r
-#define FLAG_USE_BOM 1\r
-#define FLAG_TRANSLIT 2 /* //TRANSLIT */\r
-#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */\r
-\r
-typedef unsigned char uchar;\r
-typedef unsigned short ushort;\r
-typedef unsigned int uint;\r
-\r
-typedef void* iconv_t;\r
-\r
-iconv_t iconv_open(const char *tocode, const char *fromcode);\r
-int iconv_close(iconv_t cd);\r
-size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-\r
-/* libiconv interface for vim */\r
-#if defined(MAKE_DLL)\r
-int\r
-iconvctl (iconv_t cd, int request, void* argument)\r
-{\r
- /* not supported */\r
- return 0;\r
-}\r
-#endif\r
-\r
-typedef struct compat_t compat_t;\r
-typedef struct csconv_t csconv_t;\r
-typedef struct rec_iconv_t rec_iconv_t;\r
-\r
-typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);\r
-typedef int (*f_iconv_close)(iconv_t cd);\r
-typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-typedef int* (*f_errno)(void);\r
-typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);\r
-typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);\r
-\r
-#define COMPAT_IN 1\r
-#define COMPAT_OUT 2\r
-\r
-/* unicode mapping for compatibility with other conversion table. */\r
-struct compat_t {\r
- uint in;\r
- uint out;\r
- uint flag;\r
-};\r
-\r
-struct csconv_t {\r
- int codepage;\r
- int flags;\r
- f_mbtowc mbtowc;\r
- f_wctomb wctomb;\r
- f_mblen mblen;\r
- f_flush flush;\r
- DWORD mode;\r
- compat_t *compat;\r
-};\r
-\r
-struct rec_iconv_t {\r
- iconv_t cd;\r
- f_iconv_close iconv_close;\r
- f_iconv iconv;\r
- f_errno _errno;\r
- csconv_t from;\r
- csconv_t to;\r
-#if defined(USE_LIBICONV_DLL)\r
- HMODULE hlibiconv;\r
-#endif\r
-};\r
-\r
-static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
-static int win_iconv_close(iconv_t cd);\r
-static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-\r
-static int load_mlang();\r
-static int make_csconv(const char *name, csconv_t *cv);\r
-static int name_to_codepage(const char *name);\r
-static uint utf16_to_ucs4(const ushort *wbuf);\r
-static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);\r
-static int mbtowc_flags(int codepage);\r
-static int must_use_null_useddefaultchar(int codepage);\r
-static char *strrstr(const char *str, const char *token);\r
-static char *xstrndup(const char *s, size_t n);\r
-static int seterror(int err);\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
-static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
-static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size);\r
-static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname);\r
-\r
-static HMODULE hwiniconv;\r
-#endif\r
-\r
-static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-\r
-static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);\r
-\r
-static struct {\r
- int codepage;\r
- const char *name;\r
-} codepage_alias[] = {\r
- {65001, "CP65001"},\r
- {65001, "UTF8"},\r
- {65001, "UTF-8"},\r
-\r
- {1200, "CP1200"},\r
- {1200, "UTF16LE"},\r
- {1200, "UTF-16LE"},\r
- {1200, "UCS-2LE"},\r
-\r
- {1201, "CP1201"},\r
- {1201, "UTF16BE"},\r
- {1201, "UTF-16BE"},\r
- {1201, "UCS-2BE"},\r
- {1201, "unicodeFFFE"},\r
-\r
- {12000, "CP12000"},\r
- {12000, "UTF32LE"},\r
- {12000, "UTF-32LE"},\r
-\r
- {12001, "CP12001"},\r
- {12001, "UTF32BE"},\r
- {12001, "UTF-32BE"},\r
-\r
-#ifndef GLIB_COMPILATION\r
- /*\r
- * Default is big endian.\r
- * See rfc2781 4.3 Interpreting text labelled as UTF-16.\r
- */\r
- {1201, "UTF16"},\r
- {1201, "UTF-16"},\r
- {12001, "UTF32"},\r
- {12001, "UTF-32"},\r
-#else\r
- /* Default is little endian, because the platform is */\r
- {1200, "UTF16"},\r
- {1200, "UTF-16"},\r
- {1200, "UCS-2"},\r
- {12000, "UTF32"},\r
- {12000, "UTF-32"},\r
-#endif\r
-\r
- /* copy from libiconv `iconv -l` */\r
- /* !IsValidCodePage(367) */\r
- {20127, "ANSI_X3.4-1968"},\r
- {20127, "ANSI_X3.4-1986"},\r
- {20127, "ASCII"},\r
- {20127, "CP367"},\r
- {20127, "IBM367"},\r
- {20127, "ISO-IR-6"},\r
- {20127, "ISO646-US"},\r
- {20127, "ISO_646.IRV:1991"},\r
- {20127, "US"},\r
- {20127, "US-ASCII"},\r
- {20127, "CSASCII"},\r
-\r
- /* !IsValidCodePage(819) */\r
- {1252, "CP819"},\r
- {1252, "IBM819"},\r
- {28591, "ISO-8859-1"},\r
- {28591, "ISO-IR-100"},\r
- {28591, "ISO8859-1"},\r
- {28591, "ISO_8859-1"},\r
- {28591, "ISO_8859-1:1987"},\r
- {28591, "L1"},\r
- {28591, "LATIN1"},\r
- {28591, "CSISOLATIN1"},\r
-\r
- {1250, "CP1250"},\r
- {1250, "MS-EE"},\r
- {1250, "WINDOWS-1250"},\r
-\r
- {1251, "CP1251"},\r
- {1251, "MS-CYRL"},\r
- {1251, "WINDOWS-1251"},\r
-\r
- {1252, "CP1252"},\r
- {1252, "MS-ANSI"},\r
- {1252, "WINDOWS-1252"},\r
-\r
- {1253, "CP1253"},\r
- {1253, "MS-GREEK"},\r
- {1253, "WINDOWS-1253"},\r
-\r
- {1254, "CP1254"},\r
- {1254, "MS-TURK"},\r
- {1254, "WINDOWS-1254"},\r
-\r
- {1255, "CP1255"},\r
- {1255, "MS-HEBR"},\r
- {1255, "WINDOWS-1255"},\r
-\r
- {1256, "CP1256"},\r
- {1256, "MS-ARAB"},\r
- {1256, "WINDOWS-1256"},\r
-\r
- {1257, "CP1257"},\r
- {1257, "WINBALTRIM"},\r
- {1257, "WINDOWS-1257"},\r
-\r
- {1258, "CP1258"},\r
- {1258, "WINDOWS-1258"},\r
-\r
- {850, "850"},\r
- {850, "CP850"},\r
- {850, "IBM850"},\r
- {850, "CSPC850MULTILINGUAL"},\r
-\r
- /* !IsValidCodePage(862) */\r
- {862, "862"},\r
- {862, "CP862"},\r
- {862, "IBM862"},\r
- {862, "CSPC862LATINHEBREW"},\r
-\r
- {866, "866"},\r
- {866, "CP866"},\r
- {866, "IBM866"},\r
- {866, "CSIBM866"},\r
-\r
- /* !IsValidCodePage(154) */\r
- {154, "CP154"},\r
- {154, "CYRILLIC-ASIAN"},\r
- {154, "PT154"},\r
- {154, "PTCP154"},\r
- {154, "CSPTCP154"},\r
-\r
- /* !IsValidCodePage(1133) */\r
- {1133, "CP1133"},\r
- {1133, "IBM-CP1133"},\r
-\r
- {874, "CP874"},\r
- {874, "WINDOWS-874"},\r
-\r
- /* !IsValidCodePage(51932) */\r
- {51932, "CP51932"},\r
- {51932, "MS51932"},\r
- {51932, "WINDOWS-51932"},\r
- {51932, "EUC-JP"},\r
-\r
- {932, "CP932"},\r
- {932, "MS932"},\r
- {932, "SHIFFT_JIS"},\r
- {932, "SHIFFT_JIS-MS"},\r
- {932, "SJIS"},\r
- {932, "SJIS-MS"},\r
- {932, "SJIS-OPEN"},\r
- {932, "SJIS-WIN"},\r
- {932, "WINDOWS-31J"},\r
- {932, "WINDOWS-932"},\r
- {932, "CSWINDOWS31J"},\r
-\r
- {50221, "CP50221"},\r
- {50221, "ISO-2022-JP"},\r
- {50221, "ISO-2022-JP-MS"},\r
- {50221, "ISO2022-JP"},\r
- {50221, "ISO2022-JP-MS"},\r
- {50221, "MS50221"},\r
- {50221, "WINDOWS-50221"},\r
-\r
- {936, "CP936"},\r
- {936, "GBK"},\r
- {936, "MS936"},\r
- {936, "WINDOWS-936"},\r
-\r
- {950, "CP950"},\r
- {950, "BIG5"},\r
-\r
- {949, "CP949"},\r
- {949, "UHC"},\r
- {949, "EUC-KR"},\r
-\r
- {1361, "CP1361"},\r
- {1361, "JOHAB"},\r
-\r
- {437, "437"},\r
- {437, "CP437"},\r
- {437, "IBM437"},\r
- {437, "CSPC8CODEPAGE437"},\r
-\r
- {737, "CP737"},\r
-\r
- {775, "CP775"},\r
- {775, "IBM775"},\r
- {775, "CSPC775BALTIC"},\r
-\r
- {852, "852"},\r
- {852, "CP852"},\r
- {852, "IBM852"},\r
- {852, "CSPCP852"},\r
-\r
- /* !IsValidCodePage(853) */\r
- {853, "CP853"},\r
-\r
- {855, "855"},\r
- {855, "CP855"},\r
- {855, "IBM855"},\r
- {855, "CSIBM855"},\r
-\r
- {857, "857"},\r
- {857, "CP857"},\r
- {857, "IBM857"},\r
- {857, "CSIBM857"},\r
-\r
- /* !IsValidCodePage(858) */\r
- {858, "CP858"},\r
-\r
- {860, "860"},\r
- {860, "CP860"},\r
- {860, "IBM860"},\r
- {860, "CSIBM860"},\r
-\r
- {861, "861"},\r
- {861, "CP-IS"},\r
- {861, "CP861"},\r
- {861, "IBM861"},\r
- {861, "CSIBM861"},\r
-\r
- {863, "863"},\r
- {863, "CP863"},\r
- {863, "IBM863"},\r
- {863, "CSIBM863"},\r
-\r
- {864, "CP864"},\r
- {864, "IBM864"},\r
- {864, "CSIBM864"},\r
-\r
- {865, "865"},\r
- {865, "CP865"},\r
- {865, "IBM865"},\r
- {865, "CSIBM865"},\r
-\r
- {869, "869"},\r
- {869, "CP-GR"},\r
- {869, "CP869"},\r
- {869, "IBM869"},\r
- {869, "CSIBM869"},\r
-\r
- /* !IsValidCodePage(1152) */\r
- {1125, "CP1125"},\r
-\r
- /*\r
- * Code Page Identifiers\r
- * http://msdn2.microsoft.com/en-us/library/ms776446.aspx\r
- */\r
- {37, "IBM037"}, /* IBM EBCDIC US-Canada */\r
- {437, "IBM437"}, /* OEM United States */\r
- {500, "IBM500"}, /* IBM EBCDIC International */\r
- {708, "ASMO-708"}, /* Arabic (ASMO 708) */\r
- /* 709 Arabic (ASMO-449+, BCON V4) */\r
- /* 710 Arabic - Transparent Arabic */\r
- {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */\r
- {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */\r
- {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */\r
- {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */\r
- {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */\r
- {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */\r
- {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */\r
- {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */\r
- {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */\r
- {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */\r
- {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */\r
- {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */\r
- {864, "IBM864"}, /* OEM Arabic; Arabic (864) */\r
- {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */\r
- {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */\r
- {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */\r
- {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */\r
- {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */\r
- {875, "cp875"}, /* IBM EBCDIC Greek Modern */\r
- {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */\r
- {932, "shift-jis"}, /* alternative name for it */\r
- {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */\r
- {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */\r
- {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */\r
- {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */\r
- {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */\r
- {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */\r
- {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */\r
- {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */\r
- {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */\r
- {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */\r
- {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */\r
- {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */\r
- {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */\r
- {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */\r
- {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */\r
- {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */\r
- {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */\r
- {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */\r
- {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */\r
- {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */\r
- {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */\r
- {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */\r
- {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */\r
- {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */\r
- {1361, "Johab"}, /* Korean (Johab) */\r
- {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */\r
- {10001, "x-mac-japanese"}, /* Japanese (Mac) */\r
- {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */\r
- {10003, "x-mac-korean"}, /* Korean (Mac) */\r
- {10004, "x-mac-arabic"}, /* Arabic (Mac) */\r
- {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */\r
- {10006, "x-mac-greek"}, /* Greek (Mac) */\r
- {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */\r
- {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */\r
- {10010, "x-mac-romanian"}, /* Romanian (Mac) */\r
- {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */\r
- {10021, "x-mac-thai"}, /* Thai (Mac) */\r
- {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */\r
- {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */\r
- {10081, "x-mac-turkish"}, /* Turkish (Mac) */\r
- {10082, "x-mac-croatian"}, /* Croatian (Mac) */\r
- {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */\r
- {20001, "x-cp20001"}, /* TCA Taiwan */\r
- {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */\r
- {20003, "x-cp20003"}, /* IBM5550 Taiwan */\r
- {20004, "x-cp20004"}, /* TeleText Taiwan */\r
- {20005, "x-cp20005"}, /* Wang Taiwan */\r
- {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */\r
- {20106, "x-IA5-German"}, /* IA5 German (7-bit) */\r
- {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */\r
- {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */\r
- {20127, "us-ascii"}, /* US-ASCII (7-bit) */\r
- {20261, "x-cp20261"}, /* T.61 */\r
- {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */\r
- {20273, "IBM273"}, /* IBM EBCDIC Germany */\r
- {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */\r
- {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */\r
- {20280, "IBM280"}, /* IBM EBCDIC Italy */\r
- {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */\r
- {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */\r
- {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */\r
- {20297, "IBM297"}, /* IBM EBCDIC France */\r
- {20420, "IBM420"}, /* IBM EBCDIC Arabic */\r
- {20423, "IBM423"}, /* IBM EBCDIC Greek */\r
- {20424, "IBM424"}, /* IBM EBCDIC Hebrew */\r
- {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */\r
- {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */\r
- {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */\r
- {20871, "IBM871"}, /* IBM EBCDIC Icelandic */\r
- {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */\r
- {20905, "IBM905"}, /* IBM EBCDIC Turkish */\r
- {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */\r
- {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */\r
- {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */\r
- {20949, "x-cp20949"}, /* Korean Wansung */\r
- {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */\r
- /* 21027 (deprecated) */\r
- {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */\r
- {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
- {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
- {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
- {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
- {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */\r
- {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */\r
- {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */\r
- {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */\r
- {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */\r
- {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */\r
- {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */\r
- {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */\r
- {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */\r
- {28597, "iso8859-7"}, /* ISO 8859-7 Greek */\r
- {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
- {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
- {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */\r
- {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */\r
- {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */\r
- {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */\r
- {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */\r
- {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */\r
- {29001, "x-Europa"}, /* Europa 3 */\r
- {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
- {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
- {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */\r
- {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */\r
- {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */\r
- {50225, "iso-2022-kr"}, /* ISO 2022 Korean */\r
- {50225, "iso2022-kr"}, /* ISO 2022 Korean */\r
- {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */\r
- /* 50229 ISO 2022 Traditional Chinese */\r
- /* 50930 EBCDIC Japanese (Katakana) Extended */\r
- /* 50931 EBCDIC US-Canada and Japanese */\r
- /* 50933 EBCDIC Korean Extended and Korean */\r
- /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */\r
- /* 50936 EBCDIC Simplified Chinese */\r
- /* 50937 EBCDIC US-Canada and Traditional Chinese */\r
- /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */\r
- {51932, "euc-jp"}, /* EUC Japanese */\r
- {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */\r
- {51949, "euc-kr"}, /* EUC Korean */\r
- /* 51950 EUC Traditional Chinese */\r
- {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */\r
- {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */\r
- {57002, "x-iscii-de"}, /* ISCII Devanagari */\r
- {57003, "x-iscii-be"}, /* ISCII Bengali */\r
- {57004, "x-iscii-ta"}, /* ISCII Tamil */\r
- {57005, "x-iscii-te"}, /* ISCII Telugu */\r
- {57006, "x-iscii-as"}, /* ISCII Assamese */\r
- {57007, "x-iscii-or"}, /* ISCII Oriya */\r
- {57008, "x-iscii-ka"}, /* ISCII Kannada */\r
- {57009, "x-iscii-ma"}, /* ISCII Malayalam */\r
- {57010, "x-iscii-gu"}, /* ISCII Gujarati */\r
- {57011, "x-iscii-pa"}, /* ISCII Punjabi */\r
-\r
- {0, NULL}\r
-};\r
-\r
-/*\r
- * SJIS SHIFTJIS table CP932 table\r
- * ---- --------------------------- --------------------------------\r
- * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS\r
- * 7E U+203E OVERLINE U+007E TILDE\r
- * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR\r
- * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS\r
- * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE\r
- * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO\r
- * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS\r
- * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN\r
- * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN\r
- * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN\r
- *\r
- * EUC-JP and ISO-2022-JP should be compatible with CP932.\r
- *\r
- * Kernel and MLang have different Unicode mapping table. Make sure\r
- * which API is used.\r
- */\r
-static compat_t cp932_compat[] = {\r
- {0x00A5, 0x005C, COMPAT_OUT},\r
- {0x203E, 0x007E, COMPAT_OUT},\r
- {0x2014, 0x2015, COMPAT_OUT},\r
- {0x301C, 0xFF5E, COMPAT_OUT},\r
- {0x2016, 0x2225, COMPAT_OUT},\r
- {0x2212, 0xFF0D, COMPAT_OUT},\r
- {0x00A2, 0xFFE0, COMPAT_OUT},\r
- {0x00A3, 0xFFE1, COMPAT_OUT},\r
- {0x00AC, 0xFFE2, COMPAT_OUT},\r
- {0, 0, 0}\r
-};\r
-\r
-static compat_t cp20932_compat[] = {\r
- {0x00A5, 0x005C, COMPAT_OUT},\r
- {0x203E, 0x007E, COMPAT_OUT},\r
- {0x2014, 0x2015, COMPAT_OUT},\r
- {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},\r
- {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},\r
- {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},\r
- {0, 0, 0}\r
-};\r
-\r
-static compat_t *cp51932_compat = cp932_compat;\r
-\r
-/* cp20932_compat for kernel. cp932_compat for mlang. */\r
-static compat_t *cp5022x_compat = cp932_compat;\r
-\r
-typedef HRESULT (WINAPI *CONVERTINETSTRING)(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnSrcSize,\r
- LPBYTE lpDstStr,\r
- LPINT lpnDstSize\r
-);\r
-typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnMultiCharCount,\r
- LPWSTR lpDstStr,\r
- LPINT lpnWideCharCount\r
-);\r
-typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(\r
- LPDWORD lpdwMode,\r
- DWORD dwEncoding,\r
- LPCWSTR lpSrcStr,\r
- LPINT lpnWideCharCount,\r
- LPSTR lpDstStr,\r
- LPINT lpnMultiCharCount\r
-);\r
-typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding\r
-);\r
-typedef HRESULT (WINAPI *LCIDTORFC1766A)(\r
- LCID Locale,\r
- LPSTR pszRfc1766,\r
- int nChar\r
-);\r
-typedef HRESULT (WINAPI *LCIDTORFC1766W)(\r
- LCID Locale,\r
- LPWSTR pszRfc1766,\r
- int nChar\r
-);\r
-typedef HRESULT (WINAPI *RFC1766TOLCIDA)(\r
- LCID *pLocale,\r
- LPSTR pszRfc1766\r
-);\r
-typedef HRESULT (WINAPI *RFC1766TOLCIDW)(\r
- LCID *pLocale,\r
- LPWSTR pszRfc1766\r
-);\r
-static CONVERTINETSTRING ConvertINetString;\r
-static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;\r
-static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;\r
-static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;\r
-static LCIDTORFC1766A LcidToRfc1766A;\r
-static RFC1766TOLCIDA Rfc1766ToLcidA;\r
-\r
-static int\r
-load_mlang()\r
-{\r
- HMODULE h;\r
- if (ConvertINetString != NULL)\r
- return TRUE;\r
- h = LoadLibrary("mlang.dll");\r
- if (!h)\r
- return FALSE;\r
- ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString");\r
- ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode");\r
- ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte");\r
- IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable");\r
- LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A");\r
- Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA");\r
- return TRUE;\r
-}\r
-\r
-iconv_t\r
-iconv_open(const char *tocode, const char *fromcode)\r
-{\r
- rec_iconv_t *cd;\r
-\r
- cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t));\r
- if (cd == NULL)\r
- return (iconv_t)(-1);\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
- errno = 0;\r
- if (libiconv_iconv_open(cd, tocode, fromcode))\r
- return (iconv_t)cd;\r
-#endif\r
-\r
- /* reset the errno to prevent reporting wrong error code.\r
- * 0 for unsorted error. */\r
- errno = 0;\r
- if (win_iconv_open(cd, tocode, fromcode))\r
- return (iconv_t)cd;\r
-\r
- free(cd);\r
-\r
- return (iconv_t)(-1);\r
-}\r
-\r
-int\r
-iconv_close(iconv_t _cd)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- int r = cd->iconv_close(cd->cd);\r
- int e = *(cd->_errno());\r
-#if defined(USE_LIBICONV_DLL)\r
- if (cd->hlibiconv != NULL)\r
- FreeLibrary(cd->hlibiconv);\r
-#endif\r
- free(cd);\r
- errno = e;\r
- return r;\r
-}\r
-\r
-size_t\r
-iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);\r
- errno = *(cd->_errno());\r
- return r;\r
-}\r
-\r
-static int\r
-win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)\r
-{\r
- if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to))\r
- return FALSE;\r
- cd->iconv_close = win_iconv_close;\r
- cd->iconv = win_iconv;\r
- cd->_errno = _errno;\r
- cd->cd = (iconv_t)cd;\r
- return TRUE;\r
-}\r
-\r
-static int\r
-win_iconv_close(iconv_t cd)\r
-{\r
- return 0;\r
-}\r
-\r
-static size_t\r
-win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */\r
- int insize;\r
- int outsize;\r
- int wsize;\r
- DWORD frommode;\r
- DWORD tomode;\r
- uint wc;\r
- compat_t *cp;\r
- int i;\r
-\r
- if (inbuf == NULL || *inbuf == NULL)\r
- {\r
- if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)\r
- {\r
- tomode = cd->to.mode;\r
- outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);\r
- if (outsize == -1)\r
- {\r
- cd->to.mode = tomode;\r
- return (size_t)(-1);\r
- }\r
- *outbuf += outsize;\r
- *outbytesleft -= outsize;\r
- }\r
- cd->from.mode = 0;\r
- cd->to.mode = 0;\r
- return 0;\r
- }\r
-\r
- while (*inbytesleft != 0)\r
- {\r
- frommode = cd->from.mode;\r
- tomode = cd->to.mode;\r
- wsize = MB_CHAR_MAX;\r
-\r
- insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);\r
- if (insize == -1)\r
- {\r
- cd->from.mode = frommode;\r
- return (size_t)(-1);\r
- }\r
-\r
- if (wsize == 0)\r
- {\r
- *inbuf += insize;\r
- *inbytesleft -= insize;\r
- continue;\r
- }\r
-\r
- if (cd->from.compat != NULL)\r
- {\r
- wc = utf16_to_ucs4(wbuf);\r
- cp = cd->from.compat;\r
- for (i = 0; cp[i].in != 0; ++i)\r
- {\r
- if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)\r
- {\r
- ucs4_to_utf16(cp[i].in, wbuf, &wsize);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- if (cd->to.compat != NULL)\r
- {\r
- wc = utf16_to_ucs4(wbuf);\r
- cp = cd->to.compat;\r
- for (i = 0; cp[i].in != 0; ++i)\r
- {\r
- if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)\r
- {\r
- ucs4_to_utf16(cp[i].out, wbuf, &wsize);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);\r
- if (outsize == -1)\r
- {\r
- cd->from.mode = frommode;\r
- cd->to.mode = tomode;\r
- return (size_t)(-1);\r
- }\r
-\r
- *inbuf += insize;\r
- *outbuf += outsize;\r
- *inbytesleft -= insize;\r
- *outbytesleft -= outsize;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-static int\r
-make_csconv(const char *_name, csconv_t *cv)\r
-{\r
- CPINFOEX cpinfoex;\r
- int use_compat = TRUE;\r
- int flag = 0;\r
- char *name;\r
- char *p;\r
-\r
- name = xstrndup(_name, strlen(_name));\r
- if (name == NULL)\r
- return FALSE;\r
-\r
- /* check for option "enc_name//opt1//opt2" */\r
- while ((p = strrstr(name, "//")) != NULL)\r
- {\r
- if (_stricmp(p + 2, "nocompat") == 0)\r
- use_compat = FALSE;\r
- else if (_stricmp(p + 2, "translit") == 0)\r
- flag |= FLAG_TRANSLIT;\r
- else if (_stricmp(p + 2, "ignore") == 0)\r
- flag |= FLAG_IGNORE;\r
- *p = 0;\r
- }\r
-\r
- cv->mode = 0;\r
- cv->flags = flag;\r
- cv->mblen = NULL;\r
- cv->flush = NULL;\r
- cv->compat = NULL;\r
- cv->codepage = name_to_codepage(name);\r
- if (cv->codepage == 1200 || cv->codepage == 1201)\r
- {\r
- cv->mbtowc = utf16_mbtowc;\r
- cv->wctomb = utf16_wctomb;\r
- if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0)\r
- cv->flags |= FLAG_USE_BOM;\r
- }\r
- else if (cv->codepage == 12000 || cv->codepage == 12001)\r
- {\r
- cv->mbtowc = utf32_mbtowc;\r
- cv->wctomb = utf32_wctomb;\r
- if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0)\r
- cv->flags |= FLAG_USE_BOM;\r
- }\r
- else if (cv->codepage == 65001)\r
- {\r
- cv->mbtowc = kernel_mbtowc;\r
- cv->wctomb = kernel_wctomb;\r
- cv->mblen = utf8_mblen;\r
- }\r
- else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang())\r
- {\r
- cv->mbtowc = iso2022jp_mbtowc;\r
- cv->wctomb = iso2022jp_wctomb;\r
- cv->flush = iso2022jp_flush;\r
- }\r
- else if (cv->codepage == 51932 && load_mlang())\r
- {\r
- cv->mbtowc = mlang_mbtowc;\r
- cv->wctomb = mlang_wctomb;\r
- cv->mblen = eucjp_mblen;\r
- }\r
- else if (IsValidCodePage(cv->codepage)\r
- && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0)\r
- {\r
- cv->mbtowc = kernel_mbtowc;\r
- cv->wctomb = kernel_wctomb;\r
- if (cpinfoex.MaxCharSize == 1)\r
- cv->mblen = sbcs_mblen;\r
- else if (cpinfoex.MaxCharSize == 2)\r
- cv->mblen = dbcs_mblen;\r
- else\r
- cv->mblen = mbcs_mblen;\r
- }\r
- else\r
- {\r
- /* not supported */\r
- free(name);\r
- errno = EINVAL;\r
- return FALSE;\r
- }\r
-\r
- if (use_compat)\r
- {\r
- switch (cv->codepage)\r
- {\r
- case 932: cv->compat = cp932_compat; break;\r
- case 20932: cv->compat = cp20932_compat; break;\r
- case 51932: cv->compat = cp51932_compat; break;\r
- case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break;\r
- }\r
- }\r
-\r
- free(name);\r
-\r
- return TRUE;\r
-}\r
-\r
-static int\r
-name_to_codepage(const char *name)\r
-{\r
- int i;\r
-\r
- if (*name == '\0' ||\r
- strcmp(name, "char") == 0)\r
- return GetACP();\r
- else if (strcmp(name, "wchar_t") == 0)\r
- return 1200;\r
- else if (_strnicmp(name, "cp", 2) == 0)\r
- return atoi(name + 2); /* CP123 */\r
- else if ('0' <= name[0] && name[0] <= '9')\r
- return atoi(name); /* 123 */\r
- else if (_strnicmp(name, "xx", 2) == 0)\r
- return atoi(name + 2); /* XX123 for debug */\r
-\r
- for (i = 0; codepage_alias[i].name != NULL; ++i)\r
- if (_stricmp(name, codepage_alias[i].name) == 0)\r
- return codepage_alias[i].codepage;\r
- return -1;\r
-}\r
-\r
-/*\r
- * http://www.faqs.org/rfcs/rfc2781.html\r
- */\r
-static uint\r
-utf16_to_ucs4(const ushort *wbuf)\r
-{\r
- uint wc = wbuf[0];\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;\r
- return wc;\r
-}\r
-\r
-static void\r
-ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)\r
-{\r
- if (wc < 0x10000)\r
- {\r
- wbuf[0] = wc;\r
- *wbufsize = 1;\r
- }\r
- else\r
- {\r
- wc -= 0x10000;\r
- wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);\r
- wbuf[1] = 0xDC00 | (wc & 0x3FF);\r
- *wbufsize = 2;\r
- }\r
-}\r
-\r
-/*\r
- * Check if codepage is one of those for which the dwFlags parameter\r
- * to MultiByteToWideChar() must be zero. Return zero or\r
- * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows\r
- * Server 2003 R2 claims that also codepage 65001 is one of these, but\r
- * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave\r
- * out 65001 (UTF-8), and that indeed seems to be the case on XP, it\r
- * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting\r
- * from UTF-8.\r
- */\r
-static int\r
-mbtowc_flags(int codepage)\r
-{\r
- return (codepage == 50220 || codepage == 50221 ||\r
- codepage == 50222 || codepage == 50225 ||\r
- codepage == 50227 || codepage == 50229 ||\r
- codepage == 52936 || codepage == 54936 ||\r
- (codepage >= 57002 && codepage <= 57011) ||\r
- codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;\r
-}\r
-\r
-/*\r
- * Check if codepage is one those for which the lpUsedDefaultChar\r
- * parameter to WideCharToMultiByte() must be NULL. The docs in\r
- * Platform SDK for for Windows Server 2003 R2 claims that this is the\r
- * list below, while the MSDN docs for MSVS2008 claim that it is only\r
- * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform\r
- * SDK seems to be correct, at least for XP.\r
- */\r
-static int\r
-must_use_null_useddefaultchar(int codepage)\r
-{\r
- return (codepage == 65000 || codepage == 65001 ||\r
- codepage == 50220 || codepage == 50221 ||\r
- codepage == 50222 || codepage == 50225 ||\r
- codepage == 50227 || codepage == 50229 ||\r
- codepage == 52936 || codepage == 54936 ||\r
- (codepage >= 57002 && codepage <= 57011) ||\r
- codepage == 42);\r
-}\r
-\r
-static char *\r
-strrstr(const char *str, const char *token)\r
-{\r
- int len = strlen(token);\r
- const char *p = str + strlen(str);\r
-\r
- while (str <= --p)\r
- if (p[0] == token[0] && strncmp(p, token, len) == 0)\r
- return (char *)p;\r
- return NULL;\r
-}\r
-\r
-static char *\r
-xstrndup(const char *s, size_t n)\r
-{\r
- char *p;\r
-\r
- p = (char *)malloc(n + 1);\r
- if (p == NULL)\r
- return NULL;\r
- memcpy(p, s, n);\r
- p[n] = '\0';\r
- return p;\r
-}\r
-\r
-static int\r
-seterror(int err)\r
-{\r
- errno = err;\r
- return -1;\r
-}\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
-static int\r
-libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)\r
-{\r
- HMODULE hlibiconv = NULL;\r
- HMODULE hmsvcrt = NULL;\r
- char *dllname;\r
- const char *p;\r
- const char *e;\r
- f_iconv_open _iconv_open;\r
-\r
- /*\r
- * always try to load dll, so that we can switch dll in runtime.\r
- */\r
-\r
- /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */\r
- p = getenv("WINICONV_LIBICONV_DLL");\r
- if (p == NULL)\r
- p = DEFAULT_LIBICONV_DLL;\r
- /* parse comma separated value */\r
- for ( ; *p != 0; p = (*e == ',') ? e + 1 : e)\r
- {\r
- e = strchr(p, ',');\r
- if (p == e)\r
- continue;\r
- else if (e == NULL)\r
- e = p + strlen(p);\r
- dllname = xstrndup(p, e - p);\r
- if (dllname == NULL)\r
- return FALSE;\r
- hlibiconv = LoadLibrary(dllname);\r
- free(dllname);\r
- if (hlibiconv != NULL)\r
- {\r
- if (hlibiconv == hwiniconv)\r
- {\r
- FreeLibrary(hlibiconv);\r
- hlibiconv = NULL;\r
- continue;\r
- }\r
- break;\r
- }\r
- }\r
-\r
- if (hlibiconv == NULL)\r
- goto failed;\r
-\r
- hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno");\r
- if (hmsvcrt == NULL)\r
- goto failed;\r
-\r
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open");\r
- if (_iconv_open == NULL)\r
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open");\r
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close");\r
- if (cd->iconv_close == NULL)\r
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close");\r
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv");\r
- if (cd->iconv == NULL)\r
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv");\r
- cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno");\r
- if (_iconv_open == NULL || cd->iconv_close == NULL\r
- || cd->iconv == NULL || cd->_errno == NULL)\r
- goto failed;\r
-\r
- cd->cd = _iconv_open(tocode, fromcode);\r
- if (cd->cd == (iconv_t)(-1))\r
- goto failed;\r
-\r
- cd->hlibiconv = hlibiconv;\r
- return TRUE;\r
-\r
-failed:\r
- if (hlibiconv != NULL)\r
- FreeLibrary(hlibiconv);\r
- /* do not free hmsvcrt which is obtained by GetModuleHandle() */\r
- return FALSE;\r
-}\r
-\r
-/*\r
- * Reference:\r
- * http://forums.belution.com/ja/vc/000/234/78s.shtml\r
- * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html\r
- *\r
- * The formal way is\r
- * imagehlp.h or dbghelp.h\r
- * imagehlp.lib or dbghelp.lib\r
- * ImageDirectoryEntryToData()\r
- */\r
-#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base))\r
-#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew))\r
-static PVOID\r
-MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size)\r
-{\r
- /* TODO: MappedAsImage? */\r
- PIMAGE_DATA_DIRECTORY p;\r
- p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry;\r
- if (p->VirtualAddress == 0) {\r
- *Size = 0;\r
- return NULL;\r
- }\r
- *Size = p->Size;\r
- return (PVOID)((LPBYTE)Base + p->VirtualAddress);\r
-}\r
-\r
-static HMODULE\r
-find_imported_module_by_funcname(HMODULE hModule, const char *funcname)\r
-{\r
- DWORD Base;\r
- ULONG Size;\r
- PIMAGE_IMPORT_DESCRIPTOR Imp;\r
- PIMAGE_THUNK_DATA Name; /* Import Name Table */\r
- PIMAGE_IMPORT_BY_NAME ImpName;\r
-\r
- Base = (DWORD)hModule;\r
- Imp = MyImageDirectoryEntryToData(\r
- (LPVOID)Base,\r
- TRUE,\r
- IMAGE_DIRECTORY_ENTRY_IMPORT,\r
- &Size);\r
- if (Imp == NULL)\r
- return NULL;\r
- for ( ; Imp->OriginalFirstThunk != 0; ++Imp)\r
- {\r
- Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk);\r
- for ( ; Name->u1.Ordinal != 0; ++Name)\r
- {\r
- if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal))\r
- {\r
- ImpName = (PIMAGE_IMPORT_BY_NAME)\r
- (Base + (DWORD)Name->u1.AddressOfData);\r
- if (strcmp((char *)ImpName->Name, funcname) == 0)\r
- return GetModuleHandle((char *)(Base + Imp->Name));\r
- }\r
- }\r
- }\r
- return NULL;\r
-}\r
-#endif\r
-\r
-static int\r
-sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- return 1;\r
-}\r
-\r
-static int\r
-dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;\r
- if (bufsize < len)\r
- return seterror(EINVAL);\r
- return len;\r
-}\r
-\r
-static int\r
-mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = 0;\r
-\r
- if (cv->codepage == 54936) {\r
- if (buf[0] <= 0x7F) len = 1;\r
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
- bufsize >= 2 &&\r
- ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||\r
- (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2;\r
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
- bufsize >= 4 &&\r
- buf[1] >= 0x30 && buf[1] <= 0x39) len = 4;\r
- else\r
- return seterror(EINVAL);\r
- return len;\r
- }\r
- else\r
- return seterror(EINVAL);\r
-}\r
-\r
-static int\r
-utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = 0;\r
-\r
- if (buf[0] < 0x80) len = 1;\r
- else if ((buf[0] & 0xE0) == 0xC0) len = 2;\r
- else if ((buf[0] & 0xF0) == 0xE0) len = 3;\r
- else if ((buf[0] & 0xF8) == 0xF0) len = 4;\r
- else if ((buf[0] & 0xFC) == 0xF8) len = 5;\r
- else if ((buf[0] & 0xFE) == 0xFC) len = 6;\r
-\r
- if (len == 0)\r
- return seterror(EILSEQ);\r
- else if (bufsize < len)\r
- return seterror(EINVAL);\r
- return len;\r
-}\r
-\r
-static int\r
-eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- if (buf[0] < 0x80) /* ASCII */\r
- return 1;\r
- else if (buf[0] == 0x8E) /* JIS X 0201 */\r
- {\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))\r
- return seterror(EILSEQ);\r
- return 2;\r
- }\r
- else if (buf[0] == 0x8F) /* JIS X 0212 */\r
- {\r
- if (bufsize < 3)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)\r
- || !(0xA1 <= buf[2] && buf[2] <= 0xFE))\r
- return seterror(EILSEQ);\r
- return 3;\r
- }\r
- else /* JIS X 0208 */\r
- {\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)\r
- || !(0xA1 <= buf[1] && buf[1] <= 0xFE))\r
- return seterror(EILSEQ);\r
- return 2;\r
- }\r
-}\r
-\r
-static int\r
-kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int len;\r
-\r
- len = cv->mblen(cv, buf, bufsize);\r
- if (len == -1)\r
- return -1;\r
- *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),\r
- (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);\r
- if (*wbufsize == 0)\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-static int\r
-kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- BOOL usedDefaultChar = 0;\r
- BOOL *p = NULL;\r
- int flags = 0;\r
- int len;\r
-\r
- if (bufsize == 0)\r
- return seterror(E2BIG);\r
- if (!must_use_null_useddefaultchar(cv->codepage))\r
- {\r
- p = &usedDefaultChar;\r
-#ifdef WC_NO_BEST_FIT_CHARS\r
- if (!(cv->flags & FLAG_TRANSLIT))\r
- flags |= WC_NO_BEST_FIT_CHARS;\r
-#endif\r
- }\r
- len = WideCharToMultiByte(cv->codepage, flags,\r
- (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);\r
- if (len == 0)\r
- {\r
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)\r
- return seterror(E2BIG);\r
- return seterror(EILSEQ);\r
- }\r
- else if (usedDefaultChar)\r
- return seterror(EILSEQ);\r
- else if (cv->mblen(cv, buf, len) != len) /* validate result */\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-/*\r
- * It seems that the mode (cv->mode) is fixnum.\r
- * For example, when converting iso-2022-jp(cp50221) to unicode:\r
- * in ascii sequence: mode=0xC42C0000\r
- * in jisx0208 sequence: mode=0xC42C0001\r
- * "C42C" is same for each convert session.\r
- * It should be: ((codepage-1)<<16)|state\r
- */\r
-static int\r
-mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int len;\r
- int insize;\r
- HRESULT hr;\r
-\r
- len = cv->mblen(cv, buf, bufsize);\r
- if (len == -1)\r
- return -1;\r
- insize = len;\r
- hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,\r
- (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);\r
- if (hr != S_OK || insize != len)\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-static int\r
-mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */\r
- int tmpsize = MB_CHAR_MAX;\r
- int insize = wbufsize;\r
- HRESULT hr;\r
-\r
- hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,\r
- (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);\r
- if (hr != S_OK || insize != wbufsize)\r
- return seterror(EILSEQ);\r
- else if (bufsize < tmpsize)\r
- return seterror(E2BIG);\r
- else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)\r
- return seterror(EILSEQ);\r
- memcpy(buf, tmpbuf, tmpsize);\r
- return tmpsize;\r
-}\r
-\r
-static int\r
-utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int codepage = cv->codepage;\r
-\r
- /* swap endian: 1200 <-> 1201 */\r
- if (cv->mode & UNICODE_MODE_SWAPPED)\r
- codepage ^= 1;\r
-\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- if (codepage == 1200) /* little endian */\r
- wbuf[0] = (buf[1] << 8) | buf[0];\r
- else if (codepage == 1201) /* big endian */\r
- wbuf[0] = (buf[0] << 8) | buf[1];\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (wbuf[0] == 0xFFFE)\r
- {\r
- cv->mode |= UNICODE_MODE_SWAPPED;\r
- *wbufsize = 0;\r
- return 2;\r
- }\r
- else if (wbuf[0] == 0xFEFF)\r
- {\r
- *wbufsize = 0;\r
- return 2;\r
- }\r
- }\r
-\r
- if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)\r
- return seterror(EILSEQ);\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- {\r
- if (bufsize < 4)\r
- return seterror(EINVAL);\r
- if (codepage == 1200) /* little endian */\r
- wbuf[1] = (buf[3] << 8) | buf[2];\r
- else if (codepage == 1201) /* big endian */\r
- wbuf[1] = (buf[2] << 8) | buf[3];\r
- if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))\r
- return seterror(EILSEQ);\r
- *wbufsize = 2;\r
- return 4;\r
- }\r
- *wbufsize = 1;\r
- return 2;\r
-}\r
-\r
-static int\r
-utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- int r;\r
-\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (bufsize < 2)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- memcpy(buf, "\xFF\xFE", 2);\r
- else if (cv->codepage == 1201) /* big endian */\r
- memcpy(buf, "\xFE\xFF", 2);\r
-\r
- r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2);\r
- if (r == -1)\r
- return -1;\r
- return r + 2;\r
- }\r
-\r
- if (bufsize < 2)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- {\r
- buf[0] = (wbuf[0] & 0x00FF);\r
- buf[1] = (wbuf[0] & 0xFF00) >> 8;\r
- }\r
- else if (cv->codepage == 1201) /* big endian */\r
- {\r
- buf[0] = (wbuf[0] & 0xFF00) >> 8;\r
- buf[1] = (wbuf[0] & 0x00FF);\r
- }\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- {\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- {\r
- buf[2] = (wbuf[1] & 0x00FF);\r
- buf[3] = (wbuf[1] & 0xFF00) >> 8;\r
- }\r
- else if (cv->codepage == 1201) /* big endian */\r
- {\r
- buf[2] = (wbuf[1] & 0xFF00) >> 8;\r
- buf[3] = (wbuf[1] & 0x00FF);\r
- }\r
- return 4;\r
- }\r
- return 2;\r
-}\r
-\r
-static int\r
-utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int codepage = cv->codepage;\r
- uint wc = 0;\r
-\r
- /* swap endian: 12000 <-> 12001 */\r
- if (cv->mode & UNICODE_MODE_SWAPPED)\r
- codepage ^= 1;\r
-\r
- if (bufsize < 4)\r
- return seterror(EINVAL);\r
- if (codepage == 12000) /* little endian */\r
- wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];\r
- else if (codepage == 12001) /* big endian */\r
- wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (wc == 0xFFFE0000)\r
- {\r
- cv->mode |= UNICODE_MODE_SWAPPED;\r
- *wbufsize = 0;\r
- return 4;\r
- }\r
- else if (wc == 0x0000FEFF)\r
- {\r
- *wbufsize = 0;\r
- return 4;\r
- }\r
- }\r
-\r
- if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)\r
- return seterror(EILSEQ);\r
- ucs4_to_utf16(wc, wbuf, wbufsize);\r
- return 4;\r
-}\r
-\r
-static int\r
-utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- uint wc;\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- int r;\r
-\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 12000) /* little endian */\r
- memcpy(buf, "\xFF\xFE\x00\x00", 4);\r
- else if (cv->codepage == 12001) /* big endian */\r
- memcpy(buf, "\x00\x00\xFE\xFF", 4);\r
-\r
- r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4);\r
- if (r == -1)\r
- return -1;\r
- return r + 4;\r
- }\r
-\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- wc = utf16_to_ucs4(wbuf);\r
- if (cv->codepage == 12000) /* little endian */\r
- {\r
- buf[0] = wc & 0x000000FF;\r
- buf[1] = (wc & 0x0000FF00) >> 8;\r
- buf[2] = (wc & 0x00FF0000) >> 16;\r
- buf[3] = (wc & 0xFF000000) >> 24;\r
- }\r
- else if (cv->codepage == 12001) /* big endian */\r
- {\r
- buf[0] = (wc & 0xFF000000) >> 24;\r
- buf[1] = (wc & 0x00FF0000) >> 16;\r
- buf[2] = (wc & 0x0000FF00) >> 8;\r
- buf[3] = wc & 0x000000FF;\r
- }\r
- return 4;\r
-}\r
-\r
-/*\r
- * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)\r
- * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow\r
- * 1 byte Kana)\r
- * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte\r
- * Kana - SO/SI)\r
- *\r
- * MultiByteToWideChar() and WideCharToMultiByte() behave differently\r
- * depending on Windows version. On XP, WideCharToMultiByte() doesn't\r
- * terminate result sequence with ascii escape. But Vista does.\r
- * Use MLang instead.\r
- */\r
-\r
-#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))\r
-#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)\r
-#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)\r
-\r
-#define ISO2022_SI 0\r
-#define ISO2022_SO 1\r
-\r
-/* shift in */\r
-static const char iso2022_SI_seq[] = "\x0F";\r
-/* shift out */\r
-static const char iso2022_SO_seq[] = "\x0E";\r
-\r
-typedef struct iso2022_esc_t iso2022_esc_t;\r
-struct iso2022_esc_t {\r
- const char *esc;\r
- int esc_len;\r
- int len;\r
- int cs;\r
-};\r
-\r
-#define ISO2022JP_CS_ASCII 0\r
-#define ISO2022JP_CS_JISX0201_ROMAN 1\r
-#define ISO2022JP_CS_JISX0201_KANA 2\r
-#define ISO2022JP_CS_JISX0208_1978 3\r
-#define ISO2022JP_CS_JISX0208_1983 4\r
-#define ISO2022JP_CS_JISX0212 5\r
-\r
-static iso2022_esc_t iso2022jp_esc[] = {\r
- {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},\r
- {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},\r
- {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},\r
- {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */\r
- {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},\r
- {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},\r
- {NULL, 0, 0, 0}\r
-};\r
-\r
-static int\r
-iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- char tmp[MB_CHAR_MAX];\r
- int insize;\r
- HRESULT hr;\r
- DWORD dummy = 0;\r
- int len;\r
- int esc_len;\r
- int cs;\r
- int shift;\r
- int i;\r
-\r
- if (buf[0] == 0x1B)\r
- {\r
- for (i = 0; iesc[i].esc != NULL; ++i)\r
- {\r
- esc_len = iesc[i].esc_len;\r
- if (bufsize < esc_len)\r
- {\r
- if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)\r
- return seterror(EINVAL);\r
- }\r
- else\r
- {\r
- if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)\r
- {\r
- cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);\r
- *wbufsize = 0;\r
- return esc_len;\r
- }\r
- }\r
- }\r
- /* not supported escape sequence */\r
- return seterror(EILSEQ);\r
- }\r
- else if (buf[0] == iso2022_SO_seq[0])\r
- {\r
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);\r
- *wbufsize = 0;\r
- return 1;\r
- }\r
- else if (buf[0] == iso2022_SI_seq[0])\r
- {\r
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);\r
- *wbufsize = 0;\r
- return 1;\r
- }\r
-\r
- cs = ISO2022_MODE_CS(cv->mode);\r
- shift = ISO2022_MODE_SHIFT(cv->mode);\r
-\r
- /* reset the mode for informal sequence */\r
- if (buf[0] < 0x20)\r
- {\r
- cs = ISO2022JP_CS_ASCII;\r
- shift = ISO2022_SI;\r
- }\r
-\r
- len = iesc[cs].len;\r
- if (bufsize < len)\r
- return seterror(EINVAL);\r
- for (i = 0; i < len; ++i)\r
- if (!(buf[i] < 0x80))\r
- return seterror(EILSEQ);\r
- esc_len = iesc[cs].esc_len;\r
- memcpy(tmp, iesc[cs].esc, esc_len);\r
- if (shift == ISO2022_SO)\r
- {\r
- memcpy(tmp + esc_len, iso2022_SO_seq, 1);\r
- esc_len += 1;\r
- }\r
- memcpy(tmp + esc_len, buf, len);\r
-\r
- if ((cv->codepage == 50220 || cv->codepage == 50221\r
- || cv->codepage == 50222) && shift == ISO2022_SO)\r
- {\r
- /* XXX: shift-out cannot be used for mbtowc (both kernel and\r
- * mlang) */\r
- esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;\r
- memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);\r
- memcpy(tmp + esc_len, buf, len);\r
- }\r
-\r
- insize = len + esc_len;\r
- hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,\r
- (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);\r
- if (hr != S_OK || insize != len + esc_len)\r
- return seterror(EILSEQ);\r
-\r
- /* Check for conversion error. Assuming defaultChar is 0x3F. */\r
- /* ascii should be converted from ascii */\r
- if (wbuf[0] == buf[0]\r
- && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
- return seterror(EILSEQ);\r
-\r
- /* reset the mode for informal sequence */\r
- if (cv->mode != ISO2022_MODE(cs, shift))\r
- cv->mode = ISO2022_MODE(cs, shift);\r
-\r
- return len;\r
-}\r
-\r
-static int\r
-iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- char tmp[MB_CHAR_MAX];\r
- int tmpsize = MB_CHAR_MAX;\r
- int insize = wbufsize;\r
- HRESULT hr;\r
- DWORD dummy = 0;\r
- int len;\r
- int esc_len = 0;\r
- int cs = 0;\r
- int shift;\r
- int i;\r
-\r
- /*\r
- * MultiByte = [escape sequence] + character + [escape sequence]\r
- *\r
- * Whether trailing escape sequence is added depends on which API is\r
- * used (kernel or MLang, and its version).\r
- */\r
- hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,\r
- (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);\r
- if (hr != S_OK || insize != wbufsize)\r
- return seterror(EILSEQ);\r
- else if (bufsize < tmpsize)\r
- return seterror(E2BIG);\r
-\r
- if (tmpsize == 1)\r
- {\r
- cs = ISO2022JP_CS_ASCII;\r
- esc_len = 0;\r
- }\r
- else\r
- {\r
- for (i = 1; iesc[i].esc != NULL; ++i)\r
- {\r
- esc_len = iesc[i].esc_len;\r
- if (strncmp(tmp, iesc[i].esc, esc_len) == 0)\r
- {\r
- cs = iesc[i].cs;\r
- break;\r
- }\r
- }\r
- if (iesc[i].esc == NULL)\r
- /* not supported escape sequence */\r
- return seterror(EILSEQ);\r
- }\r
-\r
- shift = ISO2022_SI;\r
- if (tmp[esc_len] == iso2022_SO_seq[0])\r
- {\r
- shift = ISO2022_SO;\r
- esc_len += 1;\r
- }\r
-\r
- len = iesc[cs].len;\r
-\r
- /* Check for converting error. Assuming defaultChar is 0x3F. */\r
- /* ascii should be converted from ascii */\r
- if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))\r
- return seterror(EILSEQ);\r
- else if (tmpsize < esc_len + len)\r
- return seterror(EILSEQ);\r
-\r
- if (cv->mode == ISO2022_MODE(cs, shift))\r
- {\r
- /* remove escape sequence */\r
- if (esc_len != 0)\r
- memmove(tmp, tmp + esc_len, len);\r
- esc_len = 0;\r
- }\r
- else\r
- {\r
- if (cs == ISO2022JP_CS_ASCII)\r
- {\r
- esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;\r
- memmove(tmp + esc_len, tmp, len);\r
- memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);\r
- }\r
- if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)\r
- {\r
- /* shift-in before changing to other mode */\r
- memmove(tmp + 1, tmp, len + esc_len);\r
- memcpy(tmp, iso2022_SI_seq, 1);\r
- esc_len += 1;\r
- }\r
- }\r
-\r
- if (bufsize < len + esc_len)\r
- return seterror(E2BIG);\r
- memcpy(buf, tmp, len + esc_len);\r
- cv->mode = ISO2022_MODE(cs, shift);\r
- return len + esc_len;\r
-}\r
-\r
-static int\r
-iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- int esc_len;\r
-\r
- if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
- {\r
- esc_len = 0;\r
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
- esc_len += 1;\r
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
- if (bufsize < esc_len)\r
- return seterror(E2BIG);\r
-\r
- esc_len = 0;\r
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
- {\r
- memcpy(buf, iso2022_SI_seq, 1);\r
- esc_len += 1;\r
- }\r
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
- {\r
- memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,\r
- iesc[ISO2022JP_CS_ASCII].esc_len);\r
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
- }\r
- return esc_len;\r
- }\r
- return 0;\r
-}\r
-\r
-#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL)\r
-BOOL WINAPI\r
-DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)\r
-{\r
- switch( fdwReason )\r
- {\r
- case DLL_PROCESS_ATTACH:\r
- hwiniconv = (HMODULE)hinstDLL;\r
- break;\r
- case DLL_THREAD_ATTACH:\r
- case DLL_THREAD_DETACH:\r
- case DLL_PROCESS_DETACH:\r
- break;\r
- }\r
- return TRUE;\r
-}\r
-#endif\r
-\r
-#if defined(MAKE_EXE)\r
-#include <stdio.h>\r
-#include <fcntl.h>\r
-#include <io.h>\r
-int\r
-main(int argc, char **argv)\r
-{\r
- char *fromcode = NULL;\r
- char *tocode = NULL;\r
- int i;\r
- char inbuf[BUFSIZ];\r
- char outbuf[BUFSIZ];\r
- const char *pin;\r
- char *pout;\r
- size_t inbytesleft;\r
- size_t outbytesleft;\r
- size_t rest = 0;\r
- iconv_t cd;\r
- size_t r;\r
- FILE *in = stdin;\r
-\r
- _setmode(_fileno(stdin), _O_BINARY);\r
- _setmode(_fileno(stdout), _O_BINARY);\r
-\r
- for (i = 1; i < argc; ++i)\r
- {\r
- if (strcmp(argv[i], "-l") == 0)\r
- {\r
- for (i = 0; codepage_alias[i].name != NULL; ++i)\r
- printf("%s\n", codepage_alias[i].name);\r
- return 0;\r
- }\r
-\r
- if (strcmp(argv[i], "-f") == 0)\r
- fromcode = argv[++i];\r
- else if (strcmp(argv[i], "-t") == 0)\r
- tocode = argv[++i];\r
- else\r
- {\r
- in = fopen(argv[i], "rb");\r
- if (in == NULL)\r
- {\r
- fprintf(stderr, "cannot open %s\n", argv[i]);\r
- return 1;\r
- }\r
- break;\r
- }\r
- }\r
-\r
- if (fromcode == NULL || tocode == NULL)\r
- {\r
- printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]);\r
- return 0;\r
- }\r
-\r
- cd = iconv_open(tocode, fromcode);\r
- if (cd == (iconv_t)(-1))\r
- {\r
- perror("iconv_open error");\r
- return 1;\r
- }\r
-\r
- while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0\r
- || rest != 0)\r
- {\r
- inbytesleft += rest;\r
- pin = inbuf;\r
- pout = outbuf;\r
- outbytesleft = sizeof(outbuf);\r
- r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);\r
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
- if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in)))\r
- {\r
- perror("conversion error");\r
- return 1;\r
- }\r
- memmove(inbuf, pin, inbytesleft);\r
- rest = inbytesleft;\r
- }\r
- pout = outbuf;\r
- outbytesleft = sizeof(outbuf);\r
- r = iconv(cd, NULL, NULL, &pout, &outbytesleft);\r
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
- if (r == (size_t)(-1))\r
- {\r
- perror("conversion error");\r
- return 1;\r
- }\r
-\r
- iconv_close(cd);\r
-\r
- return 0;\r
-}\r
-#endif\r
-\r
+/*
+ * 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 <windows.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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 <stdio.h>
+#include <fcntl.h>
+#include <io.h>
+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
+
-#include <stddef.h>\r
-typedef void* iconv_t;\r
-iconv_t iconv_open(const char *tocode, const char *fromcode);\r
-int iconv_close(iconv_t cd);\r
-size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
+#include <stddef.h>
+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);
-HRESULT WINAPI ConvertINetString(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnSrcSize,\r
- LPBYTE lpDstStr,\r
- LPINT lpnDstSize\r
-);\r
-\r
-HRESULT WINAPI ConvertINetMultiByteToUnicode(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnMultiCharCount,\r
- LPWSTR lpDstStr,\r
- LPINT lpnWideCharCount\r
-);\r
-\r
-HRESULT WINAPI ConvertINetUnicodeToMultiByte(\r
- LPDWORD lpdwMode,\r
- DWORD dwEncoding,\r
- LPCWSTR lpSrcStr,\r
- LPINT lpnWideCharCount,\r
- LPSTR lpDstStr,\r
- LPINT lpnMultiCharCount\r
-);\r
-\r
-HRESULT WINAPI IsConvertINetStringAvailable(\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding\r
-);\r
-\r
-HRESULT WINAPI LcidToRfc1766A(\r
- LCID Locale,\r
- LPSTR pszRfc1766,\r
- int nChar\r
-);\r
-\r
-HRESULT WINAPI LcidToRfc1766W(\r
- LCID Locale,\r
- LPWSTR pszRfc1766,\r
- int nChar\r
-);\r
-\r
-HRESULT WINAPI Rfc1766ToLcidA(\r
- LCID *pLocale,\r
- LPSTR pszRfc1766\r
-);\r
-\r
-HRESULT WINAPI Rfc1766ToLcidW(\r
- LCID *pLocale,\r
- LPWSTR pszRfc1766\r
-);\r
+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
+);
-win_iconv is a iconv library using Win32 API to conversion.\r
-win_iconv is placed in the public domain.\r
-Yukihiro Nakadaira <yukihiro.nakadaira@gmail.com>\r
+win_iconv is a iconv library using Win32 API to conversion.
+win_iconv is placed in the public domain.
+Yukihiro Nakadaira <yukihiro.nakadaira@gmail.com>
-/*\r
- * iconv library using Win32 API to conversion.\r
- *\r
- * This file is placed in the public domain.\r
- *\r
- * Last Change: 2009-07-06\r
- *\r
- * ENVIRONMENT VARIABLE:\r
- * WINICONV_LIBICONV_DLL\r
- * If $WINICONV_LIBICONV_DLL is set, win_iconv uses the DLL. If\r
- * loading the DLL or iconv_open() failed, falls back to internal\r
- * conversion. If a few DLL are specified as comma separated list,\r
- * the first loadable DLL is used. The DLL should have iconv_open(),\r
- * iconv_close() and iconv(). Or libiconv_open(), libiconv_close()\r
- * and libiconv().\r
- * (only available when USE_LIBICONV_DLL is defined at compile time)\r
- *\r
- * Win32 API does not support strict encoding conversion for some\r
- * codepage. And MLang function drop or replace invalid bytes and does\r
- * not return useful error status as iconv. This implementation cannot\r
- * be used for encoding validation purpose.\r
- */\r
-\r
-/* for WC_NO_BEST_FIT_CHARS */\r
-#ifndef WINVER\r
-# define WINVER 0x0500\r
-#endif\r
-\r
-#include <windows.h>\r
-#include <errno.h>\r
-#include <string.h>\r
-#include <stdlib.h>\r
-\r
-#if 0\r
-# define MAKE_EXE\r
-# define MAKE_DLL\r
-# define USE_LIBICONV_DLL\r
-#endif\r
-\r
-#if !defined(DEFAULT_LIBICONV_DLL)\r
-# define DEFAULT_LIBICONV_DLL ""\r
-#endif\r
-\r
-#define MB_CHAR_MAX 16\r
-\r
-#define UNICODE_MODE_BOM_DONE 1\r
-#define UNICODE_MODE_SWAPPED 2\r
-\r
-#define FLAG_USE_BOM 1\r
-#define FLAG_TRANSLIT 2 /* //TRANSLIT */\r
-#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */\r
-\r
-typedef unsigned char uchar;\r
-typedef unsigned short ushort;\r
-typedef unsigned int uint;\r
-\r
-typedef void* iconv_t;\r
-\r
-iconv_t iconv_open(const char *tocode, const char *fromcode);\r
-int iconv_close(iconv_t cd);\r
-size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-\r
-/* libiconv interface for vim */\r
-#if defined(MAKE_DLL)\r
-int\r
-iconvctl (iconv_t cd, int request, void* argument)\r
-{\r
- /* not supported */\r
- return 0;\r
-}\r
-#endif\r
-\r
-typedef struct compat_t compat_t;\r
-typedef struct csconv_t csconv_t;\r
-typedef struct rec_iconv_t rec_iconv_t;\r
-\r
-typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);\r
-typedef int (*f_iconv_close)(iconv_t cd);\r
-typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-typedef int* (*f_errno)(void);\r
-typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);\r
-typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);\r
-\r
-#define COMPAT_IN 1\r
-#define COMPAT_OUT 2\r
-\r
-/* unicode mapping for compatibility with other conversion table. */\r
-struct compat_t {\r
- uint in;\r
- uint out;\r
- uint flag;\r
-};\r
-\r
-struct csconv_t {\r
- int codepage;\r
- int flags;\r
- f_mbtowc mbtowc;\r
- f_wctomb wctomb;\r
- f_mblen mblen;\r
- f_flush flush;\r
- DWORD mode;\r
- compat_t *compat;\r
-};\r
-\r
-struct rec_iconv_t {\r
- iconv_t cd;\r
- f_iconv_close iconv_close;\r
- f_iconv iconv;\r
- f_errno _errno;\r
- csconv_t from;\r
- csconv_t to;\r
-#if defined(USE_LIBICONV_DLL)\r
- HMODULE hlibiconv;\r
-#endif\r
-};\r
-\r
-static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
-static int win_iconv_close(iconv_t cd);\r
-static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);\r
-\r
-static int load_mlang();\r
-static int make_csconv(const char *name, csconv_t *cv);\r
-static int name_to_codepage(const char *name);\r
-static uint utf16_to_ucs4(const ushort *wbuf);\r
-static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);\r
-static int mbtowc_flags(int codepage);\r
-static int must_use_null_useddefaultchar(int codepage);\r
-static char *strrstr(const char *str, const char *token);\r
-static char *xstrndup(const char *s, size_t n);\r
-static int seterror(int err);\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
-static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);\r
-static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size);\r
-static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname);\r
-\r
-static HMODULE hwiniconv;\r
-#endif\r
-\r
-static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);\r
-\r
-static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);\r
-static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);\r
-static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);\r
-\r
-static struct {\r
- int codepage;\r
- const char *name;\r
-} codepage_alias[] = {\r
- {65001, "CP65001"},\r
- {65001, "UTF8"},\r
- {65001, "UTF-8"},\r
-\r
- {1200, "CP1200"},\r
- {1200, "UTF16LE"},\r
- {1200, "UTF-16LE"},\r
- {1200, "UCS-2LE"},\r
-\r
- {1201, "CP1201"},\r
- {1201, "UTF16BE"},\r
- {1201, "UTF-16BE"},\r
- {1201, "UCS-2BE"},\r
- {1201, "unicodeFFFE"},\r
-\r
- {12000, "CP12000"},\r
- {12000, "UTF32LE"},\r
- {12000, "UTF-32LE"},\r
-\r
- {12001, "CP12001"},\r
- {12001, "UTF32BE"},\r
- {12001, "UTF-32BE"},\r
-\r
-#ifndef GLIB_COMPILATION\r
- /*\r
- * Default is big endian.\r
- * See rfc2781 4.3 Interpreting text labelled as UTF-16.\r
- */\r
- {1201, "UTF16"},\r
- {1201, "UTF-16"},\r
- {12001, "UTF32"},\r
- {12001, "UTF-32"},\r
-#else\r
- /* Default is little endian, because the platform is */\r
- {1200, "UTF16"},\r
- {1200, "UTF-16"},\r
- {1200, "UCS-2"},\r
- {12000, "UTF32"},\r
- {12000, "UTF-32"},\r
-#endif\r
-\r
- /* copy from libiconv `iconv -l` */\r
- /* !IsValidCodePage(367) */\r
- {20127, "ANSI_X3.4-1968"},\r
- {20127, "ANSI_X3.4-1986"},\r
- {20127, "ASCII"},\r
- {20127, "CP367"},\r
- {20127, "IBM367"},\r
- {20127, "ISO-IR-6"},\r
- {20127, "ISO646-US"},\r
- {20127, "ISO_646.IRV:1991"},\r
- {20127, "US"},\r
- {20127, "US-ASCII"},\r
- {20127, "CSASCII"},\r
-\r
- /* !IsValidCodePage(819) */\r
- {1252, "CP819"},\r
- {1252, "IBM819"},\r
- {28591, "ISO-8859-1"},\r
- {28591, "ISO-IR-100"},\r
- {28591, "ISO8859-1"},\r
- {28591, "ISO_8859-1"},\r
- {28591, "ISO_8859-1:1987"},\r
- {28591, "L1"},\r
- {28591, "LATIN1"},\r
- {28591, "CSISOLATIN1"},\r
-\r
- {1250, "CP1250"},\r
- {1250, "MS-EE"},\r
- {1250, "WINDOWS-1250"},\r
-\r
- {1251, "CP1251"},\r
- {1251, "MS-CYRL"},\r
- {1251, "WINDOWS-1251"},\r
-\r
- {1252, "CP1252"},\r
- {1252, "MS-ANSI"},\r
- {1252, "WINDOWS-1252"},\r
-\r
- {1253, "CP1253"},\r
- {1253, "MS-GREEK"},\r
- {1253, "WINDOWS-1253"},\r
-\r
- {1254, "CP1254"},\r
- {1254, "MS-TURK"},\r
- {1254, "WINDOWS-1254"},\r
-\r
- {1255, "CP1255"},\r
- {1255, "MS-HEBR"},\r
- {1255, "WINDOWS-1255"},\r
-\r
- {1256, "CP1256"},\r
- {1256, "MS-ARAB"},\r
- {1256, "WINDOWS-1256"},\r
-\r
- {1257, "CP1257"},\r
- {1257, "WINBALTRIM"},\r
- {1257, "WINDOWS-1257"},\r
-\r
- {1258, "CP1258"},\r
- {1258, "WINDOWS-1258"},\r
-\r
- {850, "850"},\r
- {850, "CP850"},\r
- {850, "IBM850"},\r
- {850, "CSPC850MULTILINGUAL"},\r
-\r
- /* !IsValidCodePage(862) */\r
- {862, "862"},\r
- {862, "CP862"},\r
- {862, "IBM862"},\r
- {862, "CSPC862LATINHEBREW"},\r
-\r
- {866, "866"},\r
- {866, "CP866"},\r
- {866, "IBM866"},\r
- {866, "CSIBM866"},\r
-\r
- /* !IsValidCodePage(154) */\r
- {154, "CP154"},\r
- {154, "CYRILLIC-ASIAN"},\r
- {154, "PT154"},\r
- {154, "PTCP154"},\r
- {154, "CSPTCP154"},\r
-\r
- /* !IsValidCodePage(1133) */\r
- {1133, "CP1133"},\r
- {1133, "IBM-CP1133"},\r
-\r
- {874, "CP874"},\r
- {874, "WINDOWS-874"},\r
-\r
- /* !IsValidCodePage(51932) */\r
- {51932, "CP51932"},\r
- {51932, "MS51932"},\r
- {51932, "WINDOWS-51932"},\r
- {51932, "EUC-JP"},\r
-\r
- {932, "CP932"},\r
- {932, "MS932"},\r
- {932, "SHIFFT_JIS"},\r
- {932, "SHIFFT_JIS-MS"},\r
- {932, "SJIS"},\r
- {932, "SJIS-MS"},\r
- {932, "SJIS-OPEN"},\r
- {932, "SJIS-WIN"},\r
- {932, "WINDOWS-31J"},\r
- {932, "WINDOWS-932"},\r
- {932, "CSWINDOWS31J"},\r
-\r
- {50221, "CP50221"},\r
- {50221, "ISO-2022-JP"},\r
- {50221, "ISO-2022-JP-MS"},\r
- {50221, "ISO2022-JP"},\r
- {50221, "ISO2022-JP-MS"},\r
- {50221, "MS50221"},\r
- {50221, "WINDOWS-50221"},\r
-\r
- {936, "CP936"},\r
- {936, "GBK"},\r
- {936, "MS936"},\r
- {936, "WINDOWS-936"},\r
-\r
- {950, "CP950"},\r
- {950, "BIG5"},\r
-\r
- {949, "CP949"},\r
- {949, "UHC"},\r
- {949, "EUC-KR"},\r
-\r
- {1361, "CP1361"},\r
- {1361, "JOHAB"},\r
-\r
- {437, "437"},\r
- {437, "CP437"},\r
- {437, "IBM437"},\r
- {437, "CSPC8CODEPAGE437"},\r
-\r
- {737, "CP737"},\r
-\r
- {775, "CP775"},\r
- {775, "IBM775"},\r
- {775, "CSPC775BALTIC"},\r
-\r
- {852, "852"},\r
- {852, "CP852"},\r
- {852, "IBM852"},\r
- {852, "CSPCP852"},\r
-\r
- /* !IsValidCodePage(853) */\r
- {853, "CP853"},\r
-\r
- {855, "855"},\r
- {855, "CP855"},\r
- {855, "IBM855"},\r
- {855, "CSIBM855"},\r
-\r
- {857, "857"},\r
- {857, "CP857"},\r
- {857, "IBM857"},\r
- {857, "CSIBM857"},\r
-\r
- /* !IsValidCodePage(858) */\r
- {858, "CP858"},\r
-\r
- {860, "860"},\r
- {860, "CP860"},\r
- {860, "IBM860"},\r
- {860, "CSIBM860"},\r
-\r
- {861, "861"},\r
- {861, "CP-IS"},\r
- {861, "CP861"},\r
- {861, "IBM861"},\r
- {861, "CSIBM861"},\r
-\r
- {863, "863"},\r
- {863, "CP863"},\r
- {863, "IBM863"},\r
- {863, "CSIBM863"},\r
-\r
- {864, "CP864"},\r
- {864, "IBM864"},\r
- {864, "CSIBM864"},\r
-\r
- {865, "865"},\r
- {865, "CP865"},\r
- {865, "IBM865"},\r
- {865, "CSIBM865"},\r
-\r
- {869, "869"},\r
- {869, "CP-GR"},\r
- {869, "CP869"},\r
- {869, "IBM869"},\r
- {869, "CSIBM869"},\r
-\r
- /* !IsValidCodePage(1152) */\r
- {1125, "CP1125"},\r
-\r
- /*\r
- * Code Page Identifiers\r
- * http://msdn2.microsoft.com/en-us/library/ms776446.aspx\r
- */\r
- {37, "IBM037"}, /* IBM EBCDIC US-Canada */\r
- {437, "IBM437"}, /* OEM United States */\r
- {500, "IBM500"}, /* IBM EBCDIC International */\r
- {708, "ASMO-708"}, /* Arabic (ASMO 708) */\r
- /* 709 Arabic (ASMO-449+, BCON V4) */\r
- /* 710 Arabic - Transparent Arabic */\r
- {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */\r
- {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */\r
- {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */\r
- {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */\r
- {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */\r
- {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */\r
- {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */\r
- {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */\r
- {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */\r
- {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */\r
- {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */\r
- {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */\r
- {864, "IBM864"}, /* OEM Arabic; Arabic (864) */\r
- {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */\r
- {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */\r
- {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */\r
- {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */\r
- {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */\r
- {875, "cp875"}, /* IBM EBCDIC Greek Modern */\r
- {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */\r
- {932, "shift-jis"}, /* alternative name for it */\r
- {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */\r
- {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */\r
- {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */\r
- {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */\r
- {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */\r
- {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */\r
- {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */\r
- {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */\r
- {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */\r
- {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */\r
- {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */\r
- {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */\r
- {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */\r
- {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */\r
- {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */\r
- {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */\r
- {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */\r
- {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */\r
- {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */\r
- {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */\r
- {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */\r
- {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */\r
- {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */\r
- {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */\r
- {1361, "Johab"}, /* Korean (Johab) */\r
- {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */\r
- {10001, "x-mac-japanese"}, /* Japanese (Mac) */\r
- {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */\r
- {10003, "x-mac-korean"}, /* Korean (Mac) */\r
- {10004, "x-mac-arabic"}, /* Arabic (Mac) */\r
- {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */\r
- {10006, "x-mac-greek"}, /* Greek (Mac) */\r
- {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */\r
- {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */\r
- {10010, "x-mac-romanian"}, /* Romanian (Mac) */\r
- {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */\r
- {10021, "x-mac-thai"}, /* Thai (Mac) */\r
- {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */\r
- {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */\r
- {10081, "x-mac-turkish"}, /* Turkish (Mac) */\r
- {10082, "x-mac-croatian"}, /* Croatian (Mac) */\r
- {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */\r
- {20001, "x-cp20001"}, /* TCA Taiwan */\r
- {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */\r
- {20003, "x-cp20003"}, /* IBM5550 Taiwan */\r
- {20004, "x-cp20004"}, /* TeleText Taiwan */\r
- {20005, "x-cp20005"}, /* Wang Taiwan */\r
- {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */\r
- {20106, "x-IA5-German"}, /* IA5 German (7-bit) */\r
- {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */\r
- {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */\r
- {20127, "us-ascii"}, /* US-ASCII (7-bit) */\r
- {20261, "x-cp20261"}, /* T.61 */\r
- {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */\r
- {20273, "IBM273"}, /* IBM EBCDIC Germany */\r
- {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */\r
- {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */\r
- {20280, "IBM280"}, /* IBM EBCDIC Italy */\r
- {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */\r
- {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */\r
- {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */\r
- {20297, "IBM297"}, /* IBM EBCDIC France */\r
- {20420, "IBM420"}, /* IBM EBCDIC Arabic */\r
- {20423, "IBM423"}, /* IBM EBCDIC Greek */\r
- {20424, "IBM424"}, /* IBM EBCDIC Hebrew */\r
- {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */\r
- {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */\r
- {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */\r
- {20871, "IBM871"}, /* IBM EBCDIC Icelandic */\r
- {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */\r
- {20905, "IBM905"}, /* IBM EBCDIC Turkish */\r
- {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */\r
- {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */\r
- {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */\r
- {20949, "x-cp20949"}, /* Korean Wansung */\r
- {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */\r
- /* 21027 (deprecated) */\r
- {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */\r
- {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
- {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */\r
- {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
- {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */\r
- {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */\r
- {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */\r
- {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */\r
- {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */\r
- {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */\r
- {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */\r
- {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */\r
- {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */\r
- {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */\r
- {28597, "iso8859-7"}, /* ISO 8859-7 Greek */\r
- {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
- {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */\r
- {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */\r
- {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */\r
- {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */\r
- {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */\r
- {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */\r
- {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */\r
- {29001, "x-Europa"}, /* Europa 3 */\r
- {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
- {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */\r
- {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */\r
- {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */\r
- {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */\r
- {50225, "iso-2022-kr"}, /* ISO 2022 Korean */\r
- {50225, "iso2022-kr"}, /* ISO 2022 Korean */\r
- {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */\r
- /* 50229 ISO 2022 Traditional Chinese */\r
- /* 50930 EBCDIC Japanese (Katakana) Extended */\r
- /* 50931 EBCDIC US-Canada and Japanese */\r
- /* 50933 EBCDIC Korean Extended and Korean */\r
- /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */\r
- /* 50936 EBCDIC Simplified Chinese */\r
- /* 50937 EBCDIC US-Canada and Traditional Chinese */\r
- /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */\r
- {51932, "euc-jp"}, /* EUC Japanese */\r
- {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */\r
- {51949, "euc-kr"}, /* EUC Korean */\r
- /* 51950 EUC Traditional Chinese */\r
- {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */\r
- {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */\r
- {57002, "x-iscii-de"}, /* ISCII Devanagari */\r
- {57003, "x-iscii-be"}, /* ISCII Bengali */\r
- {57004, "x-iscii-ta"}, /* ISCII Tamil */\r
- {57005, "x-iscii-te"}, /* ISCII Telugu */\r
- {57006, "x-iscii-as"}, /* ISCII Assamese */\r
- {57007, "x-iscii-or"}, /* ISCII Oriya */\r
- {57008, "x-iscii-ka"}, /* ISCII Kannada */\r
- {57009, "x-iscii-ma"}, /* ISCII Malayalam */\r
- {57010, "x-iscii-gu"}, /* ISCII Gujarati */\r
- {57011, "x-iscii-pa"}, /* ISCII Punjabi */\r
-\r
- {0, NULL}\r
-};\r
-\r
-/*\r
- * SJIS SHIFTJIS table CP932 table\r
- * ---- --------------------------- --------------------------------\r
- * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS\r
- * 7E U+203E OVERLINE U+007E TILDE\r
- * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR\r
- * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS\r
- * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE\r
- * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO\r
- * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS\r
- * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN\r
- * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN\r
- * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN\r
- *\r
- * EUC-JP and ISO-2022-JP should be compatible with CP932.\r
- *\r
- * Kernel and MLang have different Unicode mapping table. Make sure\r
- * which API is used.\r
- */\r
-static compat_t cp932_compat[] = {\r
- {0x00A5, 0x005C, COMPAT_OUT},\r
- {0x203E, 0x007E, COMPAT_OUT},\r
- {0x2014, 0x2015, COMPAT_OUT},\r
- {0x301C, 0xFF5E, COMPAT_OUT},\r
- {0x2016, 0x2225, COMPAT_OUT},\r
- {0x2212, 0xFF0D, COMPAT_OUT},\r
- {0x00A2, 0xFFE0, COMPAT_OUT},\r
- {0x00A3, 0xFFE1, COMPAT_OUT},\r
- {0x00AC, 0xFFE2, COMPAT_OUT},\r
- {0, 0, 0}\r
-};\r
-\r
-static compat_t cp20932_compat[] = {\r
- {0x00A5, 0x005C, COMPAT_OUT},\r
- {0x203E, 0x007E, COMPAT_OUT},\r
- {0x2014, 0x2015, COMPAT_OUT},\r
- {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},\r
- {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},\r
- {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},\r
- {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},\r
- {0, 0, 0}\r
-};\r
-\r
-static compat_t *cp51932_compat = cp932_compat;\r
-\r
-/* cp20932_compat for kernel. cp932_compat for mlang. */\r
-static compat_t *cp5022x_compat = cp932_compat;\r
-\r
-typedef HRESULT (WINAPI *CONVERTINETSTRING)(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnSrcSize,\r
- LPBYTE lpDstStr,\r
- LPINT lpnDstSize\r
-);\r
-typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(\r
- LPDWORD lpdwMode,\r
- DWORD dwSrcEncoding,\r
- LPCSTR lpSrcStr,\r
- LPINT lpnMultiCharCount,\r
- LPWSTR lpDstStr,\r
- LPINT lpnWideCharCount\r
-);\r
-typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(\r
- LPDWORD lpdwMode,\r
- DWORD dwEncoding,\r
- LPCWSTR lpSrcStr,\r
- LPINT lpnWideCharCount,\r
- LPSTR lpDstStr,\r
- LPINT lpnMultiCharCount\r
-);\r
-typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(\r
- DWORD dwSrcEncoding,\r
- DWORD dwDstEncoding\r
-);\r
-typedef HRESULT (WINAPI *LCIDTORFC1766A)(\r
- LCID Locale,\r
- LPSTR pszRfc1766,\r
- int nChar\r
-);\r
-typedef HRESULT (WINAPI *LCIDTORFC1766W)(\r
- LCID Locale,\r
- LPWSTR pszRfc1766,\r
- int nChar\r
-);\r
-typedef HRESULT (WINAPI *RFC1766TOLCIDA)(\r
- LCID *pLocale,\r
- LPSTR pszRfc1766\r
-);\r
-typedef HRESULT (WINAPI *RFC1766TOLCIDW)(\r
- LCID *pLocale,\r
- LPWSTR pszRfc1766\r
-);\r
-static CONVERTINETSTRING ConvertINetString;\r
-static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;\r
-static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;\r
-static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;\r
-static LCIDTORFC1766A LcidToRfc1766A;\r
-static RFC1766TOLCIDA Rfc1766ToLcidA;\r
-\r
-static int\r
-load_mlang()\r
-{\r
- HMODULE h;\r
- if (ConvertINetString != NULL)\r
- return TRUE;\r
- h = LoadLibrary("mlang.dll");\r
- if (!h)\r
- return FALSE;\r
- ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString");\r
- ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode");\r
- ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte");\r
- IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable");\r
- LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A");\r
- Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA");\r
- return TRUE;\r
-}\r
-\r
-iconv_t\r
-iconv_open(const char *tocode, const char *fromcode)\r
-{\r
- rec_iconv_t *cd;\r
-\r
- cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t));\r
- if (cd == NULL)\r
- return (iconv_t)(-1);\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
- errno = 0;\r
- if (libiconv_iconv_open(cd, tocode, fromcode))\r
- return (iconv_t)cd;\r
-#endif\r
-\r
- /* reset the errno to prevent reporting wrong error code.\r
- * 0 for unsorted error. */\r
- errno = 0;\r
- if (win_iconv_open(cd, tocode, fromcode))\r
- return (iconv_t)cd;\r
-\r
- free(cd);\r
-\r
- return (iconv_t)(-1);\r
-}\r
-\r
-int\r
-iconv_close(iconv_t _cd)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- int r = cd->iconv_close(cd->cd);\r
- int e = *(cd->_errno());\r
-#if defined(USE_LIBICONV_DLL)\r
- if (cd->hlibiconv != NULL)\r
- FreeLibrary(cd->hlibiconv);\r
-#endif\r
- free(cd);\r
- errno = e;\r
- return r;\r
-}\r
-\r
-size_t\r
-iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);\r
- errno = *(cd->_errno());\r
- return r;\r
-}\r
-\r
-static int\r
-win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)\r
-{\r
- if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to))\r
- return FALSE;\r
- cd->iconv_close = win_iconv_close;\r
- cd->iconv = win_iconv;\r
- cd->_errno = _errno;\r
- cd->cd = (iconv_t)cd;\r
- return TRUE;\r
-}\r
-\r
-static int\r
-win_iconv_close(iconv_t cd)\r
-{\r
- return 0;\r
-}\r
-\r
-static size_t\r
-win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)\r
-{\r
- rec_iconv_t *cd = (rec_iconv_t *)_cd;\r
- ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */\r
- int insize;\r
- int outsize;\r
- int wsize;\r
- DWORD frommode;\r
- DWORD tomode;\r
- uint wc;\r
- compat_t *cp;\r
- int i;\r
-\r
- if (inbuf == NULL || *inbuf == NULL)\r
- {\r
- if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)\r
- {\r
- tomode = cd->to.mode;\r
- outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);\r
- if (outsize == -1)\r
- {\r
- cd->to.mode = tomode;\r
- return (size_t)(-1);\r
- }\r
- *outbuf += outsize;\r
- *outbytesleft -= outsize;\r
- }\r
- cd->from.mode = 0;\r
- cd->to.mode = 0;\r
- return 0;\r
- }\r
-\r
- while (*inbytesleft != 0)\r
- {\r
- frommode = cd->from.mode;\r
- tomode = cd->to.mode;\r
- wsize = MB_CHAR_MAX;\r
-\r
- insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);\r
- if (insize == -1)\r
- {\r
- cd->from.mode = frommode;\r
- return (size_t)(-1);\r
- }\r
-\r
- if (wsize == 0)\r
- {\r
- *inbuf += insize;\r
- *inbytesleft -= insize;\r
- continue;\r
- }\r
-\r
- if (cd->from.compat != NULL)\r
- {\r
- wc = utf16_to_ucs4(wbuf);\r
- cp = cd->from.compat;\r
- for (i = 0; cp[i].in != 0; ++i)\r
- {\r
- if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)\r
- {\r
- ucs4_to_utf16(cp[i].in, wbuf, &wsize);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- if (cd->to.compat != NULL)\r
- {\r
- wc = utf16_to_ucs4(wbuf);\r
- cp = cd->to.compat;\r
- for (i = 0; cp[i].in != 0; ++i)\r
- {\r
- if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)\r
- {\r
- ucs4_to_utf16(cp[i].out, wbuf, &wsize);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);\r
- if (outsize == -1)\r
- {\r
- cd->from.mode = frommode;\r
- cd->to.mode = tomode;\r
- return (size_t)(-1);\r
- }\r
-\r
- *inbuf += insize;\r
- *outbuf += outsize;\r
- *inbytesleft -= insize;\r
- *outbytesleft -= outsize;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-static int\r
-make_csconv(const char *_name, csconv_t *cv)\r
-{\r
- CPINFOEX cpinfoex;\r
- int use_compat = TRUE;\r
- int flag = 0;\r
- char *name;\r
- char *p;\r
-\r
- name = xstrndup(_name, strlen(_name));\r
- if (name == NULL)\r
- return FALSE;\r
-\r
- /* check for option "enc_name//opt1//opt2" */\r
- while ((p = strrstr(name, "//")) != NULL)\r
- {\r
- if (_stricmp(p + 2, "nocompat") == 0)\r
- use_compat = FALSE;\r
- else if (_stricmp(p + 2, "translit") == 0)\r
- flag |= FLAG_TRANSLIT;\r
- else if (_stricmp(p + 2, "ignore") == 0)\r
- flag |= FLAG_IGNORE;\r
- *p = 0;\r
- }\r
-\r
- cv->mode = 0;\r
- cv->flags = flag;\r
- cv->mblen = NULL;\r
- cv->flush = NULL;\r
- cv->compat = NULL;\r
- cv->codepage = name_to_codepage(name);\r
- if (cv->codepage == 1200 || cv->codepage == 1201)\r
- {\r
- cv->mbtowc = utf16_mbtowc;\r
- cv->wctomb = utf16_wctomb;\r
- if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0)\r
- cv->flags |= FLAG_USE_BOM;\r
- }\r
- else if (cv->codepage == 12000 || cv->codepage == 12001)\r
- {\r
- cv->mbtowc = utf32_mbtowc;\r
- cv->wctomb = utf32_wctomb;\r
- if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0)\r
- cv->flags |= FLAG_USE_BOM;\r
- }\r
- else if (cv->codepage == 65001)\r
- {\r
- cv->mbtowc = kernel_mbtowc;\r
- cv->wctomb = kernel_wctomb;\r
- cv->mblen = utf8_mblen;\r
- }\r
- else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang())\r
- {\r
- cv->mbtowc = iso2022jp_mbtowc;\r
- cv->wctomb = iso2022jp_wctomb;\r
- cv->flush = iso2022jp_flush;\r
- }\r
- else if (cv->codepage == 51932 && load_mlang())\r
- {\r
- cv->mbtowc = mlang_mbtowc;\r
- cv->wctomb = mlang_wctomb;\r
- cv->mblen = eucjp_mblen;\r
- }\r
- else if (IsValidCodePage(cv->codepage)\r
- && GetCPInfoEx(cv->codepage, 0, &cpinfoex) != 0)\r
- {\r
- cv->mbtowc = kernel_mbtowc;\r
- cv->wctomb = kernel_wctomb;\r
- if (cpinfoex.MaxCharSize == 1)\r
- cv->mblen = sbcs_mblen;\r
- else if (cpinfoex.MaxCharSize == 2)\r
- cv->mblen = dbcs_mblen;\r
- else\r
- cv->mblen = mbcs_mblen;\r
- }\r
- else\r
- {\r
- /* not supported */\r
- free(name);\r
- errno = EINVAL;\r
- return FALSE;\r
- }\r
-\r
- if (use_compat)\r
- {\r
- switch (cv->codepage)\r
- {\r
- case 932: cv->compat = cp932_compat; break;\r
- case 20932: cv->compat = cp20932_compat; break;\r
- case 51932: cv->compat = cp51932_compat; break;\r
- case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break;\r
- }\r
- }\r
-\r
- free(name);\r
-\r
- return TRUE;\r
-}\r
-\r
-static int\r
-name_to_codepage(const char *name)\r
-{\r
- int i;\r
-\r
- if (*name == '\0' ||\r
- strcmp(name, "char") == 0)\r
- return GetACP();\r
- else if (strcmp(name, "wchar_t") == 0)\r
- return 1200;\r
- else if (_strnicmp(name, "cp", 2) == 0)\r
- return atoi(name + 2); /* CP123 */\r
- else if ('0' <= name[0] && name[0] <= '9')\r
- return atoi(name); /* 123 */\r
- else if (_strnicmp(name, "xx", 2) == 0)\r
- return atoi(name + 2); /* XX123 for debug */\r
-\r
- for (i = 0; codepage_alias[i].name != NULL; ++i)\r
- if (_stricmp(name, codepage_alias[i].name) == 0)\r
- return codepage_alias[i].codepage;\r
- return -1;\r
-}\r
-\r
-/*\r
- * http://www.faqs.org/rfcs/rfc2781.html\r
- */\r
-static uint\r
-utf16_to_ucs4(const ushort *wbuf)\r
-{\r
- uint wc = wbuf[0];\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;\r
- return wc;\r
-}\r
-\r
-static void\r
-ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)\r
-{\r
- if (wc < 0x10000)\r
- {\r
- wbuf[0] = wc;\r
- *wbufsize = 1;\r
- }\r
- else\r
- {\r
- wc -= 0x10000;\r
- wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);\r
- wbuf[1] = 0xDC00 | (wc & 0x3FF);\r
- *wbufsize = 2;\r
- }\r
-}\r
-\r
-/*\r
- * Check if codepage is one of those for which the dwFlags parameter\r
- * to MultiByteToWideChar() must be zero. Return zero or\r
- * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows\r
- * Server 2003 R2 claims that also codepage 65001 is one of these, but\r
- * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave\r
- * out 65001 (UTF-8), and that indeed seems to be the case on XP, it\r
- * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting\r
- * from UTF-8.\r
- */\r
-static int\r
-mbtowc_flags(int codepage)\r
-{\r
- return (codepage == 50220 || codepage == 50221 ||\r
- codepage == 50222 || codepage == 50225 ||\r
- codepage == 50227 || codepage == 50229 ||\r
- codepage == 52936 || codepage == 54936 ||\r
- (codepage >= 57002 && codepage <= 57011) ||\r
- codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;\r
-}\r
-\r
-/*\r
- * Check if codepage is one those for which the lpUsedDefaultChar\r
- * parameter to WideCharToMultiByte() must be NULL. The docs in\r
- * Platform SDK for for Windows Server 2003 R2 claims that this is the\r
- * list below, while the MSDN docs for MSVS2008 claim that it is only\r
- * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform\r
- * SDK seems to be correct, at least for XP.\r
- */\r
-static int\r
-must_use_null_useddefaultchar(int codepage)\r
-{\r
- return (codepage == 65000 || codepage == 65001 ||\r
- codepage == 50220 || codepage == 50221 ||\r
- codepage == 50222 || codepage == 50225 ||\r
- codepage == 50227 || codepage == 50229 ||\r
- codepage == 52936 || codepage == 54936 ||\r
- (codepage >= 57002 && codepage <= 57011) ||\r
- codepage == 42);\r
-}\r
-\r
-static char *\r
-strrstr(const char *str, const char *token)\r
-{\r
- int len = strlen(token);\r
- const char *p = str + strlen(str);\r
-\r
- while (str <= --p)\r
- if (p[0] == token[0] && strncmp(p, token, len) == 0)\r
- return (char *)p;\r
- return NULL;\r
-}\r
-\r
-static char *\r
-xstrndup(const char *s, size_t n)\r
-{\r
- char *p;\r
-\r
- p = (char *)malloc(n + 1);\r
- if (p == NULL)\r
- return NULL;\r
- memcpy(p, s, n);\r
- p[n] = '\0';\r
- return p;\r
-}\r
-\r
-static int\r
-seterror(int err)\r
-{\r
- errno = err;\r
- return -1;\r
-}\r
-\r
-#if defined(USE_LIBICONV_DLL)\r
-static int\r
-libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)\r
-{\r
- HMODULE hlibiconv = NULL;\r
- HMODULE hmsvcrt = NULL;\r
- char *dllname;\r
- const char *p;\r
- const char *e;\r
- f_iconv_open _iconv_open;\r
-\r
- /*\r
- * always try to load dll, so that we can switch dll in runtime.\r
- */\r
-\r
- /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */\r
- p = getenv("WINICONV_LIBICONV_DLL");\r
- if (p == NULL)\r
- p = DEFAULT_LIBICONV_DLL;\r
- /* parse comma separated value */\r
- for ( ; *p != 0; p = (*e == ',') ? e + 1 : e)\r
- {\r
- e = strchr(p, ',');\r
- if (p == e)\r
- continue;\r
- else if (e == NULL)\r
- e = p + strlen(p);\r
- dllname = xstrndup(p, e - p);\r
- if (dllname == NULL)\r
- return FALSE;\r
- hlibiconv = LoadLibrary(dllname);\r
- free(dllname);\r
- if (hlibiconv != NULL)\r
- {\r
- if (hlibiconv == hwiniconv)\r
- {\r
- FreeLibrary(hlibiconv);\r
- hlibiconv = NULL;\r
- continue;\r
- }\r
- break;\r
- }\r
- }\r
-\r
- if (hlibiconv == NULL)\r
- goto failed;\r
-\r
- hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno");\r
- if (hmsvcrt == NULL)\r
- goto failed;\r
-\r
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open");\r
- if (_iconv_open == NULL)\r
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open");\r
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close");\r
- if (cd->iconv_close == NULL)\r
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close");\r
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv");\r
- if (cd->iconv == NULL)\r
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv");\r
- cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno");\r
- if (_iconv_open == NULL || cd->iconv_close == NULL\r
- || cd->iconv == NULL || cd->_errno == NULL)\r
- goto failed;\r
-\r
- cd->cd = _iconv_open(tocode, fromcode);\r
- if (cd->cd == (iconv_t)(-1))\r
- goto failed;\r
-\r
- cd->hlibiconv = hlibiconv;\r
- return TRUE;\r
-\r
-failed:\r
- if (hlibiconv != NULL)\r
- FreeLibrary(hlibiconv);\r
- /* do not free hmsvcrt which is obtained by GetModuleHandle() */\r
- return FALSE;\r
-}\r
-\r
-/*\r
- * Reference:\r
- * http://forums.belution.com/ja/vc/000/234/78s.shtml\r
- * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html\r
- *\r
- * The formal way is\r
- * imagehlp.h or dbghelp.h\r
- * imagehlp.lib or dbghelp.lib\r
- * ImageDirectoryEntryToData()\r
- */\r
-#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base))\r
-#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew))\r
-static PVOID\r
-MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size)\r
-{\r
- /* TODO: MappedAsImage? */\r
- PIMAGE_DATA_DIRECTORY p;\r
- p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry;\r
- if (p->VirtualAddress == 0) {\r
- *Size = 0;\r
- return NULL;\r
- }\r
- *Size = p->Size;\r
- return (PVOID)((LPBYTE)Base + p->VirtualAddress);\r
-}\r
-\r
-static HMODULE\r
-find_imported_module_by_funcname(HMODULE hModule, const char *funcname)\r
-{\r
- DWORD Base;\r
- ULONG Size;\r
- PIMAGE_IMPORT_DESCRIPTOR Imp;\r
- PIMAGE_THUNK_DATA Name; /* Import Name Table */\r
- PIMAGE_IMPORT_BY_NAME ImpName;\r
-\r
- Base = (DWORD)hModule;\r
- Imp = MyImageDirectoryEntryToData(\r
- (LPVOID)Base,\r
- TRUE,\r
- IMAGE_DIRECTORY_ENTRY_IMPORT,\r
- &Size);\r
- if (Imp == NULL)\r
- return NULL;\r
- for ( ; Imp->OriginalFirstThunk != 0; ++Imp)\r
- {\r
- Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk);\r
- for ( ; Name->u1.Ordinal != 0; ++Name)\r
- {\r
- if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal))\r
- {\r
- ImpName = (PIMAGE_IMPORT_BY_NAME)\r
- (Base + (DWORD)Name->u1.AddressOfData);\r
- if (strcmp((char *)ImpName->Name, funcname) == 0)\r
- return GetModuleHandle((char *)(Base + Imp->Name));\r
- }\r
- }\r
- }\r
- return NULL;\r
-}\r
-#endif\r
-\r
-static int\r
-sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- return 1;\r
-}\r
-\r
-static int\r
-dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;\r
- if (bufsize < len)\r
- return seterror(EINVAL);\r
- return len;\r
-}\r
-\r
-static int\r
-mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = 0;\r
-\r
- if (cv->codepage == 54936) {\r
- if (buf[0] <= 0x7F) len = 1;\r
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
- bufsize >= 2 &&\r
- ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||\r
- (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2;\r
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&\r
- bufsize >= 4 &&\r
- buf[1] >= 0x30 && buf[1] <= 0x39) len = 4;\r
- else\r
- return seterror(EINVAL);\r
- return len;\r
- }\r
- else\r
- return seterror(EINVAL);\r
-}\r
-\r
-static int\r
-utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- int len = 0;\r
-\r
- if (buf[0] < 0x80) len = 1;\r
- else if ((buf[0] & 0xE0) == 0xC0) len = 2;\r
- else if ((buf[0] & 0xF0) == 0xE0) len = 3;\r
- else if ((buf[0] & 0xF8) == 0xF0) len = 4;\r
- else if ((buf[0] & 0xFC) == 0xF8) len = 5;\r
- else if ((buf[0] & 0xFE) == 0xFC) len = 6;\r
-\r
- if (len == 0)\r
- return seterror(EILSEQ);\r
- else if (bufsize < len)\r
- return seterror(EINVAL);\r
- return len;\r
-}\r
-\r
-static int\r
-eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize)\r
-{\r
- if (buf[0] < 0x80) /* ASCII */\r
- return 1;\r
- else if (buf[0] == 0x8E) /* JIS X 0201 */\r
- {\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))\r
- return seterror(EILSEQ);\r
- return 2;\r
- }\r
- else if (buf[0] == 0x8F) /* JIS X 0212 */\r
- {\r
- if (bufsize < 3)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)\r
- || !(0xA1 <= buf[2] && buf[2] <= 0xFE))\r
- return seterror(EILSEQ);\r
- return 3;\r
- }\r
- else /* JIS X 0208 */\r
- {\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)\r
- || !(0xA1 <= buf[1] && buf[1] <= 0xFE))\r
- return seterror(EILSEQ);\r
- return 2;\r
- }\r
-}\r
-\r
-static int\r
-kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int len;\r
-\r
- len = cv->mblen(cv, buf, bufsize);\r
- if (len == -1)\r
- return -1;\r
- *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),\r
- (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);\r
- if (*wbufsize == 0)\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-static int\r
-kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- BOOL usedDefaultChar = 0;\r
- BOOL *p = NULL;\r
- int flags = 0;\r
- int len;\r
-\r
- if (bufsize == 0)\r
- return seterror(E2BIG);\r
- if (!must_use_null_useddefaultchar(cv->codepage))\r
- {\r
- p = &usedDefaultChar;\r
-#ifdef WC_NO_BEST_FIT_CHARS\r
- if (!(cv->flags & FLAG_TRANSLIT))\r
- flags |= WC_NO_BEST_FIT_CHARS;\r
-#endif\r
- }\r
- len = WideCharToMultiByte(cv->codepage, flags,\r
- (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);\r
- if (len == 0)\r
- {\r
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)\r
- return seterror(E2BIG);\r
- return seterror(EILSEQ);\r
- }\r
- else if (usedDefaultChar)\r
- return seterror(EILSEQ);\r
- else if (cv->mblen(cv, buf, len) != len) /* validate result */\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-/*\r
- * It seems that the mode (cv->mode) is fixnum.\r
- * For example, when converting iso-2022-jp(cp50221) to unicode:\r
- * in ascii sequence: mode=0xC42C0000\r
- * in jisx0208 sequence: mode=0xC42C0001\r
- * "C42C" is same for each convert session.\r
- * It should be: ((codepage-1)<<16)|state\r
- */\r
-static int\r
-mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int len;\r
- int insize;\r
- HRESULT hr;\r
-\r
- len = cv->mblen(cv, buf, bufsize);\r
- if (len == -1)\r
- return -1;\r
- insize = len;\r
- hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,\r
- (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);\r
- if (hr != S_OK || insize != len)\r
- return seterror(EILSEQ);\r
- return len;\r
-}\r
-\r
-static int\r
-mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */\r
- int tmpsize = MB_CHAR_MAX;\r
- int insize = wbufsize;\r
- HRESULT hr;\r
-\r
- hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,\r
- (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);\r
- if (hr != S_OK || insize != wbufsize)\r
- return seterror(EILSEQ);\r
- else if (bufsize < tmpsize)\r
- return seterror(E2BIG);\r
- else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)\r
- return seterror(EILSEQ);\r
- memcpy(buf, tmpbuf, tmpsize);\r
- return tmpsize;\r
-}\r
-\r
-static int\r
-utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int codepage = cv->codepage;\r
-\r
- /* swap endian: 1200 <-> 1201 */\r
- if (cv->mode & UNICODE_MODE_SWAPPED)\r
- codepage ^= 1;\r
-\r
- if (bufsize < 2)\r
- return seterror(EINVAL);\r
- if (codepage == 1200) /* little endian */\r
- wbuf[0] = (buf[1] << 8) | buf[0];\r
- else if (codepage == 1201) /* big endian */\r
- wbuf[0] = (buf[0] << 8) | buf[1];\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (wbuf[0] == 0xFFFE)\r
- {\r
- cv->mode |= UNICODE_MODE_SWAPPED;\r
- *wbufsize = 0;\r
- return 2;\r
- }\r
- else if (wbuf[0] == 0xFEFF)\r
- {\r
- *wbufsize = 0;\r
- return 2;\r
- }\r
- }\r
-\r
- if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)\r
- return seterror(EILSEQ);\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- {\r
- if (bufsize < 4)\r
- return seterror(EINVAL);\r
- if (codepage == 1200) /* little endian */\r
- wbuf[1] = (buf[3] << 8) | buf[2];\r
- else if (codepage == 1201) /* big endian */\r
- wbuf[1] = (buf[2] << 8) | buf[3];\r
- if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))\r
- return seterror(EILSEQ);\r
- *wbufsize = 2;\r
- return 4;\r
- }\r
- *wbufsize = 1;\r
- return 2;\r
-}\r
-\r
-static int\r
-utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- int r;\r
-\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (bufsize < 2)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- memcpy(buf, "\xFF\xFE", 2);\r
- else if (cv->codepage == 1201) /* big endian */\r
- memcpy(buf, "\xFE\xFF", 2);\r
-\r
- r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2);\r
- if (r == -1)\r
- return -1;\r
- return r + 2;\r
- }\r
-\r
- if (bufsize < 2)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- {\r
- buf[0] = (wbuf[0] & 0x00FF);\r
- buf[1] = (wbuf[0] & 0xFF00) >> 8;\r
- }\r
- else if (cv->codepage == 1201) /* big endian */\r
- {\r
- buf[0] = (wbuf[0] & 0xFF00) >> 8;\r
- buf[1] = (wbuf[0] & 0x00FF);\r
- }\r
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)\r
- {\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 1200) /* little endian */\r
- {\r
- buf[2] = (wbuf[1] & 0x00FF);\r
- buf[3] = (wbuf[1] & 0xFF00) >> 8;\r
- }\r
- else if (cv->codepage == 1201) /* big endian */\r
- {\r
- buf[2] = (wbuf[1] & 0xFF00) >> 8;\r
- buf[3] = (wbuf[1] & 0x00FF);\r
- }\r
- return 4;\r
- }\r
- return 2;\r
-}\r
-\r
-static int\r
-utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- int codepage = cv->codepage;\r
- uint wc;\r
-\r
- /* swap endian: 12000 <-> 12001 */\r
- if (cv->mode & UNICODE_MODE_SWAPPED)\r
- codepage ^= 1;\r
-\r
- if (bufsize < 4)\r
- return seterror(EINVAL);\r
- if (codepage == 12000) /* little endian */\r
- wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];\r
- else if (codepage == 12001) /* big endian */\r
- wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (wc == 0xFFFE0000)\r
- {\r
- cv->mode |= UNICODE_MODE_SWAPPED;\r
- *wbufsize = 0;\r
- return 4;\r
- }\r
- else if (wc == 0x0000FEFF)\r
- {\r
- *wbufsize = 0;\r
- return 4;\r
- }\r
- }\r
-\r
- if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)\r
- return seterror(EILSEQ);\r
- ucs4_to_utf16(wc, wbuf, wbufsize);\r
- return 4;\r
-}\r
-\r
-static int\r
-utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- uint wc;\r
-\r
- if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))\r
- {\r
- int r;\r
-\r
- cv->mode |= UNICODE_MODE_BOM_DONE;\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- if (cv->codepage == 12000) /* little endian */\r
- memcpy(buf, "\xFF\xFE\x00\x00", 4);\r
- else if (cv->codepage == 12001) /* big endian */\r
- memcpy(buf, "\x00\x00\xFE\xFF", 4);\r
-\r
- r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4);\r
- if (r == -1)\r
- return -1;\r
- return r + 4;\r
- }\r
-\r
- if (bufsize < 4)\r
- return seterror(E2BIG);\r
- wc = utf16_to_ucs4(wbuf);\r
- if (cv->codepage == 12000) /* little endian */\r
- {\r
- buf[0] = wc & 0x000000FF;\r
- buf[1] = (wc & 0x0000FF00) >> 8;\r
- buf[2] = (wc & 0x00FF0000) >> 16;\r
- buf[3] = (wc & 0xFF000000) >> 24;\r
- }\r
- else if (cv->codepage == 12001) /* big endian */\r
- {\r
- buf[0] = (wc & 0xFF000000) >> 24;\r
- buf[1] = (wc & 0x00FF0000) >> 16;\r
- buf[2] = (wc & 0x0000FF00) >> 8;\r
- buf[3] = wc & 0x000000FF;\r
- }\r
- return 4;\r
-}\r
-\r
-/*\r
- * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)\r
- * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow\r
- * 1 byte Kana)\r
- * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte\r
- * Kana - SO/SI)\r
- *\r
- * MultiByteToWideChar() and WideCharToMultiByte() behave differently\r
- * depending on Windows version. On XP, WideCharToMultiByte() doesn't\r
- * terminate result sequence with ascii escape. But Vista does.\r
- * Use MLang instead.\r
- */\r
-\r
-#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))\r
-#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)\r
-#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)\r
-\r
-#define ISO2022_SI 0\r
-#define ISO2022_SO 1\r
-\r
-/* shift in */\r
-static const char iso2022_SI_seq[] = "\x0F";\r
-/* shift out */\r
-static const char iso2022_SO_seq[] = "\x0E";\r
-\r
-typedef struct iso2022_esc_t iso2022_esc_t;\r
-struct iso2022_esc_t {\r
- const char *esc;\r
- int esc_len;\r
- int len;\r
- int cs;\r
-};\r
-\r
-#define ISO2022JP_CS_ASCII 0\r
-#define ISO2022JP_CS_JISX0201_ROMAN 1\r
-#define ISO2022JP_CS_JISX0201_KANA 2\r
-#define ISO2022JP_CS_JISX0208_1978 3\r
-#define ISO2022JP_CS_JISX0208_1983 4\r
-#define ISO2022JP_CS_JISX0212 5\r
-\r
-static iso2022_esc_t iso2022jp_esc[] = {\r
- {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},\r
- {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},\r
- {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},\r
- {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */\r
- {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},\r
- {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},\r
- {NULL, 0, 0, 0}\r
-};\r
-\r
-static int\r
-iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- char tmp[MB_CHAR_MAX];\r
- int insize;\r
- HRESULT hr;\r
- DWORD dummy = 0;\r
- int len;\r
- int esc_len;\r
- int cs;\r
- int shift;\r
- int i;\r
-\r
- if (buf[0] == 0x1B)\r
- {\r
- for (i = 0; iesc[i].esc != NULL; ++i)\r
- {\r
- esc_len = iesc[i].esc_len;\r
- if (bufsize < esc_len)\r
- {\r
- if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)\r
- return seterror(EINVAL);\r
- }\r
- else\r
- {\r
- if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)\r
- {\r
- cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);\r
- *wbufsize = 0;\r
- return esc_len;\r
- }\r
- }\r
- }\r
- /* not supported escape sequence */\r
- return seterror(EILSEQ);\r
- }\r
- else if (buf[0] == iso2022_SO_seq[0])\r
- {\r
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);\r
- *wbufsize = 0;\r
- return 1;\r
- }\r
- else if (buf[0] == iso2022_SI_seq[0])\r
- {\r
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);\r
- *wbufsize = 0;\r
- return 1;\r
- }\r
-\r
- cs = ISO2022_MODE_CS(cv->mode);\r
- shift = ISO2022_MODE_SHIFT(cv->mode);\r
-\r
- /* reset the mode for informal sequence */\r
- if (buf[0] < 0x20)\r
- {\r
- cs = ISO2022JP_CS_ASCII;\r
- shift = ISO2022_SI;\r
- }\r
-\r
- len = iesc[cs].len;\r
- if (bufsize < len)\r
- return seterror(EINVAL);\r
- for (i = 0; i < len; ++i)\r
- if (!(buf[i] < 0x80))\r
- return seterror(EILSEQ);\r
- esc_len = iesc[cs].esc_len;\r
- memcpy(tmp, iesc[cs].esc, esc_len);\r
- if (shift == ISO2022_SO)\r
- {\r
- memcpy(tmp + esc_len, iso2022_SO_seq, 1);\r
- esc_len += 1;\r
- }\r
- memcpy(tmp + esc_len, buf, len);\r
-\r
- if ((cv->codepage == 50220 || cv->codepage == 50221\r
- || cv->codepage == 50222) && shift == ISO2022_SO)\r
- {\r
- /* XXX: shift-out cannot be used for mbtowc (both kernel and\r
- * mlang) */\r
- esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;\r
- memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);\r
- memcpy(tmp + esc_len, buf, len);\r
- }\r
-\r
- insize = len + esc_len;\r
- hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,\r
- (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);\r
- if (hr != S_OK || insize != len + esc_len)\r
- return seterror(EILSEQ);\r
-\r
- /* Check for conversion error. Assuming defaultChar is 0x3F. */\r
- /* ascii should be converted from ascii */\r
- if (wbuf[0] == buf[0]\r
- && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
- return seterror(EILSEQ);\r
-\r
- /* reset the mode for informal sequence */\r
- if (cv->mode != ISO2022_MODE(cs, shift))\r
- cv->mode = ISO2022_MODE(cs, shift);\r
-\r
- return len;\r
-}\r
-\r
-static int\r
-iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- char tmp[MB_CHAR_MAX];\r
- int tmpsize = MB_CHAR_MAX;\r
- int insize = wbufsize;\r
- HRESULT hr;\r
- DWORD dummy = 0;\r
- int len;\r
- int esc_len;\r
- int cs;\r
- int shift;\r
- int i;\r
-\r
- /*\r
- * MultiByte = [escape sequence] + character + [escape sequence]\r
- *\r
- * Whether trailing escape sequence is added depends on which API is\r
- * used (kernel or MLang, and its version).\r
- */\r
- hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,\r
- (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);\r
- if (hr != S_OK || insize != wbufsize)\r
- return seterror(EILSEQ);\r
- else if (bufsize < tmpsize)\r
- return seterror(E2BIG);\r
-\r
- if (tmpsize == 1)\r
- {\r
- cs = ISO2022JP_CS_ASCII;\r
- esc_len = 0;\r
- }\r
- else\r
- {\r
- for (i = 1; iesc[i].esc != NULL; ++i)\r
- {\r
- esc_len = iesc[i].esc_len;\r
- if (strncmp(tmp, iesc[i].esc, esc_len) == 0)\r
- {\r
- cs = iesc[i].cs;\r
- break;\r
- }\r
- }\r
- if (iesc[i].esc == NULL)\r
- /* not supported escape sequence */\r
- return seterror(EILSEQ);\r
- }\r
-\r
- shift = ISO2022_SI;\r
- if (tmp[esc_len] == iso2022_SO_seq[0])\r
- {\r
- shift = ISO2022_SO;\r
- esc_len += 1;\r
- }\r
-\r
- len = iesc[cs].len;\r
-\r
- /* Check for converting error. Assuming defaultChar is 0x3F. */\r
- /* ascii should be converted from ascii */\r
- if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))\r
- return seterror(EILSEQ);\r
- else if (tmpsize < esc_len + len)\r
- return seterror(EILSEQ);\r
-\r
- if (cv->mode == ISO2022_MODE(cs, shift))\r
- {\r
- /* remove escape sequence */\r
- if (esc_len != 0)\r
- memmove(tmp, tmp + esc_len, len);\r
- esc_len = 0;\r
- }\r
- else\r
- {\r
- if (cs == ISO2022JP_CS_ASCII)\r
- {\r
- esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;\r
- memmove(tmp + esc_len, tmp, len);\r
- memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);\r
- }\r
- if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)\r
- {\r
- /* shift-in before changing to other mode */\r
- memmove(tmp + 1, tmp, len + esc_len);\r
- memcpy(tmp, iso2022_SI_seq, 1);\r
- esc_len += 1;\r
- }\r
- }\r
-\r
- if (bufsize < len + esc_len)\r
- return seterror(E2BIG);\r
- memcpy(buf, tmp, len + esc_len);\r
- cv->mode = ISO2022_MODE(cs, shift);\r
- return len + esc_len;\r
-}\r
-\r
-static int\r
-iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)\r
-{\r
- iso2022_esc_t *iesc = iso2022jp_esc;\r
- int esc_len;\r
-\r
- if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))\r
- {\r
- esc_len = 0;\r
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
- esc_len += 1;\r
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
- if (bufsize < esc_len)\r
- return seterror(E2BIG);\r
-\r
- esc_len = 0;\r
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)\r
- {\r
- memcpy(buf, iso2022_SI_seq, 1);\r
- esc_len += 1;\r
- }\r
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)\r
- {\r
- memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,\r
- iesc[ISO2022JP_CS_ASCII].esc_len);\r
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;\r
- }\r
- return esc_len;\r
- }\r
- return 0;\r
-}\r
-\r
-#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL)\r
-BOOL WINAPI\r
-DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)\r
-{\r
- switch( fdwReason )\r
- {\r
- case DLL_PROCESS_ATTACH:\r
- hwiniconv = (HMODULE)hinstDLL;\r
- break;\r
- case DLL_THREAD_ATTACH:\r
- case DLL_THREAD_DETACH:\r
- case DLL_PROCESS_DETACH:\r
- break;\r
- }\r
- return TRUE;\r
-}\r
-#endif\r
-\r
-#if defined(MAKE_EXE)\r
-#include <stdio.h>\r
-#include <fcntl.h>\r
-#include <io.h>\r
-int\r
-main(int argc, char **argv)\r
-{\r
- char *fromcode = NULL;\r
- char *tocode = NULL;\r
- int i;\r
- char inbuf[BUFSIZ];\r
- char outbuf[BUFSIZ];\r
- const char *pin;\r
- char *pout;\r
- size_t inbytesleft;\r
- size_t outbytesleft;\r
- size_t rest = 0;\r
- iconv_t cd;\r
- size_t r;\r
- FILE *in = stdin;\r
-\r
- _setmode(_fileno(stdin), _O_BINARY);\r
- _setmode(_fileno(stdout), _O_BINARY);\r
-\r
- for (i = 1; i < argc; ++i)\r
- {\r
- if (strcmp(argv[i], "-l") == 0)\r
- {\r
- for (i = 0; codepage_alias[i].name != NULL; ++i)\r
- printf("%s\n", codepage_alias[i].name);\r
- return 0;\r
- }\r
-\r
- if (strcmp(argv[i], "-f") == 0)\r
- fromcode = argv[++i];\r
- else if (strcmp(argv[i], "-t") == 0)\r
- tocode = argv[++i];\r
- else\r
- {\r
- in = fopen(argv[i], "rb");\r
- if (in == NULL)\r
- {\r
- fprintf(stderr, "cannot open %s\n", argv[i]);\r
- return 1;\r
- }\r
- break;\r
- }\r
- }\r
-\r
- if (fromcode == NULL || tocode == NULL)\r
- {\r
- printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]);\r
- return 0;\r
- }\r
-\r
- cd = iconv_open(tocode, fromcode);\r
- if (cd == (iconv_t)(-1))\r
- {\r
- perror("iconv_open error");\r
- return 1;\r
- }\r
-\r
- while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0\r
- || rest != 0)\r
- {\r
- inbytesleft += rest;\r
- pin = inbuf;\r
- pout = outbuf;\r
- outbytesleft = sizeof(outbuf);\r
- r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);\r
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
- if (r == (size_t)(-1) && errno != E2BIG && (errno != EINVAL || feof(in)))\r
- {\r
- perror("conversion error");\r
- return 1;\r
- }\r
- memmove(inbuf, pin, inbytesleft);\r
- rest = inbytesleft;\r
- }\r
- pout = outbuf;\r
- outbytesleft = sizeof(outbuf);\r
- r = iconv(cd, NULL, NULL, &pout, &outbytesleft);\r
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);\r
- if (r == (size_t)(-1))\r
- {\r
- perror("conversion error");\r
- return 1;\r
- }\r
-\r
- iconv_close(cd);\r
-\r
- return 0;\r
-}\r
-#endif\r
-\r
+/*
+ * 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 <windows.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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 <stdio.h>
+#include <fcntl.h>
+#include <io.h>
+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
+
-\r
-#include "win_iconv.c"\r
-#include <stdio.h>\r
-\r
-const char *\r
-tohex(const char *str, int size)\r
-{\r
- static char buf[BUFSIZ];\r
- char *pbuf = buf;\r
- int i;\r
- buf[0] = 0;\r
- for (i = 0; i < size; ++i)\r
- pbuf += sprintf(pbuf, "%02X", str[i] & 0xFF);\r
- return buf;\r
-}\r
-\r
-const char *\r
-errstr(int errcode)\r
-{\r
- static char buf[BUFSIZ];\r
- switch (errcode)\r
- {\r
- case 0: return "NOERROR";\r
- case EINVAL: return "EINVAL";\r
- case EILSEQ: return "EILSEQ";\r
- case E2BIG: return "E2BIG";\r
- }\r
- sprintf(buf, "%d\n", errcode);\r
- return buf;\r
-}\r
-\r
-#ifdef USE_LIBICONV_DLL\r
-int use_dll;\r
-\r
-int\r
-setdll(const char *dllpath)\r
-{\r
- char buf[BUFSIZ];\r
- rec_iconv_t cd;\r
-\r
- sprintf(buf, "WINICONV_LIBICONV_DLL=%s", dllpath);\r
- putenv(buf);\r
- if (libiconv_iconv_open(&cd, "ascii", "ascii"))\r
- {\r
- FreeLibrary(cd.hlibiconv);\r
- use_dll = TRUE;\r
- return TRUE;\r
- }\r
- use_dll = FALSE;\r
- return FALSE;\r
-}\r
-#endif\r
-\r
-/*\r
- * We can test the codepage that is installed in the system.\r
- */\r
-int\r
-check_enc(const char *encname, int codepage)\r
-{\r
- iconv_t cd;\r
- int cp;\r
- cd = iconv_open("utf-8", encname);\r
- if (cd == (iconv_t)(-1))\r
- {\r
- printf("%s(%d) IS NOT SUPPORTED: SKIP THE TEST\n", encname, codepage);\r
- return FALSE;\r
- }\r
- cp = ((rec_iconv_t *)cd)->from.codepage;\r
- if (cp != codepage)\r
- {\r
- printf("%s(%d) ALIAS IS MAPPED TO DIFFERENT CODEPAGE (%d)\n", encname, codepage, cp);\r
- exit(1);\r
- }\r
- iconv_close(cd);\r
- return TRUE;\r
-}\r
-\r
-int use_dll;\r
-\r
-void\r
-test(const char *from, const char *fromstr, int fromsize, const char *to, const char *tostr, int tosize, int errcode, int bufsize, int line)\r
-{\r
- char outbuf[BUFSIZ];\r
- const char *pin;\r
- char *pout;\r
- size_t inbytesleft;\r
- size_t outbytesleft;\r
- iconv_t cd;\r
- size_t r;\r
- char dllpath[_MAX_PATH];\r
-\r
- cd = iconv_open(to, from);\r
- if (cd == (iconv_t)(-1))\r
- {\r
- printf("%s -> %s: NG: INVALID ENCODING NAME: line=%d\n", from, to, line);\r
- exit(1);\r
- }\r
-\r
-#ifdef USE_LIBICONV_DLL\r
- if (((rec_iconv_t *)cd)->hlibiconv != NULL)\r
- GetModuleFileName(((rec_iconv_t *)cd)->hlibiconv, dllpath, sizeof(dllpath));\r
-\r
- if (use_dll && ((rec_iconv_t *)cd)->hlibiconv == NULL)\r
- {\r
- printf("%s: %s -> %s: NG: FAILED TO USE DLL: line=%d\n", dllpath, from, to, line);\r
- exit(1);\r
- }\r
- else if (!use_dll && ((rec_iconv_t *)cd)->hlibiconv != NULL)\r
- {\r
- printf("%s: %s -> %s: NG: DLL IS LOADED UNEXPECTEDLY: line=%d\n", dllpath, from, to, line);\r
- exit(1);\r
- }\r
-#endif\r
-\r
- errno = 0;\r
-\r
- pin = fromstr;\r
- pout = outbuf;\r
- inbytesleft = fromsize;\r
- outbytesleft = bufsize;\r
- r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);\r
- if (r != (size_t)(-1))\r
- r = iconv(cd, NULL, NULL, &pout, &outbytesleft);\r
- *pout = 0;\r
-\r
-#ifdef USE_LIBICONV_DLL\r
- if (use_dll)\r
- printf("%s: ", dllpath);\r
-#endif\r
- printf("%s(%s) -> ", from, tohex(fromstr, fromsize));\r
- printf("%s(%s%s%s): ", to, tohex(tostr, tosize),\r
- errcode == 0 ? "" : ":",\r
- errcode == 0 ? "" : errstr(errcode));\r
- if (strcmp(outbuf, tostr) == 0 && errno == errcode)\r
- printf("OK\n");\r
- else\r
- {\r
- printf("RESULT(%s:%s): ", tohex(outbuf, sizeof(outbuf) - outbytesleft),\r
- errstr(errno));\r
- printf("NG: line=%d\n", line);\r
- exit(1);\r
- }\r
-}\r
-\r
-#define STATIC_STRLEN(arr) (sizeof(arr) - 1)\r
-\r
-#define success(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), 0, BUFSIZ, __LINE__)\r
-#define einval(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EINVAL, BUFSIZ, __LINE__)\r
-#define eilseq(from, fromstr, to, tostr) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), EILSEQ, BUFSIZ, __LINE__)\r
-#define e2big(from, fromstr, to, tostr, bufsize) test(from, fromstr, STATIC_STRLEN(fromstr), to, tostr, STATIC_STRLEN(tostr), E2BIG, bufsize, __LINE__)\r
-\r
-int\r
-main(int argc, char **argv)\r
-{\r
-#ifdef USE_LIBICONV_DLL\r
- /* test use of dll if $DEFAULT_LIBICONV_DLL was defined. */\r
- if (setdll(""))\r
- {\r
- success("ascii", "ABC", "ascii", "ABC");\r
- success("ascii", "ABC", "utf-16be", "\x00\x41\x00\x42\x00\x43");\r
- }\r
- else\r
- {\r
- printf("\nDLL TEST IS SKIPPED\n\n");\r
- }\r
-\r
- setdll("none");\r
-#endif\r
-\r
- if (check_enc("ascii", 20127))\r
- {\r
- success("ascii", "ABC", "ascii", "ABC");\r
- /* MSB is dropped. Hmm... */\r
- success("ascii", "\x80\xFF", "ascii", "\x00\x7F");\r
- }\r
-\r
- /* unicode (CP1200 CP1201 CP12000 CP12001 CP65001) */\r
- if (check_enc("utf-8", 65001)\r
- && check_enc("utf-16be", 1201) && check_enc("utf-16le", 1200)\r
- && check_enc("utf-32be", 12001) && check_enc("utf-32le", 12000)\r
- )\r
- {\r
- /* Test the BOM behavior\r
- * 1. Remove the BOM when "fromcode" is utf-16 or utf-32.\r
- * 2. Add the BOM when "tocode" is utf-16 or utf-32. */\r
- success("utf-16", "\xFE\xFF\x01\x02", "utf-16be", "\x01\x02");\r
- success("utf-16", "\xFF\xFE\x02\x01", "utf-16be", "\x01\x02");\r
- success("utf-32", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\x01\x02");\r
- success("utf-32", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\x01\x02");\r
- success("utf-16", "\xFE\xFF\x00\x01", "utf-8", "\x01");\r
-#ifndef GLIB_COMPILATION\r
- success("utf-8", "\x01", "utf-16", "\xFE\xFF\x00\x01");\r
- success("utf-8", "\x01", "utf-32", "\x00\x00\xFE\xFF\x00\x00\x00\x01");\r
-#else\r
- success("utf-8", "\x01", "utf-16", "\xFF\xFE\x01\x00");\r
- success("utf-8", "\x01", "utf-32", "\xFF\xFE\x00\x00\x01\x00\x00\x00");\r
-#endif\r
-\r
- success("utf-16be", "\xFE\xFF\x01\x02", "utf-16be", "\xFE\xFF\x01\x02");\r
- success("utf-16le", "\xFF\xFE\x02\x01", "utf-16be", "\xFE\xFF\x01\x02");\r
- success("utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02");\r
- success("utf-32le", "\xFF\xFE\x00\x00\x02\x01\x00\x00", "utf-32be", "\x00\x00\xFE\xFF\x00\x00\x01\x02");\r
- success("utf-16be", "\xFE\xFF\x00\x01", "utf-8", "\xEF\xBB\xBF\x01");\r
- success("utf-8", "\xEF\xBB\xBF\x01", "utf-8", "\xEF\xBB\xBF\x01");\r
-\r
- success("utf-16be", "\x01\x02", "utf-16le", "\x02\x01");\r
- success("utf-16le", "\x02\x01", "utf-16be", "\x01\x02");\r
- success("utf-16be", "\xFE\xFF", "utf-16le", "\xFF\xFE");\r
- success("utf-16le", "\xFF\xFE", "utf-16be", "\xFE\xFF");\r
- success("utf-32be", "\x00\x00\x03\x04", "utf-32le", "\x04\x03\x00\x00");\r
- success("utf-32le", "\x04\x03\x00\x00", "utf-32be", "\x00\x00\x03\x04");\r
- success("utf-32be", "\x00\x00\xFF\xFF", "utf-16be", "\xFF\xFF");\r
- success("utf-16be", "\xFF\xFF", "utf-32be", "\x00\x00\xFF\xFF");\r
- success("utf-32be", "\x00\x01\x00\x00", "utf-16be", "\xD8\x00\xDC\x00");\r
- success("utf-16be", "\xD8\x00\xDC\x00", "utf-32be", "\x00\x01\x00\x00");\r
- success("utf-32be", "\x00\x10\xFF\xFF", "utf-16be", "\xDB\xFF\xDF\xFF");\r
- success("utf-16be", "\xDB\xFF\xDF\xFF", "utf-32be", "\x00\x10\xFF\xFF");\r
- eilseq("utf-32be", "\x00\x11\x00\x00", "utf-16be", "");\r
- eilseq("utf-16be", "\xDB\xFF\xE0\x00", "utf-32be", "");\r
- success("utf-8", "\xE3\x81\x82", "utf-16be", "\x30\x42");\r
- einval("utf-8", "\xE3", "utf-16be", "");\r
- }\r
-\r
- /* Japanese (CP932 CP20932 CP50220 CP50221 CP50222 CP51932) */\r
- if (check_enc("cp932", 932)\r
- && check_enc("cp20932", 20932) && check_enc("euc-jp", 51932)\r
- && check_enc("cp50220", 50220) && check_enc("cp50221", 50221)\r
- && check_enc("cp50222", 50222) && check_enc("iso-2022-jp", 50221))\r
- {\r
- /* Test the compatibility for each other Japanese codepage.\r
- * And validate the escape sequence handling for iso-2022-jp. */\r
- success("utf-16be", "\xFF\x5E", "cp932", "\x81\x60");\r
- success("utf-16be", "\x30\x1C", "cp932", "\x81\x60");\r
- success("utf-16be", "\xFF\x5E", "cp932//nocompat", "\x81\x60");\r
- eilseq("utf-16be", "\x30\x1C", "cp932//nocompat", "");\r
- success("euc-jp", "\xA4\xA2", "utf-16be", "\x30\x42");\r
- einval("euc-jp", "\xA4\xA2\xA4", "utf-16be", "\x30\x42");\r
- eilseq("euc-jp", "\xA4\xA2\xFF\xFF", "utf-16be", "\x30\x42");\r
- success("cp932", "\x81\x60", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42");\r
- success("UTF-16BE", "\xFF\x5E", "iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42");\r
- eilseq("UTF-16BE", "\x30\x1C", "iso-2022-jp//nocompat", "");\r
- success("UTF-16BE", "\x30\x42\x30\x44", "iso-2022-jp", "\x1B\x24\x42\x24\x22\x24\x24\x1B\x28\x42");\r
- success("iso-2022-jp", "\x1B\x24\x42\x21\x41\x1B\x28\x42", "UTF-16BE", "\xFF\x5E");\r
- }\r
-\r
- /*\r
- * test for //translit\r
- * U+FF41 (FULLWIDTH LATIN SMALL LETTER A) <-> U+0062 (LATIN SMALL LETTER A)\r
- */\r
- eilseq("UTF-16BE", "\xFF\x41", "iso-8859-1", "");\r
- success("UTF-16BE", "\xFF\x41", "iso-8859-1//translit", "a");\r
-\r
- /*\r
- * TODO:\r
- * Test for state after iconv() failed.\r
- * Ensure iconv() error is safe and continuable.\r
- */\r
-\r
- return 0;\r
-}\r
-\r
+
+#include "win_iconv.c"
+#include <stdio.h>
+
+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;
+}
+
-/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library /\r
- * Soft-Switch Application\r
- *\r
- * Version: MPL 1.1\r
- *\r
- * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)\r
- * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version\r
- * 1.1 (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * http://www.mozilla.org/MPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS IS" basis,\r
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r
- * for the specific language governing rights and limitations under the\r
- * License.\r
- *\r
- * Contributor(s):\r
- * Tuyan Ozipek (tuyanozipek@gmail.com)\r
- * Lukasz Zwierko (lzwierko@gmail.com)\r
- * Robert Jongbloed (robertj@voxlucida.com.au)\r
- *\r
- */\r
-\r
-#include "mod_opal.h"\r
-#include <opal/patch.h>\r
-#include <rtp/rtp.h>\r
-#if PTLIB_CHECK_VERSION(2,11,1)\r
-#include <rtp/rtp_session.h>\r
-#endif\r
-#include <h323/h323pdu.h>\r
-#include <h323/gkclient.h>\r
-\r
-\r
-/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a\r
- MAXIMUM audio frames per packet is negotiated, and there is no\r
- requirement for the remote to actually send that many. So, in say GSM, we\r
- negotiate up to 3 frames or 60ms of data and the remote actually sends one\r
- (20ms) frame per packet. Perfectly legal but blows up the media handling\r
- in FS.\r
-\r
- Eventually we will get around to bundling the packets, but not yet. This\r
- compile flag will just force one frame/packet for all audio codecs.\r
- */\r
-#define IMPLEMENT_MULTI_FAME_AUDIO 0\r
-\r
-\r
-static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,\r
- switch_event_t *var_event,\r
- switch_caller_profile_t *outbound_profile,\r
- switch_core_session_t **new_session,\r
- switch_memory_pool_t **pool,\r
- switch_originate_flag_t flags,\r
- switch_call_cause_t *cancel_cause);\r
-\r
-\r
-static FSProcess *opal_process = NULL;\r
-\r
-\r
-static PConstString const ModuleName("opal");\r
-static char const ConfigFile[] = "opal.conf";\r
-#define FS_PREFIX "fs"\r
-\r
-\r
-static switch_io_routines_t opalfs_io_routines = {\r
- /*.outgoing_channel */ create_outgoing_channel,\r
- /*.read_frame */ FSConnection::read_audio_frame,\r
- /*.write_frame */ FSConnection::write_audio_frame,\r
- /*.kill_channel */ FSConnection::kill_channel,\r
- /*.send_dtmf */ FSConnection::send_dtmf,\r
- /*.receive_message */ FSConnection::receive_message,\r
- /*.receive_event */ FSConnection::receive_event,\r
- /*.state_change */ FSConnection::state_change,\r
- /*.read_video_frame */ FSConnection::read_video_frame,\r
- /*.write_video_frame */ FSConnection::write_video_frame\r
-};\r
-\r
-static switch_state_handler_table_t opalfs_event_handlers = {\r
- /*.on_init */ FSConnection::on_init,\r
- /*.on_routing */ FSConnection::on_routing,\r
- /*.on_execute */ FSConnection::on_execute,\r
- /*.on_hangup */ FSConnection::on_hangup,\r
- /*.on_exchange_media */ FSConnection::on_exchange_media,\r
- /*.on_soft_execute */ FSConnection::on_soft_execute,\r
- /*.on_consume_media*/ NULL,\r
- /*.on_hibernate*/ NULL,\r
- /*.on_reset*/ NULL,\r
- /*.on_park*/ NULL,\r
- /*.on_reporting*/ NULL,\r
- /*.on_destroy*/ FSConnection::on_destroy\r
-};\r
-\r
-\r
-SWITCH_BEGIN_EXTERN_C\r
-/*******************************************************************************/\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load);\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown);\r
-SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL);\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load)\r
-{\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n");\r
-\r
- /* Prevent the loading of OPAL codecs via "plug ins", this is a directory\r
- full of DLLs that will be loaded automatically. */\r
- (void)putenv((char *)"PTLIBPLUGINDIR=/no/thanks");\r
-\r
-\r
- *module_interface = switch_loadable_module_create_module_interface(pool, modname);\r
- if (!*module_interface) {\r
- return SWITCH_STATUS_MEMERR;\r
- }\r
-\r
- opal_process = new FSProcess();\r
- if (opal_process == NULL) {\r
- return SWITCH_STATUS_MEMERR;\r
- }\r
-\r
- if (opal_process->Initialise(*module_interface)) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n");\r
- //unloading causes a seg in linux\r
- //return SWITCH_STATUS_UNLOAD;\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- delete opal_process;\r
- opal_process = NULL;\r
- return SWITCH_STATUS_FALSE;\r
-}\r
-\r
-\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown)\r
-{\r
- delete opal_process;\r
- opal_process = NULL;\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-SWITCH_END_EXTERN_C\r
-/*******************************************************************************/\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-#if PTRACING\r
-\r
-class FSTrace : public std::ostream\r
-{\r
-private:\r
- class Buffer : public std::stringbuf\r
- {\r
- virtual int sync()\r
- {\r
- std::string s = str();\r
- if (s.empty())\r
- return 0;\r
-\r
- //Due to explicit setting of flags we know exactly what we are getting\r
- #define THREAD_ID_INDEX 2\r
- #define FILE_NAME_INDEX 3\r
- #define FILE_LINE_INDEX 4\r
-#if PTLIB_CHECK_VERSION(2,11,1)\r
- #define CONTEXT_ID_REGEX "([0-9]+|- - - - - - -)\t"\r
- #define LOG_PRINTF_FORMAT "{%s,%s} %s"\r
- #define FULL_TEXT_INDEX 6\r
-#else\r
- #define CONTEXT_ID_REGEX\r
- #define LOG_PRINTF_FORMAT "{%s} %s"\r
- #define FULL_TEXT_INDEX 5\r
-#endif\r
- PStringArray fields(7);\r
- static PRegularExpression logRE("^([0-9]+)\t *(.+)\t *([^(]+)\\(([0-9]+)\\)\t"CONTEXT_ID_REGEX"(.*)",\r
- PRegularExpression::Extended);\r
- if (!logRE.Execute(s.c_str(), fields)) {\r
- fields[1] = "4";\r
- fields[THREAD_ID_INDEX] = "unknown";\r
- fields[FILE_NAME_INDEX] = __FILE__;\r
- fields[FILE_LINE_INDEX] = __LINE__;\r
- fields[FULL_TEXT_INDEX] = s;\r
- }\r
-\r
- switch_log_level_t level;\r
- switch (fields[1].AsUnsigned()) {\r
- case 0 :\r
- level = SWITCH_LOG_ALERT;\r
- break;\r
- case 1 :\r
- level = SWITCH_LOG_ERROR;\r
- break;\r
- case 2 :\r
- level = SWITCH_LOG_WARNING;\r
- break;\r
- case 3 :\r
- level = SWITCH_LOG_INFO;\r
- break;\r
- default :\r
- level = SWITCH_LOG_DEBUG;\r
- break;\r
- }\r
-\r
- fields[4].Replace("\t", " ", true);\r
-#if PTLIB_CHECK_VERSION(2,11,1)\r
- fields[5].Replace("- - - - - - -", "-"),\r
-#endif\r
- switch_log_printf(SWITCH_CHANNEL_ID_LOG,\r
- fields[FILE_NAME_INDEX],\r
- "PTLib-OPAL",\r
- fields[FILE_LINE_INDEX].AsUnsigned(),\r
- NULL,\r
- level,\r
- LOG_PRINTF_FORMAT,\r
- fields[THREAD_ID_INDEX].GetPointer(),\r
-#if PTLIB_CHECK_VERSION(2,11,1)\r
- fields[5].GetPointer(),\r
-#endif\r
- fields[FULL_TEXT_INDEX].GetPointer());\r
-\r
- // Reset string\r
- str(std::string());\r
- return 0;\r
- }\r
- } buffer;\r
-\r
-public:\r
- FSTrace()\r
- : ostream(&buffer)\r
- {\r
- }\r
-};\r
-\r
-#endif // PTRACING\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-FSProcess::FSProcess()\r
- : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1)\r
- , m_manager(NULL)\r
-{\r
-}\r
-\r
-\r
-FSProcess::~FSProcess()\r
-{\r
- delete m_manager;\r
-#if PTRACING\r
- PTrace::SetStream(NULL); // This will delete the FSTrace object\r
-#endif\r
-}\r
-\r
-\r
-bool FSProcess::Initialise(switch_loadable_module_interface_t *iface)\r
-{\r
- m_manager = new FSManager();\r
- return m_manager != NULL && m_manager->Initialise(iface);\r
-}\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-FSManager::FSManager()\r
- : m_context("default")\r
- , m_dialplan("XML")\r
-{\r
- // These are deleted by the OpalManager class, no need to have destructor\r
- m_h323ep = new H323EndPoint(*this);\r
- m_iaxep = new IAX2EndPoint(*this);\r
- m_fsep = new FSEndPoint(*this);\r
-}\r
-\r
-\r
-bool FSManager::Initialise(switch_loadable_module_interface_t *iface)\r
-{\r
- ReadConfig(false);\r
-\r
- m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE);\r
- m_FreeSwitch->interface_name = ModuleName;\r
- m_FreeSwitch->io_routines = &opalfs_io_routines;\r
- m_FreeSwitch->state_handler = &opalfs_event_handlers;\r
-\r
- silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection;\r
-\r
- if (m_listeners.empty()) {\r
- m_h323ep->StartListener("");\r
- } else {\r
- for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) {\r
- if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) {\r
- PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name);\r
- }\r
- }\r
- }\r
-\r
- AddRouteEntry("h323:.* = "FS_PREFIX":<da>"); // config option for direct routing\r
- AddRouteEntry("iax2:.* = "FS_PREFIX":<da>"); // config option for direct routing\r
- AddRouteEntry(FS_PREFIX":.* = h323:<da>"); // config option for direct routing\r
-\r
- // Make sure all known codecs are instantiated,\r
- // these are ones we know how to translate into H.323 capabilities\r
- GetOpalG728();\r
- GetOpalG729();\r
- GetOpalG729A();\r
- GetOpalG729B();\r
- GetOpalG729AB();\r
- GetOpalG7231_6k3();\r
- GetOpalG7231_5k3();\r
- GetOpalG7231A_6k3();\r
- GetOpalG7231A_5k3();\r
- GetOpalGSM0610();\r
- GetOpalGSMAMR();\r
- GetOpaliLBC();\r
-\r
-#if !IMPLEMENT_MULTI_FAME_AUDIO\r
- OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats();\r
- for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) {\r
- if (it->GetMediaType() == OpalMediaType::Audio()) {\r
- int ms_per_frame = it->GetFrameTime()/it->GetTimeUnits();\r
- int frames_in_20_ms = (ms_per_frame+19)/ms_per_frame;\r
- it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), frames_in_20_ms);\r
- it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), frames_in_20_ms);\r
- OpalMediaFormat::SetRegisteredMediaFormat(*it);\r
- PTRACE(4, "mod_opal\tSet " << *it << " to " << frames_in_20_ms << "frames/packet");\r
- }\r
- }\r
-#endif // IMPLEMENT_MULTI_FAME_AUDIO\r
-\r
- OpalMediaFormat t38 = OpalT38;\r
- t38.SetOptionBoolean("UDPTL-Raw-Mode", true);\r
- OpalMediaFormat::SetRegisteredMediaFormat(t38);\r
-\r
- if (!m_gkAddress.IsEmpty()) {\r
- if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface))\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n",\r
- (const char *)m_h323ep->GetGatekeeper()->GetName());\r
- else\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,\r
- "Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n",\r
- (const char *)m_gkAddress,\r
- (const char *)m_gkIdentifer,\r
- (const char *)m_gkInterface);\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-\r
-switch_status_t FSManager::ReadConfig(int reload)\r
-{\r
- switch_event_t *request_params = NULL;\r
- switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS);\r
- switch_assert(request_params);\r
- switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil(""));\r
-\r
- switch_xml_t cfg;\r
- switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params);\r
- if (xml == NULL) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile);\r
- return SWITCH_STATUS_FALSE;\r
- }\r
-\r
- switch_xml_t xmlSettings = switch_xml_child(cfg, "settings");\r
- if (xmlSettings) {\r
- for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {\r
- PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));\r
- PConstString const val(switch_xml_attr_soft(xmlParam, "value"));\r
-\r
- if (var == "context") {\r
- m_context = val;\r
- } else if (var == "dialplan") {\r
- m_dialplan = val;\r
- } else if (var == "codec-prefs") {\r
- m_codecPrefs = val;\r
- } else if (var == "disable-transcoding") {\r
- m_disableTranscoding = switch_true(val);\r
- } else if (var == "dtmf-type") {\r
- if (val == "string")\r
- m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsString);\r
- else if (val == "signal")\r
- m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsTone);\r
- else if (val == "rfc2833")\r
- m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputAsRFC2833);\r
- else if (val == "in-band")\r
- m_h323ep->SetSendUserInputMode(OpalConnection::SendUserInputInBand);\r
- } else if (var == "jitter-size") {\r
- SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds\r
- } else if (var == "gk-address") {\r
- m_gkAddress = val;\r
- } else if (var == "gk-identifer") {\r
- m_gkIdentifer = val;\r
- } else if (var == "gk-interface") {\r
- m_gkInterface = val;\r
-#if PTRACING\r
- } else if (var == "trace-level") {\r
- unsigned level = val.AsUnsigned();\r
- if (level > 0) {\r
- PTrace::SetLevel(level);\r
- PTrace::ClearOptions(0xffffffff); // Everything off\r
- PTrace::SetOptions( // Except these\r
- PTrace::TraceLevel|PTrace::FileAndLine|PTrace::Thread\r
-#if PTLIB_CHECK_VERSION(2,11,1)\r
- |PTrace::ContextIdentifier\r
-#endif\r
- );\r
- PTrace::SetStream(new FSTrace);\r
- }\r
-#endif\r
- }\r
- }\r
- }\r
-\r
- switch_xml_t xmlListeners = switch_xml_child(cfg, "listeners");\r
- if (xmlListeners != NULL) {\r
- for (switch_xml_t xmlListener = switch_xml_child(xmlListeners, "listener"); xmlListener != NULL; xmlListener = xmlListener->next) {\r
-\r
- m_listeners.push_back(FSListener());\r
- FSListener & listener = m_listeners.back();\r
-\r
- listener.m_name = switch_xml_attr_soft(xmlListener, "name");\r
- if (listener.m_name.IsEmpty())\r
- listener.m_name = "unnamed";\r
-\r
- for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {\r
- PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));\r
- PConstString const val(switch_xml_attr_soft(xmlParam, "value"));\r
- if (var == "h323-ip")\r
- listener.m_address = val;\r
- else if (var == "h323-port")\r
- listener.m_port = (uint16_t)val.AsUnsigned();\r
- }\r
-\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name);\r
- }\r
- }\r
-\r
- switch_event_destroy(&request_params);\r
-\r
- if (xml)\r
- switch_xml_free(xml);\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,\r
- switch_event_t *var_event,\r
- switch_caller_profile_t *outbound_profile,\r
- switch_core_session_t **new_session,\r
- switch_memory_pool_t **pool,\r
- switch_originate_flag_t flags,\r
- switch_call_cause_t *cancel_cause)\r
-{\r
- if (opal_process == NULL)\r
- return SWITCH_CAUSE_CRASH;\r
-\r
- FSConnection::outgoing_params params;\r
- params.var_event = var_event;\r
- params.outbound_profile = outbound_profile;\r
- params.new_session = new_session;\r
- params.pool = pool;\r
- params.flags = flags;\r
- params.cancel_cause = cancel_cause;\r
- params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;\r
-\r
- if (opal_process->GetManager().SetUpCall(FS_PREFIX":", outbound_profile->destination_number, ¶ms) != NULL)\r
- return SWITCH_CAUSE_SUCCESS;\r
-\r
- if (*new_session != NULL)\r
- switch_core_session_destroy(new_session);\r
- return params.fail_cause;\r
-}\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-FSEndPoint::FSEndPoint(FSManager & manager)\r
- : OpalLocalEndPoint(manager, FS_PREFIX)\r
- , m_manager(manager)\r
-{\r
- PTRACE(4, "mod_opal\tFSEndPoint created.");\r
-}\r
-\r
-\r
-OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions)\r
-{\r
- return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData);\r
-}\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-FSConnection::FSConnection(OpalCall & call,\r
- FSEndPoint & endpoint,\r
- unsigned options,\r
- OpalConnection::StringOptions* stringOptions,\r
- outgoing_params * params)\r
- : OpalLocalConnection(call, endpoint, NULL, options, stringOptions)\r
- , m_endpoint(endpoint)\r
- , m_fsSession(NULL)\r
- , m_fsChannel(NULL)\r
- , m_flushAudio(false)\r
- , m_udptl(false)\r
-{\r
- memset(&m_read_timer, 0, sizeof(m_read_timer));\r
- memset(&m_read_codec, 0, sizeof(m_read_codec));\r
- memset(&m_write_codec, 0, sizeof(m_write_codec));\r
- memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer));\r
- memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec));\r
- memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec));\r
- memset(&m_dummy_frame, 0, sizeof(m_dummy_frame));\r
- m_dummy_frame.flags = SFF_CNG;\r
-\r
- if (params != NULL) {\r
- // If we fail, this is the cause\r
- params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;\r
-\r
- if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),\r
- SWITCH_CALL_DIRECTION_OUTBOUND, params->flags, params->pool)) == NULL) {\r
- PTRACE(1, "mod_opal\tCannot create session for outgoing call.");\r
- return;\r
- }\r
- }\r
- else {\r
- if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),\r
- SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) {\r
- PTRACE(1, "mod_opal\tCannot create session for incoming call.");\r
- return;\r
- }\r
- }\r
-\r
- if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) {\r
- switch_core_session_destroy(&m_fsSession);\r
- return;\r
- }\r
-\r
- switch_core_session_set_private(m_fsSession, this);\r
- SafeReference(); // Make sure cannot be deleted until on_destroy()\r
-\r
- if (params != NULL) {\r
- switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile);\r
- switch_channel_set_caller_profile(m_fsChannel, caller_profile);\r
- SetLocalPartyName(caller_profile->caller_id_number);\r
- SetDisplayName(caller_profile->caller_id_name);\r
-\r
- *params->new_session = m_fsSession;\r
- }\r
-\r
- switch_channel_set_state(m_fsChannel, CS_INIT);\r
-}\r
-\r
-\r
-bool FSConnection::OnOutgoingSetUp()\r
-{\r
- if (m_fsSession == NULL || m_fsChannel == NULL) {\r
- PTRACE(1, "mod_opal\tSession request failed.");\r
- return false;\r
- }\r
-\r
- // Transfer FS caller_id_number & caller_id_name from the FSConnection\r
- // to the protocol connection (e.g. H.323) so gets sent correctly\r
- // in outgoing packets\r
- PSafePtr<OpalConnection> proto = GetOtherPartyConnection();\r
- if (proto == NULL) {\r
- PTRACE(1, "mod_opal\tNo protocol connection in call.");\r
- return false;\r
- }\r
-\r
- proto->SetLocalPartyName(GetLocalPartyName());\r
- proto->SetDisplayName(GetDisplayName());\r
-\r
- switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL());\r
- return true;\r
-}\r
-\r
-\r
-bool FSConnection::OnIncoming()\r
-{\r
- if (m_fsSession == NULL || m_fsChannel == NULL) {\r
- PTRACE(1, "mod_opal\tSession request failed.");\r
- return false;\r
- }\r
-\r
- switch_core_session_add_stream(m_fsSession, NULL);\r
-\r
- PURL url = GetRemotePartyURL();\r
- switch_caller_profile_t *caller_profile = switch_caller_profile_new(\r
- switch_core_session_get_pool(m_fsSession),\r
- url.GetUserName(), /** username */\r
- m_endpoint.GetManager().GetDialPlan(), /** dial plan */\r
- GetRemotePartyName(), /** caller_id_name */\r
- GetRemotePartyNumber(), /** caller_id_number */\r
- url.GetHostName(), /** network addr */\r
- NULL, /** ANI */\r
- NULL, /** ANI II */\r
- NULL, /** RDNIS */\r
- ModuleName, /** source */\r
- m_endpoint.GetManager().GetContext(), /** set context */\r
- GetCalledPartyNumber() /** destination_number */\r
- );\r
- if (caller_profile == NULL) {\r
- PTRACE(1, "mod_opal\tCould not create caller profile");\r
- return false;\r
- }\r
-\r
- PTRACE(4, "mod_opal\tCreated switch caller profile:\n"\r
- " username = " << caller_profile->username << "\n"\r
- " dialplan = " << caller_profile->dialplan << "\n"\r
- " caller_id_name = " << caller_profile->caller_id_name << "\n"\r
- " caller_id_number = " << caller_profile->caller_id_number << "\n"\r
- " network_addr = " << caller_profile->network_addr << "\n"\r
- " source = " << caller_profile->source << "\n"\r
- " context = " << caller_profile->context << "\n"\r
- " destination_number= " << caller_profile->destination_number);\r
- switch_channel_set_caller_profile(m_fsChannel, caller_profile);\r
-\r
- switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number);\r
-\r
- if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) {\r
- PTRACE(1, "mod_opal\tCould not launch session thread");\r
- switch_core_session_destroy(&m_fsSession);\r
- m_fsChannel = NULL;\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-\r
-void FSConnection::OnEstablished()\r
-{\r
- OpalLocalConnection::OnEstablished();\r
-\r
- if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_OUTBOUND) {\r
- PTRACE(4, "mod_opal\tOnEstablished for outbound call, checking for media");\r
- if (GetMediaStream(OpalMediaType::Audio(), true) != NULL && GetMediaStream(OpalMediaType::Audio(), false) != NULL) {\r
- PTRACE(3, "mod_opal\tOnEstablished for outbound call, making call answered");\r
- switch_channel_mark_answered(m_fsChannel);\r
- }\r
- }\r
-}\r
-\r
-\r
-void FSConnection::OnReleased()\r
-{\r
- m_rxAudioOpened.Signal(); // Just in case\r
- m_txAudioOpened.Signal();\r
-\r
- if (m_fsChannel != NULL) {\r
- PTRACE(3, "mod_opal\tHanging up FS side");\r
- switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931);\r
- }\r
-\r
- OpalLocalConnection::OnReleased();\r
-}\r
-\r
-\r
-PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia)\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return false;\r
-\r
- switch_channel_mark_ring_ready(m_fsChannel);\r
- return OpalLocalConnection::SetAlerting(calleeName, withMedia);\r
-}\r
-\r
-\r
-PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration)\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return false;\r
-\r
- switch_dtmf_t dtmf = { tone, duration };\r
- PTRACE(4, "mod_opal\tSending DTMF to FS: tone=" << tone << ", duration=" << duration);\r
- return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-OpalMediaFormatList FSConnection::GetMediaFormats() const\r
-{\r
- if (m_switchMediaFormats.IsEmpty()) {\r
- const_cast<FSConnection *>(this)->SetCodecs();\r
- }\r
-\r
- return m_switchMediaFormats;\r
-}\r
-\r
-\r
-void FSConnection::SetCodecs()\r
-{\r
- int numCodecs = 0;\r
- const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];\r
-\r
- PString codec_string = switch_channel_get_variable(m_fsChannel, "absolute_codec_string");\r
- if (codec_string.IsEmpty()) {\r
- codec_string = switch_channel_get_variable(m_fsChannel, "codec_string");\r
- if (codec_string.IsEmpty()) {\r
- codec_string = m_endpoint.GetManager().GetCodecPrefs();\r
- if (codec_string.IsEmpty()) {\r
- numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));\r
- for (int i = 0; i < numCodecs; i++) {\r
- if (i > 0)\r
- codec_string += ',';\r
- codec_string += codecs[i]->iananame;\r
- }\r
- PTRACE(4, "mod_opal\tDefault to all loaded codecs=" << codec_string);\r
- }\r
- else {\r
- PTRACE(4, "mod_opal\tSettings codec-prefs=" << codec_string);\r
- }\r
- }\r
- else {\r
- PTRACE(4, "mod_opal\tChannel codec_string=" << codec_string);\r
- }\r
-\r
- PString orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE);\r
- if (!orig_codec.IsEmpty()) {\r
- if (m_endpoint.GetManager().GetDisableTranscoding()) {\r
- codec_string = orig_codec;\r
- PTRACE(4, "mod_opal\tNo transcoding, forced to originator codec=" << orig_codec);\r
- }\r
- else {\r
- codec_string.Splice(orig_codec+',', 0);\r
- PTRACE(4, "mod_opal\tSetting preference to originator codec=" << orig_codec);\r
- }\r
- }\r
- }\r
- else {\r
- PTRACE(4, "mod_opal\tChannel absolute_codec_string=" << codec_string);\r
- }\r
-\r
- {\r
- char *codec_order[SWITCH_MAX_CODECS];\r
- int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS);\r
- numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last);\r
- }\r
-\r
- for (int i = 0; i < numCodecs; i++) {\r
- const switch_codec_implementation_t *codec = codecs[i];\r
-\r
- // See if we have a match by PayloadType/rate/name\r
- OpalMediaFormat switchFormat((RTP_DataFrame::PayloadTypes)codec->ianacode,\r
- codec->samples_per_second,\r
- codec->iananame);\r
- if (!switchFormat.IsValid()) {\r
- // See if we have a match by name alone\r
- switchFormat = codec->iananame;\r
- if (!switchFormat.IsValid()) {\r
- PTRACE(2, "mod_opal\tCould not match FS codec "\r
- << codec->iananame << '@' << codec->samples_per_second\r
- << " (pt=" << (unsigned)codec->ianacode << ")"\r
- " to an OPAL media format.");\r
- continue;\r
- }\r
- }\r
-\r
- PTRACE(4, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);\r
-\r
-#if IMPLEMENT_MULTI_FAME_AUDIO\r
- // Did we match or create a new media format?\r
- if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) {\r
- // Calculate frames per packet, do not use codec->codec_frames_per_packet as that field\r
- // has slightly different semantics when used in streamed codecs such as G.711\r
- int fpp = codec->samples_per_packet/switchFormat.GetFrameTime();\r
-\r
- /* Set the frames/packet to maximum of what is in the FS table. The OPAL negotiations will\r
- drop the value from there. This might fail if there are "holes" in the FS table, e.g.\r
- if for some reason G.723.1 has 30ms and 90ms but not 60ms, then the OPAL negotiations\r
- could end up with 60ms and the codec cannot be created. The "holes" are unlikely in\r
- all but streamed codecs such as G.711, where it is theoretically possible for OPAL to\r
- come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these\r
- scenarios sufficiently rare that we can safely ignore them ... for now. */\r
-\r
- if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) {\r
- switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp);\r
- }\r
-\r
- if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) {\r
- switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp);\r
- }\r
- }\r
-#endif // IMPLEMENT_MULTI_FAME_AUDIO\r
-\r
- m_switchMediaFormats += switchFormat;\r
- }\r
-\r
-#if HAVE_T38\r
- OpalMediaFormat t38 = OpalT38;\r
-\r
- /* We need to have a T.38 options for TCS, but may be before the\r
- spandsp_mod has set it us. So, if not, we actually give to spandsp_mod. */\r
- switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, "t38_options");\r
- if (t38_options == NULL)\r
- SetT38OptionsFromMediaFormat(t38, "_preconfigured_t38_options");\r
- else {\r
- t38.SetOptionInteger("T38FaxVersion", t38_options->T38FaxVersion);\r
- t38.SetOptionInteger("T38MaxBitRate", t38_options->T38MaxBitRate);\r
- t38.SetOptionBoolean("T38FaxFillBitRemoval", t38_options->T38FaxFillBitRemoval);\r
- t38.SetOptionBoolean("T38FaxTranscodingMMR", t38_options->T38FaxTranscodingMMR);\r
- t38.SetOptionBoolean("T38FaxTranscodingJBIG", t38_options->T38FaxTranscodingJBIG);\r
- t38.SetOptionValue("T38FaxRateManagement", t38_options->T38FaxRateManagement);\r
- t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxBuffer);\r
- t38.SetOptionInteger("T38Version", t38_options->T38FaxMaxDatagram);\r
- t38.SetOptionValue("T38FaxUdpEC", t38_options->T38FaxUdpEC);\r
- }\r
-\r
- m_switchMediaFormats += t38;\r
-#endif // HAVE_T38\r
-}\r
-\r
-\r
-OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource)\r
-{\r
- return new FSMediaStream(*this, mediaFormat, sessionID, isSource);\r
-}\r
-\r
-\r
-void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)\r
-{\r
- OpalConnection::OnPatchMediaStream(isSource, patch);\r
-\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return;\r
-\r
- if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio())\r
- return;\r
-\r
- if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {\r
- PTRACE(4, "mod_opal\tOnPatchMediaStream for inbound call, flagging media opened");\r
- if (isSource)\r
- m_rxAudioOpened.Signal();\r
- else\r
- m_txAudioOpened.Signal();\r
- }\r
- else {\r
- PTRACE(4, "mod_opal\tOnPatchMediaStream for outbound call, checking media");\r
- if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) {\r
- // Have open media in both directions.\r
- if (IsEstablished()) {\r
- PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call answered");\r
- switch_channel_mark_answered(m_fsChannel);\r
- }\r
- else if (!IsReleased()) {\r
- PTRACE(3, "mod_opal\tOnPatchMediaStream for outbound call, making call pre-answered");\r
- switch_channel_mark_pre_answered(m_fsChannel);\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_init()\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return SWITCH_STATUS_FALSE;\r
-\r
- PTRACE(4, "mod_opal\tStarted routing for connection " << *this);\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_routing()\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return SWITCH_STATUS_FALSE;\r
-\r
- PTRACE(4, "mod_opal\tRouting connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_execute()\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return SWITCH_STATUS_FALSE;\r
-\r
- PTRACE(4, "mod_opal\tExecuting connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_destroy()\r
-{\r
- PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this);\r
-\r
- m_fsChannel = NULL; // Will be destroyed by FS, so don't use it any more.\r
-\r
- switch_core_codec_destroy(&m_read_codec);\r
- switch_core_codec_destroy(&m_write_codec);\r
- switch_core_codec_destroy(&m_vid_read_codec);\r
- switch_core_codec_destroy(&m_vid_write_codec);\r
- switch_core_timer_destroy(&m_read_timer);\r
- switch_core_timer_destroy(&m_vid_read_timer);\r
-\r
- switch_core_session_set_private(m_fsSession, NULL);\r
- SafeDereference();\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_hangup()\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return SWITCH_STATUS_FALSE;\r
-\r
- /* if this is still here it was our idea to hangup not opal's */\r
- ClearCallSynchronous(NULL, H323TranslateToCallEndReason(\r
- (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX));\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_exchange_media()\r
-{\r
- PTRACE(4, "mod_opal\tExchanging media on connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::on_soft_execute()\r
-{\r
- PTRACE(4, "mod_opal\tSoft execute on connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::kill_channel(int sig)\r
-{\r
- switch (sig) {\r
- case SWITCH_SIG_KILL:\r
- PTRACE(4, "mod_opal\tSignal KILL received on connection " << *this);\r
- m_rxAudioOpened.Signal();\r
- m_txAudioOpened.Signal();\r
- CloseMediaStreams();\r
- break;\r
-\r
- case SWITCH_SIG_BREAK:\r
- PTRACE(4, "mod_opal\tSignal BREAK received on connection " << *this);\r
- break;\r
-\r
- default:\r
- PTRACE(4, "mod_opal\tSignal " << sig << " received on connection " << *this);\r
- break;\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf)\r
-{\r
- PTRACE(4, "mod_opal\tReceived DTMF from FS: tone=" << dtmf->digit << ", duration=" << dtmf->duration);\r
- OnUserInputTone(dtmf->digit, dtmf->duration);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg)\r
-{\r
- if (PAssertNULL(m_fsChannel) == NULL)\r
- return SWITCH_STATUS_FALSE;\r
-\r
- switch (msg->message_id) {\r
- case SWITCH_MESSAGE_INDICATE_RINGING:\r
- case SWITCH_MESSAGE_INDICATE_PROGRESS:\r
- case SWITCH_MESSAGE_INDICATE_ANSWER:\r
- case SWITCH_MESSAGE_INDICATE_DEFLECT:\r
- if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {\r
- switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel);\r
- if (profile != NULL && profile->caller_extension != NULL)\r
- {\r
- PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
- if (other != NULL) {\r
- other->SetLocalPartyName(profile->caller_extension->extension_number);\r
- other->SetDisplayName(profile->caller_extension->extension_name);\r
- }\r
- SetLocalPartyName(profile->caller_extension->extension_number);\r
- SetDisplayName(profile->caller_extension->extension_name);\r
- }\r
- }\r
- else {\r
- return SWITCH_STATUS_FALSE;\r
- }\r
- break;\r
-\r
- default:\r
- break;\r
- }\r
-\r
- switch (msg->message_id) {\r
- case SWITCH_MESSAGE_INDICATE_BRIDGE:\r
- case SWITCH_MESSAGE_INDICATE_UNBRIDGE:\r
- case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:\r
- m_flushAudio = true;\r
- break;\r
-\r
- case SWITCH_MESSAGE_INDICATE_RINGING:\r
- AlertingIncoming();\r
- break;\r
-\r
- case SWITCH_MESSAGE_INDICATE_PROGRESS:\r
- AutoStartMediaStreams();\r
- AlertingIncoming();\r
-\r
- if (!WaitForMedia())\r
- return SWITCH_STATUS_FALSE;\r
-\r
- if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {\r
- switch_channel_mark_pre_answered(m_fsChannel);\r
- }\r
- break;\r
-\r
- case SWITCH_MESSAGE_INDICATE_ANSWER:\r
- AcceptIncoming();\r
-\r
- if (!WaitForMedia())\r
- return SWITCH_STATUS_FALSE;\r
-\r
- if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) {\r
- switch_channel_mark_answered(m_fsChannel);\r
- }\r
- break;\r
-\r
- case SWITCH_MESSAGE_INDICATE_DEFLECT:\r
- ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection());\r
- break;\r
-\r
-#if HAVE_T38\r
- case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA:\r
- {\r
- PTRACE(2, "mod_opal\tRequesting switch to T.38");\r
- PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
- if (other != NULL && other->SwitchFaxMediaStreams(true))\r
- switch_channel_set_flag(m_fsChannel, CF_REQ_MEDIA);\r
- else {\r
- PTRACE(1, "mod_opal\tMode change request to T.38 failed");\r
- }\r
- break;\r
- }\r
-\r
- case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION:\r
- PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_T38_DESCRIPTION");\r
- break;\r
-\r
- case SWITCH_MESSAGE_INDICATE_UDPTL_MODE:\r
- PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_UDPTL_MODE");\r
- m_udptl = true;\r
- break;\r
-#endif // HAVE_T38\r
-\r
- default:\r
- PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this);\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-bool FSConnection::WaitForMedia()\r
-{\r
- PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this);\r
- m_rxAudioOpened.Wait();\r
- m_txAudioOpened.Wait();\r
-\r
- if (IsReleased()) {\r
- // Call got aborted\r
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");\r
- return false;\r
- }\r
-\r
- PTRACE(3, "mod_opal\tMedia started on connection " << *this);\r
- return true;\r
-}\r
-\r
-\r
-#if HAVE_T38\r
-void FSConnection::SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname)\r
-{\r
- switch_t38_options_t *t38_options = (switch_t38_options_t *)switch_channel_get_private(m_fsChannel, varname);\r
- if (t38_options == NULL)\r
- t38_options = (switch_t38_options_t *)switch_core_session_alloc(m_fsSession, sizeof(switch_t38_options_t));\r
-\r
- PString value;\r
- mediaFormat.GetOptionValue("T38FaxRateManagement", value);\r
- t38_options->T38FaxRateManagement = switch_core_session_strdup(m_fsSession, value);\r
-\r
- mediaFormat.GetOptionValue("T38FaxUdpEC", value);\r
- t38_options->T38FaxUdpEC = switch_core_session_strdup(m_fsSession, value);\r
-\r
- t38_options->T38MaxBitRate = mediaFormat.GetOptionInteger("T38MaxBitRate", 9600);\r
- t38_options->T38FaxMaxBuffer = mediaFormat.GetOptionInteger("T38FaxMaxBuffer", 2000);\r
- t38_options->T38FaxMaxDatagram = mediaFormat.GetOptionInteger("T38FaxMaxDatagram", 528);\r
-\r
- t38_options->T38FaxFillBitRemoval = mediaFormat.GetOptionBoolean("T38FaxFillBitRemoval") ? SWITCH_TRUE : SWITCH_FALSE;\r
- t38_options->T38FaxTranscodingMMR = mediaFormat.GetOptionBoolean("T38FaxTranscodingMMR") ? SWITCH_TRUE : SWITCH_FALSE;\r
- t38_options->T38FaxTranscodingJBIG = mediaFormat.GetOptionBoolean("T38FaxTranscodingJBIG") ? SWITCH_TRUE : SWITCH_FALSE;\r
-\r
- t38_options->T38VendorInfo = switch_core_session_strdup(m_fsSession, mediaFormat.GetOptionString("T38VendorInfo"));\r
-\r
- //t38_options->remote_ip = switch_core_session_strdup(session, mediaFormat.something);\r
- //t38_options->remote_port = mediaFormat.something;\r
-\r
- switch_channel_set_private(m_fsChannel, varname, t38_options);\r
- PTRACE(3, "mod_opal\tSet " << varname);\r
-}\r
-\r
-\r
-void FSConnection::OnSwitchedT38(bool toT38, bool success)\r
-{\r
- if (toT38 && success && IndicateSwitchedT38()) {\r
- PTRACE(3, "mod_opal\tMode change request to T.38 succeeded");\r
- }\r
- else {\r
- AbortT38();\r
- }\r
-}\r
-\r
-\r
-void FSConnection::OnSwitchingT38(bool toT38)\r
-{\r
- if (toT38 && IndicateSwitchedT38()) {\r
- PTRACE(3, "mod_opal\tMode change request to T.38 started");\r
- }\r
- else {\r
- AbortT38();\r
- }\r
-}\r
-\r
-\r
-void FSConnection::AbortT38()\r
-{\r
- PTRACE(3, "mod_opal\tMode change request to T.38 failed");\r
- switch_channel_set_private(m_fsChannel, "t38_options", NULL);\r
- switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38);\r
- switch_channel_clear_app_flag_key("T38", m_fsChannel, CF_APP_T38_REQ);\r
- switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38_FAIL);\r
-}\r
-\r
-\r
-bool FSConnection::IndicateSwitchedT38()\r
-{\r
- PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
- if (other == NULL) {\r
- PTRACE(3, "mod_opal\tCan't change to T.38, no other connection");\r
- return false;\r
- }\r
-\r
- OpalMediaFormatList otherFormats = other->GetMediaFormats();\r
- OpalMediaFormatList::const_iterator t38 = otherFormats.FindFormat(OpalT38);\r
- if (t38 == otherFormats.end()) {\r
- PTRACE(3, "mod_opal\tCan't change to T.38, no remote capability");\r
- return false;\r
- }\r
-\r
- SetT38OptionsFromMediaFormat(*t38, "t38_options");\r
-\r
- switch_channel_set_variable(m_fsChannel, "has_t38", "true");\r
- switch_channel_set_app_flag_key("T38", m_fsChannel, CF_APP_T38);\r
-\r
- switch_channel_execute_on(m_fsChannel, "opal_execute_on_t38");\r
- switch_channel_api_on(m_fsChannel, "opal_api_on_t38");\r
- return true;\r
-}\r
-#endif // HAVE_T38\r
-\r
-\r
-switch_status_t FSConnection::receive_event(switch_event_t *event)\r
-{\r
- PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::state_change()\r
-{\r
- PTRACE(4, "mod_opal\tState changed on connection " << *this);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id)\r
-{\r
- return read_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
-}\r
-\r
-\r
-switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id)\r
-{\r
- return write_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
-}\r
-\r
-\r
-switch_status_t FSConnection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id)\r
-{\r
- return read_frame(OpalMediaType::Video(), frame, flag);\r
-}\r
-\r
-\r
-switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id)\r
-{\r
- return write_frame(OpalMediaType::Video(), frame, flag);\r
-}\r
-\r
-\r
-switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags)\r
-{\r
- if (!ownerCall.IsSwitchingT38()) {\r
- PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));\r
- if (stream != NULL)\r
- return stream->read_frame(frame, flags);\r
-\r
- PTRACE(2, "mod_opal\tNo stream for read of " << mediaType);\r
- }\r
-\r
- // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS\r
- *frame = &m_dummy_frame;\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags)\r
-{\r
- // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS\r
- if (ownerCall.IsSwitchingT38())\r
- return SWITCH_STATUS_SUCCESS;\r
-\r
- PSafePtr <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, true));\r
- if (stream != NULL)\r
- return stream->write_frame(frame, flags);\r
-\r
- PTRACE(2, "mod_opal\tNo stream for write of " << mediaType);\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-///////////////////////////////////////////////////////////////////////\r
-\r
-FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)\r
- : OpalMediaStream(conn, mediaFormat, sessionID, isSource)\r
- , m_connection(conn)\r
- , m_switchTimer(NULL)\r
- , m_switchCodec(NULL)\r
- , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE)\r
-{\r
- memset(&m_readFrame, 0, sizeof(m_readFrame));\r
-}\r
-\r
-\r
-PBoolean FSMediaStream::Open()\r
-{\r
- if (IsOpen()) {\r
- return true;\r
- }\r
-\r
- switch_core_session_t *fsSession = m_connection.GetSession();\r
- switch_channel_t *fsChannel = m_connection.GetChannel();\r
- if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL)\r
- return false;\r
-\r
- bool isAudio;\r
- OpalMediaType mediaType = mediaFormat.GetMediaType();\r
- if (mediaType == OpalMediaType::Audio())\r
- isAudio = true;\r
- else if (mediaType == OpalMediaType::Video())\r
- isAudio = false;\r
-#if HAVE_T38\r
- else if (mediaType == OpalMediaType::Fax()) {\r
- m_readFrame.flags = SFF_UDPTL_PACKET|SFF_PROXY_PACKET;\r
- return OpalMediaStream::Open();\r
- }\r
-#endif\r
- else {\r
- PTRACE(1, "mod_opal\tUnsupported media type: " << mediaType);\r
- return false;\r
- }\r
-\r
- int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits();\r
-\r
- if (IsSink()) {\r
- m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec;\r
- m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer;\r
- m_readFrame.codec = m_switchCodec;\r
- m_readFrame.rate = mediaFormat.GetClockRate();\r
- } else {\r
- m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec;\r
- }\r
-\r
- // The following is performed on two different instances of this object.\r
- if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP\r
- mediaFormat.GetClockRate(), ptime, 1, // Channels\r
- SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings\r
- switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {\r
- // Could not select a codecs using negotiated frames/packet, so try using default.\r
- if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP\r
- mediaFormat.GetClockRate(), 0, 1, // Channels\r
- SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings\r
- switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {\r
- PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
- << " cannot initialise " << (IsSink()? "read" : "write") << ' '\r
- << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
- switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);\r
- return false;\r
- }\r
- PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
- << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '\r
- << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
- }\r
-\r
- if (IsSink()) {\r
- if (isAudio) {\r
- switch_core_session_set_read_codec(fsSession, m_switchCodec);\r
- if (switch_core_timer_init(m_switchTimer,\r
- "soft",\r
- m_switchCodec->implementation->microseconds_per_packet / 1000,\r
- m_switchCodec->implementation->samples_per_packet,\r
- switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {\r
- PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
- << " timer init failed on " << (IsSink()? "read" : "write") << ' '\r
- << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
- switch_core_codec_destroy(m_switchCodec);\r
- m_switchCodec = NULL;\r
- return false;\r
- }\r
- } else {\r
- switch_core_session_set_video_read_codec(fsSession, m_switchCodec);\r
- switch_channel_set_flag(fsChannel, CF_VIDEO);\r
- }\r
- } else {\r
- if (isAudio) {\r
- switch_core_session_set_write_codec(fsSession, m_switchCodec);\r
- } else {\r
- switch_core_session_set_video_write_codec(fsSession, m_switchCodec);\r
- switch_channel_set_flag(fsChannel, CF_VIDEO);\r
- }\r
- }\r
-\r
- PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel)\r
- << " initialised " << (IsSink()? "read" : "write") << ' '\r
- << mediaType << " codec " << mediaFormat << " for connection " << *this);\r
-\r
- return OpalMediaStream::Open();\r
-}\r
-\r
-\r
-void FSMediaStream::InternalClose()\r
-{\r
-}\r
-\r
-\r
-PBoolean FSMediaStream::IsSynchronous() const\r
-{\r
- return true;\r
-}\r
-\r
-\r
-PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const\r
-{\r
- return false;\r
-}\r
-\r
-\r
-int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const\r
-{\r
- if (!IsOpen()) {\r
- PTRACE(1, "mod_opal\tNot open!");\r
- return -1;\r
- }\r
-\r
- if (!m_connection.IsChannelReady()) {\r
- PTRACE(1, "mod_opal\tChannel not ready!");\r
- return -1;\r
- }\r
-\r
- // We make referenced copy of pointer so can't be deleted out from under us\r
- mediaPatch = m_mediaPatch;\r
- if (mediaPatch == NULL) {\r
- /*There is a race here... sometimes we make it here and m_mediaPatch is NULL\r
- if we wait it shows up in 1ms, maybe there is a better way to wait. */\r
- PTRACE(2, "mod_opal\tPatch not ready!");\r
- return 1;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-\r
-switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)\r
-{\r
- *frame = &m_readFrame;\r
- m_readFrame.flags |= SFF_CNG;\r
-\r
- PatchPtr mediaPatch;\r
- switch (StartReadWrite(mediaPatch)) {\r
- case -1 :\r
- return SWITCH_STATUS_FALSE;\r
- case 1 :\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- if (m_connection.NeedFlushAudio()) {\r
- mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer\r
- m_readRTP.SetPayloadSize(0);\r
- } else {\r
- if (m_switchCodec != NULL) {\r
- m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);\r
- }\r
-\r
- if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) {\r
- PTRACE(1, "mod_opal\tread_frame: no source data!");\r
- return SWITCH_STATUS_FALSE;\r
- }\r
- }\r
-\r
- if (m_switchTimer != NULL) {\r
- switch_core_timer_next(m_switchTimer);\r
- }\r
-\r
- if (m_switchCodec != NULL) {\r
- if (!switch_core_codec_ready(m_switchCodec)) {\r
- PTRACE(1, "mod_opal\tread_frame: codec not ready!");\r
- return SWITCH_STATUS_FALSE;\r
- }\r
- }\r
-\r
- if (switch_test_flag(&m_readFrame, SFF_UDPTL_PACKET)) {\r
- m_readFrame.flags &= ~SFF_CNG;\r
- m_readFrame.packet = m_readRTP.GetPayloadPtr();\r
- m_readFrame.packetlen = m_readRTP.GetPayloadSize();\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- if (switch_test_flag(&m_readFrame, SFF_RAW_RTP)) {\r
- m_readFrame.flags &= ~SFF_CNG;\r
- m_readFrame.packet = m_readRTP.GetPointer();\r
- m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readRTP.GetPayloadSize();\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
-#if IMPLEMENT_MULTI_FAME_AUDIO\r
- // Repackage frames in incoming packet to agree with what FS expects.\r
- // Not implmented yet!!!!!!!!!\r
- // Cheating and only supporting one frame per packet\r
-#endif\r
-\r
- m_readFrame.buflen = m_readRTP.GetSize();\r
- m_readFrame.data = m_readRTP.GetPayloadPtr();\r
- m_readFrame.datalen = m_readRTP.GetPayloadSize();\r
- m_readFrame.timestamp = m_readRTP.GetTimestamp();\r
- m_readFrame.seq = m_readRTP.GetSequenceNumber();\r
- m_readFrame.ssrc = m_readRTP.GetSyncSource();\r
- m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE;\r
- m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType();\r
-\r
- if (m_readFrame.datalen > 0 &&\r
- m_readFrame.payload != RTP_DataFrame::CN &&\r
- m_readFrame.payload != RTP_DataFrame::Cisco_CN) {\r
- m_readFrame.flags &= ~SFF_CNG;\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags)\r
-{\r
- PatchPtr mediaPatch;\r
- switch (StartReadWrite(mediaPatch)) {\r
- case -1 :\r
- return SWITCH_STATUS_FALSE;\r
- case 1 :\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- RTP_DataFrame rtp;\r
- if (switch_test_flag(frame, SFF_RAW_RTP)) {\r
- rtp = RTP_DataFrame((const BYTE *)frame->packet, frame->packetlen, false);\r
- }\r
- else if (switch_test_flag(frame, SFF_UDPTL_PACKET)) {\r
- rtp.SetPayloadSize(frame->packetlen);\r
- memcpy(rtp.GetPayloadPtr(), frame->packet, frame->packetlen);\r
- }\r
- else {\r
- rtp.SetPayloadSize(frame->datalen);\r
- memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);\r
-\r
- rtp.SetPayloadType(mediaFormat.GetPayloadType());\r
-\r
- /* Not sure what FS is going to give us!\r
- Suspect it depends on the mod on the other side sending it. */\r
- if (frame->timestamp != 0)\r
- timestamp = frame->timestamp;\r
- else if (frame->samples != 0)\r
- timestamp += frame->samples;\r
- else if (m_switchCodec != NULL)\r
- timestamp += m_switchCodec->implementation->samples_per_packet;\r
- rtp.SetTimestamp(timestamp);\r
- }\r
-\r
- if (mediaPatch->PushFrame(rtp))\r
- return SWITCH_STATUS_SUCCESS;\r
-\r
- PTRACE(1, "mod_opal\tread_frame: push failed!");\r
- return SWITCH_STATUS_FALSE;\r
-}\r
-\r
-\r
-/* For Emacs:\r
- * Local Variables:\r
- * mode:c\r
- * indent-tabs-mode:nil\r
- * tab-width:4\r
- * c-basic-offset:4\r
- * End:\r
- * For VIM:\r
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s:\r
- */\r
+/* 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 <opal/patch.h>
+#include <rtp/rtp.h>
+#if PTLIB_CHECK_VERSION(2,11,1)
+#include <rtp/rtp_session.h>
+#endif
+#include <h323/h323pdu.h>
+#include <h323/gkclient.h>
+
+
+/* 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":<da>"); // config option for direct routing
+ AddRouteEntry("iax2:.* = "FS_PREFIX":<da>"); // config option for direct routing
+ AddRouteEntry(FS_PREFIX":.* = h323:<da>"); // 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<OpalConnection> 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<FSConnection *>(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<OpalConnection> 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<OpalConnection> 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<OpalConnection> 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 <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(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 <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(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 /\r
- * Soft-Switch Application\r
- *\r
- * Version: MPL 1.1\r
- *\r
- * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)\r
- * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version\r
- * 1.1 (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * http://www.mozilla.org/MPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS IS" basis,\r
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r
- * for the specific language governing rights and limitations under the\r
- * License.\r
- *\r
- * Contributor(s):\r
- * Tuyan Ozipek (tuyanozipek@gmail.com)\r
- * Lukasz Zwierko (lzwierko@gmail.com)\r
- * Robert Jongbloed (robertj@voxlucida.com.au)\r
- *\r
- */\r
-\r
-\r
-#ifndef __FREESWITCH_MOD_OPAL__\r
-#define __FREESWITCH_MOD_OPAL__\r
-\r
-#if defined(__GNUC__) && defined(HAVE_VISIBILITY)\r
-#pragma GCC visibility push(default)\r
-#endif\r
-\r
-#include <ptlib.h>\r
-#include <opal/manager.h>\r
-\r
-#ifndef OPAL_CHECK_VERSION\r
- #define OPAL_CHECK_VERSION(a,b,c) 0\r
-#endif\r
-\r
-#if !OPAL_CHECK_VERSION(3,12,8)\r
- #error OPAL is too old to use, must be >= 2.12.8\r
-#endif\r
-\r
-#include <ep/localep.h>\r
-#include <h323/h323ep.h>\r
-#include <iax2/iax2ep.h>\r
-\r
-#if defined(__GNUC__) && defined(HAVE_VISIBILITY)\r
-#pragma GCC visibility pop\r
-#endif\r
-\r
-#undef strcasecmp\r
-#undef strncasecmp\r
-\r
-#include <switch.h>\r
-\r
-#define MODNAME "mod_opal"\r
-\r
-#define HAVE_T38 OPAL_T38_CAPABILITY\r
-\r
-\r
-\r
-class FSEndPoint;\r
-class FSManager;\r
-\r
-\r
-class FSProcess : public PLibraryProcess\r
-{\r
- PCLASSINFO(FSProcess, PLibraryProcess);\r
- public:\r
- FSProcess();\r
- ~FSProcess();\r
-\r
- bool Initialise(switch_loadable_module_interface_t *iface);\r
-\r
- FSManager & GetManager() const\r
- {\r
- return *m_manager;\r
- }\r
-\r
- protected:\r
- FSManager * m_manager;\r
-};\r
-\r
-\r
-struct FSListener\r
-{\r
- FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { }\r
-\r
- PString m_name;\r
- PIPSocket::Address m_address;\r
- uint16_t m_port;\r
-};\r
-\r
-\r
-class FSManager : public OpalManager\r
-{\r
- PCLASSINFO(FSManager, OpalManager);\r
-\r
- public:\r
- FSManager();\r
-\r
- bool Initialise(switch_loadable_module_interface_t *iface);\r
-\r
- switch_status_t ReadConfig(int reload);\r
-\r
- switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; }\r
- const PString & GetContext() const { return m_context; }\r
- const PString & GetDialPlan() const { return m_dialplan; }\r
- const PString & GetCodecPrefs() const { return m_codecPrefs; }\r
- bool GetDisableTranscoding() const { return m_disableTranscoding; }\r
-\r
- private:\r
- switch_endpoint_interface_t *m_FreeSwitch;\r
-\r
- H323EndPoint *m_h323ep;\r
- IAX2EndPoint *m_iaxep;\r
- FSEndPoint *m_fsep;\r
-\r
- PString m_context;\r
- PString m_dialplan;\r
- PString m_codecPrefs;\r
- bool m_disableTranscoding;\r
- PString m_gkAddress;\r
- PString m_gkIdentifer;\r
- PString m_gkInterface;\r
-\r
- list <FSListener> m_listeners;\r
-};\r
-\r
-\r
-class FSEndPoint : public OpalLocalEndPoint\r
-{\r
- PCLASSINFO(FSEndPoint, OpalLocalEndPoint);\r
- public:\r
- FSEndPoint(FSManager & manager);\r
-\r
- virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions);\r
-\r
- FSManager & GetManager() const { return m_manager; }\r
-\r
- protected:\r
- FSManager & m_manager;\r
-};\r
-\r
-\r
-class FSConnection;\r
-\r
-\r
-class FSMediaStream : public OpalMediaStream\r
-{\r
- PCLASSINFO(FSMediaStream, OpalMediaStream);\r
- public:\r
- FSMediaStream(\r
- FSConnection & conn,\r
- const OpalMediaFormat & mediaFormat, ///< Media format for stream\r
- unsigned sessionID, ///< Session number for stream\r
- bool isSource ///< Is a source stream\r
- );\r
-\r
- virtual PBoolean Open();\r
- virtual PBoolean IsSynchronous() const;\r
- virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;\r
-\r
- switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);\r
- switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);\r
-\r
- protected:\r
- virtual void InternalClose();\r
- int StartReadWrite(PatchPtr & mediaPatch) const;\r
-\r
- private:\r
- bool CheckPatchAndLock();\r
-\r
- FSConnection &m_connection;\r
- switch_timer_t *m_switchTimer;\r
- switch_codec_t *m_switchCodec;\r
- switch_frame_t m_readFrame;\r
- RTP_DataFrame m_readRTP;\r
-};\r
-\r
-\r
-#define DECLARE_CALLBACK0(name) \\r
- static switch_status_t name(switch_core_session_t *session) { \\r
- FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \\r
- return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \\r
- switch_status_t name()\r
-\r
-#define DECLARE_CALLBACK1(name, type1, name1) \\r
- static switch_status_t name(switch_core_session_t *session, type1 name1) { \\r
- FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \\r
- return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \\r
- switch_status_t name(type1 name1)\r
-\r
-#define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \\r
- static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \\r
- FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \\r
- return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \\r
- switch_status_t name(type1 name1, type2 name2, type3 name3)\r
-\r
-\r
-\r
-\r
-class FSConnection : public OpalLocalConnection\r
-{\r
- PCLASSINFO(FSConnection, OpalLocalConnection)\r
-\r
- public:\r
- struct outgoing_params {\r
- switch_event_t *var_event;\r
- switch_caller_profile_t *outbound_profile;\r
- switch_core_session_t **new_session;\r
- switch_memory_pool_t **pool;\r
- switch_originate_flag_t flags;\r
- switch_call_cause_t *cancel_cause;\r
- switch_call_cause_t fail_cause;\r
- };\r
-\r
- FSConnection(OpalCall & call,\r
- FSEndPoint & endpoint,\r
- unsigned options,\r
- OpalConnection::StringOptions * stringOptions,\r
- outgoing_params * params);\r
-\r
- virtual bool OnOutgoingSetUp();\r
- virtual bool OnIncoming();\r
- virtual void OnEstablished();\r
- virtual void OnReleased();\r
- virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia);\r
- virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean);\r
- virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch);\r
- virtual OpalMediaFormatList GetMediaFormats() const;\r
- virtual PBoolean SendUserInputTone(char tone, unsigned duration);\r
-#if HAVE_T38\r
- virtual void OnSwitchedT38(bool toT38, bool success);\r
- virtual void OnSwitchingT38(bool toT38);\r
-#endif\r
-\r
- DECLARE_CALLBACK0(on_init);\r
- DECLARE_CALLBACK0(on_destroy);\r
- DECLARE_CALLBACK0(on_routing);\r
- DECLARE_CALLBACK0(on_execute);\r
- DECLARE_CALLBACK0(on_hangup);\r
-\r
- DECLARE_CALLBACK0(on_exchange_media);\r
- DECLARE_CALLBACK0(on_soft_execute);\r
-\r
- DECLARE_CALLBACK1(kill_channel, int, sig);\r
- DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf);\r
- DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg);\r
- DECLARE_CALLBACK1(receive_event, switch_event_t *, event);\r
- DECLARE_CALLBACK0(state_change);\r
- DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id);\r
- DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id);\r
- DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id);\r
- DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id);\r
-\r
- __inline switch_core_session_t *GetSession() const\r
- {\r
- return m_fsSession;\r
- }\r
-\r
- __inline switch_channel_t *GetChannel() const\r
- {\r
- return m_fsChannel;\r
- }\r
-\r
- bool IsChannelReady() const\r
- {\r
- return m_fsChannel != NULL && switch_channel_ready(m_fsChannel);\r
- }\r
-\r
- bool NeedFlushAudio()\r
- {\r
- if (!m_flushAudio)\r
- return false;\r
- m_flushAudio = false;\r
- return true;\r
- }\r
-\r
- protected:\r
- void SetCodecs();\r
- bool WaitForMedia();\r
-#if HAVE_T38\r
- void SetT38OptionsFromMediaFormat(const OpalMediaFormat & mediaFormat, const char * varname);\r
- bool IndicateSwitchedT38();\r
- void AbortT38();\r
-#endif\r
-\r
- switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags);\r
- switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags);\r
-\r
- private:\r
- FSEndPoint &m_endpoint;\r
- switch_core_session_t *m_fsSession;\r
- switch_channel_t *m_fsChannel;\r
- PSyncPoint m_rxAudioOpened;\r
- PSyncPoint m_txAudioOpened;\r
- OpalMediaFormatList m_switchMediaFormats;\r
-\r
- // If FS ever supports more than one audio and one video, this needs to change\r
- switch_timer_t m_read_timer;\r
- switch_codec_t m_read_codec;\r
- switch_codec_t m_write_codec;\r
-\r
- switch_timer_t m_vid_read_timer;\r
- switch_codec_t m_vid_read_codec;\r
- switch_codec_t m_vid_write_codec;\r
-\r
- switch_frame_t m_dummy_frame;\r
-\r
- bool m_flushAudio;\r
- bool m_udptl;\r
-\r
- friend PBoolean FSMediaStream::Open();\r
-};\r
-\r
-\r
-#endif /* __FREESWITCH_MOD_OPAL__ */\r
-\r
-/* For Emacs:\r
- * Local Variables:\r
- * mode:c\r
- * indent-tabs-mode:nil\r
- * tab-width:4\r
- * c-basic-offset:4\r
- * End:\r
- * For VIM:\r
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:s:\r
- */\r
+/* 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 <ptlib.h>
+#include <opal/manager.h>
+
+#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 <ep/localep.h>
+#include <h323/h323ep.h>
+#include <iax2/iax2ep.h>
+
+#if defined(__GNUC__) && defined(HAVE_VISIBILITY)
+#pragma GCC visibility pop
+#endif
+
+#undef strcasecmp
+#undef strncasecmp
+
+#include <switch.h>
+
+#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 <FSListener> 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:
+ */
such a type exists and the standard includes do not define it. */
#cmakedefine uint8_t unsigned char
-#ifdef HAVE_INTTYPES_H\r
-#include <inttypes.h>\r
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
#endif
#include <stdlib.h>
/* AMF number */
-typedef\r
-#if SIZEOF_FLOAT == 8\r
-float\r
-#elif SIZEOF_DOUBLE == 8\r
-double\r
-#elif SIZEOF_LONG_DOUBLE == 8\r
-long double\r
-#else\r
-uint64_t\r
-#endif\r
+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);\r
+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__ */
-#define PERL_LIB "@PERL_LIB@"\r
+#define PERL_LIB "@PERL_LIB@"
#include <math.h>
#include <ctype.h>
-void gregorian_to_jalali(/* output */ int *j_y, int *j_m, int *j_d,\r
+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,\r
+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);
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,\r
- int g_y, int g_m, int g_d)\r
-{\r
- int gy, gm, gd;\r
- int jy, jm, jd;\r
- long g_day_no, j_day_no;\r
- int j_np;\r
- \r
- int i;\r
-\r
- gy = g_y-1600;\r
- gm = g_m-1;\r
- gd = g_d-1;\r
- \r
- g_day_no = 365*gy+(gy+3)/4-(gy+99)/100+(gy+399)/400;\r
- for (i=0;i<gm;++i)\r
- g_day_no += g_days_in_month[i];\r
- if (gm>1 && ((gy%4==0 && gy%100!=0) || (gy%400==0)))\r
- /* leap and after Feb */\r
- ++g_day_no;\r
- g_day_no += gd;\r
- \r
- j_day_no = g_day_no-79;\r
- \r
- j_np = j_day_no / 12053;\r
- j_day_no %= 12053;\r
- \r
- jy = 979+33*j_np+4*(j_day_no/1461);\r
- j_day_no %= 1461;\r
- \r
- if (j_day_no >= 366) {\r
- jy += (j_day_no-1)/365;\r
- j_day_no = (j_day_no-1)%365;\r
- }\r
- \r
- for (i = 0; i < 11 && j_day_no >= j_days_in_month[i]; ++i) {\r
- j_day_no -= j_days_in_month[i];\r
- }\r
- jm = i+1;\r
- jd = j_day_no+1;\r
- *j_y = jy;\r
- *j_m = jm;\r
- *j_d = jd;\r
+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;i<gm;++i)
+ g_day_no += g_days_in_month[i];
+ if (gm>1 && ((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,\r
- int j_y, int j_m, int j_d)\r
-{\r
- int gy, gm, gd;\r
- int jy, jm, jd;\r
- long g_day_no, j_day_no;\r
- int leap;\r
-\r
- int i;\r
-\r
- jy = j_y-979;\r
- jm = j_m-1;\r
- jd = j_d-1;\r
-\r
- j_day_no = 365*jy + (jy/33)*8 + (jy%33+3)/4;\r
- for (i=0; i < jm; ++i)\r
- j_day_no += j_days_in_month[i];\r
-\r
- j_day_no += jd;\r
-\r
- g_day_no = j_day_no+79;\r
-\r
- gy = 1600 + 400*(g_day_no/146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */\r
- g_day_no = g_day_no % 146097;\r
-\r
- leap = 1;\r
- if (g_day_no >= 36525) /* 36525 = 365*100 + 100/4 */\r
- {\r
- g_day_no--;\r
- gy += 100*(g_day_no/36524); /* 36524 = 365*100 + 100/4 - 100/100 */\r
- g_day_no = g_day_no % 36524;\r
- \r
- if (g_day_no >= 365)\r
- g_day_no++;\r
- else\r
- leap = 0;\r
- }\r
-\r
- gy += 4*(g_day_no/1461); /* 1461 = 365*4 + 4/4 */\r
- g_day_no %= 1461;\r
-\r
- if (g_day_no >= 366) {\r
- leap = 0;\r
-\r
- g_day_no--;\r
- gy += g_day_no/365;\r
- g_day_no = g_day_no % 365;\r
- }\r
-\r
- for (i = 0; g_day_no >= g_days_in_month[i] + (i == 1 && leap); i++)\r
- g_day_no -= g_days_in_month[i] + (i == 1 && leap);\r
- gm = i+1;\r
- gd = g_day_no+1;\r
-\r
- *g_y = gy;\r
- *g_m = gm;\r
- *g_d = gd;\r
+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:
-/*\r
- * Copyright (c) 2007-2014, Anthony Minessale II\r
- * All rights reserved.\r
- * \r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions\r
- * are met:\r
- * \r
- * * Redistributions of source code must retain the above copyright\r
- * notice, this list of conditions and the following disclaimer.\r
- * \r
- * * Redistributions in binary form must reproduce the above copyright\r
- * notice, this list of conditions and the following disclaimer in the\r
- * documentation and/or other materials provided with the distribution.\r
- * \r
- * * Neither the name of the original author; nor the names of any contributors\r
- * may be used to endorse or promote products derived from this software\r
- * without specific prior written permission.\r
- * \r
- * \r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\r
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- *\r
- * The Initial Developer of the Original Code is\r
- * Anthony Minessale II <anthm@freeswitch.org>\r
- * Portions created by the Initial Developer are Copyright (C)\r
- * the Initial Developer. All Rights Reserved.\r
- *\r
- * Contributor(s):\r
- * \r
- * Anthony Minessale II <anthm@freeswitch.org>\r
- * Michael B. Murdock <mike@mmurdock.org>\r
- * António Silva <asilva@wirelessmundi.com>\r
- *\r
- * mod_say_pt.c -- Say for Portuguese\r
- *\r
- */\r
-\r
-#include <switch.h>\r
-#include <math.h>\r
-#include <ctype.h>\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load);\r
-SWITCH_MODULE_DEFINITION(mod_say_pt, mod_say_pt_load, NULL, NULL);\r
-\r
-#define say_num(num, meth) { \\r
- char tmp[80]; \\r
- switch_status_t tstatus; \\r
- switch_say_method_t smeth = say_args->method; \\r
- switch_say_type_t stype = say_args->type; \\r
- say_args->type = SST_ITEMS; say_args->method = meth; \\r
- switch_snprintf(tmp, sizeof(tmp), "%u", (unsigned)num); \\r
- if ((tstatus = \\r
- pt_say_general_count(session, tmp, say_args, args)) \\r
- != SWITCH_STATUS_SUCCESS) { \\r
- return tstatus; \\r
- } \\r
- say_args->method = smeth; say_args->type = stype; \\r
- } \\r
-\r
-\r
-#define say_file(...) { \\r
- char tmp[80]; \\r
- switch_status_t tstatus; \\r
- switch_snprintf(tmp, sizeof(tmp), __VA_ARGS__); \\r
- if ((tstatus = \\r
- switch_ivr_play_file(session, NULL, tmp, args)) \\r
- != SWITCH_STATUS_SUCCESS){ \\r
- return tstatus; \\r
- } \\r
- if (!switch_channel_ready(switch_core_session_get_channel(session))) { \\r
- return SWITCH_STATUS_FALSE; \\r
- }} \\r
-\r
-\r
-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)\r
-{\r
-\r
- /*a => 1xx-9xx*/\r
- if (a) {\r
- switch (a) {\r
- case 1:\r
- if (b || c) {\r
- say_file("digits/hundred.wav");\r
- } else {\r
- say_file("digits/100.wav");\r
- }\r
- break;\r
- case 2:\r
- say_file("digits/200.wav");\r
- break;\r
- case 3:\r
- say_file("digits/300.wav");\r
- break;\r
- case 5:\r
- say_file("digits/500.wav");\r
- break;\r
- default:\r
- say_file("digits/%d.wav", a);\r
- say_file("digits/hundreds.wav");\r
- break;\r
- }\r
- if (b || c) {\r
- say_file("currency/and.wav");\r
- }\r
- }\r
- /* b => 1x - 9x */\r
- if (b) {\r
- if (b > 1) {\r
- if (method == SSM_COUNTED) {\r
- say_file("digits/h-%d0.wav", b);\r
- } else {\r
- say_file("digits/%d0.wav", b);\r
- if (c > 0) {\r
- say_file("currency/and.wav");\r
- }\r
- }\r
- } else {\r
- say_file("digits/%d%d.wav", b, c);\r
- c = 0;\r
- }\r
- }\r
- /* c => 0 - 9*/\r
- if (c) {\r
- if (method == SSM_COUNTED) {\r
- say_file("digits/h-%d.wav", c);\r
- } else {\r
- say_file("digits/%d.wav", c);\r
- }\r
- }\r
-\r
- if (what && (a || b || c)) {\r
- say_file(what);\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-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)\r
-{\r
- int in;\r
- int x = 0;\r
- int places[9] = { 0 };\r
- char sbuf[128] = "";\r
- switch_status_t status;\r
-\r
- if (say_args->method == SSM_ITERATED) {\r
- if ((tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1))) {\r
- char *p;\r
- for (p = tosay; p && *p; p++) {\r
- say_file("digits/%c.wav", *p);\r
- }\r
- } else {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n");\r
- return SWITCH_STATUS_GENERR;\r
- }\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- if (!(tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1)) || strlen(tosay) > 9) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n");\r
- return SWITCH_STATUS_GENERR;\r
- }\r
-\r
- in = atoi(tosay);\r
-\r
- if (in != 0) {\r
- for (x = 8; x >= 0; x--) {\r
- int num = (int) pow(10, x);\r
- if ((places[(uint32_t) x] = in / num)) {\r
- in -= places[(uint32_t) x] * num;\r
- }\r
- }\r
-\r
- switch (say_args->method) {\r
- case SSM_COUNTED:\r
- case SSM_PRONOUNCED:\r
+/*
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Michael B. Murdock <mike@mmurdock.org>
+ * António Silva <asilva@wirelessmundi.com>
+ *
+ * mod_say_pt.c -- Say for Portuguese
+ *
+ */
+
+#include <switch.h>
+#include <math.h>
+#include <ctype.h>
+
+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) {\r
- return status;\r
- }\r
- if ((status = play_group(SSM_PRONOUNCED, places[5], places[4], places[3], "digits/thousand.wav", session, args)) != SWITCH_STATUS_SUCCESS) {\r
- return status;\r
- }\r
- if ((status = play_group(say_args->method, places[2], places[1], places[0], NULL, session, args)) != SWITCH_STATUS_SUCCESS) {\r
- return status;\r
- }\r
- break;\r
- default:\r
- break;\r
- }\r
- } else {\r
- say_file("digits/0.wav");\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-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)\r
-{\r
- int32_t t;\r
- switch_time_t target = 0, target_now = 0;\r
- switch_time_exp_t tm, tm_now;\r
- 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;\r
- switch_channel_t *channel = switch_core_session_get_channel(session);\r
- const char *tz = switch_channel_get_variable(channel, "timezone");\r
-\r
- if (say_args->type == SST_TIME_MEASUREMENT) {\r
- int64_t hours = 0;\r
- int64_t minutes = 0;\r
- int64_t seconds = 0;\r
- int64_t r = 0;\r
-\r
- if (strchr(tosay, ':')) {\r
- char *tme = switch_core_session_strdup(session, tosay);\r
- char *p;\r
-\r
- if ((p = strrchr(tme, ':'))) {\r
- *p++ = '\0';\r
- seconds = atoi(p);\r
- if ((p = strchr(tme, ':'))) {\r
- *p++ = '\0';\r
- minutes = atoi(p);\r
- hours = atoi(tme);\r
- } else {\r
- minutes = atoi(tme);\r
- }\r
- }\r
- } else {\r
- if ((seconds = atol(tosay)) <= 0) {\r
- seconds = (int64_t) switch_epoch_time_now(NULL);\r
- }\r
-\r
- if (seconds >= 60) {\r
- minutes = seconds / 60;\r
- r = seconds % 60;\r
- seconds = r;\r
- }\r
-\r
- if (minutes >= 60) {\r
- hours = minutes / 60;\r
- r = minutes % 60;\r
- minutes = r;\r
- }\r
- }\r
-\r
- if (hours) {\r
- say_num(hours, SSM_PRONOUNCED);\r
- if (hours == 1) {\r
- say_file("time/hour.wav");\r
- } else {\r
- say_file("time/hours.wav");\r
- }\r
- } else {\r
- say_file("digits/0.wav");\r
- say_file("time/hours.wav");\r
- }\r
-\r
- if (minutes) {\r
- say_num(minutes, SSM_PRONOUNCED);\r
- if (minutes == 1) {\r
- say_file("time/minute.wav");\r
- } else {\r
- say_file("time/minutes.wav");\r
- }\r
- } else {\r
- say_file("digits/0.wav");\r
- say_file("time/minutes.wav");\r
- }\r
-\r
- if (seconds) {\r
- say_num(seconds, SSM_PRONOUNCED);\r
- if (seconds == 1) {\r
- say_file("time/second.wav");\r
- } else {\r
- say_file("time/seconds.wav");\r
- }\r
- } else {\r
- say_file("digits/0.wav");\r
- say_file("time/seconds.wav");\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
- }\r
-\r
- if ((t = atol(tosay)) > 0) {\r
- target = switch_time_make(t, 0);\r
- target_now = switch_micro_time_now();\r
- } else {\r
- target = switch_micro_time_now();\r
- target_now = switch_micro_time_now();\r
- }\r
-\r
- if (tz) {\r
- int check = atoi(tz);\r
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Timezone is [%s]\n", tz);\r
- if (check) {\r
- switch_time_exp_tz(&tm, target, check);\r
- switch_time_exp_tz(&tm_now, target_now, check);\r
- } else {\r
- switch_time_exp_tz_name(tz, &tm, target);\r
- switch_time_exp_tz_name(tz, &tm_now, target_now);\r
- }\r
- } else {\r
- switch_time_exp_lt(&tm, target);\r
- switch_time_exp_lt(&tm_now, target_now);\r
- }\r
-\r
- switch (say_args->type) {\r
- case SST_CURRENT_DATE_TIME:\r
- say_date = say_time = 1;\r
- break;\r
- case SST_CURRENT_DATE:\r
- say_date = 1;\r
- break;\r
- case SST_CURRENT_TIME:\r
- say_time = 1;\r
- break;\r
- case SST_SHORT_DATE_TIME:\r
- say_time = 1;\r
- if (tm.tm_year != tm_now.tm_year) {\r
- say_date = 1;\r
- break;\r
- }\r
- if (tm.tm_yday == tm_now.tm_yday) {\r
- say_today = 1;\r
- break;\r
- }\r
- if (tm.tm_yday == tm_now.tm_yday - 1) {\r
- say_yesterday = 1;\r
- break;\r
- }\r
- if (tm.tm_yday >= tm_now.tm_yday - 5) {\r
- say_dow = 1;\r
- break;\r
- }\r
- if (tm.tm_mon != tm_now.tm_mon) {\r
- say_month = say_day = say_dow = 1;\r
- break;\r
- }\r
-\r
- say_month = say_day = say_dow = 1;\r
-\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- if (say_today) {\r
- say_file("time/today.wav");\r
- }\r
- if (say_yesterday) {\r
- say_file("time/yesterday.wav");\r
- }\r
- if (say_dow) {\r
- say_file("time/day-%d.wav", tm.tm_wday);\r
- }\r
-\r
- if (say_date) {\r
- say_year = say_month = say_day = say_dow = 1;\r
- say_today = say_yesterday = 0;\r
- }\r
-\r
- if (say_month) {\r
- say_file("time/mon-%d.wav", tm.tm_mon);\r
- }\r
- if (say_day) {\r
- say_num(tm.tm_mday, SSM_COUNTED);\r
- }\r
- if (say_year) {\r
- say_num(tm.tm_year + 1900, SSM_PRONOUNCED);\r
- }\r
-\r
- if (say_time) {\r
- int32_t hour = tm.tm_hour, pm = 0;\r
-\r
- if (say_date || say_today || say_yesterday || say_dow) {\r
- if (hour == 1) {\r
- say_file("time/at.wav");\r
- } else {\r
- say_file("time/ats.wav");\r
- }\r
- }\r
-\r
- if (hour > 12) {\r
- hour -= 12;\r
- pm = 1;\r
- } else if (hour == 12) {\r
- pm = 1;\r
- } else if (hour == 0) {\r
- hour = 12;\r
- pm = 0;\r
- }\r
-\r
- say_num(hour, SSM_PRONOUNCED);\r
-\r
- if (tm.tm_min) {\r
- say_file("currency/and.wav");\r
- say_num(tm.tm_min, SSM_PRONOUNCED);\r
- } else { \r
- say_file("time/oclock.wav");\r
- }\r
-\r
- say_file("time/%s.wav", pm ? "p-m" : "a-m");\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-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)\r
-{\r
- char sbuf[16] = ""; /* enough for 999,999,999,999.99 (w/o the commas or leading $) */\r
- char *dollars = NULL;\r
- char *cents = NULL;\r
-\r
- if (strlen(tosay) > 15 || !switch_strip_nonnumerics(tosay, sbuf, sizeof(sbuf)-1)) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n");\r
- return SWITCH_STATUS_GENERR;\r
- }\r
-\r
- dollars = sbuf;\r
-\r
- if ((cents = strchr(sbuf, '.'))) {\r
- *cents++ = '\0';\r
- if (strlen(cents) > 2) {\r
- cents[2] = '\0';\r
- }\r
- }\r
-\r
- /* If positive sign - skip over" */\r
- if (sbuf[0] == '+') {\r
- dollars++;\r
- }\r
-\r
- /* If negative say "negative" */\r
- if (sbuf[0] == '-') {\r
- say_file("currency/negative.wav");\r
- dollars++;\r
- }\r
-\r
- /* Say dollar amount */\r
- pt_say_general_count(session, dollars, say_args, args);\r
- if (atoi(dollars) == 1) {\r
- say_file("currency/dollar.wav");\r
- } else {\r
- say_file("currency/dollars.wav");\r
- }\r
-\r
- /* Say "and" */\r
- say_file("currency/and.wav");\r
-\r
- /* Say cents */\r
- if (cents) {\r
- pt_say_general_count(session, cents, say_args, args);\r
- if (atoi(cents) == 1) {\r
- say_file("currency/cent.wav");\r
- } else {\r
- say_file("currency/cents.wav");\r
- }\r
- } else {\r
- say_file("digits/0.wav");\r
- say_file("currency/cents.wav");\r
- }\r
-\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-\r
-static switch_status_t pt_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args)\r
-{\r
-\r
- switch_say_callback_t say_cb = NULL;\r
-\r
- switch (say_args->type) {\r
- case SST_NUMBER:\r
- case SST_ITEMS:\r
- case SST_PERSONS:\r
- case SST_MESSAGES:\r
- say_cb = pt_say_general_count;\r
- break;\r
- case SST_TIME_MEASUREMENT:\r
- case SST_CURRENT_DATE:\r
- case SST_CURRENT_TIME:\r
- case SST_CURRENT_DATE_TIME:\r
- case SST_SHORT_DATE_TIME:\r
- say_cb = pt_say_time;\r
- break;\r
- case SST_IP_ADDRESS:\r
- return switch_ivr_say_ip(session, tosay, pt_say_general_count, say_args, args);\r
- break;\r
- case SST_NAME_SPELLED:\r
- case SST_NAME_PHONETIC:\r
- return switch_ivr_say_spell(session, tosay, say_args, args);\r
- break;\r
- case SST_CURRENCY:\r
- say_cb = pt_say_money;\r
- break;\r
- default:\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown Say type=[%d]\n", say_args->type);\r
- break;\r
- }\r
-\r
- if (say_cb) {\r
- return say_cb(session, tosay, say_args, args);\r
- }\r
-\r
- return SWITCH_STATUS_FALSE;\r
-}\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_say_pt_load)\r
-{\r
- switch_say_interface_t *say_interface;\r
- /* connect my internal structure to the blank pointer passed to me */\r
- *module_interface = switch_loadable_module_create_module_interface(pool, modname);\r
- say_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE);\r
- say_interface->interface_name = "pt";\r
- say_interface->say_function = pt_say;\r
-\r
- /* indicate that the module should continue to be loaded */\r
- return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-/* For Emacs:\r
- * Local Variables:\r
- * mode:c\r
- * indent-tabs-mode:t\r
- * tab-width:4\r
- * c-basic-offset:4\r
- * End:\r
- * For VIM:\r
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:\r
+ } 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:
*/