charset.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:18k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. #include "mysys_priv.h"
  14. #include "mysys_err.h"
  15. #include <m_ctype.h>
  16. #include <m_string.h>
  17. #include <my_dir.h>
  18. #include <my_xml.h>
  19. /*
  20.   The code below implements this functionality:
  21.   
  22.     - Initializing charset related structures
  23.     - Loading dynamic charsets
  24.     - Searching for a proper CHARSET_INFO 
  25.       using charset name, collation name or collation ID
  26.     - Setting server default character set
  27. */
  28. my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2)
  29. {
  30.   return ((cs1 == cs2) || !strcmp(cs1->csname,cs2->csname));
  31. }
  32. static uint
  33. get_collation_number_internal(const char *name)
  34. {
  35.   CHARSET_INFO **cs;
  36.   for (cs= all_charsets;
  37.        cs < all_charsets+array_elements(all_charsets)-1 ;
  38.        cs++)
  39.   {
  40.     if ( cs[0] && cs[0]->name && 
  41.          !my_strcasecmp(&my_charset_latin1, cs[0]->name, name))
  42.       return cs[0]->number;
  43.   }  
  44.   return 0;
  45. }
  46. static my_bool init_state_maps(CHARSET_INFO *cs)
  47. {
  48.   uint i;
  49.   uchar *state_map;
  50.   uchar *ident_map;
  51.   if (!(cs->state_map= (uchar*) my_once_alloc(256, MYF(MY_WME))))
  52.     return 1;
  53.     
  54.   if (!(cs->ident_map= (uchar*) my_once_alloc(256, MYF(MY_WME))))
  55.     return 1;
  56.   state_map= cs->state_map;
  57.   ident_map= cs->ident_map;
  58.   
  59.   /* Fill state_map with states to get a faster parser */
  60.   for (i=0; i < 256 ; i++)
  61.   {
  62.     if (my_isalpha(cs,i))
  63.       state_map[i]=(uchar) MY_LEX_IDENT;
  64.     else if (my_isdigit(cs,i))
  65.       state_map[i]=(uchar) MY_LEX_NUMBER_IDENT;
  66. #if defined(USE_MB) && defined(USE_MB_IDENT)
  67.     else if (my_mbcharlen(cs, i)>1)
  68.       state_map[i]=(uchar) MY_LEX_IDENT;
  69. #endif
  70.     else if (my_isspace(cs,i))
  71.       state_map[i]=(uchar) MY_LEX_SKIP;
  72.     else
  73.       state_map[i]=(uchar) MY_LEX_CHAR;
  74.   }
  75.   state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT;
  76.   state_map[(uchar)''']=(uchar) MY_LEX_STRING;
  77.   state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT;
  78.   state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP;
  79.   state_map[(uchar)'<']= (uchar) MY_LEX_LONG_CMP_OP;
  80.   state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL;
  81.   state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT;
  82.   state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON;
  83.   state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR;
  84.   state_map[0]=(uchar) MY_LEX_EOL;
  85.   state_map[(uchar)'\']= (uchar) MY_LEX_ESCAPE;
  86.   state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT;
  87.   state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT;
  88.   state_map[(uchar)'@']= (uchar) MY_LEX_USER_END;
  89.   state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER;
  90.   state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER;
  91.   /*
  92.     Create a second map to make it faster to find identifiers
  93.   */
  94.   for (i=0; i < 256 ; i++)
  95.   {
  96.     ident_map[i]= (uchar) (state_map[i] == MY_LEX_IDENT ||
  97.    state_map[i] == MY_LEX_NUMBER_IDENT);
  98.   }
  99.   /* Special handling of hex and binary strings */
  100.   state_map[(uchar)'x']= state_map[(uchar)'X']= (uchar) MY_LEX_IDENT_OR_HEX;
  101.   state_map[(uchar)'b']= state_map[(uchar)'b']= (uchar) MY_LEX_IDENT_OR_BIN;
  102.   state_map[(uchar)'n']= state_map[(uchar)'N']= (uchar) MY_LEX_IDENT_OR_NCHAR;
  103.   return 0;
  104. }
  105. static void simple_cs_init_functions(CHARSET_INFO *cs)
  106. {
  107.   if (cs->state & MY_CS_BINSORT)
  108.     cs->coll= &my_collation_8bit_bin_handler;
  109.   else
  110.     cs->coll= &my_collation_8bit_simple_ci_handler;
  111.   
  112.   cs->cset= &my_charset_8bit_handler;
  113. }
  114. static int cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from)
  115. {
  116.   to->number= from->number ? from->number : to->number;
  117.   if (from->csname)
  118.     if (!(to->csname= my_once_strdup(from->csname,MYF(MY_WME))))
  119.       goto err;
  120.   
  121.   if (from->name)
  122.     if (!(to->name= my_once_strdup(from->name,MYF(MY_WME))))
  123.       goto err;
  124.   
  125.   if (from->comment)
  126.     if (!(to->comment= my_once_strdup(from->comment,MYF(MY_WME))))
  127.       goto err;
  128.   
  129.   if (from->ctype)
  130.   {
  131.     if (!(to->ctype= (uchar*) my_once_memdup((char*) from->ctype,
  132.      MY_CS_CTYPE_TABLE_SIZE,
  133.      MYF(MY_WME))))
  134.       goto err;
  135.     if (init_state_maps(to))
  136.       goto err;
  137.   }
  138.   if (from->to_lower)
  139.     if (!(to->to_lower= (uchar*) my_once_memdup((char*) from->to_lower,
  140. MY_CS_TO_LOWER_TABLE_SIZE,
  141. MYF(MY_WME))))
  142.       goto err;
  143.   if (from->to_upper)
  144.     if (!(to->to_upper= (uchar*) my_once_memdup((char*) from->to_upper,
  145. MY_CS_TO_UPPER_TABLE_SIZE,
  146. MYF(MY_WME))))
  147.       goto err;
  148.   if (from->sort_order)
  149.   {
  150.     if (!(to->sort_order= (uchar*) my_once_memdup((char*) from->sort_order,
  151.   MY_CS_SORT_ORDER_TABLE_SIZE,
  152.   MYF(MY_WME))))
  153.       goto err;
  154.   }
  155.   if (from->tab_to_uni)
  156.   {
  157.     uint sz= MY_CS_TO_UNI_TABLE_SIZE*sizeof(uint16);
  158.     if (!(to->tab_to_uni= (uint16*)  my_once_memdup((char*)from->tab_to_uni,
  159.     sz, MYF(MY_WME))))
  160.       goto err;
  161.   }
  162.   if (from->tailoring)
  163.     if (!(to->tailoring= my_once_strdup(from->tailoring,MYF(MY_WME))))
  164.       goto err;
  165.   return 0;
  166. err:
  167.   return 1;
  168. }
  169. static my_bool simple_cs_is_full(CHARSET_INFO *cs)
  170. {
  171.   return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper &&
  172.    cs->to_lower) &&
  173.   (cs->number && cs->name &&
  174.   (cs->sort_order || (cs->state & MY_CS_BINSORT) )));
  175. }
  176. static int add_collation(CHARSET_INFO *cs)
  177. {
  178.   if (cs->name && (cs->number ||
  179.                    (cs->number=get_collation_number_internal(cs->name))))
  180.   {
  181.     if (!all_charsets[cs->number])
  182.     {
  183.       if (!(all_charsets[cs->number]=
  184.          (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),MYF(0))))
  185.         return MY_XML_ERROR;
  186.       bzero((void*)all_charsets[cs->number],sizeof(CHARSET_INFO));
  187.     }
  188.     
  189.     if (cs->primary_number == cs->number)
  190.       cs->state |= MY_CS_PRIMARY;
  191.       
  192.     if (cs->binary_number == cs->number)
  193.       cs->state |= MY_CS_BINSORT;
  194.     
  195.     all_charsets[cs->number]->state|= cs->state;
  196.     
  197.     if (!(all_charsets[cs->number]->state & MY_CS_COMPILED))
  198.     {
  199.       CHARSET_INFO *new= all_charsets[cs->number];
  200.       if (cs_copy_data(all_charsets[cs->number],cs))
  201.         return MY_XML_ERROR;
  202.       if (!strcmp(cs->csname,"ucs2") )
  203.       {
  204. #if defined(HAVE_CHARSET_ucs2) && defined(HAVE_UCA_COLLATIONS)
  205.         new->cset= my_charset_ucs2_general_uca.cset;
  206.         new->coll= my_charset_ucs2_general_uca.coll;
  207.         new->strxfrm_multiply= my_charset_ucs2_general_uca.strxfrm_multiply;
  208.         new->min_sort_char= my_charset_ucs2_general_uca.min_sort_char;
  209.         new->max_sort_char= my_charset_ucs2_general_uca.max_sort_char;
  210.         new->mbminlen= 2;
  211.         new->mbmaxlen= 2;
  212.         new->state |= MY_CS_AVAILABLE | MY_CS_LOADED;
  213. #endif        
  214.       }
  215.       else
  216.       {
  217.         uchar *sort_order= all_charsets[cs->number]->sort_order;
  218.         simple_cs_init_functions(all_charsets[cs->number]);
  219.         new->mbminlen= 1;
  220.         new->mbmaxlen= 1;
  221.         if (simple_cs_is_full(all_charsets[cs->number]))
  222.         {
  223.           all_charsets[cs->number]->state |= MY_CS_LOADED;
  224.         }
  225.         all_charsets[cs->number]->state|= MY_CS_AVAILABLE;
  226.         
  227.         /*
  228.           Check if case sensitive sort order: A < a < B.
  229.           We need MY_CS_FLAG for regex library, and for
  230.           case sensitivity flag for 5.0 client protocol,
  231.           to support isCaseSensitive() method in JDBC driver 
  232.         */
  233.         if (sort_order && sort_order['A'] < sort_order['a'] &&
  234.                           sort_order['a'] < sort_order['B'])
  235.           all_charsets[cs->number]->state|= MY_CS_CSSORT; 
  236.       }
  237.     }
  238.     else
  239.     {
  240.       /*
  241.         We need the below to make get_charset_name()
  242.         and get_charset_number() working even if a
  243.         character set has not been really incompiled.
  244.         The above functions are used for example
  245.         in error message compiler extra/comp_err.c.
  246.         If a character set was compiled, this information
  247.         will get lost and overwritten in add_compiled_collation().
  248.       */
  249.       CHARSET_INFO *dst= all_charsets[cs->number];
  250.       dst->number= cs->number;
  251.       if (cs->comment)
  252. if (!(dst->comment= my_once_strdup(cs->comment,MYF(MY_WME))))
  253.   return MY_XML_ERROR;
  254.       if (cs->csname)
  255.         if (!(dst->csname= my_once_strdup(cs->csname,MYF(MY_WME))))
  256.   return MY_XML_ERROR;
  257.       if (cs->name)
  258. if (!(dst->name= my_once_strdup(cs->name,MYF(MY_WME))))
  259.   return MY_XML_ERROR;
  260.     }
  261.     cs->number= 0;
  262.     cs->primary_number= 0;
  263.     cs->binary_number= 0;
  264.     cs->name= NULL;
  265.     cs->state= 0;
  266.     cs->sort_order= NULL;
  267.     cs->state= 0;
  268.   }
  269.   return MY_XML_OK;
  270. }
  271. #define MY_MAX_ALLOWED_BUF 1024*1024
  272. #define MY_CHARSET_INDEX "Index.xml"
  273. const char *charsets_dir= NULL;
  274. static int charset_initialized=0;
  275. static my_bool my_read_charset_file(const char *filename, myf myflags)
  276. {
  277.   char *buf;
  278.   int  fd;
  279.   uint len;
  280.   MY_STAT stat_info;
  281.   
  282.   if (!my_stat(filename, &stat_info, MYF(myflags)) ||
  283.        ((len= (uint)stat_info.st_size) > MY_MAX_ALLOWED_BUF) ||
  284.        !(buf= (char *)my_malloc(len,myflags)))
  285.     return TRUE;
  286.   
  287.   if ((fd=my_open(filename,O_RDONLY,myflags)) < 0)
  288.   {
  289.     my_free(buf,myflags);
  290.     return TRUE;
  291.   }
  292.   len=read(fd,buf,len);
  293.   my_close(fd,myflags);
  294.   
  295.   if (my_parse_charset_xml(buf,len,add_collation))
  296.   {
  297. #ifdef NOT_YET
  298.     printf("ERROR at line %d pos %d '%s'n",
  299.    my_xml_error_lineno(&p)+1,
  300.    my_xml_error_pos(&p),
  301.    my_xml_error_string(&p));
  302. #endif
  303.   }
  304.   
  305.   my_free(buf, myflags);  
  306.   return FALSE;
  307. }
  308. char *get_charsets_dir(char *buf)
  309. {
  310.   const char *sharedir= SHAREDIR;
  311.   char *res;
  312.   DBUG_ENTER("get_charsets_dir");
  313.   if (charsets_dir != NULL)
  314.     strmake(buf, charsets_dir, FN_REFLEN-1);
  315.   else
  316.   {
  317.     if (test_if_hard_path(sharedir) ||
  318. is_prefix(sharedir, DEFAULT_CHARSET_HOME))
  319.       strxmov(buf, sharedir, "/", CHARSET_DIR, NullS);
  320.     else
  321.       strxmov(buf, DEFAULT_CHARSET_HOME, "/", sharedir, "/", CHARSET_DIR,
  322.       NullS);
  323.   }
  324.   res= convert_dirname(buf,buf,NullS);
  325.   DBUG_PRINT("info",("charsets dir: '%s'", buf));
  326.   DBUG_RETURN(res);
  327. }
  328. CHARSET_INFO *all_charsets[256];
  329. CHARSET_INFO *default_charset_info = &my_charset_latin1;
  330. void add_compiled_collation(CHARSET_INFO *cs)
  331. {
  332.   all_charsets[cs->number]= cs;
  333.   cs->state|= MY_CS_AVAILABLE;
  334. }
  335. static void *cs_alloc(uint size)
  336. {
  337.   return my_once_alloc(size, MYF(MY_WME));
  338. }
  339. #ifdef __NETWARE__
  340. my_bool STDCALL init_available_charsets(myf myflags)
  341. #else
  342. static my_bool init_available_charsets(myf myflags)
  343. #endif
  344. {
  345.   char fname[FN_REFLEN];
  346.   my_bool error=FALSE;
  347.   /*
  348.     We have to use charset_initialized to not lock on THR_LOCK_charset
  349.     inside get_internal_charset...
  350.   */
  351.   if (!charset_initialized)
  352.   {
  353.     CHARSET_INFO **cs;
  354.     /*
  355.       To make things thread safe we are not allowing other threads to interfere
  356.       while we may changing the cs_info_table
  357.     */
  358.     pthread_mutex_lock(&THR_LOCK_charset);
  359.     if (!charset_initialized)
  360.     {
  361.       bzero(&all_charsets,sizeof(all_charsets));
  362.       init_compiled_charsets(myflags);
  363.       
  364.       /* Copy compiled charsets */
  365.       for (cs=all_charsets;
  366.            cs < all_charsets+array_elements(all_charsets)-1 ;
  367.            cs++)
  368.       {
  369.         if (*cs)
  370.         {
  371.           if (cs[0]->ctype)
  372.             if (init_state_maps(*cs))
  373.               *cs= NULL;
  374.         }
  375.       }
  376.       
  377.       strmov(get_charsets_dir(fname), MY_CHARSET_INDEX);
  378.       error= my_read_charset_file(fname,myflags);
  379.       charset_initialized=1;
  380.     }
  381.     pthread_mutex_unlock(&THR_LOCK_charset);
  382.   }
  383.   return error;
  384. }
  385. void free_charsets(void)
  386. {
  387.   charset_initialized=0;
  388. }
  389. uint get_collation_number(const char *name)
  390. {
  391.   init_available_charsets(MYF(0));
  392.   return get_collation_number_internal(name);
  393. }
  394. uint get_charset_number(const char *charset_name, uint cs_flags)
  395. {
  396.   CHARSET_INFO **cs;
  397.   init_available_charsets(MYF(0));
  398.   
  399.   for (cs= all_charsets;
  400.        cs < all_charsets+array_elements(all_charsets)-1 ;
  401.        cs++)
  402.   {
  403.     if ( cs[0] && cs[0]->csname && (cs[0]->state & cs_flags) &&
  404.          !my_strcasecmp(&my_charset_latin1, cs[0]->csname, charset_name))
  405.       return cs[0]->number;
  406.   }  
  407.   return 0;
  408. }
  409. const char *get_charset_name(uint charset_number)
  410. {
  411.   CHARSET_INFO *cs;
  412.   init_available_charsets(MYF(0));
  413.   cs=all_charsets[charset_number];
  414.   if (cs && (cs->number == charset_number) && cs->name )
  415.     return (char*) cs->name;
  416.   
  417.   return (char*) "?";   /* this mimics find_type() */
  418. }
  419. static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags)
  420. {
  421.   char  buf[FN_REFLEN];
  422.   CHARSET_INFO *cs;
  423.   /*
  424.     To make things thread safe we are not allowing other threads to interfere
  425.     while we may changing the cs_info_table
  426.   */
  427.   pthread_mutex_lock(&THR_LOCK_charset);
  428.   if ((cs= all_charsets[cs_number]))
  429.   {
  430.     if (!(cs->state & MY_CS_COMPILED) && !(cs->state & MY_CS_LOADED))
  431.     {
  432.       strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS);
  433.       my_read_charset_file(buf,flags);
  434.     }
  435.     cs= (cs->state & MY_CS_AVAILABLE) ? cs : NULL;
  436.   }
  437.   if (cs && !(cs->state & MY_CS_READY))
  438.   {
  439.     if ((cs->cset->init && cs->cset->init(cs, cs_alloc)) ||
  440.         (cs->coll->init && cs->coll->init(cs, cs_alloc)))
  441.       cs= NULL;
  442.     else
  443.       cs->state|= MY_CS_READY;
  444.   }
  445.   pthread_mutex_unlock(&THR_LOCK_charset);
  446.   return cs;
  447. }
  448. CHARSET_INFO *get_charset(uint cs_number, myf flags)
  449. {
  450.   CHARSET_INFO *cs;
  451.   if (cs_number == default_charset_info->number)
  452.     return default_charset_info;
  453.   (void) init_available_charsets(MYF(0)); /* If it isn't initialized */
  454.   
  455.   if (!cs_number || cs_number >= array_elements(all_charsets)-1)
  456.     return NULL;
  457.   
  458.   cs=get_internal_charset(cs_number, flags);
  459.   if (!cs && (flags & MY_WME))
  460.   {
  461.     char index_file[FN_REFLEN], cs_string[23];
  462.     strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
  463.     cs_string[0]='#';
  464.     int10_to_str(cs_number, cs_string+1, 10);
  465.     my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_string, index_file);
  466.   }
  467.   return cs;
  468. }
  469. CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags)
  470. {
  471.   uint cs_number;
  472.   CHARSET_INFO *cs;
  473.   (void) init_available_charsets(MYF(0)); /* If it isn't initialized */
  474.   cs_number=get_collation_number(cs_name);
  475.   cs= cs_number ? get_internal_charset(cs_number,flags) : NULL;
  476.   if (!cs && (flags & MY_WME))
  477.   {
  478.     char index_file[FN_REFLEN];
  479.     strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
  480.     my_error(EE_UNKNOWN_COLLATION, MYF(ME_BELL), cs_name, index_file);
  481.   }
  482.   return cs;
  483. }
  484. CHARSET_INFO *get_charset_by_csname(const char *cs_name,
  485.     uint cs_flags,
  486.     myf flags)
  487. {
  488.   uint cs_number;
  489.   CHARSET_INFO *cs;
  490.   DBUG_ENTER("get_charset_by_csname");
  491.   DBUG_PRINT("enter",("name: '%s'", cs_name));
  492.   (void) init_available_charsets(MYF(0)); /* If it isn't initialized */
  493.   
  494.   cs_number= get_charset_number(cs_name, cs_flags);
  495.   cs= cs_number ? get_internal_charset(cs_number, flags) : NULL;
  496.   
  497.   if (!cs && (flags & MY_WME))
  498.   {
  499.     char index_file[FN_REFLEN];
  500.     strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
  501.     my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file);
  502.   }
  503.   DBUG_RETURN(cs);
  504. }
  505. ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to,
  506.                               const char *from, ulong length)
  507. {
  508.   const char *to_start= to;
  509.   const char *end;
  510. #ifdef USE_MB
  511.   my_bool use_mb_flag= use_mb(charset_info);
  512. #endif
  513.   for (end= from + length; from != end; from++)
  514.   {
  515. #ifdef USE_MB
  516.     int l;
  517.     if (use_mb_flag && (l= my_ismbchar(charset_info, from, end)))
  518.     {
  519.       while (l--)
  520. *to++= *from++;
  521.       from--;
  522.       continue;
  523.     }
  524.     /*
  525.      If the next character appears to begin a multi-byte character, we
  526.      escape that first byte of that apparent multi-byte character. (The
  527.      character just looks like a multi-byte character -- if it were actually
  528.      a multi-byte character, it would have been passed through in the test
  529.      above.)
  530.      Without this check, we can create a problem by converting an invalid
  531.      multi-byte character into a valid one. For example, 0xbf27 is not
  532.      a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = )
  533.     */
  534.     if (use_mb_flag && (l= my_mbcharlen(charset_info, *from)) > 1)
  535.     {
  536.       *to++= '\';
  537.       *to++= *from;
  538.       continue;
  539.     }
  540. #endif
  541.     switch (*from) {
  542.     case 0: /* Must be escaped for 'mysql' */
  543.       *to++= '\';
  544.       *to++= '0';
  545.       break;
  546.     case 'n': /* Must be escaped for logs */
  547.       *to++= '\';
  548.       *to++= 'n';
  549.       break;
  550.     case 'r':
  551.       *to++= '\';
  552.       *to++= 'r';
  553.       break;
  554.     case '\':
  555.       *to++= '\';
  556.       *to++= '\';
  557.       break;
  558.     case ''':
  559.       *to++= '\';
  560.       *to++= ''';
  561.       break;
  562.     case '"': /* Better safe than sorry */
  563.       *to++= '\';
  564.       *to++= '"';
  565.       break;
  566.     case '32': /* This gives problems on Win32 */
  567.       *to++= '\';
  568.       *to++= 'Z';
  569.       break;
  570.     default:
  571.       *to++= *from;
  572.     }
  573.   }
  574.   *to= 0;
  575.   return (ulong) (to - to_start);
  576. }
  577. #ifdef BACKSLASH_MBTAIL
  578. static CHARSET_INFO *fs_cset_cache= NULL;
  579. CHARSET_INFO *fs_character_set()
  580. {
  581.   if (!fs_cset_cache)
  582.   {
  583.     char buf[10]= "cp";
  584.     GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE,
  585.                   buf+2, sizeof(buf)-3);
  586.     /*
  587.       We cannot call get_charset_by_name here
  588.       because fs_character_set() is executed before
  589.       LOCK_THD_charset mutex initialization, which
  590.       is used inside get_charset_by_name.
  591.       As we're now interested in cp932 only,
  592.       let's just detect it using strcmp().
  593.     */
  594.     fs_cset_cache= !strcmp(buf, "cp932") ?
  595.                    &my_charset_cp932_japanese_ci : &my_charset_bin;
  596.   }
  597.   return fs_cset_cache;
  598. }
  599. #endif