changed to use slprintf() instead of sprintf() just about
[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   slprintf(&codepage_file_name[strlen(codepage_file_name)], 
207            sizeof(pstring)-(strlen(codepage_file_name)+1),
208            "%03d",
209            client_codepage);
210
211   if(!file_exist(codepage_file_name,&st))
212   {
213     DEBUG(0,("load_client_codepage: filename %s does not exist.\n",
214               codepage_file_name));
215     return NULL;
216   }
217
218   /* Check if it is at least big enough to hold the required
219      data. Should be 2 byte version, 2 byte codepage, 4 byte length, 
220      plus zero or more bytes of data. Note that the data cannot be more
221      than 4 * MAXCODEPAGELINES bytes.
222    */
223   size = (unsigned int)st.st_size;
224
225   if( size < CODEPAGE_HEADER_SIZE || size > (CODEPAGE_HEADER_SIZE + 4 * MAXCODEPAGELINES))
226   {
227     DEBUG(0,("load_client_codepage: file %s is an incorrect size for a \
228 code page file.\n", codepage_file_name));
229     return NULL;
230   }
231
232   /* Read the first 8 bytes of the codepage file - check
233      the version number and code page number. All the data
234      is held in little endian format.
235    */
236
237   if((fp = fopen( codepage_file_name, "r")) == NULL)
238   {
239     DEBUG(0,("load_client_codepage: cannot open file %s. Error was %s\n",
240               codepage_file_name, strerror(errno)));
241     return NULL;
242   }
243
244   if(fread( buf, 1, CODEPAGE_HEADER_SIZE, fp)!=CODEPAGE_HEADER_SIZE)
245   {
246     DEBUG(0,("load_client_codepage: cannot read header from file %s. Error was %s\n",
247               codepage_file_name, strerror(errno)));
248     goto clean_and_exit;
249   }
250
251   /* Check the version value */
252   if(SVAL(buf,CODEPAGE_VERSION_OFFSET) != CODEPAGE_FILE_VERSION_ID)
253   {
254     DEBUG(0,("load_client_codepage: filename %s has incorrect version id. \
255 Needed %hu, got %hu.\n", 
256           codepage_file_name, (uint16)CODEPAGE_FILE_VERSION_ID, 
257           SVAL(buf,CODEPAGE_VERSION_OFFSET)));
258     goto clean_and_exit;
259   }
260
261   /* Check the codepage matches */
262   if(SVAL(buf,CODEPAGE_CLIENT_CODEPAGE_OFFSET) != (uint16)client_codepage)
263   {
264     DEBUG(0,("load_client_codepage: filename %s has incorrect codepage. \
265 Needed %hu, got %hu.\n", 
266            codepage_file_name, (uint16)client_codepage, 
267            SVAL(buf,CODEPAGE_CLIENT_CODEPAGE_OFFSET)));
268     goto clean_and_exit;
269   }
270
271   /* Check the length is correct. */
272   if(IVAL(buf,CODEPAGE_LENGTH_OFFSET) != 
273                  (unsigned int)(size - CODEPAGE_HEADER_SIZE))
274   {
275     DEBUG(0,("load_client_codepage: filename %s has incorrect size headers. \
276 Needed %u, got %u.\n", codepage_file_name, size - CODEPAGE_HEADER_SIZE, 
277                IVAL(buf,CODEPAGE_LENGTH_OFFSET)));
278     goto clean_and_exit;
279   }
280
281   size -= CODEPAGE_HEADER_SIZE; /* Remove header */
282
283   /* Make sure the size is a multiple of 4. */
284   if((size % 4 ) != 0)
285   {
286     DEBUG(0,("load_client_codepage: filename %s has a codepage size not a \
287 multiple of 4.\n", codepage_file_name));
288     goto clean_and_exit;
289   }
290
291   /* Allocate space for the code page file and read it all in. */
292   if((cp_p = (codepage_p)malloc( size  + 4 )) == NULL)
293   {
294     DEBUG(0,("load_client_codepage: malloc fail.\n"));
295     goto clean_and_exit;
296   }
297
298   if(fread( (char *)cp_p, 1, size, fp)!=size)
299   {
300     DEBUG(0,("load_client_codepage: read fail on file %s. Error was %s.\n",
301               codepage_file_name, strerror(errno)));
302     goto clean_and_exit;
303   }
304
305   /* Ensure array is correctly terminated. */
306   memset(((char *)cp_p) + size, '\0', 4);
307
308   fclose(fp);
309   return cp_p;
310
311 clean_and_exit:
312
313   /* pseudo destructor :-) */
314
315   if(fp != NULL)
316     fclose(fp);
317   if(cp_p)
318     free((char *)cp_p);
319   return NULL;
320 }
321
322 /****************************************************************************
323 initialise the client codepage.
324 ****************************************************************************/
325 void codepage_initialise(int client_codepage)
326 {
327   int i;
328   static codepage_p cp = NULL;
329
330   if(cp != NULL)
331   {
332     DEBUG(6,
333       ("codepage_initialise: called twice - ignoring second client code page = %d\n",
334       client_codepage));
335     return;
336   }
337
338   DEBUG(6,("codepage_initialise: client code page = %d\n", client_codepage));
339
340   /*
341    * Known client codepages - these can be added to.
342    */
343   cp = load_client_codepage( client_codepage );
344
345   if(cp == NULL)
346   {
347 #ifdef KANJI
348     DEBUG(6,("codepage_initialise: loading dynamic codepage file %s/codepage.%d \
349 for code page %d failed. Using default client codepage 932\n", 
350              CODEPAGEDIR, client_codepage, client_codepage));
351     cp = cp_932;
352     client_codepage = KANJI_CODEPAGE;
353 #else /* KANJI */
354     DEBUG(6,("codepage_initialise: loading dynamic codepage file %s/codepage.%d \
355 for code page %d failed. Using default client codepage 850\n", 
356              CODEPAGEDIR, client_codepage, client_codepage));
357     cp = cp_850;
358     client_codepage = MSDOS_LATIN_1_CODEPAGE;
359 #endif /* KANJI */
360   }
361
362   /*
363    * Setup the function pointers for the loaded codepage.
364    */
365   initialize_multibyte_vectors( client_codepage );
366
367   if(cp)
368   {
369     for(i = 0; !((cp[i][0] == '\0') && (cp[i][1] == '\0')); i++)
370       add_dos_char(cp[i][0], (BOOL)cp[i][2], cp[i][1], (BOOL)cp[i][3]);
371   }
372 }
373
374 /*******************************************************************
375 add characters depending on a string passed by the user
376 ********************************************************************/
377 void add_char_string(char *s)
378 {
379   char *extra_chars = (char *)strdup(s);
380   char *t;
381   if (!extra_chars) return;
382
383   for (t=strtok(extra_chars," \t\r\n"); t; t=strtok(NULL," \t\r\n")) {
384     char c1=0,c2=0;
385     int i1=0,i2=0;
386     if (isdigit((unsigned char)*t) || (*t)=='-') {
387       sscanf(t,"%i:%i",&i1,&i2);
388       add_dos_char(i1,True,i2,True);
389     } else {
390       sscanf(t,"%c:%c",&c1,&c2);
391       add_dos_char((unsigned char)c1,True,(unsigned char)c2, True);
392     }
393   }
394
395   free(extra_chars);
396 }