Change argument type to 'unsigned long'.
[gnulib.git] / lib / localcharset.c
index 936d93d..2b1564e 100644 (file)
@@ -1,6 +1,6 @@
 /* Determine a canonical name for the current locale's character encoding.
 
-   Copyright (C) 2000 Free Software Foundation, Inc.
+   Copyright (C) 2000-2002 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published
@@ -17,7 +17,7 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    USA.  */
 
-/* Written by Bruno Haible <haible@clisp.cons.org>.  */
+/* Written by Bruno Haible <bruno@clisp.org>.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 # define WIN32
 #endif
 
-#ifndef WIN32
+#if defined __EMX__
+/* Assume EMX program runs on OS/2, even if compiled under DOS.  */
+# define OS2
+#endif
+
+#if !defined WIN32
 # if HAVE_LANGINFO_CODESET
 #  include <langinfo.h>
 # else
 #   include <locale.h>
 #  endif
 # endif
-#else /* WIN32 */
+#elif defined WIN32
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
 #endif
+#if defined OS2
+# define INCL_DOS
+# include <os2.h>
+#endif
+
+#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
+  /* Win32, OS/2, DOS */
+# define ISSLASH(C) ((C) == '/' || (C) == '\\')
+#endif
 
 #ifndef DIRECTORY_SEPARATOR
 # define DIRECTORY_SEPARATOR '/'
 # define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
 #endif
 
+#ifdef HAVE_GETC_UNLOCKED
+# undef getc
+# define getc getc_unlocked
+#endif
+
+#ifdef __cplusplus
+/* When compiling with "gcc -x c++", produce a function with C linkage.  */
+extern "C" const char * locale_charset (void);
+#endif
+
 /* The following static variable is declared 'volatile' to avoid a
    possible multithread problem in the function get_charset_aliases. If we
    are running in a threaded environment, and if two threads initialize
    'charset_aliases' simultaneously, both will produce the same value,
    and everything will be ok if the two assignments to 'charset_aliases'
    are atomic. But I don't know what will happen if the two assignments mix.  */
+#if __STDC__ != 1
+# define volatile /* empty */
+#endif
 /* Pointer to the contents of the charset.alias file, if it has already been
    read, else NULL.  Its format is:
    ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0'  */
-static char * volatile charset_aliases;
+static const char * volatile charset_aliases;
 
 /* Return a pointer to the contents of the charset.alias file.  */
 static const char *
 get_charset_aliases ()
 {
-  char *cp;
+  const char *cp;
 
   cp = charset_aliases;
   if (cp == NULL)
     {
-#ifndef WIN32
+#if !defined WIN32
       FILE *fp;
       const char *dir = LIBDIR;
       const char *base = "charset.alias";
@@ -135,19 +162,19 @@ get_charset_aliases ()
                  continue;
                }
              ungetc (c, fp);
-             if (fscanf(fp, "%50s %50s", buf1, buf2) < 2)
+             if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
                break;
              l1 = strlen (buf1);
              l2 = strlen (buf2);
              if (res_size == 0)
                {
                  res_size = l1 + 1 + l2 + 1;
-                 res_ptr = malloc (res_size + 1);
+                 res_ptr = (char *) malloc (res_size + 1);
                }
              else
                {
                  res_size += l1 + 1 + l2 + 1;
-                 res_ptr = realloc (res_ptr, res_size + 1);
+                 res_ptr = (char *) realloc (res_ptr, res_size + 1);
                }
              if (res_ptr == NULL)
                {
@@ -171,14 +198,29 @@ get_charset_aliases ()
       if (file_name != NULL)
        free (file_name);
 
-#else /* WIN32 */
+#else
 
       /* To avoid the troubles of installing a separate file in the same
         directory as the DLL and of retrieving the DLL's directory at
         runtime, simply inline the aliases here.  */
 
+# if defined WIN32
       cp = "CP936" "\0" "GBK" "\0"
-          "CP1361" "\0" "JOHAB" "\0";
+          "CP1361" "\0" "JOHAB" "\0"
+          "CP20127" "\0" "ASCII" "\0"
+          "CP20866" "\0" "KOI8-R" "\0"
+          "CP21866" "\0" "KOI8-RU" "\0"
+          "CP28591" "\0" "ISO-8859-1" "\0"
+          "CP28592" "\0" "ISO-8859-2" "\0"
+          "CP28593" "\0" "ISO-8859-3" "\0"
+          "CP28594" "\0" "ISO-8859-4" "\0"
+          "CP28595" "\0" "ISO-8859-5" "\0"
+          "CP28596" "\0" "ISO-8859-6" "\0"
+          "CP28597" "\0" "ISO-8859-7" "\0"
+          "CP28598" "\0" "ISO-8859-8" "\0"
+          "CP28599" "\0" "ISO-8859-9" "\0"
+          "CP28605" "\0" "ISO-8859-15" "\0";
+# endif
 #endif
 
       charset_aliases = cp;
@@ -191,7 +233,7 @@ get_charset_aliases ()
    into one of the canonical names listed in config.charset.
    The result must not be freed; it is statically allocated.
    If the canonical name cannot be determined, the result is a non-canonical
-   name or NULL.  */
+   name.  */
 
 #ifdef STATIC
 STATIC
@@ -202,7 +244,7 @@ locale_charset ()
   const char *codeset;
   const char *aliases;
 
-#ifndef WIN32
+#if !(defined WIN32 || defined OS2)
 
 # if HAVE_LANGINFO_CODESET
 
@@ -211,10 +253,14 @@ locale_charset ()
 
 # else
 
-  /* On old systems which lack it, use setlocale and getenv.  */
+  /* On old systems which lack it, use setlocale or getenv.  */
   const char *locale = NULL;
 
-#  if HAVE_SETLOCALE
+  /* But most old systems don't have a complete set of locales.  Some
+     (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
+     use setlocale here; it would return "C" when it doesn't support the
+     locale name the user has set.  */
+#  if HAVE_SETLOCALE && 0
   locale = setlocale (LC_CTYPE, NULL);
 #  endif
   if (locale == NULL || locale[0] == '\0')
@@ -235,28 +281,89 @@ locale_charset ()
 
 # endif
 
-#else /* WIN32 */
+#elif defined WIN32
 
   static char buf[2 + 10 + 1];
 
-  /* Win32 has a function returning the locale's codepage as a number.  */
+  /* Woe32 has a function returning the locale's codepage as a number.  */
   sprintf (buf, "CP%u", GetACP ());
   codeset = buf;
 
-#endif
+#elif defined OS2
+
+  const char *locale;
+  static char buf[2 + 10 + 1];
+  ULONG cp[3];
+  ULONG cplen;
 
-  if (codeset != NULL && codeset[0] != '\0')
+  /* Allow user to override the codeset, as set in the operating system,
+     with standard language environment variables.  */
+  locale = getenv ("LC_ALL");
+  if (locale == NULL || locale[0] == '\0')
     {
-      /* Resolve alias. */
-      for (aliases = get_charset_aliases ();
-          *aliases != '\0';
-          aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
-       if (!strcmp (codeset, aliases))
-         {
-           codeset = aliases + strlen (aliases) + 1;
-           break;
-         }
+      locale = getenv ("LC_CTYPE");
+      if (locale == NULL || locale[0] == '\0')
+       locale = getenv ("LANG");
     }
+  if (locale != NULL && locale[0] != '\0')
+    {
+      /* If the locale name contains an encoding after the dot, return it.  */
+      const char *dot = strchr (locale, '.');
+
+      if (dot != NULL)
+       {
+         const char *modifier;
+
+         dot++;
+         /* Look for the possible @... trailer and remove it, if any.  */
+         modifier = strchr (dot, '@');
+         if (modifier == NULL)
+           return dot;
+         if (modifier - dot < sizeof (buf))
+           {
+             memcpy (buf, dot, modifier - dot);
+             buf [modifier - dot] = '\0';
+             return buf;
+           }
+       }
+
+      /* Resolve through the charset.alias file.  */
+      codeset = locale;
+    }
+  else
+    {
+      /* OS/2 has a function returning the locale's codepage as a number.  */
+      if (DosQueryCp (sizeof (cp), cp, &cplen))
+       codeset = "";
+      else
+       {
+         sprintf (buf, "CP%u", cp[0]);
+         codeset = buf;
+       }
+    }
+
+#endif
+
+  if (codeset == NULL)
+    /* The canonical name cannot be determined.  */
+    codeset = "";
+
+  /* Resolve alias. */
+  for (aliases = get_charset_aliases ();
+       *aliases != '\0';
+       aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
+    if (strcmp (codeset, aliases) == 0
+       || (aliases[0] == '*' && aliases[1] == '\0'))
+      {
+       codeset = aliases + strlen (aliases) + 1;
+       break;
+      }
+
+  /* Don't return an empty string.  GNU libc and GNU libiconv interpret
+     the empty string as denoting "the locale's character encoding",
+     thus GNU libiconv would call this function a second time.  */
+  if (codeset[0] == '\0')
+    codeset = "ASCII";
 
   return codeset;
 }