]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Merge changes from CUPS 1.5svn-r9049 (private header support)
[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 *
71e16022 6 * Copyright 2007-2010 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 *
0a682745
MS
19 * _cupsAppleLanguage() - Get the Apple language identifier associated
20 * with a locale ID.
ef416fc2 21 * _cupsEncodingName() - Return the character encoding name string
22 * for the 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.
30 * _cupsMessageFree() - Free a messages array.
31 * _cupsMessageLoad() - Load a .po file into a messages array.
32 * _cupsMessageLookup() - Lookup a message string.
ef416fc2 33 * appleLangDefault() - Get the default locale string.
34 * cups_cache_lookup() - Lookup a language in the cache...
35 * cups_message_compare() - Compare two messages.
36 * cups_unquote() - Unquote characters in strings...
37 */
38
39/*
40 * Include necessary headers...
41 */
42
71e16022 43#include "cups-private.h"
ef416fc2 44#ifdef HAVE_LANGINFO_H
45# include <langinfo.h>
46#endif /* HAVE_LANGINFO_H */
47#ifdef WIN32
48# include <io.h>
49#else
50# include <unistd.h>
51#endif /* WIN32 */
fa73b229 52#ifdef HAVE_COREFOUNDATION_H
53# include <CoreFoundation/CoreFoundation.h>
54#endif /* HAVE_COREFOUNDATION_H */
ef416fc2 55
56
d6ae789d 57/*
58 * Local globals...
59 */
60
61#ifdef HAVE_PTHREAD_H
62static pthread_mutex_t lang_mutex = PTHREAD_MUTEX_INITIALIZER;
63 /* Mutex to control access to cache */
64#endif /* HAVE_PTHREAD_H */
65static cups_lang_t *lang_cache = NULL;
66 /* Language string cache */
ef416fc2 67static const char * const lang_encodings[] =
68 { /* Encoding strings */
69 "us-ascii", "iso-8859-1",
70 "iso-8859-2", "iso-8859-3",
71 "iso-8859-4", "iso-8859-5",
72 "iso-8859-6", "iso-8859-7",
73 "iso-8859-8", "iso-8859-9",
74 "iso-8859-10", "utf-8",
75 "iso-8859-13", "iso-8859-14",
76 "iso-8859-15", "windows-874",
77 "windows-1250", "windows-1251",
78 "windows-1252", "windows-1253",
79 "windows-1254", "windows-1255",
80 "windows-1256", "windows-1257",
81 "windows-1258", "koi8-r",
82 "koi8-u", "iso-8859-11",
bd7854cb 83 "iso-8859-16", "mac-roman",
ef416fc2 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 "windows-932", "windows-936",
102 "windows-949", "windows-950",
103 "windows-1361", "unknown",
104 "unknown", "unknown",
105 "unknown", "unknown",
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 "euc-cn", "euc-jp",
4b3f67ff
MS
134 "euc-kr", "euc-tw",
135 "jis-x0213"
ef416fc2 136 };
137
0a682745
MS
138#ifdef __APPLE__
139typedef struct
140{
141 const char * const language; /* Language ID */
142 const char * const locale; /* Locale ID */
143} _apple_language_locale_t;
144
145static const _apple_language_locale_t apple_language_locale[] =
146{ /* Locale to language ID LUT */
147 { "en" , "en_US" },
148 { "nb" , "no" },
149 { "zh-Hans" , "zh_CN" },
150 { "zh-Hant" , "zh_TW" }
151};
152#endif /* __APPLE__ */
153
154
155/*
156 * Local functions...
157 */
158
159#ifdef __APPLE__
160static const char *appleLangDefault(void);
161#endif /* __APPLE__ */
162static cups_lang_t *cups_cache_lookup(const char *name,
163 cups_encoding_t encoding);
164static int cups_message_compare(_cups_message_t *m1,
165 _cups_message_t *m2);
166static void cups_unquote(char *d, const char *s);
167
168
169#ifdef __APPLE__
170/*
171 * _cupsAppleLanguage() - Get the Apple language identifier associated
172 * with a locale ID.
173 */
174
175const char * /* O - Language ID */
176_cupsAppleLanguage(const char *locale, /* I - Locale ID */
177 char *language,/* I - Language ID buffer */
178 size_t langsize) /* I - Size of language ID buffer */
179{
180 int i; /* Looping var */
181 CFStringRef localeid, /* CF locale identifier */
182 langid; /* CF language identifier */
183
184
185 /*
186 * Copy the locale name and convert, as needed, to the Apple-specific
187 * locale identifier...
188 */
189
190 switch (strlen(locale))
191 {
192 default :
193 /*
194 * Invalid locale...
195 */
196
197 strlcpy(language, "en", langsize);
198 break;
199
200 case 2 :
201 strlcpy(language, locale, langsize);
202 break;
203
204 case 5 :
205 strlcpy(language, locale, langsize);
206
207 if (language[2] == '-')
208 {
209 /*
210 * Convert ll-cc to ll_CC...
211 */
212
213 language[2] = '_';
214 language[3] = toupper(language[3] & 255);
215 language[4] = toupper(language[4] & 255);
216 }
217 break;
218 }
219
220 for (i = 0;
221 i < (int)(sizeof(apple_language_locale) /
222 sizeof(apple_language_locale[0]));
223 i ++)
224 if (!strcmp(locale, apple_language_locale[i].locale))
225 {
226 strlcpy(language, apple_language_locale[i].language, sizeof(language));
227 break;
228 }
229
230 /*
231 * Attempt to map the locale ID to a language ID...
232 */
233
234 if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
235 kCFStringEncodingASCII)) != NULL)
236 {
237 if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
238 kCFAllocatorDefault, localeid)) != NULL)
239 {
240 CFStringGetCString(langid, language, langsize, kCFStringEncodingASCII);
241 CFRelease(langid);
242 }
243
244 CFRelease(localeid);
245 }
246
247 /*
248 * Return what we got...
249 */
250
251 return (language);
252}
253#endif /* __APPLE__ */
254
ef416fc2 255
256/*
257 * '_cupsEncodingName()' - Return the character encoding name string
258 * for the given encoding enumeration.
259 */
260
261const char * /* O - Character encoding */
262_cupsEncodingName(
263 cups_encoding_t encoding) /* I - Encoding value */
264{
265 if (encoding < 0 ||
266 encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
267 return (lang_encodings[0]);
268 else
269 return (lang_encodings[encoding]);
270}
271
272
273/*
274 * 'cupsLangDefault()' - Return the default language.
275 */
276
277cups_lang_t * /* O - Language data */
278cupsLangDefault(void)
279{
280 return (cupsLangGet(NULL));
281}
282
283
284/*
285 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
286 * for the given language.
287 */
288
289const char * /* O - Character encoding */
290cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
291{
292 if (lang == NULL)
293 return ((char*)lang_encodings[0]);
294 else
295 return ((char*)lang_encodings[lang->encoding]);
296}
297
298
299/*
300 * 'cupsLangFlush()' - Flush all language data out of the cache.
301 */
302
303void
304cupsLangFlush(void)
305{
306 cups_lang_t *lang, /* Current language */
307 *next; /* Next language */
ef416fc2 308
309
310 /*
311 * Free all languages in the cache...
312 */
313
d6ae789d 314#ifdef HAVE_PTHREAD_H
315 pthread_mutex_lock(&lang_mutex);
316#endif /* HAVE_PTHREAD_H */
317
318 for (lang = lang_cache; lang != NULL; lang = next)
ef416fc2 319 {
320 /*
321 * Free all messages...
322 */
323
324 _cupsMessageFree(lang->strings);
325
326 /*
327 * Then free the language structure itself...
328 */
329
330 next = lang->next;
331 free(lang);
332 }
333
d6ae789d 334 lang_cache = NULL;
335
336#ifdef HAVE_PTHREAD_H
337 pthread_mutex_unlock(&lang_mutex);
338#endif /* HAVE_PTHREAD_H */
ef416fc2 339}
340
341
342/*
343 * 'cupsLangFree()' - Free language data.
344 *
5a738aea 345 * This does not actually free anything; use @link cupsLangFlush@ for that.
ef416fc2 346 */
347
348void
349cupsLangFree(cups_lang_t *lang) /* I - Language to free */
350{
d6ae789d 351#ifdef HAVE_PTHREAD_H
352 pthread_mutex_lock(&lang_mutex);
353#endif /* HAVE_PTHREAD_H */
354
ef416fc2 355 if (lang != NULL && lang->used > 0)
356 lang->used --;
d6ae789d 357
358#ifdef HAVE_PTHREAD_H
359 pthread_mutex_unlock(&lang_mutex);
360#endif /* HAVE_PTHREAD_H */
ef416fc2 361}
362
363
364/*
365 * 'cupsLangGet()' - Get a language.
366 */
367
368cups_lang_t * /* O - Language data */
369cupsLangGet(const char *language) /* I - Language or locale */
370{
371 int i; /* Looping var */
480ef0fe 372#ifndef __APPLE__
373 char locale[255]; /* Copy of locale name */
374#endif /* !__APPLE__ */
375 char langname[16], /* Requested language name */
ef416fc2 376 country[16], /* Country code */
377 charset[16], /* Character set */
ef416fc2 378 *csptr, /* Pointer to CODESET string */
ef416fc2 379 *ptr, /* Pointer into language/charset */
380 real[48], /* Real language name */
381 filename[1024]; /* Filename for language locale file */
382 cups_encoding_t encoding; /* Encoding to use */
383 cups_lang_t *lang; /* Current language... */
ef416fc2 384 _cups_globals_t *cg = _cupsGlobals();
385 /* Pointer to library globals */
386 static const char * const locale_encodings[] =
387 { /* Locale charset names */
388 "ASCII", "ISO88591", "ISO88592", "ISO88593",
389 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
390 "ISO88598", "ISO88599", "ISO885910", "UTF8",
391 "ISO885913", "ISO885914", "ISO885915", "CP874",
392 "CP1250", "CP1251", "CP1252", "CP1253",
393 "CP1254", "CP1255", "CP1256", "CP1257",
394 "CP1258", "KOI8R", "KOI8U", "ISO885911",
bd7854cb 395 "ISO885916", "MACROMAN", "", "",
ef416fc2 396
397 "", "", "", "",
398 "", "", "", "",
399 "", "", "", "",
400 "", "", "", "",
401 "", "", "", "",
402 "", "", "", "",
403 "", "", "", "",
404 "", "", "", "",
405
406 "CP932", "CP936", "CP949", "CP950",
407 "CP1361", "", "", "",
408 "", "", "", "",
409 "", "", "", "",
410 "", "", "", "",
411 "", "", "", "",
412 "", "", "", "",
413 "", "", "", "",
414
415 "", "", "", "",
416 "", "", "", "",
417 "", "", "", "",
418 "", "", "", "",
419 "", "", "", "",
420 "", "", "", "",
421 "", "", "", "",
422 "", "", "", "",
423
4b3f67ff
MS
424 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
425 "SHIFT_JISX0213"
ef416fc2 426 };
427
428
f11a948a 429 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
ef416fc2 430
431#ifdef __APPLE__
757d2cad 432 /*
433 * Set the character set to UTF-8...
434 */
435
436 strcpy(charset, "UTF8");
437
ef416fc2 438 /*
439 * Apple's setlocale doesn't give us the user's localization
440 * preference so we have to look it up this way...
441 */
442
c9fc04c6
MS
443 if (!language)
444 {
b0f6947b 445 if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
c9fc04c6
MS
446 language = appleLangDefault();
447
f11a948a 448 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
c9fc04c6 449 }
757d2cad 450
ef416fc2 451#else
757d2cad 452 /*
453 * Set the charset to "unknown"...
454 */
455
456 charset[0] = '\0';
457
458 /*
459 * Use setlocale() to determine the currently set locale, and then
460 * fallback to environment variables to avoid setting the locale,
461 * since setlocale() is not thread-safe!
462 */
463
464 if (!language)
ef416fc2 465 {
466 /*
467 * First see if the locale has been set; if it is still "C" or
757d2cad 468 * "POSIX", use the environment to get the default...
ef416fc2 469 */
470
471# ifdef LC_MESSAGES
472 ptr = setlocale(LC_MESSAGES, NULL);
473# else
474 ptr = setlocale(LC_ALL, NULL);
475# endif /* LC_MESSAGES */
476
f11a948a 477 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
ef416fc2 478
479 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
ef416fc2 480 {
757d2cad 481 /*
482 * Get the character set from the LC_CTYPE locale setting...
483 */
484
485 if ((ptr = getenv("LC_CTYPE")) == NULL)
486 if ((ptr = getenv("LC_ALL")) == NULL)
487 if ((ptr = getenv("LANG")) == NULL)
488 ptr = "en_US";
489
490 if ((csptr = strchr(ptr, '.')) != NULL)
491 {
492 /*
493 * Extract the character set from the environment...
494 */
495
496 for (ptr = charset, csptr ++; *csptr; csptr ++)
497 if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
498 *ptr++ = *csptr;
499
500 *ptr = '\0';
501 }
757d2cad 502
503 /*
504 * Get the locale for messages from the LC_MESSAGES locale setting...
505 */
506
507 if ((ptr = getenv("LC_MESSAGES")) == NULL)
508 if ((ptr = getenv("LC_ALL")) == NULL)
509 if ((ptr = getenv("LANG")) == NULL)
510 ptr = "en_US";
ef416fc2 511 }
ef416fc2 512
513 if (ptr)
514 {
515 strlcpy(locale, ptr, sizeof(locale));
516 language = locale;
517
a41f09e2
MS
518 /*
519 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
520 */
521
522 if (!strncmp(locale, "nb", 2))
523 locale[1] = 'o';
524
f11a948a 525 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
ef416fc2 526 }
527 }
528#endif /* __APPLE__ */
529
530 /*
531 * If "language" is NULL at this point, then chances are we are using
532 * a language that is not installed for the base OS.
533 */
534
535 if (!language)
536 {
537 /*
757d2cad 538 * Switch to the POSIX ("C") locale...
ef416fc2 539 */
540
757d2cad 541 language = "C";
ef416fc2 542 }
543
ef416fc2 544#ifdef CODESET
545 /*
546 * On systems that support the nl_langinfo(CODESET) call, use
547 * this value as the character set...
548 */
549
757d2cad 550 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
ef416fc2 551 {
552 /*
553 * Copy all of the letters and numbers in the CODESET string...
554 */
555
556 for (ptr = charset; *csptr; csptr ++)
557 if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
558 *ptr++ = *csptr;
559
560 *ptr = '\0';
561
f11a948a 562 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
e07d4801 563 "nl_langinfo(CODESET)...", charset));
ef416fc2 564 }
565#endif /* CODESET */
566
07725fee 567 /*
568 * If we don't have a character set by now, default to UTF-8...
569 */
570
571 if (!charset[0])
572 strcpy(charset, "UTF8");
573
ef416fc2 574 /*
575 * Parse the language string passed in to a locale string. "C" is the
576 * standard POSIX locale and is copied unchanged. Otherwise the
577 * language string is converted from ll-cc[.charset] (language-country)
578 * to ll_CC[.CHARSET] to match the file naming convention used by all
579 * POSIX-compliant operating systems. Invalid language names are mapped
580 * to the POSIX locale.
581 */
582
583 country[0] = '\0';
584
585 if (language == NULL || !language[0] ||
586 !strcmp(language, "POSIX"))
587 strcpy(langname, "C");
588 else
589 {
590 /*
591 * Copy the parts of the locale string over safely...
592 */
593
594 for (ptr = langname; *language; language ++)
595 if (*language == '_' || *language == '-' || *language == '.')
596 break;
597 else if (ptr < (langname + sizeof(langname) - 1))
598 *ptr++ = tolower(*language & 255);
599
600 *ptr = '\0';
601
602 if (*language == '_' || *language == '-')
603 {
604 /*
605 * Copy the country code...
606 */
607
608 for (language ++, ptr = country; *language; language ++)
609 if (*language == '.')
610 break;
611 else if (ptr < (country + sizeof(country) - 1))
612 *ptr++ = toupper(*language & 255);
613
614 *ptr = '\0';
615 }
616
617 if (*language == '.' && !charset[0])
618 {
619 /*
620 * Copy the encoding...
621 */
622
623 for (language ++, ptr = charset; *language; language ++)
624 if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
625 *ptr++ = toupper(*language & 255);
626
627 *ptr = '\0';
628 }
629
630 /*
631 * Force a POSIX locale for an invalid language name...
632 */
633
634 if (strlen(langname) != 2)
635 {
636 strcpy(langname, "C");
637 country[0] = '\0';
638 charset[0] = '\0';
639 }
640 }
641
f11a948a 642 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
ef416fc2 643 langname, country, charset));
644
645 /*
646 * Figure out the desired encoding...
647 */
648
649 encoding = CUPS_AUTO_ENCODING;
650
651 if (charset[0])
652 {
bd7854cb 653 for (i = 0;
654 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
655 i ++)
ef416fc2 656 if (!strcasecmp(charset, locale_encodings[i]))
657 {
658 encoding = (cups_encoding_t)i;
659 break;
660 }
8ca02f3c 661
662 if (encoding == CUPS_AUTO_ENCODING)
663 {
664 /*
665 * Map alternate names for various character sets...
666 */
667
668 if (!strcasecmp(charset, "iso-2022-jp") ||
669 !strcasecmp(charset, "sjis"))
670 encoding = CUPS_WINDOWS_932;
671 else if (!strcasecmp(charset, "iso-2022-cn"))
672 encoding = CUPS_WINDOWS_936;
673 else if (!strcasecmp(charset, "iso-2022-kr"))
674 encoding = CUPS_WINDOWS_949;
675 else if (!strcasecmp(charset, "big5"))
676 encoding = CUPS_WINDOWS_950;
677 }
ef416fc2 678 }
679
f11a948a 680 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
ef416fc2 681 encoding == CUPS_AUTO_ENCODING ? "auto" :
682 lang_encodings[encoding]));
683
684 /*
685 * See if we already have this language/country loaded...
686 */
687
bd7854cb 688 if (country[0])
689 {
690 snprintf(real, sizeof(real), "%s_%s", langname, country);
ef416fc2 691
bd7854cb 692 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
693 real, real);
694 }
695 else
d6ae789d 696 {
697 strcpy(real, langname);
bd7854cb 698 filename[0] = '\0'; /* anti-compiler-warning-code */
d6ae789d 699 }
700
701#ifdef HAVE_PTHREAD_H
702 pthread_mutex_lock(&lang_mutex);
703#endif /* HAVE_PTHREAD_H */
704
c0e1af83 705 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
d6ae789d 706 {
707#ifdef HAVE_PTHREAD_H
708 pthread_mutex_unlock(&lang_mutex);
709#endif /* HAVE_PTHREAD_H */
710
f11a948a 711 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
c9fc04c6 712
d6ae789d 713 return (lang);
714 }
ef416fc2 715
716 if (!country[0] || access(filename, 0))
717 {
718 /*
719 * Country localization not available, look for generic localization...
720 */
721
bd7854cb 722 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
ef416fc2 723 langname, langname);
724
725 if (access(filename, 0))
726 {
727 /*
728 * No generic localization, so use POSIX...
729 */
730
f11a948a 731 DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename,
ae71f5de 732 strerror(errno)));
bd7854cb 733
bd7854cb 734 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
ef416fc2 735 }
ef416fc2 736 }
737
738 /*
739 * See if there is a free language available; if so, use that
740 * record...
741 */
742
d6ae789d 743 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 744 if (lang->used == 0)
745 break;
746
747 if (lang == NULL)
748 {
749 /*
750 * Allocate memory for the language and add it to the cache.
751 */
752
753 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
d6ae789d 754 {
755#ifdef HAVE_PTHREAD_H
756 pthread_mutex_unlock(&lang_mutex);
757#endif /* HAVE_PTHREAD_H */
758
ef416fc2 759 return (NULL);
d6ae789d 760 }
ef416fc2 761
d6ae789d 762 lang->next = lang_cache;
763 lang_cache = lang;
ef416fc2 764 }
bd7854cb 765 else
766 {
767 /*
768 * Free all old strings as needed...
769 */
ef416fc2 770
bd7854cb 771 _cupsMessageFree(lang->strings);
772 }
ef416fc2 773
774 /*
775 * Then assign the language and encoding fields...
776 */
777
778 lang->used ++;
779 strlcpy(lang->language, real, sizeof(lang->language));
780
781 if (encoding != CUPS_AUTO_ENCODING)
782 lang->encoding = encoding;
783 else
784 lang->encoding = CUPS_UTF8;
785
786 /*
787 * Read the strings from the file...
788 */
789
8b116e60 790 lang->strings = _cupsMessageLoad(filename, 1);
ef416fc2 791
792 /*
793 * Return...
794 */
795
d6ae789d 796#ifdef HAVE_PTHREAD_H
797 pthread_mutex_unlock(&lang_mutex);
798#endif /* HAVE_PTHREAD_H */
799
ef416fc2 800 return (lang);
801}
802
803
804/*
805 * '_cupsLangString()' - Get a message string.
806 *
807 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
808 * convert the string to the language encoding.
809 */
810
811const char * /* O - Localized message */
812_cupsLangString(cups_lang_t *lang, /* I - Language */
813 const char *message) /* I - Message */
814{
815 /*
816 * Range check input...
817 */
818
819 if (!lang || !message)
820 return (message);
821
d6ae789d 822#ifdef HAVE_PTHREAD_H
823 {
824 const char *s; /* Localized message */
825
826 pthread_mutex_lock(&lang_mutex);
827
828 s = _cupsMessageLookup(lang->strings, message);
829
830 pthread_mutex_unlock(&lang_mutex);
831
832 return (s);
833 }
834#else
ef416fc2 835 return (_cupsMessageLookup(lang->strings, message));
d6ae789d 836#endif /* HAVE_PTHREAD_H */
ef416fc2 837}
838
839
840/*
841 * '_cupsMessageFree()' - Free a messages array.
842 */
843
844void
845_cupsMessageFree(cups_array_t *a) /* I - Message array */
846{
847 _cups_message_t *m; /* Current message */
848
849
850 for (m = (_cups_message_t *)cupsArrayFirst(a);
851 m;
852 m = (_cups_message_t *)cupsArrayNext(a))
853 {
854 /*
855 * Remove the message from the array, then free the message and strings.
856 */
857
858 cupsArrayRemove(a, m);
859
860 if (m->id)
861 free(m->id);
862
863 if (m->str)
864 free(m->str);
865
866 free(m);
867 }
868
869 /*
870 * Free the array...
871 */
872
873 cupsArrayDelete(a);
874}
875
876
877/*
878 * '_cupsMessageLoad()' - Load a .po file into a messages array.
879 */
880
881cups_array_t * /* O - New message array */
8b116e60
MS
882_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
883 int unquote) /* I - Unescape \foo in strings */
ef416fc2 884{
885 cups_file_t *fp; /* Message file */
886 cups_array_t *a; /* Message array */
887 _cups_message_t *m; /* Current message */
888 char s[4096], /* String buffer */
889 *ptr, /* Pointer into buffer */
890 *temp; /* New string */
891 int length; /* Length of combined strings */
892
893
e07d4801 894 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
bd7854cb 895
ef416fc2 896 /*
897 * Create an array to hold the messages...
898 */
899
900 if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
c9fc04c6 901 {
e07d4801 902 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
ef416fc2 903 return (NULL);
c9fc04c6 904 }
ef416fc2 905
906 /*
907 * Open the message catalog file...
908 */
909
910 if ((fp = cupsFileOpen(filename, "r")) == NULL)
c9fc04c6 911 {
e07d4801 912 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
c9fc04c6 913 strerror(errno)));
ef416fc2 914 return (a);
c9fc04c6 915 }
ef416fc2 916
917 /*
918 * Read messages from the catalog file until EOF...
919 *
920 * The format is the GNU gettext .po format, which is fairly simple:
921 *
922 * msgid "some text"
923 * msgstr "localized text"
924 *
bd7854cb 925 * The ID and localized text can span multiple lines using the form:
ef416fc2 926 *
bd7854cb 927 * msgid ""
928 * "some long text"
929 * msgstr ""
930 * "localized text spanning "
ef416fc2 931 * "multiple lines"
932 */
933
934 m = NULL;
935
936 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
937 {
938 /*
939 * Skip blank and comment lines...
940 */
941
942 if (s[0] == '#' || !s[0])
943 continue;
944
945 /*
946 * Strip the trailing quote...
947 */
948
949 if ((ptr = strrchr(s, '\"')) == NULL)
950 continue;
951
952 *ptr = '\0';
953
954 /*
955 * Find start of value...
956 */
957
958 if ((ptr = strchr(s, '\"')) == NULL)
959 continue;
960
961 ptr ++;
962
963 /*
964 * Unquote the text...
965 */
966
8b116e60
MS
967 if (unquote)
968 cups_unquote(ptr, ptr);
ef416fc2 969
970 /*
971 * Create or add to a message...
972 */
973
974 if (!strncmp(s, "msgid", 5))
975 {
bd7854cb 976 /*
977 * Add previous message as needed...
978 */
979
980 if (m)
981 cupsArrayAdd(a, m);
982
983 /*
984 * Create a new message with the given msgid string...
985 */
986
ef416fc2 987 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
988 {
989 cupsFileClose(fp);
990 return (a);
991 }
992
91c84a35
MS
993 if ((m->id = strdup(ptr)) == NULL)
994 {
995 free(m);
996 cupsFileClose(fp);
997 return (a);
998 }
ef416fc2 999 }
bd7854cb 1000 else if (s[0] == '\"' && m)
ef416fc2 1001 {
bd7854cb 1002 /*
1003 * Append to current string...
1004 */
ef416fc2 1005
b86bc4cf 1006 length = (int)strlen(m->str ? m->str : m->id);
ef416fc2 1007
bd7854cb 1008 if ((temp = realloc(m->str ? m->str : m->id,
1009 length + strlen(ptr) + 1)) == NULL)
1010 {
1011 cupsFileClose(fp);
1012 return (a);
1013 }
ef416fc2 1014
bd7854cb 1015 if (m->str)
1016 {
ef416fc2 1017 /*
bd7854cb 1018 * Copy the new portion to the end of the msgstr string - safe
1019 * to use strcpy because the buffer is allocated to the correct
1020 * size...
ef416fc2 1021 */
1022
bd7854cb 1023 m->str = temp;
1024
ef416fc2 1025 strcpy(m->str + length, ptr);
1026 }
1027 else
1028 {
1029 /*
bd7854cb 1030 * Copy the new portion to the end of the msgid string - safe
1031 * to use strcpy because the buffer is allocated to the correct
1032 * size...
ef416fc2 1033 */
1034
bd7854cb 1035 m->id = temp;
1036
1037 strcpy(m->id + length, ptr);
ef416fc2 1038 }
1039 }
bd7854cb 1040 else if (!strncmp(s, "msgstr", 6) && m)
1041 {
1042 /*
1043 * Set the string...
1044 */
1045
91c84a35
MS
1046 if ((m->str = strdup(ptr)) == NULL)
1047 {
1048 cupsFileClose(fp);
1049 return (a);
1050 }
bd7854cb 1051 }
ef416fc2 1052 }
1053
bd7854cb 1054 /*
1055 * Add the last message string to the array as needed...
1056 */
1057
1058 if (m)
1059 cupsArrayAdd(a, m);
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
1094 if (match && match->str)
1095 return (match->str);
1096 else
1097 return (m);
1098}
1099
1100
ef416fc2 1101#ifdef __APPLE__
ef416fc2 1102/*
1103 * 'appleLangDefault()' - Get the default locale string.
1104 */
1105
1106static const char * /* O - Locale string */
1107appleLangDefault(void)
1108{
b94498cf 1109 int i; /* Looping var */
db1f069b
MS
1110 CFBundleRef bundle; /* Main bundle (if any) */
1111 CFArrayRef bundleList; /* List of localizations in bundle */
ef416fc2 1112 CFPropertyListRef localizationList;
1113 /* List of localization data */
1114 CFStringRef languageName; /* Current name */
1115 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1116 char *lang; /* LANG environment variable */
ef416fc2 1117 _cups_globals_t *cg = _cupsGlobals();
1118 /* Pointer to library globals */
1119
1120
b0f6947b
MS
1121 DEBUG_puts("2appleLangDefault()");
1122
ef416fc2 1123 /*
1124 * Only do the lookup and translation the first time.
1125 */
1126
1127 if (!cg->language[0])
1128 {
b0f6947b 1129 if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
db1f069b 1130 {
26d47ec6 1131 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1132 return (cg->language);
1133 }
1134 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1135 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1136 {
26d47ec6 1137 localizationList =
db1f069b 1138 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
26d47ec6 1139
db1f069b
MS
1140 CFRelease(bundleList);
1141 }
1142 else
1143 localizationList =
1144 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1145 kCFPreferencesCurrentApplication);
1146
1147 if (localizationList)
1148 {
1149 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1150 CFArrayGetCount(localizationList) > 0)
ef416fc2 1151 {
db1f069b
MS
1152 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1153
1154 if (languageName &&
1155 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1156 {
db1f069b
MS
1157 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1158 kCFAllocatorDefault, languageName);
ef416fc2 1159
db1f069b
MS
1160 if (localeName)
1161 {
1162 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1163 kCFStringEncodingASCII);
1164 CFRelease(localeName);
ef416fc2 1165
e07d4801 1166 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
db1f069b 1167 cg->language));
26d47ec6 1168
db1f069b
MS
1169 /*
1170 * Map new language identifiers to locales...
1171 */
b94498cf 1172
db1f069b 1173 for (i = 0;
0a682745
MS
1174 i < (int)(sizeof(apple_language_locale) /
1175 sizeof(apple_language_locale[0]));
1176 i ++)
db1f069b 1177 {
0a682745 1178 if (!strcmp(cg->language, apple_language_locale[i].language))
b94498cf 1179 {
e07d4801 1180 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
0a682745
MS
1181 cg->language, apple_language_locale[i].locale));
1182 strlcpy(cg->language, apple_language_locale[i].locale,
db1f069b
MS
1183 sizeof(cg->language));
1184 break;
b94498cf 1185 }
db1f069b 1186 }
b94498cf 1187
db1f069b
MS
1188 /*
1189 * Convert language subtag into region subtag...
1190 */
b94498cf 1191
db1f069b
MS
1192 if (cg->language[2] == '-')
1193 cg->language[2] = '_';
b94498cf 1194
db1f069b
MS
1195 if (!strchr(cg->language, '.'))
1196 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1197 }
26d47ec6 1198 }
26d47ec6 1199 }
db1f069b
MS
1200
1201 CFRelease(localizationList);
ef416fc2 1202 }
1203
1204 /*
1205 * If we didn't find the language, default to en_US...
1206 */
1207
1208 if (!cg->language[0])
1209 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1210 }
1211
1212 /*
1213 * Return the cached locale...
1214 */
1215
1216 return (cg->language);
1217}
ef416fc2 1218#endif /* __APPLE__ */
1219
1220
1221/*
1222 * 'cups_cache_lookup()' - Lookup a language in the cache...
1223 */
1224
1225static cups_lang_t * /* O - Language data or NULL */
1226cups_cache_lookup(const char *name,/* I - Name of locale */
1227 cups_encoding_t encoding)
1228 /* I - Encoding of locale */
1229{
1230 cups_lang_t *lang; /* Current language */
1231
1232
e07d4801 1233 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
ef416fc2 1234 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1235 lang_encodings[encoding]));
1236
1237 /*
1238 * Loop through the cache and return a match if found...
1239 */
1240
d6ae789d 1241 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1242 {
e07d4801
MS
1243 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1244 "encoding=%d(%s)", lang, lang->language, lang->encoding,
ef416fc2 1245 lang_encodings[lang->encoding]));
1246
1247 if (!strcmp(lang->language, name) &&
1248 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1249 {
1250 lang->used ++;
1251
e07d4801 1252 DEBUG_puts("8cups_cache_lookup: returning match!");
ef416fc2 1253
1254 return (lang);
1255 }
1256 }
1257
e07d4801 1258 DEBUG_puts("8cups_cache_lookup: returning NULL!");
ef416fc2 1259
1260 return (NULL);
1261}
1262
1263
1264/*
1265 * 'cups_message_compare()' - Compare two messages.
1266 */
1267
1268static int /* O - Result of comparison */
1269cups_message_compare(
1270 _cups_message_t *m1, /* I - First message */
1271 _cups_message_t *m2) /* I - Second message */
1272{
1273 return (strcmp(m1->id, m2->id));
1274}
1275
1276
1277/*
1278 * 'cups_unquote()' - Unquote characters in strings...
1279 */
1280
1281static void
1282cups_unquote(char *d, /* O - Unquoted string */
1283 const char *s) /* I - Original string */
1284{
1285 while (*s)
1286 {
1287 if (*s == '\\')
1288 {
1289 s ++;
1290 if (isdigit(*s))
1291 {
1292 *d = 0;
1293
1294 while (isdigit(*s))
1295 {
1296 *d = *d * 8 + *s - '0';
1297 s ++;
1298 }
d09495fa 1299
1300 d ++;
ef416fc2 1301 }
1302 else
1303 {
1304 if (*s == 'n')
1305 *d ++ = '\n';
1306 else if (*s == 'r')
1307 *d ++ = '\r';
1308 else if (*s == 't')
1309 *d ++ = '\t';
1310 else
1311 *d++ = *s;
1312
1313 s ++;
1314 }
1315 }
1316 else
1317 *d++ = *s++;
1318 }
1319
1320 *d = '\0';
1321}
1322
1323
1324/*
75bd9771 1325 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
ef416fc2 1326 */