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