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