home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / mysys / charset.c < prev    next >
C/C++ Source or Header  |  2000-10-11  |  15KB  |  565 lines

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This library is free software; you can redistribute it and/or
  4.    modify it under the terms of the GNU Library General Public
  5.    License as published by the Free Software Foundation; either
  6.    version 2 of the License, or (at your option) any later version.
  7.    
  8.    This library is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.    Library General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU Library General Public
  14.    License along with this library; if not, write to the Free
  15.    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  16.    MA 02111-1307, USA */
  17.  
  18. #include "mysys_priv.h"
  19. #include "mysys_err.h"
  20. #include <m_ctype.h>
  21. #include <m_string.h>
  22. #include <my_dir.h>
  23.  
  24. typedef struct cs_id_st {
  25.   char *name;
  26.   uint number;
  27. } CS_ID;
  28.  
  29. const char *charsets_dir = NULL;
  30. static DYNAMIC_ARRAY cs_info_table;
  31. static CS_ID **available_charsets;
  32. static int charset_initialized=0;
  33.  
  34. #define MAX_LINE  1024
  35.  
  36. #define CTYPE_TABLE_SIZE      257
  37. #define TO_LOWER_TABLE_SIZE   256
  38. #define TO_UPPER_TABLE_SIZE   256
  39. #define SORT_ORDER_TABLE_SIZE 256
  40.  
  41. struct simpleconfig_buf_st {
  42.   FILE *f;
  43.   char  buf[MAX_LINE];
  44.   char *p;
  45. };
  46.  
  47. /* Defined in strings/ctype.c */
  48.  
  49. CHARSET_INFO *find_compiled_charset(uint cs_number);
  50. uint compiled_charset_number(const char *name);
  51. const char *compiled_charset_name(uint charset_number);
  52.  
  53.  
  54. static uint num_from_csname(CS_ID **cs, const char *name)
  55. {
  56.   CS_ID **c;
  57.   for (c = cs; *c; ++c)
  58.     if (!strcmp((*c)->name, name))
  59.       return (*c)->number;
  60.   return 0;   /* this mimics find_type() */
  61. }
  62.  
  63. static char *name_from_csnum(CS_ID **cs, uint number)
  64. {
  65.   CS_ID **c;
  66.   if(cs)
  67.     for (c = cs; *c; ++c)
  68.       if ((*c)->number == number)
  69.     return (*c)->name;
  70.   return (char*) "?";   /* this mimics find_type() */
  71. }
  72.  
  73. static my_bool get_word(struct simpleconfig_buf_st *fb, char *buf)
  74. {
  75.   char *endptr=fb->p;
  76.  
  77.   for (;;)
  78.   {
  79.     while (isspace(*endptr))
  80.       ++endptr;
  81.     if (*endptr && *endptr != '#')        /* Not comment */
  82.       break;                    /* Found something */
  83.     if ((fgets(fb->buf, sizeof(fb->buf), fb->f)) == NULL)
  84.       return TRUE; /* end of file */
  85.     endptr = fb->buf;
  86.   }
  87.  
  88.   while (!isspace(*endptr))
  89.     *buf++= *endptr++;
  90.   *buf=0;
  91.   fb->p = endptr;
  92.  
  93.   return FALSE;
  94. }
  95.  
  96.  
  97. char *get_charsets_dir(char *buf)
  98. {
  99.   const char *sharedir = SHAREDIR;
  100.   DBUG_ENTER("get_charsets_dir");
  101.  
  102.   if (charsets_dir != NULL)
  103.     strnmov(buf, charsets_dir, FN_REFLEN);
  104.   else
  105.   {
  106.     if (test_if_hard_path(sharedir) ||
  107.     is_prefix(sharedir, DEFAULT_CHARSET_HOME))
  108.       strxmov(buf, sharedir, "/", CHARSET_DIR, NullS);
  109.     else
  110.       strxmov(buf, DEFAULT_CHARSET_HOME, "/", sharedir, "/", CHARSET_DIR,
  111.           NullS);
  112.   }
  113.   convert_dirname(buf);
  114.   DBUG_PRINT("info",("charsets dir='%s'", buf));
  115.   DBUG_RETURN(strend(buf));
  116. }
  117.  
  118.  
  119. static my_bool read_charset_index(CS_ID ***charsets, myf myflags)
  120. {
  121.   struct simpleconfig_buf_st fb;
  122.   char buf[MAX_LINE], num_buf[MAX_LINE];
  123.   DYNAMIC_ARRAY cs;
  124.   CS_ID *csid;
  125.  
  126.   strmov(get_charsets_dir(buf), "Index");
  127.  
  128.   if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL)
  129.     return TRUE;
  130.   fb.buf[0] = '\0';
  131.   fb.p = fb.buf;
  132.  
  133.   if (init_dynamic_array(&cs, sizeof(CS_ID *), 32, 32))
  134.     return TRUE;
  135.  
  136.   while (!get_word(&fb, buf) && !get_word(&fb, num_buf))
  137.   {
  138.     uint csnum;
  139.     uint length;
  140.  
  141.     if (!(csnum = atoi(num_buf)))
  142.     {
  143.       /* corrupt Index file */
  144.       my_fclose(fb.f,myflags);
  145.       return TRUE;
  146.     }
  147.  
  148.     if (!(csid = (CS_ID*) my_once_alloc(sizeof(CS_ID), myflags)) ||
  149.         !(csid->name=
  150.            (char*) my_once_alloc(length= (uint) strlen(buf)+1, myflags)))
  151.     {
  152.       my_fclose(fb.f,myflags);
  153.       return TRUE;
  154.     }
  155.     memcpy(csid->name,buf,length);
  156.     csid->number = csnum;
  157.  
  158.     insert_dynamic(&cs, (gptr) &csid);
  159.   }
  160.   my_fclose(fb.f,myflags);
  161.  
  162.  
  163.   if (!(*charsets =
  164.       (CS_ID **) my_once_alloc((cs.elements + 1) * sizeof(CS_ID *), myflags)))
  165.     return TRUE;
  166.   /* unwarranted chumminess with dynamic_array implementation? */
  167.   memcpy((byte *) *charsets, cs.buffer, cs.elements * sizeof(CS_ID *));
  168.   (*charsets)[cs.elements] = NULL;
  169.   delete_dynamic(&cs);  
  170.  
  171.   return FALSE;
  172. }
  173.  
  174.  
  175. static my_bool init_available_charsets(myf myflags)
  176. {
  177.   my_bool error=0;
  178.   /*
  179.     We have to use charset_initialized to not lock on THR_LOCK_charset
  180.     inside get_internal_charset...
  181.    */
  182.   if (!charset_initialized)
  183.   {
  184.   /*
  185.     To make things thread safe we are not allowing other threads to interfere
  186.     while we may changing the cs_info_table
  187.   */
  188.     pthread_mutex_lock(&THR_LOCK_charset);
  189.     if (!cs_info_table.buffer)            /* If not initialized */
  190.     {
  191.       init_dynamic_array(&cs_info_table, sizeof(CHARSET_INFO*), 16, 8);
  192.       error = read_charset_index(&available_charsets, myflags);
  193.     }
  194.     charset_initialized=1;
  195.     pthread_mutex_unlock(&THR_LOCK_charset);
  196.   }
  197.   if(!available_charsets || !available_charsets[0])
  198.     error = TRUE;
  199.   return error;
  200. }
  201.  
  202.  
  203. void free_charsets(void)
  204. {
  205.   delete_dynamic(&cs_info_table);
  206. }
  207.  
  208.  
  209. static my_bool fill_array(uchar *array, int sz, struct simpleconfig_buf_st *fb)
  210. {
  211.   char buf[MAX_LINE];
  212.   while (sz--)
  213.   {
  214.     if (get_word(fb, buf))
  215.     {
  216.       DBUG_PRINT("error",("get_word failed, expecting %d more words", sz + 1));
  217.       return 1;
  218.     }
  219.     *array++ = (uchar) strtol(buf, NULL, 16);
  220.   }
  221.   return 0;
  222. }
  223.  
  224.  
  225. static void get_charset_conf_name(uint cs_number, char *buf)
  226. {
  227.   strxmov(get_charsets_dir(buf),
  228.           name_from_csnum(available_charsets, cs_number), ".conf", NullS);
  229. }
  230.  
  231.  
  232. static my_bool read_charset_file(uint cs_number, CHARSET_INFO *set,
  233.                  myf myflags)
  234. {
  235.   struct simpleconfig_buf_st fb;
  236.   char buf[FN_REFLEN];
  237.   my_bool result;
  238.   DBUG_ENTER("read_charset_file");
  239.   DBUG_PRINT("enter",("cs_number: %d", cs_number));
  240.  
  241.   if (cs_number <= 0)
  242.     DBUG_RETURN(TRUE);
  243.  
  244.   get_charset_conf_name(cs_number, buf);
  245.   DBUG_PRINT("info",("file name: %s", buf));
  246.  
  247.   if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL)
  248.     DBUG_RETURN(TRUE);
  249.  
  250.   fb.buf[0] = '\0';                /* Init for get_word */
  251.   fb.p = fb.buf;
  252.  
  253.   result=FALSE;
  254.   if (fill_array(set->ctype,      CTYPE_TABLE_SIZE,      &fb) ||
  255.       fill_array(set->to_lower,   TO_LOWER_TABLE_SIZE,   &fb) ||
  256.       fill_array(set->to_upper,   TO_UPPER_TABLE_SIZE,   &fb) ||
  257.       fill_array(set->sort_order, SORT_ORDER_TABLE_SIZE, &fb))
  258.     result=TRUE;
  259.  
  260.   my_fclose(fb.f, MYF(0));
  261.   DBUG_RETURN(result);
  262. }
  263.  
  264.  
  265. uint get_charset_number(const char *charset_name)
  266. {
  267.   my_bool error;
  268.   error = init_available_charsets(MYF(0));    /* If it isn't initialized */
  269.   if (error)
  270.     return compiled_charset_number(charset_name);
  271.   else
  272.     return num_from_csname(available_charsets, charset_name);
  273. }
  274.  
  275. const char *get_charset_name(uint charset_number)
  276. {
  277.   my_bool error;
  278.   error = init_available_charsets(MYF(0));    /* If it isn't initialized */
  279.   if (error)
  280.     return compiled_charset_name(charset_number);
  281.   else
  282.     return name_from_csnum(available_charsets, charset_number);
  283. }
  284.  
  285.  
  286. static CHARSET_INFO *find_charset(CHARSET_INFO **table, uint cs_number,
  287.                                   size_t tablesz)
  288. {
  289.   uint i;
  290.   for (i = 0; i < tablesz; ++i)
  291.     if (table[i]->number == cs_number)
  292.       return table[i];
  293.   return NULL;
  294. }
  295.  
  296. static CHARSET_INFO *find_charset_by_name(CHARSET_INFO **table, const char *name,
  297.                       size_t tablesz)
  298. {
  299.   uint i;
  300.   for (i = 0; i < tablesz; ++i)
  301.     if (!strcmp(table[i]->name,name))
  302.       return table[i];
  303.   return NULL;
  304. }
  305.  
  306. static CHARSET_INFO *add_charset(uint cs_number, const char *cs_name)
  307. {
  308.   CHARSET_INFO tmp_cs,*cs;
  309.   uchar tmp_ctype[CTYPE_TABLE_SIZE];
  310.   uchar tmp_to_lower[TO_LOWER_TABLE_SIZE];
  311.   uchar tmp_to_upper[TO_UPPER_TABLE_SIZE];
  312.   uchar tmp_sort_order[SORT_ORDER_TABLE_SIZE];
  313.  
  314.   /* Don't allocate memory if we are not sure we can find the char set */
  315.   cs= &tmp_cs;
  316.   bzero((char*) cs, sizeof(*cs));
  317.   cs->ctype=tmp_ctype;
  318.   cs->to_lower=tmp_to_lower;
  319.   cs->to_upper=tmp_to_upper;
  320.   cs->sort_order=tmp_sort_order;
  321.   if (read_charset_file(cs_number, cs, MYF(MY_WME)))
  322.     return NULL;
  323.  
  324.   cs           = (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),
  325.                            MYF(MY_WME));
  326.   *cs=tmp_cs;
  327.   cs->name     = (char *) my_once_alloc((uint) strlen(cs_name)+1, MYF(MY_WME));
  328.   cs->ctype    = (uchar*) my_once_alloc(CTYPE_TABLE_SIZE,      MYF(MY_WME));
  329.   cs->to_lower = (uchar*) my_once_alloc(TO_LOWER_TABLE_SIZE,   MYF(MY_WME));
  330.   cs->to_upper = (uchar*) my_once_alloc(TO_UPPER_TABLE_SIZE,   MYF(MY_WME));
  331.   cs->sort_order=(uchar*) my_once_alloc(SORT_ORDER_TABLE_SIZE, MYF(MY_WME));
  332.   cs->number   = cs_number;
  333.   memcpy((char*) cs->name,     (char*) cs_name,    strlen(cs_name) + 1);
  334.   memcpy((char*) cs->ctype,     (char*) tmp_ctype,    sizeof(tmp_ctype));
  335.   memcpy((char*) cs->to_lower, (char*) tmp_to_lower,    sizeof(tmp_to_lower));
  336.   memcpy((char*) cs->to_upper, (char*) tmp_to_upper,    sizeof(tmp_to_upper));
  337.   memcpy((char*) cs->sort_order, (char*) tmp_sort_order,
  338.      sizeof(tmp_sort_order));
  339.   insert_dynamic(&cs_info_table, (gptr) &cs);
  340.   return cs;
  341. }
  342.  
  343. static CHARSET_INFO *get_internal_charset(uint cs_number)
  344. {
  345.   CHARSET_INFO *cs;
  346.   /*
  347.     To make things thread safe we are not allowing other threads to interfere
  348.     while we may changing the cs_info_table
  349.   */
  350.   pthread_mutex_lock(&THR_LOCK_charset);
  351.   if (!(cs = find_charset((CHARSET_INFO**) cs_info_table.buffer, cs_number,
  352.               cs_info_table.elements)))
  353.     if (!(cs = find_compiled_charset(cs_number)))
  354.       cs=add_charset(cs_number, get_charset_name(cs_number));
  355.   pthread_mutex_unlock(&THR_LOCK_charset);
  356.   return cs;
  357. }
  358.  
  359.  
  360. static CHARSET_INFO *get_internal_charset_by_name(const char *name)
  361. {
  362.   CHARSET_INFO *cs;
  363.   /*
  364.     To make things thread safe we are not allowing other threads to interfere
  365.     while we may changing the cs_info_table
  366.   */
  367.   pthread_mutex_lock(&THR_LOCK_charset);
  368.   if (!(cs = find_charset_by_name((CHARSET_INFO**) cs_info_table.buffer, name,
  369.                  cs_info_table.elements)))
  370.     if (!(cs = find_compiled_charset_by_name(name)))
  371.       cs=add_charset(get_charset_number(name), name);
  372.   pthread_mutex_unlock(&THR_LOCK_charset);
  373.   return cs;
  374. }
  375.  
  376.  
  377. CHARSET_INFO *get_charset(uint cs_number, myf flags)
  378. {
  379.   CHARSET_INFO *cs;
  380.   (void) init_available_charsets(MYF(0));    /* If it isn't initialized */
  381.   cs=get_internal_charset(cs_number);
  382.  
  383.   if (!cs && (flags & MY_WME))
  384.   {
  385.     char index_file[FN_REFLEN], cs_string[23];
  386.     strmov(get_charsets_dir(index_file), "Index");
  387.     cs_string[0]='#';
  388.     int10_to_str(cs_number, cs_string+1, 10);
  389.     my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_string, index_file);
  390.   }
  391.   return cs;
  392. }
  393.  
  394. my_bool set_default_charset(uint cs, myf flags)
  395. {
  396.   CHARSET_INFO *new;
  397.   DBUG_ENTER("set_default_charset");
  398.   DBUG_PRINT("enter",("character set: %d",(int) cs));
  399.   new = get_charset(cs, flags);
  400.   if (!new)
  401.   {
  402.     DBUG_PRINT("error",("Couldn't set default character set"));
  403.     DBUG_RETURN(TRUE);   /* error */
  404.   }
  405.   default_charset_info = new;
  406.   DBUG_RETURN(FALSE);
  407. }
  408.  
  409. CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags)
  410. {
  411.   CHARSET_INFO *cs;
  412.   (void) init_available_charsets(MYF(0));    /* If it isn't initialized */
  413.   cs=get_internal_charset_by_name(cs_name);
  414.  
  415.   if (!cs && (flags & MY_WME))
  416.   {
  417.     char index_file[FN_REFLEN];
  418.     strmov(get_charsets_dir(index_file), "Index");
  419.     my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file);
  420.   }
  421.  
  422.   return cs;
  423. }
  424.  
  425. my_bool set_default_charset_by_name(const char *cs_name, myf flags)
  426. {
  427.   CHARSET_INFO *new;
  428.   DBUG_ENTER("set_default_charset_by_name");
  429.   DBUG_PRINT("enter",("character set: %s", cs_name));
  430.   new = get_charset_by_name(cs_name, flags);
  431.   if (!new)
  432.   {
  433.     DBUG_PRINT("error",("Couldn't set default character set"));
  434.     DBUG_RETURN(TRUE);   /* error */
  435.   }
  436.  
  437.   default_charset_info = new;
  438.   DBUG_RETURN(FALSE);
  439. }
  440.  
  441. /* Only append name if it doesn't exist from before */
  442.  
  443. static my_bool charset_in_string(const char *name, DYNAMIC_STRING *s)
  444. {
  445.   uint length= (uint) strlen(name);
  446.   const char *pos;
  447.   for (pos=s->str ; (pos=strstr(pos,name)) ; pos++)
  448.   {
  449.     if (! pos[length] || pos[length] == ' ')
  450.       return TRUE;                /* Already existed */
  451.   }
  452.  
  453.   return FALSE;
  454. }
  455.  
  456. static void charset_append(DYNAMIC_STRING *s, const char *name)
  457. {
  458.   if (!charset_in_string(name, s)) {
  459.     dynstr_append(s, name);
  460.     dynstr_append(s, " ");
  461.   }
  462. }
  463.  
  464.  
  465. /* Returns a dynamically-allocated string listing the character sets
  466.    requested.  The caller is responsible for freeing the memory. */
  467.  
  468. char * list_charsets(myf want_flags)
  469. {
  470.   DYNAMIC_STRING s;
  471.   char *p;
  472.  
  473.   (void)init_available_charsets(MYF(0));
  474.   init_dynamic_string(&s, NullS, 256, 1024);
  475.  
  476.   if (want_flags & MY_COMPILED_SETS)
  477.   {
  478.     CHARSET_INFO *cs;
  479.     for (cs = compiled_charsets; cs->number > 0; cs++)
  480.     {
  481.       dynstr_append(&s, cs->name);
  482.       dynstr_append(&s, " ");
  483.     }
  484.   }
  485.  
  486.   if (want_flags & MY_CONFIG_SETS)
  487.   {
  488.     CS_ID **c;
  489.     char buf[FN_REFLEN];
  490.     MY_STAT status;
  491.  
  492.     if((c=available_charsets))
  493.       for (; *c; ++c)
  494.     {
  495.       if (charset_in_string((*c)->name, &s))
  496.         continue;
  497.       get_charset_conf_name((*c)->number, buf);
  498.       if (!my_stat(buf, &status, MYF(0)))
  499.         continue;       /* conf file doesn't exist */
  500.       dynstr_append(&s, (*c)->name);
  501.       dynstr_append(&s, " ");
  502.     }
  503.   }
  504.  
  505.   if (want_flags & MY_INDEX_SETS)
  506.   {
  507.     CS_ID **c;
  508.     for (c = available_charsets; *c; ++c)
  509.       charset_append(&s, (*c)->name);
  510.   }
  511.  
  512.   if (want_flags & MY_LOADED_SETS)
  513.   {
  514.     uint i;
  515.     for (i = 0; i < cs_info_table.elements; i++)
  516.       charset_append(&s, 
  517.              dynamic_element(&cs_info_table, i, CHARSET_INFO *)->name);
  518.   }
  519.   s.str[s.length - 1] = '\0';   /* chop trailing space */
  520.   p = my_strdup(s.str, MYF(MY_WME));
  521.   dynstr_free(&s);
  522.  
  523.   return p;
  524. }
  525.  
  526. /****************************************************************************
  527. * Code for debugging.
  528. ****************************************************************************/
  529.  
  530.  
  531. static void _print_array(uint8 *data, uint size)
  532. {
  533.   uint i;
  534.   for (i = 0; i < size; ++i)
  535.   {
  536.     if (i == 0 || i % 16 == size % 16) printf("  ");
  537.     printf(" %02x", data[i]);
  538.     if ((i+1) % 16 == size % 16) printf("\n");
  539.   }
  540. }
  541.  
  542. /* _print_csinfo is called from test_charset.c */
  543. void _print_csinfo(CHARSET_INFO *cs)
  544. {
  545.   printf("%s #%d\n", cs->name, cs->number);
  546.   printf("ctype:\n"); _print_array(cs->ctype, 257);
  547.   printf("to_lower:\n"); _print_array(cs->to_lower, 256);
  548.   printf("to_upper:\n"); _print_array(cs->to_upper, 256);
  549.   printf("sort_order:\n"); _print_array(cs->sort_order, 256);
  550.   printf("collate:    %3s (%d, %p, %p, %p, %p, %p)\n",
  551.          cs->strxfrm_multiply ? "yes" : "no",
  552.          cs->strxfrm_multiply,
  553.          cs->strcoll,
  554.          cs->strxfrm,
  555.          cs->strnncoll,
  556.          cs->strnxfrm,
  557.          cs->like_range);
  558.   printf("multi-byte: %3s (%d, %p, %p, %p)\n",
  559.          cs->mbmaxlen ? "yes" : "no",
  560.          cs->mbmaxlen,
  561.          cs->ismbchar,
  562.          cs->ismbhead,
  563.          cs->mbcharlen);
  564. }
  565.