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