LCOV - code coverage report
Current view: top level - src/tds - iconv.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 298 435 68.5 %
Date: 2024-03-23 09:12:27 Functions: 18 19 94.7 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns
       3             :  * Copyright (C) 2010  Frediano Ziglio
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU Library General Public
       7             :  * License as published by the Free Software Foundation; either
       8             :  * version 2 of the License, or (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * Library General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Library General Public
      16             :  * License along with this library; if not, write to the
      17             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18             :  * Boston, MA 02111-1307, USA.
      19             :  */
      20             : 
      21             : /**
      22             :  * \file
      23             :  * \brief Handle character conversions to/from server
      24             :  */
      25             : 
      26             : #include <config.h>
      27             : 
      28             : #include <stdarg.h>
      29             : #include <stdio.h>
      30             : #include <assert.h>
      31             : 
      32             : #if HAVE_STRING_H
      33             : #include <string.h>
      34             : #endif /* HAVE_STRING_H */
      35             : #if HAVE_ERRNO_H
      36             : #include <errno.h>
      37             : #endif
      38             : 
      39             : #include <freetds/tds.h>
      40             : #include <freetds/iconv.h>
      41             : #include <freetds/bool.h>
      42             : #include <freetds/bytes.h>
      43             : #if HAVE_ICONV
      44             : #include <iconv.h>
      45             : #endif
      46             : 
      47             : #define CHARSIZE(charset) ( ((charset)->min_bytes_per_char == (charset)->max_bytes_per_char )? \
      48             :                                 (charset)->min_bytes_per_char : 0 )
      49             : 
      50             : 
      51             : static int collate2charset(TDSCONNECTION * conn, TDS_UCHAR collate[5]);
      52             : static size_t skip_one_input_sequence(iconv_t cd, const TDS_ENCODING * charset, const char **input, size_t * input_size);
      53             : static int tds_iconv_info_init(TDSICONV * char_conv, int client_canonic, int server_canonic);
      54             : static bool tds_iconv_init(void);
      55             : static void _iconv_close(iconv_t * cd);
      56             : static void tds_iconv_info_close(TDSICONV * char_conv);
      57             : 
      58             : 
      59             : /**
      60             :  * \ingroup libtds
      61             :  * \defgroup conv Charset conversion
      62             :  * Convert between different charsets.
      63             :  */
      64             : 
      65             : #define TDS_ICONV_ENCODING_TABLES
      66             : #include <freetds/encodings.h>
      67             : 
      68             : /* this will contain real iconv names */
      69             : static const char *iconv_names[TDS_VECTOR_SIZE(canonic_charsets)];
      70             : static bool iconv_initialized = false;
      71             : static const char *ucs2name;
      72             : 
      73             : enum
      74             : { POS_ISO1, POS_UTF8, POS_UCS2LE, POS_UCS2BE };
      75             : 
      76             : static const struct {
      77             :         uint32_t len;
      78             :         /* this field must be aligned at least to 2 bytes */
      79             :         char data[12];
      80             : } test_strings[4] = {
      81             :         /* same string in required charsets */
      82             :         { 4, "Ao\xD3\xE5" },
      83             :         { 6, "Ao\xC3\x93\xC3\xA5" },
      84             :         { 8, "A\x00o\x000\xD3\x00\xE5\x00" },
      85             :         { 8, "\x00" "A\x00o\x000\xD3\x00\xE5" },
      86             : };
      87             : 
      88             : /**
      89             :  * Initialize charset searching for UTF-8, UCS-2 and ISO8859-1
      90             :  */
      91             : static bool
      92        1390 : tds_iconv_init(void)
      93             : {
      94             :         int i;
      95             :         iconv_t cd;
      96             : 
      97             :         /* first entries should be constants */
      98             :         assert(strcmp(canonic_charsets[POS_ISO1].name, "ISO-8859-1") == 0);
      99             :         assert(strcmp(canonic_charsets[POS_UTF8].name, "UTF-8") == 0);
     100             :         assert(strcmp(canonic_charsets[POS_UCS2LE].name, "UCS-2LE") == 0);
     101             :         assert(strcmp(canonic_charsets[POS_UCS2BE].name, "UCS-2BE") == 0);
     102             : 
     103             :         /* fast tests for GNU-iconv */
     104        1390 :         cd = tds_sys_iconv_open("ISO-8859-1", "UTF-8");
     105        1390 :         if (cd != (iconv_t) -1) {
     106        1390 :                 iconv_names[POS_ISO1] = "ISO-8859-1";
     107        1390 :                 iconv_names[POS_UTF8] = "UTF-8";
     108        1390 :                 tds_sys_iconv_close(cd);
     109             :         } else {
     110             : 
     111             :                 /* search names for ISO8859-1 and UTF-8 */
     112           0 :                 for (i = 0; iconv_aliases[i].alias; ++i) {
     113             :                         int j;
     114             : 
     115           0 :                         if (iconv_aliases[i].canonic != POS_ISO1)
     116           0 :                                 continue;
     117           0 :                         for (j = 0; iconv_aliases[j].alias; ++j) {
     118           0 :                                 if (iconv_aliases[j].canonic != POS_UTF8)
     119           0 :                                         continue;
     120             : 
     121           0 :                                 cd = tds_sys_iconv_open(iconv_aliases[i].alias, iconv_aliases[j].alias);
     122           0 :                                 if (cd != (iconv_t) -1) {
     123           0 :                                         iconv_names[POS_ISO1] = iconv_aliases[i].alias;
     124           0 :                                         iconv_names[POS_UTF8] = iconv_aliases[j].alias;
     125           0 :                                         tds_sys_iconv_close(cd);
     126           0 :                                         break;
     127             :                                 }
     128             :                         }
     129           0 :                         if (iconv_names[POS_ISO1])
     130             :                                 break;
     131             :                 }
     132             :                 /* required characters not found !!! */
     133           0 :                 if (!iconv_names[POS_ISO1]) {
     134           0 :                         tdsdump_log(TDS_DBG_ERROR, "iconv name for ISO-8859-1 not found\n");
     135             :                         return false;
     136             :                 }
     137             :         }
     138             : 
     139             :         /* now search for UCS-2 */
     140        1390 :         cd = tds_sys_iconv_open(iconv_names[POS_ISO1], "UCS-2LE");
     141        1390 :         if (cd != (iconv_t) -1) {
     142        1390 :                 iconv_names[POS_UCS2LE] = "UCS-2LE";
     143        1390 :                 tds_sys_iconv_close(cd);
     144             :         }
     145        1390 :         cd = tds_sys_iconv_open(iconv_names[POS_ISO1], "UCS-2BE");
     146        1390 :         if (cd != (iconv_t) -1) {
     147        1390 :                 iconv_names[POS_UCS2BE] = "UCS-2BE";
     148        1390 :                 tds_sys_iconv_close(cd);
     149             :         }
     150             : 
     151             :         /* long search needed ?? */
     152        1390 :         if (!iconv_names[POS_UCS2LE] || !iconv_names[POS_UCS2BE]) {
     153           0 :                 for (i = 0; iconv_aliases[i].alias; ++i) {
     154           0 :                         if (strncmp(canonic_charsets[iconv_aliases[i].canonic].name, "UCS-2", 5) != 0)
     155           0 :                                 continue;
     156             : 
     157           0 :                         cd = tds_sys_iconv_open(iconv_aliases[i].alias, iconv_names[POS_ISO1]);
     158           0 :                         if (cd != (iconv_t) -1) {
     159             :                                 char ib[1];
     160             :                                 char ob[4];
     161             :                                 size_t il, ol;
     162             :                                 ICONV_CONST char *pib;
     163             :                                 char *pob;
     164           0 :                                 int byte_sequence = 0;
     165             : 
     166             :                                 /* try to convert 'A' and check result */
     167           0 :                                 ib[0] = 0x41;
     168           0 :                                 pib = ib;
     169           0 :                                 pob = ob;
     170           0 :                                 il = 1;
     171           0 :                                 ol = 4;
     172           0 :                                 ob[0] = ob[1] = 0;
     173           0 :                                 if (tds_sys_iconv(cd, &pib, &il, &pob, &ol) != (size_t) - 1) {
     174             :                                         /* byte order sequence ?? */
     175           0 :                                         if (ol == 0) {
     176           0 :                                                 ob[0] = ob[2];
     177           0 :                                                 byte_sequence = 1;
     178             :                                                 /* TODO save somewhere */
     179             :                                         }
     180             : 
     181             :                                         /* save name without sequence (if present) */
     182           0 :                                         if (ob[0])
     183           0 :                                                 il = POS_UCS2LE;
     184             :                                         else
     185           0 :                                                 il = POS_UCS2BE;
     186           0 :                                         if (!iconv_names[il] || !byte_sequence)
     187           0 :                                                 iconv_names[il] = iconv_aliases[i].alias;
     188             :                                 }
     189           0 :                                 tds_sys_iconv_close(cd);
     190             :                         }
     191             :                 }
     192             :         }
     193             :         /* we need a UCS-2 (big endian or little endian) */
     194        1390 :         if (!iconv_names[POS_UCS2LE] && !iconv_names[POS_UCS2BE]) {
     195           0 :                 tdsdump_log(TDS_DBG_ERROR, "iconv name for UCS-2 not found\n");
     196             :                 return false;
     197             :         }
     198             : 
     199        1390 :         ucs2name = iconv_names[POS_UCS2LE] ? iconv_names[POS_UCS2LE] : iconv_names[POS_UCS2BE];
     200             : 
     201        6950 :         for (i = 0; i < 4; ++i)
     202        5576 :                 tdsdump_log(TDS_DBG_INFO1, "local name for %s is %s\n", canonic_charsets[i].name,
     203          16 :                             iconv_names[i] ? iconv_names[i] : "(null)");
     204             : 
     205             :         /* base conversions checks */
     206       22240 :         for (i = 0; i < 4 * 4; ++i) {
     207       22240 :                 const int from = i / 4;
     208       22240 :                 const int to = i % 4;
     209             :                 char ob[16];
     210             :                 size_t il, ol;
     211             :                 ICONV_CONST char *pib;
     212             :                 char *pob;
     213             :                 size_t res;
     214             : 
     215       22240 :                 if (!iconv_names[from] || !iconv_names[to])
     216           0 :                         continue;
     217       22240 :                 cd = tds_sys_iconv_open(iconv_names[to], iconv_names[from]);
     218       22240 :                 if (cd == (iconv_t) -1) {
     219           0 :                         tdsdump_log(TDS_DBG_ERROR, "iconv_open(%s, %s) failed\n", iconv_names[to], iconv_names[from]);
     220           0 :                         return false;
     221             :                 }
     222             : 
     223       22240 :                 pib = (ICONV_CONST char *) test_strings[from].data;
     224       22240 :                 il = test_strings[from].len;
     225       22240 :                 pob = ob;
     226       22240 :                 ol = sizeof(ob);
     227       22240 :                 res = tds_sys_iconv(cd, &pib, &il, &pob, &ol);
     228       22240 :                 tds_sys_iconv_close(cd);
     229             : 
     230       22240 :                 if (res != 0
     231       22240 :                     || sizeof(ob) - ol != test_strings[to].len
     232       22240 :                     || memcmp(ob, test_strings[to].data, test_strings[to].len) != 0) {
     233           0 :                         tdsdump_log(TDS_DBG_ERROR, "iconv(%s, %s) failed res %d\n", iconv_names[to], iconv_names[from], (int) res);
     234           0 :                         tdsdump_log(TDS_DBG_ERROR, "len %d\n", (int) (sizeof(ob) - ol));
     235             :                         return false;
     236             :                 }
     237             :         }
     238             : 
     239             :         /* success (it should always occurs) */
     240             :         return true;
     241             : }
     242             : 
     243             : /**
     244             :  * Get iconv name given canonic
     245             :  */
     246             : static const char *
     247        2398 : tds_set_iconv_name(int charset)
     248             : {
     249             :         int i;
     250             :         iconv_t cd;
     251             :         const char *name;
     252             : 
     253        2398 :         assert(iconv_initialized);
     254             : 
     255             :         /* try using canonic name and UTF-8 and UCS2 */
     256        2398 :         name = canonic_charsets[charset].name;
     257        2398 :         cd = tds_sys_iconv_open(iconv_names[POS_UTF8], name);
     258        2398 :         if (cd != (iconv_t) -1)
     259             :                 goto found;
     260           0 :         cd = tds_sys_iconv_open(ucs2name, name);
     261           0 :         if (cd != (iconv_t) -1)
     262             :                 goto found;
     263             : 
     264             :         /* try all alternatives */
     265           0 :         for (i = 0; iconv_aliases[i].alias; ++i) {
     266           0 :                 if (iconv_aliases[i].canonic != charset)
     267           0 :                         continue;
     268             : 
     269           0 :                 name = iconv_aliases[i].alias;
     270           0 :                 cd = tds_sys_iconv_open(iconv_names[POS_UTF8], name);
     271           0 :                 if (cd != (iconv_t) -1)
     272             :                         goto found;
     273           0 :                 cd = tds_sys_iconv_open(ucs2name, name);
     274           0 :                 if (cd != (iconv_t) -1)
     275             :                         goto found;
     276             :         }
     277             : 
     278             :         /* charset not found, pretend it's ISO 8859-1 */
     279           0 :         iconv_names[charset] = canonic_charsets[POS_ISO1].name;
     280           0 :         return NULL;
     281             : 
     282        2398 : found:
     283        2398 :         iconv_names[charset] = name;
     284        2398 :         tds_sys_iconv_close(cd);
     285        2398 :         return name;
     286             : }
     287             : 
     288             : static void
     289             : tds_iconv_reset(TDSICONV *conv)
     290             : {
     291             :         /*
     292             :          * (min|max)_bytes_per_char can be used to divide
     293             :          * so init to safe values
     294             :          */
     295       18268 :         conv->to.charset.min_bytes_per_char = 1;
     296       18268 :         conv->to.charset.max_bytes_per_char = 1;
     297       18268 :         conv->from.charset.min_bytes_per_char = 1;
     298       18268 :         conv->from.charset.max_bytes_per_char = 1;
     299             : 
     300       18268 :         conv->to.charset.name = conv->from.charset.name = "";
     301       18268 :         conv->to.charset.canonic = conv->from.charset.canonic = 0;
     302       18268 :         conv->to.cd = (iconv_t) -1;
     303       18268 :         conv->from.cd = (iconv_t) -1;
     304             : }
     305             : 
     306             : /**
     307             :  * Allocate iconv stuff
     308             :  * \return 0 for success
     309             :  */
     310             : int
     311        3570 : tds_iconv_alloc(TDSCONNECTION * conn)
     312             : {
     313             :         int i;
     314             :         TDSICONV *char_conv;
     315             : 
     316        3570 :         assert(!conn->char_convs);
     317        3570 :         if (!(conn->char_convs = tds_new(TDSICONV *, initial_char_conv_count + 1)))
     318             :                 return 1;
     319        3570 :         char_conv = tds_new0(TDSICONV, initial_char_conv_count);
     320        3570 :         if (!char_conv) {
     321           0 :                 TDS_ZERO_FREE(conn->char_convs);
     322           0 :                 return 1;
     323             :         }
     324        3570 :         conn->char_conv_count = initial_char_conv_count + 1;
     325             : 
     326       10710 :         for (i = 0; i < initial_char_conv_count; ++i) {
     327        7140 :                 conn->char_convs[i] = &char_conv[i];
     328       14280 :                 tds_iconv_reset(&char_conv[i]);
     329             :         }
     330             : 
     331             :         /* chardata is just a pointer to another iconv info */
     332        3570 :         conn->char_convs[initial_char_conv_count] = conn->char_convs[client2server_chardata];
     333             : 
     334        3570 :         return 0;
     335             : }
     336             : 
     337             : /**
     338             :  * \addtogroup conv
     339             :  * @{ 
     340             :  * Set up the initial iconv conversion descriptors.
     341             :  * When the socket is allocated, three TDSICONV structures are attached to iconv.  
     342             :  * They have fixed meanings:
     343             :  *      \li 0. Client <-> UCS-2 (client2ucs2)
     344             :  *      \li 1. Client <-> server single-byte charset (client2server_chardata)
     345             :  *
     346             :  * Other designs that use less data are possible, but these three conversion needs are 
     347             :  * very often needed.  By reserving them, we avoid searching the array for our most common purposes.
     348             :  *
     349             :  * To solve different iconv names and portability problems FreeTDS maintains 
     350             :  * a list of aliases each charset.  
     351             :  * 
     352             :  * First we discover the names of our minimum required charsets (UTF-8, ISO8859-1 and UCS2).  
     353             :  * Later, as and when it's needed, we try to discover others.
     354             :  *
     355             :  * There is one list of canonic names (GNU iconv names) and two sets of aliases
     356             :  * (one for other iconv implementations and another for Sybase). For every
     357             :  * canonic charset name we cache the iconv name found during discovery. 
     358             :  */
     359             : TDSRET
     360        3446 : tds_iconv_open(TDSCONNECTION * conn, const char *charset, int use_utf16)
     361             : {
     362             :         static const char UCS_2LE[] = "UCS-2LE";
     363             :         int canonic;
     364        3446 :         int canonic_charset = tds_canonical_charset(charset);
     365        3446 :         int canonic_env_charset = conn->env.charset ? tds_canonical_charset(conn->env.charset) : -1;
     366             :         int fOK;
     367             : 
     368        3446 :         TDS_ENCODING *client = &conn->char_convs[client2ucs2]->from.charset;
     369        3446 :         TDS_ENCODING *server = &conn->char_convs[client2ucs2]->to.charset;
     370             : 
     371        3446 :         tdsdump_log(TDS_DBG_FUNC, "tds_iconv_open(%p, %s, %d)\n", conn, charset, use_utf16);
     372             : 
     373             :         /* TDS 5.0 support only UTF-16 encodings */
     374        3446 :         if (IS_TDS50(conn))
     375         670 :                 use_utf16 = true;
     376             : 
     377             :         /* initialize */
     378        3446 :         if (!iconv_initialized) {
     379        1390 :                 if (!tds_iconv_init()) {
     380           0 :                         tdsdump_log(TDS_DBG_ERROR, "error: tds_iconv_init() failed; "
     381             :                                                    "try using GNU libiconv library\n");
     382             :                         return TDS_FAIL;
     383             :                 }
     384        1390 :                 iconv_initialized = true;
     385             :         }
     386             : 
     387             :         /* 
     388             :          * Client <-> UCS-2 (client2ucs2)
     389             :          */
     390        3446 :         tdsdump_log(TDS_DBG_FUNC, "setting up conversions for client charset \"%s\"\n", charset);
     391             : 
     392        3446 :         tdsdump_log(TDS_DBG_FUNC, "preparing iconv for \"%s\" <-> \"%s\" conversion\n", charset, UCS_2LE);
     393             : 
     394        3446 :         fOK = 0;
     395        3446 :         if (use_utf16) {
     396        2752 :                 canonic = TDS_CHARSET_UTF_16LE;
     397        2752 :                 fOK = tds_iconv_info_init(conn->char_convs[client2ucs2], canonic_charset, canonic);
     398             :         }
     399        2752 :         if (!fOK) {
     400         694 :                 canonic = TDS_CHARSET_UCS_2LE;
     401         694 :                 fOK = tds_iconv_info_init(conn->char_convs[client2ucs2], canonic_charset, canonic);
     402             :         }
     403        3446 :         if (!fOK)
     404             :                 return TDS_FAIL;
     405             : 
     406             :         /* 
     407             :          * How many UTF-8 bytes we need is a function of what the input character set is.
     408             :          * TODO This could definitely be more sophisticated, but it deals with the common case.
     409             :          */
     410        3446 :         if (client->min_bytes_per_char == 1 && client->max_bytes_per_char == 4 && server->max_bytes_per_char == 1) {
     411             :                 /* ie client is UTF-8 and server is ISO-8859-1 or variant. */
     412           0 :                 client->max_bytes_per_char = 3;
     413             :         }
     414             : 
     415             :         /* 
     416             :          * Client <-> server single-byte charset
     417             :          * TODO: the server hasn't reported its charset yet, so this logic can't work here.  
     418             :          *       not sure what to do about that yet.  
     419             :          */
     420        3446 :         conn->char_convs[client2server_chardata]->flags = TDS_ENCODING_MEMCPY;
     421        3446 :         if (canonic_env_charset >= 0) {
     422           0 :                 tdsdump_log(TDS_DBG_FUNC, "preparing iconv for \"%s\" <-> \"%s\" conversion\n", charset, conn->env.charset);
     423           0 :                 fOK = tds_iconv_info_init(conn->char_convs[client2server_chardata], canonic_charset, canonic_env_charset);
     424           0 :                 if (!fOK)
     425             :                         return TDS_FAIL;
     426             :         } else {
     427        3446 :                 conn->char_convs[client2server_chardata]->from.charset = canonic_charsets[canonic_charset];
     428        3446 :                 conn->char_convs[client2server_chardata]->to.charset = canonic_charsets[canonic_charset];
     429             :         }
     430             : 
     431        3446 :         tdsdump_log(TDS_DBG_FUNC, "tds_iconv_open: done\n");
     432             :         return TDS_SUCCESS;
     433             : }
     434             : 
     435             : /**
     436             :  * Open iconv descriptors to convert between character sets (both directions).
     437             :  * 1.  Look up the canonical names of the character sets.
     438             :  * 2.  Look up their widths.
     439             :  * 3.  Ask iconv to open a conversion descriptor.
     440             :  * 4.  Fail if any of the above offer any resistance.  
     441             :  * \remarks The charset names written to \a iconv will be the canonical names, 
     442             :  *          not necessarily the names passed in. 
     443             :  */
     444             : static int
     445        7772 : tds_iconv_info_init(TDSICONV * char_conv, int client_canonical, int server_canonical)
     446             : {
     447        7772 :         TDS_ENCODING *client = &char_conv->from.charset;
     448        7772 :         TDS_ENCODING *server = &char_conv->to.charset;
     449             : 
     450        7772 :         assert(char_conv->to.cd == (iconv_t) -1);
     451        7772 :         assert(char_conv->from.cd == (iconv_t) -1);
     452             : 
     453        7772 :         if (client_canonical < 0) {
     454           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_info_init: client charset name \"%d\" invalid\n", client_canonical);
     455             :                 return 0;
     456             :         }
     457             : 
     458        7772 :         if (server_canonical < 0) {
     459           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_info_init: server charset name \"%d\" invalid\n", server_canonical);
     460             :                 return 0;
     461             :         }
     462             : 
     463        7772 :         *client = canonic_charsets[client_canonical];
     464        7772 :         *server = canonic_charsets[server_canonical];
     465             : 
     466             :         /* special case, same charset, no conversion */
     467        7772 :         if (client_canonical == server_canonical) {
     468          90 :                 char_conv->to.cd = (iconv_t) -1;
     469          90 :                 char_conv->from.cd = (iconv_t) -1;
     470          90 :                 char_conv->flags = TDS_ENCODING_MEMCPY;
     471          90 :                 return 1;
     472             :         }
     473             : 
     474        7682 :         char_conv->flags = 0;
     475             : 
     476             :         /* get iconv names */
     477        7682 :         if (!iconv_names[client_canonical]) {
     478           0 :                 if (!tds_set_iconv_name(client_canonical)) {
     479           0 :                         tdsdump_log(TDS_DBG_FUNC, "Charset %d not supported by iconv, using \"%s\" instead\n",
     480             :                                                   client_canonical, iconv_names[client_canonical]);
     481             :                 }
     482             :         }
     483             :         
     484        7682 :         if (!iconv_names[server_canonical]) {
     485        2398 :                 if (!tds_set_iconv_name(server_canonical)) {
     486           0 :                         tdsdump_log(TDS_DBG_FUNC, "Charset %d not supported by iconv, using \"%s\" instead\n",
     487             :                                                   server_canonical, iconv_names[server_canonical]);
     488             :                 }
     489             :         }
     490             : 
     491        7682 :         char_conv->to.cd = tds_sys_iconv_open(iconv_names[server_canonical], iconv_names[client_canonical]);
     492        7682 :         if (char_conv->to.cd == (iconv_t) -1) {
     493           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_info_init: cannot convert \"%s\"->\"%s\"\n", client->name, server->name);
     494             :         }
     495             : 
     496        7682 :         char_conv->from.cd = tds_sys_iconv_open(iconv_names[client_canonical], iconv_names[server_canonical]);
     497        7682 :         if (char_conv->from.cd == (iconv_t) -1) {
     498           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_info_init: cannot convert \"%s\"->\"%s\"\n", server->name, client->name);
     499             :         }
     500             : 
     501             :         /* TODO, do some optimizations like UCS2 -> UTF8 min,max = 2,2 (UCS2) and 1,4 (UTF8) */
     502             : 
     503             :         /* tdsdump_log(TDS_DBG_FUNC, "tds_iconv_info_init: converting \"%s\"->\"%s\"\n", client->name, server->name); */
     504             : 
     505             :         return 1;
     506             : }
     507             : 
     508             : 
     509             : static void
     510             : _iconv_close(iconv_t * cd)
     511             : {
     512             :         static const iconv_t invalid = (iconv_t) -1;
     513             : 
     514       30072 :         if (*cd != invalid) {
     515       15364 :                 tds_sys_iconv_close(*cd);
     516       15364 :                 *cd = invalid;
     517             :         }
     518             : }
     519             : 
     520             : static void
     521       15036 : tds_iconv_info_close(TDSICONV * char_conv)
     522             : {
     523       30072 :         _iconv_close(&char_conv->to.cd);
     524       30072 :         _iconv_close(&char_conv->from.cd);
     525       15036 : }
     526             : 
     527             : void
     528           0 : tds_iconv_close(TDSCONNECTION * conn)
     529             : {
     530             :         int i;
     531             : 
     532       15036 :         for (i = 0; i < conn->char_conv_count; ++i)
     533       15036 :                 tds_iconv_info_close(conn->char_convs[i]);
     534           0 : }
     535             : 
     536             : #define CHUNK_ALLOC 4
     537             : 
     538             : void
     539        3570 : tds_iconv_free(TDSCONNECTION * conn)
     540             : {
     541             :         int i;
     542             : 
     543        3570 :         if (!conn->char_convs)
     544             :                 return;
     545        3570 :         tds_iconv_close(conn);
     546             : 
     547        3570 :         free(conn->char_convs[0]);
     548        6352 :         for (i = initial_char_conv_count + 1; i < conn->char_conv_count; i += CHUNK_ALLOC)
     549        2782 :                 free(conn->char_convs[i]);
     550        3570 :         TDS_ZERO_FREE(conn->char_convs);
     551        3570 :         conn->char_conv_count = 0;
     552             : }
     553             : 
     554             : static void
     555             : tds_iconv_err(TDSSOCKET *tds, int err)
     556             : {
     557        5081 :         if (tds)
     558        3313 :                 tdserror(tds_get_ctx(tds), tds, err, 0);
     559             : }
     560             : 
     561             : /** 
     562             :  * Wrapper around iconv(3).  Same parameters, with slightly different behavior.
     563             :  * \param tds state information for the socket and the TDS protocol
     564             :  * \param io Enumerated value indicating whether the data are being sent to or received from the server. 
     565             :  * \param conv information about the encodings involved, including the iconv(3) conversion descriptors. 
     566             :  * \param inbuf address of pointer to the input buffer of data to be converted.  
     567             :  * \param inbytesleft address of count of bytes in \a inbuf.
     568             :  * \param outbuf address of pointer to the output buffer.  
     569             :  * \param outbytesleft address of count of bytes in \a outbuf.
     570             :  * \retval number of irreversible conversions performed.  -1 on error, see iconv(3) documentation for 
     571             :  * a description of the possible values of \e errno.  
     572             :  * \remarks Unlike iconv(3), none of the arguments can be nor point to NULL.  Like iconv(3), all pointers will 
     573             :  *      be updated.  Success is signified by a nonnegative return code and \a *inbytesleft == 0.  
     574             :  *      If the conversion descriptor in \a iconv is -1 or NULL, \a inbuf is copied to \a outbuf, 
     575             :  *      and all parameters updated accordingly. 
     576             :  * 
     577             :  *      If a character in \a inbuf cannot be converted because no such cbaracter exists in the
     578             :  *      \a outbuf character set, we emit messages similar to the ones Sybase emits when it fails such a conversion. 
     579             :  *      The message varies depending on the direction of the data.  
     580             :  *      On a read error, we emit Msg 2403, Severity 16 (EX_INFO):
     581             :  *              "WARNING! Some character(s) could not be converted into client's character set. 
     582             :  *                      Unconverted bytes were changed to question marks ('?')."
     583             :  *      On a write error we emit Msg 2402, Severity 16 (EX_USER):
     584             :  *              "Error converting client characters into server's character set. Some character(s) could not be converted."
     585             :  *        and return an error code.  Client libraries relying on this routine should reflect an error back to the application.  
     586             :  *
     587             :  * \todo Check for variable multibyte non-UTF-8 input character set.  
     588             :  * \todo Use more robust error message generation.  
     589             :  * \todo For reads, cope with \a outbuf encodings that don't have the equivalent of an ASCII '?'.  
     590             :  * \todo Support alternative to '?' for the replacement character.  
     591             :  */
     592             : size_t
     593      784877 : tds_iconv(TDSSOCKET * tds, TDSICONV * conv, TDS_ICONV_DIRECTION io,
     594             :           const char **inbuf, size_t * inbytesleft, char **outbuf, size_t * outbytesleft)
     595             : {
     596             :         static const iconv_t invalid = (iconv_t) -1;
     597      784877 :         TDSICONVDIR *from = NULL;
     598      784877 :         TDSICONVDIR *to = NULL;
     599             : 
     600      784877 :         iconv_t error_cd = invalid;
     601             : 
     602      784877 :         char quest_mark[] = "?";      /* best to leave non-const; implementations vary */
     603             :         ICONV_CONST char *pquest_mark;
     604             :         size_t lquest_mark;
     605             :         size_t irreversible;
     606             :         size_t one_character;
     607      784877 :         bool eilseq_raised = false;
     608             :         int conv_errno;
     609             :         /* cast away const-ness */
     610      784877 :         TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &conv->suppress;
     611             : 
     612      784877 :         assert(inbuf && inbytesleft && outbuf && outbytesleft);
     613             : 
     614             :         /* if empty there's nothing to return.
     615             :          * This fix case with some iconv implementation that does
     616             :          * not handle *inbuf == NULL and *inbytesleft == 0 as
     617             :          * empty strings
     618             :          */
     619      784877 :         if (*inbytesleft == 0)
     620             :                 return 0;
     621             : 
     622      784599 :         switch (io) {
     623      130101 :         case to_server:
     624      130101 :                 from = &conv->from;
     625      130101 :                 to = &conv->to;
     626      130101 :                 break;
     627      654498 :         case to_client:
     628      654498 :                 from = &conv->to;
     629      654498 :                 to = &conv->from;
     630      654498 :                 break;
     631           0 :         default:
     632           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv: unable to determine if %d means in or out.  \n", io);
     633           0 :                 assert(io == to_server || io == to_client);
     634             :                 break;
     635             :         }
     636             : 
     637             :         /* silly case, memcpy */
     638      784599 :         if (conv->flags & TDS_ENCODING_MEMCPY || to->cd == invalid) {
     639      156146 :                 size_t len = *inbytesleft < *outbytesleft ? *inbytesleft : *outbytesleft;
     640             : 
     641      156146 :                 memcpy(*outbuf, *inbuf, len);
     642      156146 :                 conv_errno = *inbytesleft > *outbytesleft ? E2BIG : 0;
     643      156146 :                 *inbytesleft -= len;
     644      156146 :                 *outbytesleft -= len;
     645      156146 :                 *inbuf += len;
     646      156146 :                 *outbuf += len;
     647      156146 :                 errno = conv_errno;
     648      156146 :                 return conv_errno ? (size_t) -1 : 0;
     649             :         }
     650             : 
     651             :         /*
     652             :          * Call iconv() as many times as necessary, until we reach the end of input or exhaust output.  
     653             :          */
     654             :         for (;;) {
     655     1076552 :                 conv_errno = 0;
     656     1076552 :                 irreversible = tds_sys_iconv(to->cd, (ICONV_CONST char **) inbuf, inbytesleft, outbuf, outbytesleft);
     657             : 
     658             :                 /* iconv success, return */
     659     1076552 :                 if (irreversible != (size_t) - 1) {
     660             :                         /* here we detect end of conversion and try to reset shift state */
     661      890010 :                         if (inbuf) {
     662             :                                 /*
     663             :                                  * if inbuf or *inbuf is NULL iconv reset the shift state.
     664             :                                  * Note that setting inbytesleft to NULL can cause core so don't do it!
     665             :                                  */
     666      445005 :                                 inbuf = NULL;
     667      445005 :                                 continue;
     668             :                         }
     669             :                         break;
     670             :                 }
     671             : 
     672             :                 /* save errno, other function could change its value */
     673      186542 :                 conv_errno = errno;
     674             : 
     675      186542 :                 if (conv_errno == EILSEQ)
     676        8936 :                         eilseq_raised = true;
     677             : 
     678      186542 :                 if (conv_errno != EILSEQ || io != to_client || !inbuf)
     679             :                         break;
     680             :                 /* 
     681             :                  * Invalid input sequence encountered reading from server. 
     682             :                  * Skip one input sequence, adjusting pointers. 
     683             :                  */
     684        3094 :                 one_character = skip_one_input_sequence(to->cd, &from->charset, inbuf, inbytesleft);
     685             : 
     686        3094 :                 if (!one_character)
     687             :                         break;
     688             : 
     689             :                 /* 
     690             :                  * To replace invalid input with '?', we have to convert a UTF-8 '?' into the output character set.  
     691             :                  * In unimaginably weird circumstances, this might be impossible.
     692             :                  * We use UTF-8 instead of ASCII because some implementations 
     693             :                  * do not convert singlebyte <-> singlebyte.
     694             :                  */
     695        3094 :                 if (error_cd == invalid) {
     696        1550 :                         error_cd = tds_sys_iconv_open(to->charset.name, iconv_names[POS_UTF8]);
     697        1550 :                         if (error_cd == invalid) {
     698             :                                 break;  /* what to do? */
     699             :                         }
     700             :                 }
     701             : 
     702        3094 :                 lquest_mark = 1;
     703        3094 :                 pquest_mark = quest_mark;
     704             : 
     705        3094 :                 irreversible = tds_sys_iconv(error_cd, &pquest_mark, &lquest_mark, outbuf, outbytesleft);
     706             : 
     707        3094 :                 if (irreversible == (size_t) - 1)
     708             :                         break;
     709             : 
     710        3094 :                 if (!*inbytesleft)
     711             :                         break;
     712             :         }
     713             : 
     714      628453 :         if (eilseq_raised && !suppress->eilseq) {
     715             :                 /* invalid multibyte input sequence encountered */
     716        5081 :                 if (io == to_client) {
     717        1544 :                         if (irreversible == (size_t) - 1) {
     718             :                                 tds_iconv_err(tds, TDSEICONV2BIG);
     719             :                         } else {
     720        1544 :                                 tds_iconv_err(tds, TDSEICONVI);
     721        1544 :                                 conv_errno = 0;
     722             :                         }
     723             :                 } else {
     724             :                         tds_iconv_err(tds, TDSEICONVO);
     725             :                 }
     726        5081 :                 suppress->eilseq = 1;
     727             :         }
     728             : 
     729      626909 :         switch (conv_errno) {
     730        2376 :         case EINVAL:            /* incomplete multibyte sequence is encountered */
     731        2376 :                 if (suppress->einval)
     732             :                         break;
     733             :                 /* in chunk conversion this can mean we end a chunk inside a character */
     734           0 :                 tds_iconv_err(tds, TDSEICONVAVAIL);
     735           0 :                 suppress->einval = 1;
     736           0 :                 break;
     737      175230 :         case E2BIG:             /* output buffer has no more room */
     738      175230 :                 if (suppress->e2big)
     739             :                         break;
     740           0 :                 tds_iconv_err(tds, TDSEICONVIU);
     741           0 :                 suppress->e2big = 1;
     742           0 :                 break;
     743             :         default:
     744             :                 break;
     745             :         }
     746             : 
     747      806059 :         if (error_cd != invalid) {
     748        1550 :                 tds_sys_iconv_close(error_cd);
     749             :         }
     750             : 
     751      628453 :         errno = conv_errno;
     752      628453 :         return irreversible;
     753             : }
     754             : 
     755             : /**
     756             :  * Get a iconv info structure, allocate and initialize if needed
     757             :  */
     758             : TDSICONV *
     759       20004 : tds_iconv_get_info(TDSCONNECTION * conn, int canonic_client, int canonic_server)
     760             : {
     761             :         TDSICONV *info;
     762             :         int i;
     763             : 
     764             :         /* search a charset from already allocated charsets */
     765       61270 :         for (i = conn->char_conv_count; --i >= initial_char_conv_count;)
     766       36940 :                 if (canonic_client == conn->char_convs[i]->from.charset.canonic
     767       28130 :                     && canonic_server == conn->char_convs[i]->to.charset.canonic)
     768             :                         return conn->char_convs[i];
     769             : 
     770             :         /* allocate a new iconv structure */
     771        4326 :         if (conn->char_conv_count % CHUNK_ALLOC == ((initial_char_conv_count + 1) % CHUNK_ALLOC)) {
     772             :                 TDSICONV **p;
     773             :                 TDSICONV *infos;
     774             : 
     775        2782 :                 infos = tds_new(TDSICONV, CHUNK_ALLOC);
     776        2782 :                 if (!infos)
     777             :                         return NULL;
     778        2782 :                 p = (TDSICONV **) realloc(conn->char_convs, sizeof(TDSICONV *) * (conn->char_conv_count + CHUNK_ALLOC));
     779        2782 :                 if (!p) {
     780           0 :                         free(infos);
     781           0 :                         return NULL;
     782             :                 }
     783        2782 :                 conn->char_convs = p;
     784        2782 :                 memset(infos, 0, sizeof(TDSICONV) * CHUNK_ALLOC);
     785       13910 :                 for (i = 0; i < CHUNK_ALLOC; ++i) {
     786       11128 :                         conn->char_convs[i + conn->char_conv_count] = &infos[i];
     787       22256 :                         tds_iconv_reset(&infos[i]);
     788             :                 }
     789             :         }
     790        4326 :         info = conn->char_convs[conn->char_conv_count++];
     791             : 
     792             :         /* init */
     793        4326 :         if (tds_iconv_info_init(info, canonic_client, canonic_server))
     794             :                 return info;
     795             : 
     796           0 :         tds_iconv_info_close(info);
     797           0 :         --conn->char_conv_count;
     798           0 :         return NULL;
     799             : }
     800             : 
     801             : TDSICONV *
     802          16 : tds_iconv_get(TDSCONNECTION * conn, const char *client_charset, const char *server_charset)
     803             : {
     804          16 :         int canonic_client_charset_num = tds_canonical_charset(client_charset);
     805          16 :         int canonic_server_charset_num = tds_canonical_charset(server_charset);
     806             : 
     807          16 :         if (canonic_client_charset_num < 0) {
     808           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_get: what is charset \"%s\"?\n", client_charset);
     809             :                 return NULL;
     810             :         }
     811          16 :         if (canonic_server_charset_num < 0) {
     812           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_iconv_get: what is charset \"%s\"?\n", server_charset);
     813             :                 return NULL;
     814             :         }
     815             : 
     816          16 :         return tds_iconv_get_info(conn, canonic_client_charset_num, canonic_server_charset_num);
     817             : }
     818             : 
     819             : /* change singlebyte conversions according to server */
     820             : static void
     821        7540 : tds_srv_charset_changed_num(TDSCONNECTION * conn, int canonic_charset_num)
     822             : {
     823        7540 :         TDSICONV *char_conv = conn->char_convs[client2server_chardata];
     824             : 
     825        7540 :         if (IS_TDS7_PLUS(conn) && canonic_charset_num == TDS_CHARSET_ISO_8859_1)
     826           0 :                 canonic_charset_num = TDS_CHARSET_CP1252;
     827             : 
     828        7540 :         tdsdump_log(TDS_DBG_FUNC, "setting server single-byte charset to \"%s\"\n", canonic_charsets[canonic_charset_num].name);
     829             : 
     830        7540 :         if (canonic_charset_num == char_conv->to.charset.canonic)
     831             :                 return;
     832             : 
     833             :         /* find and set conversion */
     834        2276 :         char_conv = tds_iconv_get_info(conn, conn->char_convs[client2ucs2]->from.charset.canonic, canonic_charset_num);
     835        2276 :         if (char_conv)
     836        2276 :                 conn->char_convs[client2server_chardata] = char_conv;
     837             : }
     838             : 
     839             : void
     840        3592 : tds_srv_charset_changed(TDSCONNECTION * conn, const char *charset)
     841             : {
     842        3592 :         int n = tds_canonical_charset(charset);
     843             : 
     844             :         /* ignore request to change to unknown charset */
     845        3592 :         if (n < 0) {
     846           0 :                 tdsdump_log(TDS_DBG_FUNC, "tds_srv_charset_changed: what is charset \"%s\"?\n", charset);
     847             :                 return;
     848             :         }
     849             : 
     850        3592 :         tds_srv_charset_changed_num(conn, n);
     851             : }
     852             : 
     853             : /* change singlebyte conversions according to server */
     854             : void
     855        3948 : tds7_srv_charset_changed(TDSCONNECTION * conn, TDS_UCHAR collation[5])
     856             : {
     857        3948 :         tds_srv_charset_changed_num(conn, collate2charset(conn, collation));
     858        3948 : }
     859             : 
     860             : /**
     861             :  * Move the input sequence pointer to the next valid position.
     862             :  * Used when an input character cannot be converted.  
     863             :  * \returns number of bytes to skip.
     864             :  */
     865             : /* FIXME possible buffer reading overflow ?? */
     866             : static size_t
     867        3094 : skip_one_input_sequence(iconv_t cd, const TDS_ENCODING * charset, const char **input, size_t * input_size)
     868             : {
     869        3094 :         unsigned charsize = CHARSIZE(charset);
     870             :         char ib[16];
     871             :         char ob[16];
     872             :         ICONV_CONST char *pib;
     873             :         char *pob;
     874             :         size_t il, ol, l;
     875             :         iconv_t cd2;
     876             : 
     877             : 
     878             :         /* usually fixed size and UTF-8 do not have state, so do not reset it */
     879           0 :         if (charsize)
     880             :                 goto skip_charsize;
     881             : 
     882        3094 :         if (0 == strcmp(charset->name, "UTF-8")) {
     883             :                 /*
     884             :                  * Deal with UTF-8.  
     885             :                  * bytes | bits | representation
     886             :                  *     1 |    7 | 0vvvvvvv
     887             :                  *     2 |   11 | 110vvvvv 10vvvvvv
     888             :                  *     3 |   16 | 1110vvvv 10vvvvvv 10vvvvvv
     889             :                  *     4 |   21 | 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv
     890             :                  */
     891        3088 :                 int c = **input;
     892             : 
     893        3088 :                 c = c & (c >> 1);
     894             :                 do {
     895        6176 :                         ++charsize;
     896        6176 :                 } while ((c <<= 1) & 0x80);
     897             :                 goto skip_charsize;
     898             :         }
     899             : 
     900             :         /* handle state encoding */
     901             : 
     902             :         /* extract state from iconv */
     903           6 :         pob = ib;
     904           6 :         ol = sizeof(ib);
     905           6 :         tds_sys_iconv(cd, NULL, NULL, &pob, &ol);
     906             : 
     907             :         /* init destination conversion */
     908             :         /* TODO use largest fixed size for this platform */
     909           6 :         cd2 = tds_sys_iconv_open("UCS-4", charset->name);
     910           6 :         if (cd2 == (iconv_t) -1)
     911             :                 return 0;
     912             : 
     913             :         /* add part of input */
     914           6 :         il = ol;
     915           6 :         if (il > *input_size)
     916           0 :                 il = *input_size;
     917           6 :         l = sizeof(ib) - ol;
     918           6 :         memcpy(ib + l, *input, il);
     919           6 :         il += l;
     920             : 
     921             :         /* translate a single character */
     922           6 :         pib = ib;
     923           6 :         pob = ob;
     924             :         /* TODO use size of largest fixed charset */
     925           6 :         ol = 4;
     926           6 :         tds_sys_iconv(cd2, &pib, &il, &pob, &ol);
     927             : 
     928             :         /* adjust input */
     929           6 :         l = (pib - ib) - l;
     930           6 :         *input += l;
     931           6 :         *input_size -= l;
     932             : 
     933             :         /* extract state */
     934           6 :         pob = ib;
     935           6 :         ol = sizeof(ib);
     936           6 :         tds_sys_iconv(cd, NULL, NULL, &pob, &ol);
     937             : 
     938             :         /* set input state */
     939           6 :         pib = ib;
     940           6 :         il = sizeof(ib) - ol;
     941           6 :         pob = ob;
     942           6 :         ol = sizeof(ob);
     943           6 :         tds_sys_iconv(cd, &pib, &il, &pob, &ol);
     944             : 
     945           6 :         tds_sys_iconv_close(cd2);
     946             : 
     947           6 :         if (l != 0)
     948             :                 return l;
     949             : 
     950             :         /* last blindly attempt, skip minimum bytes */
     951           6 :         charsize = charset->min_bytes_per_char;
     952             : 
     953             :         /* fall through */
     954             : 
     955        3094 : skip_charsize:
     956        3094 :         if (charsize > *input_size)
     957             :                 return 0;
     958        3094 :         *input += charsize;
     959        3094 :         *input_size -= charsize;
     960        3094 :         return charsize;
     961             : }
     962             : 
     963             : #include <freetds/charset_lookup.h>
     964             : 
     965             : /**
     966             :  * Determine canonical iconv character set.
     967             :  * \returns canonical position, or -1 if lookup failed.
     968             :  * \remarks Returned name can be used in bytes_per_char(), above.
     969             :  */
     970             : int
     971       10786 : tds_canonical_charset(const char *charset_name)
     972             : {
     973       10786 :         const struct charset_alias *c = charset_lookup(charset_name, strlen(charset_name));
     974       10786 :         return c ? c->canonic : -1;
     975             : }
     976             : 
     977             : /**
     978             :  * Determine canonical iconv character set name.  
     979             :  * \returns canonical name, or NULL if lookup failed.
     980             :  * \remarks Returned name can be used in bytes_per_char(), above.
     981             :  */
     982             : const char *
     983        2832 : tds_canonical_charset_name(const char *charset_name)
     984             : {
     985             :         int res;
     986             : 
     987             :         /* get numeric pos */
     988        2832 :         res = tds_canonical_charset(charset_name);
     989        2832 :         if (res >= 0)
     990        2832 :                 return canonic_charsets[res].name;
     991             : 
     992             :         return charset_name;    /* hope for the best */
     993             : }
     994             : 
     995             : static int
     996       19858 : collate2charset(TDSCONNECTION * conn, TDS_UCHAR collate[5])
     997             : {
     998       19858 :         int cp = 0;
     999       19858 :         const int sql_collate = collate[4];
    1000             :         /* extract 16 bit of LCID (it's 20 bits but higher 4 are just variations) */
    1001       19858 :         const int lcid = TDS_GET_UA2LE(collate);
    1002             : 
    1003             :         /* starting with bit 20 (little endian, so 3rd byte bit 4) there are 8 bits:
    1004             :          * fIgnoreCase fIgnoreAccent fIgnoreKana fIgnoreWidth fBinary fBinary2 fUTF8 FRESERVEDBIT
    1005             :          * so fUTF8 is on the 4th byte bit 2 */
    1006       19858 :         if ((collate[3] & 0x4) != 0 && IS_TDS74_PLUS(conn))
    1007             :                 return TDS_CHARSET_UTF_8;
    1008             : 
    1009             :         /*
    1010             :          * The table from the MSQLServer reference "Windows Collation Designators" 
    1011             :          * and from " NLS Information for Microsoft Windows XP".
    1012             :          *
    1013             :          * See also https://go.microsoft.com/fwlink/?LinkId=119987 [MSDN-SQLCollation]
    1014             :          */
    1015             : 
    1016       19858 :         switch (sql_collate) {
    1017             :         case 30:                /* SQL_Latin1_General_CP437_BIN */
    1018             :         case 31:                /* SQL_Latin1_General_CP437_CS_AS */
    1019             :         case 32:                /* SQL_Latin1_General_CP437_CI_AS */
    1020             :         case 33:                /* SQL_Latin1_General_Pref_CP437_CI_AS */
    1021             :         case 34:                /* SQL_Latin1_General_CP437_CI_AI */
    1022             :                 return TDS_CHARSET_CP437;
    1023           0 :         case 40:                /* SQL_Latin1_General_CP850_BIN */
    1024             :         case 41:                /* SQL_Latin1_General_CP850_CS_AS */
    1025             :         case 42:                /* SQL_Latin1_General_CP850_CI_AS */
    1026             :         case 43:                /* SQL_Latin1_General_Pref_CP850_CI_AS */
    1027             :         case 44:                /* SQL_Latin1_General_CP850_CI_AI */
    1028             :         case 49:                /* SQL_1xCompat_CP850_CI_AS */
    1029             :         case 55:                /* SQL_AltDiction_CP850_CS_AS */
    1030             :         case 56:                /* SQL_AltDiction_Pref_CP850_CI_AS */
    1031             :         case 57:                /* SQL_AltDiction_CP850_CI_AI */
    1032             :         case 58:                /* SQL_Scandinavian_Pref_CP850_CI_AS */
    1033             :         case 59:                /* SQL_Scandinavian_CP850_CS_AS */
    1034             :         case 60:                /* SQL_Scandinavian_CP850_CI_AS */
    1035             :         case 61:                /* SQL_AltDiction_CP850_CI_AS */
    1036           0 :                 return TDS_CHARSET_CP850;
    1037           0 :         case 80:                /* SQL_Latin1_General_1250_BIN */
    1038             :         case 81:                /* SQL_Latin1_General_CP1250_CS_AS */
    1039             :         case 82:                /* SQL_Latin1_General_CP1250_CI_AS */
    1040           0 :                 return TDS_CHARSET_CP1250;
    1041           0 :         case 105:               /* SQL_Latin1_General_CP1251_CS_AS */
    1042             :         case 106:               /* SQL_Latin1_General_CP1251_CI_AS */
    1043           0 :                 return TDS_CHARSET_CP1251;
    1044           0 :         case 113:               /* SQL_Latin1_General_CP1253_CS_AS */
    1045             :         case 114:               /* SQL_Latin1_General_CP1253_CI_AS */
    1046             :         case 120:               /* SQL_MixDiction_CP1253_CS_AS */
    1047             :         case 121:               /* SQL_AltDiction_CP1253_CS_AS */
    1048             :         case 122:               /* SQL_AltDiction2_CP1253_CS_AS */
    1049             :         case 124:               /* SQL_Latin1_General_CP1253_CI_AI */
    1050           0 :                 return TDS_CHARSET_CP1253;
    1051           0 :         case 137:               /* SQL_Latin1_General_CP1255_CS_AS */
    1052             :         case 138:               /* SQL_Latin1_General_CP1255_CI_AS */
    1053           0 :                 return TDS_CHARSET_CP1255;
    1054           0 :         case 145:               /* SQL_Latin1_General_CP1256_CS_AS */
    1055             :         case 146:               /* SQL_Latin1_General_CP1256_CI_AS */
    1056           0 :                 return TDS_CHARSET_CP1256;
    1057           0 :         case 153:               /* SQL_Latin1_General_CP1257_CS_AS */
    1058             :         case 154:               /* SQL_Latin1_General_CP1257_CI_AS */
    1059           0 :                 return TDS_CHARSET_CP1257;
    1060             :         }
    1061             : 
    1062       19858 :         switch (lcid) {
    1063             :         case 0x405:
    1064             :         case 0x40e:             /* 0x1040e */
    1065             :         case 0x415:
    1066             :         case 0x418:
    1067             :         case 0x41a:
    1068             :         case 0x41b:
    1069             :         case 0x41c:
    1070             :         case 0x424:
    1071             :         case 0x442:
    1072             :         case 0x81a:
    1073             :         case 0x104e:            /* ?? */
    1074             :         case 0x141a:
    1075             :                 cp = TDS_CHARSET_CP1250;
    1076             :                 break;
    1077           0 :         case 0x402:
    1078             :         case 0x419:
    1079             :         case 0x422:
    1080             :         case 0x423:
    1081             :         case 0x42f:
    1082             :         case 0x43f:
    1083             :         case 0x440:
    1084             :         case 0x444:
    1085             :         case 0x450:
    1086             :         case 0x82c:
    1087             :         case 0x843:
    1088             :         case 0xc1a:
    1089             :         case 0x46d:
    1090             :         case 0x201a:
    1091             :         case 0x485:
    1092           0 :                 cp = TDS_CHARSET_CP1251;
    1093           0 :                 break;
    1094       19840 :         case 0x1007:
    1095             :         case 0x1009:
    1096             :         case 0x100a:
    1097             :         case 0x100c:
    1098             :         case 0x1407:
    1099             :         case 0x1409:
    1100             :         case 0x140a:
    1101             :         case 0x140c:
    1102             :         case 0x1809:
    1103             :         case 0x180a:
    1104             :         case 0x180c:
    1105             :         case 0x1c09:
    1106             :         case 0x1c0a:
    1107             :         case 0x2009:
    1108             :         case 0x200a:
    1109             :         case 0x2409:
    1110             :         case 0x240a:
    1111             :         case 0x2809:
    1112             :         case 0x280a:
    1113             :         case 0x2c09:
    1114             :         case 0x2c0a:
    1115             :         case 0x3009:
    1116             :         case 0x300a:
    1117             :         case 0x3409:
    1118             :         case 0x340a:
    1119             :         case 0x380a:
    1120             :         case 0x3c0a:
    1121             :         case 0x400a:
    1122             :         case 0x403:
    1123             :         case 0x406:
    1124             :         case 0x417:
    1125             :         case 0x42e:
    1126             :         case 0x43b:
    1127             :         case 0x452:
    1128             :         case 0x462:
    1129             :         case 0x47a:
    1130             :         case 0x47c:
    1131             :         case 0x47e:
    1132             :         case 0x483:
    1133             :         case 0x407:             /* 0x10407 */
    1134             :         case 0x409:
    1135             :         case 0x40a:
    1136             :         case 0x40b:
    1137             :         case 0x40c:
    1138             :         case 0x40f:
    1139             :         case 0x410:
    1140             :         case 0x413:
    1141             :         case 0x414:
    1142             :         case 0x416:
    1143             :         case 0x41d:
    1144             :         case 0x421:
    1145             :         case 0x42d:
    1146             :         case 0x436:
    1147             :         case 0x437:             /* 0x10437 */
    1148             :         case 0x438:
    1149             :                 /*case 0x439:  ??? Unicode only */
    1150             :         case 0x43e:
    1151             :         case 0x440a:
    1152             :         case 0x441:
    1153             :         case 0x456:
    1154             :         case 0x480a:
    1155             :         case 0x4c0a:
    1156             :         case 0x500a:
    1157             :         case 0x807:
    1158             :         case 0x809:
    1159             :         case 0x80a:
    1160             :         case 0x80c:
    1161             :         case 0x810:
    1162             :         case 0x813:
    1163             :         case 0x814:
    1164             :         case 0x816:
    1165             :         case 0x81d:
    1166             :         case 0x83b:
    1167             :         case 0x83e:
    1168             :         case 0x85f:
    1169             :         case 0xc07:
    1170             :         case 0xc09:
    1171             :         case 0xc0a:
    1172             :         case 0xc0c:
    1173       19840 :                 cp = TDS_CHARSET_CP1252;
    1174       19840 :                 break;
    1175           0 :         case 0x408:
    1176           0 :                 cp = TDS_CHARSET_CP1253;
    1177           0 :                 break;
    1178           0 :         case 0x41f:
    1179             :         case 0x42c:
    1180             :         case 0x443:
    1181           0 :                 cp = TDS_CHARSET_CP1254;
    1182           0 :                 break;
    1183           6 :         case 0x40d:
    1184           6 :                 cp = TDS_CHARSET_CP1255;
    1185           6 :                 break;
    1186           0 :         case 0x1001:
    1187             :         case 0x1401:
    1188             :         case 0x1801:
    1189             :         case 0x1c01:
    1190             :         case 0x2001:
    1191             :         case 0x2401:
    1192             :         case 0x2801:
    1193             :         case 0x2c01:
    1194             :         case 0x3001:
    1195             :         case 0x3401:
    1196             :         case 0x3801:
    1197             :         case 0x3c01:
    1198             :         case 0x4001:
    1199             :         case 0x401:
    1200             :         case 0x480:
    1201             :         case 0x420:
    1202             :         case 0x429:
    1203             :         case 0x48c:
    1204             :         case 0x801:
    1205             :         case 0xc01:
    1206           0 :                 cp = TDS_CHARSET_CP1256;
    1207           0 :                 break;
    1208           0 :         case 0x425:
    1209             :         case 0x426:
    1210             :         case 0x427:
    1211             :         case 0x827:             /* ?? */
    1212           0 :                 cp = TDS_CHARSET_CP1257;
    1213           0 :                 break;
    1214           0 :         case 0x42a:
    1215           0 :                 cp = TDS_CHARSET_CP1258;
    1216           0 :                 break;
    1217           0 :         case 0x41e:
    1218           0 :                 cp = TDS_CHARSET_CP874;
    1219           0 :                 break;
    1220           0 :         case 0x411:             /* 0x10411 */
    1221           0 :                 cp = TDS_CHARSET_CP932;
    1222           0 :                 break;
    1223          12 :         case 0x1004:
    1224             :         case 0x804:             /* 0x20804 */
    1225          12 :                 cp = TDS_CHARSET_GB18030;
    1226          12 :                 break;
    1227           0 :         case 0x412:             /* 0x10412 */
    1228           0 :                 cp = TDS_CHARSET_CP949;
    1229           0 :                 break;
    1230           0 :         case 0x1404:
    1231             :         case 0x404:             /* 0x30404 */
    1232             :         case 0xc04:
    1233           0 :                 cp = TDS_CHARSET_CP950;
    1234           0 :                 break;
    1235           0 :         default:
    1236           0 :                 cp = TDS_CHARSET_CP1252;
    1237             :         }
    1238             : 
    1239             :         return cp;
    1240             : }
    1241             : 
    1242             : /**
    1243             :  * Get iconv information from a LCID (to support different column encoding under MSSQL2K)
    1244             :  */
    1245             : TDSICONV *
    1246       15910 : tds_iconv_from_collate(TDSCONNECTION * conn, TDS_UCHAR collate[5])
    1247             : {
    1248       15910 :         int canonic_charset = collate2charset(conn, collate);
    1249             : 
    1250             :         /* same as client (usually this is true, so this improve performance) ? */
    1251       15910 :         if (conn->char_convs[client2server_chardata]->to.charset.canonic == canonic_charset)
    1252             :                 return conn->char_convs[client2server_chardata];
    1253             : 
    1254        2328 :         return tds_iconv_get_info(conn, conn->char_convs[client2ucs2]->from.charset.canonic, canonic_charset);
    1255             : }
    1256             : 
    1257             : /** @} */

Generated by: LCOV version 1.13