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