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