Change the multibyte character set support so that
[kai/samba.git] / source3 / lib / charset.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Character set handling
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define CHARSET_C
23 #include "includes.h"
24
25 extern int DEBUGLEVEL;
26
27 /*
28  * Codepage definitions.
29  */
30
31 #if !defined(KANJI)
32 /* lower->upper mapping for IBM Code Page 850 - MS-DOS Latin 1 */
33 unsigned char cp_850[][4] = {
34 /* dec col/row oct hex  description */
35 /* 133  08/05  205  85  a grave */
36 /* 183  11/07  267  B7  A grave */      {0x85,0xB7,1,1},
37 /* 160  10/00  240  A0  a acute */
38 /* 181  11/05  265  B5  A acute */      {0xA0,0xB5,1,1},
39 /* 131  08/03  203  83  a circumflex */
40 /* 182  11/06  266  B6  A circumflex */ {0x83,0xB6,1,1},
41 /* 198  12/06  306  C6  a tilde */
42 /* 199  12/07  307  C7  A tilde */      {0xC6,0xC7,1,1},
43 /* 132  08/04  204  84  a diaeresis */
44 /* 142  08/14  216  8E  A diaeresis */  {0x84,0x8E,1,1},
45 /* 134  08/06  206  86  a ring */
46 /* 143  08/15  217  8F  A ring */       {0x86,0x8F,1,1},
47 /* 145  09/01  221  91  ae diphthong */
48 /* 146  09/02  222  92  AE diphthong */ {0x91,0x92,1,1},
49 /* 135  08/07  207  87  c cedilla */
50 /* 128  08/00  200  80  C cedilla */    {0x87,0x80,1,1},
51 /* 138  08/10  212  8A  e grave */
52 /* 212  13/04  324  D4  E grave */      {0x8A,0xD4,1,1},
53 /* 130  08/02  202  82  e acute */
54 /* 144  09/00  220  90  E acute */      {0x82,0x90,1,1},
55 /* 136  08/08  210  88  e circumflex */
56 /* 210  13/02  322  D2  E circumflex */ {0x88,0xD2,1,1},
57 /* 137  08/09  211  89  e diaeresis */
58 /* 211  13/03  323  D3  E diaeresis */  {0x89,0xD3,1,1},
59 /* 141  08/13  215  8D  i grave */
60 /* 222  13/14  336  DE  I grave */      {0x8D,0xDE,1,1},
61 /* 161  10/01  241  A1  i acute */
62 /* 214  13/06  326  D6  I acute */      {0xA1,0xD6,1,1},
63 /* 140  08/12  214  8C  i circumflex */
64 /* 215  13/07  327  D7  I circumflex */ {0x8C,0xD7,1,1},
65 /* 139  08/11  213  8B  i diaeresis */
66 /* 216  13/08  330  D8  I diaeresis */  {0x8B,0xD8,1,1},
67 /* 208  13/00  320  D0  Icelandic eth */
68 /* 209  13/01  321  D1  Icelandic Eth */ {0xD0,0xD1,1,1},
69 /* 164  10/04  244  A4  n tilde */
70 /* 165  10/05  245  A5  N tilde */      {0xA4,0xA5,1,1},
71 /* 149  09/05  225  95  o grave */
72 /* 227  14/03  343  E3  O grave */      {0x95,0xE3,1,1},
73 /* 162  10/02  242  A2  o acute */
74 /* 224  14/00  340  E0  O acute */      {0xA2,0xE0,1,1},
75 /* 147  09/03  223  93  o circumflex */
76 /* 226  14/02  342  E2  O circumflex */ {0x93,0xE2,1,1},
77 /* 228  14/04  344  E4  o tilde */
78 /* 229  14/05  345  E5  O tilde */      {0xE4,0xE5,1,1},
79 /* 148  09/04  224  94  o diaeresis */
80 /* 153  09/09  231  99  O diaeresis */  {0x94,0x99,1,1},
81 /* 155  09/11  233  9B  o slash */
82 /* 157  09/13  235  9D  O slash */      {0x9B,0x9D,1,1},
83 /* 151  09/07  227  97  u grave */
84 /* 235  14/11  353  EB  U grave */      {0x97,0xEB,1,1},
85 /* 163  10/03  243  A3  u acute */
86 /* 233  14/09  351  E9  U acute */      {0xA3,0xE9,1,1},
87 /* 150  09/06  226  96  u circumflex */
88 /* 234  14/10  352  EA  U circumflex */ {0x96,0xEA,1,1},
89 /* 129  08/01  201  81  u diaeresis */
90 /* 154  09/10  232  9A  U diaeresis */  {0x81,0x9A,1,1},
91 /* 236  14/12  354  EC  y acute */
92 /* 237  14/13  355  ED  Y acute */      {0xEC,0xED,1,1},
93 /* 231  14/07  347  E7  Icelandic thorn */
94 /* 232  14/08  350  E8  Icelandic Thorn */ {0xE7,0xE8,1,1},
95    
96   {0x9C,0,0,0},     /* Pound        */
97   {0,0,0,0}
98 };
99 #else /* KANJI */ 
100 /* lower->upper mapping for IBM Code Page 932 - MS-DOS Japanese SJIS */
101 unsigned char cp_932[][4] = {
102   {0,0,0,0}
103 };
104 #endif /* KANJI */
105
106 char xx_dos_char_map[256];
107 char xx_upper_char_map[256];
108 char xx_lower_char_map[256];
109
110 char *dos_char_map = xx_dos_char_map;
111 char *upper_char_map = xx_upper_char_map;
112 char *lower_char_map = xx_lower_char_map;
113
114 /*
115  * This code has been extended to deal with ascynchronous mappings
116  * like MS-DOS Latin US (Code page 437) where things like :
117  * a acute are capitalized to 'A', but the reverse mapping
118  * must not hold true. This allows the filename case insensitive
119  * matching in do_match() to work, as the DOS/Win95/NT client 
120  * uses 'A' as a mask to match against characters like a acute.
121  * This is the meaning behind the parameters that allow a
122  * mapping from lower to upper, but not upper to lower.
123  */
124
125 static void add_dos_char(int lower, BOOL map_lower_to_upper, 
126                          int upper, BOOL map_upper_to_lower)
127 {
128   lower &= 0xff;
129   upper &= 0xff;
130   DEBUG(6,("Adding chars 0x%x 0x%x (l->u = %s) (u->l = %s)\n",lower,upper,
131          map_lower_to_upper ? "True" : "False",
132          map_upper_to_lower ? "True" : "False"));
133   if (lower) dos_char_map[lower] = 1;
134   if (upper) dos_char_map[upper] = 1;
135   lower_char_map[lower] = (char)lower; /* Define tolower(lower) */
136   upper_char_map[upper] = (char)upper; /* Define toupper(upper) */
137   if (lower && upper) {
138     if(map_upper_to_lower)
139     lower_char_map[upper] = (char)lower;
140     if(map_lower_to_upper)
141     upper_char_map[lower] = (char)upper;
142   }
143 }
144
145 /****************************************************************************
146 initialise the charset arrays
147 ****************************************************************************/
148 void charset_initialise(void)
149 {
150   int i;
151
152 #ifdef LC_ALL
153   /* include <locale.h> in includes.h if available for OS                  */
154   /* we take only standard 7-bit ASCII definitions from ctype              */
155   setlocale(LC_ALL,"C");
156 #endif
157
158   for (i= 0;i<=255;i++) {
159     dos_char_map[i] = 0;
160   }
161
162   for (i=0;i<=127;i++) {
163     if (isalnum((char)i) || strchr("._^$~!#%&-{}()@'`",(char)i))
164       add_dos_char(i,False,0,False);
165   }
166
167   for (i=0; i<=255; i++) {
168     char c = (char)i;
169     upper_char_map[i] = lower_char_map[i] = c;
170
171     /* Some systems have buggy isupper/islower for characters
172        above 127. Best not to rely on them. */
173     if(i < 128) {
174       if (isupper(c)) lower_char_map[i] = tolower(c);
175       if (islower(c)) upper_char_map[i] = toupper(c);
176     }
177   }
178 }
179
180 /****************************************************************************
181 load the client codepage.
182 ****************************************************************************/
183
184 typedef unsigned char (*codepage_p)[4];
185
186 static codepage_p load_client_codepage( int client_codepage )
187 {
188   pstring codepage_file_name;
189   unsigned char buf[8];
190   FILE *fp = NULL;
191   unsigned int size;
192   codepage_p cp_p = NULL;
193   struct stat st;
194
195   DEBUG(5, ("load_client_codepage: loading codepage %d.\n", client_codepage));
196
197   if(strlen(CODEPAGEDIR) + 14 > sizeof(codepage_file_name))
198   {
199     DEBUG(0,("load_client_codepage: filename too long to load\n"));
200     return NULL;
201   }
202
203   strcpy(codepage_file_name, CODEPAGEDIR);
204   strcat(codepage_file_name, "/");
205   strcat(codepage_file_name, "codepage.");
206   sprintf( &codepage_file_name[strlen(codepage_file_name)], "%03d",
207            client_codepage);
208
209   if(!file_exist(codepage_file_name,&st))
210   {
211     DEBUG(0,("load_client_codepage: filename %s does not exist.\n",
212               codepage_file_name));
213     return NULL;
214   }
215
216   /* Check if it is at least big enough to hold the required
217      data. Should be 2 byte version, 2 byte codepage, 4 byte length, 
218      plus zero or more bytes of data. Note that the data cannot be more
219      than 4 * MAXCODEPAGELINES bytes.
220    */
221   size = (unsigned int)st.st_size;
222
223   if( size < CODEPAGE_HEADER_SIZE || size > (CODEPAGE_HEADER_SIZE + 4 * MAXCODEPAGELINES))
224   {
225     DEBUG(0,("load_client_codepage: file %s is an incorrect size for a \
226 code page file.\n", codepage_file_name));
227     return NULL;
228   }
229
230   /* Read the first 8 bytes of the codepage file - check
231      the version number and code page number. All the data
232      is held in little endian format.
233    */
234
235   if((fp = fopen( codepage_file_name, "r")) == NULL)
236   {
237     DEBUG(0,("load_client_codepage: cannot open file %s. Error was %s\n",
238               codepage_file_name, strerror(errno)));
239     return NULL;
240   }
241
242   if(fread( buf, 1, CODEPAGE_HEADER_SIZE, fp)!=CODEPAGE_HEADER_SIZE)
243   {
244     DEBUG(0,("load_client_codepage: cannot read header from file %s. Error was %s\n",
245               codepage_file_name, strerror(errno)));
246     goto clean_and_exit;
247   }
248
249   /* Check the version value */
250   if(SVAL(buf,CODEPAGE_VERSION_OFFSET) != CODEPAGE_FILE_VERSION_ID)
251   {
252     DEBUG(0,("load_client_codepage: filename %s has incorrect version id. \
253 Needed %hu, got %hu.\n", 
254           codepage_file_name, (uint16)CODEPAGE_FILE_VERSION_ID, 
255           SVAL(buf,CODEPAGE_VERSION_OFFSET)));
256     goto clean_and_exit;
257   }
258
259   /* Check the codepage matches */
260   if(SVAL(buf,CODEPAGE_CLIENT_CODEPAGE_OFFSET) != (uint16)client_codepage)
261   {
262     DEBUG(0,("load_client_codepage: filename %s has incorrect codepage. \
263 Needed %hu, got %hu.\n", 
264            codepage_file_name, (uint16)client_codepage, 
265            SVAL(buf,CODEPAGE_CLIENT_CODEPAGE_OFFSET)));
266     goto clean_and_exit;
267   }
268
269   /* Check the length is correct. */
270   if(IVAL(buf,CODEPAGE_LENGTH_OFFSET) != 
271                  (unsigned int)(size - CODEPAGE_HEADER_SIZE))
272   {
273     DEBUG(0,("load_client_codepage: filename %s has incorrect size headers. \
274 Needed %u, got %u.\n", codepage_file_name, size - CODEPAGE_HEADER_SIZE, 
275                IVAL(buf,CODEPAGE_LENGTH_OFFSET)));
276     goto clean_and_exit;
277   }
278
279   size -= CODEPAGE_HEADER_SIZE; /* Remove header */
280
281   /* Make sure the size is a multiple of 4. */
282   if((size % 4 ) != 0)
283   {
284     DEBUG(0,("load_client_codepage: filename %s has a codepage size not a \
285 multiple of 4.\n", codepage_file_name));
286     goto clean_and_exit;
287   }
288
289   /* Allocate space for the code page file and read it all in. */
290   if((cp_p = (codepage_p)malloc( size  + 4 )) == NULL)
291   {
292     DEBUG(0,("load_client_codepage: malloc fail.\n"));
293     goto clean_and_exit;
294   }
295
296   if(fread( (char *)cp_p, 1, size, fp)!=size)
297   {
298     DEBUG(0,("load_client_codepage: read fail on file %s. Error was %s.\n",
299               codepage_file_name, strerror(errno)));
300     goto clean_and_exit;
301   }
302
303   /* Ensure array is correctly terminated. */
304   memset(((char *)cp_p) + size, '\0', 4);
305
306   fclose(fp);
307   return cp_p;
308
309 clean_and_exit:
310
311   /* pseudo destructor :-) */
312
313   if(fp != NULL)
314     fclose(fp);
315   if(cp_p)
316     free((char *)cp_p);
317   return NULL;
318 }
319
320 /****************************************************************************
321 initialise the client codepage.
322 ****************************************************************************/
323 void codepage_initialise(int client_codepage)
324 {
325   int i;
326   static codepage_p cp = NULL;
327
328   if(cp != NULL)
329   {
330     DEBUG(6,
331       ("codepage_initialise: called twice - ignoring second client code page = %d\n",
332       client_codepage));
333     return;
334   }
335
336   DEBUG(6,("codepage_initialise: client code page = %d\n", client_codepage));
337
338   /*
339    * Known client codepages - these can be added to.
340    */
341   cp = load_client_codepage( client_codepage );
342
343   if(cp == NULL)
344   {
345 #ifdef KANJI
346     DEBUG(6,("codepage_initialise: loading dynamic codepage file %s/codepage.%d \
347 for code page %d failed. Using default client codepage 932\n", 
348              CODEPAGEDIR, client_codepage, client_codepage));
349     cp = cp_932;
350     client_codepage = KANJI_CODEPAGE;
351 #else /* KANJI */
352     DEBUG(6,("codepage_initialise: loading dynamic codepage file %s/codepage.%d \
353 for code page %d failed. Using default client codepage 850\n", 
354              CODEPAGEDIR, client_codepage, client_codepage));
355     cp = cp_850;
356     client_codepage = MSDOS_LATIN_1_CODEPAGE;
357 #endif /* KANJI */
358   }
359
360   /*
361    * Setup the function pointers for the loaded codepage.
362    */
363   initialize_multibyte_vectors( client_codepage );
364
365   if(cp)
366   {
367     for(i = 0; !((cp[i][0] == '\0') && (cp[i][1] == '\0')); i++)
368       add_dos_char(cp[i][0], (BOOL)cp[i][2], cp[i][1], (BOOL)cp[i][3]);
369   }
370 }
371
372 /*******************************************************************
373 add characters depending on a string passed by the user
374 ********************************************************************/
375 void add_char_string(char *s)
376 {
377   char *extra_chars = (char *)strdup(s);
378   char *t;
379   if (!extra_chars) return;
380
381   for (t=strtok(extra_chars," \t\r\n"); t; t=strtok(NULL," \t\r\n")) {
382     char c1=0,c2=0;
383     int i1=0,i2=0;
384     if (isdigit((unsigned char)*t) || (*t)=='-') {
385       sscanf(t,"%i:%i",&i1,&i2);
386       add_dos_char(i1,True,i2,True);
387     } else {
388       sscanf(t,"%c:%c",&c1,&c2);
389       add_dos_char((unsigned char)c1,True,(unsigned char)c2, True);
390     }
391   }
392
393   free(extra_chars);
394 }