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