Imported Upstream version 0.9.4
[platform/upstream/libunistring.git] / lib / localcharset.c
1 /* Determine a canonical name for the current locale's character encoding.
2
3    Copyright (C) 2000-2006, 2008-2014 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU Lesser General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public License along
16    with this program; if not, see <http://www.gnu.org/licenses/>.  */
17
18 /* Written by Bruno Haible <bruno@clisp.org>.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include "localcharset.h"
24
25 #include <fcntl.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #if defined __APPLE__ && defined __MACH__ && HAVE_LANGINFO_CODESET
32 # define DARWIN7 /* Darwin 7 or newer, i.e. Mac OS X 10.3 or newer */
33 #endif
34
35 #if defined _WIN32 || defined __WIN32__
36 # define WINDOWS_NATIVE
37 # include <locale.h>
38 #endif
39
40 #if defined __EMX__
41 /* Assume EMX program runs on OS/2, even if compiled under DOS.  */
42 # ifndef OS2
43 #  define OS2
44 # endif
45 #endif
46
47 #if !defined WINDOWS_NATIVE
48 # include <unistd.h>
49 # if HAVE_LANGINFO_CODESET
50 #  include <langinfo.h>
51 # else
52 #  if 0 /* see comment below */
53 #   include <locale.h>
54 #  endif
55 # endif
56 # ifdef __CYGWIN__
57 #  define WIN32_LEAN_AND_MEAN
58 #  include <windows.h>
59 # endif
60 #elif defined WINDOWS_NATIVE
61 # define WIN32_LEAN_AND_MEAN
62 # include <windows.h>
63 #endif
64 #if defined OS2
65 # define INCL_DOS
66 # include <os2.h>
67 #endif
68
69 /* For MB_CUR_MAX_L */
70 #if defined DARWIN7
71 # include <xlocale.h>
72 #endif
73
74 #if ENABLE_RELOCATABLE
75 # include "relocatable.h"
76 #else
77 # define relocate(pathname) (pathname)
78 #endif
79
80 /* Get LIBDIR.  */
81 #ifndef LIBDIR
82 # include "configmake.h"
83 #endif
84
85 /* Define O_NOFOLLOW to 0 on platforms where it does not exist.  */
86 #ifndef O_NOFOLLOW
87 # define O_NOFOLLOW 0
88 #endif
89
90 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
91   /* Native Windows, Cygwin, OS/2, DOS */
92 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
93 #endif
94
95 #ifndef DIRECTORY_SEPARATOR
96 # define DIRECTORY_SEPARATOR '/'
97 #endif
98
99 #ifndef ISSLASH
100 # define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
101 #endif
102
103 #if HAVE_DECL_GETC_UNLOCKED
104 # undef getc
105 # define getc getc_unlocked
106 #endif
107
108 /* The following static variable is declared 'volatile' to avoid a
109    possible multithread problem in the function get_charset_aliases. If we
110    are running in a threaded environment, and if two threads initialize
111    'charset_aliases' simultaneously, both will produce the same value,
112    and everything will be ok if the two assignments to 'charset_aliases'
113    are atomic. But I don't know what will happen if the two assignments mix.  */
114 #if __STDC__ != 1
115 # define volatile /* empty */
116 #endif
117 /* Pointer to the contents of the charset.alias file, if it has already been
118    read, else NULL.  Its format is:
119    ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0'  */
120 static const char * volatile charset_aliases;
121
122 /* Return a pointer to the contents of the charset.alias file.  */
123 static const char *
124 get_charset_aliases (void)
125 {
126   const char *cp;
127
128   cp = charset_aliases;
129   if (cp == NULL)
130     {
131 #if !(defined DARWIN7 || defined VMS || defined WINDOWS_NATIVE || defined __CYGWIN__)
132       const char *dir;
133       const char *base = "charset.alias";
134       char *file_name;
135
136       /* Make it possible to override the charset.alias location.  This is
137          necessary for running the testsuite before "make install".  */
138       dir = getenv ("CHARSETALIASDIR");
139       if (dir == NULL || dir[0] == '\0')
140         dir = relocate (LIBDIR);
141
142       /* Concatenate dir and base into freshly allocated file_name.  */
143       {
144         size_t dir_len = strlen (dir);
145         size_t base_len = strlen (base);
146         int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
147         file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
148         if (file_name != NULL)
149           {
150             memcpy (file_name, dir, dir_len);
151             if (add_slash)
152               file_name[dir_len] = DIRECTORY_SEPARATOR;
153             memcpy (file_name + dir_len + add_slash, base, base_len + 1);
154           }
155       }
156
157       if (file_name == NULL)
158         /* Out of memory.  Treat the file as empty.  */
159         cp = "";
160       else
161         {
162           int fd;
163
164           /* Open the file.  Reject symbolic links on platforms that support
165              O_NOFOLLOW.  This is a security feature.  Without it, an attacker
166              could retrieve parts of the contents (namely, the tail of the
167              first line that starts with "* ") of an arbitrary file by placing
168              a symbolic link to that file under the name "charset.alias" in
169              some writable directory and defining the environment variable
170              CHARSETALIASDIR to point to that directory.  */
171           fd = open (file_name,
172                      O_RDONLY | (HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0));
173           if (fd < 0)
174             /* File not found.  Treat it as empty.  */
175             cp = "";
176           else
177             {
178               FILE *fp;
179
180               fp = fdopen (fd, "r");
181               if (fp == NULL)
182                 {
183                   /* Out of memory.  Treat the file as empty.  */
184                   close (fd);
185                   cp = "";
186                 }
187               else
188                 {
189                   /* Parse the file's contents.  */
190                   char *res_ptr = NULL;
191                   size_t res_size = 0;
192
193                   for (;;)
194                     {
195                       int c;
196                       char buf1[50+1];
197                       char buf2[50+1];
198                       size_t l1, l2;
199                       char *old_res_ptr;
200
201                       c = getc (fp);
202                       if (c == EOF)
203                         break;
204                       if (c == '\n' || c == ' ' || c == '\t')
205                         continue;
206                       if (c == '#')
207                         {
208                           /* Skip comment, to end of line.  */
209                           do
210                             c = getc (fp);
211                           while (!(c == EOF || c == '\n'));
212                           if (c == EOF)
213                             break;
214                           continue;
215                         }
216                       ungetc (c, fp);
217                       if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
218                         break;
219                       l1 = strlen (buf1);
220                       l2 = strlen (buf2);
221                       old_res_ptr = res_ptr;
222                       if (res_size == 0)
223                         {
224                           res_size = l1 + 1 + l2 + 1;
225                           res_ptr = (char *) malloc (res_size + 1);
226                         }
227                       else
228                         {
229                           res_size += l1 + 1 + l2 + 1;
230                           res_ptr = (char *) realloc (res_ptr, res_size + 1);
231                         }
232                       if (res_ptr == NULL)
233                         {
234                           /* Out of memory. */
235                           res_size = 0;
236                           free (old_res_ptr);
237                           break;
238                         }
239                       strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
240                       strcpy (res_ptr + res_size - (l2 + 1), buf2);
241                     }
242                   fclose (fp);
243                   if (res_size == 0)
244                     cp = "";
245                   else
246                     {
247                       *(res_ptr + res_size) = '\0';
248                       cp = res_ptr;
249                     }
250                 }
251             }
252
253           free (file_name);
254         }
255
256 #else
257
258 # if defined DARWIN7
259       /* To avoid the trouble of installing a file that is shared by many
260          GNU packages -- many packaging systems have problems with this --,
261          simply inline the aliases here.  */
262       cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
263            "ISO8859-2" "\0" "ISO-8859-2" "\0"
264            "ISO8859-4" "\0" "ISO-8859-4" "\0"
265            "ISO8859-5" "\0" "ISO-8859-5" "\0"
266            "ISO8859-7" "\0" "ISO-8859-7" "\0"
267            "ISO8859-9" "\0" "ISO-8859-9" "\0"
268            "ISO8859-13" "\0" "ISO-8859-13" "\0"
269            "ISO8859-15" "\0" "ISO-8859-15" "\0"
270            "KOI8-R" "\0" "KOI8-R" "\0"
271            "KOI8-U" "\0" "KOI8-U" "\0"
272            "CP866" "\0" "CP866" "\0"
273            "CP949" "\0" "CP949" "\0"
274            "CP1131" "\0" "CP1131" "\0"
275            "CP1251" "\0" "CP1251" "\0"
276            "eucCN" "\0" "GB2312" "\0"
277            "GB2312" "\0" "GB2312" "\0"
278            "eucJP" "\0" "EUC-JP" "\0"
279            "eucKR" "\0" "EUC-KR" "\0"
280            "Big5" "\0" "BIG5" "\0"
281            "Big5HKSCS" "\0" "BIG5-HKSCS" "\0"
282            "GBK" "\0" "GBK" "\0"
283            "GB18030" "\0" "GB18030" "\0"
284            "SJIS" "\0" "SHIFT_JIS" "\0"
285            "ARMSCII-8" "\0" "ARMSCII-8" "\0"
286            "PT154" "\0" "PT154" "\0"
287          /*"ISCII-DEV" "\0" "?" "\0"*/
288            "*" "\0" "UTF-8" "\0";
289 # endif
290
291 # if defined VMS
292       /* To avoid the troubles of an extra file charset.alias_vms in the
293          sources of many GNU packages, simply inline the aliases here.  */
294       /* The list of encodings is taken from the OpenVMS 7.3-1 documentation
295          "Compaq C Run-Time Library Reference Manual for OpenVMS systems"
296          section 10.7 "Handling Different Character Sets".  */
297       cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
298            "ISO8859-2" "\0" "ISO-8859-2" "\0"
299            "ISO8859-5" "\0" "ISO-8859-5" "\0"
300            "ISO8859-7" "\0" "ISO-8859-7" "\0"
301            "ISO8859-8" "\0" "ISO-8859-8" "\0"
302            "ISO8859-9" "\0" "ISO-8859-9" "\0"
303            /* Japanese */
304            "eucJP" "\0" "EUC-JP" "\0"
305            "SJIS" "\0" "SHIFT_JIS" "\0"
306            "DECKANJI" "\0" "DEC-KANJI" "\0"
307            "SDECKANJI" "\0" "EUC-JP" "\0"
308            /* Chinese */
309            "eucTW" "\0" "EUC-TW" "\0"
310            "DECHANYU" "\0" "DEC-HANYU" "\0"
311            "DECHANZI" "\0" "GB2312" "\0"
312            /* Korean */
313            "DECKOREAN" "\0" "EUC-KR" "\0";
314 # endif
315
316 # if defined WINDOWS_NATIVE || defined __CYGWIN__
317       /* To avoid the troubles of installing a separate file in the same
318          directory as the DLL and of retrieving the DLL's directory at
319          runtime, simply inline the aliases here.  */
320
321       cp = "CP936" "\0" "GBK" "\0"
322            "CP1361" "\0" "JOHAB" "\0"
323            "CP20127" "\0" "ASCII" "\0"
324            "CP20866" "\0" "KOI8-R" "\0"
325            "CP20936" "\0" "GB2312" "\0"
326            "CP21866" "\0" "KOI8-RU" "\0"
327            "CP28591" "\0" "ISO-8859-1" "\0"
328            "CP28592" "\0" "ISO-8859-2" "\0"
329            "CP28593" "\0" "ISO-8859-3" "\0"
330            "CP28594" "\0" "ISO-8859-4" "\0"
331            "CP28595" "\0" "ISO-8859-5" "\0"
332            "CP28596" "\0" "ISO-8859-6" "\0"
333            "CP28597" "\0" "ISO-8859-7" "\0"
334            "CP28598" "\0" "ISO-8859-8" "\0"
335            "CP28599" "\0" "ISO-8859-9" "\0"
336            "CP28605" "\0" "ISO-8859-15" "\0"
337            "CP38598" "\0" "ISO-8859-8" "\0"
338            "CP51932" "\0" "EUC-JP" "\0"
339            "CP51936" "\0" "GB2312" "\0"
340            "CP51949" "\0" "EUC-KR" "\0"
341            "CP51950" "\0" "EUC-TW" "\0"
342            "CP54936" "\0" "GB18030" "\0"
343            "CP65001" "\0" "UTF-8" "\0";
344 # endif
345 #endif
346
347       charset_aliases = cp;
348     }
349
350   return cp;
351 }
352
353 /* Determine the current locale's character encoding, and canonicalize it
354    into one of the canonical names listed in config.charset.
355    The result must not be freed; it is statically allocated.
356    If the canonical name cannot be determined, the result is a non-canonical
357    name.  */
358
359 #ifdef STATIC
360 STATIC
361 #endif
362 const char *
363 locale_charset (void)
364 {
365   const char *codeset;
366   const char *aliases;
367
368 #if !(defined WINDOWS_NATIVE || defined OS2)
369
370 # if HAVE_LANGINFO_CODESET
371
372   /* Most systems support nl_langinfo (CODESET) nowadays.  */
373   codeset = nl_langinfo (CODESET);
374
375 #  ifdef __CYGWIN__
376   /* Cygwin < 1.7 does not have locales.  nl_langinfo (CODESET) always
377      returns "US-ASCII".  Return the suffix of the locale name from the
378      environment variables (if present) or the codepage as a number.  */
379   if (codeset != NULL && strcmp (codeset, "US-ASCII") == 0)
380     {
381       const char *locale;
382       static char buf[2 + 10 + 1];
383
384       locale = getenv ("LC_ALL");
385       if (locale == NULL || locale[0] == '\0')
386         {
387           locale = getenv ("LC_CTYPE");
388           if (locale == NULL || locale[0] == '\0')
389             locale = getenv ("LANG");
390         }
391       if (locale != NULL && locale[0] != '\0')
392         {
393           /* If the locale name contains an encoding after the dot, return
394              it.  */
395           const char *dot = strchr (locale, '.');
396
397           if (dot != NULL)
398             {
399               const char *modifier;
400
401               dot++;
402               /* Look for the possible @... trailer and remove it, if any.  */
403               modifier = strchr (dot, '@');
404               if (modifier == NULL)
405                 return dot;
406               if (modifier - dot < sizeof (buf))
407                 {
408                   memcpy (buf, dot, modifier - dot);
409                   buf [modifier - dot] = '\0';
410                   return buf;
411                 }
412             }
413         }
414
415       /* The Windows API has a function returning the locale's codepage as a
416          number: GetACP().  This encoding is used by Cygwin, unless the user
417          has set the environment variable CYGWIN=codepage:oem (which very few
418          people do).
419          Output directed to console windows needs to be converted (to
420          GetOEMCP() if the console is using a raster font, or to
421          GetConsoleOutputCP() if it is using a TrueType font).  Cygwin does
422          this conversion transparently (see winsup/cygwin/fhandler_console.cc),
423          converting to GetConsoleOutputCP().  This leads to correct results,
424          except when SetConsoleOutputCP has been called and a raster font is
425          in use.  */
426       sprintf (buf, "CP%u", GetACP ());
427       codeset = buf;
428     }
429 #  endif
430
431 # else
432
433   /* On old systems which lack it, use setlocale or getenv.  */
434   const char *locale = NULL;
435
436   /* But most old systems don't have a complete set of locales.  Some
437      (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
438      use setlocale here; it would return "C" when it doesn't support the
439      locale name the user has set.  */
440 #  if 0
441   locale = setlocale (LC_CTYPE, NULL);
442 #  endif
443   if (locale == NULL || locale[0] == '\0')
444     {
445       locale = getenv ("LC_ALL");
446       if (locale == NULL || locale[0] == '\0')
447         {
448           locale = getenv ("LC_CTYPE");
449           if (locale == NULL || locale[0] == '\0')
450             locale = getenv ("LANG");
451         }
452     }
453
454   /* On some old systems, one used to set locale = "iso8859_1". On others,
455      you set it to "language_COUNTRY.charset". In any case, we resolve it
456      through the charset.alias file.  */
457   codeset = locale;
458
459 # endif
460
461 #elif defined WINDOWS_NATIVE
462
463   static char buf[2 + 10 + 1];
464
465   /* The Windows API has a function returning the locale's codepage as
466      a number, but the value doesn't change according to what the
467      'setlocale' call specified.  So we use it as a last resort, in
468      case the string returned by 'setlocale' doesn't specify the
469      codepage.  */
470   char *current_locale = setlocale (LC_ALL, NULL);
471   char *pdot;
472
473   /* If they set different locales for different categories,
474      'setlocale' will return a semi-colon separated list of locale
475      values.  To make sure we use the correct one, we choose LC_CTYPE.  */
476   if (strchr (current_locale, ';'))
477     current_locale = setlocale (LC_CTYPE, NULL);
478
479   pdot = strrchr (current_locale, '.');
480   if (pdot)
481     sprintf (buf, "CP%s", pdot + 1);
482   else
483     {
484       /* The Windows API has a function returning the locale's codepage as a
485         number: GetACP().
486         When the output goes to a console window, it needs to be provided in
487         GetOEMCP() encoding if the console is using a raster font, or in
488         GetConsoleOutputCP() encoding if it is using a TrueType font.
489         But in GUI programs and for output sent to files and pipes, GetACP()
490         encoding is the best bet.  */
491       sprintf (buf, "CP%u", GetACP ());
492     }
493   codeset = buf;
494
495 #elif defined OS2
496
497   const char *locale;
498   static char buf[2 + 10 + 1];
499   ULONG cp[3];
500   ULONG cplen;
501
502   /* Allow user to override the codeset, as set in the operating system,
503      with standard language environment variables.  */
504   locale = getenv ("LC_ALL");
505   if (locale == NULL || locale[0] == '\0')
506     {
507       locale = getenv ("LC_CTYPE");
508       if (locale == NULL || locale[0] == '\0')
509         locale = getenv ("LANG");
510     }
511   if (locale != NULL && locale[0] != '\0')
512     {
513       /* If the locale name contains an encoding after the dot, return it.  */
514       const char *dot = strchr (locale, '.');
515
516       if (dot != NULL)
517         {
518           const char *modifier;
519
520           dot++;
521           /* Look for the possible @... trailer and remove it, if any.  */
522           modifier = strchr (dot, '@');
523           if (modifier == NULL)
524             return dot;
525           if (modifier - dot < sizeof (buf))
526             {
527               memcpy (buf, dot, modifier - dot);
528               buf [modifier - dot] = '\0';
529               return buf;
530             }
531         }
532
533       /* Resolve through the charset.alias file.  */
534       codeset = locale;
535     }
536   else
537     {
538       /* OS/2 has a function returning the locale's codepage as a number.  */
539       if (DosQueryCp (sizeof (cp), cp, &cplen))
540         codeset = "";
541       else
542         {
543           sprintf (buf, "CP%u", cp[0]);
544           codeset = buf;
545         }
546     }
547
548 #endif
549
550   if (codeset == NULL)
551     /* The canonical name cannot be determined.  */
552     codeset = "";
553
554   /* Resolve alias. */
555   for (aliases = get_charset_aliases ();
556        *aliases != '\0';
557        aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
558     if (strcmp (codeset, aliases) == 0
559         || (aliases[0] == '*' && aliases[1] == '\0'))
560       {
561         codeset = aliases + strlen (aliases) + 1;
562         break;
563       }
564
565   /* Don't return an empty string.  GNU libc and GNU libiconv interpret
566      the empty string as denoting "the locale's character encoding",
567      thus GNU libiconv would call this function a second time.  */
568   if (codeset[0] == '\0')
569     codeset = "ASCII";
570
571 #ifdef DARWIN7
572   /* Mac OS X sets MB_CUR_MAX to 1 when LC_ALL=C, and "UTF-8"
573      (the default codeset) does not work when MB_CUR_MAX is 1.  */
574   if (strcmp (codeset, "UTF-8") == 0 && MB_CUR_MAX_L (uselocale (NULL)) <= 1)
575     codeset = "ASCII";
576 #endif
577
578   return codeset;
579 }