/** * Handle conversions from TDS (N)CHAR to ODBC (W)CHAR */ static SQLLEN odbc_convert_char(TDS_STMT * stmt, TDSCOLUMN * curcol, TDS_CHAR * src, TDS_UINT srclen, int desttype, TDS_CHAR * dest, SQLULEN destlen) { const char *ib; char *ob; size_t il, ol, char_size; /* FIXME MARS not correct cause is the global tds but stmt->tds can be NULL on SQLGetData */ TDSSOCKET *tds = stmt->dbc->tds_socket; TDSICONV *conv = curcol->char_conv; if (!conv) conv = tds->conn->char_convs[client2server_chardata]; if (desttype == SQL_C_WCHAR) { /* SQL_C_WCHAR, convert to wide encode */ conv = tds_iconv_get(tds->conn, ODBC_WIDE_NAME, conv->to.charset.name); if (!conv) conv = tds_iconv_get(tds->conn, ODBC_WIDE_NAME, "ISO-8859-1"); #ifdef ENABLE_ODBC_WIDE } else { conv = tds_iconv_get(tds->conn, tds_dstr_cstr(&stmt->dbc->original_charset), conv->to.charset.name); if (!conv) conv = tds_iconv_get(tds->conn, tds_dstr_cstr(&stmt->dbc->original_charset), "ISO-8859-1"); if (!conv) conv = tds_iconv_get(tds->conn, "ISO-8859-1", "ISO-8859-1"); #endif } ib = src; il = srclen; ob = dest; ol = 0; char_size = desttype == SQL_C_CHAR ? 1 : SIZEOF_SQLWCHAR; if (destlen >= char_size) { ol = destlen - char_size; memset(&conv->suppress, 0, sizeof(conv->suppress)); conv->suppress.e2big = 1; /* TODO check return value */ tds_iconv(tds, conv, to_client, &ib, &il, &ob, &ol); ol = ob - dest; /* bytes written */ if (curcol) curcol->column_text_sqlgetdatapos += ib - src; /* terminate string */ memset(ob, 0, char_size); } /* returned size have to take into account buffer left unconverted */ if (il == 0 || (conv->from.charset.min_bytes_per_char == conv->from.charset.max_bytes_per_char && conv->to.charset.min_bytes_per_char == conv->to.charset.max_bytes_per_char)) { ol += il * conv->from.charset.min_bytes_per_char / conv->to.charset.min_bytes_per_char; } else { /* TODO convert and discard ?? or return proper SQL_NO_TOTAL values ?? */ return SQL_NO_TOTAL; } return ol; }
static SQLLEN _bcp_iconv_helper(const TDS_DBC *dbc, const TDSCOLUMN *bindcol, const TDS_CHAR * src, size_t srclen, char * dest, size_t destlen) { if (bindcol->char_conv) { char *orig_dest = dest; if (tds_iconv(dbc->tds_socket, bindcol->char_conv, to_server, &src, &srclen, &dest, &destlen) == (size_t)-1) return -1; return dest - orig_dest; } if (destlen > srclen) destlen = srclen; memcpy(dest, src, destlen); return destlen; }
/* * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary * is not aligned with the character boundary. In that event, it will return an error, and * some number of bytes (less than a character) will remain in the tail end of temp[]. They are * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read. */ static int read_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv, size_t * wire_size, char **outbuf, size_t * outbytesleft) { TEMP_INIT(256); /* * temp (above) is the "preconversion" buffer, the place where the UCS-2 data * are parked before converting them to ASCII. It has to have a size, * and there's no advantage to allocating dynamically. * This also avoids any memory allocation error. */ const char *bufp; size_t bufleft = 0; const size_t max_output = *outbytesleft; /* cast away const for message suppression sub-structure */ TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress; memset(suppress, 0, sizeof(char_conv->suppress)); for (bufp = temp; *wire_size > 0 && *outbytesleft > 0; bufp = temp + bufleft) { assert(bufp >= temp); /* read a chunk of data */ bufleft = TEMP_SIZE - bufleft; if (bufleft > *wire_size) bufleft = *wire_size; tds_get_n(tds, (char *) bufp, (int)bufleft); *wire_size -= bufleft; bufleft += bufp - temp; /* Convert chunk and write to dest. */ bufp = temp; /* always convert from start of buffer */ suppress->einval = *wire_size > 0; /* EINVAL matters only on the last chunk. */ if ((size_t)-1 == tds_iconv(tds, char_conv, to_client, &bufp, &bufleft, outbuf, outbytesleft)) { tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: tds_iconv returned errno %d\n", errno); if (errno != EILSEQ) { tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: " "Gave up converting %u bytes due to error %d.\n", (unsigned int) bufleft, errno); tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", bufp, bufleft); } if (bufp == temp) { /* tds_iconv did not convert anything, avoid infinite loop */ tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: draining remaining %u bytes.\n", (unsigned int) *wire_size); tds_get_n(tds, NULL, (int)(*wire_size)); /* perhaps we should read unconverted data into outbuf? */ *wire_size = 0; break; } if (bufleft) { memmove(temp, bufp, bufleft); } } } assert(*wire_size == 0 || *outbytesleft == 0); TEMP_FREE; return (int)(max_output - *outbytesleft); }