]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Merge changes from CUPS 1.6svn-r10510.
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
75bd9771 2 * "$Id: language.c 7558 2008-05-12 23:46:44Z mike $"
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
MS
136 "euc-kr", "euc-tw",
137 "jis-x0213"
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
449 strcpy(charset, "UTF8");
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])
585 strcpy(charset, "UTF8");
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"))
600 strcpy(langname, "C");
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 {
649 strcpy(langname, "C");
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
d6ae789d 704 strcpy(real, langname);
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
791 if (!lang || !message)
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 */
850
851
e07d4801 852 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
bd7854cb 853
ef416fc2 854 /*
855 * Create an array to hold the messages...
856 */
857
a29fd7dd 858 if ((a = _cupsMessageNew(NULL)) == NULL)
c9fc04c6 859 {
e07d4801 860 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
ef416fc2 861 return (NULL);
c9fc04c6 862 }
ef416fc2 863
864 /*
865 * Open the message catalog file...
866 */
867
868 if ((fp = cupsFileOpen(filename, "r")) == NULL)
c9fc04c6 869 {
e07d4801 870 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
c9fc04c6 871 strerror(errno)));
ef416fc2 872 return (a);
c9fc04c6 873 }
ef416fc2 874
875 /*
876 * Read messages from the catalog file until EOF...
877 *
878 * The format is the GNU gettext .po format, which is fairly simple:
879 *
880 * msgid "some text"
881 * msgstr "localized text"
882 *
bd7854cb 883 * The ID and localized text can span multiple lines using the form:
ef416fc2 884 *
bd7854cb 885 * msgid ""
886 * "some long text"
887 * msgstr ""
888 * "localized text spanning "
ef416fc2 889 * "multiple lines"
890 */
891
892 m = NULL;
893
894 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
895 {
896 /*
897 * Skip blank and comment lines...
898 */
899
900 if (s[0] == '#' || !s[0])
901 continue;
902
903 /*
904 * Strip the trailing quote...
905 */
906
907 if ((ptr = strrchr(s, '\"')) == NULL)
908 continue;
909
910 *ptr = '\0';
911
912 /*
913 * Find start of value...
914 */
c8fef167 915
ef416fc2 916 if ((ptr = strchr(s, '\"')) == NULL)
917 continue;
918
919 ptr ++;
920
921 /*
922 * Unquote the text...
923 */
924
8b116e60
MS
925 if (unquote)
926 cups_unquote(ptr, ptr);
ef416fc2 927
928 /*
929 * Create or add to a message...
930 */
931
932 if (!strncmp(s, "msgid", 5))
933 {
bd7854cb 934 /*
935 * Add previous message as needed...
936 */
937
938 if (m)
82cc1f9a
MS
939 {
940 if (m->str && m->str[0])
941 {
942 cupsArrayAdd(a, m);
943 }
944 else
945 {
946 /*
947 * Translation is empty, don't add it... (STR #4033)
948 */
949
950 free(m->id);
951 if (m->str)
952 free(m->str);
953 free(m);
954 }
955 }
bd7854cb 956
957 /*
958 * Create a new message with the given msgid string...
959 */
960
ef416fc2 961 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
962 {
963 cupsFileClose(fp);
964 return (a);
965 }
966
91c84a35
MS
967 if ((m->id = strdup(ptr)) == NULL)
968 {
969 free(m);
970 cupsFileClose(fp);
971 return (a);
972 }
ef416fc2 973 }
bd7854cb 974 else if (s[0] == '\"' && m)
ef416fc2 975 {
bd7854cb 976 /*
977 * Append to current string...
978 */
ef416fc2 979
b86bc4cf 980 length = (int)strlen(m->str ? m->str : m->id);
ef416fc2 981
bd7854cb 982 if ((temp = realloc(m->str ? m->str : m->id,
983 length + strlen(ptr) + 1)) == NULL)
984 {
82cc1f9a
MS
985 if (m->str)
986 free(m->str);
987 free(m->id);
988 free(m);
989
bd7854cb 990 cupsFileClose(fp);
991 return (a);
992 }
ef416fc2 993
bd7854cb 994 if (m->str)
995 {
ef416fc2 996 /*
bd7854cb 997 * Copy the new portion to the end of the msgstr string - safe
998 * to use strcpy because the buffer is allocated to the correct
999 * size...
ef416fc2 1000 */
1001
bd7854cb 1002 m->str = temp;
1003
ef416fc2 1004 strcpy(m->str + length, ptr);
1005 }
1006 else
1007 {
1008 /*
bd7854cb 1009 * Copy the new portion to the end of the msgid string - safe
1010 * to use strcpy because the buffer is allocated to the correct
1011 * size...
ef416fc2 1012 */
1013
bd7854cb 1014 m->id = temp;
1015
1016 strcpy(m->id + length, ptr);
ef416fc2 1017 }
1018 }
bd7854cb 1019 else if (!strncmp(s, "msgstr", 6) && m)
1020 {
1021 /*
1022 * Set the string...
1023 */
1024
91c84a35
MS
1025 if ((m->str = strdup(ptr)) == NULL)
1026 {
82cc1f9a
MS
1027 free(m->id);
1028 free(m);
1029
91c84a35
MS
1030 cupsFileClose(fp);
1031 return (a);
1032 }
bd7854cb 1033 }
ef416fc2 1034 }
1035
bd7854cb 1036 /*
1037 * Add the last message string to the array as needed...
1038 */
1039
1040 if (m)
82cc1f9a
MS
1041 {
1042 if (m->str && m->str[0])
1043 {
1044 cupsArrayAdd(a, m);
1045 }
1046 else
1047 {
1048 /*
1049 * Translation is empty, don't add it... (STR #4033)
1050 */
1051
1052 free(m->id);
1053 if (m->str)
1054 free(m->str);
1055 free(m);
1056 }
1057 }
bd7854cb 1058
ef416fc2 1059 /*
1060 * Close the message catalog file and return the new array...
1061 */
1062
1063 cupsFileClose(fp);
1064
e07d4801 1065 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
c9fc04c6
MS
1066 cupsArrayCount(a)));
1067
ef416fc2 1068 return (a);
1069}
1070
1071
1072/*
1073 * '_cupsMessageLookup()' - Lookup a message string.
1074 */
1075
1076const char * /* O - Localized message */
1077_cupsMessageLookup(cups_array_t *a, /* I - Message array */
1078 const char *m) /* I - Message */
1079{
1080 _cups_message_t key, /* Search key */
1081 *match; /* Matching message */
1082
1083
1084 /*
1085 * Lookup the message string; if it doesn't exist in the catalog,
1086 * then return the message that was passed to us...
1087 */
1088
1089 key.id = (char *)m;
1090 match = (_cups_message_t *)cupsArrayFind(a, &key);
1091
0837b7e8
MS
1092#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1093 if (!match && cupsArrayUserData(a))
1094 {
1095 /*
1096 * Try looking the string up in the cups.strings dictionary...
1097 */
1098
1099 CFDictionaryRef dict; /* cups.strings dictionary */
1100 CFStringRef cfm, /* Message as a CF string */
1101 cfstr; /* Localized text as a CF string */
1102
1103 dict = (CFDictionaryRef)cupsArrayUserData(a);
1104 cfm = CFStringCreateWithCString(kCFAllocatorDefault, m,
1105 kCFStringEncodingUTF8);
1106 match = calloc(1, sizeof(_cups_message_t));
1107 match->id = strdup(m);
1108 cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
1109
1110 if (cfstr)
1111 {
1112 char buffer[1024]; /* Message buffer */
1113
1114 CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
1115 match->str = strdup(buffer);
c8fef167
MS
1116
1117 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1118 m, buffer));
0837b7e8
MS
1119 }
1120 else
c8fef167 1121 {
0837b7e8
MS
1122 match->str = strdup(m);
1123
c8fef167
MS
1124 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m));
1125 }
1126
0837b7e8 1127 cupsArrayAdd(a, match);
e60ec91f
MS
1128
1129 if (cfm)
1130 CFRelease(cfm);
0837b7e8
MS
1131 }
1132#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1133
ef416fc2 1134 if (match && match->str)
1135 return (match->str);
1136 else
1137 return (m);
1138}
1139
1140
a29fd7dd
MS
1141/*
1142 * '_cupsMessageNew()' - Make a new message catalog array.
1143 */
1144
1145cups_array_t * /* O - Array */
1146_cupsMessageNew(void *context) /* I - User data */
1147{
1148 return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
1149 (cups_ahash_func_t)NULL, 0,
1150 (cups_acopy_func_t)NULL,
1151 (cups_afree_func_t)cups_message_free));
1152}
1153
1154
ef416fc2 1155#ifdef __APPLE__
ef416fc2 1156/*
1157 * 'appleLangDefault()' - Get the default locale string.
1158 */
1159
1160static const char * /* O - Locale string */
1161appleLangDefault(void)
1162{
b94498cf 1163 int i; /* Looping var */
db1f069b
MS
1164 CFBundleRef bundle; /* Main bundle (if any) */
1165 CFArrayRef bundleList; /* List of localizations in bundle */
ef416fc2 1166 CFPropertyListRef localizationList;
1167 /* List of localization data */
1168 CFStringRef languageName; /* Current name */
1169 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1170 char *lang; /* LANG environment variable */
ef416fc2 1171 _cups_globals_t *cg = _cupsGlobals();
1172 /* Pointer to library globals */
1173
1174
b0f6947b
MS
1175 DEBUG_puts("2appleLangDefault()");
1176
ef416fc2 1177 /*
1178 * Only do the lookup and translation the first time.
1179 */
1180
1181 if (!cg->language[0])
1182 {
b0f6947b 1183 if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
db1f069b 1184 {
26d47ec6 1185 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1186 return (cg->language);
1187 }
1188 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1189 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1190 {
26d47ec6 1191 localizationList =
db1f069b 1192 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
26d47ec6 1193
db1f069b
MS
1194 CFRelease(bundleList);
1195 }
1196 else
1197 localizationList =
1198 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1199 kCFPreferencesCurrentApplication);
1200
1201 if (localizationList)
1202 {
1203 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1204 CFArrayGetCount(localizationList) > 0)
ef416fc2 1205 {
db1f069b
MS
1206 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1207
1208 if (languageName &&
1209 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1210 {
db1f069b
MS
1211 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1212 kCFAllocatorDefault, languageName);
ef416fc2 1213
db1f069b
MS
1214 if (localeName)
1215 {
1216 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1217 kCFStringEncodingASCII);
1218 CFRelease(localeName);
ef416fc2 1219
e07d4801 1220 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
db1f069b 1221 cg->language));
26d47ec6 1222
db1f069b
MS
1223 /*
1224 * Map new language identifiers to locales...
1225 */
b94498cf 1226
db1f069b 1227 for (i = 0;
0a682745
MS
1228 i < (int)(sizeof(apple_language_locale) /
1229 sizeof(apple_language_locale[0]));
1230 i ++)
db1f069b 1231 {
0a682745 1232 if (!strcmp(cg->language, apple_language_locale[i].language))
b94498cf 1233 {
e07d4801 1234 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
0a682745 1235 cg->language, apple_language_locale[i].locale));
c8fef167 1236 strlcpy(cg->language, apple_language_locale[i].locale,
db1f069b
MS
1237 sizeof(cg->language));
1238 break;
b94498cf 1239 }
db1f069b 1240 }
b94498cf 1241
db1f069b
MS
1242 /*
1243 * Convert language subtag into region subtag...
1244 */
b94498cf 1245
db1f069b
MS
1246 if (cg->language[2] == '-')
1247 cg->language[2] = '_';
b94498cf 1248
db1f069b
MS
1249 if (!strchr(cg->language, '.'))
1250 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1251 }
26d47ec6 1252 }
26d47ec6 1253 }
db1f069b
MS
1254
1255 CFRelease(localizationList);
ef416fc2 1256 }
c8fef167 1257
ef416fc2 1258 /*
1259 * If we didn't find the language, default to en_US...
1260 */
1261
1262 if (!cg->language[0])
1263 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1264 }
1265
1266 /*
1267 * Return the cached locale...
1268 */
1269
1270 return (cg->language);
1271}
0837b7e8
MS
1272
1273
1274# ifdef CUPS_BUNDLEDIR
1275/*
1276 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1277 */
1278
1279static cups_array_t * /* O - Message catalog */
1280appleMessageLoad(const char *locale) /* I - Locale ID */
1281{
1282 char filename[1024], /* Path to cups.strings file */
1283 applelang[256]; /* Apple language ID */
1284 CFURLRef url; /* URL to cups.strings file */
c8fef167
MS
1285 CFReadStreamRef stream = NULL; /* File stream */
1286 CFPropertyListRef plist = NULL; /* Localization file */
1287#ifdef DEBUG
1288 CFErrorRef error = NULL; /* Error when opening file */
1289#endif /* DEBUG */
1290
1291
1292 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
0837b7e8
MS
1293
1294 /*
1295 * Load the cups.strings file...
1296 */
1297
1298 snprintf(filename, sizeof(filename),
1299 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
1300 _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
c8fef167
MS
1301 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
1302
1303 if (access(filename, 0))
1304 {
1305 /*
1306 * Try alternate lproj directory names...
1307 */
1308
1309 if (!strncmp(locale, "en", 2))
1310 locale = "English";
1311 else if (!strncmp(locale, "nb", 2) || !strncmp(locale, "nl", 2))
1312 locale = "Dutch";
1313 else if (!strncmp(locale, "fr", 2))
1314 locale = "French";
1315 else if (!strncmp(locale, "de", 2))
1316 locale = "German";
1317 else if (!strncmp(locale, "it", 2))
1318 locale = "Italian";
1319 else if (!strncmp(locale, "ja", 2))
1320 locale = "Japanese";
1321 else if (!strncmp(locale, "es", 2))
1322 locale = "Spanish";
1323
1324 snprintf(filename, sizeof(filename),
1325 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
1326 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
1327 }
1328
1329 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1330 (UInt8 *)filename,
1331 strlen(filename), false);
1332 if (url)
1333 {
1334 stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
1335 if (stream)
1336 {
82f97232
MS
1337 /*
1338 * Read the property list containing the localization data.
1339 *
1340 * NOTE: This code currently generates a clang "potential leak"
1341 * warning, but the object is released in _cupsMessageFree().
1342 */
1343
c8fef167
MS
1344 CFReadStreamOpen(stream);
1345
1346#ifdef DEBUG
1347 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1348 kCFPropertyListImmutable, NULL,
1349 &error);
1350 if (error)
1351 {
1352 CFStringRef msg = CFErrorCopyDescription(error);
1353 /* Error message */
1354
1355 CFStringGetCString(msg, filename, sizeof(filename),
1356 kCFStringEncodingUTF8);
1357 DEBUG_printf(("1appleMessageLoad: %s", filename));
1358
82f97232 1359 CFRelease(msg);
c8fef167
MS
1360 CFRelease(error);
1361 }
1362
1363#else
1364 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1365 kCFPropertyListImmutable, NULL,
1366 NULL);
1367#endif /* DEBUG */
1368
1369 if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
1370 {
1371 CFRelease(plist);
1372 plist = NULL;
1373 }
1374
1375 CFRelease(stream);
1376 }
1377
1378 CFRelease(url);
1379 }
0837b7e8 1380
c8fef167
MS
1381 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
1382 plist));
0837b7e8
MS
1383
1384 /*
1385 * Create and return an empty array to act as a cache for messages, passing the
c8fef167 1386 * plist as the user data.
0837b7e8
MS
1387 */
1388
a29fd7dd 1389 return (_cupsMessageNew((void *)plist));
0837b7e8
MS
1390}
1391# endif /* CUPS_BUNDLEDIR */
ef416fc2 1392#endif /* __APPLE__ */
1393
1394
1395/*
1396 * 'cups_cache_lookup()' - Lookup a language in the cache...
1397 */
1398
1399static cups_lang_t * /* O - Language data or NULL */
0837b7e8
MS
1400cups_cache_lookup(
1401 const char *name, /* I - Name of locale */
1402 cups_encoding_t encoding) /* I - Encoding of locale */
ef416fc2 1403{
1404 cups_lang_t *lang; /* Current language */
1405
1406
e07d4801 1407 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
ef416fc2 1408 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1409 lang_encodings[encoding]));
1410
1411 /*
1412 * Loop through the cache and return a match if found...
1413 */
1414
d6ae789d 1415 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1416 {
e07d4801
MS
1417 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1418 "encoding=%d(%s)", lang, lang->language, lang->encoding,
ef416fc2 1419 lang_encodings[lang->encoding]));
1420
1421 if (!strcmp(lang->language, name) &&
1422 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1423 {
1424 lang->used ++;
1425
e07d4801 1426 DEBUG_puts("8cups_cache_lookup: returning match!");
ef416fc2 1427
1428 return (lang);
1429 }
1430 }
1431
e07d4801 1432 DEBUG_puts("8cups_cache_lookup: returning NULL!");
ef416fc2 1433
1434 return (NULL);
1435}
1436
1437
1438/*
1439 * 'cups_message_compare()' - Compare two messages.
1440 */
1441
1442static int /* O - Result of comparison */
1443cups_message_compare(
1444 _cups_message_t *m1, /* I - First message */
1445 _cups_message_t *m2) /* I - Second message */
1446{
1447 return (strcmp(m1->id, m2->id));
1448}
1449
1450
0837b7e8
MS
1451/*
1452 * 'cups_message_free()' - Free a message.
1453 */
1454
1455static void
1456cups_message_free(_cups_message_t *m) /* I - Message */
1457{
1458 if (m->id)
1459 free(m->id);
1460
1461 if (m->str)
1462 free(m->str);
1463
1464 free(m);
1465}
1466
1467
9c80ffa2
MS
1468/*
1469 * 'cups_message_load()' - Load the message catalog for a language.
1470 */
1471
1472static void
1473cups_message_load(cups_lang_t *lang) /* I - Language */
1474{
1475#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1476 lang->strings = appleMessageLoad(lang->language);
1477
1478#else
1479 char filename[1024]; /* Filename for language locale file */
1480 _cups_globals_t *cg = _cupsGlobals();
1481 /* Pointer to library globals */
1482
1483
1484 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
1485 lang->language, lang->language);
1486
1487 if (strchr(lang->language, '_') && access(filename, 0))
1488 {
1489 /*
1490 * Country localization not available, look for generic localization...
1491 */
1492
1493 snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
1494 lang->language, lang->language);
1495
1496 if (access(filename, 0))
1497 {
1498 /*
1499 * No generic localization, so use POSIX...
1500 */
1501
1502 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
1503 strerror(errno)));
1504
1505 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
1506 }
1507 }
1508
1509 /*
1510 * Read the strings from the file...
1511 */
1512
1513 lang->strings = _cupsMessageLoad(filename, 1);
1514#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1515}
1516
1517
ef416fc2 1518/*
1519 * 'cups_unquote()' - Unquote characters in strings...
1520 */
1521
1522static void
1523cups_unquote(char *d, /* O - Unquoted string */
1524 const char *s) /* I - Original string */
1525{
1526 while (*s)
1527 {
1528 if (*s == '\\')
1529 {
1530 s ++;
1531 if (isdigit(*s))
1532 {
1533 *d = 0;
1534
1535 while (isdigit(*s))
1536 {
1537 *d = *d * 8 + *s - '0';
1538 s ++;
1539 }
d09495fa 1540
1541 d ++;
ef416fc2 1542 }
1543 else
1544 {
1545 if (*s == 'n')
1546 *d ++ = '\n';
1547 else if (*s == 'r')
1548 *d ++ = '\r';
1549 else if (*s == 't')
1550 *d ++ = '\t';
1551 else
1552 *d++ = *s;
1553
1554 s ++;
1555 }
1556 }
1557 else
1558 *d++ = *s++;
1559 }
1560
1561 *d = '\0';
1562}
1563
1564
1565/*
75bd9771 1566 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
ef416fc2 1567 */