]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/language.c
Merge changes from 1.4svn-r7067.
[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 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 * _cupsEncodingName() - Return the character encoding name string
20 * for the given encoding enumeration.
21 * cupsLangDefault() - Return the default language.
22 * cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
23 * for the given language.
24 * cupsLangFlush() - Flush all language data out of the cache.
25 * cupsLangFree() - Free language data.
26 * cupsLangGet() - Get a language.
27 * _cupsLangString() - Get a message string.
28 * _cupsMessageFree() - Free a messages array.
29 * _cupsMessageLoad() - Load a .po file into a messages array.
30 * _cupsMessageLookup() - Lookup a message string.
31 * appleLangDefault() - Get the default locale string.
32 * cups_cache_lookup() - Lookup a language in the cache...
33 * cups_message_compare() - Compare two messages.
34 * cups_unquote() - Unquote characters in strings...
35 */
36
37 /*
38 * Include necessary headers...
39 */
40
41 #include "globals.h"
42 #include "debug.h"
43 #include <stdlib.h>
44 #include <errno.h>
45 #ifdef HAVE_LANGINFO_H
46 # include <langinfo.h>
47 #endif /* HAVE_LANGINFO_H */
48 #ifdef WIN32
49 # include <io.h>
50 #else
51 # include <unistd.h>
52 #endif /* WIN32 */
53 #ifdef HAVE_COREFOUNDATION_H
54 # include <CoreFoundation/CoreFoundation.h>
55 #endif /* HAVE_COREFOUNDATION_H */
56
57
58 /*
59 * Local globals...
60 */
61
62 #ifdef HAVE_PTHREAD_H
63 static pthread_mutex_t lang_mutex = PTHREAD_MUTEX_INITIALIZER;
64 /* Mutex to control access to cache */
65 #endif /* HAVE_PTHREAD_H */
66 static cups_lang_t *lang_cache = NULL;
67 /* Language string cache */
68
69
70 /*
71 * Local functions...
72 */
73
74 #ifdef __APPLE__
75 static const char *appleLangDefault(void);
76 #endif /* __APPLE__ */
77 static cups_lang_t *cups_cache_lookup(const char *name,
78 cups_encoding_t encoding);
79 static int cups_message_compare(_cups_message_t *m1,
80 _cups_message_t *m2);
81 static void cups_unquote(char *d, const char *s);
82
83
84 /*
85 * Local globals...
86 */
87
88 static const char * const lang_encodings[] =
89 { /* Encoding strings */
90 "us-ascii", "iso-8859-1",
91 "iso-8859-2", "iso-8859-3",
92 "iso-8859-4", "iso-8859-5",
93 "iso-8859-6", "iso-8859-7",
94 "iso-8859-8", "iso-8859-9",
95 "iso-8859-10", "utf-8",
96 "iso-8859-13", "iso-8859-14",
97 "iso-8859-15", "windows-874",
98 "windows-1250", "windows-1251",
99 "windows-1252", "windows-1253",
100 "windows-1254", "windows-1255",
101 "windows-1256", "windows-1257",
102 "windows-1258", "koi8-r",
103 "koi8-u", "iso-8859-11",
104 "iso-8859-16", "mac-roman",
105 "unknown", "unknown",
106 "unknown", "unknown",
107 "unknown", "unknown",
108 "unknown", "unknown",
109 "unknown", "unknown",
110 "unknown", "unknown",
111 "unknown", "unknown",
112 "unknown", "unknown",
113 "unknown", "unknown",
114 "unknown", "unknown",
115 "unknown", "unknown",
116 "unknown", "unknown",
117 "unknown", "unknown",
118 "unknown", "unknown",
119 "unknown", "unknown",
120 "unknown", "unknown",
121 "unknown", "unknown",
122 "windows-932", "windows-936",
123 "windows-949", "windows-950",
124 "windows-1361", "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 "unknown", "unknown",
137 "unknown", "unknown",
138 "unknown", "unknown",
139 "unknown", "unknown",
140 "unknown", "unknown",
141 "unknown", "unknown",
142 "unknown", "unknown",
143 "unknown", "unknown",
144 "unknown", "unknown",
145 "unknown", "unknown",
146 "unknown", "unknown",
147 "unknown", "unknown",
148 "unknown", "unknown",
149 "unknown", "unknown",
150 "unknown", "unknown",
151 "unknown", "unknown",
152 "unknown", "unknown",
153 "unknown", "unknown",
154 "euc-cn", "euc-jp",
155 "euc-kr", "euc-tw"
156 };
157
158
159 /*
160 * '_cupsEncodingName()' - Return the character encoding name string
161 * for the given encoding enumeration.
162 */
163
164 const char * /* O - Character encoding */
165 _cupsEncodingName(
166 cups_encoding_t encoding) /* I - Encoding value */
167 {
168 if (encoding < 0 ||
169 encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
170 return (lang_encodings[0]);
171 else
172 return (lang_encodings[encoding]);
173 }
174
175
176 /*
177 * 'cupsLangDefault()' - Return the default language.
178 */
179
180 cups_lang_t * /* O - Language data */
181 cupsLangDefault(void)
182 {
183 return (cupsLangGet(NULL));
184 }
185
186
187 /*
188 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
189 * for the given language.
190 */
191
192 const char * /* O - Character encoding */
193 cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
194 {
195 if (lang == NULL)
196 return ((char*)lang_encodings[0]);
197 else
198 return ((char*)lang_encodings[lang->encoding]);
199 }
200
201
202 /*
203 * 'cupsLangFlush()' - Flush all language data out of the cache.
204 */
205
206 void
207 cupsLangFlush(void)
208 {
209 cups_lang_t *lang, /* Current language */
210 *next; /* Next language */
211
212
213 /*
214 * Free all languages in the cache...
215 */
216
217 #ifdef HAVE_PTHREAD_H
218 pthread_mutex_lock(&lang_mutex);
219 #endif /* HAVE_PTHREAD_H */
220
221 for (lang = lang_cache; lang != NULL; lang = next)
222 {
223 /*
224 * Free all messages...
225 */
226
227 _cupsMessageFree(lang->strings);
228
229 /*
230 * Then free the language structure itself...
231 */
232
233 next = lang->next;
234 free(lang);
235 }
236
237 lang_cache = NULL;
238
239 #ifdef HAVE_PTHREAD_H
240 pthread_mutex_unlock(&lang_mutex);
241 #endif /* HAVE_PTHREAD_H */
242 }
243
244
245 /*
246 * 'cupsLangFree()' - Free language data.
247 *
248 * This does not actually free anything; use cupsLangFlush() for that.
249 */
250
251 void
252 cupsLangFree(cups_lang_t *lang) /* I - Language to free */
253 {
254 #ifdef HAVE_PTHREAD_H
255 pthread_mutex_lock(&lang_mutex);
256 #endif /* HAVE_PTHREAD_H */
257
258 if (lang != NULL && lang->used > 0)
259 lang->used --;
260
261 #ifdef HAVE_PTHREAD_H
262 pthread_mutex_unlock(&lang_mutex);
263 #endif /* HAVE_PTHREAD_H */
264 }
265
266
267 /*
268 * 'cupsLangGet()' - Get a language.
269 */
270
271 cups_lang_t * /* O - Language data */
272 cupsLangGet(const char *language) /* I - Language or locale */
273 {
274 int i; /* Looping var */
275 #ifndef __APPLE__
276 char locale[255]; /* Copy of locale name */
277 #endif /* !__APPLE__ */
278 char langname[16], /* Requested language name */
279 country[16], /* Country code */
280 charset[16], /* Character set */
281 *csptr, /* Pointer to CODESET string */
282 *ptr, /* Pointer into language/charset */
283 real[48], /* Real language name */
284 filename[1024]; /* Filename for language locale file */
285 cups_encoding_t encoding; /* Encoding to use */
286 cups_lang_t *lang; /* Current language... */
287 _cups_globals_t *cg = _cupsGlobals();
288 /* Pointer to library globals */
289 static const char * const locale_encodings[] =
290 { /* Locale charset names */
291 "ASCII", "ISO88591", "ISO88592", "ISO88593",
292 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
293 "ISO88598", "ISO88599", "ISO885910", "UTF8",
294 "ISO885913", "ISO885914", "ISO885915", "CP874",
295 "CP1250", "CP1251", "CP1252", "CP1253",
296 "CP1254", "CP1255", "CP1256", "CP1257",
297 "CP1258", "KOI8R", "KOI8U", "ISO885911",
298 "ISO885916", "MACROMAN", "", "",
299
300 "", "", "", "",
301 "", "", "", "",
302 "", "", "", "",
303 "", "", "", "",
304 "", "", "", "",
305 "", "", "", "",
306 "", "", "", "",
307 "", "", "", "",
308
309 "CP932", "CP936", "CP949", "CP950",
310 "CP1361", "", "", "",
311 "", "", "", "",
312 "", "", "", "",
313 "", "", "", "",
314 "", "", "", "",
315 "", "", "", "",
316 "", "", "", "",
317
318 "", "", "", "",
319 "", "", "", "",
320 "", "", "", "",
321 "", "", "", "",
322 "", "", "", "",
323 "", "", "", "",
324 "", "", "", "",
325 "", "", "", "",
326
327 "EUCCN", "EUCJP", "EUCKR", "EUCTW"
328 };
329
330
331 DEBUG_printf(("cupsLangGet(language=\"%s\")\n", language ? language : "(null)"));
332
333 #ifdef __APPLE__
334 /*
335 * Set the character set to UTF-8...
336 */
337
338 strcpy(charset, "UTF8");
339
340 /*
341 * Apple's setlocale doesn't give us the user's localization
342 * preference so we have to look it up this way...
343 */
344
345 if (!language && (language = getenv("LANG")) == NULL)
346 language = appleLangDefault();
347
348 #else
349 /*
350 * Set the charset to "unknown"...
351 */
352
353 charset[0] = '\0';
354
355 /*
356 * Use setlocale() to determine the currently set locale, and then
357 * fallback to environment variables to avoid setting the locale,
358 * since setlocale() is not thread-safe!
359 */
360
361 if (!language)
362 {
363 /*
364 * First see if the locale has been set; if it is still "C" or
365 * "POSIX", use the environment to get the default...
366 */
367
368 # ifdef LC_MESSAGES
369 ptr = setlocale(LC_MESSAGES, NULL);
370 # else
371 ptr = setlocale(LC_ALL, NULL);
372 # endif /* LC_MESSAGES */
373
374 DEBUG_printf(("cupsLangGet: current locale is \"%s\"\n",
375 ptr ? ptr : "(null)"));
376
377 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
378 {
379 /*
380 * Get the character set from the LC_CTYPE locale setting...
381 */
382
383 if ((ptr = getenv("LC_CTYPE")) == NULL)
384 if ((ptr = getenv("LC_ALL")) == NULL)
385 if ((ptr = getenv("LANG")) == NULL)
386 ptr = "en_US";
387
388 if ((csptr = strchr(ptr, '.')) != NULL)
389 {
390 /*
391 * Extract the character set from the environment...
392 */
393
394 for (ptr = charset, csptr ++; *csptr; csptr ++)
395 if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
396 *ptr++ = *csptr;
397
398 *ptr = '\0';
399 }
400
401 /*
402 * Get the locale for messages from the LC_MESSAGES locale setting...
403 */
404
405 if ((ptr = getenv("LC_MESSAGES")) == NULL)
406 if ((ptr = getenv("LC_ALL")) == NULL)
407 if ((ptr = getenv("LANG")) == NULL)
408 ptr = "en_US";
409 }
410
411 if (ptr)
412 {
413 strlcpy(locale, ptr, sizeof(locale));
414 language = locale;
415
416 /*
417 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
418 */
419
420 if (!strncmp(locale, "nb", 2))
421 locale[1] = 'o';
422
423 DEBUG_printf(("cupsLangGet: new language value is \"%s\"\n", language));
424 }
425 }
426 #endif /* __APPLE__ */
427
428 /*
429 * If "language" is NULL at this point, then chances are we are using
430 * a language that is not installed for the base OS.
431 */
432
433 if (!language)
434 {
435 /*
436 * Switch to the POSIX ("C") locale...
437 */
438
439 language = "C";
440 }
441
442 #ifdef CODESET
443 /*
444 * On systems that support the nl_langinfo(CODESET) call, use
445 * this value as the character set...
446 */
447
448 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
449 {
450 /*
451 * Copy all of the letters and numbers in the CODESET string...
452 */
453
454 for (ptr = charset; *csptr; csptr ++)
455 if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
456 *ptr++ = *csptr;
457
458 *ptr = '\0';
459
460 DEBUG_printf(("cupsLangGet: charset set to \"%s\" via nl_langinfo(CODESET)...\n",
461 charset));
462 }
463 #endif /* CODESET */
464
465 /*
466 * If we don't have a character set by now, default to UTF-8...
467 */
468
469 if (!charset[0])
470 strcpy(charset, "UTF8");
471
472 /*
473 * Parse the language string passed in to a locale string. "C" is the
474 * standard POSIX locale and is copied unchanged. Otherwise the
475 * language string is converted from ll-cc[.charset] (language-country)
476 * to ll_CC[.CHARSET] to match the file naming convention used by all
477 * POSIX-compliant operating systems. Invalid language names are mapped
478 * to the POSIX locale.
479 */
480
481 country[0] = '\0';
482
483 if (language == NULL || !language[0] ||
484 !strcmp(language, "POSIX"))
485 strcpy(langname, "C");
486 else
487 {
488 /*
489 * Copy the parts of the locale string over safely...
490 */
491
492 for (ptr = langname; *language; language ++)
493 if (*language == '_' || *language == '-' || *language == '.')
494 break;
495 else if (ptr < (langname + sizeof(langname) - 1))
496 *ptr++ = tolower(*language & 255);
497
498 *ptr = '\0';
499
500 if (*language == '_' || *language == '-')
501 {
502 /*
503 * Copy the country code...
504 */
505
506 for (language ++, ptr = country; *language; language ++)
507 if (*language == '.')
508 break;
509 else if (ptr < (country + sizeof(country) - 1))
510 *ptr++ = toupper(*language & 255);
511
512 *ptr = '\0';
513 }
514
515 if (*language == '.' && !charset[0])
516 {
517 /*
518 * Copy the encoding...
519 */
520
521 for (language ++, ptr = charset; *language; language ++)
522 if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
523 *ptr++ = toupper(*language & 255);
524
525 *ptr = '\0';
526 }
527
528 /*
529 * Force a POSIX locale for an invalid language name...
530 */
531
532 if (strlen(langname) != 2)
533 {
534 strcpy(langname, "C");
535 country[0] = '\0';
536 charset[0] = '\0';
537 }
538 }
539
540 DEBUG_printf(("cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"\n",
541 langname, country, charset));
542
543 /*
544 * Figure out the desired encoding...
545 */
546
547 encoding = CUPS_AUTO_ENCODING;
548
549 if (charset[0])
550 {
551 for (i = 0;
552 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
553 i ++)
554 if (!strcasecmp(charset, locale_encodings[i]))
555 {
556 encoding = (cups_encoding_t)i;
557 break;
558 }
559
560 if (encoding == CUPS_AUTO_ENCODING)
561 {
562 /*
563 * Map alternate names for various character sets...
564 */
565
566 if (!strcasecmp(charset, "iso-2022-jp") ||
567 !strcasecmp(charset, "sjis"))
568 encoding = CUPS_WINDOWS_932;
569 else if (!strcasecmp(charset, "iso-2022-cn"))
570 encoding = CUPS_WINDOWS_936;
571 else if (!strcasecmp(charset, "iso-2022-kr"))
572 encoding = CUPS_WINDOWS_949;
573 else if (!strcasecmp(charset, "big5"))
574 encoding = CUPS_WINDOWS_950;
575 }
576 }
577
578 DEBUG_printf(("cupsLangGet: encoding=%d(%s)\n", encoding,
579 encoding == CUPS_AUTO_ENCODING ? "auto" :
580 lang_encodings[encoding]));
581
582 /*
583 * See if we already have this language/country loaded...
584 */
585
586 if (country[0])
587 {
588 snprintf(real, sizeof(real), "%s_%s", langname, country);
589
590 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
591 real, real);
592 }
593 else
594 {
595 strcpy(real, langname);
596 filename[0] = '\0'; /* anti-compiler-warning-code */
597 }
598
599 #ifdef HAVE_PTHREAD_H
600 pthread_mutex_lock(&lang_mutex);
601 #endif /* HAVE_PTHREAD_H */
602
603 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
604 {
605 #ifdef HAVE_PTHREAD_H
606 pthread_mutex_unlock(&lang_mutex);
607 #endif /* HAVE_PTHREAD_H */
608
609 return (lang);
610 }
611
612 if (!country[0] || access(filename, 0))
613 {
614 /*
615 * Country localization not available, look for generic localization...
616 */
617
618 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
619 langname, langname);
620
621 if (access(filename, 0))
622 {
623 /*
624 * No generic localization, so use POSIX...
625 */
626
627 DEBUG_printf(("access(\"%s\", 0): %s\n", filename, strerror(errno)));
628
629 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
630 }
631 }
632
633 /*
634 * See if there is a free language available; if so, use that
635 * record...
636 */
637
638 for (lang = lang_cache; lang != NULL; lang = lang->next)
639 if (lang->used == 0)
640 break;
641
642 if (lang == NULL)
643 {
644 /*
645 * Allocate memory for the language and add it to the cache.
646 */
647
648 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
649 {
650 #ifdef HAVE_PTHREAD_H
651 pthread_mutex_unlock(&lang_mutex);
652 #endif /* HAVE_PTHREAD_H */
653
654 return (NULL);
655 }
656
657 lang->next = lang_cache;
658 lang_cache = lang;
659 }
660 else
661 {
662 /*
663 * Free all old strings as needed...
664 */
665
666 _cupsMessageFree(lang->strings);
667 }
668
669 /*
670 * Then assign the language and encoding fields...
671 */
672
673 lang->used ++;
674 strlcpy(lang->language, real, sizeof(lang->language));
675
676 if (encoding != CUPS_AUTO_ENCODING)
677 lang->encoding = encoding;
678 else
679 lang->encoding = CUPS_UTF8;
680
681 /*
682 * Read the strings from the file...
683 */
684
685 lang->strings = _cupsMessageLoad(filename);
686
687 /*
688 * Return...
689 */
690
691 #ifdef HAVE_PTHREAD_H
692 pthread_mutex_unlock(&lang_mutex);
693 #endif /* HAVE_PTHREAD_H */
694
695 return (lang);
696 }
697
698
699 /*
700 * '_cupsLangString()' - Get a message string.
701 *
702 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
703 * convert the string to the language encoding.
704 */
705
706 const char * /* O - Localized message */
707 _cupsLangString(cups_lang_t *lang, /* I - Language */
708 const char *message) /* I - Message */
709 {
710 /*
711 * Range check input...
712 */
713
714 if (!lang || !message)
715 return (message);
716
717 #ifdef HAVE_PTHREAD_H
718 {
719 const char *s; /* Localized message */
720
721 pthread_mutex_lock(&lang_mutex);
722
723 s = _cupsMessageLookup(lang->strings, message);
724
725 pthread_mutex_unlock(&lang_mutex);
726
727 return (s);
728 }
729 #else
730 return (_cupsMessageLookup(lang->strings, message));
731 #endif /* HAVE_PTHREAD_H */
732 }
733
734
735 /*
736 * '_cupsMessageFree()' - Free a messages array.
737 */
738
739 void
740 _cupsMessageFree(cups_array_t *a) /* I - Message array */
741 {
742 _cups_message_t *m; /* Current message */
743
744
745 for (m = (_cups_message_t *)cupsArrayFirst(a);
746 m;
747 m = (_cups_message_t *)cupsArrayNext(a))
748 {
749 /*
750 * Remove the message from the array, then free the message and strings.
751 */
752
753 cupsArrayRemove(a, m);
754
755 if (m->id)
756 free(m->id);
757
758 if (m->str)
759 free(m->str);
760
761 free(m);
762 }
763
764 /*
765 * Free the array...
766 */
767
768 cupsArrayDelete(a);
769 }
770
771
772 /*
773 * '_cupsMessageLoad()' - Load a .po file into a messages array.
774 */
775
776 cups_array_t * /* O - New message array */
777 _cupsMessageLoad(const char *filename) /* I - Message catalog to load */
778 {
779 cups_file_t *fp; /* Message file */
780 cups_array_t *a; /* Message array */
781 _cups_message_t *m; /* Current message */
782 char s[4096], /* String buffer */
783 *ptr, /* Pointer into buffer */
784 *temp; /* New string */
785 int length; /* Length of combined strings */
786
787
788 DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
789
790 /*
791 * Create an array to hold the messages...
792 */
793
794 if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
795 return (NULL);
796
797 /*
798 * Open the message catalog file...
799 */
800
801 if ((fp = cupsFileOpen(filename, "r")) == NULL)
802 return (a);
803
804 /*
805 * Read messages from the catalog file until EOF...
806 *
807 * The format is the GNU gettext .po format, which is fairly simple:
808 *
809 * msgid "some text"
810 * msgstr "localized text"
811 *
812 * The ID and localized text can span multiple lines using the form:
813 *
814 * msgid ""
815 * "some long text"
816 * msgstr ""
817 * "localized text spanning "
818 * "multiple lines"
819 */
820
821 m = NULL;
822
823 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
824 {
825 /*
826 * Skip blank and comment lines...
827 */
828
829 if (s[0] == '#' || !s[0])
830 continue;
831
832 /*
833 * Strip the trailing quote...
834 */
835
836 if ((ptr = strrchr(s, '\"')) == NULL)
837 continue;
838
839 *ptr = '\0';
840
841 /*
842 * Find start of value...
843 */
844
845 if ((ptr = strchr(s, '\"')) == NULL)
846 continue;
847
848 ptr ++;
849
850 /*
851 * Unquote the text...
852 */
853
854 cups_unquote(ptr, ptr);
855
856 /*
857 * Create or add to a message...
858 */
859
860 if (!strncmp(s, "msgid", 5))
861 {
862 /*
863 * Add previous message as needed...
864 */
865
866 if (m)
867 cupsArrayAdd(a, m);
868
869 /*
870 * Create a new message with the given msgid string...
871 */
872
873 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
874 {
875 cupsFileClose(fp);
876 return (a);
877 }
878
879 m->id = strdup(ptr);
880 }
881 else if (s[0] == '\"' && m)
882 {
883 /*
884 * Append to current string...
885 */
886
887 length = (int)strlen(m->str ? m->str : m->id);
888
889 if ((temp = realloc(m->str ? m->str : m->id,
890 length + strlen(ptr) + 1)) == NULL)
891 {
892 cupsFileClose(fp);
893 return (a);
894 }
895
896 if (m->str)
897 {
898 /*
899 * Copy the new portion to the end of the msgstr string - safe
900 * to use strcpy because the buffer is allocated to the correct
901 * size...
902 */
903
904 m->str = temp;
905
906 strcpy(m->str + length, ptr);
907 }
908 else
909 {
910 /*
911 * Copy the new portion to the end of the msgid string - safe
912 * to use strcpy because the buffer is allocated to the correct
913 * size...
914 */
915
916 m->id = temp;
917
918 strcpy(m->id + length, ptr);
919 }
920 }
921 else if (!strncmp(s, "msgstr", 6) && m)
922 {
923 /*
924 * Set the string...
925 */
926
927 m->str = strdup(ptr);
928 }
929 }
930
931 /*
932 * Add the last message string to the array as needed...
933 */
934
935 if (m)
936 cupsArrayAdd(a, m);
937
938 /*
939 * Close the message catalog file and return the new array...
940 */
941
942 cupsFileClose(fp);
943
944 return (a);
945 }
946
947
948 /*
949 * '_cupsMessageLookup()' - Lookup a message string.
950 */
951
952 const char * /* O - Localized message */
953 _cupsMessageLookup(cups_array_t *a, /* I - Message array */
954 const char *m) /* I - Message */
955 {
956 _cups_message_t key, /* Search key */
957 *match; /* Matching message */
958
959
960 /*
961 * Lookup the message string; if it doesn't exist in the catalog,
962 * then return the message that was passed to us...
963 */
964
965 key.id = (char *)m;
966 match = (_cups_message_t *)cupsArrayFind(a, &key);
967
968 if (match && match->str)
969 return (match->str);
970 else
971 return (m);
972 }
973
974
975 #ifdef __APPLE__
976 /*
977 * Code & data to translate OSX's language names to their ISO 639-1 locale.
978 *
979 * The first version uses the new CoreFoundation API added in 10.3 (Panther),
980 * the second is for 10.2 (Jaguar).
981 */
982
983 # ifdef HAVE_CF_LOCALE_ID
984
985 typedef struct
986 {
987 const char * const name; /* Language name */
988 const char * const locale; /* Locale name */
989 } _apple_name_locale_t;
990
991 static const _apple_name_locale_t apple_name_locale[] =
992 {
993 { "en" , "en_US" },
994 { "nb" , "no" },
995 { "zh-Hans" , "zh_CN" },
996 { "zh-Hant" , "zh_TW" }
997 };
998
999
1000 /*
1001 * 'appleLangDefault()' - Get the default locale string.
1002 */
1003
1004 static const char * /* O - Locale string */
1005 appleLangDefault(void)
1006 {
1007 int i; /* Looping var */
1008 CFBundleRef bundle; /* Main bundle (if any) */
1009 CFArrayRef bundleList; /* List of localizations in bundle */
1010 CFPropertyListRef localizationList;
1011 /* List of localization data */
1012 CFStringRef languageName; /* Current name */
1013 CFStringRef localeName; /* Canonical from of name */
1014 char *lang; /* LANG environment variable */
1015 _cups_globals_t *cg = _cupsGlobals();
1016 /* Pointer to library globals */
1017
1018
1019 /*
1020 * Only do the lookup and translation the first time.
1021 */
1022
1023 if (!cg->language[0])
1024 {
1025 if ((lang = getenv("LANG")))
1026 {
1027 strlcpy(cg->language, lang, sizeof(cg->language));
1028 return (cg->language);
1029 }
1030 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1031 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
1032 {
1033 localizationList =
1034 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
1035
1036 CFRelease(bundleList);
1037 }
1038 else
1039 localizationList =
1040 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1041 kCFPreferencesCurrentApplication);
1042
1043 if (localizationList)
1044 {
1045 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1046 CFArrayGetCount(localizationList) > 0)
1047 {
1048 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1049
1050 if (languageName &&
1051 CFGetTypeID(languageName) == CFStringGetTypeID())
1052 {
1053 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1054 kCFAllocatorDefault, languageName);
1055
1056 if (localeName)
1057 {
1058 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1059 kCFStringEncodingASCII);
1060 CFRelease(localeName);
1061
1062 DEBUG_printf(("appleLangDefault: cg->language=\"%s\"\n",
1063 cg->language));
1064
1065 /*
1066 * Map new language identifiers to locales...
1067 */
1068
1069 for (i = 0;
1070 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1071 i++)
1072 {
1073 if (!strcmp(cg->language, apple_name_locale[i].name))
1074 {
1075 DEBUG_printf(("appleLangDefault: mapping \"%s\" to \"%s\"...\n",
1076 cg->language, apple_name_locale[i].locale));
1077 strlcpy(cg->language, apple_name_locale[i].locale,
1078 sizeof(cg->language));
1079 break;
1080 }
1081 }
1082
1083 /*
1084 * Convert language subtag into region subtag...
1085 */
1086
1087 if (cg->language[2] == '-')
1088 cg->language[2] = '_';
1089
1090 if (!strchr(cg->language, '.'))
1091 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1092 }
1093 }
1094 }
1095
1096 CFRelease(localizationList);
1097 }
1098
1099 /*
1100 * If we didn't find the language, default to en_US...
1101 */
1102
1103 if (!cg->language[0])
1104 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1105 }
1106
1107 /*
1108 * Return the cached locale...
1109 */
1110
1111 return (cg->language);
1112 }
1113 # else
1114 /*
1115 * Code & data to translate OSX 10.2's language names to their ISO 639-1
1116 * locale.
1117 */
1118
1119 typedef struct
1120 {
1121 const char * const name; /* Language name */
1122 const char * const locale; /* Locale name */
1123 } _apple_name_locale_t;
1124
1125 static const _apple_name_locale_t apple_name_locale[] =
1126 {
1127 { "English" , "en_US.UTF-8" }, { "French" , "fr.UTF-8" },
1128 { "German" , "de.UTF-8" }, { "Italian" , "it.UTF-8" },
1129 { "Dutch" , "nl.UTF-8" }, { "Swedish" , "sv.UTF-8" },
1130 { "Spanish" , "es.UTF-8" }, { "Danish" , "da.UTF-8" },
1131 { "Portuguese" , "pt.UTF-8" }, { "Norwegian" , "no.UTF-8" },
1132 { "Hebrew" , "he.UTF-8" }, { "Japanese" , "ja.UTF-8" },
1133 { "Arabic" , "ar.UTF-8" }, { "Finnish" , "fi.UTF-8" },
1134 { "Greek" , "el.UTF-8" }, { "Icelandic" , "is.UTF-8" },
1135 { "Maltese" , "mt.UTF-8" }, { "Turkish" , "tr.UTF-8" },
1136 { "Croatian" , "hr.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1137 { "Urdu" , "ur.UTF-8" }, { "Hindi" , "hi.UTF-8" },
1138 { "Thai" , "th.UTF-8" }, { "Korean" , "ko.UTF-8" },
1139 { "Lithuanian" , "lt.UTF-8" }, { "Polish" , "pl.UTF-8" },
1140 { "Hungarian" , "hu.UTF-8" }, { "Estonian" , "et.UTF-8" },
1141 { "Latvian" , "lv.UTF-8" }, { "Sami" , "se.UTF-8" },
1142 { "Faroese" , "fo.UTF-8" }, { "Farsi" , "fa.UTF-8" },
1143 { "Russian" , "ru.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1144 { "Dutch" , "nl.UTF-8" }, { "Irish" , "ga.UTF-8" },
1145 { "Albanian" , "sq.UTF-8" }, { "Romanian" , "ro.UTF-8" },
1146 { "Czech" , "cs.UTF-8" }, { "Slovak" , "sk.UTF-8" },
1147 { "Slovenian" , "sl.UTF-8" }, { "Yiddish" , "yi.UTF-8" },
1148 { "Serbian" , "sr.UTF-8" }, { "Macedonian" , "mk.UTF-8" },
1149 { "Bulgarian" , "bg.UTF-8" }, { "Ukrainian" , "uk.UTF-8" },
1150 { "Byelorussian", "be.UTF-8" }, { "Uzbek" , "uz.UTF-8" },
1151 { "Kazakh" , "kk.UTF-8" }, { "Azerbaijani", "az.UTF-8" },
1152 { "Azerbaijani" , "az.UTF-8" }, { "Armenian" , "hy.UTF-8" },
1153 { "Georgian" , "ka.UTF-8" }, { "Moldavian" , "mo.UTF-8" },
1154 { "Kirghiz" , "ky.UTF-8" }, { "Tajiki" , "tg.UTF-8" },
1155 { "Turkmen" , "tk.UTF-8" }, { "Mongolian" , "mn.UTF-8" },
1156 { "Mongolian" , "mn.UTF-8" }, { "Pashto" , "ps.UTF-8" },
1157 { "Kurdish" , "ku.UTF-8" }, { "Kashmiri" , "ks.UTF-8" },
1158 { "Sindhi" , "sd.UTF-8" }, { "Tibetan" , "bo.UTF-8" },
1159 { "Nepali" , "ne.UTF-8" }, { "Sanskrit" , "sa.UTF-8" },
1160 { "Marathi" , "mr.UTF-8" }, { "Bengali" , "bn.UTF-8" },
1161 { "Assamese" , "as.UTF-8" }, { "Gujarati" , "gu.UTF-8" },
1162 { "Punjabi" , "pa.UTF-8" }, { "Oriya" , "or.UTF-8" },
1163 { "Malayalam" , "ml.UTF-8" }, { "Kannada" , "kn.UTF-8" },
1164 { "Tamil" , "ta.UTF-8" }, { "Telugu" , "te.UTF-8" },
1165 { "Sinhalese" , "si.UTF-8" }, { "Burmese" , "my.UTF-8" },
1166 { "Khmer" , "km.UTF-8" }, { "Lao" , "lo.UTF-8" },
1167 { "Vietnamese" , "vi.UTF-8" }, { "Indonesian" , "id.UTF-8" },
1168 { "Tagalog" , "tl.UTF-8" }, { "Malay" , "ms.UTF-8" },
1169 { "Malay" , "ms.UTF-8" }, { "Amharic" , "am.UTF-8" },
1170 { "Tigrinya" , "ti.UTF-8" }, { "Oromo" , "om.UTF-8" },
1171 { "Somali" , "so.UTF-8" }, { "Swahili" , "sw.UTF-8" },
1172 { "Kinyarwanda" , "rw.UTF-8" }, { "Rundi" , "rn.UTF-8" },
1173 { "Nyanja" , "" }, { "Malagasy" , "mg.UTF-8" },
1174 { "Esperanto" , "eo.UTF-8" }, { "Welsh" , "cy.UTF-8" },
1175 { "Basque" , "eu.UTF-8" }, { "Catalan" , "ca.UTF-8" },
1176 { "Latin" , "la.UTF-8" }, { "Quechua" , "qu.UTF-8" },
1177 { "Guarani" , "gn.UTF-8" }, { "Aymara" , "ay.UTF-8" },
1178 { "Tatar" , "tt.UTF-8" }, { "Uighur" , "ug.UTF-8" },
1179 { "Dzongkha" , "dz.UTF-8" }, { "Javanese" , "jv.UTF-8" },
1180 { "Sundanese" , "su.UTF-8" }, { "Galician" , "gl.UTF-8" },
1181 { "Afrikaans" , "af.UTF-8" }, { "Breton" , "br.UTF-8" },
1182 { "Inuktitut" , "iu.UTF-8" }, { "Scottish" , "gd.UTF-8" },
1183 { "Manx" , "gv.UTF-8" }, { "Irish" , "ga.UTF-8" },
1184 { "Tongan" , "to.UTF-8" }, { "Greek" , "el.UTF-8" },
1185 { "Greenlandic" , "kl.UTF-8" }, { "Azerbaijani", "az.UTF-8" }
1186 };
1187
1188
1189 /*
1190 * 'appleLangDefault()' - Get the default locale string.
1191 */
1192
1193 static const char * /* O - Locale string */
1194 appleLangDefault(void)
1195 {
1196 int i; /* Looping var */
1197 CFPropertyListRef localizationList;
1198 /* List of localization data */
1199 CFStringRef localizationName;
1200 /* Current name */
1201 char buff[256]; /* Temporary buffer */
1202 _cups_globals_t *cg = _cupsGlobals();
1203 /* Pointer to library globals */
1204 char *lang; /* LANG environment variable */
1205
1206
1207 /*
1208 * Only do the lookup and translation the first time.
1209 */
1210
1211 if (!cg->language[0])
1212 {
1213 if ((lang = getenv("LANG")))
1214 strlcpy(cg->language, lang, sizeof(cg->language));
1215 else
1216 {
1217 localizationList =
1218 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1219 kCFPreferencesCurrentApplication);
1220
1221 if (localizationList != NULL)
1222 {
1223 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1224 CFArrayGetCount(localizationList) > 0)
1225 {
1226 localizationName = CFArrayGetValueAtIndex(localizationList, 0);
1227
1228 if (localizationName != NULL &&
1229 CFGetTypeID(localizationName) == CFStringGetTypeID())
1230 {
1231 CFIndex length = CFStringGetLength(localizationName);
1232
1233 if (length <= sizeof(buff) &&
1234 CFStringGetCString(localizationName, buff, sizeof(buff),
1235 kCFStringEncodingASCII))
1236 {
1237 buff[sizeof(buff) - 1] = '\0';
1238
1239 for (i = 0;
1240 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1241 i++)
1242 {
1243 if (!strcasecmp(buff, apple_name_locale[i].name))
1244 {
1245 strlcpy(cg->language, apple_name_locale[i].locale,
1246 sizeof(cg->language));
1247 break;
1248 }
1249 }
1250 }
1251 }
1252 }
1253
1254 CFRelease(localizationList);
1255 }
1256 }
1257
1258 /*
1259 * If we didn't find the language, default to en_US...
1260 */
1261
1262 if (!cg->language[0])
1263 strlcpy(cg->language, apple_name_locale[0].locale, sizeof(cg->language));
1264 }
1265
1266 /*
1267 * Return the cached locale...
1268 */
1269
1270 return (cg->language);
1271 }
1272 # endif /* HAVE_CF_LOCALE_ID */
1273 #endif /* __APPLE__ */
1274
1275
1276 /*
1277 * 'cups_cache_lookup()' - Lookup a language in the cache...
1278 */
1279
1280 static cups_lang_t * /* O - Language data or NULL */
1281 cups_cache_lookup(const char *name,/* I - Name of locale */
1282 cups_encoding_t encoding)
1283 /* I - Encoding of locale */
1284 {
1285 cups_lang_t *lang; /* Current language */
1286
1287
1288 DEBUG_printf(("cups_cache_lookup(name=\"%s\", encoding=%d(%s))\n", name,
1289 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1290 lang_encodings[encoding]));
1291
1292 /*
1293 * Loop through the cache and return a match if found...
1294 */
1295
1296 for (lang = lang_cache; lang != NULL; lang = lang->next)
1297 {
1298 DEBUG_printf(("cups_cache_lookup: lang=%p, language=\"%s\", encoding=%d(%s)\n",
1299 lang, lang->language, lang->encoding,
1300 lang_encodings[lang->encoding]));
1301
1302 if (!strcmp(lang->language, name) &&
1303 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1304 {
1305 lang->used ++;
1306
1307 DEBUG_puts("cups_cache_lookup: returning match!");
1308
1309 return (lang);
1310 }
1311 }
1312
1313 DEBUG_puts("cups_cache_lookup: returning NULL!");
1314
1315 return (NULL);
1316 }
1317
1318
1319 /*
1320 * 'cups_message_compare()' - Compare two messages.
1321 */
1322
1323 static int /* O - Result of comparison */
1324 cups_message_compare(
1325 _cups_message_t *m1, /* I - First message */
1326 _cups_message_t *m2) /* I - Second message */
1327 {
1328 return (strcmp(m1->id, m2->id));
1329 }
1330
1331
1332 /*
1333 * 'cups_unquote()' - Unquote characters in strings...
1334 */
1335
1336 static void
1337 cups_unquote(char *d, /* O - Unquoted string */
1338 const char *s) /* I - Original string */
1339 {
1340 while (*s)
1341 {
1342 if (*s == '\\')
1343 {
1344 s ++;
1345 if (isdigit(*s))
1346 {
1347 *d = 0;
1348
1349 while (isdigit(*s))
1350 {
1351 *d = *d * 8 + *s - '0';
1352 s ++;
1353 }
1354
1355 d ++;
1356 }
1357 else
1358 {
1359 if (*s == 'n')
1360 *d ++ = '\n';
1361 else if (*s == 'r')
1362 *d ++ = '\r';
1363 else if (*s == 't')
1364 *d ++ = '\t';
1365 else
1366 *d++ = *s;
1367
1368 s ++;
1369 }
1370 }
1371 else
1372 *d++ = *s++;
1373 }
1374
1375 *d = '\0';
1376 }
1377
1378
1379 /*
1380 * End of "$Id: language.c 6916 2007-09-05 21:14:08Z mike $".
1381 */