#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef WIN32
# include <io.h>
#else
# include <unistd.h>
#endif
#include "string.h"
#include "language.h"
#include "debug.h"
#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
static const char *appleLangDefault(void);
#endif
static cups_lang_t *cups_cache_lookup(const char *name,
cups_encoding_t encoding);
static cups_lang_t *lang_cache = NULL;
static const char lang_blank[] = "";
static const char * const lang_encodings[] =
{
"us-ascii",
"iso-8859-1",
"iso-8859-2",
"iso-8859-3",
"iso-8859-4",
"iso-8859-5",
"iso-8859-6",
"iso-8859-7",
"iso-8859-8",
"iso-8859-9",
"iso-8859-10",
"utf-8",
"iso-8859-13",
"iso-8859-14",
"iso-8859-15",
"windows-874",
"windows-1250",
"windows-1251",
"windows-1252",
"windows-1253",
"windows-1254",
"windows-1255",
"windows-1256",
"windows-1257",
"windows-1258",
"koi8-r",
"koi8-u"
};
static const char * const lang_default[] =
{
#include "cups_C.h"
NULL
};
char *
cupsLangEncoding(cups_lang_t *lang)
{
if (lang == NULL)
return ((char*)lang_encodings[0]);
else
return ((char*)lang_encodings[lang->encoding]);
}
void
cupsLangFlush(void)
{
int i;
cups_lang_t *lang,
*next;
for (lang = lang_cache; lang != NULL; lang = next)
{
for (i = 0; i < CUPS_MSG_MAX; i ++)
if (lang->messages[i] != NULL && lang->messages[i] != lang_blank)
free(lang->messages[i]);
next = lang->next;
free(lang);
}
lang_cache = NULL;
}
void
cupsLangFree(cups_lang_t *lang)
{
if (lang != NULL && lang->used > 0)
lang->used --;
}
cups_lang_t *
cupsLangGet(const char *language)
{
int i, count;
char langname[16],
country[16],
charset[16],
*ptr,
real[48],
filename[1024],
*localedir;
cups_encoding_t encoding;
FILE *fp;
char line[1024];
cups_msg_t msg;
char *text;
cups_lang_t *lang;
char *oldlocale;
static const char * const locale_encodings[] =
{
"ASCII",
"ISO8859-1",
"ISO8859-2",
"ISO8859-3",
"ISO8859-4",
"ISO8859-5",
"ISO8859-6",
"ISO8859-7",
"ISO8859-8",
"ISO8859-9",
"ISO8859-10",
"UTF-8",
"ISO8859-13",
"ISO8859-14",
"ISO8859-15",
"CP874",
"CP1250",
"CP1251",
"CP1252",
"CP1253",
"CP1254",
"CP1255",
"CP1256",
"CP1257",
"CP1258",
"KOI8R",
"KOI8U"
};
#ifdef __APPLE__
if (language == NULL)
language = appleLangDefault();
#elif defined(LC_MESSAGES)
if (language == NULL)
{
language = setlocale(LC_MESSAGES, NULL);
if (!strcmp(language, "C") || !strcmp(language, "POSIX"))
language = setlocale(LC_MESSAGES, "");
}
#else
if (language == NULL)
{
language = setlocale(LC_ALL, NULL);
if (!strcmp(language, "C") || !strcmp(language, "POSIX"))
language = setlocale(LC_ALL, "");
}
#endif
#if defined(__APPLE__)
#elif !defined(LC_CTYPE)
oldlocale = _cupsSaveLocale(LC_ALL, "C");
#else
oldlocale = _cupsSaveLocale(LC_CTYPE, "C");
#endif
country[0] = '\0';
charset[0] = '\0';
if (language == NULL || !language[0] ||
strcmp(language, "POSIX") == 0)
strcpy(langname, "C");
else
{
for (ptr = langname; *language; language ++)
if (*language == '_' || *language == '-' || *language == '.')
break;
else if (ptr < (langname + sizeof(langname) - 1))
*ptr++ = tolower(*language);
*ptr = '\0';
if (*language == '_' || *language == '-')
{
language ++;
for (ptr = country; *language; language ++)
if (*language == '.')
break;
else if (ptr < (country + sizeof(country) - 1))
*ptr++ = toupper(*language);
*ptr = '\0';
}
if (*language == '.')
{
language ++;
for (ptr = charset; *language; language ++)
if (ptr < (charset + sizeof(charset) - 1))
*ptr++ = toupper(*language);
*ptr = '\0';
}
if (strlen(langname) != 2)
{
strcpy(langname, "C");
country[0] = '\0';
charset[0] = '\0';
}
}
#if defined(__APPLE__)
#elif !defined(LC_CTYPE)
_cupsRestoreLocale(LC_ALL, oldlocale);
#else
_cupsRestoreLocale(LC_CTYPE, oldlocale);
#endif
encoding = CUPS_US_ASCII;
if (charset[0])
{
for (i = 0; i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0])); i ++)
if (!strcmp(charset, locale_encodings[i]))
{
encoding = (cups_encoding_t)i;
break;
}
}
if ((localedir = getenv("LOCALEDIR")) == NULL)
localedir = CUPS_LOCALEDIR;
snprintf(real, sizeof(real), "%s_%s", langname, country);
if ((lang = cups_cache_lookup(real, encoding)) != NULL)
return (lang);
snprintf(filename, sizeof(filename), "%s/%s/cups_%s", localedir, real, real);
if (!country[0] || access(filename, 0))
{
if ((lang = cups_cache_lookup(langname, encoding)) != NULL)
return (lang);
snprintf(filename, sizeof(filename), "%s/%s/cups_%s", localedir,
langname, langname);
if (access(filename, 0))
{
strcpy(real, "C");
snprintf(filename, sizeof(filename), "%s/C/cups_C", localedir);
}
else
strcpy(real, langname);
}
if (strcmp(real, "C"))
fp = fopen(filename, "r");
else
fp = NULL;
if (fp == NULL)
strlcpy(line, lang_default[0], sizeof(line));
else if (fgets(line, sizeof(line), fp) == NULL)
{
fclose(fp);
return (NULL);
}
i = strlen(line) - 1;
if (line[i] == '\n')
line[i] = '\0';
for (lang = lang_cache; lang != NULL; lang = lang->next)
if (lang->used == 0)
break;
if (lang == NULL)
{
if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
{
fclose(fp);
return (NULL);
}
lang->next = lang_cache;
lang_cache = lang;
}
for (i = 0; i < CUPS_MSG_MAX; i ++)
{
if (lang->messages[i] != NULL && lang->messages[i] != lang_blank)
free(lang->messages[i]);
lang->messages[i] = (char*)lang_blank;
}
lang->used ++;
strlcpy(lang->language, real, sizeof(lang->language));
if (charset[0])
lang->encoding = encoding;
else
{
for (i = 0; i < (sizeof(lang_encodings) / sizeof(lang_encodings[0])); i ++)
if (strcmp(lang_encodings[i], line) == 0)
{
lang->encoding = (cups_encoding_t)i;
break;
}
}
msg = (cups_msg_t)-1;
count = 1;
for (;;)
{
if (fp == NULL)
{
if (lang_default[count] == NULL)
break;
strlcpy(line, lang_default[count], sizeof(line));
}
else if (fgets(line, sizeof(line), fp) == NULL)
break;
count ++;
i = strlen(line) - 1;
if (line[i] == '\n')
line[i] = '\0';
if (line[0] == '\0')
continue;
if (isdigit(line[0]))
msg = (cups_msg_t)atoi(line);
else
msg ++;
if (msg < 0 || msg >= CUPS_MSG_MAX)
continue;
text = line;
while (isdigit(*text))
text ++;
while (isspace(*text))
text ++;
lang->messages[msg] = strdup(text);
}
if (fp != NULL)
fclose(fp);
return (lang);
}
void
_cupsRestoreLocale(int category,
char *oldlocale)
{
DEBUG_printf(("_cupsRestoreLocale(category=%d, oldlocale=\"%s\")\n",
category, oldlocale));
if (oldlocale)
{
setlocale(category, oldlocale);
free(oldlocale);
}
}
char *
_cupsSaveLocale(int category,
const char *locale)
{
char *oldlocale;
DEBUG_printf(("_cupsSaveLocale(category=%d, locale=\"%s\")\n",
category, locale));
if ((oldlocale = setlocale(category, locale)) != NULL)
return (strdup(oldlocale));
else
return (NULL);
}
#ifdef __APPLE__
#ifdef HAVE_CF_LOCALE_ID
static const char *
appleLangDefault(void)
{
CFPropertyListRef localizationList;
CFStringRef languageName;
CFStringRef localeName;
static char language[32] = { 0 };
if (language[0] == '\0')
{
localizationList =
CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
kCFPreferencesCurrentApplication);
if (localizationList != NULL)
{
if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
CFArrayGetCount(localizationList) > 0)
{
languageName = CFArrayGetValueAtIndex(localizationList, 0);
if (languageName != NULL &&
CFGetTypeID(languageName) == CFStringGetTypeID())
{
localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault,
languageName);
if (localeName != NULL)
{
CFStringGetCString(localeName, language, sizeof(language),
kCFStringEncodingASCII);
CFRelease(localeName);
if (strcmp(language, "en") == 0)
strlcpy(language, "en_US.UTF-8", sizeof(language));
else if (strchr(language, '.') == NULL)
strlcat(language, ".UTF-8", sizeof(language));
}
}
}
CFRelease(localizationList);
}
if (language[0] == '\0')
strlcpy(language, "en_US.UTF-8", sizeof(language));
}
return (language);
}
#else
typedef struct
{
const char * const name;
const char * const locale;
} apple_name_locale_t;
static const apple_name_locale_t apple_name_locale[] =
{
{ "English" , "en_US.UTF-8" }, { "French" , "fr.UTF-8" },
{ "German" , "de.UTF-8" }, { "Italian" , "it.UTF-8" },
{ "Dutch" , "nl.UTF-8" }, { "Swedish" , "sv.UTF-8" },
{ "Spanish" , "es.UTF-8" }, { "Danish" , "da.UTF-8" },
{ "Portuguese" , "pt.UTF-8" }, { "Norwegian" , "no.UTF-8" },
{ "Hebrew" , "he.UTF-8" }, { "Japanese" , "ja.UTF-8" },
{ "Arabic" , "ar.UTF-8" }, { "Finnish" , "fi.UTF-8" },
{ "Greek" , "el.UTF-8" }, { "Icelandic" , "is.UTF-8" },
{ "Maltese" , "mt.UTF-8" }, { "Turkish" , "tr.UTF-8" },
{ "Croatian" , "hr.UTF-8" }, { "Chinese" , "zh.UTF-8" },
{ "Urdu" , "ur.UTF-8" }, { "Hindi" , "hi.UTF-8" },
{ "Thai" , "th.UTF-8" }, { "Korean" , "ko.UTF-8" },
{ "Lithuanian" , "lt.UTF-8" }, { "Polish" , "pl.UTF-8" },
{ "Hungarian" , "hu.UTF-8" }, { "Estonian" , "et.UTF-8" },
{ "Latvian" , "lv.UTF-8" }, { "Sami" , "se.UTF-8" },
{ "Faroese" , "fo.UTF-8" }, { "Farsi" , "fa.UTF-8" },
{ "Russian" , "ru.UTF-8" }, { "Chinese" , "zh.UTF-8" },
{ "Dutch" , "nl.UTF-8" }, { "Irish" , "ga.UTF-8" },
{ "Albanian" , "sq.UTF-8" }, { "Romanian" , "ro.UTF-8" },
{ "Czech" , "cs.UTF-8" }, { "Slovak" , "sk.UTF-8" },
{ "Slovenian" , "sl.UTF-8" }, { "Yiddish" , "yi.UTF-8" },
{ "Serbian" , "sr.UTF-8" }, { "Macedonian" , "mk.UTF-8" },
{ "Bulgarian" , "bg.UTF-8" }, { "Ukrainian" , "uk.UTF-8" },
{ "Byelorussian", "be.UTF-8" }, { "Uzbek" , "uz.UTF-8" },
{ "Kazakh" , "kk.UTF-8" }, { "Azerbaijani", "az.UTF-8" },
{ "Azerbaijani" , "az.UTF-8" }, { "Armenian" , "hy.UTF-8" },
{ "Georgian" , "ka.UTF-8" }, { "Moldavian" , "mo.UTF-8" },
{ "Kirghiz" , "ky.UTF-8" }, { "Tajiki" , "tg.UTF-8" },
{ "Turkmen" , "tk.UTF-8" }, { "Mongolian" , "mn.UTF-8" },
{ "Mongolian" , "mn.UTF-8" }, { "Pashto" , "ps.UTF-8" },
{ "Kurdish" , "ku.UTF-8" }, { "Kashmiri" , "ks.UTF-8" },
{ "Sindhi" , "sd.UTF-8" }, { "Tibetan" , "bo.UTF-8" },
{ "Nepali" , "ne.UTF-8" }, { "Sanskrit" , "sa.UTF-8" },
{ "Marathi" , "mr.UTF-8" }, { "Bengali" , "bn.UTF-8" },
{ "Assamese" , "as.UTF-8" }, { "Gujarati" , "gu.UTF-8" },
{ "Punjabi" , "pa.UTF-8" }, { "Oriya" , "or.UTF-8" },
{ "Malayalam" , "ml.UTF-8" }, { "Kannada" , "kn.UTF-8" },
{ "Tamil" , "ta.UTF-8" }, { "Telugu" , "te.UTF-8" },
{ "Sinhalese" , "si.UTF-8" }, { "Burmese" , "my.UTF-8" },
{ "Khmer" , "km.UTF-8" }, { "Lao" , "lo.UTF-8" },
{ "Vietnamese" , "vi.UTF-8" }, { "Indonesian" , "id.UTF-8" },
{ "Tagalog" , "tl.UTF-8" }, { "Malay" , "ms.UTF-8" },
{ "Malay" , "ms.UTF-8" }, { "Amharic" , "am.UTF-8" },
{ "Tigrinya" , "ti.UTF-8" }, { "Oromo" , "om.UTF-8" },
{ "Somali" , "so.UTF-8" }, { "Swahili" , "sw.UTF-8" },
{ "Kinyarwanda" , "rw.UTF-8" }, { "Rundi" , "rn.UTF-8" },
{ "Nyanja" , "" }, { "Malagasy" , "mg.UTF-8" },
{ "Esperanto" , "eo.UTF-8" }, { "Welsh" , "cy.UTF-8" },
{ "Basque" , "eu.UTF-8" }, { "Catalan" , "ca.UTF-8" },
{ "Latin" , "la.UTF-8" }, { "Quechua" , "qu.UTF-8" },
{ "Guarani" , "gn.UTF-8" }, { "Aymara" , "ay.UTF-8" },
{ "Tatar" , "tt.UTF-8" }, { "Uighur" , "ug.UTF-8" },
{ "Dzongkha" , "dz.UTF-8" }, { "Javanese" , "jv.UTF-8" },
{ "Sundanese" , "su.UTF-8" }, { "Galician" , "gl.UTF-8" },
{ "Afrikaans" , "af.UTF-8" }, { "Breton" , "br.UTF-8" },
{ "Inuktitut" , "iu.UTF-8" }, { "Scottish" , "gd.UTF-8" },
{ "Manx" , "gv.UTF-8" }, { "Irish" , "ga.UTF-8" },
{ "Tongan" , "to.UTF-8" }, { "Greek" , "el.UTF-8" },
{ "Greenlandic" , "kl.UTF-8" }, { "Azerbaijani", "az.UTF-8" }
};
static const char *
appleLangDefault(void)
{
int i;
CFPropertyListRef localizationList;
CFStringRef localizationName;
char buff[256];
static const char *language = NULL;
if (language == NULL)
{
localizationList =
CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
kCFPreferencesCurrentApplication);
if (localizationList != NULL)
{
if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
CFArrayGetCount(localizationList) > 0)
{
localizationName = CFArrayGetValueAtIndex(localizationList, 0);
if (localizationName != NULL &&
CFGetTypeID(localizationName) == CFStringGetTypeID())
{
CFIndex length = CFStringGetLength(localizationName);
if (length <= sizeof(buff) &&
CFStringGetCString(localizationName, buff, sizeof(buff),
kCFStringEncodingASCII))
{
buff[sizeof(buff) - 1] = '\0';
for (i = 0;
i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
i++)
{
if (strcasecmp(buff, apple_name_locale[i].name) == 0)
{
language = apple_name_locale[i].locale;
break;
}
}
}
}
}
CFRelease(localizationList);
}
if (language == NULL)
language = apple_name_locale[0].locale;
}
return (language);
}
#endif
#endif
static cups_lang_t *
cups_cache_lookup(const char *name,
cups_encoding_t encoding)
{
cups_lang_t *lang;
for (lang = lang_cache; lang != NULL; lang = lang->next)
if (!strcmp(lang->language, name) && encoding == lang->encoding)
{
lang->used ++;
return (lang);
}
return (NULL);
}