]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/language.c
Merge changes from CUPS 1.4svn-r8606.
[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-2009 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(("2cupsLangGet(language=\"%s\")", language));
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(("4cupsLangGet: language=\"%s\"", 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(("4cupsLangGet: current locale is \"%s\"", ptr));
481
482 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
483 {
484 /*
485 * Get the character set from the LC_CTYPE locale setting...
486 */
487
488 if ((ptr = getenv("LC_CTYPE")) == NULL)
489 if ((ptr = getenv("LC_ALL")) == NULL)
490 if ((ptr = getenv("LANG")) == NULL)
491 ptr = "en_US";
492
493 if ((csptr = strchr(ptr, '.')) != NULL)
494 {
495 /*
496 * Extract the character set from the environment...
497 */
498
499 for (ptr = charset, csptr ++; *csptr; csptr ++)
500 if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
501 *ptr++ = *csptr;
502
503 *ptr = '\0';
504 }
505
506 /*
507 * Get the locale for messages from the LC_MESSAGES locale setting...
508 */
509
510 if ((ptr = getenv("LC_MESSAGES")) == NULL)
511 if ((ptr = getenv("LC_ALL")) == NULL)
512 if ((ptr = getenv("LANG")) == NULL)
513 ptr = "en_US";
514 }
515
516 if (ptr)
517 {
518 strlcpy(locale, ptr, sizeof(locale));
519 language = locale;
520
521 /*
522 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
523 */
524
525 if (!strncmp(locale, "nb", 2))
526 locale[1] = 'o';
527
528 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
529 }
530 }
531 #endif /* __APPLE__ */
532
533 /*
534 * If "language" is NULL at this point, then chances are we are using
535 * a language that is not installed for the base OS.
536 */
537
538 if (!language)
539 {
540 /*
541 * Switch to the POSIX ("C") locale...
542 */
543
544 language = "C";
545 }
546
547 #ifdef CODESET
548 /*
549 * On systems that support the nl_langinfo(CODESET) call, use
550 * this value as the character set...
551 */
552
553 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
554 {
555 /*
556 * Copy all of the letters and numbers in the CODESET string...
557 */
558
559 for (ptr = charset; *csptr; csptr ++)
560 if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
561 *ptr++ = *csptr;
562
563 *ptr = '\0';
564
565 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
566 "nl_langinfo(CODESET)...", charset));
567 }
568 #endif /* CODESET */
569
570 /*
571 * If we don't have a character set by now, default to UTF-8...
572 */
573
574 if (!charset[0])
575 strcpy(charset, "UTF8");
576
577 /*
578 * Parse the language string passed in to a locale string. "C" is the
579 * standard POSIX locale and is copied unchanged. Otherwise the
580 * language string is converted from ll-cc[.charset] (language-country)
581 * to ll_CC[.CHARSET] to match the file naming convention used by all
582 * POSIX-compliant operating systems. Invalid language names are mapped
583 * to the POSIX locale.
584 */
585
586 country[0] = '\0';
587
588 if (language == NULL || !language[0] ||
589 !strcmp(language, "POSIX"))
590 strcpy(langname, "C");
591 else
592 {
593 /*
594 * Copy the parts of the locale string over safely...
595 */
596
597 for (ptr = langname; *language; language ++)
598 if (*language == '_' || *language == '-' || *language == '.')
599 break;
600 else if (ptr < (langname + sizeof(langname) - 1))
601 *ptr++ = tolower(*language & 255);
602
603 *ptr = '\0';
604
605 if (*language == '_' || *language == '-')
606 {
607 /*
608 * Copy the country code...
609 */
610
611 for (language ++, ptr = country; *language; language ++)
612 if (*language == '.')
613 break;
614 else if (ptr < (country + sizeof(country) - 1))
615 *ptr++ = toupper(*language & 255);
616
617 *ptr = '\0';
618 }
619
620 if (*language == '.' && !charset[0])
621 {
622 /*
623 * Copy the encoding...
624 */
625
626 for (language ++, ptr = charset; *language; language ++)
627 if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
628 *ptr++ = toupper(*language & 255);
629
630 *ptr = '\0';
631 }
632
633 /*
634 * Force a POSIX locale for an invalid language name...
635 */
636
637 if (strlen(langname) != 2)
638 {
639 strcpy(langname, "C");
640 country[0] = '\0';
641 charset[0] = '\0';
642 }
643 }
644
645 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
646 langname, country, charset));
647
648 /*
649 * Figure out the desired encoding...
650 */
651
652 encoding = CUPS_AUTO_ENCODING;
653
654 if (charset[0])
655 {
656 for (i = 0;
657 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
658 i ++)
659 if (!strcasecmp(charset, locale_encodings[i]))
660 {
661 encoding = (cups_encoding_t)i;
662 break;
663 }
664
665 if (encoding == CUPS_AUTO_ENCODING)
666 {
667 /*
668 * Map alternate names for various character sets...
669 */
670
671 if (!strcasecmp(charset, "iso-2022-jp") ||
672 !strcasecmp(charset, "sjis"))
673 encoding = CUPS_WINDOWS_932;
674 else if (!strcasecmp(charset, "iso-2022-cn"))
675 encoding = CUPS_WINDOWS_936;
676 else if (!strcasecmp(charset, "iso-2022-kr"))
677 encoding = CUPS_WINDOWS_949;
678 else if (!strcasecmp(charset, "big5"))
679 encoding = CUPS_WINDOWS_950;
680 }
681 }
682
683 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
684 encoding == CUPS_AUTO_ENCODING ? "auto" :
685 lang_encodings[encoding]));
686
687 /*
688 * See if we already have this language/country loaded...
689 */
690
691 if (country[0])
692 {
693 snprintf(real, sizeof(real), "%s_%s", langname, country);
694
695 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
696 real, real);
697 }
698 else
699 {
700 strcpy(real, langname);
701 filename[0] = '\0'; /* anti-compiler-warning-code */
702 }
703
704 #ifdef HAVE_PTHREAD_H
705 pthread_mutex_lock(&lang_mutex);
706 #endif /* HAVE_PTHREAD_H */
707
708 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
709 {
710 #ifdef HAVE_PTHREAD_H
711 pthread_mutex_unlock(&lang_mutex);
712 #endif /* HAVE_PTHREAD_H */
713
714 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
715
716 return (lang);
717 }
718
719 if (!country[0] || access(filename, 0))
720 {
721 /*
722 * Country localization not available, look for generic localization...
723 */
724
725 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
726 langname, langname);
727
728 if (access(filename, 0))
729 {
730 /*
731 * No generic localization, so use POSIX...
732 */
733
734 DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename,
735 strerror(errno)));
736
737 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
738 }
739 }
740
741 /*
742 * See if there is a free language available; if so, use that
743 * record...
744 */
745
746 for (lang = lang_cache; lang != NULL; lang = lang->next)
747 if (lang->used == 0)
748 break;
749
750 if (lang == NULL)
751 {
752 /*
753 * Allocate memory for the language and add it to the cache.
754 */
755
756 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
757 {
758 #ifdef HAVE_PTHREAD_H
759 pthread_mutex_unlock(&lang_mutex);
760 #endif /* HAVE_PTHREAD_H */
761
762 return (NULL);
763 }
764
765 lang->next = lang_cache;
766 lang_cache = lang;
767 }
768 else
769 {
770 /*
771 * Free all old strings as needed...
772 */
773
774 _cupsMessageFree(lang->strings);
775 }
776
777 /*
778 * Then assign the language and encoding fields...
779 */
780
781 lang->used ++;
782 strlcpy(lang->language, real, sizeof(lang->language));
783
784 if (encoding != CUPS_AUTO_ENCODING)
785 lang->encoding = encoding;
786 else
787 lang->encoding = CUPS_UTF8;
788
789 /*
790 * Read the strings from the file...
791 */
792
793 lang->strings = _cupsMessageLoad(filename, 1);
794
795 /*
796 * Return...
797 */
798
799 #ifdef HAVE_PTHREAD_H
800 pthread_mutex_unlock(&lang_mutex);
801 #endif /* HAVE_PTHREAD_H */
802
803 return (lang);
804 }
805
806
807 /*
808 * '_cupsLangString()' - Get a message string.
809 *
810 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
811 * convert the string to the language encoding.
812 */
813
814 const char * /* O - Localized message */
815 _cupsLangString(cups_lang_t *lang, /* I - Language */
816 const char *message) /* I - Message */
817 {
818 /*
819 * Range check input...
820 */
821
822 if (!lang || !message)
823 return (message);
824
825 #ifdef HAVE_PTHREAD_H
826 {
827 const char *s; /* Localized message */
828
829 pthread_mutex_lock(&lang_mutex);
830
831 s = _cupsMessageLookup(lang->strings, message);
832
833 pthread_mutex_unlock(&lang_mutex);
834
835 return (s);
836 }
837 #else
838 return (_cupsMessageLookup(lang->strings, message));
839 #endif /* HAVE_PTHREAD_H */
840 }
841
842
843 /*
844 * '_cupsMessageFree()' - Free a messages array.
845 */
846
847 void
848 _cupsMessageFree(cups_array_t *a) /* I - Message array */
849 {
850 _cups_message_t *m; /* Current message */
851
852
853 for (m = (_cups_message_t *)cupsArrayFirst(a);
854 m;
855 m = (_cups_message_t *)cupsArrayNext(a))
856 {
857 /*
858 * Remove the message from the array, then free the message and strings.
859 */
860
861 cupsArrayRemove(a, m);
862
863 if (m->id)
864 free(m->id);
865
866 if (m->str)
867 free(m->str);
868
869 free(m);
870 }
871
872 /*
873 * Free the array...
874 */
875
876 cupsArrayDelete(a);
877 }
878
879
880 /*
881 * '_cupsMessageLoad()' - Load a .po file into a messages array.
882 */
883
884 cups_array_t * /* O - New message array */
885 _cupsMessageLoad(const char *filename, /* I - Message catalog to load */
886 int unquote) /* I - Unescape \foo in strings */
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(("4_cupsMessageLoad(filename=\"%s\")", 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("5_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(("5_cupsMessageLoad: Unable to open file: %s",
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 if (unquote)
971 cups_unquote(ptr, ptr);
972
973 /*
974 * Create or add to a message...
975 */
976
977 if (!strncmp(s, "msgid", 5))
978 {
979 /*
980 * Add previous message as needed...
981 */
982
983 if (m)
984 cupsArrayAdd(a, m);
985
986 /*
987 * Create a new message with the given msgid string...
988 */
989
990 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
991 {
992 cupsFileClose(fp);
993 return (a);
994 }
995
996 if ((m->id = strdup(ptr)) == NULL)
997 {
998 free(m);
999 cupsFileClose(fp);
1000 return (a);
1001 }
1002 }
1003 else if (s[0] == '\"' && m)
1004 {
1005 /*
1006 * Append to current string...
1007 */
1008
1009 length = (int)strlen(m->str ? m->str : m->id);
1010
1011 if ((temp = realloc(m->str ? m->str : m->id,
1012 length + strlen(ptr) + 1)) == NULL)
1013 {
1014 cupsFileClose(fp);
1015 return (a);
1016 }
1017
1018 if (m->str)
1019 {
1020 /*
1021 * Copy the new portion to the end of the msgstr string - safe
1022 * to use strcpy because the buffer is allocated to the correct
1023 * size...
1024 */
1025
1026 m->str = temp;
1027
1028 strcpy(m->str + length, ptr);
1029 }
1030 else
1031 {
1032 /*
1033 * Copy the new portion to the end of the msgid string - safe
1034 * to use strcpy because the buffer is allocated to the correct
1035 * size...
1036 */
1037
1038 m->id = temp;
1039
1040 strcpy(m->id + length, ptr);
1041 }
1042 }
1043 else if (!strncmp(s, "msgstr", 6) && m)
1044 {
1045 /*
1046 * Set the string...
1047 */
1048
1049 if ((m->str = strdup(ptr)) == NULL)
1050 {
1051 cupsFileClose(fp);
1052 return (a);
1053 }
1054 }
1055 }
1056
1057 /*
1058 * Add the last message string to the array as needed...
1059 */
1060
1061 if (m)
1062 cupsArrayAdd(a, m);
1063
1064 /*
1065 * Close the message catalog file and return the new array...
1066 */
1067
1068 cupsFileClose(fp);
1069
1070 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1071 cupsArrayCount(a)));
1072
1073 return (a);
1074 }
1075
1076
1077 /*
1078 * '_cupsMessageLookup()' - Lookup a message string.
1079 */
1080
1081 const char * /* O - Localized message */
1082 _cupsMessageLookup(cups_array_t *a, /* I - Message array */
1083 const char *m) /* I - Message */
1084 {
1085 _cups_message_t key, /* Search key */
1086 *match; /* Matching message */
1087
1088
1089 /*
1090 * Lookup the message string; if it doesn't exist in the catalog,
1091 * then return the message that was passed to us...
1092 */
1093
1094 key.id = (char *)m;
1095 match = (_cups_message_t *)cupsArrayFind(a, &key);
1096
1097 if (match && match->str)
1098 return (match->str);
1099 else
1100 return (m);
1101 }
1102
1103
1104 #ifdef __APPLE__
1105 /*
1106 * 'appleLangDefault()' - Get the default locale string.
1107 */
1108
1109 static const char * /* O - Locale string */
1110 appleLangDefault(void)
1111 {
1112 int i; /* Looping var */
1113 CFBundleRef bundle; /* Main bundle (if any) */
1114 CFArrayRef bundleList; /* List of localizations in bundle */
1115 CFPropertyListRef localizationList;
1116 /* List of localization data */
1117 CFStringRef languageName; /* Current name */
1118 CFStringRef localeName; /* Canonical from of name */
1119 char *lang; /* LANG environment variable */
1120 _cups_globals_t *cg = _cupsGlobals();
1121 /* Pointer to library globals */
1122
1123
1124 /*
1125 * Only do the lookup and translation the first time.
1126 */
1127
1128 if (!cg->language[0])
1129 {
1130 if ((lang = getenv("LANG")))
1131 {
1132 strlcpy(cg->language, lang, sizeof(cg->language));
1133 return (cg->language);
1134 }
1135 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1136 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
1137 {
1138 localizationList =
1139 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
1140
1141 CFRelease(bundleList);
1142 }
1143 else
1144 localizationList =
1145 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1146 kCFPreferencesCurrentApplication);
1147
1148 if (localizationList)
1149 {
1150 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1151 CFArrayGetCount(localizationList) > 0)
1152 {
1153 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1154
1155 if (languageName &&
1156 CFGetTypeID(languageName) == CFStringGetTypeID())
1157 {
1158 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1159 kCFAllocatorDefault, languageName);
1160
1161 if (localeName)
1162 {
1163 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1164 kCFStringEncodingASCII);
1165 CFRelease(localeName);
1166
1167 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
1168 cg->language));
1169
1170 /*
1171 * Map new language identifiers to locales...
1172 */
1173
1174 for (i = 0;
1175 i < (int)(sizeof(apple_language_locale) /
1176 sizeof(apple_language_locale[0]));
1177 i ++)
1178 {
1179 if (!strcmp(cg->language, apple_language_locale[i].language))
1180 {
1181 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
1182 cg->language, apple_language_locale[i].locale));
1183 strlcpy(cg->language, apple_language_locale[i].locale,
1184 sizeof(cg->language));
1185 break;
1186 }
1187 }
1188
1189 /*
1190 * Convert language subtag into region subtag...
1191 */
1192
1193 if (cg->language[2] == '-')
1194 cg->language[2] = '_';
1195
1196 if (!strchr(cg->language, '.'))
1197 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1198 }
1199 }
1200 }
1201
1202 CFRelease(localizationList);
1203 }
1204
1205 /*
1206 * If we didn't find the language, default to en_US...
1207 */
1208
1209 if (!cg->language[0])
1210 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1211 }
1212
1213 /*
1214 * Return the cached locale...
1215 */
1216
1217 return (cg->language);
1218 }
1219 #endif /* __APPLE__ */
1220
1221
1222 /*
1223 * 'cups_cache_lookup()' - Lookup a language in the cache...
1224 */
1225
1226 static cups_lang_t * /* O - Language data or NULL */
1227 cups_cache_lookup(const char *name,/* I - Name of locale */
1228 cups_encoding_t encoding)
1229 /* I - Encoding of locale */
1230 {
1231 cups_lang_t *lang; /* Current language */
1232
1233
1234 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
1235 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1236 lang_encodings[encoding]));
1237
1238 /*
1239 * Loop through the cache and return a match if found...
1240 */
1241
1242 for (lang = lang_cache; lang != NULL; lang = lang->next)
1243 {
1244 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1245 "encoding=%d(%s)", lang, lang->language, lang->encoding,
1246 lang_encodings[lang->encoding]));
1247
1248 if (!strcmp(lang->language, name) &&
1249 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1250 {
1251 lang->used ++;
1252
1253 DEBUG_puts("8cups_cache_lookup: returning match!");
1254
1255 return (lang);
1256 }
1257 }
1258
1259 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1260
1261 return (NULL);
1262 }
1263
1264
1265 /*
1266 * 'cups_message_compare()' - Compare two messages.
1267 */
1268
1269 static int /* O - Result of comparison */
1270 cups_message_compare(
1271 _cups_message_t *m1, /* I - First message */
1272 _cups_message_t *m2) /* I - Second message */
1273 {
1274 return (strcmp(m1->id, m2->id));
1275 }
1276
1277
1278 /*
1279 * 'cups_unquote()' - Unquote characters in strings...
1280 */
1281
1282 static void
1283 cups_unquote(char *d, /* O - Unquoted string */
1284 const char *s) /* I - Original string */
1285 {
1286 while (*s)
1287 {
1288 if (*s == '\\')
1289 {
1290 s ++;
1291 if (isdigit(*s))
1292 {
1293 *d = 0;
1294
1295 while (isdigit(*s))
1296 {
1297 *d = *d * 8 + *s - '0';
1298 s ++;
1299 }
1300
1301 d ++;
1302 }
1303 else
1304 {
1305 if (*s == 'n')
1306 *d ++ = '\n';
1307 else if (*s == 'r')
1308 *d ++ = '\r';
1309 else if (*s == 't')
1310 *d ++ = '\t';
1311 else
1312 *d++ = *s;
1313
1314 s ++;
1315 }
1316 }
1317 else
1318 *d++ = *s++;
1319 }
1320
1321 *d = '\0';
1322 }
1323
1324
1325 /*
1326 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
1327 */