diff --git a/CMakeLists.txt b/CMakeLists.txt index ec280600..db53765b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,10 @@ IF(WITH_SQLITE) ADD_DEFINITIONS(-DHAVE_SQLITE) ENDIF() +IF(NOT WIN32) + INCLUDE(cmake/FindIconv.cmake) +ENDIF() + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/include/mysql_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/mysql_version.h) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/include/my_config.h.in diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake new file mode 100644 index 00000000..de45c1b5 --- /dev/null +++ b/cmake/FindIconv.cmake @@ -0,0 +1,52 @@ +# Copyright (c) 2010 Michael Bell + +if (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + # Already in cache, be silent + set(ICONV_FIND_QUIETLY TRUE) +endif (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +find_path(ICONV_INCLUDE_DIR iconv.h) + +find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) + +if (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + set (ICONV_FOUND TRUE) +endif (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) + +if (ICONV_FOUND) + include(CheckCSourceCompiles) + CHECK_C_SOURCE_COMPILES(" + #include + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } +" ICONV_SECOND_ARGUMENT_IS_CONST ) +endif (ICONV_FOUND) + +set (CMAKE_REQUIRED_INCLUDES) +set (CMAKE_REQUIRED_LIBRARIES) + +if (ICONV_FOUND) + if (NOT ICONV_FIND_QUIETLY) + message (STATUS "Found Iconv: ${ICONV_LIBRARIES}") + endif (NOT ICONV_FIND_QUIETLY) +else (ICONV_FOUND) + if (Iconv_FIND_REQUIRED) + message (FATAL_ERROR "Could not find Iconv") + endif (Iconv_FIND_REQUIRED) +endif (ICONV_FOUND) + +MARK_AS_ADVANCED( + ICONV_INCLUDE_DIR + ICONV_LIBRARIES + ICONV_SECOND_ARGUMENT_IS_CONST +) diff --git a/include/config-win.h b/include/config-win.h index 702feba1..64dad707 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -213,9 +213,9 @@ inline double ulonglong2double(ulonglong value) #define HAVE_STRPBRK #define HAVE_STRSTR -#ifdef NOT_USED +#ifdef WIN32 #define HAVE_SNPRINTF /* Gave link error */ -#define _snprintf snprintf +#define snprintf _snprintf #endif #ifdef _MSC_VER diff --git a/include/m_ctype.h b/include/m_ctype.h index 5022e2b1..ff187369 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -42,8 +42,8 @@ typedef struct charset_info_st uint state; char *csname; char *name; - char *comment; char *dir; + char *encoding; uint char_minlen; uint char_maxlen; uint (*mb_charlen)(uint c); @@ -52,6 +52,9 @@ typedef struct charset_info_st extern const CHARSET_INFO compiled_charsets[]; extern CHARSET_INFO *default_charset_info; +extern CHARSET_INFO *my_charset_bin; +extern CHARSET_INFO *my_charset_latin1; +extern CHARSET_INFO *my_charset_utf8_general_ci; CHARSET_INFO *find_compiled_charset(uint cs_number); CHARSET_INFO *find_compiled_charset_by_name(const char *name); diff --git a/include/ma_dyncol.h b/include/ma_dyncol.h new file mode 100644 index 00000000..594ef702 --- /dev/null +++ b/include/ma_dyncol.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2011, Monty Program Ab + Copyright (c) 2011, Oleksandr Byelkin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY ``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 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. +*/ + +#ifndef ma_dyncol_h +#define ma_dyncol_h + +#ifndef LIBMARIADB +#include +#include +#endif +#include + +/* + Limits of implementation +*/ +#define MAX_TOTAL_NAME_LENGTH 65535 +#define MAX_NAME_LENGTH (MAX_TOTAL_NAME_LENGTH/4) + +/* NO and OK is the same used just to show semantics */ +#define ER_DYNCOL_NO ER_DYNCOL_OK + +enum enum_dyncol_func_result +{ + ER_DYNCOL_OK= 0, + ER_DYNCOL_YES= 1, /* For functions returning 0/1 */ + ER_DYNCOL_FORMAT= -1, /* Wrong format of the encoded string */ + ER_DYNCOL_LIMIT= -2, /* Some limit reached */ + ER_DYNCOL_RESOURCE= -3, /* Out of resourses */ + ER_DYNCOL_DATA= -4, /* Incorrect input data */ + ER_DYNCOL_UNKNOWN_CHARSET= -5, /* Unknown character set */ + ER_DYNCOL_TRUNCATED= 2 /* OK, but data was truncated */ +}; + +typedef DYNAMIC_STRING DYNAMIC_COLUMN; + +enum enum_dynamic_column_type +{ + DYN_COL_NULL= 0, + DYN_COL_INT, + DYN_COL_UINT, + DYN_COL_DOUBLE, + DYN_COL_STRING, + DYN_COL_DECIMAL, + DYN_COL_DATETIME, + DYN_COL_DATE, + DYN_COL_TIME, + DYN_COL_DYNCOL +}; + +typedef enum enum_dynamic_column_type DYNAMIC_COLUMN_TYPE; + +struct st_dynamic_column_value +{ + DYNAMIC_COLUMN_TYPE type; + union + { + long long long_value; + unsigned long long ulong_value; + double double_value; + struct { + MYSQL_LEX_STRING value; + CHARSET_INFO *charset; + } string; +#ifndef LIBMARIADB + struct { + decimal_digit_t buffer[DECIMAL_BUFF_LENGTH]; + decimal_t value; + } decimal; +#endif + MYSQL_TIME time_value; + } x; +}; + +typedef struct st_dynamic_column_value DYNAMIC_COLUMN_VALUE; + +#ifdef MADYNCOL_DEPRECATED +enum enum_dyncol_func_result +dynamic_column_create(DYNAMIC_COLUMN *str, + uint column_nr, DYNAMIC_COLUMN_VALUE *value); + +enum enum_dyncol_func_result +dynamic_column_create_many(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values); +enum enum_dyncol_func_result +dynamic_column_update(DYNAMIC_COLUMN *org, uint column_nr, + DYNAMIC_COLUMN_VALUE *value); +enum enum_dyncol_func_result +dynamic_column_update_many(DYNAMIC_COLUMN *str, + uint add_column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values); + +enum enum_dyncol_func_result +dynamic_column_exists(DYNAMIC_COLUMN *org, uint column_nr); + +enum enum_dyncol_func_result +dynamic_column_list(DYNAMIC_COLUMN *org, DYNAMIC_ARRAY *array_of_uint); + +enum enum_dyncol_func_result +dynamic_column_get(DYNAMIC_COLUMN *org, uint column_nr, + DYNAMIC_COLUMN_VALUE *store_it_here); +#endif + +/* new functions */ +enum enum_dyncol_func_result +mariadb_dyncol_create_many(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_string); +enum enum_dyncol_func_result +mariadb_dyncol_create_many_named(DYNAMIC_COLUMN *str, + uint column_count, + MYSQL_LEX_STRING *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_string); + + +enum enum_dyncol_func_result +mariadb_dyncol_update_many(DYNAMIC_COLUMN *str, + uint add_column_count, + uint *column_keys, + DYNAMIC_COLUMN_VALUE *values); +enum enum_dyncol_func_result +mariadb_dyncol_update_many_named(DYNAMIC_COLUMN *str, + uint add_column_count, + MYSQL_LEX_STRING *column_keys, + DYNAMIC_COLUMN_VALUE *values); + + +enum enum_dyncol_func_result +mariadb_dyncol_exists(DYNAMIC_COLUMN *org, uint column_nr); +enum enum_dyncol_func_result +mariadb_dyncol_exists_named(DYNAMIC_COLUMN *str, MYSQL_LEX_STRING *name); + +/* List of not NULL columns */ +enum enum_dyncol_func_result +mariadb_dyncol_list(DYNAMIC_COLUMN *str, uint *count, uint **nums); +enum enum_dyncol_func_result +mariadb_dyncol_list_named(DYNAMIC_COLUMN *str, uint *count, + MYSQL_LEX_STRING **names); + +/* + if the column do not exists it is NULL +*/ +enum enum_dyncol_func_result +mariadb_dyncol_get(DYNAMIC_COLUMN *org, uint column_nr, + DYNAMIC_COLUMN_VALUE *store_it_here); +enum enum_dyncol_func_result +mariadb_dyncol_get_named(DYNAMIC_COLUMN *str, MYSQL_LEX_STRING *name, + DYNAMIC_COLUMN_VALUE *store_it_here); + +my_bool mariadb_dyncol_has_names(DYNAMIC_COLUMN *str); + +enum enum_dyncol_func_result +mariadb_dyncol_check(DYNAMIC_COLUMN *str); + +enum enum_dyncol_func_result +mariadb_dyncol_json(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json); + +void mariadb_dyncol_free(DYNAMIC_COLUMN *str); + +#define mariadb_dyncol_init(A) memset((A), 0, sizeof(DYNAMIC_COLUMN)) +#define dynamic_column_initialize(A) mariadb_dyncol_init((A)) +#define dynamic_column_column_free(A) mariadb_dyncol_free((A)) + +/* conversion of values to 3 base types */ +enum enum_dyncol_func_result +mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val, + CHARSET_INFO *cs, my_bool quote); +enum enum_dyncol_func_result +mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val); +enum enum_dyncol_func_result +mariadb_dyncol_val_double(double *dbl, DYNAMIC_COLUMN_VALUE *val); + +enum enum_dyncol_func_result +mariadb_dyncol_unpack(DYNAMIC_COLUMN *str, + uint *count, + MYSQL_LEX_STRING **names, DYNAMIC_COLUMN_VALUE **vals); + +int mariadb_dyncol_column_cmp_named(const MYSQL_LEX_STRING *s1, + const MYSQL_LEX_STRING *s2); + +enum enum_dyncol_func_result +mariadb_dyncol_column_count(DYNAMIC_COLUMN *str, uint *column_count); + +#define mariadb_dyncol_value_init(V) (V)->type= DYN_COL_NULL + +/* + Prepare value for using as decimal +*/ +void mariadb_dyncol_prepare_decimal(DYNAMIC_COLUMN_VALUE *value); + +#endif diff --git a/include/my_global.h b/include/my_global.h index 7cfcc6e9..9aa43949 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -330,6 +330,14 @@ typedef unsigned short ushort; #define ASCII_BITS_USED 8 /* Bit char used */ #define NEAR_F /* No near function handling */ +struct st_mysql_lex_string +{ + char *str; + size_t length; +}; +typedef struct st_mysql_lex_string LEX_STRING; +typedef struct st_mysql_lex_string MYSQL_LEX_STRING; + /* Some types that is different between systems */ typedef int File; /* File descriptor */ @@ -499,6 +507,14 @@ extern double my_atof(const char*); /* #define sigset(A,B) signal((A),(B)) */ #endif +#if defined(_lint) || defined(FORCE_INIT_OF_VARS) || \ + defined(__cplusplus) || !defined(__GNUC__) +#define UNINIT_VAR(x) x= 0 +#else +/* GCC specific self-initialization which inhibits the warning. */ +#define UNINIT_VAR(x) x= x +#endif + /* Remove some things that mit_thread break or doesn't support */ #if defined(HAVE_mit_thread) && defined(THREAD) #undef HAVE_PREAD @@ -521,25 +537,20 @@ extern double my_atof(const char*); #define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL) #endif -#if SIZEOF_LONG == 4 -#define INT_MIN32 (long) 0x80000000L -#define INT_MAX32 (long) 0x7FFFFFFFL +#define INT_MIN64 (~0x7FFFFFFFFFFFFFFFLL) +#define INT_MAX64 0x7FFFFFFFFFFFFFFFLL +#define INT_MIN32 (~0x7FFFFFFFL) +#define INT_MAX32 0x7FFFFFFFL #define UINT_MAX32 0xFFFFFFFFL -#define INT_MIN24 ((long) 0xff800000L) -#define INT_MAX24 0x007fffffL -#define INT_MIN16 ((short int) 0x8000) -#define INT_MAX16 0x7FFF -#define UINT_MAX16 0xFFFF -#define INT_MIN8 ((char) 0x80) -#define INT_MAX8 ((char) 0x7F) -#else /* Probably Alpha */ -#define INT_MIN32 ((long) (int) 0x80000000) -#define INT_MAX32 ((long) (int) 0x7FFFFFFF) -#define INT_MIN24 ((long) (int) 0xff800000) -#define INT_MAX24 ((long) (int) 0x007fffff) -#define INT_MIN16 ((short int) 0xffff8000) -#define INT_MAX16 ((short int) 0x00007FFF) -#endif +#define INT_MIN24 (~0x007FFFFF) +#define INT_MAX24 0x007FFFFF +#define UINT_MAX24 0x00FFFFFF +#define INT_MIN16 (~0x7FFF) +#define INT_MAX16 0x7FFF +#define UINT_MAX16 0xFFFF +#define INT_MIN8 (~0x7F) +#define INT_MAX8 0x7F +#define UINT_MAX8 0xFF #ifndef ULL #ifdef HAVE_LONG_LONG @@ -549,6 +560,15 @@ extern double my_atof(const char*); #endif #endif +#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX) +/* First check for ANSI C99 definition: */ +#ifdef ULLONG_MAX +#define ULONGLONG_MAX ULLONG_MAX +#else +#define ULONGLONG_MAX ((unsigned long long)(~0ULL)) +#endif +#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/ + /* From limits.h instead */ #ifndef DBL_MIN #define DBL_MIN 4.94065645841246544e-324 @@ -638,7 +658,7 @@ typedef long longlong; #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif - +#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1) #ifdef USE_RAID /* The following is done with a if to not get problems with pre-processors diff --git a/include/mysql.h b/include/mysql.h index 2b524fb0..84b880b9 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -272,11 +272,6 @@ typedef struct st_mysql { void *extension; } MYSQL; -typedef struct st_mysql_lex_string { - char *str; - size_t length; -} MYSQL_LEX_STRING; - struct st_mysql_options_extention { char *plugin_dir; char *default_auth; @@ -319,6 +314,9 @@ typedef struct st_mysql_time enum enum_mysql_timestamp_type time_type; } MYSQL_TIME; +#define AUTO_SEC_PART_DIGITS 31 +#define SEC_PART_DIGITS 6 + typedef struct character_set { unsigned int number; /* character set number */ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index bf113ec7..58cc3ba1 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -13,10 +13,12 @@ ENDIF() ADD_DEFINITIONS(-D ENABLED_LOCAL_INFILE) ADD_DEFINITIONS(-D HAVE_COMPRESS) +ADD_DEFINITIONS(-D LIBMARIADB) ADD_DEFINITIONS(-D THREAD) SET(LIBMARIADB_SOURCES array.c +ma_dyncol.c bchange.c bmove.c bmove_upp.c @@ -24,6 +26,7 @@ my_charset.c violite.c net.c charset.c +ma_time.c dbug.c default.c errmsg.c @@ -132,13 +135,13 @@ ELSE() ENDIF() ADD_LIBRARY(mariadbclient STATIC ${LIBMARIADB_SOURCES}) -TARGET_LINK_LIBRARIES(mariadbclient ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${MATH_LIBRARY}) +TARGET_LINK_LIBRARIES(mariadbclient ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${MATH_LIBRARY} ${ICONV_LIBRARIES}) IF(OPENSSL_FOUND) TARGET_LINK_LIBRARIES(mariadbclient ${SSL_LIBRARIES}) ENDIF() ADD_LIBRARY(libmariadb SHARED ${LIBMARIADB_SOURCES}) -TARGET_LINK_LIBRARIES(libmariadb ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${MATH_LIBRARY}) +TARGET_LINK_LIBRARIES(libmariadb ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${MATH_LIBRARY} ${ICONV_LIBRARIES}) IF(OPENSSL_FOUND) TARGET_LINK_LIBRARIES(libmariadb ${SSL_LIBRARIES}) diff --git a/libmariadb/charset.c b/libmariadb/charset.c index 66c28d97..46c98e0a 100644 --- a/libmariadb/charset.c +++ b/libmariadb/charset.c @@ -22,6 +22,9 @@ #include CHARSET_INFO *default_charset_info = (CHARSET_INFO *)&compiled_charsets[5]; +CHARSET_INFO *my_charset_bin= (CHARSET_INFO *)&compiled_charsets[32]; +CHARSET_INFO *my_charset_latin1= (CHARSET_INFO *)&compiled_charsets[5]; +CHARSET_INFO *my_charset_utf8_general_ci= (CHARSET_INFO *)&compiled_charsets[21]; CHARSET_INFO *get_charset_by_nr(uint cs_number) { @@ -52,7 +55,7 @@ CHARSET_INFO *get_charset_by_name(const char *cs_name) { int i= 0; - while (compiled_charsets[i].nr && strcmp(cs_name, compiled_charsets[i].name) != 0) + while (compiled_charsets[i].nr && strcmp(cs_name, compiled_charsets[i].csname) != 0) i++; return (compiled_charsets[i].nr) ? (CHARSET_INFO *)&compiled_charsets[i] : NULL; diff --git a/libmariadb/libmariadb_exports.def b/libmariadb/libmariadb_exports.def index c4b69cca..a0a2e208 100644 --- a/libmariadb/libmariadb_exports.def +++ b/libmariadb/libmariadb_exports.def @@ -106,3 +106,23 @@ EXPORTS mysql_stmt_more_results mariadb_connection mysql_get_server_name + mariadb_dyncol_create_many + mariadb_dyncol_create_many_named + mariadb_dyncol_update_many + mariadb_dyncol_update_many_named + mariadb_dyncol_exists + mariadb_dyncol_exists_named + mariadb_dyncol_list + mariadb_dyncol_list_named + mariadb_dyncol_get + mariadb_dyncol_get_named + mariadb_dyncol_has_names + mariadb_dyncol_check + mariadb_dyncol_val_str + mariadb_dyncol_val_long + mariadb_dyncol_val_double + mariadb_dyncol_unpack + mariadb_dyncol_column_cmp_named + mariadb_dyncol_column_count + mariadb_dyncol_json + diff --git a/libmariadb/ma_dyncol.c b/libmariadb/ma_dyncol.c new file mode 100644 index 00000000..c341f0e4 --- /dev/null +++ b/libmariadb/ma_dyncol.c @@ -0,0 +1,4406 @@ +/* Copyright (c) 2011,2013 Monty Program Ab; + Copyright (c) 2011,2012 Oleksandr Byelkin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY ``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 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. +*/ + +/* + Numeric format: + =============== + * Fixed header part + 1 byte flags: + 0,1 bits - - 1 + 2-7 bits - 0 + 2 bytes column counter + * Columns directory sorted by column number, each entry contains of: + 2 bytes column number + bytes (1-4) combined offset from beginning of + the data segment + 3 bit type + * Data of above columns size of data and length depend on type + + Columns with names: + =================== + * Fixed header part + 1 byte flags: + 0,1 bits - - 2 + 2 bit - 1 (means format with names) + 3,4 bits - 00 (means - 2, + now 2 is the only supported size) + 5-7 bits - 0 + 2 bytes column counter + * Variable header part (now it is actually fixed part) + (2) bytes size of stored names pool + * Column directory sorted by names, each consists of + (2) bytes offset of name + bytes (2-5)bytes combined offset from beginning of + the data segment + 4 bit type + * Names stored one after another + * Data of above columns size of data and length depend on type +*/ + +#include +#include "mysys_priv.h" +#include +#include +#include +#include + + + +#ifndef LIBMARIADB +uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, + const char *from, uint32 from_length, + CHARSET_INFO *from_cs, uint *errors); +#else +size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len, + unsigned int digits); +#endif +/* + Flag byte bits + + 2 bits which determinate size of offset in the header -1 +*/ +/* mask to get above bits */ +#define DYNCOL_FLG_OFFSET (1|2) +#define DYNCOL_FLG_NAMES 4 +#define DYNCOL_FLG_NMOFFSET (8|16) +/** + All known flags mask that could be set. + + @note DYNCOL_FLG_NMOFFSET should be 0 for now. +*/ +#define DYNCOL_FLG_KNOWN (1|2|4) + +/* formats */ +enum enum_dyncol_format +{ + dyncol_fmt_num= 0, + dyncol_fmt_str= 1 +}; + +/* dynamic column size reserve */ +#define DYNCOL_SYZERESERVE 80 + +#define DYNCOL_OFFSET_ERROR 0xffffffff + +/* length of fixed string header 1 byte - flags, 2 bytes - columns counter */ +#define FIXED_HEADER_SIZE 3 +/* + length of fixed string header with names + 1 byte - flags, 2 bytes - columns counter, 2 bytes - name pool size +*/ +#define FIXED_HEADER_SIZE_NM 5 + +#define COLUMN_NUMBER_SIZE 2 +/* 2 bytes offset from the name pool */ +#define COLUMN_NAMEPTR_SIZE 2 + +#define MAX_OFFSET_LENGTH 4 +#define MAX_OFFSET_LENGTH_NM 5 + +#define DYNCOL_NUM_CHAR 6 + +my_bool mariadb_dyncol_has_names(DYNAMIC_COLUMN *str) +{ + if (str->length < 1) + return FALSE; + return test(str->str[0] & DYNCOL_FLG_NAMES); +} + +static enum enum_dyncol_func_result +dynamic_column_time_store(DYNAMIC_COLUMN *str, + MYSQL_TIME *value, enum enum_dyncol_format format); +static enum enum_dyncol_func_result +dynamic_column_date_store(DYNAMIC_COLUMN *str, + MYSQL_TIME *value); +static enum enum_dyncol_func_result +dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length); +static enum enum_dyncol_func_result +dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length); +static enum enum_dyncol_func_result +dynamic_column_get_internal(DYNAMIC_COLUMN *str, + DYNAMIC_COLUMN_VALUE *store_it_here, + uint num_key, LEX_STRING *str_key); +static enum enum_dyncol_func_result +dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key, + LEX_STRING *str_key); +static enum enum_dyncol_func_result +dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str, + uint add_column_count, + void *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool string_keys); +static int plan_sort_num(const void *a, const void *b); +static int plan_sort_named(const void *a, const void *b); + +/* + Structure to hold information about dynamic columns record and + iterate through it. +*/ + +struct st_dyn_header +{ + uchar *header, *nmpool, *dtpool, *data_end; + size_t offset_size; + size_t entry_size; + size_t header_size; + size_t nmpool_size; + size_t data_size; + /* dyncol_fmt_num - numeric columns, dyncol_fmt_str - column names */ + enum enum_dyncol_format format; + uint column_count; + + uchar *entry, *data, *name; + size_t offset; + size_t length; + enum enum_dynamic_column_type type; +}; + +typedef struct st_dyn_header DYN_HEADER; + +static inline my_bool read_fixed_header(DYN_HEADER *hdr, + DYNAMIC_COLUMN *str); +static void set_fixed_header(DYNAMIC_COLUMN *str, + uint offset_size, + uint column_count); + +/* + Calculate entry size (E) and header size (H) by offset size (O) and column + count (C) and fixed part of entry size (F). +*/ + +#define calc_param(E,H,F,O,C) do { \ + (*(E))= (O) + F; \ + (*(H))= (*(E)) * (C); \ +}while(0); + + +/** + Name pool size functions, for numeric format it is 0 +*/ + +static size_t name_size_num(void *keys __attribute__((unused)), + uint i __attribute__((unused))) +{ + return 0; +} + + +/** + Name pool size functions. +*/ +static size_t name_size_named(void *keys, uint i) +{ + return ((LEX_STRING *) keys)[i].length; +} + + +/** + Comparator function for references on column numbers for qsort + (numeric format) +*/ + +static int column_sort_num(const void *a, const void *b) +{ + return **((uint **)a) - **((uint **)b); +} + +/** + Comparator function for references on column numbers for qsort + (names format) +*/ + +int mariadb_dyncol_column_cmp_named(const LEX_STRING *s1, const LEX_STRING *s2) +{ + /* + We compare instead of subtraction to avoid data loss in case of huge + length difference (more then fit in int). + */ + int rc= (s1->length > s2->length ? 1 : + (s1->length < s2->length ? -1 : 0)); + if (rc == 0) + rc= memcmp((void *)s1->str, (void *)s2->str, + (size_t) s1->length); + return rc; +} + + +/** + Comparator function for references on column numbers for qsort + (names format) +*/ + +static int column_sort_named(const void *a, const void *b) +{ + return mariadb_dyncol_column_cmp_named(*((LEX_STRING **)a), + *((LEX_STRING **)b)); +} + + +/** + Check limit function (numeric format) +*/ + +static my_bool check_limit_num(const void *val) +{ + return **((uint **)val) > UINT_MAX16; +} + + +/** + Check limit function (names format) +*/ + +static my_bool check_limit_named(const void *val) +{ + return (*((LEX_STRING **)val))->length > MAX_NAME_LENGTH; +} + + +/** + Write numeric format static header part. +*/ + +static void set_fixed_header_num(DYNAMIC_COLUMN *str, DYN_HEADER *hdr) +{ + set_fixed_header(str, (uint)hdr->offset_size, hdr->column_count); + hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE; + hdr->nmpool= hdr->dtpool= hdr->header + hdr->header_size; +} + + +/** + Write names format static header part. +*/ + +static void set_fixed_header_named(DYNAMIC_COLUMN *str, DYN_HEADER *hdr) +{ + DBUG_ASSERT(hdr->column_count <= 0xffff); + DBUG_ASSERT(hdr->offset_size <= MAX_OFFSET_LENGTH_NM); + /* size of data offset, named format flag, size of names offset (0 means 2) */ + str->str[0]= + (char) ((str->str[0] & ~(DYNCOL_FLG_OFFSET | DYNCOL_FLG_NMOFFSET)) | + (hdr->offset_size - 2) | DYNCOL_FLG_NAMES); + int2store(str->str + 1, hdr->column_count); /* columns number */ + int2store(str->str + 3, hdr->nmpool_size); + hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE_NM; + hdr->nmpool= hdr->header + hdr->header_size; + hdr->dtpool= hdr->nmpool + hdr->nmpool_size; +} + + +/** + Store offset and type information in the given place + + @param place Beginning of the index entry + @param offset_size Size of offset field in bytes + @param type Type to be written + @param offset Offset to be written +*/ + +static my_bool type_and_offset_store_num(uchar *place, size_t offset_size, + DYNAMIC_COLUMN_TYPE type, + size_t offset) +{ + ulong val = (((ulong) offset) << 3) | (type - 1); + DBUG_ASSERT(type != DYN_COL_NULL); + DBUG_ASSERT(((type - 1) & (~7)) == 0); /* fit in 3 bits */ + DBUG_ASSERT(offset_size >= 1 && offset_size <= 4); + + /* Index entry starts with column number; jump over it */ + place+= COLUMN_NUMBER_SIZE; + + switch (offset_size) { + case 1: + if (offset >= 0x1f) /* all 1 value is reserved */ + return TRUE; + place[0]= (uchar)val; + break; + case 2: + if (offset >= 0x1fff) /* all 1 value is reserved */ + return TRUE; + int2store(place, val); + break; + case 3: + if (offset >= 0x1fffff) /* all 1 value is reserved */ + return TRUE; + int3store(place, val); + break; + case 4: + if (offset >= 0x1fffffff) /* all 1 value is reserved */ + return TRUE; + int4store(place, val); + break; + default: + return TRUE; + } + return FALSE; +} + + +static my_bool type_and_offset_store_named(uchar *place, size_t offset_size, + DYNAMIC_COLUMN_TYPE type, + size_t offset) +{ + ulonglong val = (((ulong) offset) << 4) | (type - 1); + DBUG_ASSERT(type != DYN_COL_NULL); + DBUG_ASSERT(((type - 1) & (~0xf)) == 0); /* fit in 4 bits */ + DBUG_ASSERT(offset_size >= 2 && offset_size <= 5); + + /* Index entry starts with name offset; jump over it */ + place+= COLUMN_NAMEPTR_SIZE; + switch (offset_size) { + case 2: + if (offset >= 0xfff) /* all 1 value is reserved */ + return TRUE; + int2store(place, val); + break; + case 3: + if (offset >= 0xfffff) /* all 1 value is reserved */ + return TRUE; + int3store(place, val); + break; + case 4: + if (offset >= 0xfffffff) /* all 1 value is reserved */ + return TRUE; + int4store(place, val); + break; + case 5: +#if SIZEOF_SIZE_T > 4 + if (offset >= 0xfffffffffull) /* all 1 value is reserved */ + return TRUE; +#endif + int5store(place, val); + break; + case 1: + default: + return TRUE; + } + return FALSE; +} + +/** + Write numeric format header entry + 2 bytes - column number + 1-4 bytes - data offset combined with type + + @param hdr descriptor of dynamic column record + @param column_key pointer to uint (column number) + @param value value which will be written (only type used) + @param offset offset of the data +*/ + +static my_bool put_header_entry_num(DYN_HEADER *hdr, + void *column_key, + DYNAMIC_COLUMN_VALUE *value, + size_t offset) +{ + uint *column_number= (uint *)column_key; + int2store(hdr->entry, *column_number); + DBUG_ASSERT(hdr->nmpool_size == 0); + if (type_and_offset_store_num(hdr->entry, hdr->offset_size, + value->type, + offset)) + return TRUE; + hdr->entry= hdr->entry + hdr->entry_size; + return FALSE; +} + + +/** + Write names format header entry + 1 byte - name length + 2 bytes - name offset in the name pool + 1-4 bytes - data offset combined with type + + @param hdr descriptor of dynamic column record + @param column_key pointer to LEX_STRING (column name) + @param value value which will be written (only type used) + @param offset offset of the data +*/ + +static my_bool put_header_entry_named(DYN_HEADER *hdr, + void *column_key, + DYNAMIC_COLUMN_VALUE *value, + size_t offset) +{ + LEX_STRING *column_name= (LEX_STRING *)column_key; + DBUG_ASSERT(column_name->length <= MAX_NAME_LENGTH); + DBUG_ASSERT(hdr->name - hdr->nmpool < (long) 0x10000L); + int2store(hdr->entry, hdr->name - hdr->nmpool); + memcpy(hdr->name, column_name->str, column_name->length); + DBUG_ASSERT(hdr->nmpool_size != 0 || column_name->length == 0); + if (type_and_offset_store_named(hdr->entry, hdr->offset_size, + value->type, + offset)) + return TRUE; + hdr->entry+= hdr->entry_size; + hdr->name+= column_name->length; + return FALSE; +} + + +/** + Calculate length of offset field for given data length + + @param data_length Length of the data segment + + @return number of bytes +*/ + +static size_t dynamic_column_offset_bytes_num(size_t data_length) +{ + if (data_length < 0x1f) /* all 1 value is reserved */ + return 1; + if (data_length < 0x1fff) /* all 1 value is reserved */ + return 2; + if (data_length < 0x1fffff) /* all 1 value is reserved */ + return 3; + if (data_length < 0x1fffffff) /* all 1 value is reserved */ + return 4; + return MAX_OFFSET_LENGTH + 1; /* For an error generation*/ +} + +static size_t dynamic_column_offset_bytes_named(size_t data_length) +{ + if (data_length < 0xfff) /* all 1 value is reserved */ + return 2; + if (data_length < 0xfffff) /* all 1 value is reserved */ + return 3; + if (data_length < 0xfffffff) /* all 1 value is reserved */ + return 4; +#if SIZEOF_SIZE_T > 4 + if (data_length < 0xfffffffffull) /* all 1 value is reserved */ +#endif + return 5; + return MAX_OFFSET_LENGTH_NM + 1; /* For an error generation */ +} + +/** + Read offset and type information from index entry + + @param type Where to put type info + @param offset Where to put offset info + @param place beginning of the type and offset + @param offset_size Size of offset field in bytes +*/ + +static my_bool type_and_offset_read_num(DYNAMIC_COLUMN_TYPE *type, + size_t *offset, + uchar *place, size_t offset_size) +{ + ulong UNINIT_VAR(val); + ulong UNINIT_VAR(lim); + + DBUG_ASSERT(offset_size >= 1 && offset_size <= 4); + + switch (offset_size) { + case 1: + val= (ulong)place[0]; + lim= 0x1f; + break; + case 2: + val= uint2korr(place); + lim= 0x1fff; + break; + case 3: + val= uint3korr(place); + lim= 0x1fffff; + break; + case 4: + val= uint4korr(place); + lim= 0x1fffffff; + break; + default: + DBUG_ASSERT(0); /* impossible */ + return 1; + } + *type= (val & 0x7) + 1; + *offset= val >> 3; + return (*offset >= lim); +} + +static my_bool type_and_offset_read_named(DYNAMIC_COLUMN_TYPE *type, + size_t *offset, + uchar *place, size_t offset_size) +{ + ulonglong UNINIT_VAR(val); + ulonglong UNINIT_VAR(lim); + DBUG_ASSERT(offset_size >= 2 && offset_size <= 5); + + switch (offset_size) { + case 2: + val= uint2korr(place); + lim= 0xfff; + break; + case 3: + val= uint3korr(place); + lim= 0xfffff; + break; + case 4: + val= uint4korr(place); + lim= 0xfffffff; + break; + case 5: + val= uint5korr(place); + lim= 0xfffffffffull; + break; + case 1: + default: + DBUG_ASSERT(0); /* impossible */ + return 1; + } + *type= (val & 0xf) + 1; + *offset= val >> 4; + return (*offset >= lim); +} + +/** + Format descriptor, contain constants and function references for + format processing +*/ + +struct st_service_funcs +{ + /* size of fixed header */ + uint fixed_hdr; + /* size of fixed part of header entry */ + uint fixed_hdr_entry; + + /*size of array element which stores keys */ + uint key_size_in_array; + + /* Maximum data offset size in bytes */ + size_t max_offset_size; + + size_t (*name_size) + (void *, uint); + int (*column_sort) + (const void *a, const void *b); + my_bool (*check_limit) + (const void *val); + void (*set_fixed_hdr) + (DYNAMIC_COLUMN *str, DYN_HEADER *hdr); + my_bool (*put_header_entry)(DYN_HEADER *hdr, + void *column_key, + DYNAMIC_COLUMN_VALUE *value, + size_t offset); + int (*plan_sort)(const void *a, const void *b); + size_t (*dynamic_column_offset_bytes)(size_t data_length); + my_bool (*type_and_offset_read)(DYNAMIC_COLUMN_TYPE *type, + size_t *offset, + uchar *place, size_t offset_size); + +}; + + +/** + Actual our 2 format descriptors +*/ + +static struct st_service_funcs fmt_data[2]= +{ + { + FIXED_HEADER_SIZE, + COLUMN_NUMBER_SIZE, + sizeof(uint), + MAX_OFFSET_LENGTH, + &name_size_num, + &column_sort_num, + &check_limit_num, + &set_fixed_header_num, + &put_header_entry_num, + &plan_sort_num, + &dynamic_column_offset_bytes_num, + &type_and_offset_read_num + }, + { + FIXED_HEADER_SIZE_NM, + COLUMN_NAMEPTR_SIZE, + sizeof(LEX_STRING), + MAX_OFFSET_LENGTH_NM, + &name_size_named, + &column_sort_named, + &check_limit_named, + &set_fixed_header_named, + &put_header_entry_named, + &plan_sort_named, + &dynamic_column_offset_bytes_named, + &type_and_offset_read_named + } +}; + + +/** + Read dynamic column record header and fill the descriptor + + @param hdr dynamic columns record descriptor to fill + @param str dynamic columns record + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +init_read_hdr(DYN_HEADER *hdr, DYNAMIC_COLUMN *str) +{ + if (read_fixed_header(hdr, str)) + return ER_DYNCOL_FORMAT; + hdr->header= (uchar*)str->str + fmt_data[hdr->format].fixed_hdr; + calc_param(&hdr->entry_size, &hdr->header_size, + fmt_data[hdr->format].fixed_hdr_entry, hdr->offset_size, + hdr->column_count); + hdr->nmpool= hdr->header + hdr->header_size; + hdr->dtpool= hdr->nmpool + hdr->nmpool_size; + hdr->data_size= str->length - fmt_data[hdr->format].fixed_hdr - + hdr->header_size - hdr->nmpool_size; + hdr->data_end= (uchar*)str->str + str->length; + return ER_DYNCOL_OK; +} + + +/** + Initialize dynamic column string with (make it empty but correct format) + + @param str The string to initialize + @param size Amount of preallocated memory for the string. + + @retval FALSE OK + @retval TRUE error +*/ + +static my_bool dynamic_column_init_named(DYNAMIC_COLUMN *str, size_t size) +{ + DBUG_ASSERT(size != 0); + + /* + Make string with no fields (empty header) + - First \0 is flags + - other 2 \0 is number of fields + */ + if (init_dynamic_string(str, NULL, size, DYNCOL_SYZERESERVE)) + return TRUE; + return FALSE; +} + + +/** + Calculate how many bytes needed to store val as variable length integer + where first bit indicate continuation of the sequence. + + @param val The value for which we are calculating length + + @return number of bytes +*/ + +static size_t dynamic_column_var_uint_bytes(ulonglong val) +{ + size_t len= 0; + do + { + len++; + val>>= 7; + } while (val); + return len; +} + + +/** + Stores variable length unsigned integer value to a string + + @param str The string where to append the value + @param val The value to put in the string + + @return ER_DYNCOL_* return code + + @notes + This is used to store a number together with other data in the same + object. (Like decimals, length of string etc) + (As we don't know the length of this object, we can't store 0 in 0 bytes) +*/ + +static enum enum_dyncol_func_result +dynamic_column_var_uint_store(DYNAMIC_COLUMN *str, ulonglong val) +{ + if (dynstr_realloc(str, 10)) /* max what we can use */ + return ER_DYNCOL_RESOURCE; + + do + { + ulonglong rest= val >> 7; + str->str[str->length++]= ((val & 0x7f) | (rest ? 0x80 : 0x00)); + val= rest; + } while (val); + return ER_DYNCOL_OK; +} + + +/** + Reads variable length unsigned integer value from a string + + @param data The string from which the int should be read + @param data_length Max length of data + @param len Where to put length of the string read in bytes + + @return value of the unsigned integer read from the string + + In case of error, *len is set to 0 +*/ + +static ulonglong +dynamic_column_var_uint_get(uchar *data, size_t data_length, + size_t *len) +{ + ulonglong val= 0; + uint length; + uchar *end= data + data_length; + + for (length=0; data < end ; data++) + { + val+= (((ulonglong)((*data) & 0x7f)) << (length * 7)); + length++; + if (!((*data) & 0x80)) + { + /* End of data */ + *len= length; + return val; + } + } + /* Something was wrong with data */ + *len= 0; /* Mark error */ + return 0; +} + + +/** + Calculate how many bytes needed to store val as unsigned. + + @param val The value for which we are calculating length + + @return number of bytes (0-8) +*/ + +static size_t dynamic_column_uint_bytes(ulonglong val) +{ + size_t len; + + for (len= 0; val ; val>>= 8, len++) + ; + return len; +} + + +/** + Append the string with given unsigned int value. + + @param str The string where to put the value + @param val The value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_uint_store(DYNAMIC_COLUMN *str, ulonglong val) +{ + if (dynstr_realloc(str, 8)) /* max what we can use */ + return ER_DYNCOL_RESOURCE; + + for (; val; val>>= 8) + str->str[str->length++]= (char) (val & 0xff); + return ER_DYNCOL_OK; +} + + +/** + Read unsigned int value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_uint_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + ulonglong value= 0; + size_t i; + + for (i= 0; i < length; i++) + value+= ((ulonglong)data[i]) << (i*8); + + store_it_here->x.ulong_value= value; + return ER_DYNCOL_OK; +} + +/** + Calculate how many bytes needed to store val as signed in following encoding: + 0 -> 0 + -1 -> 1 + 1 -> 2 + -2 -> 3 + 2 -> 4 + ... + + @param val The value for which we are calculating length + + @return number of bytes +*/ + +static size_t dynamic_column_sint_bytes(longlong val) +{ + return dynamic_column_uint_bytes((val << 1) ^ + (val < 0 ? 0xffffffffffffffffull : 0)); +} + + +/** + Append the string with given signed int value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_sint_store(DYNAMIC_COLUMN *str, longlong val) +{ + return dynamic_column_uint_store(str, + (val << 1) ^ + (val < 0 ? 0xffffffffffffffffULL : 0)); +} + + +/** + Read signed int value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_sint_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + ulonglong val; + dynamic_column_uint_read(store_it_here, data, length); + val= store_it_here->x.ulong_value; + if (val & 1) + val= (val >> 1) ^ 0xffffffffffffffffULL; + else + val>>= 1; + store_it_here->x.long_value= (longlong) val; + return ER_DYNCOL_OK; +} + + +/** + Calculate how many bytes needed to store the value. + + @param value The value for which we are calculating length + + @return + Error: (size_t) ~0 + ok number of bytes +*/ + +static size_t +dynamic_column_value_len(DYNAMIC_COLUMN_VALUE *value, + enum enum_dyncol_format format) +{ + switch (value->type) { + case DYN_COL_NULL: + return 0; + case DYN_COL_INT: + return dynamic_column_sint_bytes(value->x.long_value); + case DYN_COL_UINT: + return dynamic_column_uint_bytes(value->x.ulong_value); + case DYN_COL_DOUBLE: + return 8; + case DYN_COL_STRING: +#ifdef LIBMARIADB + return (dynamic_column_var_uint_bytes(value->x.string.charset->nr) + + value->x.string.value.length); +#else + return (dynamic_column_var_uint_bytes(value->x.string.charset->number) + + value->x.string.value.length); +#endif +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + { + int precision= value->x.decimal.value.intg + value->x.decimal.value.frac; + int scale= value->x.decimal.value.frac; + + if (precision == 0 || decimal_is_zero(&value->x.decimal.value)) + { + /* This is here to simplify dynamic_column_decimal_store() */ + value->x.decimal.value.intg= value->x.decimal.value.frac= 0; + return 0; + } + /* + Check if legal decimal; This is needed to not get an assert in + decimal_bin_size(). However this should be impossible as all + decimals entered here should be valid and we have the special check + above to handle the unlikely but possible case that decimal.value.intg + and decimal.frac is 0. + */ + if (scale < 0 || precision <= 0) + { + DBUG_ASSERT(0); /* Impossible */ + return (size_t) ~0; + } + return (dynamic_column_var_uint_bytes(value->x.decimal.value.intg) + + dynamic_column_var_uint_bytes(value->x.decimal.value.frac) + + decimal_bin_size(precision, scale)); + } +#endif + case DYN_COL_DATETIME: + if (format == dyncol_fmt_num || value->x.time_value.second_part) + /* date+time in bits: 14 + 4 + 5 + 10 + 6 + 6 + 20 + 1 66bits ~= 9 bytes*/ + return 9; + else + return 6; + case DYN_COL_DATE: + /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/ + return 3; + case DYN_COL_TIME: + if (format == dyncol_fmt_num || value->x.time_value.second_part) + /* time in bits: 10 + 6 + 6 + 20 + 1 = 43bits ~= 6bytes*/ + return 6; + else + return 3; + case DYN_COL_DYNCOL: + return value->x.string.value.length; + } + DBUG_ASSERT(0); + return 0; +} + + +/** + Append double value to a string + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_double_store(DYNAMIC_COLUMN *str, double val) +{ + if (dynstr_realloc(str, 8)) + return ER_DYNCOL_RESOURCE; + float8store(str->str + str->length, val); + str->length+= 8; + return ER_DYNCOL_OK; +} + + +/** + Read double value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_double_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + if (length != 8) + return ER_DYNCOL_FORMAT; + float8get(store_it_here->x.double_value, data); + return ER_DYNCOL_OK; +} + + +/** + Append the string with given string value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_string_store(DYNAMIC_COLUMN *str, LEX_STRING *string, + CHARSET_INFO *charset) +{ + enum enum_dyncol_func_result rc; +#ifdef LIBMARIADB + if ((rc= dynamic_column_var_uint_store(str, charset->nr))) +#else + if ((rc= dynamic_column_var_uint_store(str, charset->number))) +#endif + return rc; + if (dynstr_append_mem(str, string->str, string->length)) + return ER_DYNCOL_RESOURCE; + return ER_DYNCOL_OK; +} + +/** + Append the string with given string value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_dyncol_store(DYNAMIC_COLUMN *str, LEX_STRING *string) +{ + if (dynstr_append_mem(str, string->str, string->length)) + return ER_DYNCOL_RESOURCE; + return ER_DYNCOL_OK; +} + +/** + Read string value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_string_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + size_t len; + uint charset_nr= (uint)dynamic_column_var_uint_get(data, length, &len); + if (len == 0) /* Wrong packed number */ + return ER_DYNCOL_FORMAT; + store_it_here->x.string.charset= get_charset_by_nr(charset_nr); + if (store_it_here->x.string.charset == NULL) + return ER_DYNCOL_UNKNOWN_CHARSET; + data+= len; + store_it_here->x.string.value.length= (length-= len); + store_it_here->x.string.value.str= (char*) data; + return ER_DYNCOL_OK; +} + +/** + Read Dynamic columns packet string value of given length + from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_dyncol_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + store_it_here->x.string.charset= my_charset_bin; + store_it_here->x.string.value.length= length; + store_it_here->x.string.value.str= (char*) data; + return ER_DYNCOL_OK; +} + +/** + Append the string with given decimal value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ +#ifndef LIBMARIADB +static enum enum_dyncol_func_result +dynamic_column_decimal_store(DYNAMIC_COLUMN *str, + decimal_t *value) +{ + uint bin_size; + int precision= value->intg + value->frac; + + /* Store decimal zero as empty string */ + if (precision == 0) + return ER_DYNCOL_OK; + + bin_size= decimal_bin_size(precision, value->frac); + if (dynstr_realloc(str, bin_size + 20)) + return ER_DYNCOL_RESOURCE; + + /* The following can't fail as memory is already allocated */ + (void) dynamic_column_var_uint_store(str, value->intg); + (void) dynamic_column_var_uint_store(str, value->frac); + + decimal2bin(value, (uchar *) str->str + str->length, + precision, value->frac); + str->length+= bin_size; + return ER_DYNCOL_OK; +} + + +/** + Prepare the value to be used as decimal. + + @param value The value structure which sould be setup. +*/ + +void mariadb_dyncol_prepare_decimal(DYNAMIC_COLUMN_VALUE *value) +{ + value->x.decimal.value.buf= value->x.decimal.buffer; + value->x.decimal.value.len= DECIMAL_BUFF_LENGTH; + /* just to be safe */ + value->type= DYN_COL_DECIMAL; + decimal_make_zero(&value->x.decimal.value); +} + +void dynamic_column_prepare_decimal(DYNAMIC_COLUMN_VALUE *value) +{ + mariadb_dyncol_prepare_decimal(value); +} + + + +/** + Read decimal value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_decimal_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + size_t intg_len, frac_len; + int intg, frac, precision, scale; + + dynamic_column_prepare_decimal(store_it_here); + /* Decimals 0.0 is stored as a zero length string */ + if (length == 0) + return ER_DYNCOL_OK; /* value contains zero */ + + intg= (int)dynamic_column_var_uint_get(data, length, &intg_len); + data+= intg_len; + frac= (int)dynamic_column_var_uint_get(data, length - intg_len, &frac_len); + data+= frac_len; + + /* Check the size of data is correct */ + precision= intg + frac; + scale= frac; + if (scale < 0 || precision <= 0 || scale > precision || + (length - intg_len - frac_len) > + (size_t) (DECIMAL_BUFF_LENGTH*sizeof(decimal_digit_t)) || + decimal_bin_size(intg + frac, frac) != + (int) (length - intg_len - frac_len)) + return ER_DYNCOL_FORMAT; + + if (bin2decimal(data, &store_it_here->x.decimal.value, precision, scale) != + E_DEC_OK) + return ER_DYNCOL_FORMAT; + return ER_DYNCOL_OK; +} +#endif + +/** + Append the string with given datetime value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value, + enum enum_dyncol_format format) +{ + enum enum_dyncol_func_result rc; + /* + 0<----year---->00000!<-hours--><---microseconds---> + 12345678901234123412345 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456><123456><123456><123456> + */ + if ((rc= dynamic_column_date_store(str, value)) || + (rc= dynamic_column_time_store(str, value, format))) + return rc; + return ER_DYNCOL_OK; +} + + +/** + Read datetime value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_time_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT; + /* + 0<----year---->00000!<-hours--><---microseconds---> + 12345678901234123412345 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456><123456><123456><123456> + */ + if (length != 9 && length != 6) + goto err; + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + if ((rc= dynamic_column_date_read_internal(store_it_here, data, 3)) || + (rc= dynamic_column_time_read_internal(store_it_here, data + 3, + length - 3))) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return rc; +} + + +/** + Append the string with given time value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value, + enum enum_dyncol_format format) +{ + uchar *buf; + if (dynstr_realloc(str, 6)) + return ER_DYNCOL_RESOURCE; + + buf= ((uchar *)str->str) + str->length; + + if (value->time_type == MYSQL_TIMESTAMP_NONE || + value->time_type == MYSQL_TIMESTAMP_ERROR || + value->time_type == MYSQL_TIMESTAMP_DATE) + { + value->neg= 0; + value->second_part= 0; + value->hour= 0; + value->minute= 0; + value->second= 0; + } + DBUG_ASSERT(value->hour <= 838); + DBUG_ASSERT(value->minute <= 59); + DBUG_ASSERT(value->second <= 59); + DBUG_ASSERT(value->second_part <= 999999); + if (format == dyncol_fmt_num || value->second_part) + { + /* + 00000!<-hours--><---microseconds---> + 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456> + */ + buf[0]= (value->second_part & 0xff); + buf[1]= ((value->second_part & 0xff00) >> 8); + buf[2]= (uchar)(((value->second & 0xf) << 4) | + ((value->second_part & 0xf0000) >> 16)); + buf[3]= ((value->minute << 2) | ((value->second & 0x30) >> 4)); + buf[4]= (value->hour & 0xff); + buf[5]= ((value->neg ? 0x4 : 0) | (value->hour >> 8)); + str->length+= 6; + } + else + { + /* + !<-hours--> + 11234567890123456123456 + <123456><123456><123456> + */ + buf[0]= (value->second) | ((value->minute & 0x3) << 6); + buf[1]= (value->minute >> 2) | ((value->hour & 0xf) << 4); + buf[2]= (value->hour >> 4) | (value->neg ? 0x80 : 0); + str->length+= 3; + } + + return ER_DYNCOL_OK; +} + + +/** + Read time value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + store_it_here->x.time_value.year= store_it_here->x.time_value.month= + store_it_here->x.time_value.day= 0; + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_TIME; + return dynamic_column_time_read_internal(store_it_here, data, length); +} + +/** + Internal function for reading time part from the string. + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + if (length != 6 && length != 3) + goto err; + if (length == 6) + { + /* + 00000!<-hours--><---microseconds---> + 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456> + */ + store_it_here->x.time_value.second_part= (data[0] | + (data[1] << 8) | + ((data[2] & 0xf) << 16)); + store_it_here->x.time_value.second= ((data[2] >> 4) | + ((data[3] & 0x3) << 4)); + store_it_here->x.time_value.minute= (data[3] >> 2); + store_it_here->x.time_value.hour= (((((uint)data[5]) & 0x3 ) << 8) | data[4]); + store_it_here->x.time_value.neg= ((data[5] & 0x4) ? 1 : 0); + } + else + { + /* + !<-hours--> + 11234567890123456123456 + <123456><123456><123456> + */ + store_it_here->x.time_value.second_part= 0; + store_it_here->x.time_value.second= (data[0] & 0x3f); + store_it_here->x.time_value.minute= (data[0] >> 6) | ((data[1] & 0xf) << 2); + store_it_here->x.time_value.hour= (data[1] >> 4) | ((data[2] & 0x3f) << 4); + store_it_here->x.time_value.neg= ((data[2] & 0x80) ? 1 : 0); + } + if (store_it_here->x.time_value.second > 59 || + store_it_here->x.time_value.minute > 59 || + store_it_here->x.time_value.hour > 838 || + store_it_here->x.time_value.second_part > 999999) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return ER_DYNCOL_FORMAT; +} + + +/** + Append the string with given date value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value) +{ + uchar *buf; + if (dynstr_realloc(str, 3)) + return ER_DYNCOL_RESOURCE; + + buf= ((uchar *)str->str) + str->length; + if (value->time_type == MYSQL_TIMESTAMP_NONE || + value->time_type == MYSQL_TIMESTAMP_ERROR || + value->time_type == MYSQL_TIMESTAMP_TIME) + value->year= value->month= value->day = 0; + DBUG_ASSERT(value->year <= 9999); + DBUG_ASSERT(value->month <= 12); + DBUG_ASSERT(value->day <= 31); + /* + 0<----year----> + 012345678901234123412345 + <123456><123456><123456> + */ + buf[0]= (value->day | + ((value->month & 0x7) << 5)); + buf[1]= ((value->month >> 3) | ((value->year & 0x7F) << 1)); + buf[2]= (value->year >> 7); + str->length+= 3; + return ER_DYNCOL_OK; +} + + + +/** + Read date value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + store_it_here->x.time_value.neg= 0; + store_it_here->x.time_value.second_part= 0; + store_it_here->x.time_value.hour= 0; + store_it_here->x.time_value.minute= 0; + store_it_here->x.time_value.second= 0; + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATE; + return dynamic_column_date_read_internal(store_it_here, data, length); +} + +/** + Internal function for reading date part from the string. + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, + size_t length) +{ + if (length != 3) + goto err; + /* + 0<----year----> + 12345678901234123412345 + <123456><123456><123456> + */ + store_it_here->x.time_value.day= (data[0] & 0x1f); + store_it_here->x.time_value.month= (((data[1] & 0x1) << 3) | + (data[0] >> 5)); + store_it_here->x.time_value.year= ((((uint)data[2]) << 7) | + (data[1] >> 1)); + if (store_it_here->x.time_value.day > 31 || + store_it_here->x.time_value.month > 12 || + store_it_here->x.time_value.year > 9999) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return ER_DYNCOL_FORMAT; +} + + +/** + Append the string with given value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +data_store(DYNAMIC_COLUMN *str, DYNAMIC_COLUMN_VALUE *value, + enum enum_dyncol_format format) +{ + switch (value->type) { + case DYN_COL_INT: + return dynamic_column_sint_store(str, value->x.long_value); + case DYN_COL_UINT: + return dynamic_column_uint_store(str, value->x.ulong_value); + case DYN_COL_DOUBLE: + return dynamic_column_double_store(str, value->x.double_value); + case DYN_COL_STRING: + return dynamic_column_string_store(str, &value->x.string.value, + value->x.string.charset); +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + return dynamic_column_decimal_store(str, &value->x.decimal.value); +#endif + case DYN_COL_DATETIME: + /* date+time in bits: 14 + 4 + 5 + 5 + 6 + 6 40bits = 5 bytes */ + return dynamic_column_date_time_store(str, &value->x.time_value, format); + case DYN_COL_DATE: + /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/ + return dynamic_column_date_store(str, &value->x.time_value); + case DYN_COL_TIME: + /* time in bits: 5 + 6 + 6 = 17bits ~= 3bytes*/ + return dynamic_column_time_store(str, &value->x.time_value, format); + case DYN_COL_DYNCOL: + return dynamic_column_dyncol_store(str, &value->x.string.value); + case DYN_COL_NULL: + break; /* Impossible */ + } + DBUG_ASSERT(0); + return ER_DYNCOL_OK; /* Impossible */ +} + + +/** + Write information to the fixed header + + @param str String where to write the header + @param offset_size Size of offset field in bytes + @param column_count Number of columns +*/ + +static void set_fixed_header(DYNAMIC_COLUMN *str, + uint offset_size, + uint column_count) +{ + DBUG_ASSERT(column_count <= 0xffff); + DBUG_ASSERT(offset_size <= MAX_OFFSET_LENGTH); + str->str[0]= ((str->str[0] & ~DYNCOL_FLG_OFFSET) | + (offset_size - 1)); /* size of offset */ + int2store(str->str + 1, column_count); /* columns number */ + DBUG_ASSERT((str->str[0] & (~DYNCOL_FLG_KNOWN)) == 0); +} + +/** + Adds columns into the empty string + + @param str String where to write the data (the record) + @param hdr Dynamic columns record descriptor + @param column_count Number of columns in the arrays + @param column_keys Array of columns keys (uint or LEX_STRING) + @param values Array of columns values + @param new_str True if we need to allocate new string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_new_column_store(DYNAMIC_COLUMN *str, + DYN_HEADER *hdr, + uint column_count, + void *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_str) +{ + struct st_service_funcs *fmt= fmt_data + hdr->format; + void **columns_order; + uchar *element; + uint i; + enum enum_dyncol_func_result rc= ER_DYNCOL_RESOURCE; + size_t all_headers_size; + + if (!(columns_order= malloc(sizeof(void*)*column_count))) + return ER_DYNCOL_RESOURCE; + if (new_str) + { + if (dynamic_column_init_named(str, + fmt->fixed_hdr + + hdr->header_size + + hdr->nmpool_size + + hdr->data_size + + DYNCOL_SYZERESERVE)) + goto err; + } + else + { + str->length= 0; + if (dynstr_realloc(str, + fmt->fixed_hdr + + hdr->header_size + + hdr->nmpool_size + + hdr->data_size + + DYNCOL_SYZERESERVE)) + goto err; + } + if (!column_count) + return ER_DYNCOL_OK; + + bzero(str->str, fmt->fixed_hdr); + str->length= fmt->fixed_hdr; + + /* sort columns for the header */ + for (i= 0, element= (uchar *) column_keys; + i < column_count; + i++, element+= fmt->key_size_in_array) + columns_order[i]= (void *)element; + qsort(columns_order, (size_t)column_count, sizeof(void*), fmt->column_sort); + + /* + For now we don't allow creating two columns with the same number + at the time of create. This can be fixed later to just use the later + by comparing the pointers. + */ + for (i= 0; i < column_count - 1; i++) + { + if ((*fmt->check_limit)(&columns_order[i]) || + (*fmt->column_sort)(&columns_order[i], &columns_order[i + 1]) == 0) + { + rc= ER_DYNCOL_DATA; + goto err; + } + } + if ((*fmt->check_limit)(&columns_order[i])) + { + rc= ER_DYNCOL_DATA; + goto err; + } + + (*fmt->set_fixed_hdr)(str, hdr); + /* reserve place for header and name pool */ + str->length+= hdr->header_size + hdr->nmpool_size; + + hdr->entry= hdr->header; + hdr->name= hdr->nmpool; + all_headers_size= fmt->fixed_hdr + hdr->header_size + hdr->nmpool_size; + for (i= 0; i < column_count; i++) + { + uint ord= (uint)(((uchar*)columns_order[i] - (uchar*)column_keys) / + fmt->key_size_in_array); + if (values[ord].type != DYN_COL_NULL) + { + /* Store header first in the str */ + if ((*fmt->put_header_entry)(hdr, columns_order[i], values + ord, + str->length - all_headers_size)) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + + /* Store value in 'str + str->length' and increase str->length */ + if ((rc= data_store(str, values + ord, hdr->format))) + goto err; + } + } + rc= ER_DYNCOL_OK; +err: + free(columns_order); + return rc; +} + +/** + Calculate size of header, name pool and data pool + + @param hdr descriptor of dynamic column record + @param column_count number of elements in arrays + @param column_count Number of columns in the arrays + @param column_keys Array of columns keys (uint or LEX_STRING) + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +calc_var_sizes(DYN_HEADER *hdr, + uint column_count, + void *column_keys, + DYNAMIC_COLUMN_VALUE *values) +{ + struct st_service_funcs *fmt= fmt_data + hdr->format; + uint i; + hdr->nmpool_size= hdr->data_size= 0; + hdr->column_count= 0; + for (i= 0; i < column_count; i++) + { + if (values[i].type != DYN_COL_NULL) + { + size_t tmp; + hdr->column_count++; + hdr->data_size+= (tmp= dynamic_column_value_len(values + i, + hdr->format)); + if (tmp == (size_t) ~0) + return ER_DYNCOL_DATA; + hdr->nmpool_size+= (*fmt->name_size)(column_keys, i); + } + } + /* + We can handle data up to 0x1fffffff (old format) and + 0xfffffffff (new format) bytes now. + */ + if ((hdr->offset_size= fmt->dynamic_column_offset_bytes(hdr->data_size)) >= + fmt->max_offset_size) + return ER_DYNCOL_LIMIT; + + /* header entry is column number or string pointer + offset & type */ + hdr->entry_size= fmt->fixed_hdr_entry + hdr->offset_size; + hdr->header_size= hdr->column_count * hdr->entry_size; + return ER_DYNCOL_OK; +} + +/** + Create packed string which contains given columns (internal multi format) + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_keys Array of columns keys (format dependent) + @param values Array of columns values + @param new_str True if we need allocate new string + @param string_keys keys are strings + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_create_many_internal_fmt(DYNAMIC_COLUMN *str, + uint column_count, + void *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_str, + my_bool string_keys) +{ + DYN_HEADER header; + enum enum_dyncol_func_result rc; + bzero(&header, sizeof(header)); + header.format= (string_keys ? 1 : 0); + + if (new_str) + { + /* to make dynstr_free() working in case of errors */ + bzero(str, sizeof(DYNAMIC_COLUMN)); + } + + if ((rc= calc_var_sizes(&header, column_count, column_keys, values)) < 0) + return rc; + + return dynamic_new_column_store(str, &header, + column_count, + column_keys, values, + new_str); +} + + +/** + Create packed string which contains given columns + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_create_many(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values) +{ + DBUG_ENTER("dynamic_column_create_many"); + DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count, + column_numbers, values, + TRUE, FALSE)); +} + +/** + Create packed string which contains given columns + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + @param new_string True if we need allocate new string + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_create_many(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_string) +{ + DBUG_ENTER("mariadb_dyncol_create_many"); + DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count, + column_numbers, values, + new_string, FALSE)); +} + +/** + Create packed string which contains given columns + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_keys Array of columns keys + @param values Array of columns value + @param new_string True if we need allocate new string + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_create_many_named(DYNAMIC_COLUMN *str, + uint column_count, + LEX_STRING *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_string) +{ + DBUG_ENTER("mariadb_dyncol_create_many_named"); + DBUG_RETURN(dynamic_column_create_many_internal_fmt(str, column_count, + column_keys, values, + new_string, TRUE)); +} + +/** + Create packed string which contains given column + + @param str String where to write the data + @param column_number Column number + @param value The columns value + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_create(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *value) +{ + DBUG_ENTER("dynamic_column_create"); + DBUG_RETURN(dynamic_column_create_many(str, 1, &column_nr, value)); +} + + +/** + Calculate length of data between given two header entries + + @param entry Pointer to the first entry + @param entry_next Pointer to the last entry + @param header_end Pointer to the header end + @param offset_size Size of offset field in bytes + @param last_offset Size of the data segment + + @return number of bytes +*/ + +static size_t get_length_interval(uchar *entry, uchar *entry_next, + uchar *header_end, size_t offset_size, + size_t last_offset) +{ + size_t offset, offset_next; + DYNAMIC_COLUMN_TYPE type, type_next; + DBUG_ASSERT(entry < entry_next); + + if (type_and_offset_read_num(&type, &offset, entry + COLUMN_NUMBER_SIZE, + offset_size)) + return DYNCOL_OFFSET_ERROR; + if (entry_next >= header_end) + return (last_offset - offset); + if (type_and_offset_read_num(&type_next, &offset_next, + entry_next + COLUMN_NUMBER_SIZE, offset_size)) + return DYNCOL_OFFSET_ERROR; + return (offset_next - offset); +} + + +/** + Calculate length of data between given hdr->entry and next_entry + + @param hdr descriptor of dynamic column record + @param next_entry next header entry (can point just after last header + entry) + + @return number of bytes +*/ + +static size_t hdr_interval_length(DYN_HEADER *hdr, uchar *next_entry) +{ + struct st_service_funcs *fmt= fmt_data + hdr->format; + size_t next_entry_offset; + DYNAMIC_COLUMN_TYPE next_entry_type; + DBUG_ASSERT(hdr->entry < next_entry); + DBUG_ASSERT(hdr->entry >= hdr->header); + DBUG_ASSERT(next_entry <= hdr->header + hdr->header_size); + + if ((*fmt->type_and_offset_read)(&hdr->type, &hdr->offset, + hdr->entry + fmt->fixed_hdr_entry, + hdr->offset_size)) + return DYNCOL_OFFSET_ERROR; + if (next_entry == hdr->header + hdr->header_size) + return hdr->data_size - hdr->offset; + if ((*fmt->type_and_offset_read)(&next_entry_type, &next_entry_offset, + next_entry + fmt->fixed_hdr_entry, + hdr->offset_size)) + return DYNCOL_OFFSET_ERROR; + return (next_entry_offset - hdr->offset); +} + + +/** + Comparator function for references to header entries for qsort +*/ + +static int header_compar_num(const void *a, const void *b) +{ + uint va= uint2korr((uchar*)a), vb= uint2korr((uchar*)b); + return (va > vb ? 1 : (va < vb ? -1 : 0)); +} + + +/** + Find entry in the numeric format header by the column number + + @param hdr descriptor of dynamic column record + @param key number to find + + @return pointer to the entry or NULL +*/ + +static uchar *find_entry_num(DYN_HEADER *hdr, uint key) +{ + uchar header_entry[2+4]; + DBUG_ASSERT(hdr->format == dyncol_fmt_num); + int2store(header_entry, key); + return hdr->entry= bsearch(header_entry, hdr->header, + (size_t)hdr->column_count, + hdr->entry_size, &header_compar_num); +} + + +/** + Read name from header entry + + @param hdr descriptor of dynamic column record + @param entry pointer to the header entry + @param name where to put name + + @return 0 ok + @return 1 error in data +*/ + +static my_bool read_name(DYN_HEADER *hdr, uchar *entry, LEX_STRING *name) +{ + size_t nmoffset= uint2korr(entry); + uchar *next_entry= entry + hdr->entry_size; + + if (nmoffset > hdr->nmpool_size) + return 1; + + name->str= (char *)hdr->nmpool + nmoffset; + if (next_entry == hdr->header + hdr->header_size) + name->length= hdr->nmpool_size - nmoffset; + else + { + size_t next_nmoffset= uint2korr(next_entry); + if (next_nmoffset > hdr->nmpool_size) + return 1; + name->length= next_nmoffset - nmoffset; + } + return 0; +} + + +/** + Find entry in the names format header by the column number + + @param hdr descriptor of dynamic column record + @param key name to find + + @return pointer to the entry or NULL +*/ +static uchar *find_entry_named(DYN_HEADER *hdr, LEX_STRING *key) +{ + uchar *min= hdr->header; + uchar *max= hdr->header + (hdr->column_count - 1) * hdr->entry_size; + uchar *mid; + DBUG_ASSERT(hdr->format == dyncol_fmt_str); + DBUG_ASSERT(hdr->nmpool != NULL); + while (max >= min) + { + LEX_STRING name; + int cmp; + mid= hdr->header + ((min - hdr->header) + + (max - hdr->header)) / + 2 / + hdr->entry_size * hdr->entry_size; + if (read_name(hdr, mid, &name)) + return NULL; + cmp= mariadb_dyncol_column_cmp_named(&name, key); + if (cmp < 0) + min= mid + hdr->entry_size; + else if (cmp > 0) + max= mid - hdr->entry_size; + else + return mid; + } + return NULL; +} + + +/** + Write number in the buffer (backward direction - starts from the buffer end) + + @return pointer on the number begining +*/ + +static char *backwritenum(char *chr, uint numkey) +{ + if (numkey == 0) + *(--chr)= '0'; + else + while (numkey > 0) + { + *(--chr)= '0' + numkey % 10; + numkey/= 10; + } + return chr; +} + + +/** + Find column and fill information about it + + @param hdr descriptor of dynamic column record + @param numkey Number of the column to fetch (if strkey is NULL) + @param strkey Name of the column to fetch (or NULL) + + @return 0 ok + @return 1 error in data +*/ + +static my_bool +find_column(DYN_HEADER *hdr, uint numkey, LEX_STRING *strkey) +{ + LEX_STRING nmkey; + char nmkeybuff[DYNCOL_NUM_CHAR]; /* to fit max 2 bytes number */ + DBUG_ASSERT(hdr->header != NULL); + + if (hdr->header + hdr->header_size > hdr->data_end) + return TRUE; + + /* fix key */ + if (hdr->format == dyncol_fmt_num && strkey != NULL) + { + char *end; + numkey= (uint) strtoul(strkey->str, &end, 10); + if (end != strkey->str + strkey->length) + { + /* we can't find non-numeric key among numeric ones */ + hdr->type= DYN_COL_NULL; + return 0; + } + } + else if (hdr->format == dyncol_fmt_str && strkey == NULL) + { + nmkey.str= backwritenum(nmkeybuff + sizeof(nmkeybuff), numkey); + nmkey.length= (nmkeybuff + sizeof(nmkeybuff)) - nmkey.str; + strkey= &nmkey; + } + if (hdr->format == dyncol_fmt_num) + hdr->entry= find_entry_num(hdr, numkey); + else + hdr->entry= find_entry_named(hdr, strkey); + + if (!hdr->entry) + { + /* Column not found */ + hdr->type= DYN_COL_NULL; + return 0; + } + hdr->length= hdr_interval_length(hdr, hdr->entry + hdr->entry_size); + hdr->data= hdr->dtpool + hdr->offset; + /* + Check that the found data is withing the ranges. This can happen if + we get data with wrong offsets. + */ + if (hdr->length == DYNCOL_OFFSET_ERROR || + hdr->length > INT_MAX || hdr->offset > hdr->data_size) + return 1; + + return 0; +} + + +/** + Read and check the header of the dynamic string + + @param hdr descriptor of dynamic column record + @param str Dynamic string + + @retval FALSE OK + @retval TRUE error + + Note + We don't check for str->length == 0 as all code that calls this + already have handled this case. +*/ + +static inline my_bool read_fixed_header(DYN_HEADER *hdr, + DYNAMIC_COLUMN *str) +{ + DBUG_ASSERT(str != NULL && str->length != 0); + if ((str->length < 1) || + (str->str[0] & (~DYNCOL_FLG_KNOWN))) + return 1; + hdr->format= ((str->str[0] & DYNCOL_FLG_NAMES) ? + dyncol_fmt_str: + dyncol_fmt_num); + if ((str->length < fmt_data[hdr->format].fixed_hdr)) + return 1; /* Wrong header */ + hdr->offset_size= (str->str[0] & DYNCOL_FLG_OFFSET) + 1 + + (hdr->format == dyncol_fmt_str ? 1 : 0); + hdr->column_count= uint2korr(str->str + 1); + if (hdr->format == dyncol_fmt_str) + hdr->nmpool_size= uint2korr(str->str + 3); // only 2 bytes supported for now + else + hdr->nmpool_size= 0; + return 0; +} + + +/** + Get dynamic column value by column number + + @param str The packed string to extract the column + @param column_nr Number of column to fetch + @param store_it_here Where to store the extracted value + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_get(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *store_it_here) +{ + return dynamic_column_get_internal(str, store_it_here, column_nr, NULL); +} + +enum enum_dyncol_func_result +mariadb_dyncol_get(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *store_it_here) +{ + return dynamic_column_get_internal(str, store_it_here, column_nr, NULL); +} + + +/** + Get dynamic column value by name + + @param str The packed string to extract the column + @param name Name of column to fetch + @param store_it_here Where to store the extracted value + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_get_named(DYNAMIC_COLUMN *str, LEX_STRING *name, + DYNAMIC_COLUMN_VALUE *store_it_here) +{ + DBUG_ASSERT(name != NULL); + return dynamic_column_get_internal(str, store_it_here, 0, name); +} + + +static enum enum_dyncol_func_result +dynamic_column_get_value(DYN_HEADER *hdr, DYNAMIC_COLUMN_VALUE *store_it_here) +{ + static enum enum_dyncol_func_result rc; + switch ((store_it_here->type= hdr->type)) { + case DYN_COL_INT: + rc= dynamic_column_sint_read(store_it_here, hdr->data, hdr->length); + break; + case DYN_COL_UINT: + rc= dynamic_column_uint_read(store_it_here, hdr->data, hdr->length); + break; + case DYN_COL_DOUBLE: + rc= dynamic_column_double_read(store_it_here, hdr->data, hdr->length); + break; + case DYN_COL_STRING: + rc= dynamic_column_string_read(store_it_here, hdr->data, hdr->length); + break; +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + rc= dynamic_column_decimal_read(store_it_here, hdr->data, hdr->length); + break; +#endif + case DYN_COL_DATETIME: + rc= dynamic_column_date_time_read(store_it_here, hdr->data, + hdr->length); + break; + case DYN_COL_DATE: + rc= dynamic_column_date_read(store_it_here, hdr->data, hdr->length); + break; + case DYN_COL_TIME: + rc= dynamic_column_time_read(store_it_here, hdr->data, hdr->length); + break; + case DYN_COL_NULL: + rc= ER_DYNCOL_OK; + break; + case DYN_COL_DYNCOL: + rc= dynamic_column_dyncol_read(store_it_here, hdr->data, hdr->length); + break; + default: + rc= ER_DYNCOL_FORMAT; + store_it_here->type= DYN_COL_NULL; + break; + } + return rc; +} + +/** + Get dynamic column value by number or name + + @param str The packed string to extract the column + @param store_it_here Where to store the extracted value + @param numkey Number of the column to fetch (if strkey is NULL) + @param strkey Name of the column to fetch (or NULL) + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_get_internal(DYNAMIC_COLUMN *str, + DYNAMIC_COLUMN_VALUE *store_it_here, + uint num_key, LEX_STRING *str_key) +{ + DYN_HEADER header; + enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT; + bzero(&header, sizeof(header)); + + if (str->length == 0) + goto null; + + if ((rc= init_read_hdr(&header, str)) < 0) + goto err; + + if (header.column_count == 0) + goto null; + + if (find_column(&header, num_key, str_key)) + goto err; + + rc= dynamic_column_get_value(&header, store_it_here); + return rc; + +null: + rc= ER_DYNCOL_OK; +err: + store_it_here->type= DYN_COL_NULL; + return rc; +} + + +/** + Check existence of the column in the packed string (by number) + + @param str The packed string to check the column + @param column_nr Number of column to check + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_exists(DYNAMIC_COLUMN *str, uint column_nr) +{ + return dynamic_column_exists_internal(str, column_nr, NULL); +} + +enum enum_dyncol_func_result +mariadb_dyncol_exists(DYNAMIC_COLUMN *str, uint column_nr) +{ + return dynamic_column_exists_internal(str, column_nr, NULL); +} + +/** + Check existence of the column in the packed string (by name) + + @param str The packed string to check the column + @param name Name of column to check + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_exists_named(DYNAMIC_COLUMN *str, LEX_STRING *name) +{ + DBUG_ASSERT(name != NULL); + return dynamic_column_exists_internal(str, 0, name); +} + + +/** + Check existence of the column in the packed string (by name of number) + + @param str The packed string to check the column + @param num_key Number of the column to fetch (if strkey is NULL) + @param str_key Name of the column to fetch (or NULL) + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key, + LEX_STRING *str_key) +{ + DYN_HEADER header; + enum enum_dyncol_func_result rc; + bzero(&header, sizeof(header)); + + if (str->length == 0) + return ER_DYNCOL_NO; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + + if (header.column_count == 0) + return ER_DYNCOL_NO; /* no columns */ + + if (find_column(&header, num_key, str_key)) + return ER_DYNCOL_FORMAT; + + return (header.type != DYN_COL_NULL ? ER_DYNCOL_YES : ER_DYNCOL_NO); +} + + +/** + List not-null columns in the packed string (only numeric format) + + @param str The packed string + @param array_of_uint Where to put reference on created array + + @return ER_DYNCOL_* return code +*/ +enum enum_dyncol_func_result +dynamic_column_list(DYNAMIC_COLUMN *str, DYNAMIC_ARRAY *array_of_uint) +{ + DYN_HEADER header; + uchar *read; + uint i; + enum enum_dyncol_func_result rc; + + bzero(array_of_uint, sizeof(*array_of_uint)); /* In case of errors */ + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + + if (header.format != dyncol_fmt_num) + return ER_DYNCOL_FORMAT; + + if (header.entry_size * header.column_count + FIXED_HEADER_SIZE > + str->length) + return ER_DYNCOL_FORMAT; + + if (my_init_dynamic_array(array_of_uint, sizeof(uint), header.column_count, 0)) + return ER_DYNCOL_RESOURCE; + + for (i= 0, read= header.header; + i < header.column_count; + i++, read+= header.entry_size) + { + uint nm= uint2korr(read); + /* Insert can't never fail as it's pre-allocated above */ + (void) insert_dynamic(array_of_uint, (uchar *)&nm); + } + return ER_DYNCOL_OK; +} + +/** + List not-null columns in the packed string (only numeric format) + + @param str The packed string + @param array_of_uint Where to put reference on created array + + @return ER_DYNCOL_* return code +*/ +enum enum_dyncol_func_result +mariadb_dyncol_list(DYNAMIC_COLUMN *str, uint *count, uint **nums) +{ + DYN_HEADER header; + uchar *read; + uint i; + enum enum_dyncol_func_result rc; + + (*nums)= 0; /* In case of errors */ + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + + if (header.format != dyncol_fmt_num) + return ER_DYNCOL_FORMAT; + + if (header.entry_size * header.column_count + FIXED_HEADER_SIZE > + str->length) + return ER_DYNCOL_FORMAT; + + if (!((*nums)= (uint *)my_malloc(sizeof(uint) * header.column_count, MYF(0)))) + return ER_DYNCOL_RESOURCE; + + for (i= 0, read= header.header; + i < header.column_count; + i++, read+= header.entry_size) + { + (*nums)[i]= uint2korr(read); + } + (*count)= header.column_count; + return ER_DYNCOL_OK; +} + +/** + List not-null columns in the packed string (any format) + + @param str The packed string + @param count Number of names in the list + @param names Where to put names list (should be freed) + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_list_named(DYNAMIC_COLUMN *str, uint *count, LEX_STRING **names) +{ + DYN_HEADER header; + uchar *read; + char *pool; + struct st_service_funcs *fmt; + uint i; + enum enum_dyncol_func_result rc; + + (*names)= 0; (*count)= 0; + + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + + fmt= fmt_data + header.format; + + if (header.entry_size * header.column_count + fmt->fixed_hdr > + str->length) + return ER_DYNCOL_FORMAT; + + if (header.format == dyncol_fmt_num) + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count + + DYNCOL_NUM_CHAR * header.column_count, MYF(0)); + else + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count + + header.nmpool_size + header.column_count, MYF(0)); + if (!(*names)) + return ER_DYNCOL_RESOURCE; + pool= ((char *)(*names)) + sizeof(LEX_STRING) * header.column_count; + + for (i= 0, read= header.header; + i < header.column_count; + i++, read+= header.entry_size) + { + if (header.format == dyncol_fmt_num) + { + uint nm= uint2korr(read); + (*names)[i].str= pool; + pool+= DYNCOL_NUM_CHAR; + (*names)[i].length= + longlong2str(nm, (*names)[i].str, 10) - (*names)[i].str; + } + else + { + LEX_STRING tmp; + if (read_name(&header, read, &tmp)) + return ER_DYNCOL_FORMAT; + (*names)[i].length= tmp.length; + (*names)[i].str= pool; + pool+= tmp.length + 1; + memcpy((*names)[i].str, (const void *)tmp.str, tmp.length); + (*names)[i].str[tmp.length]= '\0'; // just for safety + } + } + (*count)= header.column_count; + return ER_DYNCOL_OK; +} + +/** + Find the place of the column in the header or place where it should be put + + @param hdr descriptor of dynamic column record + @param key Name or number of column to fetch + (depends on string_key) + @param string_key True if we gave pointer to LEX_STRING. + + @retval TRUE found + @retval FALSE pointer set to the next row +*/ + +static my_bool +find_place(DYN_HEADER *hdr, void *key, my_bool string_keys) +{ + uint mid, start, end, val; + int UNINIT_VAR(flag); + LEX_STRING str; + char buff[DYNCOL_NUM_CHAR]; + my_bool need_conversion= ((string_keys ? dyncol_fmt_str : dyncol_fmt_num) != + hdr->format); + /* new format can't be numeric if the old one is names */ + DBUG_ASSERT(string_keys || + hdr->format == dyncol_fmt_num); + + start= 0; + end= hdr->column_count -1; + mid= 1; + while (start != end) + { + uint val; + mid= (start + end) / 2; + hdr->entry= hdr->header + mid * hdr->entry_size; + if (!string_keys) + { + val= uint2korr(hdr->entry); + flag= CMP_NUM(*((uint *)key), val); + } + else + { + if (need_conversion) + { + str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry)); + str.length= (buff + sizeof(buff)) - str.str; + } + else + { + DBUG_ASSERT(hdr->format == dyncol_fmt_str); + if (read_name(hdr, hdr->entry, &str)) + return 0; + } + flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str); + } + if (flag <= 0) + end= mid; + else + start= mid + 1; + } + hdr->entry= hdr->header + start * hdr->entry_size; + if (start != mid) + { + if (!string_keys) + { + val= uint2korr(hdr->entry); + flag= CMP_NUM(*((uint *)key), val); + } + else + { + if (need_conversion) + { + str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry)); + str.length= (buff + sizeof(buff)) - str.str; + } + else + { + DBUG_ASSERT(hdr->format == dyncol_fmt_str); + if (read_name(hdr, hdr->entry, &str)) + return 0; + } + flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str); + } + } + if (flag > 0) + hdr->entry+= hdr->entry_size; /* Point at next bigger key */ + return flag == 0; +} + + +/* + It is internal structure which describes a plan of changing the record + of dynamic columns +*/ + +typedef enum {PLAN_REPLACE, PLAN_ADD, PLAN_DELETE, PLAN_NOP} PLAN_ACT; + +struct st_plan { + DYNAMIC_COLUMN_VALUE *val; + void *key; + uchar *place; + size_t length; + long long hdelta, ddelta, ndelta; + long long mv_offset, mv_length; + uint mv_end; + PLAN_ACT act; +}; +typedef struct st_plan PLAN; + + +/** + Sort function for plan by column number +*/ + +static int plan_sort_num(const void *a, const void *b) +{ + return *((uint *)((PLAN *)a)->key) - *((uint *)((PLAN *)b)->key); +} + + +/** + Sort function for plan by column name +*/ + +static int plan_sort_named(const void *a, const void *b) +{ + return mariadb_dyncol_column_cmp_named((LEX_STRING *)((PLAN *)a)->key, + (LEX_STRING *)((PLAN *)b)->key); +} + +#define DELTA_CHECK(S, D, C) \ + if ((S) == 0) \ + (S)= (D); \ + else if (((S) > 0 && (D) < 0) || \ + ((S) < 0 && (D) > 0)) \ + { \ + (C)= TRUE; \ + } + +/** + Update dynamic column by copying in a new record (string). + + @param str Dynamic column record to change + @param plan Plan of changing the record + @param add_column_count number of records in the plan array. + @param hdr descriptor of old dynamic column record + @param new_hdr descriptor of new dynamic column record + @param convert need conversion from numeric to names format + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_update_copy(DYNAMIC_COLUMN *str, PLAN *plan, + uint add_column_count, + DYN_HEADER *hdr, DYN_HEADER *new_hdr, + my_bool convert) +{ + DYNAMIC_COLUMN tmp; + struct st_service_funcs *fmt= fmt_data + hdr->format, + *new_fmt= fmt_data + new_hdr->format; + uint i, j, k; + size_t all_headers_size; + + if (dynamic_column_init_named(&tmp, + (new_fmt->fixed_hdr + new_hdr->header_size + + new_hdr->nmpool_size + + new_hdr->data_size + DYNCOL_SYZERESERVE))) + { + return ER_DYNCOL_RESOURCE; + } + bzero(tmp.str, new_fmt->fixed_hdr); + (*new_fmt->set_fixed_hdr)(&tmp, new_hdr); + /* Adjust tmp to contain whole the future header */ + tmp.length= new_fmt->fixed_hdr + new_hdr->header_size + new_hdr->nmpool_size; + + + /* + Copy data to the new string + i= index in array of changes + j= index in packed string header index + */ + new_hdr->entry= new_hdr->header; + new_hdr->name= new_hdr->nmpool; + all_headers_size= new_fmt->fixed_hdr + + new_hdr->header_size + new_hdr->nmpool_size; + for (i= 0, j= 0; i < add_column_count || j < hdr->column_count; i++) + { + size_t UNINIT_VAR(first_offset); + uint start= j, end; + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + + if (i == add_column_count) + j= end= hdr->column_count; + else + { + /* + old data portion. We don't need to check that j < column_count + as plan[i].place is guaranteed to have a pointer inside the + data. + */ + while (hdr->header + j * hdr->entry_size < plan[i].place) + j++; + end= j; + if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; /* data at 'j' will be removed */ + } + + /* + Adjust all headers since last loop. + We have to do this as the offset for data has moved + */ + for (k= start; k < end; k++) + { + uchar *read= hdr->header + k * hdr->entry_size; + void *key; + LEX_STRING name; + size_t offs; + uint nm; + DYNAMIC_COLUMN_TYPE tp; + char buff[DYNCOL_NUM_CHAR]; + + if (hdr->format == dyncol_fmt_num) + { + if (convert) + { + name.str= backwritenum(buff + sizeof(buff), uint2korr(read)); + name.length= (buff + sizeof(buff)) - name.str; + key= &name; + } + else + { + nm= uint2korr(read); /* Column nummber */ + key= &nm; + } + } + else + { + if (read_name(hdr, read, &name)) + goto err; + key= &name; + } + if (fmt->type_and_offset_read(&tp, &offs, + read + fmt->fixed_hdr_entry, + hdr->offset_size)) + goto err; + if (k == start) + first_offset= offs; + else if (offs < first_offset) + goto err; + + offs+= plan[i].ddelta; + { + DYNAMIC_COLUMN_VALUE val; + val.type= tp; // only the type used in the header + if ((*new_fmt->put_header_entry)(new_hdr, key, &val, offs)) + goto err; + } + } + + /* copy first the data that was not replaced in original packed data */ + if (start < end) + { + size_t data_size; + /* Add old data last in 'tmp' */ + hdr->entry= hdr->header + start * hdr->entry_size; + data_size= + hdr_interval_length(hdr, hdr->header + end * hdr->entry_size); + if (data_size == DYNCOL_OFFSET_ERROR || + (long) data_size < 0 || + data_size > hdr->data_size - first_offset) + goto err; + + memcpy(tmp.str + tmp.length, (char *)hdr->dtpool + first_offset, + data_size); + tmp.length+= data_size; + } + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + if ((*new_fmt->put_header_entry)(new_hdr, plan[i].key, + plan[i].val, + tmp.length - all_headers_size)) + goto err; + data_store(&tmp, plan[i].val, new_hdr->format); /* Append new data */ + } + } + } + dynamic_column_column_free(str); + *str= tmp; + return ER_DYNCOL_OK; +err: + dynamic_column_column_free(&tmp); + return ER_DYNCOL_FORMAT; +} + +static enum enum_dyncol_func_result +dynamic_column_update_move_left(DYNAMIC_COLUMN *str, PLAN *plan, + size_t offset_size, + size_t entry_size, + size_t header_size, + size_t new_offset_size, + size_t new_entry_size, + size_t new_header_size, + uint column_count, + uint new_column_count, + uint add_column_count, + uchar *header_end, + size_t max_offset) +{ + uchar *write; + uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE; + uint i, j, k; + size_t curr_offset; + + write= (uchar *)str->str + FIXED_HEADER_SIZE; + set_fixed_header(str, (uint)new_offset_size, new_column_count); + + /* + Move headers first. + i= index in array of changes + j= index in packed string header index + */ + for (curr_offset= 0, i= 0, j= 0; + i < add_column_count || j < column_count; + i++) + { + size_t UNINIT_VAR(first_offset); + uint start= j, end; + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + + if (i == add_column_count) + j= end= column_count; + else + { + /* + old data portion. We don't need to check that j < column_count + as plan[i].place is guaranteed to have a pointer inside the + data. + */ + while (header_base + j * entry_size < plan[i].place) + j++; + end= j; + if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; /* data at 'j' will be removed */ + } + plan[i].mv_end= end; + + { + DYNAMIC_COLUMN_TYPE tp; + if (type_and_offset_read_num(&tp, &first_offset, + header_base + start * entry_size + + COLUMN_NUMBER_SIZE, offset_size)) + return ER_DYNCOL_FORMAT; + } + /* find data to be moved */ + if (start < end) + { + size_t data_size= + get_length_interval(header_base + start * entry_size, + header_base + end * entry_size, + header_end, offset_size, max_offset); + if (data_size == DYNCOL_OFFSET_ERROR || + (long) data_size < 0 || + data_size > max_offset - first_offset) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta); + plan[i].mv_offset= first_offset; + plan[i].mv_length= data_size; + curr_offset+= data_size; + } + else + { + plan[i].mv_length= 0; + plan[i].mv_offset= curr_offset; + } + + if (plan[i].ddelta == 0 && offset_size == new_offset_size && + plan[i].act != PLAN_DELETE) + write+= entry_size * (end - start); + else + { + /* + Adjust all headers since last loop. + We have to do this as the offset for data has moved + */ + for (k= start; k < end; k++) + { + uchar *read= header_base + k * entry_size; + size_t offs; + uint nm; + DYNAMIC_COLUMN_TYPE tp; + + nm= uint2korr(read); /* Column nummber */ + if (type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE, + offset_size)) + return ER_DYNCOL_FORMAT; + + if (k > start && offs < first_offset) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + + offs+= plan[i].ddelta; + int2store(write, nm); + /* write rest of data at write + COLUMN_NUMBER_SIZE */ + type_and_offset_store_num(write, new_offset_size, tp, offs); + write+= new_entry_size; + } + } + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + int2store(write, *((uint *)plan[i].key)); + type_and_offset_store_num(write, new_offset_size, + plan[i].val[0].type, + curr_offset); + write+= new_entry_size; + curr_offset+= plan[i].length; + } + } + } + + /* + Move data. + i= index in array of changes + j= index in packed string header index + */ + str->length= (FIXED_HEADER_SIZE + new_header_size); + for (i= 0, j= 0; + i < add_column_count || j < column_count; + i++) + { + uint start= j, end; + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + + j= end= plan[i].mv_end; + if (i != add_column_count && + (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; + + /* copy first the data that was not replaced in original packed data */ + if (start < end && plan[i].mv_length) + { + memmove((header_base + new_header_size + + plan[i].mv_offset + plan[i].ddelta), + header_base + header_size + plan[i].mv_offset, + plan[i].mv_length); + } + str->length+= plan[i].mv_length; + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + data_store(str, plan[i].val, dyncol_fmt_num);/* Append new data */ + } + } + } + return ER_DYNCOL_OK; +} + +#ifdef UNUSED +static enum enum_dyncol_func_result +dynamic_column_update_move_right(DYNAMIC_COLUMN *str, PLAN *plan, + size_t offset_size, + size_t entry_size, + size_t header_size, + size_t new_offset_size, + size_t new_entry_size, + size_t new_header_size, + uint column_count, + uint new_column_count, + uint add_column_count, + uchar *header_end, + size_t max_offset) +{ + uchar *write; + uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE; + uint i, j, k; + size_t curr_offset; + + write= (uchar *)str->str + FIXED_HEADER_SIZE; + set_fixed_header(str, new_offset_size, new_column_count); + + /* + Move data first. + i= index in array of changes + j= index in packed string header index + */ + for (curr_offset= 0, i= 0, j= 0; + i < add_column_count || j < column_count; + i++) + { + size_t UNINIT_VAR(first_offset); + uint start= j, end; + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + + if (i == add_column_count) + j= end= column_count; + else + { + /* + old data portion. We don't need to check that j < column_count + as plan[i].place is guaranteed to have a pointer inside the + data. + */ + while (header_base + j * entry_size < plan[i].place) + j++; + end= j; + if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; /* data at 'j' will be removed */ + } + plan[i].mv_end= end; + + { + DYNAMIC_COLUMN_TYPE tp; + type_and_offset_read_num(&tp, &first_offset, + header_base + + start * entry_size + COLUMN_NUMBER_SIZE, + offset_size); + } + /* find data to be moved */ + if (start < end) + { + size_t data_size= + get_length_interval(header_base + start * entry_size, + header_base + end * entry_size, + header_end, offset_size, max_offset); + if (data_size == DYNCOL_OFFSET_ERROR || + (long) data_size < 0 || + data_size > max_offset - first_offset) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta); + plan[i].mv_offset= first_offset; + plan[i].mv_length= data_size; + curr_offset+= data_size; + } + else + { + plan[i].mv_length= 0; + plan[i].mv_offset= curr_offset; + } + + if (plan[i].ddelta == 0 && offset_size == new_offset_size && + plan[i].act != PLAN_DELETE) + write+= entry_size * (end - start); + else + { + /* + Adjust all headers since last loop. + We have to do this as the offset for data has moved + */ + for (k= start; k < end; k++) + { + uchar *read= header_base + k * entry_size; + size_t offs; + uint nm; + DYNAMIC_COLUMN_TYPE tp; + + nm= uint2korr(read); /* Column nummber */ + type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE, + offset_size); + if (k > start && offs < first_offset) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + + offs+= plan[i].ddelta; + int2store(write, nm); + /* write rest of data at write + COLUMN_NUMBER_SIZE */ + if (type_and_offset_store_num(write, new_offset_size, tp, offs)) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + write+= new_entry_size; + } + } + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + int2store(write, *((uint *)plan[i].key)); + if (type_and_offset_store_num(write, new_offset_size, + plan[i].val[0].type, + curr_offset)) + { + str->length= 0; // just something valid + return ER_DYNCOL_FORMAT; + } + write+= new_entry_size; + curr_offset+= plan[i].length; + } + } + } + + /* + Move headers. + i= index in array of changes + j= index in packed string header index + */ + str->length= (FIXED_HEADER_SIZE + new_header_size); + for (i= 0, j= 0; + i < add_column_count || j < column_count; + i++) + { + uint start= j, end; + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + + j= end= plan[i].mv_end; + if (i != add_column_count && + (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; + + /* copy first the data that was not replaced in original packed data */ + if (start < end && plan[i].mv_length) + { + memmove((header_base + new_header_size + + plan[i].mv_offset + plan[i].ddelta), + header_base + header_size + plan[i].mv_offset, + plan[i].mv_length); + } + str->length+= plan[i].mv_length; + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + data_store(str, plan[i].val, dyncol_fmt_num); /* Append new data */ + } + } + } + return ER_DYNCOL_OK; +} +#endif + +/** + Update the packed string with the given columns + + @param str String where to write the data + @param add_column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ +/* plan allocated on the stack */ +#define IN_PLACE_PLAN 4 + +enum enum_dyncol_func_result +dynamic_column_update_many(DYNAMIC_COLUMN *str, + uint add_column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values) +{ + return dynamic_column_update_many_fmt(str, add_column_count, column_numbers, + values, FALSE); +} + +enum enum_dyncol_func_result +mariadb_dyncol_update_many(DYNAMIC_COLUMN *str, + uint add_column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values) +{ + return dynamic_column_update_many_fmt(str, add_column_count, column_numbers, + values, FALSE); +} + +enum enum_dyncol_func_result +mariadb_dyncol_update_many_named(DYNAMIC_COLUMN *str, + uint add_column_count, + LEX_STRING *column_names, + DYNAMIC_COLUMN_VALUE *values) +{ + return dynamic_column_update_many_fmt(str, add_column_count, column_names, + values, TRUE); +} + +static uint numlen(uint val) +{ + uint res; + if (val == 0) + return 1; + res= 0; + while(val) + { + res++; + val/=10; + } + return res; +} + +static enum enum_dyncol_func_result +dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str, + uint add_column_count, + void *column_keys, + DYNAMIC_COLUMN_VALUE *values, + my_bool string_keys) +{ + PLAN *plan, *alloc_plan= NULL, in_place_plan[IN_PLACE_PLAN]; + uchar *element; + DYN_HEADER header, new_header; + struct st_service_funcs *fmt, *new_fmt; + long long data_delta= 0, name_delta= 0; + uint i; + uint not_null; + long long header_delta= 0; + long long header_delta_sign, data_delta_sign; + int copy= FALSE; + enum enum_dyncol_func_result rc; + my_bool convert; + + if (add_column_count == 0) + return ER_DYNCOL_OK; + + bzero(&header, sizeof(header)); + bzero(&new_header, sizeof(new_header)); + new_header.format= (string_keys ? dyncol_fmt_str : dyncol_fmt_num); + new_fmt= fmt_data + new_header.format; + + /* + Get columns in column order. As the data in 'str' is already + in column order this allows to replace all columns in one loop. + */ + if (IN_PLACE_PLAN > add_column_count) + plan= in_place_plan; + else if (!(alloc_plan= plan= + (PLAN *)my_malloc(sizeof(PLAN) * (add_column_count + 1), MYF(0)))) + return ER_DYNCOL_RESOURCE; + + not_null= add_column_count; + for (i= 0, element= (uchar *) column_keys; + i < add_column_count; + i++, element+= new_fmt->key_size_in_array) + { + if ((*new_fmt->check_limit)(&element)) + { + rc= ER_DYNCOL_DATA; + goto end; + } + + plan[i].val= values + i; + plan[i].key= element; + if (values[i].type == DYN_COL_NULL) + not_null--; + + } + + if (str->length == 0) + { + /* + Just add new columns. If there was no columns to add we return + an empty string. + */ + goto create_new_string; + } + + /* Check that header is ok */ + if ((rc= init_read_hdr(&header, str)) < 0) + goto end; + fmt= fmt_data + header.format; + /* new format can't be numeric if the old one is names */ + DBUG_ASSERT(new_header.format == dyncol_fmt_str || + header.format == dyncol_fmt_num); + if (header.column_count == 0) + goto create_new_string; + + qsort(plan, (size_t)add_column_count, sizeof(PLAN), new_fmt->plan_sort); + + new_header.column_count= header.column_count; + new_header.nmpool_size= header.nmpool_size; + if ((convert= (new_header.format == dyncol_fmt_str && + header.format == dyncol_fmt_num))) + { + DBUG_ASSERT(new_header.nmpool_size == 0); + for(i= 0, header.entry= header.header; + i < header.column_count; + i++, header.entry+= header.entry_size) + { + new_header.nmpool_size+= numlen(uint2korr(header.entry)); + } + } + + if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + + /* + Calculate how many columns and data is added/deleted and make a 'plan' + for each of them. + */ + for (i= 0; i < add_column_count; i++) + { + /* + For now we don't allow creating two columns with the same number + at the time of create. This can be fixed later to just use the later + by comparing the pointers. + */ + if (i < add_column_count - 1 && + new_fmt->column_sort(&plan[i].key, &plan[i + 1].key) == 0) + { + rc= ER_DYNCOL_DATA; + goto end; + } + + /* Set common variables for all plans */ + plan[i].ddelta= data_delta; + plan[i].ndelta= name_delta; + /* get header delta in entries */ + plan[i].hdelta= header_delta; + plan[i].length= 0; /* Length if NULL */ + + if (find_place(&header, plan[i].key, string_keys)) + { + size_t entry_data_size, entry_name_size= 0; + + /* Data existed; We have to replace or delete it */ + + entry_data_size= hdr_interval_length(&header, header.entry + + header.entry_size); + if (entry_data_size == DYNCOL_OFFSET_ERROR || + (long) entry_data_size < 0) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + + if (new_header.format == dyncol_fmt_str) + { + if (header.format == dyncol_fmt_str) + { + LEX_STRING name; + if (read_name(&header, header.entry, &name)) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + entry_name_size= name.length; + } + else + entry_name_size= numlen(uint2korr(header.entry)); + } + + if (plan[i].val->type == DYN_COL_NULL) + { + /* Inserting a NULL means delete the old data */ + + plan[i].act= PLAN_DELETE; /* Remove old value */ + header_delta--; /* One row less in header */ + data_delta-= entry_data_size; /* Less data to store */ + name_delta-= entry_name_size; + } + else + { + /* Replace the value */ + + plan[i].act= PLAN_REPLACE; + /* get data delta in bytes */ + if ((plan[i].length= dynamic_column_value_len(plan[i].val, + new_header.format)) == + (size_t) ~0) + { + rc= ER_DYNCOL_DATA; + goto end; + } + data_delta+= plan[i].length - entry_data_size; + if (new_header.format == dyncol_fmt_str) + { + name_delta+= ((LEX_STRING *)(plan[i].key))->length - entry_name_size; + } + } + } + else + { + /* Data did not exists. Add if it it's not NULL */ + + if (plan[i].val->type == DYN_COL_NULL) + { + plan[i].act= PLAN_NOP; /* Mark entry to be skiped */ + } + else + { + /* Add new value */ + + plan[i].act= PLAN_ADD; + header_delta++; /* One more row in header */ + /* get data delta in bytes */ + if ((plan[i].length= dynamic_column_value_len(plan[i].val, + new_header.format)) == + (size_t) ~0) + { + rc= ER_DYNCOL_DATA; + goto end; + } + data_delta+= plan[i].length; + if (new_header.format == dyncol_fmt_str) + name_delta+= ((LEX_STRING *)plan[i].key)->length; + } + } + plan[i].place= header.entry; + } + plan[add_column_count].hdelta= header_delta; + plan[add_column_count].ddelta= data_delta; + plan[add_column_count].act= PLAN_NOP; + plan[add_column_count].place= header.dtpool; + + new_header.column_count= (uint)(header.column_count + header_delta); + + /* + Check if it is only "increasing" or only "decreasing" plan for (header + and data separately). + */ + new_header.data_size= header.data_size + data_delta; + new_header.nmpool_size= new_header.nmpool_size + name_delta; + DBUG_ASSERT(new_header.format != dyncol_fmt_num || + new_header.nmpool_size == 0); + if ((new_header.offset_size= + new_fmt->dynamic_column_offset_bytes(new_header.data_size)) >= + new_fmt->max_offset_size) + { + rc= ER_DYNCOL_LIMIT; + goto end; + } + + copy= ((header.format != new_header.format) || + (new_header.format == dyncol_fmt_str)); + /* if (new_header.offset_size!=offset_size) then we have to rewrite header */ + header_delta_sign= + ((int)new_header.offset_size + new_fmt->fixed_hdr_entry) - + ((int)header.offset_size + fmt->fixed_hdr_entry); + data_delta_sign= 0; + // plan[add_column_count] contains last deltas. + for (i= 0; i <= add_column_count && !copy; i++) + { + /* This is the check for increasing/decreasing */ + DELTA_CHECK(header_delta_sign, plan[i].hdelta, copy); + DELTA_CHECK(data_delta_sign, plan[i].ddelta, copy); + } + calc_param(&new_header.entry_size, &new_header.header_size, + new_fmt->fixed_hdr_entry, + new_header.offset_size, new_header.column_count); + + /* + Need copy because: + 1, Header/data parts moved in different directions. + 2. There is no enough allocated space in the string. + 3. Header and data moved in different directions. + */ + if (copy || /*1.*/ + str->max_length < str->length + header_delta + data_delta || /*2.*/ + ((header_delta_sign < 0 && data_delta_sign > 0) || + (header_delta_sign > 0 && data_delta_sign < 0))) /*3.*/ + rc= dynamic_column_update_copy(str, plan, add_column_count, + &header, &new_header, + convert); + else + if (header_delta_sign < 0) + rc= dynamic_column_update_move_left(str, plan, header.offset_size, + header.entry_size, + header.header_size, + new_header.offset_size, + new_header.entry_size, + new_header.header_size, + header.column_count, + new_header.column_count, + add_column_count, header.dtpool, + header.data_size); + else + /* + rc= dynamic_column_update_move_right(str, plan, offset_size, + entry_size, header_size, + new_header.offset_size, + new_header.entry_size, + new_heder.header_size, column_count, + new_header.column_count, + add_column_count, header_end, + header.data_size); + */ + rc= dynamic_column_update_copy(str, plan, add_column_count, + &header, &new_header, + convert); +end: +#ifndef LIBMARIADB + my_free(alloc_plan); +#else + my_free((gptr)alloc_plan, MYF(0)); +#endif + return rc; + +create_new_string: + /* There is no columns from before, so let's just add the new ones */ + rc= ER_DYNCOL_OK; +#ifndef LIBMARIADB + my_free(alloc_plan); +#else + my_free((gptr)alloc_plan, MYF(0)); +#endif + if (not_null != 0) + rc= dynamic_column_create_many_internal_fmt(str, add_column_count, + (uint*)column_keys, values, + str->str == NULL, + string_keys); + goto end; +} + + +/** + Update the packed string with the given column + + @param str String where to write the data + @param column_number Array of columns number + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + + +int dynamic_column_update(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *value) +{ + return dynamic_column_update_many(str, 1, &column_nr, value); +} + + +enum enum_dyncol_func_result +mariadb_dyncol_check(DYNAMIC_COLUMN *str) +{ + struct st_service_funcs *fmt; + enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT; + DYN_HEADER header; + uint i; + size_t data_offset= 0, name_offset= 0; + size_t prev_data_offset= 0, prev_name_offset= 0; + LEX_STRING name= {0,0}, prev_name= {0,0}; + uint num= 0, prev_num= 0; + void *key, *prev_key; + enum enum_dynamic_column_type type= DYN_COL_NULL, prev_type= DYN_COL_NULL; + + DBUG_ENTER("dynamic_column_check"); + + if (str->length == 0) + { + DBUG_PRINT("info", ("empty string is OK")); + DBUG_RETURN(ER_DYNCOL_OK); + } + + bzero(&header, sizeof(header)); + + /* Check that header is OK */ + if (read_fixed_header(&header, str)) + { + DBUG_PRINT("info", ("Reading fixed string header failed")); + goto end; + } + fmt= fmt_data + header.format; + calc_param(&header.entry_size, &header.header_size, + fmt->fixed_hdr_entry, header.offset_size, + header.column_count); + /* headers are out of string length (no space for data and part of headers) */ + if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length) + { + DBUG_PRINT("info", ("Fixed header: %u Header size: %u " + "Name pool size: %u but Strig length: %u", + (uint)fmt->fixed_hdr, + (uint)header.header_size, + (uint)header.nmpool_size, + (uint)str->length)); + goto end; + } + header.header= (uchar*)str->str + fmt->fixed_hdr; + header.nmpool= header.header + header.header_size; + header.dtpool= header.nmpool + header.nmpool_size; + header.data_size= str->length - fmt->fixed_hdr - + header.header_size - header.nmpool_size; + + /* read and check headers */ + if (header.format == dyncol_fmt_num) + { + key= # + prev_key= &prev_num; + } + else + { + key= &name; + prev_key= &prev_name; + } + for (i= 0, header.entry= header.header; + i < header.column_count; + i++, header.entry+= header.entry_size) + { + + if (header.format == dyncol_fmt_num) + { + num= uint2korr(header.entry); + } + else + { + DBUG_ASSERT(header.format == dyncol_fmt_str); + if (read_name(&header, header.entry, &name)) + { + DBUG_PRINT("info", ("Reading name failed: Field order: %u" + " Name offset: %u" + " Name pool size: %u", + (uint) i, + uint2korr(header.entry), + (uint)header.nmpool_size)); + goto end; + } + name_offset= name.str - (char *)header.nmpool; + } + if ((*fmt->type_and_offset_read)(&type, &data_offset, + header.entry + fmt->fixed_hdr_entry, + header.offset_size)) + goto end; + + DBUG_ASSERT(type != DYN_COL_NULL); + if (data_offset > header.data_size) + { + DBUG_PRINT("info", ("Field order: %u Data offset: %u" + " > Data pool size: %u", + (uint)i, + (uint)data_offset, + (uint)header.data_size)); + goto end; + } + if (prev_type != DYN_COL_NULL) + { + /* It is not first entry */ + if (prev_data_offset >= data_offset) + { + DBUG_PRINT("info", ("Field order: %u Previous data offset: %u" + " >= Current data offset: %u", + (uint)i, + (uint)prev_data_offset, + (uint)data_offset)); + goto end; + } + if (prev_name_offset > name_offset) + { + DBUG_PRINT("info", ("Field order: %u Previous name offset: %u" + " > Current name offset: %u", + (uint)i, + (uint)prev_data_offset, + (uint)data_offset)); + goto end; + } + if ((*fmt->column_sort)(&prev_key, &key) >= 0) + { + DBUG_PRINT("info", ("Field order: %u Previous key >= Current key", + (uint)i)); + goto end; + } + } + prev_num= num; + prev_name= name; + prev_data_offset= data_offset; + prev_name_offset= name_offset; + prev_type= type; + } + + /* check data, which we can */ + for (i= 0, header.entry= header.header; + i < header.column_count; + i++, header.entry+= header.entry_size) + { + DYNAMIC_COLUMN_VALUE store; + // already checked by previouse pass + (*fmt->type_and_offset_read)(&header.type, &header.offset, + header.entry + fmt->fixed_hdr_entry, + header.offset_size); + header.length= + hdr_interval_length(&header, header.entry + header.entry_size); + header.data= header.dtpool + header.offset; + switch ((header.type)) { + case DYN_COL_INT: + rc= dynamic_column_sint_read(&store, header.data, header.length); + break; + case DYN_COL_UINT: + rc= dynamic_column_uint_read(&store, header.data, header.length); + break; + case DYN_COL_DOUBLE: + rc= dynamic_column_double_read(&store, header.data, header.length); + break; + case DYN_COL_STRING: + rc= dynamic_column_string_read(&store, header.data, header.length); + break; +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + rc= dynamic_column_decimal_read(&store, header.data, header.length); + break; +#endif + case DYN_COL_DATETIME: + rc= dynamic_column_date_time_read(&store, header.data, + header.length); + break; + case DYN_COL_DATE: + rc= dynamic_column_date_read(&store, header.data, header.length); + break; + case DYN_COL_TIME: + rc= dynamic_column_time_read(&store, header.data, header.length); + break; + case DYN_COL_DYNCOL: + rc= dynamic_column_dyncol_read(&store, header.data, header.length); + break; + case DYN_COL_NULL: + default: + rc= ER_DYNCOL_FORMAT; + goto end; + } + if (rc != ER_DYNCOL_OK) + { + DBUG_ASSERT(rc < 0); + DBUG_PRINT("info", ("Field order: %u Can't read data: %i", + (uint)i, (int) rc)); + goto end; + } + } + + rc= ER_DYNCOL_OK; +end: + DBUG_RETURN(rc); +} + +enum enum_dyncol_func_result +mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val, + CHARSET_INFO *cs, char quote) +{ + char buff[40]; + size_t len; + switch (val->type) { + case DYN_COL_INT: + len= snprintf(buff, sizeof(buff), "%lld", val->x.long_value); + if (dynstr_append_mem(str, buff, len)) + return ER_DYNCOL_RESOURCE; + break; + case DYN_COL_UINT: + len= snprintf(buff, sizeof(buff), "%llu", val->x.ulong_value); + if (dynstr_append_mem(str, buff, len)) + return ER_DYNCOL_RESOURCE; + break; + case DYN_COL_DOUBLE: + len= snprintf(buff, sizeof(buff), "%g", val->x.double_value); + if (dynstr_realloc(str, len + (quote ? 2 : 0))) + return ER_DYNCOL_RESOURCE; + if (quote) + str->str[str->length++]= quote; + dynstr_append_mem(str, buff, len); + if (quote) + str->str[str->length++]= quote; + break; + case DYN_COL_DYNCOL: + case DYN_COL_STRING: + { + char *alloc= NULL; + char *from= val->x.string.value.str; + ulong bufflen; + my_bool conv= ((val->x.string.charset == cs) || + !strcmp(val->x.string.charset->name, cs->name)); + my_bool rc; + len= val->x.string.value.length; + bufflen= (ulong)(len * (conv ? cs->char_maxlen : 1)); + if (dynstr_realloc(str, bufflen)) + return ER_DYNCOL_RESOURCE; + + // guaranty UTF-8 string for value + if (!conv) + { +#ifndef LIBMARIADB + uint dummy_errors; +#endif + if (!quote) + { + /* convert to the destination */ + str->length+= +#ifndef LIBMARIADB + copy_and_convert_extended(str->str, bufflen, + cs, + from, (uint32)len, + val->x.string.charset, + &dummy_errors); +#else + convert_string(from, &len, val->x.string.charset, + str->str, (size_t *)&bufflen, cs); +#endif + return ER_DYNCOL_OK; + } + if ((alloc= (char *)my_malloc(bufflen, MYF(0)))) + { + len= +#ifndef LIBMARIADB + copy_and_convert_extended(alloc, bufflen, cs, + from, (uint32)len, + val->x.string.charset, + &dummy_errors); +#else + convert_string(from, &len, val->x.string.charset, + alloc, (size_t *)&bufflen, cs); +#endif + from= alloc; + } + else + return ER_DYNCOL_RESOURCE; + } + if (quote) + rc= dynstr_append_mem(str, "e, 1); + rc= dynstr_append_mem(str, from, len); + if (quote) + rc= dynstr_append_mem(str, "e, 1); + if (alloc) +#ifndef LIBMARIADB + my_free(alloc); +#else + my_free((gptr)alloc, MYF(0)); +#endif + if (rc) + return ER_DYNCOL_RESOURCE; + break; + } +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + { + int len= sizeof(buff); + decimal2string(&val->x.decimal.value, buff, &len, + 0, val->x.decimal.value.frac, + '0'); + if (dynstr_append_mem(str, buff, len)) + return ER_DYNCOL_RESOURCE; + break; + } +#endif + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: +#ifndef LIBMARIADB + len= my_TIME_to_str(&val->x.time_value, buff, AUTO_SEC_PART_DIGITS); +#else + len= mariadb_time_to_string(&val->x.time_value, buff, 39, AUTO_SEC_PART_DIGITS); +#endif + if (dynstr_realloc(str, len + (quote ? 2 : 0))) + return ER_DYNCOL_RESOURCE; + if (quote) + str->str[str->length++]= '"'; + dynstr_append_mem(str, buff, len); + if (quote) + str->str[str->length++]= '"'; + break; + case DYN_COL_NULL: + if (dynstr_append_mem(str, "null", 4)) + return ER_DYNCOL_RESOURCE; + break; + default: + return(ER_DYNCOL_FORMAT); + } + return(ER_DYNCOL_OK); +} + +enum enum_dyncol_func_result +mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val) +{ + enum enum_dyncol_func_result rc= ER_DYNCOL_OK; + *ll= 0; + switch (val->type) { + case DYN_COL_INT: + *ll= val->x.long_value; + break; + case DYN_COL_UINT: + *ll= (longlong)val->x.ulong_value; + if (val->x.ulong_value > ULONGLONG_MAX) + rc= ER_DYNCOL_TRUNCATED; + break; + case DYN_COL_DOUBLE: + *ll= (longlong)val->x.double_value; + if (((double) *ll) != val->x.double_value) + rc= ER_DYNCOL_TRUNCATED; + break; + case DYN_COL_STRING: + { + char *src= val->x.string.value.str; + size_t len= val->x.string.value.length; + longlong i= 0, sign= 1; + + while (len && isspace(*src)) src++,len--; + + if (len) + { + if (*src == '-') + { + sign= -1; + src++; + } else if (*src == '-') + src++; + while(len && isdigit(*src)) + { + i= i * 10 + (*src - '0'); + src++; + } + } + else + rc= ER_DYNCOL_TRUNCATED; + if (len) + rc= ER_DYNCOL_TRUNCATED; + *ll= i * sign; + break; + } +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + if (decimal2longlong(&val->x.decimal.value, ll) != E_DEC_OK) + rc= ER_DYNCOL_TRUNCATED; + break; +#endif + case DYN_COL_DATETIME: + *ll= (val->x.time_value.year * 10000000000ull + + val->x.time_value.month * 100000000L + + val->x.time_value.day * 1000000 + + val->x.time_value.hour * 10000 + + val->x.time_value.minute * 100 + + val->x.time_value.second) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_DATE: + *ll= (val->x.time_value.year * 10000 + + val->x.time_value.month * 100 + + val->x.time_value.day) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_TIME: + *ll= (val->x.time_value.hour * 10000 + + val->x.time_value.minute * 100 + + val->x.time_value.second) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_DYNCOL: + case DYN_COL_NULL: + rc= ER_DYNCOL_TRUNCATED; + break; + default: + return(ER_DYNCOL_FORMAT); + } + return(rc); +} + + +enum enum_dyncol_func_result +mariadb_dyncol_val_double(double *dbl, DYNAMIC_COLUMN_VALUE *val) +{ + enum enum_dyncol_func_result rc= ER_DYNCOL_OK; + *dbl= 0; + switch (val->type) { + case DYN_COL_INT: + *dbl= (double)val->x.long_value; + if (((longlong) *dbl) != val->x.long_value) + rc= ER_DYNCOL_TRUNCATED; + break; + case DYN_COL_UINT: + *dbl= (double)val->x.ulong_value; + if (((ulonglong) *dbl) != val->x.ulong_value) + rc= ER_DYNCOL_TRUNCATED; + break; + case DYN_COL_DOUBLE: + *dbl= val->x.double_value; + break; + case DYN_COL_STRING: + { + char *str, *end; + if ((str= malloc(val->x.string.value.length + 1))) + return ER_DYNCOL_RESOURCE; + memcpy(str, val->x.string.value.str, val->x.string.value.length); + str[val->x.string.value.length]= '\0'; + *dbl= strtod(str, &end); + if (*end != '\0') + rc= ER_DYNCOL_TRUNCATED; + } +#ifndef LIBMARIADB + case DYN_COL_DECIMAL: + if (decimal2double(&val->x.decimal.value, dbl) != E_DEC_OK) + rc= ER_DYNCOL_TRUNCATED; + break; +#endif + case DYN_COL_DATETIME: + *dbl= (double)(val->x.time_value.year * 10000000000ull + + val->x.time_value.month * 100000000L + + val->x.time_value.day * 1000000 + + val->x.time_value.hour * 10000 + + val->x.time_value.minute * 100 + + val->x.time_value.second) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_DATE: + *dbl= (double)(val->x.time_value.year * 10000 + + val->x.time_value.month * 100 + + val->x.time_value.day) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_TIME: + *dbl= (double)(val->x.time_value.hour * 10000 + + val->x.time_value.minute * 100 + + val->x.time_value.second) * + (val->x.time_value.neg ? -1 : 1); + break; + case DYN_COL_DYNCOL: + case DYN_COL_NULL: + rc= ER_DYNCOL_TRUNCATED; + break; + default: + return(ER_DYNCOL_FORMAT); + } + return(rc); +} + + +/** + Convert to JSON + + @param str The packed string + @param json Where to put json result + + @return ER_DYNCOL_* return code +*/ + +#define JSON_STACK_PROTECTION 10 + +static enum enum_dyncol_func_result +mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json, + uint lvl) +{ + DYN_HEADER header; + uint i; + enum enum_dyncol_func_result rc; + + if (lvl >= JSON_STACK_PROTECTION) + { + rc= ER_DYNCOL_RESOURCE; + goto err; + } + + + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + goto err; + + if (header.entry_size * header.column_count + FIXED_HEADER_SIZE > + str->length) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + + rc= ER_DYNCOL_RESOURCE; + + if (dynstr_append_mem(json, "{", 1)) + goto err; + for (i= 0, header.entry= header.header; + i < header.column_count; + i++, header.entry+= header.entry_size) + { + DYNAMIC_COLUMN_VALUE val; + if (i != 0 && dynstr_append_mem(json, ",", 1)) + goto err; + header.length= + hdr_interval_length(&header, header.entry + header.entry_size); + header.data= header.dtpool + header.offset; + /* + Check that the found data is withing the ranges. This can happen if + we get data with wrong offsets. + */ + if (header.length == DYNCOL_OFFSET_ERROR || + header.length > INT_MAX || header.offset > header.data_size) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + if ((rc= dynamic_column_get_value(&header, &val)) < 0) + goto err; + if (header.format == dyncol_fmt_num) + { + uint nm= uint2korr(header.entry); + if (dynstr_realloc(json, DYNCOL_NUM_CHAR + 3)) + goto err; + json->str[json->length++]= '"'; + json->length+= (my_snprintf(json->str + json->length, + DYNCOL_NUM_CHAR, "%u", nm)); + } + else + { + LEX_STRING name; + if (read_name(&header, header.entry, &name)) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + if (dynstr_realloc(json, name.length + 3)) + goto err; + json->str[json->length++]= '"'; + memcpy(json->str + json->length, name.str, name.length); + json->length+= name.length; + } + json->str[json->length++]= '"'; + json->str[json->length++]= ':'; + if (val.type == DYN_COL_DYNCOL) + { + /* here we use it only for read so can cheat a bit */ + DYNAMIC_COLUMN dc; + bzero(&dc, sizeof(dc)); + dc.str= val.x.string.value.str; + dc.length= val.x.string.value.length; + if (mariadb_dyncol_json_internal(&dc, json, lvl + 1) < 0) + { + dc.str= NULL; dc.length= 0; + goto err; + } + dc.str= NULL; dc.length= 0; + } + else + { + if ((rc= mariadb_dyncol_val_str(json, &val, + my_charset_utf8_general_ci, '"')) < 0) + goto err; + } + } + if (dynstr_append_mem(json, "}", 1)) + { + rc= ER_DYNCOL_RESOURCE; + goto err; + } + return ER_DYNCOL_OK; + +err: + json->length= 0; + return rc; +} + +enum enum_dyncol_func_result +mariadb_dyncol_json(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json) +{ + + if (init_dynamic_string(json, NULL, str->length * 2, 100)) + return ER_DYNCOL_RESOURCE; + + return mariadb_dyncol_json_internal(str, json, 1); +} + +/** + Convert to DYNAMIC_COLUMN_VALUE values and names (LEX_STING) dynamic array + + @param str The packed string + @param count number of elements in the arrays + @param names Where to put names (should be free by user) + @param vals Where to put values (should be free by user) + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_unpack(DYNAMIC_COLUMN *str, + uint *count, + LEX_STRING **names, DYNAMIC_COLUMN_VALUE **vals) +{ + DYN_HEADER header; + char *nm; + uint i; + enum enum_dyncol_func_result rc; + + *count= 0; *names= 0; *vals= 0; + + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + + + if (header.entry_size * header.column_count + FIXED_HEADER_SIZE > + str->length) + return ER_DYNCOL_FORMAT; + + *vals= (DYNAMIC_COLUMN_VALUE *)my_malloc(sizeof(DYNAMIC_COLUMN_VALUE)* header.column_count, MYF(0)); + if (header.format == dyncol_fmt_num) + { + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count + + DYNCOL_NUM_CHAR * header.column_count, MYF(0)); + nm= (char *)(names + sizeof(LEX_STRING) * header.column_count); + } + else + { + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count, MYF(0)); + nm= 0; + } + if (!(*vals) || !(*names)) + { + rc= ER_DYNCOL_RESOURCE; + goto err; + } + + for (i= 0, header.entry= header.header; + i < header.column_count; + i++, header.entry+= header.entry_size) + { + header.length= + hdr_interval_length(&header, header.entry + header.entry_size); + header.data= header.dtpool + header.offset; + /* + Check that the found data is withing the ranges. This can happen if + we get data with wrong offsets. + */ + if (header.length == DYNCOL_OFFSET_ERROR || + header.length > INT_MAX || header.offset > header.data_size) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + if ((rc= dynamic_column_get_value(&header, (*vals) + i)) < 0) + goto err; + + if (header.format == dyncol_fmt_num) + { + uint num= uint2korr(header.entry); + (*names)[i].str= nm; + (*names)[i].length= snprintf(nm, DYNCOL_NUM_CHAR, "%u", num); + nm+= (*names)[i].length + 1; + } + else + { + if (read_name(&header, header.entry, (*names) + i)) + { + rc= ER_DYNCOL_FORMAT; + goto err; + } + } + } + + *count= header.column_count; + return ER_DYNCOL_OK; + +err: + if (*vals) + { +#ifndef LIBMARIADB + my_free(*vals); +#else + my_free((gptr)*vals, MYF(0)); +#endif + *vals= 0; + } + if (*names) + { +#ifndef LIBMARIADB + my_free(*names); +#else + my_free((gptr)*names, MYF(0)); +#endif + *names= 0; + } + return rc; +} + + +/** + Get not NULL column count + + @param str The packed string + @param column_count Where to put column count + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +mariadb_dyncol_column_count(DYNAMIC_COLUMN *str, uint *column_count) +{ + DYN_HEADER header; + enum enum_dyncol_func_result rc; + + if ((rc= init_read_hdr(&header, str)) < 0) + return rc; + *column_count= header.column_count; + return rc; +} + +/** + Release dynamic column memory + + @param str dynamic column + @return void +*/ +void mariadb_dyncol_free(DYNAMIC_COLUMN *str) +{ + return dynstr_free(str); +} diff --git a/libmariadb/ma_time.c b/libmariadb/ma_time.c new file mode 100644 index 00000000..0b755064 --- /dev/null +++ b/libmariadb/ma_time.c @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (C) 2013 Monty Program AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*****************************************************************************/ +#include "mysys_priv.h" +#include +#include +#include + + +size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len, + unsigned int digits) +{ + size_t length; + + if (!time_str || !len) + return 0; + + if (digits == AUTO_SEC_PART_DIGITS) + digits= MIN((tm->second_part) ? SEC_PART_DIGITS : 0, 15); + + switch(tm->time_type) { + case MYSQL_TIMESTAMP_DATE: + length= snprintf(time_str, len, "%04u-%02u-%02u", tm->year, tm->month, tm->day); + digits= 0; + break; + case MYSQL_TIMESTAMP_DATETIME: + length= snprintf(time_str, len, "%04u-%02u-%02u %02u:%02u:%02u", + tm->year, tm->month, tm->day, tm->hour, tm->minute, tm->second); + break; + case MYSQL_TIMESTAMP_TIME: + length= snprintf(time_str, len, "%s%02u:%02u:%02u", + (tm->neg ? "-" : ""), tm->hour, tm->minute, tm->second); + break; + default: + time_str[0]= '\0'; + return 0; + break; + } + if (digits && (len < length)) + { + char helper[16]; + snprintf(helper, 16, ".%%0%du", digits); + length+= snprintf(time_str + length, len - length, helper, digits); + } + return length; +} + diff --git a/libmariadb/my_charset.c b/libmariadb/my_charset.c index ac5c133c..3c05a157 100644 --- a/libmariadb/my_charset.c +++ b/libmariadb/my_charset.c @@ -52,6 +52,8 @@ #include #include +#include + /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -492,170 +494,170 @@ mysql_mbcharlen_utf32(unsigned int utf32 __attribute((unused))) /* {{{ mysql_charsets */ const CHARSET_INFO compiled_charsets[] = { - { 1, 1, "big5","big5_chinese_ci", "", "", 1, 2, mysql_mbcharlen_big5, check_mb_big5}, - { 3, 1, "dec8", "dec8_swedisch_ci", "", "", 1, 1, NULL, NULL}, - { 4, 1, "cp850", "cp850_general_ci", "", "", 1, 1, NULL, NULL}, - { 6, 1, "hp8", "hp8_english_ci", "", "", 1, 1, NULL, NULL}, - { 7, 1, "koi8r", "koi8r_general_ci", "", "", 1, 1, NULL, NULL}, - { 8, 1, "latin1", "latin1_swedish_ci", "", "", 1, 1, NULL, NULL}, - { 9, 1, "latin2", "latin2_general_ci", "", "", 1, 1, NULL, NULL}, + { 1, 1, "big5","big5_chinese_ci", "", "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5}, + { 3, 1, "dec8", "dec8_swedisch_ci", "", "DEC", 1, 1, NULL, NULL}, + { 4, 1, "cp850", "cp850_general_ci", "", "CP850", 1, 1, NULL, NULL}, + { 6, 1, "hp8", "hp8_english_ci", "", "HP-ROMAN8", 1, 1, NULL, NULL}, + { 7, 1, "koi8r", "koi8r_general_ci", "", "KOI8R", 1, 1, NULL, NULL}, + { 8, 1, "latin1", "latin1_swedish_ci", "", "LATIN1", 1, 1, NULL, NULL}, + { 9, 1, "latin2", "latin2_general_ci", "", "LATIN2", 1, 1, NULL, NULL}, { 10, 1, "swe7", "swe7_swedish_ci", "", "", 1, 1, NULL, NULL}, - { 11, 1, "ascii", "ascii_general_ci", "", "", 1, 1, NULL, NULL}, - { 12, 1, "ujis", "ujis_japanese_ci", "", "", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis}, - { 13, 1, "sjis", "sjis_japanese_ci", "", "", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis}, - { 16, 1, "hebrew", "hebrew_general_ci", "", "", 1, 1, NULL, NULL}, - { 18, 1, "tis620", "tis620_thai_ci", "", "", 1, 1, NULL, NULL}, - { 19, 1, "euckr", "euckr_korean_ci", "", "", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr}, - { 22, 1, "koi8u", "koi8u_general_ci", "", "", 1, 1, NULL, NULL}, - { 24, 1, "gb2312", "gb2312_chinese_ci", "", "", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312}, - { 25, 1, "greek", "greek_general_ci", "", "", 1, 1, NULL, NULL}, - { 26, 1, "cp1250", "cp1250_general_ci", "", "", 1, 1, NULL, NULL}, - { 28, 1, "gbk", "gbk_chinese_ci", "", "", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk}, - { 30, 1, "latin5", "latin5_turkish_ci", "", "", 1, 1, NULL, NULL}, - { 32, 1, "armscii8", "armscii8_general_ci", "", "", 1, 1, NULL, NULL}, - { 33, 1, UTF8_MB3, UTF8_MB3"_general_ci", "UTF-8 Unicode", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 35, 1, "ucs2", "ucs2_general_ci", "UCS-2 Unicode", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 36, 1, "cp866", "cp866_general_ci", "", "", 1, 1, NULL, NULL}, + { 11, 1, "ascii", "ascii_general_ci", "", "ASCII", 1, 1, NULL, NULL}, + { 12, 1, "ujis", "ujis_japanese_ci", "", "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis}, + { 13, 1, "sjis", "sjis_japanese_ci", "", "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis}, + { 16, 1, "hebrew", "hebrew_general_ci", "", "HEBREW", 1, 1, NULL, NULL}, + { 18, 1, "tis620", "tis620_thai_ci", "", "TIS620", 1, 1, NULL, NULL}, + { 19, 1, "euckr", "euckr_korean_ci", "", "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr}, + { 22, 1, "koi8u", "koi8u_general_ci", "", "KOI8U", 1, 1, NULL, NULL}, + { 24, 1, "gb2312", "gb2312_chinese_ci", "", "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312}, + { 25, 1, "greek", "greek_general_ci", "", "GREEK", 1, 1, NULL, NULL}, + { 26, 1, "cp1250", "cp1250_general_ci", "", "CP1250", 1, 1, NULL, NULL}, + { 28, 1, "gbk", "gbk_chinese_ci", "", "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk}, + { 30, 1, "latin5", "latin5_turkish_ci", "", "LATIN5", 1, 1, NULL, NULL}, + { 32, 1, "armscii8", "armscii8_general_ci", "", "ARMSCII-8", 1, 1, NULL, NULL}, + { 33, 1, UTF8_MB3, UTF8_MB3"_general_ci", "UTF-8 Unicode", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 35, 1, "ucs2", "ucs2_general_ci", "UCS-2 Unicode", "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 36, 1, "cp866", "cp866_general_ci", "", "CP866", 1, 1, NULL, NULL}, { 37, 1, "keybcs2", "keybcs2_general_ci", "", "", 1, 1, NULL, NULL}, - { 38, 1, "macce", "macce_general_ci", "", "", 1, 1, NULL, NULL}, - { 39, 1, "macroman", "macroman_general_ci", "", "", 1, 1, NULL, NULL}, - { 40, 1, "cp852", "cp852_general_ci", "", "", 1, 1, NULL, NULL}, - { 41, 1, "latin7", "latin7_general_ci", "", "", 1, 1, NULL, NULL}, - { 51, 1, "cp1251", "cp1251_general_ci", "", "", 1, 1, NULL, NULL}, - { 57, 1, "cp1256", "cp1256_general_ci", "", "", 1, 1, NULL, NULL}, - { 59, 1, "cp1257", "cp1257_general_ci", "", "", 1, 1, NULL, NULL}, - { 63, 1, "binary", "binary", "", "", 1, 1, NULL, NULL}, - { 92, 1, "geostd8", "geostd8_general_ci", "", "", 1, 1, NULL, NULL}, - { 95, 1, "cp932", "cp932_japanese_ci", "", "", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932}, - { 97, 1, "eucjpms", "eucjpms_japanese_ci", "", "", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms}, - { 2, 1, "latin2", "latin2_czech_cs", "", "", 1, 1, NULL, NULL}, - { 5, 1, "latin1", "latin1_german_ci", "", "", 1, 1, NULL, NULL}, - { 14, 1, "cp1251", "cp1251_bulgarian_ci", "", "", 1, 1, NULL, NULL}, - { 15, 1, "latin1", "latin1_danish_ci", "", "", 1, 1, NULL, NULL}, + { 38, 1, "macce", "macce_general_ci", "", "CP1282", 1, 1, NULL, NULL}, + { 39, 1, "macroman", "macroman_general_ci", "MACINTOSH", "", 1, 1, NULL, NULL}, + { 40, 1, "cp852", "cp852_general_ci", "", "CP852", 1, 1, NULL, NULL}, + { 41, 1, "latin7", "latin7_general_ci", "", "LATIN7", 1, 1, NULL, NULL}, + { 51, 1, "cp1251", "cp1251_general_ci", "", "CP1251", 1, 1, NULL, NULL}, + { 57, 1, "cp1256", "cp1256_general_ci", "", "CP1256", 1, 1, NULL, NULL}, + { 59, 1, "cp1257", "cp1257_general_ci", "", "CP1257", 1, 1, NULL, NULL}, + { 63, 1, "binary", "binary", "", "ASCII", 1, 1, NULL, NULL}, + { 92, 1, "geostd8", "geostd8_general_ci", "", "GEORGIAN-PS", 1, 1, NULL, NULL}, + { 95, 1, "cp932", "cp932_japanese_ci", "", "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932}, + { 97, 1, "eucjpms", "eucjpms_japanese_ci", "", "EUC-JP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms}, + { 2, 1, "latin2", "latin2_czech_cs", "", "LATIN2", 1, 1, NULL, NULL}, + { 5, 1, "latin1", "latin1_german_ci", "", "LATIN1", 1, 1, NULL, NULL}, + { 14, 1, "cp1251", "cp1251_bulgarian_ci", "", "CP1251", 1, 1, NULL, NULL}, + { 15, 1, "latin1", "latin1_danish_ci", "", "LATIN1", 1, 1, NULL, NULL}, { 17, 1, "filename", "filename", "", "", 1, 5, NULL, NULL}, - { 20, 1, "latin7", "latin7_estonian_cs", "", "", 1, 1, NULL, NULL}, - { 21, 1, "latin2", "latin2_hungarian_ci", "", "", 1, 1, NULL, NULL}, - { 23, 1, "cp1251", "cp1251_ukrainian_ci", "", "", 1, 1, NULL, NULL}, - { 27, 1, "latin2", "latin2_croatian_ci", "", "", 1, 1, NULL, NULL}, - { 29, 1, "cp1257", "cp1257_lithunian_ci", "", "", 1, 1, NULL, NULL}, - { 31, 1, "latin1", "latin1_german2_ci", "", "", 1, 1, NULL, NULL}, - { 34, 1, "cp1250", "cp1250_czech_cs", "", "", 1, 1, NULL, NULL}, - { 42, 1, "latin7", "latin7_general_cs", "", "", 1, 1, NULL, NULL}, - { 43, 1, "macce", "macce_bin", "", "", 1, 1, NULL, NULL}, - { 44, 1, "cp1250", "cp1250_croatian_ci", "", "", 1, 1, NULL, NULL}, - { 45, 1, UTF8_MB4, UTF8_MB4"_general_ci", "UTF-8 Unicode", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 46, 1, UTF8_MB4, UTF8_MB4"_bin", "UTF-8 Unicode", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 47, 1, "latin1", "latin1_bin", "", "", 1, 1, NULL, NULL}, - { 48, 1, "latin1", "latin1_general_ci", "", "", 1, 1, NULL, NULL}, - { 49, 1, "latin1", "latin1_general_cs", "", "", 1, 1, NULL, NULL}, - { 50, 1, "cp1251", "cp1251_bin", "", "", 1, 1, NULL, NULL}, - { 52, 1, "cp1251", "cp1251_general_cs", "", "", 1, 1, NULL, NULL}, - { 53, 1, "macroman", "macroman_bin", "", "", 1, 1, NULL, NULL}, - { 54, 1, "utf16", "utf16_general_ci", "UTF_16 Unicode", "", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16}, - { 55, 1, "utf16", "utf16_bin", "UTF-16 Unicode", "", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16}, - { 58, 1, "cp1257", "cp1257_bin", "", "", 1, 1, NULL, NULL}, + { 20, 1, "latin7", "latin7_estonian_cs", "", "LATIN7", 1, 1, NULL, NULL}, + { 21, 1, "latin2", "latin2_hungarian_ci", "", "LATIN2", 1, 1, NULL, NULL}, + { 23, 1, "cp1251", "cp1251_ukrainian_ci", "", "CP1251", 1, 1, NULL, NULL}, + { 27, 1, "latin2", "latin2_croatian_ci", "", "LATIN2", 1, 1, NULL, NULL}, + { 29, 1, "cp1257", "cp1257_lithunian_ci", "", "CP1257", 1, 1, NULL, NULL}, + { 31, 1, "latin1", "latin1_german2_ci", "", "LATIN1", 1, 1, NULL, NULL}, + { 34, 1, "cp1250", "cp1250_czech_cs", "", "CP1250", 1, 1, NULL, NULL}, + { 42, 1, "latin7", "latin7_general_cs", "", "LATIN7", 1, 1, NULL, NULL}, + { 43, 1, "macce", "macce_bin", "", "MACINTOSH", 1, 1, NULL, NULL}, + { 44, 1, "cp1250", "cp1250_croatian_ci", "", "CP1250", 1, 1, NULL, NULL}, + { 45, 1, UTF8_MB4, UTF8_MB4"_general_ci", "UTF-8 Unicode", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 46, 1, UTF8_MB4, UTF8_MB4"_bin", "UTF-8 Unicode", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 47, 1, "latin1", "latin1_bin", "", "LATIN1", 1, 1, NULL, NULL}, + { 48, 1, "latin1", "latin1_general_ci", "", "LATIN1", 1, 1, NULL, NULL}, + { 49, 1, "latin1", "latin1_general_cs", "", "LATIN1", 1, 1, NULL, NULL}, + { 50, 1, "cp1251", "cp1251_bin", "", "CP1251", 1, 1, NULL, NULL}, + { 52, 1, "cp1251", "cp1251_general_cs", "", "CP1251", 1, 1, NULL, NULL}, + { 53, 1, "macroman", "macroman_bin", "", "MACINTOSH", 1, 1, NULL, NULL}, + { 54, 1, "utf16", "utf16_general_ci", "UTF_16 Unicode", "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16}, + { 55, 1, "utf16", "utf16_bin", "UTF-16 Unicode", "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16}, + { 58, 1, "cp1257", "cp1257_bin", "", "CP1257", 1, 1, NULL, NULL}, #ifdef USED_TO_BE_SO_BEFORE_MYSQL_5_5 - { 60, 1, "armascii8", "armascii8_bin", "", "", 1, 1, NULL, NULL}, + { 60, 1, "armascii8", "armascii8_bin", "", "ARMSCII-8", 1, 1, NULL, NULL}, #endif - { 60, 1, "utf32", "utf32_general_ci", "UTF-32 Unicode", "", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, - { 61, 1, "utf32", "utf32_bin", "UTF-32 Unicode", "", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, - { 65, 1, "ascii", "ascii_bin", "", "", 1, 1, NULL, NULL}, - { 66, 1, "cp1250", "cp1250_bin", "", "", 1, 1, NULL, NULL}, - { 67, 1, "cp1256", "cp1256_bin", "", "", 1, 1, NULL, NULL}, - { 68, 1, "cp866", "cp866_bin", "", "", 1, 1, NULL, NULL}, - { 69, 1, "dec8", "dec8_bin", "", "", 1, 1, NULL, NULL}, - { 70, 1, "greek", "greek_bin", "", "", 1, 1, NULL, NULL}, - { 71, 1, "hebew", "hebrew_bin", "", "", 1, 1, NULL, NULL}, - { 72, 1, "hp8", "hp8_bin", "", "", 1, 1, NULL, NULL}, + { 60, 1, "utf32", "utf32_general_ci", "UTF-32 Unicode", "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, + { 61, 1, "utf32", "utf32_bin", "UTF-32 Unicode", "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, + { 65, 1, "ascii", "ascii_bin", "", "ASCII", 1, 1, NULL, NULL}, + { 66, 1, "cp1250", "cp1250_bin", "", "CP1250", 1, 1, NULL, NULL}, + { 67, 1, "cp1256", "cp1256_bin", "", "CP1256", 1, 1, NULL, NULL}, + { 68, 1, "cp866", "cp866_bin", "", "CP866", 1, 1, NULL, NULL}, + { 69, 1, "dec8", "dec8_bin", "", "DEC", 1, 1, NULL, NULL}, + { 70, 1, "greek", "greek_bin", "", "GREEK", 1, 1, NULL, NULL}, + { 71, 1, "hebrew", "hebrew_bin", "", "hebrew", 1, 1, NULL, NULL}, + { 72, 1, "hp8", "hp8_bin", "", "HPROMAN-8", 1, 1, NULL, NULL}, { 73, 1, "keybcs2", "keybcs2_bin", "", "", 1, 1, NULL, NULL}, - { 74, 1, "koi8r", "koi8r_bin", "", "", 1, 1, NULL, NULL}, - { 75, 1, "koi8u", "koi8u_bin", "", "", 1, 1, NULL, NULL}, - { 77, 1, "latin2", "latin2_bin", "", "", 1, 1, NULL, NULL}, - { 78, 1, "latin5", "latin5_bin", "", "", 1, 1, NULL, NULL}, - { 79, 1, "latin7", "latin7_bin", "", "", 1, 1, NULL, NULL}, - { 80, 1, "cp850", "cp850_bin", "", "", 1, 1, NULL, NULL}, - { 81, 1, "cp852", "cp852_bin", "", "", 1, 1, NULL, NULL}, + { 74, 1, "koi8r", "koi8r_bin", "", "KOI8R", 1, 1, NULL, NULL}, + { 75, 1, "koi8u", "koi8u_bin", "", "KOI8U", 1, 1, NULL, NULL}, + { 77, 1, "latin2", "latin2_bin", "", "LATIN2", 1, 1, NULL, NULL}, + { 78, 1, "latin5", "latin5_bin", "", "LATIN5", 1, 1, NULL, NULL}, + { 79, 1, "latin7", "latin7_bin", "", "LATIN7", 1, 1, NULL, NULL}, + { 80, 1, "cp850", "cp850_bin", "", "CP850", 1, 1, NULL, NULL}, + { 81, 1, "cp852", "cp852_bin", "", "CP852", 1, 1, NULL, NULL}, { 82, 1, "swe7", "swe7_bin", "", "", 1, 1, NULL, NULL}, - { 93, 1, "geostd8", "geostd8_bin", "", "", 1, 1, NULL, NULL}, - { 83, 1, UTF8_MB3, UTF8_MB3"_bin", "UTF-8 Unicode", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 84, 1, "big5", "big5_bin", "", "", 1, 2, mysql_mbcharlen_big5, check_mb_big5}, - { 85, 1, "euckr", "euckr_bin", "", "", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr}, - { 86, 1, "gb2312", "gb2312_bin", "", "", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312}, - { 87, 1, "gbk", "gbk_bin", "", "", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk}, - { 88, 1, "sjis", "sjis_bin", "", "", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis}, - { 89, 1, "tis620", "tis620_bin", "", "", 1, 1, NULL, NULL}, - { 90, 1, "ucs2", "ucs2_bin", "UCS-2 Unicode", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 91, 1, "ujis", "ujis_bin", "", "", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis}, - { 94, 1, "latin1", "latin1_spanish_ci", "", "", 1, 1, NULL, NULL}, - { 96, 1, "cp932", "cp932_bin", "", "", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932}, - { 99, 1, "cp1250", "cp1250_polish_ci", "", "", 1, 1, NULL, NULL}, - { 98, 1, "eucjpms", "eucjpms_bin", "", "", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms}, - { 128, 1, "ucs2", "ucs2_unicode_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 129, 1, "ucs2", "ucs2_icelandic_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 130, 1, "ucs2", "ucs2_latvian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 131, 1, "ucs2", "ucs2_romanian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 132, 1, "ucs2", "ucs2_slovenian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 133, 1, "ucs2", "ucs2_polish_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 134, 1, "ucs2", "ucs2_estonian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 135, 1, "ucs2", "ucs2_spanish_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 136, 1, "ucs2", "ucs2_swedish_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 137, 1, "ucs2", "ucs2_turkish_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 138, 1, "ucs2", "ucs2_czech_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 139, 1, "ucs2", "ucs2_danish_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 140, 1, "ucs2", "ucs2_lithunian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 141, 1, "ucs2", "ucs2_slovak_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 142, 1, "ucs2", "ucs2_spanish2_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 143, 1, "ucs2", "ucs2_roman_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 144, 1, "ucs2", "ucs2_persian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 145, 1, "ucs2", "ucs2_esperanto_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 146, 1, "ucs2", "ucs2_hungarian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 147, 1, "ucs2", "ucs2_sinhala_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, - { 149, 1, "ucs2", "ucs2_croatian_ci", "", "", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */ + { 93, 1, "geostd8", "geostd8_bin", "", "GEORGIAN-PS", 1, 1, NULL, NULL}, + { 83, 1, UTF8_MB3, UTF8_MB3"_bin", "UTF-8 Unicode", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 84, 1, "big5", "big5_bin", "", "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5}, + { 85, 1, "euckr", "euckr_bin", "", "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr}, + { 86, 1, "gb2312", "gb2312_bin", "", "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312}, + { 87, 1, "gbk", "gbk_bin", "", "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk}, + { 88, 1, "sjis", "sjis_bin", "", "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis}, + { 89, 1, "tis620", "tis620_bin", "TIS620", "", 1, 1, NULL, NULL}, + { 90, 1, "ucs2", "ucs2_bin", "UCS-2 Unicode", "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 91, 1, "ujis", "ujis_bin", "", "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis}, + { 94, 1, "latin1", "latin1_spanish_ci", "", "LATIN1", 1, 1, NULL, NULL}, + { 96, 1, "cp932", "cp932_bin", "", "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932}, + { 99, 1, "cp1250", "cp1250_polish_ci", "", "CP1250", 1, 1, NULL, NULL}, + { 98, 1, "eucjpms", "eucjpms_bin", "", "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms}, + { 128, 1, "ucs2", "ucs2_unicode_ci", "", "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 129, 1, "ucs2", "ucs2_icelandic_ci", "", "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 130, 1, "ucs2", "ucs2_latvian_ci", "", "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 131, 1, "ucs2", "ucs2_romanian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 132, 1, "ucs2", "ucs2_slovenian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 133, 1, "ucs2", "ucs2_polish_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 134, 1, "ucs2", "ucs2_estonian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 135, 1, "ucs2", "ucs2_spanish_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 136, 1, "ucs2", "ucs2_swedish_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 137, 1, "ucs2", "ucs2_turkish_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 138, 1, "ucs2", "ucs2_czech_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 139, 1, "ucs2", "ucs2_danish_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 140, 1, "ucs2", "ucs2_lithunian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 141, 1, "ucs2", "ucs2_slovak_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 142, 1, "ucs2", "ucs2_spanish2_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 143, 1, "ucs2", "ucs2_roman_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 144, 1, "ucs2", "ucs2_persian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 145, 1, "ucs2", "ucs2_esperanto_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 146, 1, "ucs2", "ucs2_hungarian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 147, 1, "ucs2", "ucs2_sinhala_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, + { 149, 1, "ucs2", "ucs2_croatian_ci", "", "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */ - { 192, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 193, 1, UTF8_MB3, UTF8_MB3"_icelandic_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 194, 1, UTF8_MB3, UTF8_MB3"_latvian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 195, 1, UTF8_MB3, UTF8_MB3"_romanian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 196, 1, UTF8_MB3, UTF8_MB3"_slovenian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 197, 1, UTF8_MB3, UTF8_MB3"_polish_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 198, 1, UTF8_MB3, UTF8_MB3"_estonian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 119, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 200, 1, UTF8_MB3, UTF8_MB3"_swedish_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 201, 1, UTF8_MB3, UTF8_MB3"_turkish_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 202, 1, UTF8_MB3, UTF8_MB3"_czech_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 203, 1, UTF8_MB3, UTF8_MB3"_danish_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, - { 204, 1, UTF8_MB3, UTF8_MB3"_lithunian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, - { 205, 1, UTF8_MB3, UTF8_MB3"_slovak_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 206, 1, UTF8_MB3, UTF8_MB3"_spanish2_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 207, 1, UTF8_MB3, UTF8_MB3"_roman_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 208, 1, UTF8_MB3, UTF8_MB3"_persian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 209, 1, UTF8_MB3, UTF8_MB3"_esperanto_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 210, 1, UTF8_MB3, UTF8_MB3"_hungarian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 211, 1, UTF8_MB3, UTF8_MB3"_sinhala_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 213, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", "", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ + { 192, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 193, 1, UTF8_MB3, UTF8_MB3"_icelandic_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 194, 1, UTF8_MB3, UTF8_MB3"_latvian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 195, 1, UTF8_MB3, UTF8_MB3"_romanian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 196, 1, UTF8_MB3, UTF8_MB3"_slovenian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 197, 1, UTF8_MB3, UTF8_MB3"_polish_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 198, 1, UTF8_MB3, UTF8_MB3"_estonian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 119, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 200, 1, UTF8_MB3, UTF8_MB3"_swedish_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 201, 1, UTF8_MB3, UTF8_MB3"_turkish_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 202, 1, UTF8_MB3, UTF8_MB3"_czech_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 203, 1, UTF8_MB3, UTF8_MB3"_danish_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, + { 204, 1, UTF8_MB3, UTF8_MB3"_lithunian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, + { 205, 1, UTF8_MB3, UTF8_MB3"_slovak_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 206, 1, UTF8_MB3, UTF8_MB3"_spanish2_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 207, 1, UTF8_MB3, UTF8_MB3"_roman_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 208, 1, UTF8_MB3, UTF8_MB3"_persian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 209, 1, UTF8_MB3, UTF8_MB3"_esperanto_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 210, 1, UTF8_MB3, UTF8_MB3"_hungarian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 211, 1, UTF8_MB3, UTF8_MB3"_sinhala_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 213, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", "UTF8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ - { 224, 1, UTF8_MB4, UTF8_MB4"_unicode_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 225, 1, UTF8_MB4, UTF8_MB4"_icelandic_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 226, 1, UTF8_MB4, UTF8_MB4"_latvian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 227, 1, UTF8_MB4, UTF8_MB4"_romanian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 228, 1, UTF8_MB4, UTF8_MB4"_slovenian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 229, 1, UTF8_MB4, UTF8_MB4"_polish_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 230, 1, UTF8_MB4, UTF8_MB4"_estonian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 231, 1, UTF8_MB4, UTF8_MB4"_spanish_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 232, 1, UTF8_MB4, UTF8_MB4"_swedish_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 233, 1, UTF8_MB4, UTF8_MB4"_turkish_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 234, 1, UTF8_MB4, UTF8_MB4"_czech_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 235, 1, UTF8_MB4, UTF8_MB4"_danish_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 236, 1, UTF8_MB4, UTF8_MB4"_lithuanian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 237, 1, UTF8_MB4, UTF8_MB4"_slovak_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 238, 1, UTF8_MB4, UTF8_MB4"_spanish2_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 239, 1, UTF8_MB4, UTF8_MB4"_roman_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 240, 1, UTF8_MB4, UTF8_MB4"_persian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 241, 1, UTF8_MB4, UTF8_MB4"_esperanto_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 242, 1, UTF8_MB4, UTF8_MB4"_hungarian_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 243, 1, UTF8_MB4, UTF8_MB4"_sinhala_ci", "", "", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 224, 1, UTF8_MB4, UTF8_MB4"_unicode_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 225, 1, UTF8_MB4, UTF8_MB4"_icelandic_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 226, 1, UTF8_MB4, UTF8_MB4"_latvian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 227, 1, UTF8_MB4, UTF8_MB4"_romanian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 228, 1, UTF8_MB4, UTF8_MB4"_slovenian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 229, 1, UTF8_MB4, UTF8_MB4"_polish_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 230, 1, UTF8_MB4, UTF8_MB4"_estonian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 231, 1, UTF8_MB4, UTF8_MB4"_spanish_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 232, 1, UTF8_MB4, UTF8_MB4"_swedish_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 233, 1, UTF8_MB4, UTF8_MB4"_turkish_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 234, 1, UTF8_MB4, UTF8_MB4"_czech_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 235, 1, UTF8_MB4, UTF8_MB4"_danish_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 236, 1, UTF8_MB4, UTF8_MB4"_lithuanian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 237, 1, UTF8_MB4, UTF8_MB4"_slovak_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 238, 1, UTF8_MB4, UTF8_MB4"_spanish2_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 239, 1, UTF8_MB4, UTF8_MB4"_roman_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 240, 1, UTF8_MB4, UTF8_MB4"_persian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 241, 1, UTF8_MB4, UTF8_MB4"_esperanto_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 242, 1, UTF8_MB4, UTF8_MB4"_hungarian_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 243, 1, UTF8_MB4, UTF8_MB4"_sinhala_ci", "", "UTF8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, - { 254, 1, UTF8_MB3, UTF8_MB3"_general_cs", "", "", 1, 3, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 254, 1, UTF8_MB3, UTF8_MB3"_general_cs", "", "UTF8", 1, 3, mysql_mbcharlen_utf8, check_mb_utf8_valid}, { 0, 0, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL} }; /* }}} */ @@ -835,6 +837,7 @@ struct st_madb_os_charset { char *identifier; char *description; char *charset; + char *iconv_cs; unsigned char supported; }; @@ -846,227 +849,228 @@ struct st_madb_os_charset MADB_OS_CHARSET[]= { #ifdef _WIN32 /* Windows code pages */ - {"037", "IBM EBCDIC US-Canada", NULL, MADB_CS_UNSUPPORTED}, - {"437", "OEM United States", "cp850", MADB_CS_APPROX}, - {"500", "IBM EBCDIC International", NULL, MADB_CS_UNSUPPORTED}, - {"708", "Arabic (ASMO 708)", NULL, MADB_CS_UNSUPPORTED}, - {"709", "Arabic (ASMO-449+, BCON V4)", NULL, MADB_CS_UNSUPPORTED}, - {"710", "Transparent Arabic", NULL, MADB_CS_UNSUPPORTED}, - {"720", "Arabic (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"737", "Greek (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"775", "Baltic (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"850", "Western European (DOS)", "cp850", MADB_CS_EXACT}, - {"852", "Central European (DOS)", "cp852", MADB_CS_EXACT}, - {"855", "Cyrillic (primarily Russian)", NULL, MADB_CS_UNSUPPORTED}, - {"857", "Turkish (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"858", "OEM Multilingual Latin 1 + Euro symbol", "cp850", MADB_CS_EXACT}, - {"860", "Portuguese (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"861", "Icelandic (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"862", "Hebrew (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"863", "French Canadian (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"864", "Arabic (864)", NULL, MADB_CS_UNSUPPORTED}, - {"865", "Nordic (DOS)", NULL, MADB_CS_UNSUPPORTED}, - {"866", "Cyrillic (DOS)", "cp866", MADB_CS_EXACT}, - {"869", "Greek, Modern (DOS)", "greek", MADB_CS_EXACT}, - {"870", "IBM EBCDIC Multilingual Latin 2", NULL, MADB_CS_UNSUPPORTED}, - {"874", "Thai (Windows)", "tis620", MADB_CS_UNSUPPORTED}, - {"875", "Greek Modern", NULL, MADB_CS_UNSUPPORTED}, - {"932", "Japanese (Shift-JIS)", "cp932", MADB_CS_EXACT}, - {"936", "Chinese Simplified (GB2312)", "gbk", MADB_CS_EXACT}, - {"949", "ANSI/OEM Korean (Unified Hangul Code)", "euckr", MADB_CS_EXACT}, - {"950", "Chinese Traditional (Big5)", "big5", MADB_CS_EXACT}, - {"1026", "EBCDIC Turkish (Latin 5)", NULL, MADB_CS_UNSUPPORTED}, - {"1047", "EBCDIC Latin 1/Open System", NULL, MADB_CS_UNSUPPORTED}, - {"1140", "IBM EBCDIC (US-Canada-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1141", "IBM EBCDIC (Germany-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1142", "IBM EBCDIC (Denmark-Norway-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1143", "IBM EBCDIC (Finland-Sweden-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1144", "IBM EBCDIC (Italy-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1145", "IBM EBCDIC (Spain-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1146", "IBM EBCDIC (UK-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1147", "IBM EBCDIC (France-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1148", "IBM EBCDIC (International-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1149", "IBM EBCDIC (Icelandic-Euro)", NULL, MADB_CS_UNSUPPORTED}, - {"1200", "UTF-16, little endian byte order", NULL, MADB_CS_UNSUPPORTED}, - {"1201", "UTF-16, big endian byte order", "utf16", MADB_CS_UNSUPPORTED}, - {"1250", "Central European (Windows)", "cp1250", MADB_CS_EXACT}, - {"1251", "Cyrillic (Windows)", "cp1251", MADB_CS_EXACT}, - {"1252", "Western European (Windows)", "latin1", MADB_CS_EXACT}, - {"1253", "Greek (Windows)", "greek", MADB_CS_EXACT}, - {"1254", "Turkish (Windows)", "latin5", MADB_CS_EXACT}, - {"1255", "Hebrew (Windows)", "hewbrew", MADB_CS_EXACT}, - {"1256", "Arabic (Windows)", "cp1256", MADB_CS_EXACT}, - {"1257", "Baltic (Windows)","cp1257", MADB_CS_EXACT}, - {"1258", "Vietnamese (Windows)", NULL, MADB_CS_UNSUPPORTED}, - {"1361", "Korean (Johab)", NULL, MADB_CS_UNSUPPORTED}, - {"10000", "Western European (Mac)", "macroman", MADB_CS_EXACT}, - {"10001", "Japanese (Mac)", "sjis", MADB_CS_EXACT}, - {"10002", "Chinese Traditional (Mac)", "big5", MADB_CS_EXACT}, - {"10003", "Korean (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10004", "Arabic (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10005", "Hebrew (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10006", "Greek (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10007", "Cyrillic (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10008", "Chinese Simplified (Mac)", "gb2312", MADB_CS_EXACT}, - {"10010", "Romanian (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10017", "Ukrainian (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10021", "Thai (Mac)", "tis620", MADB_CS_EXACT}, - {"10029", "Central European (Mac)", "macce", MADB_CS_EXACT}, - {"10079", "Icelandic (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10081", "Turkish (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"10082", "Croatian (Mac)", NULL, MADB_CS_UNSUPPORTED}, - {"12000", "Unicode UTF-32, little endian byte order", NULL, MADB_CS_UNSUPPORTED}, - {"12001", "Unicode UTF-32, big endian byte order", "utf32", MADB_CS_UNSUPPORTED}, - {"20000", "Chinese Traditional (CNS)", NULL, MADB_CS_UNSUPPORTED}, - {"20001", "TCA Taiwan", NULL, MADB_CS_UNSUPPORTED}, - {"20002", "Chinese Traditional (Eten)", NULL, MADB_CS_UNSUPPORTED}, - {"20003", "IBM5550 Taiwan", NULL, MADB_CS_UNSUPPORTED}, - {"20004", "TeleText Taiwan", NULL, MADB_CS_UNSUPPORTED}, - {"20005", "Wang Taiwan", NULL, MADB_CS_UNSUPPORTED}, - {"20105", "Western European (IA5)", NULL, MADB_CS_UNSUPPORTED}, - {"20106", "IA5 German (7-bit)", NULL, MADB_CS_UNSUPPORTED}, - {"20107", "Swedish (7-bit)", NULL, MADB_CS_UNSUPPORTED}, - {"20108", "Norwegian (7-bit)", NULL, MADB_CS_UNSUPPORTED}, - {"20127", "US-ASCII (7-bit)", NULL, MADB_CS_UNSUPPORTED}, - {"20261", "T.61", NULL, MADB_CS_UNSUPPORTED}, - {"20269", "Non-Spacing Accent", NULL, MADB_CS_UNSUPPORTED}, - {"20273", "EBCDIC Germany", NULL, MADB_CS_UNSUPPORTED}, - {"20277", "EBCDIC Denmark-Norway", NULL, MADB_CS_UNSUPPORTED}, - {"20278", "EBCDIC Finland-Sweden", NULL, MADB_CS_UNSUPPORTED}, - {"20280", "EBCDIC Italy", NULL, MADB_CS_UNSUPPORTED}, - {"20284", "EBCDIC Latin America-Spain", NULL, MADB_CS_UNSUPPORTED}, - {"20285", "EBCDIC United Kingdom", NULL, MADB_CS_UNSUPPORTED}, - {"20290", "EBCDIC Japanese Katakana Extended", NULL, MADB_CS_UNSUPPORTED}, - {"20297", "EBCDIC France", NULL, MADB_CS_UNSUPPORTED}, - {"20420", "EBCDIC Arabic", NULL, MADB_CS_UNSUPPORTED}, - {"20423", "EBCDIC Greek", NULL, MADB_CS_UNSUPPORTED}, - {"20424", "EBCDIC Hebrew", NULL, MADB_CS_UNSUPPORTED}, - {"20833", "EBCDIC Korean Extended", NULL, MADB_CS_UNSUPPORTED}, - {"20838", "EBCDIC Thai", NULL, MADB_CS_UNSUPPORTED}, - {"20866", "Cyrillic (KOI8-R)", "koi8r", MADB_CS_EXACT}, - {"20871", "EBCDIC Icelandic", NULL, MADB_CS_UNSUPPORTED}, - {"20880", "EBCDIC Cyrillic Russian", NULL, MADB_CS_UNSUPPORTED}, - {"20905", "EBCDIC Turkish", NULL, MADB_CS_UNSUPPORTED}, - {"20924", "EBCDIC Latin 1/Open System (1047 + Euro symbol)", NULL, MADB_CS_UNSUPPORTED}, - {"20932", "Japanese (JIS 0208-1990 and 0121-1990)", "ujis", MADB_CS_EXACT}, - {"20936", "Chinese Simplified (GB2312-80)", "gb2312", MADB_CS_APPROX}, - {"20949", "Korean Wansung", "euckr", MADB_CS_APPROX}, - {"21025", "EBCDIC Cyrillic Serbian-Bulgarian", NULL, MADB_CS_UNSUPPORTED}, - {"21866", "Cyrillic (KOI8-U)", "koi8u", MADB_CS_EXACT}, - {"28591", "Western European (ISO)", "latin1", MADB_CS_APPROX}, - {"28592", "Central European (ISO)", "latin2", MADB_CS_EXACT}, - {"28593", "Latin 3", NULL, MADB_CS_UNSUPPORTED}, - {"28594", "Baltic", NULL, MADB_CS_UNSUPPORTED}, - {"28595", "ISO 8859-5 Cyrillic", NULL, MADB_CS_UNSUPPORTED}, - {"28596", "ISO 8859-6 Arabic", NULL, MADB_CS_UNSUPPORTED}, - {"28597", "ISO 8859-7 Greek", "greek", MADB_CS_EXACT}, - {"28598", "Hebrew (ISO-Visual)", "hebrew", MADB_CS_EXACT}, - {"28599", "ISO 8859-9 Turkish", "latin5", MADB_CS_EXACT}, - {"28603", "ISO 8859-13 Estonian", "latin7", MADB_CS_EXACT}, - {"28605", "8859-15 Latin 9", NULL, MADB_CS_UNSUPPORTED}, - {"29001", "Europa 3", NULL, MADB_CS_UNSUPPORTED}, - {"38598", "ISO 8859-8 Hebrew; Hebrew (ISO-Logical)", "hebrew", MADB_CS_EXACT}, - {"50220", "ISO 2022 Japanese with no halfwidth Katakana", NULL, MADB_CS_UNSUPPORTED}, - {"50221", "ISO 2022 Japanese with halfwidth Katakana", NULL, MADB_CS_UNSUPPORTED}, - {"50222", "ISO 2022 Japanese JIS X 0201-1989", NULL, MADB_CS_UNSUPPORTED}, - {"50225", "ISO 2022 Korean", NULL, MADB_CS_UNSUPPORTED}, - {"50227", "ISO 2022 Simplified Chinese", NULL, MADB_CS_UNSUPPORTED}, - {"50229", "ISO 2022 Traditional Chinese", NULL, MADB_CS_UNSUPPORTED}, - {"50930", "EBCDIC Japanese (Katakana) Extended", NULL, MADB_CS_UNSUPPORTED}, - {"50931", "EBCDIC US-Canada and Japanese", NULL, MADB_CS_UNSUPPORTED}, - {"50933", "EBCDIC Korean Extended and Korean", NULL, MADB_CS_UNSUPPORTED}, - {"50935", "EBCDIC Simplified Chinese Extended and Simplified Chinese", NULL, MADB_CS_UNSUPPORTED}, - {"50936", "EBCDIC Simplified Chinese", NULL, MADB_CS_UNSUPPORTED}, - {"50937", "EBCDIC US-Canada and Traditional Chinese", NULL, MADB_CS_UNSUPPORTED}, - {"50939", "EBCDIC Japanese (Latin) Extended and Japanese", NULL, MADB_CS_UNSUPPORTED}, - {"51932", "EUC Japanese", "ujis", MADB_CS_EXACT}, - {"51936", "EUC Simplified Chinese; Chinese Simplified (EUC)", "gb2312", MADB_CS_EXACT}, - {"51949", "EUC Korean", "euckr", MADB_CS_EXACT}, - {"51950", "EUC Traditional Chinese", "big5", MADB_CS_EXACT}, - {"52936", "Chinese Simplified (HZ)", NULL, MADB_CS_UNSUPPORTED}, - {"54936", "Chinese Simplified (GB18030)", NULL, MADB_CS_UNSUPPORTED}, - {"57002", "ISCII Devanagari", NULL, MADB_CS_UNSUPPORTED}, - {"57003", "ISCII Bengali", NULL, MADB_CS_UNSUPPORTED}, - {"57004", "ISCII Tamil", NULL, MADB_CS_UNSUPPORTED}, - {"57005", "ISCII Telugu", NULL, MADB_CS_UNSUPPORTED}, - {"57006", "ISCII Assamese", NULL, MADB_CS_UNSUPPORTED}, - {"57007", "ISCII Oriya", NULL, MADB_CS_UNSUPPORTED}, - {"57008", "ISCII Kannada", NULL, MADB_CS_UNSUPPORTED}, - {"57009", "ISCII Malayalam", NULL, MADB_CS_UNSUPPORTED}, - {"57010", "ISCII Gujarati", NULL, MADB_CS_UNSUPPORTED}, - {"57011", "ISCII Punjabi", NULL, MADB_CS_UNSUPPORTED}, - {"65000", "utf-7 Unicode (UTF-7)", NULL, MADB_CS_UNSUPPORTED}, - {"65001", "utf-8 Unicode (UTF-8)", "utf8", MADB_CS_EXACT}, + {"037", "IBM EBCDIC US-Canada", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"437", "OEM United States", "cp850", NULL, MADB_CS_APPROX}, + {"500", "IBM EBCDIC International", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"708", "Arabic (ASMO 708)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"709", "Arabic (ASMO-449+, BCON V4)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"710", "Transparent Arabic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"720", "Arabic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"737", "Greek (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"775", "Baltic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"850", "Western European (DOS)", "cp850", NULL, MADB_CS_EXACT}, + {"852", "Central European (DOS)", "cp852", NULL, MADB_CS_EXACT}, + {"855", "Cyrillic (primarily Russian)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"857", "Turkish (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"858", "OEM Multilingual Latin 1 + Euro symbol", "cp850", NULL, MADB_CS_EXACT}, + {"860", "Portuguese (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"861", "Icelandic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"862", "Hebrew (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"863", "French Canadian (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"864", "Arabic (864)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"865", "Nordic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"866", "Cyrillic (DOS)", "cp866", NULL, MADB_CS_EXACT}, + {"869", "Greek, Modern (DOS)", "greek", NULL, MADB_CS_EXACT}, + {"870", "IBM EBCDIC Multilingual Latin 2", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"874", "Thai (Windows)", "tis620", NULL, MADB_CS_UNSUPPORTED}, + {"875", "Greek Modern", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"932", "Japanese (Shift-JIS)", "cp932", NULL, MADB_CS_EXACT}, + {"936", "Chinese Simplified (GB2312)", "gbk", NULL, MADB_CS_EXACT}, + {"949", "ANSI/OEM Korean (Unified Hangul Code)", "euckr", NULL, MADB_CS_EXACT}, + {"950", "Chinese Traditional (Big5)", "big5", NULL, MADB_CS_EXACT}, + {"1026", "EBCDIC Turkish (Latin 5)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1047", "EBCDIC Latin 1/Open System", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1140", "IBM EBCDIC (US-Canada-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1141", "IBM EBCDIC (Germany-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1142", "IBM EBCDIC (Denmark-Norway-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1143", "IBM EBCDIC (Finland-Sweden-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1144", "IBM EBCDIC (Italy-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1145", "IBM EBCDIC (Spain-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1146", "IBM EBCDIC (UK-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1147", "IBM EBCDIC (France-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1148", "IBM EBCDIC (International-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1149", "IBM EBCDIC (Icelandic-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1200", "UTF-16, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1201", "UTF-16, big endian byte order", "utf16", NULL, MADB_CS_UNSUPPORTED}, + {"1250", "Central European (Windows)", "cp1250", NULL, MADB_CS_EXACT}, + {"1251", "Cyrillic (Windows)", "cp1251", NULL, MADB_CS_EXACT}, + {"1252", "Western European (Windows)", "latin1", NULL, MADB_CS_EXACT}, + {"1253", "Greek (Windows)", "greek", NULL, MADB_CS_EXACT}, + {"1254", "Turkish (Windows)", "latin5", NULL, MADB_CS_EXACT}, + {"1255", "Hebrew (Windows)", "hewbrew", NULL, MADB_CS_EXACT}, + {"1256", "Arabic (Windows)", "cp1256", NULL, MADB_CS_EXACT}, + {"1257", "Baltic (Windows)","cp1257", NULL, MADB_CS_EXACT}, + {"1258", "Vietnamese (Windows)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"1361", "Korean (Johab)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10000", "Western European (Mac)", "macroman", NULL, MADB_CS_EXACT}, + {"10001", "Japanese (Mac)", "sjis", NULL, MADB_CS_EXACT}, + {"10002", "Chinese Traditional (Mac)", "big5", NULL, MADB_CS_EXACT}, + {"10003", "Korean (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10004", "Arabic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10005", "Hebrew (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10006", "Greek (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10007", "Cyrillic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10008", "Chinese Simplified (Mac)", "gb2312", NULL, MADB_CS_EXACT}, + {"10010", "Romanian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10017", "Ukrainian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10021", "Thai (Mac)", "tis620", NULL, MADB_CS_EXACT}, + {"10029", "Central European (Mac)", "macce", NULL, MADB_CS_EXACT}, + {"10079", "Icelandic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10081", "Turkish (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"10082", "Croatian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"12000", "Unicode UTF-32, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"12001", "Unicode UTF-32, big endian byte order", "utf32", NULL, MADB_CS_UNSUPPORTED}, + {"20000", "Chinese Traditional (CNS)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20001", "TCA Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20002", "Chinese Traditional (Eten)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20003", "IBM5550 Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20004", "TeleText Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20005", "Wang Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20105", "Western European (IA5)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20106", "IA5 German (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20107", "Swedish (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20108", "Norwegian (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20127", "US-ASCII (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20261", "T.61", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20269", "Non-Spacing Accent", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20273", "EBCDIC Germany", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20277", "EBCDIC Denmark-Norway", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20278", "EBCDIC Finland-Sweden", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20280", "EBCDIC Italy", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20284", "EBCDIC Latin America-Spain", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20285", "EBCDIC United Kingdom", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20290", "EBCDIC Japanese Katakana Extended", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20297", "EBCDIC France", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20420", "EBCDIC Arabic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20423", "EBCDIC Greek", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20424", "EBCDIC Hebrew", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20833", "EBCDIC Korean Extended", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20838", "EBCDIC Thai", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20866", "Cyrillic (KOI8-R)", "koi8r", NULL, MADB_CS_EXACT}, + {"20871", "EBCDIC Icelandic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20880", "EBCDIC Cyrillic Russian", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20905", "EBCDIC Turkish", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20924", "EBCDIC Latin 1/Open System (1047 + Euro symbol)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"20932", "Japanese (JIS 0208-1990 and 0121-1990)", "ujis", NULL, MADB_CS_EXACT}, + {"20936", "Chinese Simplified (GB2312-80)", "gb2312", NULL, MADB_CS_APPROX}, + {"20949", "Korean Wansung", "euckr", NULL, MADB_CS_APPROX}, + {"21025", "EBCDIC Cyrillic Serbian-Bulgarian", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"21866", "Cyrillic (KOI8-U)", "koi8u", NULL, MADB_CS_EXACT}, + {"28591", "Western European (ISO)", "latin1", NULL, MADB_CS_APPROX}, + {"28592", "Central European (ISO)", "latin2", NULL, MADB_CS_EXACT}, + {"28593", "Latin 3", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"28594", "Baltic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"28595", "ISO 8859-5 Cyrillic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"28596", "ISO 8859-6 Arabic", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"28597", "ISO 8859-7 Greek", "greek", NULL, MADB_CS_EXACT}, + {"28598", "Hebrew (ISO-Visual)", "hebrew", NULL, MADB_CS_EXACT}, + {"28599", "ISO 8859-9 Turkish", "latin5", NULL, MADB_CS_EXACT}, + {"28603", "ISO 8859-13 Estonian", "latin7", NULL, MADB_CS_EXACT}, + {"28605", "8859-15 Latin 9", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"29001", "Europa 3", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"38598", "ISO 8859-8 Hebrew; Hebrew (ISO-Logical)", "hebrew", NULL, MADB_CS_EXACT}, + {"50220", "ISO 2022 Japanese with no halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50221", "ISO 2022 Japanese with halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50222", "ISO 2022 Japanese JIS X 0201-1989", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50225", "ISO 2022 Korean", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50227", "ISO 2022 Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50229", "ISO 2022 Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50930", "EBCDIC Japanese (Katakana) Extended", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50931", "EBCDIC US-Canada and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50933", "EBCDIC Korean Extended and Korean", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50935", "EBCDIC Simplified Chinese Extended and Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50936", "EBCDIC Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50937", "EBCDIC US-Canada and Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"50939", "EBCDIC Japanese (Latin) Extended and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"51932", "EUC Japanese", "ujis", NULL, MADB_CS_EXACT}, + {"51936", "EUC Simplified Chinese; Chinese Simplified (EUC)", "gb2312", NULL, MADB_CS_EXACT}, + {"51949", "EUC Korean", "euckr", NULL, MADB_CS_EXACT}, + {"51950", "EUC Traditional Chinese", "big5", NULL, MADB_CS_EXACT}, + {"52936", "Chinese Simplified (HZ)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"54936", "Chinese Simplified (GB18030)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57002", "ISCII Devanagari", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57003", "ISCII Bengali", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57004", "ISCII Tamil", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57005", "ISCII Telugu", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57006", "ISCII Assamese", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57007", "ISCII Oriya", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57008", "ISCII Kannada", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57009", "ISCII Malayalam", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57010", "ISCII Gujarati", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"57011", "ISCII Punjabi", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"65000", "utf-7 Unicode (UTF-7)", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"65001", "utf-8 Unicode (UTF-8)", "utf8", NULL, MADB_CS_EXACT}, /* non Windows */ #else - {"ASCII", "US-ASCII", "ascii", MADB_CS_APPROX}, - {"US-ASCII", "US-ASCII", "ascii", MADB_CS_APPROX}, - {"Big5", "Chinese for Taiwan Multi-byte set", "big5", MADB_CS_EXACT}, - {"CP866", "IBM 866", "cp866", MADB_CS_EXACT}, - {"IBM-1252", "Catalan Spain", "cp1252", MADB_CS_EXACT}, - {"ISCII-DEV", "Hindi", NULL, MADB_CS_UNSUPPORTED}, - {"ISO-8859-1", "ISO-8859-1", "latin1", MADB_CS_APPROX}, - {"ISO8859-1", "ISO-8859-1", "latin1", MADB_CS_APPROX}, - {"ISO_8859-1", "ISO-8859-1", "latin1", MADB_CS_APPROX}, - {"ISO88591", "ISO-8859-1", "latin1", MADB_CS_APPROX}, - {"ISO-8859-13", "ISO-8859-13", "latin7", MADB_CS_EXACT}, - {"ISO8859-13", "ISO-8859-13", "latin7", MADB_CS_EXACT}, - {"ISO_8859-13", "ISO-8859-13", "latin7", MADB_CS_EXACT}, - {"ISO885913", "ISO-8859-13", "latin7", MADB_CS_EXACT}, - {"ISO-8859-15", "ISO-8859-15", "latin9", MADB_CS_UNSUPPORTED}, - {"ISO8859-15", "ISO-8859-15", "latin9", MADB_CS_UNSUPPORTED}, - {"ISO_8859-15", "ISO-8859-15", "latin9", MADB_CS_UNSUPPORTED}, - {"ISO885915", "ISO-8859-15", "latin9", MADB_CS_UNSUPPORTED}, - {"ISO-8859-2", "ISO-8859-2", "latin2", MADB_CS_EXACT}, - {"ISO8859-2", "ISO-8859-2", "latin2", MADB_CS_EXACT}, - {"ISO_8859-2", "ISO-8859-2", "latin2", MADB_CS_EXACT}, - {"ISO88592", "ISO-8859-2", "latin2", MADB_CS_EXACT}, - {"ISO-8859-7", "ISO-8859-7", "greek", MADB_CS_EXACT}, - {"ISO8859-7", "ISO-8859-7", "greek", MADB_CS_EXACT}, - {"ISO_8859-7", "ISO-8859-7", "greek", MADB_CS_EXACT}, - {"ISO88597", "ISO-8859-7", "greek", MADB_CS_EXACT}, - {"ISO-8859-8", "ISO-8859-8", "hebrew", MADB_CS_EXACT}, - {"ISO8859-8", "ISO-8859-8", "hebrew", MADB_CS_EXACT}, - {"ISO_8859-8", "ISO-8859-8", "hebrew", MADB_CS_EXACT}, - {"ISO88598", "ISO-8859-8", "hebrew", MADB_CS_EXACT}, - {"ISO-8859-9", "ISO-8859-9", "latin5", MADB_CS_EXACT}, - {"ISO8859-9", "ISO-8859-9", "latin5", MADB_CS_EXACT}, - {"ISO_8859-9", "ISO-8859-9", "latin5", MADB_CS_EXACT}, - {"ISO88599", "ISO-8859-9", "latin5", MADB_CS_EXACT}, - {"ISO-8859-4", "ISO-8859-4", NULL, MADB_CS_UNSUPPORTED}, - {"ISO8859-4", "ISO-8859-4", NULL, MADB_CS_UNSUPPORTED}, - {"ISO_8859-4", "ISO-8859-4", NULL, MADB_CS_UNSUPPORTED}, - {"ISO88594", "ISO-8859-4", NULL, MADB_CS_UNSUPPORTED}, - {"ISO-8859-5", "ISO-8859-5", NULL, MADB_CS_UNSUPPORTED}, - {"ISO8859-5", "ISO-8859-5", NULL, MADB_CS_UNSUPPORTED}, - {"ISO_8859-5", "ISO-8859-5", NULL, MADB_CS_UNSUPPORTED}, - {"ISO88595", "ISO-8859-5", NULL, MADB_CS_UNSUPPORTED}, - {"KOI8-R", "KOI8-R", "koi8r", MADB_CS_EXACT}, - {"koi8r", "KOI8-R", "koi8r", MADB_CS_EXACT}, - {"KOI8-U", "KOI8-U", "koi8u", MADB_CS_EXACT}, - {"koi8u", "KOI8-U", "koi8u", MADB_CS_EXACT}, - {"koi8t", "KOI8-T", NULL, MADB_CS_UNSUPPORTED}, - {"KOI8-T", "KOI8-T", NULL, MADB_CS_UNSUPPORTED}, - {"SJIS", "SHIFT_JIS", "sjis", MADB_CS_EXACT}, - {"Shift-JIS", "SHIFT_JIS", "sjis", MADB_CS_EXACT}, - {"ansi1251", "Cyrillic", "cp1251", MADB_CS_EXACT}, - {"cp1251", "Cyrillic", "cp1251", MADB_CS_EXACT}, - {"armscii8", "Armenian", "armscii8", MADB_CS_EXACT}, - {"armscii-8", "Armenian", "armscii8", MADB_CS_EXACT}, - {"big5hkscs", "Big5-HKSCS", NULL, MADB_CS_UNSUPPORTED}, - {"cp1255", "Hebrew", "cp1255", MADB_CS_EXACT}, - {"eucCN", "GB-2312", "gb2312", MADB_CS_EXACT}, - {"eucJP", "UJIS", "ujis", MADB_CS_EXACT}, - {"eucKR", "EUC-KR", "euckr", MADB_CS_EXACT}, - {"euctw", "EUC-TW", NULL, MADB_CS_UNSUPPORTED}, - {"gb18030", "GB 18030-2000", "gb18030", MADB_CS_UNSUPPORTED}, - {"gb2312", "GB2312", "gb2312", MADB_CS_EXACT}, - {"gbk", "GBK", "gbk", MADB_CS_EXACT}, - {"georgianps", "Georgian", "geostd8", MADB_CS_EXACT}, - {"utf8", "UTF8", "utf8", MADB_CS_EXACT}, - {"utf-8", "UTF8", "utf8", MADB_CS_EXACT}, + /* iconv encodings */ + {"ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX}, + {"US-ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX}, + {"Big5", "Chinese for Taiwan Multi-byte set", "big5", "BIG5", MADB_CS_EXACT}, + {"CP866", "IBM 866", "cp866", "CP866", MADB_CS_EXACT}, + {"IBM-1252", "Catalan Spain", "cp1252", "CP1252", MADB_CS_EXACT}, + {"ISCII-DEV", "Hindi", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"ISO-8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX}, + {"ISO8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX}, + {"ISO_8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX}, + {"ISO88591", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX}, + {"ISO-8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT}, + {"ISO8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT}, + {"ISO_8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT}, + {"ISO885913", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT}, + {"ISO-8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED}, + {"ISO8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED}, + {"ISO_8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED}, + {"ISO885915", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED}, + {"ISO-8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT}, + {"ISO8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT}, + {"ISO_8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT}, + {"ISO88592", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT}, + {"ISO-8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT}, + {"ISO8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT}, + {"ISO_8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT}, + {"ISO88597", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT}, + {"ISO-8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT}, + {"ISO8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT}, + {"ISO_8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT}, + {"ISO88598", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT}, + {"ISO-8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT}, + {"ISO8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT}, + {"ISO_8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT}, + {"ISO88599", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT}, + {"ISO-8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED}, + {"ISO8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED}, + {"ISO_8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED}, + {"ISO88594", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED}, + {"ISO-8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED}, + {"ISO8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED}, + {"ISO_8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED}, + {"ISO88595", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED}, + {"KOI8-R", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT}, + {"koi8r", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT}, + {"KOI8-U", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT}, + {"koi8u", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT}, + {"koi8t", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED}, + {"KOI8-T", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED}, + {"SJIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT}, + {"Shift-JIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT}, + {"ansi1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT}, + {"cp1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT}, + {"armscii8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT}, + {"armscii-8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT}, + {"big5hkscs", "Big5-HKSCS", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"cp1255", "Hebrew", "cp1255", "CP1255", MADB_CS_EXACT}, + {"eucCN", "GB-2312", "gb2312", "GB2312", MADB_CS_EXACT}, + {"eucJP", "UJIS", "ujis", "UJIS", MADB_CS_EXACT}, + {"eucKR", "EUC-KR", "euckr", "EUCKR", MADB_CS_EXACT}, + {"euctw", "EUC-TW", NULL, NULL, MADB_CS_UNSUPPORTED}, + {"gb18030", "GB 18030-2000", "gb18030", "GB18030", MADB_CS_UNSUPPORTED}, + {"gb2312", "GB2312", "gb2312", "GB2312", MADB_CS_EXACT}, + {"gbk", "GBK", "gbk", "GBK", MADB_CS_EXACT}, + {"georgianps", "Georgian", "geostd8", "GEORGIAN-PS", MADB_CS_EXACT}, + {"utf8", "UTF8", "utf8", "UTF8", MADB_CS_EXACT}, + {"utf-8", "UTF8", "utf8", "UTF8", MADB_CS_EXACT}, #endif - {NULL, NULL, NULL, 0} + {NULL, NULL, NULL, NULL, 0} }; /* }}} */ @@ -1113,3 +1117,29 @@ int madb_get_windows_cp(const char *charset) #endif /* }}} */ +size_t convert_string(const char *from, size_t *from_len, CHARSET_INFO *from_cs, + char *to, size_t *to_len, CHARSET_INFO *to_cs) +{ + iconv_t conv= 0; + size_t rc= -1; + size_t save_len= *to_len; + char to_encoding[128]; + /* check if conversion is supported */ + if (!from_cs || !from_cs->encoding || !from_cs->encoding[0] || + !to_cs || !to_cs->encoding || !to_cs->encoding[0]) + goto error; + + snprintf(to_encoding, 128, "%s//TRANSLIT", to_cs->encoding); + + if ((conv= iconv_open(to_encoding, from_cs->encoding)) == (iconv_t)-1) + goto error; + + if ((rc= iconv(conv, (char **)&from, from_len, &to, to_len)) == -1) + goto error; + rc= save_len - *to_len; +error: + if (conv != (iconv_t)-1) + iconv_close(conv); + return rc; +} + diff --git a/libmariadb/net.c b/libmariadb/net.c index fb0871a0..b0d732a8 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -416,7 +416,7 @@ net_real_write(NET *net,const char *packet,size_t len) my_progname,vio_errno(net->vio)); #endif /* EXTRA_DEBUG */ net->error=2; /* Close socket */ - net->last_errno= (interrupted ? + net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED : ER_NET_ERROR_ON_WRITE); goto end; } diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index b26c1823..6d3587ea 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -19,9 +19,10 @@ ENABLE_TESTING() INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/unittest/mytap) +ADD_DEFINITIONS(-DLIBMARIADB) SET(API_TESTS "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" - "sp" "result" "connection" "misc" "ssl" "ps_new" "sqlite3" "thread") + "sp" "result" "connection" "misc" "ssl" "ps_new" "sqlite3" "thread" "cs_conv") FOREACH(API_TEST ${API_TESTS}) ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) diff --git a/unittest/libmariadb/cs_conv.c b/unittest/libmariadb/cs_conv.c new file mode 100644 index 00000000..4e99d833 --- /dev/null +++ b/unittest/libmariadb/cs_conv.c @@ -0,0 +1,128 @@ +/* +Copyright (c) 2013 Monty Program AB. All rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "my_test.h" +#include + + +typedef struct { + char *cs_from; + char *cs_to; + char *hex_source; + char *source; + size_t len; +} CS_CONV; + + + +static int test_cs_conversion(MYSQL *mysql) +{ + CS_CONV cs_conv[]= { {"latin1", "utf8", NULL, "Günter André",0}, + {"koi8r", "utf8", NULL, "×ÁÓÑ", 0}, + {"koi8r", "utf8", NULL, "÷áóñ", 0}, + {"koi8r", "utf8", NULL, "É", 0}, + {"koi8r", "utf8", NULL, "Ê", 0}, + {"ucs2", "utf8", "0x0041040B", "\x00\x41\x04\x0B", 4}, + {"ucs2", "utf8", "0x039C03C903B4", "\x03\x9C\x03\xC9\x03\xB4",6}, + {"ucs2", "utf8", "0x039C03C903B403B11F770308", "\x03\x9C\x03\xC9\x03\xB4\x03\xB1\x1F\x77\x03\x08", 0}, + {"ucs2", "utf8", "0x0030", "\x00\x30", 2}, + {"ucs2", "utf8", NULL, "£Ã£±", 0}, + {"ucs2", "utf8", NULL, "£Ã£±", 0}, + {"ucs2", "utf8", NULL, "£Ã£±", 0}, + {"ucs2", "sjis", NULL, "£Ã£±", 0}, + {"ucs2", "ujis", NULL, "£Ã£±", 0}, + {"eucjpms", "ujis", NULL, "C1", 0}, + {"latin1", "sjis", NULL, "C1", 0}, + {"utf8", "latin1", NULL, "C1", 0}, + /* C8 pane not supported + {"big5", "utf8", "0xC84041", "\xC8\x40\x41", 0}, */ + {"latin1", "utf8", "0xFF8F", "\xFF\x8F", 0}, + {NULL, NULL, NULL} + }; + int i= 0, rc; + + setlocale(LC_ALL, "en_GB"); + + while (cs_conv[i].cs_from) + { + char query[1024]; + size_t from_len, to_len= 1024; + CHARSET_INFO *cs_from, *cs_to; + MYSQL_RES *res; + MYSQL_ROW row; + char str_converted[1024]; + char str_expected[1024]; + memset(str_converted, 0, 1024); + memset(str_expected, 0, 1024); + + FAIL_IF(!(cs_from= get_charset_by_name(cs_conv[i].cs_from)), "invalid character set"); + FAIL_IF(!(cs_to= get_charset_by_name(cs_conv[i].cs_to)), "invalid character set"); + + + snprintf(query, 1024, "SET NAMES %s", cs_conv[i].cs_to); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + snprintf(query, 1024, "SELECT CONVERT(_%s %s%s%s using %s)", + cs_conv[i].cs_from, + cs_conv[i].hex_source ? "" : "\"", + cs_conv[i].hex_source ? cs_conv[i].hex_source : cs_conv[i].source, + cs_conv[i].hex_source ? "" : "\"", + cs_conv[i].cs_to); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(!res, "expected result set"); + + FAIL_IF(!(row= mysql_fetch_row(res)), "fetching row failed"); + strcpy(str_converted, row[0]); + mysql_free_result(res); + + from_len= cs_conv[i].len ? cs_conv[i].len : strlen(cs_conv[i].source); + FAIL_IF(convert_string(cs_conv[i].source, &from_len, cs_from, + str_expected, &to_len, cs_to) < 1, "conversion error occured"); + + if (strcmp(str_converted, str_expected)) + { + diag("Error converting from %s to %s\ndb:'%s' library: '%s'", + cs_from->csname, cs_to->csname, str_converted, str_expected); + return FAIL; + } + i++; + } + + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_cs_conversion", test_cs_conversion, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, 0} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/win-iconv/iconv.h b/win-iconv/iconv.h new file mode 100644 index 00000000..7457a3fd --- /dev/null +++ b/win-iconv/iconv.h @@ -0,0 +1,14 @@ +#ifndef _LIBICONV_H +#define _LIBICONV_H +#include +#ifdef __cplusplus +extern "C" { +#endif +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); +#ifdef __cplusplus +} +#endif +#endif//_LIBICONV_H \ No newline at end of file diff --git a/win-iconv/mlang.h b/win-iconv/mlang.h new file mode 100644 index 00000000..525f484e --- /dev/null +++ b/win-iconv/mlang.h @@ -0,0 +1,54 @@ +HRESULT WINAPI ConvertINetString( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + DWORD dwDstEncoding, + LPCSTR lpSrcStr, + LPINT lpnSrcSize, + LPBYTE lpDstStr, + LPINT lpnDstSize +); + +HRESULT WINAPI ConvertINetMultiByteToUnicode( + LPDWORD lpdwMode, + DWORD dwSrcEncoding, + LPCSTR lpSrcStr, + LPINT lpnMultiCharCount, + LPWSTR lpDstStr, + LPINT lpnWideCharCount +); + +HRESULT WINAPI ConvertINetUnicodeToMultiByte( + LPDWORD lpdwMode, + DWORD dwEncoding, + LPCWSTR lpSrcStr, + LPINT lpnWideCharCount, + LPSTR lpDstStr, + LPINT lpnMultiCharCount +); + +HRESULT WINAPI IsConvertINetStringAvailable( + DWORD dwSrcEncoding, + DWORD dwDstEncoding +); + +HRESULT WINAPI LcidToRfc1766A( + LCID Locale, + LPSTR pszRfc1766, + int nChar +); + +HRESULT WINAPI LcidToRfc1766W( + LCID Locale, + LPWSTR pszRfc1766, + int nChar +); + +HRESULT WINAPI Rfc1766ToLcidA( + LCID *pLocale, + LPSTR pszRfc1766 +); + +HRESULT WINAPI Rfc1766ToLcidW( + LCID *pLocale, + LPWSTR pszRfc1766 +); diff --git a/win-iconv/win_iconv.c b/win-iconv/win_iconv.c new file mode 100644 index 00000000..ad5f32d7 --- /dev/null +++ b/win-iconv/win_iconv.c @@ -0,0 +1,2051 @@ +/* + * iconv implementation using Win32 API to convert. + * + * This file is placed in the public domain. + */ + +/* for WC_NO_BEST_FIT_CHARS */ +#ifndef WINVER +# define WINVER 0x0500 +#endif + +#define STRICT +#include +#include +#include +#include + +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +/* WORKAROUND: */ +#ifndef UNDER_CE +#define GetProcAddressA GetProcAddress +#endif + +#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 */ + +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(void); +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, "UCS2LE"}, + {1200, "UCS-2LE"}, + + {1201, "CP1201"}, + {1201, "UTF16BE"}, + {1201, "UTF-16BE"}, + {1201, "UCS2BE"}, + {1201, "UCS-2BE"}, + {1201, "unicodeFFFE"}, + + {12000, "CP12000"}, + {12000, "UTF32LE"}, + {12000, "UTF-32LE"}, + {12000, "UCS4LE"}, + {12000, "UCS-4LE"}, + + {12001, "CP12001"}, + {12001, "UTF32BE"}, + {12001, "UTF-32BE"}, + {12001, "UCS4BE"}, + {12001, "UCS-4BE"}, + +#ifndef GLIB_COMPILATION + /* + * Default is big endian. + * See rfc2781 4.3 Interpreting text labelled as UTF-16. + */ + {1201, "UTF16"}, + {1201, "UTF-16"}, + {1201, "UCS2"}, + {1201, "UCS-2"}, + {12001, "UTF32"}, + {12001, "UTF-32"}, + {12001, "UCS-4"}, + {12001, "UCS4"}, +#else + /* Default is little endian, because the platform is */ + {1200, "UTF16"}, + {1200, "UTF-16"}, + {1200, "UCS2"}, + {1200, "UCS-2"}, + {12000, "UTF32"}, + {12000, "UTF-32"}, + {12000, "UCS4"}, + {12000, "UCS-4"}, +#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"}, + {950, "BIG5HKSCS"}, + {950, "BIG5-HKSCS"}, + + {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) */ + {950, "big5hkscs"}, /* ANSI/OEM Traditional Chinese (Hong Kong SAR); Chinese Traditional (Big5-HKSCS) */ + {950, "big5-hkscs"}, /* alternative name for it */ + {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(void) +{ + HMODULE h; + if (ConvertINetString != NULL) + return TRUE; + h = LoadLibrary(TEXT("mlang.dll")); + if (!h) + return FALSE; + ConvertINetString = (CONVERTINETSTRING)GetProcAddressA(h, "ConvertINetString"); + ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddressA(h, "ConvertINetMultiByteToUnicode"); + ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddressA(h, "ConvertINetUnicodeToMultiByte"); + IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddressA(h, "IsConvertINetStringAvailable"); + LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddressA(h, "LcidToRfc1766A"); + Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddressA(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 UNUSED) +{ + 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) + { + if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG) + { + outsize = 0; + } + else + { + 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) + { + if (cd->to.flags & FLAG_IGNORE) + { + cd->from.mode = frommode; + insize = 1; + wsize = 0; + } + else + { + 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) + { + if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG) + { + cd->to.mode = tomode; + outsize = 0; + } + else + { + 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) +{ + CPINFO cpinfo; + 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 || + _stricmp(name, "UCS-2") == 0 || _stricmp(name, "UCS2") == 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 || + _stricmp(name, "UCS-4") == 0 || _stricmp(name, "UCS4") == 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) + && GetCPInfo(cv->codepage, &cpinfo) != 0) + { + cv->mbtowc = kernel_mbtowc; + cv->wctomb = kernel_wctomb; + if (cpinfo.MaxCharSize == 1) + cv->mblen = sbcs_mblen; + else if (cpinfo.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 = LoadLibraryA(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)GetProcAddressA(hlibiconv, "libiconv_open"); + if (_iconv_open == NULL) + _iconv_open = (f_iconv_open)GetProcAddressA(hlibiconv, "iconv_open"); + cd->iconv_close = (f_iconv_close)GetProcAddressA(hlibiconv, "libiconv_close"); + if (cd->iconv_close == NULL) + cd->iconv_close = (f_iconv_close)GetProcAddressA(hlibiconv, "iconv_close"); + cd->iconv = (f_iconv)GetProcAddressA(hlibiconv, "libiconv"); + if (cd->iconv == NULL) + cd->iconv = (f_iconv)GetProcAddressA(hlibiconv, "iconv"); + cd->_errno = (f_errno)GetProcAddressA(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_PTR Base; + ULONG Size; + PIMAGE_IMPORT_DESCRIPTOR Imp; + PIMAGE_THUNK_DATA Name; /* Import Name Table */ + PIMAGE_IMPORT_BY_NAME ImpName; + + Base = (DWORD_PTR)hModule; + Imp = (PIMAGE_IMPORT_DESCRIPTOR)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_PTR)Name->u1.AddressOfData); + if (strcmp((char *)ImpName->Name, funcname) == 0) + return GetModuleHandleA((char *)(Base + Imp->Name)); + } + } + } + return NULL; +} +#endif + +static int +sbcs_mblen(csconv_t *cv UNUSED, const uchar *buf UNUSED, int bufsize UNUSED) +{ + 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 UNUSED, 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 UNUSED, 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 && !(cv->flags & FLAG_TRANSLIT)) + return seterror(EILSEQ); + else if (cv->mblen(cv, buf, len) != len) /* validate result */ + return seterror(EILSEQ); + return len; +} + +/* + * It seems that the mode (cv->mode) is fixnum. + * For example, when converting iso-2022-jp(cp50221) to unicode: + * in ascii sequence: mode=0xC42C0000 + * in jisx0208 sequence: mode=0xC42C0001 + * "C42C" is same for each convert session. + * It should be: ((codepage-1)<<16)|state + */ +static int +mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int len; + int insize; + HRESULT hr; + + len = cv->mblen(cv, buf, bufsize); + if (len == -1) + return -1; + insize = len; + hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, + (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len) + return seterror(EILSEQ); + return len; +} + +static int +mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + + hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) + return seterror(EILSEQ); + memcpy(buf, tmpbuf, tmpsize); + return tmpsize; +} + +static int +utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + + /* swap endian: 1200 <-> 1201 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 2) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[0] = (buf[1] << 8) | buf[0]; + else if (codepage == 1201) /* big endian */ + wbuf[0] = (buf[0] << 8) | buf[1]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wbuf[0] == 0xFFFE) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 2; + } + else if (wbuf[0] == 0xFEFF) + { + *wbufsize = 0; + return 2; + } + } + + if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) + return seterror(EILSEQ); + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 1200) /* little endian */ + wbuf[1] = (buf[3] << 8) | buf[2]; + else if (codepage == 1201) /* big endian */ + wbuf[1] = (buf[2] << 8) | buf[3]; + if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) + return seterror(EILSEQ); + *wbufsize = 2; + return 4; + } + *wbufsize = 1; + return 2; +} + +static int +utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + memcpy(buf, "\xFF\xFE", 2); + else if (cv->codepage == 1201) /* big endian */ + memcpy(buf, "\xFE\xFF", 2); + + r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); + if (r == -1) + return -1; + return r + 2; + } + + if (bufsize < 2) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[0] = (wbuf[0] & 0x00FF); + buf[1] = (wbuf[0] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[0] = (wbuf[0] & 0xFF00) >> 8; + buf[1] = (wbuf[0] & 0x00FF); + } + if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) + { + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 1200) /* little endian */ + { + buf[2] = (wbuf[1] & 0x00FF); + buf[3] = (wbuf[1] & 0xFF00) >> 8; + } + else if (cv->codepage == 1201) /* big endian */ + { + buf[2] = (wbuf[1] & 0xFF00) >> 8; + buf[3] = (wbuf[1] & 0x00FF); + } + return 4; + } + return 2; +} + +static int +utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + int codepage = cv->codepage; + uint wc; + + /* swap endian: 12000 <-> 12001 */ + if (cv->mode & UNICODE_MODE_SWAPPED) + codepage ^= 1; + + if (bufsize < 4) + return seterror(EINVAL); + if (codepage == 12000) /* little endian */ + wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + else if (codepage == 12001) /* big endian */ + wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + cv->mode |= UNICODE_MODE_BOM_DONE; + if (wc == 0xFFFE0000) + { + cv->mode |= UNICODE_MODE_SWAPPED; + *wbufsize = 0; + return 4; + } + else if (wc == 0x0000FEFF) + { + *wbufsize = 0; + return 4; + } + } + + if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) + return seterror(EILSEQ); + ucs4_to_utf16(wc, wbuf, wbufsize); + return 4; +} + +static int +utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + uint wc; + + if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) + { + int r; + + cv->mode |= UNICODE_MODE_BOM_DONE; + if (bufsize < 4) + return seterror(E2BIG); + if (cv->codepage == 12000) /* little endian */ + memcpy(buf, "\xFF\xFE\x00\x00", 4); + else if (cv->codepage == 12001) /* big endian */ + memcpy(buf, "\x00\x00\xFE\xFF", 4); + + r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); + if (r == -1) + return -1; + return r + 4; + } + + if (bufsize < 4) + return seterror(E2BIG); + wc = utf16_to_ucs4(wbuf); + if (cv->codepage == 12000) /* little endian */ + { + buf[0] = wc & 0x000000FF; + buf[1] = (wc & 0x0000FF00) >> 8; + buf[2] = (wc & 0x00FF0000) >> 16; + buf[3] = (wc & 0xFF000000) >> 24; + } + else if (cv->codepage == 12001) /* big endian */ + { + buf[0] = (wc & 0xFF000000) >> 24; + buf[1] = (wc & 0x00FF0000) >> 16; + buf[2] = (wc & 0x0000FF00) >> 8; + buf[3] = wc & 0x000000FF; + } + return 4; +} + +/* + * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) + * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow + * 1 byte Kana) + * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte + * Kana - SO/SI) + * + * MultiByteToWideChar() and WideCharToMultiByte() behave differently + * depending on Windows version. On XP, WideCharToMultiByte() doesn't + * terminate result sequence with ascii escape. But Vista does. + * Use MLang instead. + */ + +#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) +#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) +#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) + +#define ISO2022_SI 0 +#define ISO2022_SO 1 + +/* shift in */ +static const char iso2022_SI_seq[] = "\x0F"; +/* shift out */ +static const char iso2022_SO_seq[] = "\x0E"; + +typedef struct iso2022_esc_t iso2022_esc_t; +struct iso2022_esc_t { + const char *esc; + int esc_len; + int len; + int cs; +}; + +#define ISO2022JP_CS_ASCII 0 +#define ISO2022JP_CS_JISX0201_ROMAN 1 +#define ISO2022JP_CS_JISX0201_KANA 2 +#define ISO2022JP_CS_JISX0208_1978 3 +#define ISO2022JP_CS_JISX0208_1983 4 +#define ISO2022JP_CS_JISX0212 5 + +static iso2022_esc_t iso2022jp_esc[] = { + {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, + {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, + {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, + {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ + {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, + {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, + {NULL, 0, 0, 0} +}; + +static int +iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int insize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len; + int cs; + int shift; + int i; + + if (buf[0] == 0x1B) + { + for (i = 0; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (bufsize < esc_len) + { + if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) + return seterror(EINVAL); + } + else + { + if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) + { + cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); + *wbufsize = 0; + return esc_len; + } + } + } + /* not supported escape sequence */ + return seterror(EILSEQ); + } + else if (buf[0] == iso2022_SO_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); + *wbufsize = 0; + return 1; + } + else if (buf[0] == iso2022_SI_seq[0]) + { + cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); + *wbufsize = 0; + return 1; + } + + cs = ISO2022_MODE_CS(cv->mode); + shift = ISO2022_MODE_SHIFT(cv->mode); + + /* reset the mode for informal sequence */ + if (buf[0] < 0x20) + { + cs = ISO2022JP_CS_ASCII; + shift = ISO2022_SI; + } + + len = iesc[cs].len; + if (bufsize < len) + return seterror(EINVAL); + for (i = 0; i < len; ++i) + if (!(buf[i] < 0x80)) + return seterror(EILSEQ); + esc_len = iesc[cs].esc_len; + memcpy(tmp, iesc[cs].esc, esc_len); + if (shift == ISO2022_SO) + { + memcpy(tmp + esc_len, iso2022_SO_seq, 1); + esc_len += 1; + } + memcpy(tmp + esc_len, buf, len); + + if ((cv->codepage == 50220 || cv->codepage == 50221 + || cv->codepage == 50222) && shift == ISO2022_SO) + { + /* XXX: shift-out cannot be used for mbtowc (both kernel and + * mlang) */ + esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; + memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); + memcpy(tmp + esc_len, buf, len); + } + + insize = len + esc_len; + hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, + (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); + if (hr != S_OK || insize != len + esc_len) + return seterror(EILSEQ); + + /* Check for conversion error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (wbuf[0] == buf[0] + && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + return seterror(EILSEQ); + + /* reset the mode for informal sequence */ + if (cv->mode != ISO2022_MODE(cs, shift)) + cv->mode = ISO2022_MODE(cs, shift); + + return len; +} + +static int +iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + char tmp[MB_CHAR_MAX]; + int tmpsize = MB_CHAR_MAX; + int insize = wbufsize; + HRESULT hr; + DWORD dummy = 0; + int len; + int esc_len; + int cs; + int shift; + int i; + + /* + * MultiByte = [escape sequence] + character + [escape sequence] + * + * Whether trailing escape sequence is added depends on which API is + * used (kernel or MLang, and its version). + */ + hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, + (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); + if (hr != S_OK || insize != wbufsize) + return seterror(EILSEQ); + else if (bufsize < tmpsize) + return seterror(E2BIG); + + if (tmpsize == 1) + { + cs = ISO2022JP_CS_ASCII; + esc_len = 0; + } + else + { + for (i = 1; iesc[i].esc != NULL; ++i) + { + esc_len = iesc[i].esc_len; + if (strncmp(tmp, iesc[i].esc, esc_len) == 0) + { + cs = iesc[i].cs; + break; + } + } + if (iesc[i].esc == NULL) + /* not supported escape sequence */ + return seterror(EILSEQ); + } + + shift = ISO2022_SI; + if (tmp[esc_len] == iso2022_SO_seq[0]) + { + shift = ISO2022_SO; + esc_len += 1; + } + + len = iesc[cs].len; + + /* Check for converting error. Assuming defaultChar is 0x3F. */ + /* ascii should be converted from ascii */ + if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) + return seterror(EILSEQ); + else if (tmpsize < esc_len + len) + return seterror(EILSEQ); + + if (cv->mode == ISO2022_MODE(cs, shift)) + { + /* remove escape sequence */ + if (esc_len != 0) + memmove(tmp, tmp + esc_len, len); + esc_len = 0; + } + else + { + if (cs == ISO2022JP_CS_ASCII) + { + esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; + memmove(tmp + esc_len, tmp, len); + memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); + } + if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) + { + /* shift-in before changing to other mode */ + memmove(tmp + 1, tmp, len + esc_len); + memcpy(tmp, iso2022_SI_seq, 1); + esc_len += 1; + } + } + + if (bufsize < len + esc_len) + return seterror(E2BIG); + memcpy(buf, tmp, len + esc_len); + cv->mode = ISO2022_MODE(cs, shift); + return len + esc_len; +} + +static int +iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) +{ + iso2022_esc_t *iesc = iso2022jp_esc; + int esc_len; + + if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) + { + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + esc_len += 1; + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + if (bufsize < esc_len) + return seterror(E2BIG); + + esc_len = 0; + if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) + { + memcpy(buf, iso2022_SI_seq, 1); + esc_len += 1; + } + if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) + { + memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, + iesc[ISO2022JP_CS_ASCII].esc_len); + esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; + } + return esc_len; + } + return 0; +} + +#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL) +BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) +{ + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + hwiniconv = (HMODULE)hinstDLL; + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +#if defined(MAKE_EXE) +#include +#include +#include +int +main(int argc, char **argv) +{ + char *fromcode = NULL; + char *tocode = NULL; + int i; + char inbuf[BUFSIZ]; + char outbuf[BUFSIZ]; + char *pin; + char *pout; + size_t inbytesleft; + size_t outbytesleft; + size_t rest = 0; + iconv_t cd; + size_t r; + FILE *in = stdin; + FILE *out = stdout; + int ignore = 0; + char *p; + + _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 if (strcmp(argv[i], "-c") == 0) + ignore = 1; + else if (strcmp(argv[i], "--output") == 0) + { + out = fopen(argv[++i], "wb"); + if(out == NULL) + { + fprintf(stderr, "cannot open %s\n", argv[i]); + return 1; + } + } + 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 [-c] -f from-enc -t to-enc [file]\n", argv[0]); + return 0; + } + + if (ignore) + { + p = tocode; + tocode = (char *)malloc(strlen(p) + strlen("//IGNORE") + 1); + if (tocode == NULL) + { + perror("fatal error"); + return 1; + } + strcpy(tocode, p); + strcat(tocode, "//IGNORE"); + } + + 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, out); + 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, out); + if (r == (size_t)(-1)) + { + perror("conversion error"); + return 1; + } + + iconv_close(cd); + + return 0; +} +#endif +