* rclex.c: (cpp_line): Add code_page pragma support.
[external/binutils.git] / binutils / winduni.c
1 /* winduni.c -- unicode support for the windres program.
2    Copyright 1997, 1998, 2000, 2001, 2003, 2007
3    Free Software Foundation, Inc.
4    Written by Ian Lance Taylor, Cygnus Support.
5    Rewritten by Kai Tietz, Onevision.
6
7    This file is part of GNU Binutils.
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
22    02110-1301, USA.  */
23
24 /* This file contains unicode support routines for the windres
25    program.  Ideally, we would have generic unicode support which
26    would work on all systems.  However, we don't.  Instead, on a
27    Windows host, we are prepared to call some Windows routines.  This
28    means that we will generate different output on Windows and Unix
29    hosts, but that seems better than not really supporting unicode at
30    all.  */
31
32 #include "sysdep.h"
33 #include "bfd.h"
34 #include "libiberty.h" /* for xstrdup */
35 #include "bucomm.h"
36 /* Must be include before windows.h and winnls.h.  */
37 #if defined (_WIN32) || defined (__CYGWIN__)
38 #include <windows.h>
39 #include <winnls.h>
40 #endif
41 #include "winduni.h"
42 #include "safe-ctype.h"
43
44 #if HAVE_ICONV_H
45 #include <iconv.h>
46 #endif
47
48 static rc_uint_type wind_WideCharToMultiByte (rc_uint_type, const unichar *, char *, rc_uint_type);
49 static rc_uint_type wind_MultiByteToWideChar (rc_uint_type, const char *, unichar *, rc_uint_type);
50 static int unichar_isascii (const unichar *, rc_uint_type);
51
52 /* Convert an ASCII string to a unicode string.  We just copy it,
53    expanding chars to shorts, rather than doing something intelligent.  */
54  
55 #if !defined (_WIN32) && !defined (__CYGWIN__)
56
57 /* Codepages mapped.  */
58 static local_iconv_map codepages[] =
59 {
60   { 0, "MS-ANSI" },
61   { 1, "WINDOWS-1252" },
62   { 437, "MS-ANSI" },
63   { 737, "MS-GREEK" },
64   { 775, "WINBALTRIM" },
65   { 850, "MS-ANSI" },
66   { 852, "MS-EE" },
67   { 857, "MS-TURK" },
68   { 862, "CP862" },
69   { 864, "CP864" },
70   { 866, "MS-CYRL" },
71   { 874, "WINDOWS-874" },
72   { 932, "CP932" },
73   { 936, "CP936" },
74   { 949, "CP949" },
75   { 950, "CP950" },
76   { 1250, "WINDOWS-1250" },
77   { 1251, "WINDOWS-1251" },
78   { 1252, "WINDOWS-1252" },
79   { 1253, "WINDOWS-1253" },
80   { 1254, "WINDOWS-1254" },
81   { 1255, "WINDOWS-1255" },
82   { 1256, "WINDOWS-1256" },
83   { 1257, "WINDOWS-1257" },
84   { 1258, "WINDOWS-1258" },
85   { CP_UTF7, "UTF-7" },
86   { CP_UTF8, "UTF-8" },
87   { CP_UTF16, "UTF-16" },
88   { (rc_uint_type) -1, NULL }
89 };
90
91 /* Languages supported.  */
92 static const wind_language_t languages[] =
93 {
94   { 0x0000, 437, 1252, "Neutral", "Neutral" },
95   { 0x0401, 864, 1256, "Arabic", "Saudi Arabia" },    { 0x0402, 866, 1251, "Bulgarian", "Bulgaria" },
96   { 0x0403, 850, 1252, "Catalan", "Spain" },          { 0x0404, 950,  950, "Chinese", "Taiwan" },
97   { 0x0405, 852, 1250, "Czech", "Czech Republic" },   { 0x0406, 850, 1252, "Danish", "Denmark" },
98   { 0x0407, 850, 1252, "German", "Germany" },         { 0x0408, 737, 1253, "Greek", "Greece" },
99   { 0x0409, 437, 1252, "English", "United States" },  { 0x040A, 850, 1252, "Spanish - Traditional Sort", "Spain" },
100   { 0x040B, 850, 1252, "Finnish", "Finland" },        { 0x040C, 850, 1252, "French", "France" },
101   { 0x040D, 862, 1255, "Hebrew", "Israel" },          { 0x040E, 852, 1250, "Hungarian", "Hungary" },
102   { 0x040F, 850, 1252, "Icelandic", "Iceland" },      { 0x0410, 850, 1252, "Italian", "Italy" },
103   { 0x0411, 932,  932, "Japanese", "Japan" },         { 0x0412, 949,  949, "Korean", "Korea (south)" },
104   { 0x0413, 850, 1252, "Dutch", "Netherlands" },      { 0x0414, 850, 1252, "Norwegian (BokmÃ¥l)", "Norway" },
105   { 0x0415, 852, 1250, "Polish", "Poland" },          { 0x0416, 850, 1252, "Portuguese", "Brazil" },
106   { 0x0418, 852, 1250, "Romanian", "Romania" },       { 0x0419, 866, 1251, "Russian", "Russia" },
107   { 0x041A, 852, 1250, "Croatian", "Croatia" },       { 0x041B, 852, 1250, "Slovak", "Slovakia" },
108   { 0x041C, 852, 1250, "Albanian", "Albania" },       { 0x041D, 850, 1252, "Swedish", "Sweden" },
109   { 0x041E, 874,  874, "Thai", "Thailand" },          { 0x041F, 857, 1254, "Turkish", "Turkey" },
110   { 0x0421, 850, 1252, "Indonesian", "Indonesia" },   { 0x0422, 866, 1251, "Ukrainian", "Ukraine" },
111   { 0x0423, 866, 1251, "Belarusian", "Belarus" },     { 0x0424, 852, 1250, "Slovene", "Slovenia" },
112   { 0x0425, 775, 1257, "Estonian", "Estonia" },       { 0x0426, 775, 1257, "Latvian", "Latvia" },
113   { 0x0427, 775, 1257, "Lithuanian", "Lithuania" },
114   { 0x0429, 864, 1256, "Arabic", "Farsi" },           { 0x042A,1258, 1258, "Vietnamese", "Vietnam" },
115   { 0x042D, 850, 1252, "Basque", "Spain" },
116   { 0x042F, 866, 1251, "Macedonian", "Former Yugoslav Republic of Macedonia" },
117   { 0x0436, 850, 1252, "Afrikaans", "South Africa" },
118   { 0x0438, 850, 1252, "Faroese", "Faroe Islands" },
119   { 0x043C, 437, 1252, "Irish", "Ireland" },
120   { 0x043E, 850, 1252, "Malay", "Malaysia" },
121   { 0x0801, 864, 1256, "Arabic", "Iraq" },
122   { 0x0804, 936,  936, "Chinese (People's republic of China)", "People's republic of China" },
123   { 0x0807, 850, 1252, "German", "Switzerland" },
124   { 0x0809, 850, 1252, "English", "United Kingdom" }, { 0x080A, 850, 1252, "Spanish", "Mexico" },
125   { 0x080C, 850, 1252, "French", "Belgium" },
126   { 0x0810, 850, 1252, "Italian", "Switzerland" },
127   { 0x0813, 850, 1252, "Dutch", "Belgium" },          { 0x0814, 850, 1252, "Norwegian (Nynorsk)", "Norway" },
128   { 0x0816, 850, 1252, "Portuguese", "Portugal" },
129   { 0x081A, 852, 1252, "Serbian (latin)", "Yugoslavia" },
130   { 0x081D, 850, 1252, "Swedish (Finland)", "Finland" },
131   { 0x0C01, 864, 1256, "Arabic", "Egypt" },
132   { 0x0C04, 950,  950, "Chinese", "Hong Kong" },
133   { 0x0C07, 850, 1252, "German", "Austria" },
134   { 0x0C09, 850, 1252, "English", "Australia" },      { 0x0C0A, 850, 1252, "Spanish - International Sort", "Spain" },
135   { 0x0C0C, 850, 1252, "French", "Canada"},
136   { 0x0C1A, 855, 1251, "Serbian (Cyrillic)", "Serbia" },
137   { 0x1001, 864, 1256, "Arabic", "Libya" },
138   { 0x1004, 936,  936, "Chinese", "Singapore" },
139   { 0x1007, 850, 1252, "German", "Luxembourg" },
140   { 0x1009, 850, 1252, "English", "Canada" },
141   { 0x100A, 850, 1252, "Spanish", "Guatemala" },
142   { 0x100C, 850, 1252, "French", "Switzerland" },
143   { 0x1401, 864, 1256, "Arabic", "Algeria" },
144   { 0x1407, 850, 1252, "German", "Liechtenstein" },
145   { 0x1409, 850, 1252, "English", "New Zealand" },    { 0x140A, 850, 1252, "Spanish", "Costa Rica" },
146   { 0x140C, 850, 1252, "French", "Luxembourg" },
147   { 0x1801, 864, 1256, "Arabic", "Morocco" },
148   { 0x1809, 850, 1252, "English", "Ireland" },        { 0x180A, 850, 1252, "Spanish", "Panama" },
149   { 0x180C, 850, 1252, "French", "Monaco" },
150   { 0x1C01, 864, 1256, "Arabic", "Tunisia" },
151   { 0x1C09, 437, 1252, "English", "South Africa" },   { 0x1C0A, 850, 1252, "Spanish", "Dominican Republic" },
152   { 0x2001, 864, 1256, "Arabic", "Oman" },
153   { 0x2009, 850, 1252, "English", "Jamaica" },        { 0x200A, 850, 1252, "Spanish", "Venezuela" },
154   { 0x2401, 864, 1256, "Arabic", "Yemen" },
155   { 0x2409, 850, 1252, "English", "Caribbean" },      { 0x240A, 850, 1252, "Spanish", "Colombia" },
156   { 0x2801, 864, 1256, "Arabic", "Syria" },
157   { 0x2809, 850, 1252, "English", "Belize" },         { 0x280A, 850, 1252, "Spanish", "Peru" },
158   { 0x2C01, 864, 1256, "Arabic", "Jordan" },
159   { 0x2C09, 437, 1252, "English", "Trinidad & Tobago" },{ 0x2C0A, 850, 1252, "Spanish", "Argentina" },
160   { 0x3001, 864, 1256, "Arabic", "Lebanon" },
161   { 0x3009, 437, 1252, "English", "Zimbabwe" },       { 0x300A, 850, 1252, "Spanish", "Ecuador" },
162   { 0x3401, 864, 1256, "Arabic", "Kuwait" },
163   { 0x3409, 437, 1252, "English", "Philippines" },    { 0x340A, 850, 1252, "Spanish", "Chile" },
164   { 0x3801, 864, 1256, "Arabic", "United Arab Emirates" },
165   { 0x380A, 850, 1252, "Spanish", "Uruguay" },
166   { 0x3C01, 864, 1256, "Arabic", "Bahrain" },
167   { 0x3C0A, 850, 1252, "Spanish", "Paraguay" },
168   { 0x4001, 864, 1256, "Arabic", "Qatar" },
169   { 0x400A, 850, 1252, "Spanish", "Bolivia" },
170   { 0x440A, 850, 1252, "Spanish", "El Salvador" },
171   { 0x480A, 850, 1252, "Spanish", "Honduras" },
172   { 0x4C0A, 850, 1252, "Spanish", "Nicaragua" },
173   { 0x500A, 850, 1252, "Spanish", "Puerto Rico" },
174   { (unsigned) -1,  0,      0, NULL, NULL }
175 };
176
177 #endif
178
179 /* Specifies the default codepage to be used for unicode
180    transformations.  By default this is CP_ACP.  */
181 rc_uint_type wind_default_codepage = CP_ACP;
182
183 /* Specifies the currently used codepage for unicode
184    transformations.  By default this is CP_ACP.  */
185 rc_uint_type wind_current_codepage = CP_ACP;
186
187 /* Convert an ASCII string to a unicode string.  We just copy it,
188    expanding chars to shorts, rather than doing something intelligent.  */
189
190 void
191 unicode_from_ascii (rc_uint_type *length, unichar **unicode, const char *ascii)
192 {
193   unicode_from_codepage (length, unicode, ascii, wind_current_codepage);
194 }
195
196 /* Convert an unicode string to an ASCII string.  We just copy it,
197    shrink shorts to chars, rather than doing something intelligent.
198    Shorts with not within the char range are replaced by '_'.  */
199
200 void
201 ascii_from_unicode (rc_uint_type *length, const unichar *unicode, char **ascii)
202 {
203   codepage_from_unicode (length, unicode, ascii, wind_current_codepage);
204 }
205
206 /* Print the unicode string UNICODE to the file E.  LENGTH is the
207    number of characters to print, or -1 if we should print until the
208    end of the string.  FIXME: On a Windows host, we should be calling
209    some Windows function, probably WideCharToMultiByte.  */
210
211 void
212 unicode_print (FILE *e, const unichar *unicode, rc_uint_type length)
213 {
214   while (1)
215     {
216       unichar ch;
217
218       if (length == 0)
219         return;
220       if ((bfd_signed_vma) length > 0)
221         --length;
222
223       ch = *unicode;
224
225       if (ch == 0 && (bfd_signed_vma) length < 0)
226         return;
227
228       ++unicode;
229
230       if ((ch & 0x7f) == ch)
231         {
232           if (ch == '\\')
233             fputs ("\\\\", e);
234           else if (ch == '"')
235             fputs ("\"\"", e);
236           else if (ISPRINT (ch))
237             putc (ch, e);
238           else
239             {
240               switch (ch)
241                 {
242                 case ESCAPE_A:
243                   fputs ("\\a", e);
244                   break;
245
246                 case ESCAPE_B:
247                   fputs ("\\b", e);
248                   break;
249
250                 case ESCAPE_F:
251                   fputs ("\\f", e);
252                   break;
253
254                 case ESCAPE_N:
255                   fputs ("\\n", e);
256                   break;
257
258                 case ESCAPE_R:
259                   fputs ("\\r", e);
260                   break;
261
262                 case ESCAPE_T:
263                   fputs ("\\t", e);
264                   break;
265
266                 case ESCAPE_V:
267                   fputs ("\\v", e);
268                   break;
269
270                 default:
271                   fprintf (e, "\\%03o", (unsigned int) ch);
272                   break;
273                 }
274             }
275         }
276       else if ((ch & 0xff) == ch)
277         fprintf (e, "\\%03o", (unsigned int) ch);
278       else
279         fprintf (e, "\\x%04x", (unsigned int) ch);
280     }
281 }
282
283 /* Print a unicode string to a file.  */
284
285 void
286 ascii_print (FILE *e, const char *s, rc_uint_type length)
287 {
288   while (1)
289     {
290       char ch;
291
292       if (length == 0)
293         return;
294       if ((bfd_signed_vma) length > 0)
295         --length;
296
297       ch = *s;
298
299       if (ch == 0 && (bfd_signed_vma) length < 0)
300         return;
301
302       ++s;
303
304       if ((ch & 0x7f) == ch)
305         {
306           if (ch == '\\')
307             fputs ("\\\\", e);
308           else if (ch == '"')
309             fputs ("\"\"", e);
310           else if (ISPRINT (ch))
311             putc (ch, e);
312           else
313             {
314               switch (ch)
315                 {
316                 case ESCAPE_A:
317                   fputs ("\\a", e);
318                   break;
319
320                 case ESCAPE_B:
321                   fputs ("\\b", e);
322                   break;
323
324                 case ESCAPE_F:
325                   fputs ("\\f", e);
326                   break;
327
328                 case ESCAPE_N:
329                   fputs ("\\n", e);
330                   break;
331
332                 case ESCAPE_R:
333                   fputs ("\\r", e);
334                   break;
335
336                 case ESCAPE_T:
337                   fputs ("\\t", e);
338                   break;
339
340                 case ESCAPE_V:
341                   fputs ("\\v", e);
342                   break;
343
344                 default:
345                   fprintf (e, "\\%03o", (unsigned int) ch);
346                   break;
347                 }
348             }
349         }
350       else
351         fprintf (e, "\\%03o", (unsigned int) ch & 0xff);
352     }
353 }
354
355 rc_uint_type
356 unichar_len (const unichar *unicode)
357 {
358   rc_uint_type r = 0;
359
360   if (unicode)
361     while (unicode[r] != 0)
362       r++;
363   else
364     --r;
365   return r;
366 }
367
368 unichar *
369 unichar_dup (const unichar *unicode)
370 {
371   unichar *r;
372   int len;
373
374   if (! unicode)
375     return NULL;
376   for (len = 0; unicode[len] != 0; ++len)
377     ;
378   ++len;
379   r = ((unichar *) res_alloc (len * sizeof (unichar)));
380   memcpy (r, unicode, len * sizeof (unichar));
381   return r;
382 }
383
384 unichar *
385 unichar_dup_uppercase (const unichar *u)
386 {
387   unichar *r = unichar_dup (u);
388   int i;
389
390   if (! r)
391     return NULL;
392
393   for (i = 0; r[i] != 0; ++i)
394     {
395       if (r[i] >= 'a' && r[i] <= 'z')
396         r[i] &= 0xdf;
397     }
398   return r;
399 }
400
401 static int
402 unichar_isascii (const unichar *u, rc_uint_type len)
403 {
404   rc_uint_type i;
405
406   if ((bfd_signed_vma) len < 0)
407     {
408       if (u)
409         len = (rc_uint_type) unichar_len (u);
410       else
411         len = 0;
412     }
413
414   for (i = 0; i < len; i++)
415     if ((u[i] & 0xff80) != 0)
416       return 0;
417   return 1;
418 }
419
420 void
421 unicode_print_quoted (FILE *e, const unichar *u, rc_uint_type len)
422 {
423   if (! unichar_isascii (u, len))
424     fputc ('L', e);
425   fputc ('"', e);
426   unicode_print (e, u, len);
427   fputc ('"', e);
428 }
429
430 int
431 unicode_is_valid_codepage (rc_uint_type cp)
432 {
433   if ((cp & 0xffff) != cp)
434     return 0;
435   if (cp == CP_UTF16 || cp == CP_ACP)
436     return 1;
437
438 #if !defined (_WIN32) && !defined (__CYGWIN__)
439   if (! wind_find_codepage_info (cp))
440     return 0;
441   return 1;
442 #else
443   return !! IsValidCodePage ((UINT) cp);
444 #endif
445 }
446
447 #if defined (_WIN32) || defined (__CYGWIN__)
448
449 #define max_cp_string_len 6
450
451 static unsigned int
452 codepage_from_langid (unsigned short langid)
453 {
454   char cp_string [max_cp_string_len];
455   int c;
456
457   memset (cp_string, 0, max_cp_string_len);
458   /* LOCALE_RETURN_NUMBER flag would avoid strtoul conversion,
459      but is unavailable on Win95.  */
460   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
461                       LOCALE_IDEFAULTANSICODEPAGE,
462                       cp_string, max_cp_string_len);
463   /* If codepage data for an LCID is not installed on users's system,
464      GetLocaleInfo returns an empty string.  Fall back to system ANSI
465      default. */
466   if (c == 0)
467     return CP_ACP;
468   return strtoul (cp_string, 0, 10);
469 }
470
471 static unsigned int
472 wincodepage_from_langid (unsigned short langid)
473 {
474   char cp_string [max_cp_string_len];
475   int c;
476
477   memset (cp_string, 0, max_cp_string_len);
478   /* LOCALE_RETURN_NUMBER flag would avoid strtoul conversion,
479      but is unavailable on Win95.  */
480   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
481                       LOCALE_IDEFAULTCODEPAGE,
482                       cp_string, max_cp_string_len);
483   /* If codepage data for an LCID is not installed on users's system,
484      GetLocaleInfo returns an empty string.  Fall back to system ANSI
485      default. */
486   if (c == 0)
487     return CP_OEM;
488   return strtoul (cp_string, 0, 10);
489 }
490
491 static char *
492 lang_from_langid (unsigned short langid)
493 {
494   char cp_string[261];
495   int c;
496
497   memset (cp_string, 0, 261);
498   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
499                       LOCALE_SENGLANGUAGE,
500                       cp_string, 260);
501   /* If codepage data for an LCID is not installed on users's system,
502      GetLocaleInfo returns an empty string.  Fall back to system ANSI
503      default. */
504   if (c == 0)
505     strcpy (cp_string, "Neutral");
506   return xstrdup (cp_string);
507 }
508
509 static char *
510 country_from_langid (unsigned short langid)
511 {
512   char cp_string[261];
513   int c;
514
515   memset (cp_string, 0, 261);
516   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
517                       LOCALE_SENGCOUNTRY,
518                       cp_string, 260);
519   /* If codepage data for an LCID is not installed on users's system,
520      GetLocaleInfo returns an empty string.  Fall back to system ANSI
521      default. */
522   if (c == 0)
523     strcpy (cp_string, "Neutral");
524   return xstrdup (cp_string);
525 }
526
527 #endif
528
529 const wind_language_t *
530 wind_find_language_by_id (unsigned id)
531 {
532 #if !defined (_WIN32) && !defined (__CYGWIN__)
533   int i;
534
535   if (! id)
536     return NULL;
537   for (i = 0; languages[i].id != (unsigned) -1 && languages[i].id != id; i++)
538     ;
539   if (languages[i].id == id)
540     return &languages[i];
541   return NULL;
542 #else
543   static wind_language_t wl;
544
545   wl.id = id;
546   wl.doscp = codepage_from_langid ((unsigned short) id);
547   wl.wincp = wincodepage_from_langid ((unsigned short) id);
548   wl.name = lang_from_langid ((unsigned short) id);
549   wl.country = country_from_langid ((unsigned short) id);
550
551   return & wl;
552 #endif
553 }
554
555 const local_iconv_map *
556 wind_find_codepage_info (unsigned cp)
557 {
558 #if !defined (_WIN32) && !defined (__CYGWIN__)
559   int i;
560
561   for (i = 0; codepages[i].codepage != (rc_uint_type) -1 && codepages[i].codepage != cp; i++)
562     ;
563   if (codepages[i].codepage == (rc_uint_type) -1)
564     return NULL;
565   return &codepages[i];
566 #else
567   static local_iconv_map lim;
568   if (!unicode_is_valid_codepage (cp))
569         return NULL;
570   lim.codepage = cp;
571   lim.iconv_name = "";
572   return & lim;
573 #endif
574 }
575
576 /* Convert an Codepage string to a unicode string.  */
577
578 void
579 unicode_from_codepage (rc_uint_type *length, unichar **u, const char *src, rc_uint_type cp)
580 {
581   rc_uint_type len;
582
583   len = wind_MultiByteToWideChar (cp, src, NULL, 0);
584   if (len)
585     {
586       *u = ((unichar *) res_alloc (len));
587       wind_MultiByteToWideChar (cp, src, *u, len);
588     }
589   /* Discount the trailing '/0'.  If MultiByteToWideChar failed,
590      this will set *length to -1.  */
591   len -= sizeof (unichar);
592
593   if (length != NULL)
594     *length = len / sizeof (unichar);
595 }
596
597 /* Convert an unicode string to an codepage string.  */
598
599 void
600 codepage_from_unicode (rc_uint_type *length, const unichar *unicode, char **ascii, rc_uint_type cp)
601 {
602   rc_uint_type len;
603
604   len = wind_WideCharToMultiByte (cp, unicode, NULL, 0);
605   if (len)
606     {
607       *ascii = (char *) res_alloc (len * sizeof (char));
608       wind_WideCharToMultiByte (cp, unicode, *ascii, len);
609     }
610   /* Discount the trailing '/0'.  If MultiByteToWideChar failed,
611      this will set *length to -1.  */
612   len--;
613
614   if (length != NULL)
615     *length = len;
616 }
617
618 #ifdef HAVE_ICONV_H
619 static int
620 iconv_onechar (iconv_t cd, const char *s, char *d, int d_len, const char **n_s, char **n_d)
621 {
622   int i;
623
624   for (i = 1; i <= 32; i++)
625     {
626       char *tmp_d = d;
627       const char *tmp_s = s;
628       size_t ret;
629       size_t s_left = (size_t) i;
630       size_t d_left = (size_t) d_len;
631
632       ret = iconv (cd, & tmp_s, & s_left, & tmp_d, & d_left);
633
634       if (ret != (size_t) -1)
635         {
636           *n_s = tmp_s;
637           *n_d = tmp_d;
638           return 0;
639         }
640     }
641
642   return 1;
643 }
644
645 static const char *
646 wind_iconv_cp (rc_uint_type cp)
647 {
648   const local_iconv_map *lim = wind_find_codepage_info (cp);
649
650   if (!lim)
651     return NULL;
652   return lim->iconv_name;
653 }
654 #endif /* HAVE_ICONV_H */
655
656 static rc_uint_type
657 wind_MultiByteToWideChar (rc_uint_type cp, const char *mb,
658                           unichar *u, rc_uint_type u_len)
659 {
660   rc_uint_type ret = 0;
661
662 #if defined (_WIN32) || defined (__CYGWIN__)
663   ret = (rc_uint_type) MultiByteToWideChar (cp, MB_PRECOMPOSED,
664                                             mb, -1, u, u_len);
665   /* Convert to bytes. */
666   ret *= sizeof (unichar);
667
668 #elif defined (HAVE_ICONV_H)
669   int first = 1;
670   char tmp[32];
671   char *p_tmp;
672   const char *iconv_name = wind_iconv_cp (cp);
673
674   if (!mb || !iconv_name)
675     return 0;
676   iconv_t cd = iconv_open ("UTF-16", iconv_name);
677
678   while (1)
679     {
680       int iret;
681       const char *n_mb;
682       char *n_tmp;
683
684       p_tmp = tmp;
685       iret = iconv_onechar (cd, (const char *) mb, p_tmp, 32, & n_mb, & n_tmp);
686       if (first)
687         {
688           first = 0;
689           continue;
690         }
691       if (!iret)
692         {
693           size_t l_tmp = (size_t) (n_tmp - p_tmp);
694
695           if (u)
696             {
697               if ((size_t) u_len < l_tmp)
698                 break;
699               memcpy (u, tmp, l_tmp);
700               u += l_tmp/2;
701               u_len -= l_tmp;
702             }
703           ret += l_tmp;
704         }
705       else
706         break;
707       if (tmp[0] == 0 && tmp[1] == 0)
708         break;
709       mb = n_mb;
710     }
711   iconv_close (cd);
712 #else
713   if (cp)
714     ret = 0;
715   ret = strlen (mb) + 1;
716   ret *= sizeof (unichar);
717   if (u != NULL && u_len != 0)
718     {
719       do
720         {
721           *u++ = ((unichar) *mb) & 0xff;
722           --u_len; mb++;
723         }
724       while (u_len != 0 && mb[-1] != 0);
725     }
726   if (u != NULL && u_len != 0)
727     *u = 0;
728 #endif
729   return ret;
730 }
731
732 static rc_uint_type
733 wind_WideCharToMultiByte (rc_uint_type cp, const unichar *u, char *mb, rc_uint_type mb_len)
734 {
735   rc_uint_type ret = 0;
736 #if defined (_WIN32) || defined (__CYGWIN__)
737   WINBOOL used_def = FALSE;
738
739   ret = (rc_uint_type) WideCharToMultiByte (cp, 0, u, -1, mb, mb_len,
740                                             NULL, & used_def);
741 #elif defined (HAVE_ICONV_H)
742   int first = 1;
743   char tmp[32];
744   char *p_tmp;
745   const char *iconv_name = wind_iconv_cp (cp);
746
747   if (!u || !iconv_name)
748     return 0;
749   iconv_t cd = iconv_open (iconv_name, "UTF-16");
750
751   while (1)
752     {
753       int iret;
754       const char *n_u;
755       char *n_tmp;
756
757       p_tmp = tmp;
758       iret = iconv_onechar (cd, (const char *) u, p_tmp, 32, &n_u, & n_tmp);
759       if (first)
760         {
761           first = 0;
762           continue;
763         }
764       if (!iret)
765         {
766           size_t l_tmp = (size_t) (n_tmp - p_tmp);
767
768           if (mb)
769             {
770               if ((size_t) mb_len < l_tmp)
771                 break;
772               memcpy (mb, tmp, l_tmp);
773               mb += l_tmp;
774               mb_len -= l_tmp;
775             }
776           ret += l_tmp;
777         }
778       else
779         break;
780       if (u[0] == 0)
781         break;
782       u = (const unichar *) n_u;
783     }
784   iconv_close (cd);
785 #else
786   if (cp)
787     ret = 0;
788
789   while (u[ret] != 0)
790     ++ret;
791
792   ++ret;
793
794   if (mb)
795     {
796       while (*u != 0 && mb_len != 0)
797         {
798           if (u[0] == (u[0] & 0x7f))
799             *mb++ = (char) u[0];
800           else
801             *mb++ = '_';
802           ++u; --mb_len;
803         }
804       if (mb_len != 0)
805         *mb = 0;
806     }
807 #endif
808   return ret;
809 }