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