]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/language.c
Merge changes from CUPS 1.4svn-r8443.
[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, 1);
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 int unquote) /* I - Unescape \foo in strings */
888 {
889 cups_file_t *fp; /* Message file */
890 cups_array_t *a; /* Message array */
891 _cups_message_t *m; /* Current message */
892 char s[4096], /* String buffer */
893 *ptr, /* Pointer into buffer */
894 *temp; /* New string */
895 int length; /* Length of combined strings */
896
897
898 DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
899
900 /*
901 * Create an array to hold the messages...
902 */
903
904 if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
905 {
906 DEBUG_puts("_cupsMessageLoad: Unable to allocate array!");
907 return (NULL);
908 }
909
910 /*
911 * Open the message catalog file...
912 */
913
914 if ((fp = cupsFileOpen(filename, "r")) == NULL)
915 {
916 DEBUG_printf(("_cupsMessageLoad: Unable to open file: %s\n",
917 strerror(errno)));
918 return (a);
919 }
920
921 /*
922 * Read messages from the catalog file until EOF...
923 *
924 * The format is the GNU gettext .po format, which is fairly simple:
925 *
926 * msgid "some text"
927 * msgstr "localized text"
928 *
929 * The ID and localized text can span multiple lines using the form:
930 *
931 * msgid ""
932 * "some long text"
933 * msgstr ""
934 * "localized text spanning "
935 * "multiple lines"
936 */
937
938 m = NULL;
939
940 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
941 {
942 /*
943 * Skip blank and comment lines...
944 */
945
946 if (s[0] == '#' || !s[0])
947 continue;
948
949 /*
950 * Strip the trailing quote...
951 */
952
953 if ((ptr = strrchr(s, '\"')) == NULL)
954 continue;
955
956 *ptr = '\0';
957
958 /*
959 * Find start of value...
960 */
961
962 if ((ptr = strchr(s, '\"')) == NULL)
963 continue;
964
965 ptr ++;
966
967 /*
968 * Unquote the text...
969 */
970
971 if (unquote)
972 cups_unquote(ptr, ptr);
973
974 /*
975 * Create or add to a message...
976 */
977
978 if (!strncmp(s, "msgid", 5))
979 {
980 /*
981 * Add previous message as needed...
982 */
983
984 if (m)
985 cupsArrayAdd(a, m);
986
987 /*
988 * Create a new message with the given msgid string...
989 */
990
991 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
992 {
993 cupsFileClose(fp);
994 return (a);
995 }
996
997 if ((m->id = strdup(ptr)) == NULL)
998 {
999 free(m);
1000 cupsFileClose(fp);
1001 return (a);
1002 }
1003 }
1004 else if (s[0] == '\"' && m)
1005 {
1006 /*
1007 * Append to current string...
1008 */
1009
1010 length = (int)strlen(m->str ? m->str : m->id);
1011
1012 if ((temp = realloc(m->str ? m->str : m->id,
1013 length + strlen(ptr) + 1)) == NULL)
1014 {
1015 cupsFileClose(fp);
1016 return (a);
1017 }
1018
1019 if (m->str)
1020 {
1021 /*
1022 * Copy the new portion to the end of the msgstr string - safe
1023 * to use strcpy because the buffer is allocated to the correct
1024 * size...
1025 */
1026
1027 m->str = temp;
1028
1029 strcpy(m->str + length, ptr);
1030 }
1031 else
1032 {
1033 /*
1034 * Copy the new portion to the end of the msgid string - safe
1035 * to use strcpy because the buffer is allocated to the correct
1036 * size...
1037 */
1038
1039 m->id = temp;
1040
1041 strcpy(m->id + length, ptr);
1042 }
1043 }
1044 else if (!strncmp(s, "msgstr", 6) && m)
1045 {
1046 /*
1047 * Set the string...
1048 */
1049
1050 if ((m->str = strdup(ptr)) == NULL)
1051 {
1052 cupsFileClose(fp);
1053 return (a);
1054 }
1055 }
1056 }
1057
1058 /*
1059 * Add the last message string to the array as needed...
1060 */
1061
1062 if (m)
1063 cupsArrayAdd(a, m);
1064
1065 /*
1066 * Close the message catalog file and return the new array...
1067 */
1068
1069 cupsFileClose(fp);
1070
1071 DEBUG_printf(("_cupsMessageLoad: Returning %d messages...\n",
1072 cupsArrayCount(a)));
1073
1074 return (a);
1075 }
1076
1077
1078 /*
1079 * '_cupsMessageLookup()' - Lookup a message string.
1080 */
1081
1082 const char * /* O - Localized message */
1083 _cupsMessageLookup(cups_array_t *a, /* I - Message array */
1084 const char *m) /* I - Message */
1085 {
1086 _cups_message_t key, /* Search key */
1087 *match; /* Matching message */
1088
1089
1090 /*
1091 * Lookup the message string; if it doesn't exist in the catalog,
1092 * then return the message that was passed to us...
1093 */
1094
1095 key.id = (char *)m;
1096 match = (_cups_message_t *)cupsArrayFind(a, &key);
1097
1098 if (match && match->str)
1099 return (match->str);
1100 else
1101 return (m);
1102 }
1103
1104
1105 #ifdef __APPLE__
1106 /*
1107 * 'appleLangDefault()' - Get the default locale string.
1108 */
1109
1110 static const char * /* O - Locale string */
1111 appleLangDefault(void)
1112 {
1113 int i; /* Looping var */
1114 CFBundleRef bundle; /* Main bundle (if any) */
1115 CFArrayRef bundleList; /* List of localizations in bundle */
1116 CFPropertyListRef localizationList;
1117 /* List of localization data */
1118 CFStringRef languageName; /* Current name */
1119 CFStringRef localeName; /* Canonical from of name */
1120 char *lang; /* LANG environment variable */
1121 _cups_globals_t *cg = _cupsGlobals();
1122 /* Pointer to library globals */
1123
1124
1125 /*
1126 * Only do the lookup and translation the first time.
1127 */
1128
1129 if (!cg->language[0])
1130 {
1131 if ((lang = getenv("LANG")))
1132 {
1133 strlcpy(cg->language, lang, sizeof(cg->language));
1134 return (cg->language);
1135 }
1136 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1137 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
1138 {
1139 localizationList =
1140 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
1141
1142 CFRelease(bundleList);
1143 }
1144 else
1145 localizationList =
1146 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1147 kCFPreferencesCurrentApplication);
1148
1149 if (localizationList)
1150 {
1151 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1152 CFArrayGetCount(localizationList) > 0)
1153 {
1154 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1155
1156 if (languageName &&
1157 CFGetTypeID(languageName) == CFStringGetTypeID())
1158 {
1159 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1160 kCFAllocatorDefault, languageName);
1161
1162 if (localeName)
1163 {
1164 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1165 kCFStringEncodingASCII);
1166 CFRelease(localeName);
1167
1168 DEBUG_printf(("appleLangDefault: cg->language=\"%s\"\n",
1169 cg->language));
1170
1171 /*
1172 * Map new language identifiers to locales...
1173 */
1174
1175 for (i = 0;
1176 i < (int)(sizeof(apple_language_locale) /
1177 sizeof(apple_language_locale[0]));
1178 i ++)
1179 {
1180 if (!strcmp(cg->language, apple_language_locale[i].language))
1181 {
1182 DEBUG_printf(("appleLangDefault: mapping \"%s\" to \"%s\"...\n",
1183 cg->language, apple_language_locale[i].locale));
1184 strlcpy(cg->language, apple_language_locale[i].locale,
1185 sizeof(cg->language));
1186 break;
1187 }
1188 }
1189
1190 /*
1191 * Convert language subtag into region subtag...
1192 */
1193
1194 if (cg->language[2] == '-')
1195 cg->language[2] = '_';
1196
1197 if (!strchr(cg->language, '.'))
1198 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1199 }
1200 }
1201 }
1202
1203 CFRelease(localizationList);
1204 }
1205
1206 /*
1207 * If we didn't find the language, default to en_US...
1208 */
1209
1210 if (!cg->language[0])
1211 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1212 }
1213
1214 /*
1215 * Return the cached locale...
1216 */
1217
1218 return (cg->language);
1219 }
1220 #endif /* __APPLE__ */
1221
1222
1223 /*
1224 * 'cups_cache_lookup()' - Lookup a language in the cache...
1225 */
1226
1227 static cups_lang_t * /* O - Language data or NULL */
1228 cups_cache_lookup(const char *name,/* I - Name of locale */
1229 cups_encoding_t encoding)
1230 /* I - Encoding of locale */
1231 {
1232 cups_lang_t *lang; /* Current language */
1233
1234
1235 DEBUG_printf(("cups_cache_lookup(name=\"%s\", encoding=%d(%s))\n", name,
1236 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1237 lang_encodings[encoding]));
1238
1239 /*
1240 * Loop through the cache and return a match if found...
1241 */
1242
1243 for (lang = lang_cache; lang != NULL; lang = lang->next)
1244 {
1245 DEBUG_printf(("cups_cache_lookup: lang=%p, language=\"%s\", encoding=%d(%s)\n",
1246 lang, lang->language, lang->encoding,
1247 lang_encodings[lang->encoding]));
1248
1249 if (!strcmp(lang->language, name) &&
1250 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1251 {
1252 lang->used ++;
1253
1254 DEBUG_puts("cups_cache_lookup: returning match!");
1255
1256 return (lang);
1257 }
1258 }
1259
1260 DEBUG_puts("cups_cache_lookup: returning NULL!");
1261
1262 return (NULL);
1263 }
1264
1265
1266 /*
1267 * 'cups_message_compare()' - Compare two messages.
1268 */
1269
1270 static int /* O - Result of comparison */
1271 cups_message_compare(
1272 _cups_message_t *m1, /* I - First message */
1273 _cups_message_t *m2) /* I - Second message */
1274 {
1275 return (strcmp(m1->id, m2->id));
1276 }
1277
1278
1279 /*
1280 * 'cups_unquote()' - Unquote characters in strings...
1281 */
1282
1283 static void
1284 cups_unquote(char *d, /* O - Unquoted string */
1285 const char *s) /* I - Original string */
1286 {
1287 while (*s)
1288 {
1289 if (*s == '\\')
1290 {
1291 s ++;
1292 if (isdigit(*s))
1293 {
1294 *d = 0;
1295
1296 while (isdigit(*s))
1297 {
1298 *d = *d * 8 + *s - '0';
1299 s ++;
1300 }
1301
1302 d ++;
1303 }
1304 else
1305 {
1306 if (*s == 'n')
1307 *d ++ = '\n';
1308 else if (*s == 'r')
1309 *d ++ = '\r';
1310 else if (*s == 't')
1311 *d ++ = '\t';
1312 else
1313 *d++ = *s;
1314
1315 s ++;
1316 }
1317 }
1318 else
1319 *d++ = *s++;
1320 }
1321
1322 *d = '\0';
1323 }
1324
1325
1326 /*
1327 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
1328 */