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