/* Determine a canonical name for the current locale's character encoding. Copyright (C) 2000-2001 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 by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by Bruno Haible . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #if defined _WIN32 || defined __WIN32__ #undef WIN32 /* avoid warning on mingw32 */ #define WIN32 #endif #ifndef WIN32 #if HAVE_LANGINFO_CODESET #include #else #if HAVE_SETLOCALE #include #endif #endif #else /* WIN32 */ #define WIN32_LEAN_AND_MEAN #ifdef HAVE_WINSOCK2_H #include #endif #include #endif #ifndef DIRECTORY_SEPARATOR #define DIRECTORY_SEPARATOR '/' #endif #ifndef ISSLASH #define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) #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 const char *volatile charset_aliases; /* Return a pointer to the contents of the charset.alias file. */ static const char * get_charset_aliases(void) { const char *cp; cp = charset_aliases; if (cp == NULL) { #ifndef WIN32 FILE *fp; const char *dir = LIBDIR; const char *base = "charset.alias"; char *file_name; /* Concatenate dir and base into freshly allocated file_name. */ { size_t dir_len = strlen(dir); size_t base_len = strlen(base); int add_slash = (dir_len > 0 && !ISSLASH(dir[dir_len - 1])); file_name = (char *)malloc(dir_len + add_slash + base_len + 1); if (file_name != NULL) { memcpy(file_name, dir, dir_len); if (add_slash) file_name[dir_len] = DIRECTORY_SEPARATOR; memcpy(file_name + dir_len + add_slash, base, base_len + 1); } } if (file_name == NULL || (fp = fopen(file_name, "rb")) == NULL) /* Out of memory or file not found, treat it as empty. */ cp = ""; else { /* Parse the file's contents. */ int c; char buf1[50 + 1]; char buf2[50 + 1]; char *res_ptr = NULL; size_t res_size = 0; size_t l1, l2; for (;;) { c = getc(fp); if (c == EOF) break; if (c == '\n' || c == ' ' || c == '\t') continue; if (c == '#') { /* Skip comment, to end of line. */ do c = getc(fp); while (!(c == EOF || c == '\n')); if (c == EOF) break; continue; } ungetc(c, fp); 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 = (char *)malloc(res_size + 1); } else { res_size += l1 + 1 + l2 + 1; res_ptr = (char *)realloc(res_ptr, res_size + 1); } if (res_ptr == NULL) { /* Out of memory. */ res_size = 0; break; } strcpy(res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1); strcpy(res_ptr + res_size - (l2 + 1), buf2); } fclose(fp); if (res_size == 0) cp = ""; else { *(res_ptr + res_size) = '\0'; cp = res_ptr; } } if (file_name != NULL) free(file_name); #else /* WIN32 */ /* 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. */ cp = "CP936" "\0" "GBK" "\0" "CP1361" "\0" "JOHAB" "\0"; #endif charset_aliases = cp; } return cp; } /* Determine the current locale's character encoding, and canonicalize it 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. */ /* Should be in .h file, used in _nl_init_domain_conv() function. */ const char *elinks_locale_charset(void); #ifdef STATIC STATIC #endif const char * elinks_locale_charset(void) { const char *codeset; const char *aliases; #ifndef WIN32 #if HAVE_LANGINFO_CODESET /* Most systems support nl_langinfo (CODESET) nowadays. */ codeset = nl_langinfo(CODESET); #else /* On old systems which lack it, use setlocale or getenv. */ const char *locale = NULL; /* 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') { locale = getenv("LC_ALL"); if (locale == NULL || locale[0] == '\0') { locale = getenv("LC_CTYPE"); if (locale == NULL || locale[0] == '\0') locale = getenv("LANG"); } } /* On some old systems, one used to set locale = "iso8859_1". On others, you set it to "language_COUNTRY.charset". In any case, we resolve it through the charset.alias file. */ codeset = locale; #endif #else /* WIN32 */ static char buf[2 + 10 + 1]; /* Win32 has a function returning the locale's codepage as a number. */ sprintf(buf, "CP%u", GetACP()); 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; } return codeset; }