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