PowerPC64 --plt-align
[platform/upstream/binutils.git] / binutils / winduni.c
1 /* winduni.c -- unicode support for the windres program.
2    Copyright (C) 1997-2014 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Cygnus Support.
4    Rewritten by Kai Tietz, Onevision.
5
6    This file is part of GNU Binutils.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21    02110-1301, USA.  */
22
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
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-16LE" },
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\345l)", "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 ASCII string with length A_LENGTH to a unicode string.  We just
197    copy it, expanding chars to shorts, rather than doing something intelligent.
198    This routine converts also \0 within a string.  */
199
200 void
201 unicode_from_ascii_len (rc_uint_type *length, unichar **unicode, const char *ascii, rc_uint_type a_length)
202 {
203   char *tmp, *p;
204   rc_uint_type tlen, elen, idx = 0;
205
206   *unicode = NULL;
207
208   if (!a_length)
209     {
210       if (length)
211         *length = 0;
212       return;
213     }
214
215   /* Make sure we have zero terminated string.  */
216   p = tmp = (char *) alloca (a_length + 1);
217   memcpy (tmp, ascii, a_length);
218   tmp[a_length] = 0;
219
220   while (a_length > 0)
221     {
222       unichar *utmp, *up;
223
224       tlen = strlen (p);
225
226       if (tlen > a_length)
227         tlen = a_length;
228       if (*p == 0)
229         {
230           /* Make room for one more character.  */
231           utmp = (unichar *) res_alloc (sizeof (unichar) * (idx + 1));
232           if (idx > 0)
233             {
234               memcpy (utmp, *unicode, idx * sizeof (unichar));
235             }
236           *unicode = utmp;
237           utmp[idx++] = 0;
238           --a_length;
239           p++;
240           continue;
241         }
242       utmp = NULL;
243       elen = 0;
244       elen = wind_MultiByteToWideChar (wind_current_codepage, p, NULL, 0);
245       if (elen)
246         {
247           utmp = ((unichar *) res_alloc (elen + sizeof (unichar) * 2));
248           wind_MultiByteToWideChar (wind_current_codepage, p, utmp, elen);
249           elen /= sizeof (unichar);
250           elen --;
251         }
252       else
253         {
254           /* Make room for one more character.  */
255           utmp = (unichar *) res_alloc (sizeof (unichar) * (idx + 1));
256           if (idx > 0)
257             {
258               memcpy (utmp, *unicode, idx * sizeof (unichar));
259             }
260           *unicode = utmp;
261           utmp[idx++] = ((unichar) *p) & 0xff;
262           --a_length;
263           p++;
264           continue;
265         }
266       p += tlen;
267       a_length -= tlen;
268
269       up = (unichar *) res_alloc (sizeof (unichar) * (idx + elen));
270       if (idx > 0)
271         memcpy (up, *unicode, idx * sizeof (unichar));
272
273       *unicode = up;
274       if (elen)
275         memcpy (&up[idx], utmp, sizeof (unichar) * elen);
276
277       idx += elen;
278     }
279
280   if (length)
281     *length = idx;
282 }
283
284 /* Convert an unicode string to an ASCII string.  We just copy it,
285    shrink shorts to chars, rather than doing something intelligent.
286    Shorts with not within the char range are replaced by '_'.  */
287
288 void
289 ascii_from_unicode (rc_uint_type *length, const unichar *unicode, char **ascii)
290 {
291   codepage_from_unicode (length, unicode, ascii, wind_current_codepage);
292 }
293
294 /* Print the unicode string UNICODE to the file E.  LENGTH is the
295    number of characters to print, or -1 if we should print until the
296    end of the string.  FIXME: On a Windows host, we should be calling
297    some Windows function, probably WideCharToMultiByte.  */
298
299 void
300 unicode_print (FILE *e, const unichar *unicode, rc_uint_type length)
301 {
302   while (1)
303     {
304       unichar ch;
305
306       if (length == 0)
307         return;
308       if ((bfd_signed_vma) length > 0)
309         --length;
310
311       ch = *unicode;
312
313       if (ch == 0 && (bfd_signed_vma) length < 0)
314         return;
315
316       ++unicode;
317
318       if ((ch & 0x7f) == ch)
319         {
320           if (ch == '\\')
321             fputs ("\\\\", e);
322           else if (ch == '"')
323             fputs ("\"\"", e);
324           else if (ISPRINT (ch))
325             putc (ch, e);
326           else
327             {
328               switch (ch)
329                 {
330                 case ESCAPE_A:
331                   fputs ("\\a", e);
332                   break;
333
334                 case ESCAPE_B:
335                   fputs ("\\b", e);
336                   break;
337
338                 case ESCAPE_F:
339                   fputs ("\\f", e);
340                   break;
341
342                 case ESCAPE_N:
343                   fputs ("\\n", e);
344                   break;
345
346                 case ESCAPE_R:
347                   fputs ("\\r", e);
348                   break;
349
350                 case ESCAPE_T:
351                   fputs ("\\t", e);
352                   break;
353
354                 case ESCAPE_V:
355                   fputs ("\\v", e);
356                   break;
357
358                 default:
359                   fprintf (e, "\\%03o", (unsigned int) ch);
360                   break;
361                 }
362             }
363         }
364       else if ((ch & 0xff) == ch)
365         fprintf (e, "\\%03o", (unsigned int) ch);
366       else
367         fprintf (e, "\\x%04x", (unsigned int) ch);
368     }
369 }
370
371 /* Print a unicode string to a file.  */
372
373 void
374 ascii_print (FILE *e, const char *s, rc_uint_type length)
375 {
376   while (1)
377     {
378       char ch;
379
380       if (length == 0)
381         return;
382       if ((bfd_signed_vma) length > 0)
383         --length;
384
385       ch = *s;
386
387       if (ch == 0 && (bfd_signed_vma) length < 0)
388         return;
389
390       ++s;
391
392       if ((ch & 0x7f) == ch)
393         {
394           if (ch == '\\')
395             fputs ("\\\\", e);
396           else if (ch == '"')
397             fputs ("\"\"", e);
398           else if (ISPRINT (ch))
399             putc (ch, e);
400           else
401             {
402               switch (ch)
403                 {
404                 case ESCAPE_A:
405                   fputs ("\\a", e);
406                   break;
407
408                 case ESCAPE_B:
409                   fputs ("\\b", e);
410                   break;
411
412                 case ESCAPE_F:
413                   fputs ("\\f", e);
414                   break;
415
416                 case ESCAPE_N:
417                   fputs ("\\n", e);
418                   break;
419
420                 case ESCAPE_R:
421                   fputs ("\\r", e);
422                   break;
423
424                 case ESCAPE_T:
425                   fputs ("\\t", e);
426                   break;
427
428                 case ESCAPE_V:
429                   fputs ("\\v", e);
430                   break;
431
432                 default:
433                   fprintf (e, "\\%03o", (unsigned int) ch);
434                   break;
435                 }
436             }
437         }
438       else
439         fprintf (e, "\\%03o", (unsigned int) ch & 0xff);
440     }
441 }
442
443 rc_uint_type
444 unichar_len (const unichar *unicode)
445 {
446   rc_uint_type r = 0;
447
448   if (unicode)
449     while (unicode[r] != 0)
450       r++;
451   else
452     --r;
453   return r;
454 }
455
456 unichar *
457 unichar_dup (const unichar *unicode)
458 {
459   unichar *r;
460   int len;
461
462   if (! unicode)
463     return NULL;
464   for (len = 0; unicode[len] != 0; ++len)
465     ;
466   ++len;
467   r = ((unichar *) res_alloc (len * sizeof (unichar)));
468   memcpy (r, unicode, len * sizeof (unichar));
469   return r;
470 }
471
472 unichar *
473 unichar_dup_uppercase (const unichar *u)
474 {
475   unichar *r = unichar_dup (u);
476   int i;
477
478   if (! r)
479     return NULL;
480
481   for (i = 0; r[i] != 0; ++i)
482     {
483       if (r[i] >= 'a' && r[i] <= 'z')
484         r[i] &= 0xdf;
485     }
486   return r;
487 }
488
489 static int
490 unichar_isascii (const unichar *u, rc_uint_type len)
491 {
492   rc_uint_type i;
493
494   if ((bfd_signed_vma) len < 0)
495     {
496       if (u)
497         len = (rc_uint_type) unichar_len (u);
498       else
499         len = 0;
500     }
501
502   for (i = 0; i < len; i++)
503     if ((u[i] & 0xff80) != 0)
504       return 0;
505   return 1;
506 }
507
508 void
509 unicode_print_quoted (FILE *e, const unichar *u, rc_uint_type len)
510 {
511   if (! unichar_isascii (u, len))
512     fputc ('L', e);
513   fputc ('"', e);
514   unicode_print (e, u, len);
515   fputc ('"', e);
516 }
517
518 int
519 unicode_is_valid_codepage (rc_uint_type cp)
520 {
521   if ((cp & 0xffff) != cp)
522     return 0;
523   if (cp == CP_UTF16 || cp == CP_ACP)
524     return 1;
525
526 #if !defined (_WIN32) && !defined (__CYGWIN__)
527   if (! wind_find_codepage_info (cp))
528     return 0;
529   return 1;
530 #else
531   return !! IsValidCodePage ((UINT) cp);
532 #endif
533 }
534
535 #if defined (_WIN32) || defined (__CYGWIN__)
536
537 #define max_cp_string_len 6
538
539 static unsigned int
540 codepage_from_langid (unsigned short langid)
541 {
542   char cp_string [max_cp_string_len];
543   int c;
544
545   memset (cp_string, 0, max_cp_string_len);
546   /* LOCALE_RETURN_NUMBER flag would avoid strtoul conversion,
547      but is unavailable on Win95.  */
548   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
549                       LOCALE_IDEFAULTANSICODEPAGE,
550                       cp_string, max_cp_string_len);
551   /* If codepage data for an LCID is not installed on users's system,
552      GetLocaleInfo returns an empty string.  Fall back to system ANSI
553      default. */
554   if (c == 0)
555     return CP_ACP;
556   return strtoul (cp_string, 0, 10);
557 }
558
559 static unsigned int
560 wincodepage_from_langid (unsigned short langid)
561 {
562   char cp_string [max_cp_string_len];
563   int c;
564
565   memset (cp_string, 0, max_cp_string_len);
566   /* LOCALE_RETURN_NUMBER flag would avoid strtoul conversion,
567      but is unavailable on Win95.  */
568   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
569                       LOCALE_IDEFAULTCODEPAGE,
570                       cp_string, max_cp_string_len);
571   /* If codepage data for an LCID is not installed on users's system,
572      GetLocaleInfo returns an empty string.  Fall back to system ANSI
573      default. */
574   if (c == 0)
575     return CP_OEM;
576   return strtoul (cp_string, 0, 10);
577 }
578
579 static char *
580 lang_from_langid (unsigned short langid)
581 {
582   char cp_string[261];
583   int c;
584
585   memset (cp_string, 0, 261);
586   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
587                       LOCALE_SENGLANGUAGE,
588                       cp_string, 260);
589   /* If codepage data for an LCID is not installed on users's system,
590      GetLocaleInfo returns an empty string.  Fall back to system ANSI
591      default. */
592   if (c == 0)
593     strcpy (cp_string, "Neutral");
594   return xstrdup (cp_string);
595 }
596
597 static char *
598 country_from_langid (unsigned short langid)
599 {
600   char cp_string[261];
601   int c;
602
603   memset (cp_string, 0, 261);
604   c = GetLocaleInfoA (MAKELCID (langid, SORT_DEFAULT),
605                       LOCALE_SENGCOUNTRY,
606                       cp_string, 260);
607   /* If codepage data for an LCID is not installed on users's system,
608      GetLocaleInfo returns an empty string.  Fall back to system ANSI
609      default. */
610   if (c == 0)
611     strcpy (cp_string, "Neutral");
612   return xstrdup (cp_string);
613 }
614
615 #endif
616
617 const wind_language_t *
618 wind_find_language_by_id (unsigned id)
619 {
620 #if !defined (_WIN32) && !defined (__CYGWIN__)
621   int i;
622
623   if (! id)
624     return NULL;
625   for (i = 0; languages[i].id != (unsigned) -1 && languages[i].id != id; i++)
626     ;
627   if (languages[i].id == id)
628     return &languages[i];
629   return NULL;
630 #else
631   static wind_language_t wl;
632
633   wl.id = id;
634   wl.doscp = codepage_from_langid ((unsigned short) id);
635   wl.wincp = wincodepage_from_langid ((unsigned short) id);
636   wl.name = lang_from_langid ((unsigned short) id);
637   wl.country = country_from_langid ((unsigned short) id);
638
639   return & wl;
640 #endif
641 }
642
643 const local_iconv_map *
644 wind_find_codepage_info (unsigned cp)
645 {
646 #if !defined (_WIN32) && !defined (__CYGWIN__)
647   int i;
648
649   for (i = 0; codepages[i].codepage != (rc_uint_type) -1 && codepages[i].codepage != cp; i++)
650     ;
651   if (codepages[i].codepage == (rc_uint_type) -1)
652     return NULL;
653   return &codepages[i];
654 #else
655   static local_iconv_map lim;
656   if (!unicode_is_valid_codepage (cp))
657         return NULL;
658   lim.codepage = cp;
659   lim.iconv_name = "";
660   return & lim;
661 #endif
662 }
663
664 /* Convert an Codepage string to a unicode string.  */
665
666 void
667 unicode_from_codepage (rc_uint_type *length, unichar **u, const char *src, rc_uint_type cp)
668 {
669   rc_uint_type len;
670
671   len = wind_MultiByteToWideChar (cp, src, NULL, 0);
672   if (len)
673     {
674       *u = ((unichar *) res_alloc (len));
675       wind_MultiByteToWideChar (cp, src, *u, len);
676     }
677   /* Discount the trailing '/0'.  If MultiByteToWideChar failed,
678      this will set *length to -1.  */
679   len -= sizeof (unichar);
680
681   if (length != NULL)
682     *length = len / sizeof (unichar);
683 }
684
685 /* Convert an unicode string to an codepage string.  */
686
687 void
688 codepage_from_unicode (rc_uint_type *length, const unichar *unicode, char **ascii, rc_uint_type cp)
689 {
690   rc_uint_type len;
691
692   len = wind_WideCharToMultiByte (cp, unicode, NULL, 0);
693   if (len)
694     {
695       *ascii = (char *) res_alloc (len * sizeof (char));
696       wind_WideCharToMultiByte (cp, unicode, *ascii, len);
697     }
698   /* Discount the trailing '/0'.  If MultiByteToWideChar failed,
699      this will set *length to -1.  */
700   len--;
701
702   if (length != NULL)
703     *length = len;
704 }
705
706 #if defined (HAVE_ICONV) && !defined (_WIN32) && !defined (__CYGWIN__)
707 static int
708 iconv_onechar (iconv_t cd, ICONV_CONST char *s, char *d, int d_len, const char **n_s, char **n_d)
709 {
710   int i;
711
712   for (i = 1; i <= 32; i++)
713     {
714       char *tmp_d = d;
715       ICONV_CONST char *tmp_s = s;
716       size_t ret;
717       size_t s_left = (size_t) i;
718       size_t d_left = (size_t) d_len;
719
720       ret = iconv (cd, & tmp_s, & s_left, & tmp_d, & d_left);
721
722       if (ret != (size_t) -1)
723         {
724           *n_s = tmp_s;
725           *n_d = tmp_d;
726           return 0;
727         }
728     }
729
730   return 1;
731 }
732
733 static const char *
734 wind_iconv_cp (rc_uint_type cp)
735 {
736   const local_iconv_map *lim = wind_find_codepage_info (cp);
737
738   if (!lim)
739     return NULL;
740   return lim->iconv_name;
741 }
742 #endif /* HAVE_ICONV */
743
744 static rc_uint_type
745 wind_MultiByteToWideChar (rc_uint_type cp, const char *mb,
746                           unichar *u, rc_uint_type u_len)
747 {
748   rc_uint_type ret = 0;
749
750 #if defined (_WIN32) || defined (__CYGWIN__)
751   rc_uint_type conv_flags = MB_PRECOMPOSED;
752
753   /* MB_PRECOMPOSED is not allowed for UTF-7 or UTF-8.
754      MultiByteToWideChar will set the last error to
755      ERROR_INVALID_FLAGS if we do. */
756   if (cp == CP_UTF8 || cp == CP_UTF7)
757     conv_flags = 0;
758
759   ret = (rc_uint_type) MultiByteToWideChar (cp, conv_flags,
760                                             mb, -1, u, u_len);
761   /* Convert to bytes. */
762   ret *= sizeof (unichar);
763
764 #elif defined (HAVE_ICONV)
765   int first = 1;
766   char tmp[32];
767   char *p_tmp;
768   const char *iconv_name = wind_iconv_cp (cp);
769
770   if (!mb || !iconv_name)
771     return 0;
772   iconv_t cd = iconv_open ("UTF-16LE", iconv_name);
773
774   while (1)
775     {
776       int iret;
777       const char *n_mb = "";
778       char *n_tmp = "";
779
780       p_tmp = tmp;
781       iret = iconv_onechar (cd, (ICONV_CONST char *) mb, p_tmp, 32, & n_mb, & n_tmp);
782       if (first)
783         {
784           first = 0;
785           continue;
786         }
787       if (!iret)
788         {
789           size_t l_tmp = (size_t) (n_tmp - p_tmp);
790
791           if (u)
792             {
793               if ((size_t) u_len < l_tmp)
794                 break;
795               memcpy (u, tmp, l_tmp);
796               u += l_tmp/2;
797               u_len -= l_tmp;
798             }
799           ret += l_tmp;
800         }
801       else
802         break;
803       if (tmp[0] == 0 && tmp[1] == 0)
804         break;
805       mb = n_mb;
806     }
807   iconv_close (cd);
808 #else
809   if (cp)
810     ret = 0;
811   ret = strlen (mb) + 1;
812   ret *= sizeof (unichar);
813   if (u != NULL && u_len != 0)
814     {
815       do
816         {
817           *u++ = ((unichar) *mb) & 0xff;
818           --u_len; mb++;
819         }
820       while (u_len != 0 && mb[-1] != 0);
821     }
822   if (u != NULL && u_len != 0)
823     *u = 0;
824 #endif
825   return ret;
826 }
827
828 static rc_uint_type
829 wind_WideCharToMultiByte (rc_uint_type cp, const unichar *u, char *mb, rc_uint_type mb_len)
830 {
831   rc_uint_type ret = 0;
832 #if defined (_WIN32) || defined (__CYGWIN__)
833   WINBOOL used_def = FALSE;
834
835   ret = (rc_uint_type) WideCharToMultiByte (cp, 0, u, -1, mb, mb_len,
836                                             NULL, & used_def);
837 #elif defined (HAVE_ICONV)
838   int first = 1;
839   char tmp[32];
840   char *p_tmp;
841   const char *iconv_name = wind_iconv_cp (cp);
842
843   if (!u || !iconv_name)
844     return 0;
845   iconv_t cd = iconv_open (iconv_name, "UTF-16LE");
846
847   while (1)
848     {
849       int iret;
850       const char *n_u = "";
851       char *n_tmp = "";
852
853       p_tmp = tmp;
854       iret = iconv_onechar (cd, (ICONV_CONST char *) u, p_tmp, 32, &n_u, & n_tmp);
855       if (first)
856         {
857           first = 0;
858           continue;
859         }
860       if (!iret)
861         {
862           size_t l_tmp = (size_t) (n_tmp - p_tmp);
863
864           if (mb)
865             {
866               if ((size_t) mb_len < l_tmp)
867                 break;
868               memcpy (mb, tmp, l_tmp);
869               mb += l_tmp;
870               mb_len -= l_tmp;
871             }
872           ret += l_tmp;
873         }
874       else
875         break;
876       if (u[0] == 0)
877         break;
878       u = (const unichar *) n_u;
879     }
880   iconv_close (cd);
881 #else
882   if (cp)
883     ret = 0;
884
885   while (u[ret] != 0)
886     ++ret;
887
888   ++ret;
889
890   if (mb)
891     {
892       while (*u != 0 && mb_len != 0)
893         {
894           if (u[0] == (u[0] & 0x7f))
895             *mb++ = (char) u[0];
896           else
897             *mb++ = '_';
898           ++u; --mb_len;
899         }
900       if (mb_len != 0)
901         *mb = 0;
902     }
903 #endif
904   return ret;
905 }