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