]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Add header validation option to ipptool.
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * I18N/language support for CUPS.
ef416fc2 3 *
c245abfe 4 * Copyright 2007-2017 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 6 *
7e86f2f6
MS
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
57b7b66b 11 * missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 12 *
7e86f2f6 13 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 14 */
15
16/*
17 * Include necessary headers...
18 */
19
71e16022 20#include "cups-private.h"
ef416fc2 21#ifdef HAVE_LANGINFO_H
22# include <langinfo.h>
23#endif /* HAVE_LANGINFO_H */
24#ifdef WIN32
25# include <io.h>
26#else
27# include <unistd.h>
28#endif /* WIN32 */
fa73b229 29#ifdef HAVE_COREFOUNDATION_H
30# include <CoreFoundation/CoreFoundation.h>
31#endif /* HAVE_COREFOUNDATION_H */
ef416fc2 32
33
d6ae789d 34/*
35 * Local globals...
36 */
37
6d2f911b 38static _cups_mutex_t lang_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 39 /* Mutex to control access to cache */
d6ae789d 40static cups_lang_t *lang_cache = NULL;
41 /* Language string cache */
ef416fc2 42static const char * const lang_encodings[] =
43 { /* Encoding strings */
44 "us-ascii", "iso-8859-1",
45 "iso-8859-2", "iso-8859-3",
46 "iso-8859-4", "iso-8859-5",
47 "iso-8859-6", "iso-8859-7",
48 "iso-8859-8", "iso-8859-9",
49 "iso-8859-10", "utf-8",
50 "iso-8859-13", "iso-8859-14",
cc754834
MS
51 "iso-8859-15", "cp874",
52 "cp1250", "cp1251",
53 "cp1252", "cp1253",
54 "cp1254", "cp1255",
55 "cp1256", "cp1257",
56 "cp1258", "koi8-r",
ef416fc2 57 "koi8-u", "iso-8859-11",
a2326b5b 58 "iso-8859-16", "mac",
ef416fc2 59 "unknown", "unknown",
60 "unknown", "unknown",
61 "unknown", "unknown",
62 "unknown", "unknown",
63 "unknown", "unknown",
64 "unknown", "unknown",
65 "unknown", "unknown",
66 "unknown", "unknown",
67 "unknown", "unknown",
68 "unknown", "unknown",
69 "unknown", "unknown",
70 "unknown", "unknown",
71 "unknown", "unknown",
72 "unknown", "unknown",
73 "unknown", "unknown",
74 "unknown", "unknown",
75 "unknown", "unknown",
cc754834
MS
76 "cp932", "cp936",
77 "cp949", "cp950",
78 "cp1361", "unknown",
ef416fc2 79 "unknown", "unknown",
80 "unknown", "unknown",
81 "unknown", "unknown",
82 "unknown", "unknown",
83 "unknown", "unknown",
84 "unknown", "unknown",
85 "unknown", "unknown",
86 "unknown", "unknown",
87 "unknown", "unknown",
88 "unknown", "unknown",
89 "unknown", "unknown",
90 "unknown", "unknown",
91 "unknown", "unknown",
92 "unknown", "unknown",
93 "unknown", "unknown",
94 "unknown", "unknown",
95 "unknown", "unknown",
96 "unknown", "unknown",
97 "unknown", "unknown",
98 "unknown", "unknown",
99 "unknown", "unknown",
100 "unknown", "unknown",
101 "unknown", "unknown",
102 "unknown", "unknown",
103 "unknown", "unknown",
104 "unknown", "unknown",
105 "unknown", "unknown",
106 "unknown", "unknown",
107 "unknown", "unknown",
108 "euc-cn", "euc-jp",
4b3f67ff 109 "euc-kr", "euc-tw",
23ee1efa 110 "shift_jisx0213"
ef416fc2 111 };
112
0a682745
MS
113#ifdef __APPLE__
114typedef struct
115{
116 const char * const language; /* Language ID */
117 const char * const locale; /* Locale ID */
118} _apple_language_locale_t;
119
120static const _apple_language_locale_t apple_language_locale[] =
454708a4
MS
121{ /* Language to locale ID LUT */
122 { "en", "en_US" },
123 { "nb", "no" },
124 { "nb_NO", "no" },
125 { "zh-Hans", "zh_CN" },
2650d637 126 { "zh_HANS", "zh_CN" },
454708a4 127 { "zh-Hant", "zh_TW" },
2650d637 128 { "zh_HANT", "zh_TW" },
454708a4 129 { "zh-Hant_CN", "zh_TW" }
0a682745
MS
130};
131#endif /* __APPLE__ */
132
133
134/*
135 * Local functions...
136 */
137
82f97232 138
0a682745
MS
139#ifdef __APPLE__
140static const char *appleLangDefault(void);
0837b7e8 141# ifdef CUPS_BUNDLEDIR
82f97232
MS
142# ifndef CF_RETURNS_RETAINED
143# if __has_feature(attribute_cf_returns_retained)
144# define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
145# else
146# define CF_RETURNS_RETAINED
147# endif /* __has_feature(attribute_cf_returns_retained) */
148# endif /* !CF_RETURNED_RETAINED */
149static cups_array_t *appleMessageLoad(const char *locale)
150 CF_RETURNS_RETAINED;
0837b7e8 151# endif /* CUPS_BUNDLEDIR */
0a682745
MS
152#endif /* __APPLE__ */
153static cups_lang_t *cups_cache_lookup(const char *name,
154 cups_encoding_t encoding);
155static int cups_message_compare(_cups_message_t *m1,
156 _cups_message_t *m2);
0837b7e8 157static void cups_message_free(_cups_message_t *m);
9c80ffa2 158static void cups_message_load(cups_lang_t *lang);
0a682745
MS
159static void cups_unquote(char *d, const char *s);
160
161
162#ifdef __APPLE__
163/*
0837b7e8
MS
164 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
165 * locale ID.
0a682745
MS
166 */
167
168const char * /* O - Language ID */
169_cupsAppleLanguage(const char *locale, /* I - Locale ID */
170 char *language,/* I - Language ID buffer */
171 size_t langsize) /* I - Size of language ID buffer */
172{
173 int i; /* Looping var */
174 CFStringRef localeid, /* CF locale identifier */
175 langid; /* CF language identifier */
176
177
178 /*
179 * Copy the locale name and convert, as needed, to the Apple-specific
180 * locale identifier...
181 */
182
183 switch (strlen(locale))
184 {
185 default :
186 /*
187 * Invalid locale...
188 */
189
190 strlcpy(language, "en", langsize);
191 break;
192
193 case 2 :
194 strlcpy(language, locale, langsize);
195 break;
196
197 case 5 :
198 strlcpy(language, locale, langsize);
199
200 if (language[2] == '-')
201 {
202 /*
203 * Convert ll-cc to ll_CC...
204 */
205
206 language[2] = '_';
7e86f2f6
MS
207 language[3] = (char)toupper(language[3] & 255);
208 language[4] = (char)toupper(language[4] & 255);
0a682745
MS
209 }
210 break;
211 }
212
213 for (i = 0;
214 i < (int)(sizeof(apple_language_locale) /
215 sizeof(apple_language_locale[0]));
216 i ++)
217 if (!strcmp(locale, apple_language_locale[i].locale))
218 {
219 strlcpy(language, apple_language_locale[i].language, sizeof(language));
220 break;
221 }
222
223 /*
224 * Attempt to map the locale ID to a language ID...
225 */
226
227 if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
228 kCFStringEncodingASCII)) != NULL)
229 {
230 if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
231 kCFAllocatorDefault, localeid)) != NULL)
232 {
7e86f2f6 233 CFStringGetCString(langid, language, (CFIndex)langsize, kCFStringEncodingASCII);
0a682745
MS
234 CFRelease(langid);
235 }
236
237 CFRelease(localeid);
238 }
239
240 /*
241 * Return what we got...
242 */
243
244 return (language);
245}
454708a4
MS
246
247
248/*
249 * '_cupsAppleLocale()' - Get the locale associated with an Apple language ID.
250 */
251
252const char * /* O - Locale */
253_cupsAppleLocale(CFStringRef languageName, /* I - Apple language ID */
254 char *locale, /* I - Buffer for locale */
255 size_t localesize) /* I - Size of buffer */
256{
257 int i; /* Looping var */
258 CFStringRef localeName; /* Locale as a CF string */
4fac8376
MS
259#ifdef DEBUG
260 char temp[1024]; /* Temporary string */
454708a4
MS
261
262
4fac8376
MS
263 if (!CFStringGetCString(languageName, temp, (CFIndex)sizeof(temp), kCFStringEncodingASCII))
264 temp[0] = '\0';
265
41c0a573 266 DEBUG_printf(("_cupsAppleLocale(languageName=%p(%s), locale=%p, localsize=%d)", (void *)languageName, temp, (void *)locale, (int)localesize));
4fac8376
MS
267#endif /* DEBUG */
268
454708a4
MS
269 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, languageName);
270
271 if (localeName)
272 {
273 /*
274 * Copy the locale name and tweak as needed...
275 */
276
277 if (!CFStringGetCString(localeName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
278 *locale = '\0';
279
4fac8376
MS
280 DEBUG_printf(("_cupsAppleLocale: locale=\"%s\"", locale));
281
454708a4
MS
282 CFRelease(localeName);
283
284 /*
285 * Map new language identifiers to locales...
286 */
287
288 for (i = 0;
289 i < (int)(sizeof(apple_language_locale) /
290 sizeof(apple_language_locale[0]));
291 i ++)
292 {
2650d637
MS
293 size_t len = strlen(apple_language_locale[i].language);
294
295 if (!strcmp(locale, apple_language_locale[i].language) ||
296 (!strncmp(locale, apple_language_locale[i].language, len) && (locale[len] == '_' || locale[len] == '-')))
454708a4 297 {
4fac8376 298 DEBUG_printf(("_cupsAppleLocale: Updating locale to \"%s\".", apple_language_locale[i].locale));
454708a4
MS
299 strlcpy(locale, apple_language_locale[i].locale, localesize);
300 break;
301 }
302 }
303 }
304 else
305 {
306 /*
307 * Just try the Apple language name...
308 */
309
310 if (!CFStringGetCString(languageName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
311 *locale = '\0';
312 }
313
314 if (!*locale)
4fac8376
MS
315 {
316 DEBUG_puts("_cupsAppleLocale: Returning NULL.");
454708a4 317 return (NULL);
4fac8376 318 }
454708a4
MS
319
320 /*
321 * Convert language subtag into region subtag...
322 */
323
324 if (locale[2] == '-')
325 locale[2] = '_';
fdc80a91
MS
326 else if (locale[3] == '-')
327 locale[3] = '_';
454708a4
MS
328
329 if (!strchr(locale, '.'))
330 strlcat(locale, ".UTF-8", localesize);
331
4fac8376
MS
332 DEBUG_printf(("_cupsAppleLocale: Returning \"%s\".", locale));
333
454708a4
MS
334 return (locale);
335}
0a682745
MS
336#endif /* __APPLE__ */
337
ef416fc2 338
339/*
340 * '_cupsEncodingName()' - Return the character encoding name string
341 * for the given encoding enumeration.
342 */
343
344const char * /* O - Character encoding */
345_cupsEncodingName(
346 cups_encoding_t encoding) /* I - Encoding value */
347{
7e86f2f6
MS
348 if (encoding < CUPS_US_ASCII ||
349 encoding >= (cups_encoding_t)(sizeof(lang_encodings) / sizeof(lang_encodings[0])))
c7017ecc
MS
350 {
351 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
352 encoding, lang_encodings[0]));
ef416fc2 353 return (lang_encodings[0]);
c7017ecc 354 }
ef416fc2 355 else
c7017ecc
MS
356 {
357 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
358 encoding, lang_encodings[encoding]));
ef416fc2 359 return (lang_encodings[encoding]);
c7017ecc 360 }
ef416fc2 361}
362
363
364/*
365 * 'cupsLangDefault()' - Return the default language.
366 */
367
368cups_lang_t * /* O - Language data */
369cupsLangDefault(void)
370{
371 return (cupsLangGet(NULL));
372}
373
374
375/*
376 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
377 * for the given language.
378 */
379
380const char * /* O - Character encoding */
381cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
382{
383 if (lang == NULL)
384 return ((char*)lang_encodings[0]);
385 else
386 return ((char*)lang_encodings[lang->encoding]);
387}
388
389
390/*
391 * 'cupsLangFlush()' - Flush all language data out of the cache.
392 */
393
394void
395cupsLangFlush(void)
396{
397 cups_lang_t *lang, /* Current language */
398 *next; /* Next language */
ef416fc2 399
400
401 /*
402 * Free all languages in the cache...
403 */
404
6d2f911b 405 _cupsMutexLock(&lang_mutex);
d6ae789d 406
407 for (lang = lang_cache; lang != NULL; lang = next)
ef416fc2 408 {
409 /*
410 * Free all messages...
411 */
412
413 _cupsMessageFree(lang->strings);
414
415 /*
416 * Then free the language structure itself...
417 */
418
419 next = lang->next;
420 free(lang);
421 }
422
d6ae789d 423 lang_cache = NULL;
424
6d2f911b 425 _cupsMutexUnlock(&lang_mutex);
ef416fc2 426}
427
428
429/*
430 * 'cupsLangFree()' - Free language data.
431 *
5a738aea 432 * This does not actually free anything; use @link cupsLangFlush@ for that.
ef416fc2 433 */
434
435void
436cupsLangFree(cups_lang_t *lang) /* I - Language to free */
437{
6d2f911b 438 _cupsMutexLock(&lang_mutex);
d6ae789d 439
ef416fc2 440 if (lang != NULL && lang->used > 0)
441 lang->used --;
d6ae789d 442
6d2f911b 443 _cupsMutexUnlock(&lang_mutex);
ef416fc2 444}
445
446
447/*
448 * 'cupsLangGet()' - Get a language.
449 */
450
451cups_lang_t * /* O - Language data */
452cupsLangGet(const char *language) /* I - Language or locale */
453{
454 int i; /* Looping var */
480ef0fe 455#ifndef __APPLE__
456 char locale[255]; /* Copy of locale name */
457#endif /* !__APPLE__ */
458 char langname[16], /* Requested language name */
ef416fc2 459 country[16], /* Country code */
460 charset[16], /* Character set */
ef416fc2 461 *csptr, /* Pointer to CODESET string */
ef416fc2 462 *ptr, /* Pointer into language/charset */
0837b7e8 463 real[48]; /* Real language name */
ef416fc2 464 cups_encoding_t encoding; /* Encoding to use */
465 cups_lang_t *lang; /* Current language... */
ef416fc2 466 static const char * const locale_encodings[] =
467 { /* Locale charset names */
468 "ASCII", "ISO88591", "ISO88592", "ISO88593",
469 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
470 "ISO88598", "ISO88599", "ISO885910", "UTF8",
471 "ISO885913", "ISO885914", "ISO885915", "CP874",
472 "CP1250", "CP1251", "CP1252", "CP1253",
473 "CP1254", "CP1255", "CP1256", "CP1257",
474 "CP1258", "KOI8R", "KOI8U", "ISO885911",
bd7854cb 475 "ISO885916", "MACROMAN", "", "",
ef416fc2 476
477 "", "", "", "",
478 "", "", "", "",
479 "", "", "", "",
480 "", "", "", "",
481 "", "", "", "",
482 "", "", "", "",
483 "", "", "", "",
484 "", "", "", "",
485
486 "CP932", "CP936", "CP949", "CP950",
487 "CP1361", "", "", "",
488 "", "", "", "",
489 "", "", "", "",
490 "", "", "", "",
491 "", "", "", "",
492 "", "", "", "",
493 "", "", "", "",
494
495 "", "", "", "",
496 "", "", "", "",
497 "", "", "", "",
498 "", "", "", "",
499 "", "", "", "",
500 "", "", "", "",
501 "", "", "", "",
502 "", "", "", "",
503
4b3f67ff
MS
504 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
505 "SHIFT_JISX0213"
ef416fc2 506 };
507
508
f11a948a 509 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
ef416fc2 510
511#ifdef __APPLE__
757d2cad 512 /*
513 * Set the character set to UTF-8...
514 */
515
5a9febac 516 strlcpy(charset, "UTF8", sizeof(charset));
757d2cad 517
ef416fc2 518 /*
c8fef167 519 * Apple's setlocale doesn't give us the user's localization
ef416fc2 520 * preference so we have to look it up this way...
521 */
522
c9fc04c6
MS
523 if (!language)
524 {
b0f6947b 525 if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
c9fc04c6
MS
526 language = appleLangDefault();
527
f11a948a 528 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
c9fc04c6 529 }
757d2cad 530
ef416fc2 531#else
757d2cad 532 /*
533 * Set the charset to "unknown"...
534 */
535
536 charset[0] = '\0';
537
538 /*
539 * Use setlocale() to determine the currently set locale, and then
540 * fallback to environment variables to avoid setting the locale,
541 * since setlocale() is not thread-safe!
542 */
543
544 if (!language)
ef416fc2 545 {
546 /*
547 * First see if the locale has been set; if it is still "C" or
757d2cad 548 * "POSIX", use the environment to get the default...
ef416fc2 549 */
550
551# ifdef LC_MESSAGES
552 ptr = setlocale(LC_MESSAGES, NULL);
553# else
554 ptr = setlocale(LC_ALL, NULL);
555# endif /* LC_MESSAGES */
556
f11a948a 557 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
ef416fc2 558
559 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
ef416fc2 560 {
757d2cad 561 /*
562 * Get the character set from the LC_CTYPE locale setting...
563 */
564
565 if ((ptr = getenv("LC_CTYPE")) == NULL)
566 if ((ptr = getenv("LC_ALL")) == NULL)
567 if ((ptr = getenv("LANG")) == NULL)
568 ptr = "en_US";
569
570 if ((csptr = strchr(ptr, '.')) != NULL)
571 {
572 /*
573 * Extract the character set from the environment...
574 */
575
576 for (ptr = charset, csptr ++; *csptr; csptr ++)
7cf5915e 577 if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr))
757d2cad 578 *ptr++ = *csptr;
579
580 *ptr = '\0';
581 }
757d2cad 582
583 /*
584 * Get the locale for messages from the LC_MESSAGES locale setting...
585 */
586
587 if ((ptr = getenv("LC_MESSAGES")) == NULL)
588 if ((ptr = getenv("LC_ALL")) == NULL)
589 if ((ptr = getenv("LANG")) == NULL)
590 ptr = "en_US";
ef416fc2 591 }
ef416fc2 592
593 if (ptr)
594 {
595 strlcpy(locale, ptr, sizeof(locale));
596 language = locale;
597
a41f09e2
MS
598 /*
599 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
600 */
601
602 if (!strncmp(locale, "nb", 2))
603 locale[1] = 'o';
604
f11a948a 605 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
ef416fc2 606 }
607 }
608#endif /* __APPLE__ */
609
610 /*
611 * If "language" is NULL at this point, then chances are we are using
612 * a language that is not installed for the base OS.
613 */
614
615 if (!language)
616 {
617 /*
757d2cad 618 * Switch to the POSIX ("C") locale...
ef416fc2 619 */
620
757d2cad 621 language = "C";
ef416fc2 622 }
623
ef416fc2 624#ifdef CODESET
625 /*
626 * On systems that support the nl_langinfo(CODESET) call, use
627 * this value as the character set...
628 */
629
757d2cad 630 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
ef416fc2 631 {
632 /*
633 * Copy all of the letters and numbers in the CODESET string...
634 */
635
636 for (ptr = charset; *csptr; csptr ++)
7cf5915e 637 if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1))
ef416fc2 638 *ptr++ = *csptr;
639
640 *ptr = '\0';
641
f11a948a 642 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
e07d4801 643 "nl_langinfo(CODESET)...", charset));
ef416fc2 644 }
645#endif /* CODESET */
646
07725fee 647 /*
648 * If we don't have a character set by now, default to UTF-8...
649 */
650
651 if (!charset[0])
5a9febac 652 strlcpy(charset, "UTF8", sizeof(charset));
07725fee 653
ef416fc2 654 /*
655 * Parse the language string passed in to a locale string. "C" is the
656 * standard POSIX locale and is copied unchanged. Otherwise the
657 * language string is converted from ll-cc[.charset] (language-country)
658 * to ll_CC[.CHARSET] to match the file naming convention used by all
659 * POSIX-compliant operating systems. Invalid language names are mapped
660 * to the POSIX locale.
661 */
662
663 country[0] = '\0';
664
665 if (language == NULL || !language[0] ||
666 !strcmp(language, "POSIX"))
5a9febac 667 strlcpy(langname, "C", sizeof(langname));
ef416fc2 668 else
669 {
670 /*
671 * Copy the parts of the locale string over safely...
672 */
673
674 for (ptr = langname; *language; language ++)
675 if (*language == '_' || *language == '-' || *language == '.')
676 break;
677 else if (ptr < (langname + sizeof(langname) - 1))
7e86f2f6 678 *ptr++ = (char)tolower(*language & 255);
ef416fc2 679
680 *ptr = '\0';
681
682 if (*language == '_' || *language == '-')
683 {
684 /*
685 * Copy the country code...
686 */
687
688 for (language ++, ptr = country; *language; language ++)
689 if (*language == '.')
690 break;
691 else if (ptr < (country + sizeof(country) - 1))
7e86f2f6 692 *ptr++ = (char)toupper(*language & 255);
ef416fc2 693
694 *ptr = '\0';
695 }
696
697 if (*language == '.' && !charset[0])
698 {
699 /*
700 * Copy the encoding...
701 */
702
703 for (language ++, ptr = charset; *language; language ++)
7cf5915e 704 if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
7e86f2f6 705 *ptr++ = (char)toupper(*language & 255);
ef416fc2 706
707 *ptr = '\0';
708 }
709
710 /*
711 * Force a POSIX locale for an invalid language name...
712 */
713
fdc80a91 714 if (strlen(langname) != 2 && strlen(langname) != 3)
ef416fc2 715 {
5a9febac 716 strlcpy(langname, "C", sizeof(langname));
ef416fc2 717 country[0] = '\0';
718 charset[0] = '\0';
719 }
720 }
721
f11a948a 722 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
ef416fc2 723 langname, country, charset));
724
725 /*
726 * Figure out the desired encoding...
727 */
728
729 encoding = CUPS_AUTO_ENCODING;
730
731 if (charset[0])
732 {
bd7854cb 733 for (i = 0;
734 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
735 i ++)
88f9aafc 736 if (!_cups_strcasecmp(charset, locale_encodings[i]))
ef416fc2 737 {
738 encoding = (cups_encoding_t)i;
739 break;
740 }
8ca02f3c 741
742 if (encoding == CUPS_AUTO_ENCODING)
743 {
744 /*
745 * Map alternate names for various character sets...
746 */
747
88f9aafc
MS
748 if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
749 !_cups_strcasecmp(charset, "sjis"))
8ca02f3c 750 encoding = CUPS_WINDOWS_932;
88f9aafc 751 else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
8ca02f3c 752 encoding = CUPS_WINDOWS_936;
88f9aafc 753 else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
8ca02f3c 754 encoding = CUPS_WINDOWS_949;
88f9aafc 755 else if (!_cups_strcasecmp(charset, "big5"))
8ca02f3c 756 encoding = CUPS_WINDOWS_950;
757 }
ef416fc2 758 }
759
f11a948a 760 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
ef416fc2 761 encoding == CUPS_AUTO_ENCODING ? "auto" :
762 lang_encodings[encoding]));
763
764 /*
765 * See if we already have this language/country loaded...
766 */
767
bd7854cb 768 if (country[0])
bd7854cb 769 snprintf(real, sizeof(real), "%s_%s", langname, country);
bd7854cb 770 else
5a9febac 771 strlcpy(real, langname, sizeof(real));
d6ae789d 772
6d2f911b 773 _cupsMutexLock(&lang_mutex);
d6ae789d 774
c0e1af83 775 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
d6ae789d 776 {
6d2f911b 777 _cupsMutexUnlock(&lang_mutex);
d6ae789d 778
f11a948a 779 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
c9fc04c6 780
d6ae789d 781 return (lang);
782 }
ef416fc2 783
ef416fc2 784 /*
785 * See if there is a free language available; if so, use that
786 * record...
787 */
788
d6ae789d 789 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 790 if (lang->used == 0)
791 break;
792
793 if (lang == NULL)
794 {
795 /*
796 * Allocate memory for the language and add it to the cache.
797 */
798
799 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
d6ae789d 800 {
6d2f911b 801 _cupsMutexUnlock(&lang_mutex);
d6ae789d 802
ef416fc2 803 return (NULL);
d6ae789d 804 }
ef416fc2 805
d6ae789d 806 lang->next = lang_cache;
807 lang_cache = lang;
ef416fc2 808 }
bd7854cb 809 else
810 {
811 /*
812 * Free all old strings as needed...
813 */
ef416fc2 814
bd7854cb 815 _cupsMessageFree(lang->strings);
9c80ffa2 816 lang->strings = NULL;
bd7854cb 817 }
ef416fc2 818
819 /*
820 * Then assign the language and encoding fields...
821 */
822
823 lang->used ++;
824 strlcpy(lang->language, real, sizeof(lang->language));
825
826 if (encoding != CUPS_AUTO_ENCODING)
827 lang->encoding = encoding;
828 else
829 lang->encoding = CUPS_UTF8;
830
ef416fc2 831 /*
832 * Return...
833 */
834
6d2f911b 835 _cupsMutexUnlock(&lang_mutex);
d6ae789d 836
ef416fc2 837 return (lang);
838}
839
840
841/*
842 * '_cupsLangString()' - Get a message string.
843 *
844 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
845 * convert the string to the language encoding.
846 */
847
848const char * /* O - Localized message */
849_cupsLangString(cups_lang_t *lang, /* I - Language */
850 const char *message) /* I - Message */
851{
6d2f911b
MS
852 const char *s; /* Localized message */
853
a9357c9d
MS
854
855 DEBUG_printf(("_cupsLangString(lang=%p, message=\"%s\")", (void *)lang, message));
856
ef416fc2 857 /*
858 * Range check input...
859 */
860
37e7e6e0 861 if (!lang || !message || !*message)
ef416fc2 862 return (message);
863
6d2f911b 864 _cupsMutexLock(&lang_mutex);
d6ae789d 865
9c80ffa2
MS
866 /*
867 * Load the message catalog if needed...
868 */
869
870 if (!lang->strings)
871 cups_message_load(lang);
872
6d2f911b 873 s = _cupsMessageLookup(lang->strings, message);
d6ae789d 874
6d2f911b 875 _cupsMutexUnlock(&lang_mutex);
d6ae789d 876
6d2f911b 877 return (s);
ef416fc2 878}
879
880
881/*
882 * '_cupsMessageFree()' - Free a messages array.
883 */
884
885void
886_cupsMessageFree(cups_array_t *a) /* I - Message array */
887{
0837b7e8
MS
888#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
889 /*
890 * Release the cups.strings dictionary as needed...
891 */
ef416fc2 892
0837b7e8
MS
893 if (cupsArrayUserData(a))
894 CFRelease((CFDictionaryRef)cupsArrayUserData(a));
895#endif /* __APPLE__ && CUPS_BUNDLEDIR */
ef416fc2 896
897 /*
898 * Free the array...
899 */
900
901 cupsArrayDelete(a);
902}
903
904
905/*
906 * '_cupsMessageLoad()' - Load a .po file into a messages array.
907 */
908
909cups_array_t * /* O - New message array */
8b116e60 910_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
0837b7e8 911 int unquote) /* I - Unescape \foo in strings? */
ef416fc2 912{
913 cups_file_t *fp; /* Message file */
914 cups_array_t *a; /* Message array */
915 _cups_message_t *m; /* Current message */
916 char s[4096], /* String buffer */
917 *ptr, /* Pointer into buffer */
918 *temp; /* New string */
7e86f2f6
MS
919 size_t length, /* Length of combined strings */
920 ptrlen; /* Length of string */
ef416fc2 921
922
e07d4801 923 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
bd7854cb 924
ef416fc2 925 /*
926 * Create an array to hold the messages...
927 */
928
a29fd7dd 929 if ((a = _cupsMessageNew(NULL)) == NULL)
c9fc04c6 930 {
e07d4801 931 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
ef416fc2 932 return (NULL);
c9fc04c6 933 }
ef416fc2 934
935 /*
936 * Open the message catalog file...
937 */
938
939 if ((fp = cupsFileOpen(filename, "r")) == NULL)
c9fc04c6 940 {
e07d4801 941 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
c9fc04c6 942 strerror(errno)));
ef416fc2 943 return (a);
c9fc04c6 944 }
ef416fc2 945
946 /*
947 * Read messages from the catalog file until EOF...
948 *
949 * The format is the GNU gettext .po format, which is fairly simple:
950 *
951 * msgid "some text"
952 * msgstr "localized text"
953 *
bd7854cb 954 * The ID and localized text can span multiple lines using the form:
ef416fc2 955 *
bd7854cb 956 * msgid ""
957 * "some long text"
958 * msgstr ""
959 * "localized text spanning "
ef416fc2 960 * "multiple lines"
961 */
962
963 m = NULL;
964
965 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
966 {
967 /*
968 * Skip blank and comment lines...
969 */
970
971 if (s[0] == '#' || !s[0])
972 continue;
973
974 /*
975 * Strip the trailing quote...
976 */
977
978 if ((ptr = strrchr(s, '\"')) == NULL)
979 continue;
980
981 *ptr = '\0';
982
983 /*
984 * Find start of value...
985 */
c8fef167 986
ef416fc2 987 if ((ptr = strchr(s, '\"')) == NULL)
988 continue;
989
990 ptr ++;
991
992 /*
993 * Unquote the text...
994 */
995
8b116e60
MS
996 if (unquote)
997 cups_unquote(ptr, ptr);
ef416fc2 998
999 /*
1000 * Create or add to a message...
1001 */
1002
1003 if (!strncmp(s, "msgid", 5))
1004 {
bd7854cb 1005 /*
1006 * Add previous message as needed...
1007 */
1008
1009 if (m)
82cc1f9a
MS
1010 {
1011 if (m->str && m->str[0])
1012 {
1013 cupsArrayAdd(a, m);
1014 }
1015 else
1016 {
1017 /*
1018 * Translation is empty, don't add it... (STR #4033)
1019 */
1020
1021 free(m->id);
1022 if (m->str)
1023 free(m->str);
1024 free(m);
1025 }
1026 }
bd7854cb 1027
1028 /*
1029 * Create a new message with the given msgid string...
1030 */
1031
ef416fc2 1032 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
1033 {
1034 cupsFileClose(fp);
1035 return (a);
1036 }
1037
91c84a35
MS
1038 if ((m->id = strdup(ptr)) == NULL)
1039 {
1040 free(m);
1041 cupsFileClose(fp);
1042 return (a);
1043 }
ef416fc2 1044 }
bd7854cb 1045 else if (s[0] == '\"' && m)
ef416fc2 1046 {
bd7854cb 1047 /*
1048 * Append to current string...
1049 */
ef416fc2 1050
7e86f2f6 1051 length = strlen(m->str ? m->str : m->id);
5a9febac 1052 ptrlen = strlen(ptr);
ef416fc2 1053
7e86f2f6 1054 if ((temp = realloc(m->str ? m->str : m->id, length + ptrlen + 1)) == NULL)
bd7854cb 1055 {
82cc1f9a
MS
1056 if (m->str)
1057 free(m->str);
1058 free(m->id);
1059 free(m);
1060
bd7854cb 1061 cupsFileClose(fp);
1062 return (a);
1063 }
ef416fc2 1064
bd7854cb 1065 if (m->str)
1066 {
ef416fc2 1067 /*
bd7854cb 1068 * Copy the new portion to the end of the msgstr string - safe
5a9febac 1069 * to use memcpy because the buffer is allocated to the correct
bd7854cb 1070 * size...
ef416fc2 1071 */
1072
bd7854cb 1073 m->str = temp;
1074
5a9febac 1075 memcpy(m->str + length, ptr, ptrlen + 1);
ef416fc2 1076 }
1077 else
1078 {
1079 /*
bd7854cb 1080 * Copy the new portion to the end of the msgid string - safe
5a9febac 1081 * to use memcpy because the buffer is allocated to the correct
bd7854cb 1082 * size...
ef416fc2 1083 */
1084
bd7854cb 1085 m->id = temp;
1086
5a9febac 1087 memcpy(m->id + length, ptr, ptrlen + 1);
ef416fc2 1088 }
1089 }
bd7854cb 1090 else if (!strncmp(s, "msgstr", 6) && m)
1091 {
1092 /*
1093 * Set the string...
1094 */
1095
91c84a35
MS
1096 if ((m->str = strdup(ptr)) == NULL)
1097 {
82cc1f9a
MS
1098 free(m->id);
1099 free(m);
1100
91c84a35
MS
1101 cupsFileClose(fp);
1102 return (a);
1103 }
bd7854cb 1104 }
ef416fc2 1105 }
1106
bd7854cb 1107 /*
1108 * Add the last message string to the array as needed...
1109 */
1110
1111 if (m)
82cc1f9a
MS
1112 {
1113 if (m->str && m->str[0])
1114 {
1115 cupsArrayAdd(a, m);
1116 }
1117 else
1118 {
1119 /*
1120 * Translation is empty, don't add it... (STR #4033)
1121 */
1122
1123 free(m->id);
1124 if (m->str)
1125 free(m->str);
1126 free(m);
1127 }
1128 }
bd7854cb 1129
ef416fc2 1130 /*
1131 * Close the message catalog file and return the new array...
1132 */
1133
1134 cupsFileClose(fp);
1135
e07d4801 1136 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
c9fc04c6
MS
1137 cupsArrayCount(a)));
1138
ef416fc2 1139 return (a);
1140}
1141
1142
1143/*
1144 * '_cupsMessageLookup()' - Lookup a message string.
1145 */
1146
1147const char * /* O - Localized message */
1148_cupsMessageLookup(cups_array_t *a, /* I - Message array */
1149 const char *m) /* I - Message */
1150{
1151 _cups_message_t key, /* Search key */
1152 *match; /* Matching message */
1153
1154
a9357c9d
MS
1155 DEBUG_printf(("_cupsMessageLookup(a=%p, m=\"%s\")", (void *)a, m));
1156
ef416fc2 1157 /*
1158 * Lookup the message string; if it doesn't exist in the catalog,
1159 * then return the message that was passed to us...
1160 */
1161
1162 key.id = (char *)m;
1163 match = (_cups_message_t *)cupsArrayFind(a, &key);
1164
0837b7e8
MS
1165#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1166 if (!match && cupsArrayUserData(a))
1167 {
1168 /*
1169 * Try looking the string up in the cups.strings dictionary...
1170 */
1171
1172 CFDictionaryRef dict; /* cups.strings dictionary */
1173 CFStringRef cfm, /* Message as a CF string */
1174 cfstr; /* Localized text as a CF string */
1175
1176 dict = (CFDictionaryRef)cupsArrayUserData(a);
1177 cfm = CFStringCreateWithCString(kCFAllocatorDefault, m,
1178 kCFStringEncodingUTF8);
1179 match = calloc(1, sizeof(_cups_message_t));
1180 match->id = strdup(m);
1181 cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
1182
1183 if (cfstr)
1184 {
1185 char buffer[1024]; /* Message buffer */
1186
1187 CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
1188 match->str = strdup(buffer);
c8fef167
MS
1189
1190 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1191 m, buffer));
0837b7e8
MS
1192 }
1193 else
c8fef167 1194 {
0837b7e8
MS
1195 match->str = strdup(m);
1196
c8fef167
MS
1197 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m));
1198 }
1199
0837b7e8 1200 cupsArrayAdd(a, match);
e60ec91f
MS
1201
1202 if (cfm)
1203 CFRelease(cfm);
0837b7e8
MS
1204 }
1205#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1206
ef416fc2 1207 if (match && match->str)
1208 return (match->str);
1209 else
1210 return (m);
1211}
1212
1213
a29fd7dd
MS
1214/*
1215 * '_cupsMessageNew()' - Make a new message catalog array.
1216 */
1217
1218cups_array_t * /* O - Array */
1219_cupsMessageNew(void *context) /* I - User data */
1220{
1221 return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
1222 (cups_ahash_func_t)NULL, 0,
1223 (cups_acopy_func_t)NULL,
1224 (cups_afree_func_t)cups_message_free));
1225}
1226
1227
ef416fc2 1228#ifdef __APPLE__
ef416fc2 1229/*
1230 * 'appleLangDefault()' - Get the default locale string.
1231 */
1232
1233static const char * /* O - Locale string */
1234appleLangDefault(void)
1235{
db1f069b
MS
1236 CFBundleRef bundle; /* Main bundle (if any) */
1237 CFArrayRef bundleList; /* List of localizations in bundle */
b2656447 1238 CFPropertyListRef localizationList = NULL;
ef416fc2 1239 /* List of localization data */
1240 CFStringRef languageName; /* Current name */
26d47ec6 1241 char *lang; /* LANG environment variable */
ef416fc2 1242 _cups_globals_t *cg = _cupsGlobals();
1243 /* Pointer to library globals */
1244
1245
b0f6947b
MS
1246 DEBUG_puts("2appleLangDefault()");
1247
ef416fc2 1248 /*
1249 * Only do the lookup and translation the first time.
1250 */
1251
1252 if (!cg->language[0])
1253 {
b0f6947b 1254 if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
db1f069b 1255 {
94436c5a 1256 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang));
26d47ec6 1257 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1258 return (cg->language);
1259 }
1260 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1261 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1262 {
b2656447
MS
1263 CFURLRef resources = CFBundleCopyResourcesDirectoryURL(bundle);
1264
94436c5a
MS
1265 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1266
b2656447
MS
1267 if (resources)
1268 {
1269 CFStringRef cfpath = CFURLCopyPath(resources);
1270 char path[1024];
1271
1272 if (cfpath)
1273 {
1274 /*
1275 * See if we have an Info.plist file in the bundle...
1276 */
1277
36474350 1278 CFStringGetCString(cfpath, path, sizeof(path), kCFStringEncodingUTF8);
b2656447
MS
1279 DEBUG_printf(("3appleLangDefault: Got a resource URL (\"%s\")", path));
1280 strlcat(path, "Contents/Info.plist", sizeof(path));
1281
1282 if (!access(path, R_OK))
1283 localizationList = CFBundleCopyPreferredLocalizationsFromArray(bundleList);
1284 else
1285 DEBUG_puts("3appleLangDefault: No Info.plist, ignoring resource URL...");
1286
1287 CFRelease(cfpath);
1288 }
1289
1290 CFRelease(resources);
1291 }
1292 else
1293 DEBUG_puts("3appleLangDefault: No resource URL.");
26d47ec6 1294
db1f069b
MS
1295 CFRelease(bundleList);
1296 }
b2656447
MS
1297
1298 if (!localizationList)
94436c5a
MS
1299 {
1300 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1301
db1f069b
MS
1302 localizationList =
1303 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1304 kCFPreferencesCurrentApplication);
94436c5a 1305 }
db1f069b
MS
1306
1307 if (localizationList)
1308 {
94436c5a
MS
1309#ifdef DEBUG
1310 if (CFGetTypeID(localizationList) == CFArrayGetTypeID())
1311 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1312 (int)CFArrayGetCount(localizationList)));
1313 else
1314 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1315#endif /* DEBUG */
1316
db1f069b
MS
1317 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1318 CFArrayGetCount(localizationList) > 0)
ef416fc2 1319 {
db1f069b
MS
1320 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1321
1322 if (languageName &&
1323 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1324 {
454708a4 1325 if (_cupsAppleLocale(languageName, cg->language, sizeof(cg->language)))
94436c5a 1326 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
db1f069b 1327 cg->language));
94436c5a 1328 else
454708a4 1329 DEBUG_puts("3appleLangDefault: Unable to get locale.");
26d47ec6 1330 }
26d47ec6 1331 }
db1f069b
MS
1332
1333 CFRelease(localizationList);
ef416fc2 1334 }
c8fef167 1335
ef416fc2 1336 /*
1337 * If we didn't find the language, default to en_US...
1338 */
1339
1340 if (!cg->language[0])
94436c5a
MS
1341 {
1342 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
ef416fc2 1343 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
94436c5a 1344 }
ef416fc2 1345 }
36474350
MS
1346 else
1347 DEBUG_printf(("3appleLangDefault: Using previous locale \"%s\".", cg->language));
ef416fc2 1348
1349 /*
1350 * Return the cached locale...
1351 */
1352
1353 return (cg->language);
1354}
0837b7e8
MS
1355
1356
1357# ifdef CUPS_BUNDLEDIR
1358/*
1359 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1360 */
1361
1362static cups_array_t * /* O - Message catalog */
1363appleMessageLoad(const char *locale) /* I - Locale ID */
1364{
1365 char filename[1024], /* Path to cups.strings file */
36ac4374 1366 applelang[256], /* Apple language ID */
fdc80a91 1367 baselang[4]; /* Base language */
0837b7e8 1368 CFURLRef url; /* URL to cups.strings file */
c8fef167
MS
1369 CFReadStreamRef stream = NULL; /* File stream */
1370 CFPropertyListRef plist = NULL; /* Localization file */
1371#ifdef DEBUG
a9357c9d
MS
1372 const char *cups_strings = getenv("CUPS_STRINGS");
1373 /* Test strings file */
c8fef167
MS
1374 CFErrorRef error = NULL; /* Error when opening file */
1375#endif /* DEBUG */
1376
1377
1378 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
0837b7e8
MS
1379
1380 /*
1381 * Load the cups.strings file...
1382 */
1383
a9357c9d
MS
1384#ifdef DEBUG
1385 if (cups_strings)
1386 {
1387 DEBUG_puts("1appleMessageLoad: Using debug CUPS_STRINGS file.");
1388 strlcpy(filename, cups_strings, sizeof(filename));
1389 }
1390 else
1391#endif /* DEBUG */
1392
0837b7e8
MS
1393 snprintf(filename, sizeof(filename),
1394 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
1395 _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
88da9d73
MS
1396
1397 if (access(filename, 0))
1398 {
1399 /*
1400 * <rdar://problem/22086642>
1401 *
1402 * Try with original locale string...
1403 */
1404
a9357c9d 1405 DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
88da9d73
MS
1406 snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
1407 }
1408
b052deed
MS
1409 if (access(filename, 0))
1410 {
1411 /*
1412 * <rdar://problem/25292403>
1413 *
1414 * Try with just the language code...
1415 */
1416
a9357c9d
MS
1417 DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
1418
b052deed 1419 strlcpy(baselang, locale, sizeof(baselang));
fdc80a91
MS
1420 if (baselang[3] == '-' || baselang[3] == '_')
1421 baselang[3] = '\0';
1422
b052deed
MS
1423 snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", baselang);
1424 }
1425
c8fef167
MS
1426 if (access(filename, 0))
1427 {
1428 /*
1429 * Try alternate lproj directory names...
1430 */
1431
a9357c9d
MS
1432 DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
1433
c8fef167
MS
1434 if (!strncmp(locale, "en", 2))
1435 locale = "English";
b052deed
MS
1436 else if (!strncmp(locale, "nb", 2))
1437 locale = "no";
1438 else if (!strncmp(locale, "nl", 2))
c8fef167
MS
1439 locale = "Dutch";
1440 else if (!strncmp(locale, "fr", 2))
1441 locale = "French";
1442 else if (!strncmp(locale, "de", 2))
1443 locale = "German";
1444 else if (!strncmp(locale, "it", 2))
1445 locale = "Italian";
1446 else if (!strncmp(locale, "ja", 2))
1447 locale = "Japanese";
1448 else if (!strncmp(locale, "es", 2))
1449 locale = "Spanish";
fdc80a91 1450 else if (!strcmp(locale, "zh_HK") || !strncasecmp(locale, "zh-Hant", 7) || !strncasecmp(locale, "zh_Hant", 7))
bd3e2e22
MS
1451 {
1452 /*
1453 * <rdar://problem/22130168>
14fdcd7a 1454 * <rdar://problem/27245567>
bd3e2e22
MS
1455 *
1456 * Try zh_TW first, then zh... Sigh...
1457 */
1458
1459 if (!access(CUPS_BUNDLEDIR "/Resources/zh_TW.lproj/cups.strings", 0))
1460 locale = "zh_TW";
1461 else
1462 locale = "zh";
1463 }
36ac4374
MS
1464 else if (strstr(locale, "_") != NULL || strstr(locale, "-") != NULL)
1465 {
1466 /*
1467 * Drop country code, just try language...
1468 */
1469
1470 strlcpy(baselang, locale, sizeof(baselang));
fdc80a91
MS
1471 if (baselang[3] == '-' || baselang[3] == '_')
1472 baselang[3] = '\0';
1473
36ac4374
MS
1474 locale = baselang;
1475 }
c8fef167
MS
1476
1477 snprintf(filename, sizeof(filename),
1478 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
c8fef167
MS
1479 }
1480
a9357c9d
MS
1481 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
1482
c8fef167
MS
1483 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1484 (UInt8 *)filename,
7e86f2f6 1485 (CFIndex)strlen(filename), false);
c8fef167
MS
1486 if (url)
1487 {
1488 stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
1489 if (stream)
1490 {
82f97232
MS
1491 /*
1492 * Read the property list containing the localization data.
1493 *
1494 * NOTE: This code currently generates a clang "potential leak"
1495 * warning, but the object is released in _cupsMessageFree().
1496 */
1497
c8fef167
MS
1498 CFReadStreamOpen(stream);
1499
1500#ifdef DEBUG
1501 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1502 kCFPropertyListImmutable, NULL,
1503 &error);
1504 if (error)
1505 {
1506 CFStringRef msg = CFErrorCopyDescription(error);
1507 /* Error message */
1508
1509 CFStringGetCString(msg, filename, sizeof(filename),
1510 kCFStringEncodingUTF8);
1511 DEBUG_printf(("1appleMessageLoad: %s", filename));
1512
82f97232 1513 CFRelease(msg);
c8fef167
MS
1514 CFRelease(error);
1515 }
1516
1517#else
1518 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1519 kCFPropertyListImmutable, NULL,
1520 NULL);
1521#endif /* DEBUG */
1522
1523 if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
1524 {
1525 CFRelease(plist);
1526 plist = NULL;
1527 }
1528
1529 CFRelease(stream);
1530 }
1531
1532 CFRelease(url);
1533 }
0837b7e8 1534
c8fef167
MS
1535 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
1536 plist));
0837b7e8
MS
1537
1538 /*
1539 * Create and return an empty array to act as a cache for messages, passing the
c8fef167 1540 * plist as the user data.
0837b7e8
MS
1541 */
1542
a29fd7dd 1543 return (_cupsMessageNew((void *)plist));
0837b7e8
MS
1544}
1545# endif /* CUPS_BUNDLEDIR */
ef416fc2 1546#endif /* __APPLE__ */
1547
1548
1549/*
1550 * 'cups_cache_lookup()' - Lookup a language in the cache...
1551 */
1552
1553static cups_lang_t * /* O - Language data or NULL */
0837b7e8
MS
1554cups_cache_lookup(
1555 const char *name, /* I - Name of locale */
1556 cups_encoding_t encoding) /* I - Encoding of locale */
ef416fc2 1557{
1558 cups_lang_t *lang; /* Current language */
1559
1560
e07d4801 1561 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
ef416fc2 1562 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1563 lang_encodings[encoding]));
1564
1565 /*
1566 * Loop through the cache and return a match if found...
1567 */
1568
d6ae789d 1569 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1570 {
e07d4801 1571 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
807315e6 1572 "encoding=%d(%s)", (void *)lang, lang->language, lang->encoding,
ef416fc2 1573 lang_encodings[lang->encoding]));
1574
1575 if (!strcmp(lang->language, name) &&
1576 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1577 {
1578 lang->used ++;
1579
e07d4801 1580 DEBUG_puts("8cups_cache_lookup: returning match!");
ef416fc2 1581
1582 return (lang);
1583 }
1584 }
1585
e07d4801 1586 DEBUG_puts("8cups_cache_lookup: returning NULL!");
ef416fc2 1587
1588 return (NULL);
1589}
1590
1591
1592/*
1593 * 'cups_message_compare()' - Compare two messages.
1594 */
1595
1596static int /* O - Result of comparison */
1597cups_message_compare(
1598 _cups_message_t *m1, /* I - First message */
1599 _cups_message_t *m2) /* I - Second message */
1600{
1601 return (strcmp(m1->id, m2->id));
1602}
1603
1604
0837b7e8
MS
1605/*
1606 * 'cups_message_free()' - Free a message.
1607 */
1608
1609static void
1610cups_message_free(_cups_message_t *m) /* I - Message */
1611{
1612 if (m->id)
1613 free(m->id);
1614
1615 if (m->str)
1616 free(m->str);
1617
1618 free(m);
1619}
1620
1621
9c80ffa2
MS
1622/*
1623 * 'cups_message_load()' - Load the message catalog for a language.
1624 */
1625
1626static void
1627cups_message_load(cups_lang_t *lang) /* I - Language */
1628{
1629#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1630 lang->strings = appleMessageLoad(lang->language);
1631
1632#else
1633 char filename[1024]; /* Filename for language locale file */
1634 _cups_globals_t *cg = _cupsGlobals();
1635 /* Pointer to library globals */
1636
1637
1638 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
1639 lang->language, lang->language);
1640
1641 if (strchr(lang->language, '_') && access(filename, 0))
1642 {
1643 /*
1644 * Country localization not available, look for generic localization...
1645 */
1646
1647 snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
1648 lang->language, lang->language);
1649
1650 if (access(filename, 0))
1651 {
1652 /*
1653 * No generic localization, so use POSIX...
1654 */
1655
1656 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
1657 strerror(errno)));
1658
1659 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
1660 }
1661 }
1662
1663 /*
1664 * Read the strings from the file...
1665 */
1666
1667 lang->strings = _cupsMessageLoad(filename, 1);
1668#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1669}
1670
1671
ef416fc2 1672/*
1673 * 'cups_unquote()' - Unquote characters in strings...
1674 */
1675
1676static void
1677cups_unquote(char *d, /* O - Unquoted string */
1678 const char *s) /* I - Original string */
1679{
1680 while (*s)
1681 {
1682 if (*s == '\\')
1683 {
1684 s ++;
1685 if (isdigit(*s))
1686 {
1687 *d = 0;
1688
1689 while (isdigit(*s))
1690 {
1691 *d = *d * 8 + *s - '0';
1692 s ++;
1693 }
d09495fa 1694
1695 d ++;
ef416fc2 1696 }
1697 else
1698 {
1699 if (*s == 'n')
1700 *d ++ = '\n';
1701 else if (*s == 'r')
1702 *d ++ = '\r';
1703 else if (*s == 't')
1704 *d ++ = '\t';
1705 else
1706 *d++ = *s;
1707
1708 s ++;
1709 }
1710 }
1711 else
1712 *d++ = *s++;
1713 }
1714
1715 *d = '\0';
1716}