]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/language.c
Merge changes from CUPS 1.3.1.
[thirdparty/cups.git] / cups / language.c
1 /*
2 * "$Id: language.c 6917 2007-09-05 21:14:17Z 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 DEBUG_printf(("cupsLangGet: new language value is \"%s\"\n",
417 language ? language : "(null)"));
418 }
419 }
420 #endif /* __APPLE__ */
421
422 /*
423 * If "language" is NULL at this point, then chances are we are using
424 * a language that is not installed for the base OS.
425 */
426
427 if (!language)
428 {
429 /*
430 * Switch to the POSIX ("C") locale...
431 */
432
433 language = "C";
434 }
435
436 #ifdef CODESET
437 /*
438 * On systems that support the nl_langinfo(CODESET) call, use
439 * this value as the character set...
440 */
441
442 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
443 {
444 /*
445 * Copy all of the letters and numbers in the CODESET string...
446 */
447
448 for (ptr = charset; *csptr; csptr ++)
449 if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
450 *ptr++ = *csptr;
451
452 *ptr = '\0';
453
454 DEBUG_printf(("cupsLangGet: charset set to \"%s\" via nl_langinfo(CODESET)...\n",
455 charset));
456 }
457 #endif /* CODESET */
458
459 /*
460 * If we don't have a character set by now, default to UTF-8...
461 */
462
463 if (!charset[0])
464 strcpy(charset, "UTF8");
465
466 /*
467 * Parse the language string passed in to a locale string. "C" is the
468 * standard POSIX locale and is copied unchanged. Otherwise the
469 * language string is converted from ll-cc[.charset] (language-country)
470 * to ll_CC[.CHARSET] to match the file naming convention used by all
471 * POSIX-compliant operating systems. Invalid language names are mapped
472 * to the POSIX locale.
473 */
474
475 country[0] = '\0';
476
477 if (language == NULL || !language[0] ||
478 !strcmp(language, "POSIX"))
479 strcpy(langname, "C");
480 else
481 {
482 /*
483 * Copy the parts of the locale string over safely...
484 */
485
486 for (ptr = langname; *language; language ++)
487 if (*language == '_' || *language == '-' || *language == '.')
488 break;
489 else if (ptr < (langname + sizeof(langname) - 1))
490 *ptr++ = tolower(*language & 255);
491
492 *ptr = '\0';
493
494 if (*language == '_' || *language == '-')
495 {
496 /*
497 * Copy the country code...
498 */
499
500 for (language ++, ptr = country; *language; language ++)
501 if (*language == '.')
502 break;
503 else if (ptr < (country + sizeof(country) - 1))
504 *ptr++ = toupper(*language & 255);
505
506 *ptr = '\0';
507 }
508
509 if (*language == '.' && !charset[0])
510 {
511 /*
512 * Copy the encoding...
513 */
514
515 for (language ++, ptr = charset; *language; language ++)
516 if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
517 *ptr++ = toupper(*language & 255);
518
519 *ptr = '\0';
520 }
521
522 /*
523 * Force a POSIX locale for an invalid language name...
524 */
525
526 if (strlen(langname) != 2)
527 {
528 strcpy(langname, "C");
529 country[0] = '\0';
530 charset[0] = '\0';
531 }
532 }
533
534 DEBUG_printf(("cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"\n",
535 langname, country, charset));
536
537 /*
538 * Figure out the desired encoding...
539 */
540
541 encoding = CUPS_AUTO_ENCODING;
542
543 if (charset[0])
544 {
545 for (i = 0;
546 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
547 i ++)
548 if (!strcasecmp(charset, locale_encodings[i]))
549 {
550 encoding = (cups_encoding_t)i;
551 break;
552 }
553
554 if (encoding == CUPS_AUTO_ENCODING)
555 {
556 /*
557 * Map alternate names for various character sets...
558 */
559
560 if (!strcasecmp(charset, "iso-2022-jp") ||
561 !strcasecmp(charset, "sjis"))
562 encoding = CUPS_WINDOWS_932;
563 else if (!strcasecmp(charset, "iso-2022-cn"))
564 encoding = CUPS_WINDOWS_936;
565 else if (!strcasecmp(charset, "iso-2022-kr"))
566 encoding = CUPS_WINDOWS_949;
567 else if (!strcasecmp(charset, "big5"))
568 encoding = CUPS_WINDOWS_950;
569 }
570 }
571
572 DEBUG_printf(("cupsLangGet: encoding=%d(%s)\n", encoding,
573 encoding == CUPS_AUTO_ENCODING ? "auto" :
574 lang_encodings[encoding]));
575
576 /*
577 * See if we already have this language/country loaded...
578 */
579
580 if (country[0])
581 {
582 snprintf(real, sizeof(real), "%s_%s", langname, country);
583
584 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
585 real, real);
586 }
587 else
588 {
589 strcpy(real, langname);
590 filename[0] = '\0'; /* anti-compiler-warning-code */
591 }
592
593 #ifdef HAVE_PTHREAD_H
594 pthread_mutex_lock(&lang_mutex);
595 #endif /* HAVE_PTHREAD_H */
596
597 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
598 {
599 #ifdef HAVE_PTHREAD_H
600 pthread_mutex_unlock(&lang_mutex);
601 #endif /* HAVE_PTHREAD_H */
602
603 return (lang);
604 }
605
606 if (!country[0] || access(filename, 0))
607 {
608 /*
609 * Country localization not available, look for generic localization...
610 */
611
612 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
613 langname, langname);
614
615 if (access(filename, 0))
616 {
617 /*
618 * No generic localization, so use POSIX...
619 */
620
621 DEBUG_printf(("access(\"%s\", 0): %s\n", filename, strerror(errno)));
622
623 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
624 }
625 }
626
627 /*
628 * See if there is a free language available; if so, use that
629 * record...
630 */
631
632 for (lang = lang_cache; lang != NULL; lang = lang->next)
633 if (lang->used == 0)
634 break;
635
636 if (lang == NULL)
637 {
638 /*
639 * Allocate memory for the language and add it to the cache.
640 */
641
642 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
643 {
644 #ifdef HAVE_PTHREAD_H
645 pthread_mutex_unlock(&lang_mutex);
646 #endif /* HAVE_PTHREAD_H */
647
648 return (NULL);
649 }
650
651 lang->next = lang_cache;
652 lang_cache = lang;
653 }
654 else
655 {
656 /*
657 * Free all old strings as needed...
658 */
659
660 _cupsMessageFree(lang->strings);
661 }
662
663 /*
664 * Then assign the language and encoding fields...
665 */
666
667 lang->used ++;
668 strlcpy(lang->language, real, sizeof(lang->language));
669
670 if (encoding != CUPS_AUTO_ENCODING)
671 lang->encoding = encoding;
672 else
673 lang->encoding = CUPS_UTF8;
674
675 /*
676 * Read the strings from the file...
677 */
678
679 lang->strings = _cupsMessageLoad(filename);
680
681 /*
682 * Return...
683 */
684
685 #ifdef HAVE_PTHREAD_H
686 pthread_mutex_unlock(&lang_mutex);
687 #endif /* HAVE_PTHREAD_H */
688
689 return (lang);
690 }
691
692
693 /*
694 * '_cupsLangString()' - Get a message string.
695 *
696 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
697 * convert the string to the language encoding.
698 */
699
700 const char * /* O - Localized message */
701 _cupsLangString(cups_lang_t *lang, /* I - Language */
702 const char *message) /* I - Message */
703 {
704 /*
705 * Range check input...
706 */
707
708 if (!lang || !message)
709 return (message);
710
711 #ifdef HAVE_PTHREAD_H
712 {
713 const char *s; /* Localized message */
714
715 pthread_mutex_lock(&lang_mutex);
716
717 s = _cupsMessageLookup(lang->strings, message);
718
719 pthread_mutex_unlock(&lang_mutex);
720
721 return (s);
722 }
723 #else
724 return (_cupsMessageLookup(lang->strings, message));
725 #endif /* HAVE_PTHREAD_H */
726 }
727
728
729 /*
730 * '_cupsMessageFree()' - Free a messages array.
731 */
732
733 void
734 _cupsMessageFree(cups_array_t *a) /* I - Message array */
735 {
736 _cups_message_t *m; /* Current message */
737
738
739 for (m = (_cups_message_t *)cupsArrayFirst(a);
740 m;
741 m = (_cups_message_t *)cupsArrayNext(a))
742 {
743 /*
744 * Remove the message from the array, then free the message and strings.
745 */
746
747 cupsArrayRemove(a, m);
748
749 if (m->id)
750 free(m->id);
751
752 if (m->str)
753 free(m->str);
754
755 free(m);
756 }
757
758 /*
759 * Free the array...
760 */
761
762 cupsArrayDelete(a);
763 }
764
765
766 /*
767 * '_cupsMessageLoad()' - Load a .po file into a messages array.
768 */
769
770 cups_array_t * /* O - New message array */
771 _cupsMessageLoad(const char *filename) /* I - Message catalog to load */
772 {
773 cups_file_t *fp; /* Message file */
774 cups_array_t *a; /* Message array */
775 _cups_message_t *m; /* Current message */
776 char s[4096], /* String buffer */
777 *ptr, /* Pointer into buffer */
778 *temp; /* New string */
779 int length; /* Length of combined strings */
780
781
782 DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
783
784 /*
785 * Create an array to hold the messages...
786 */
787
788 if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
789 return (NULL);
790
791 /*
792 * Open the message catalog file...
793 */
794
795 if ((fp = cupsFileOpen(filename, "r")) == NULL)
796 return (a);
797
798 /*
799 * Read messages from the catalog file until EOF...
800 *
801 * The format is the GNU gettext .po format, which is fairly simple:
802 *
803 * msgid "some text"
804 * msgstr "localized text"
805 *
806 * The ID and localized text can span multiple lines using the form:
807 *
808 * msgid ""
809 * "some long text"
810 * msgstr ""
811 * "localized text spanning "
812 * "multiple lines"
813 */
814
815 m = NULL;
816
817 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
818 {
819 /*
820 * Skip blank and comment lines...
821 */
822
823 if (s[0] == '#' || !s[0])
824 continue;
825
826 /*
827 * Strip the trailing quote...
828 */
829
830 if ((ptr = strrchr(s, '\"')) == NULL)
831 continue;
832
833 *ptr = '\0';
834
835 /*
836 * Find start of value...
837 */
838
839 if ((ptr = strchr(s, '\"')) == NULL)
840 continue;
841
842 ptr ++;
843
844 /*
845 * Unquote the text...
846 */
847
848 cups_unquote(ptr, ptr);
849
850 /*
851 * Create or add to a message...
852 */
853
854 if (!strncmp(s, "msgid", 5))
855 {
856 /*
857 * Add previous message as needed...
858 */
859
860 if (m)
861 cupsArrayAdd(a, m);
862
863 /*
864 * Create a new message with the given msgid string...
865 */
866
867 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
868 {
869 cupsFileClose(fp);
870 return (a);
871 }
872
873 m->id = strdup(ptr);
874 }
875 else if (s[0] == '\"' && m)
876 {
877 /*
878 * Append to current string...
879 */
880
881 length = (int)strlen(m->str ? m->str : m->id);
882
883 if ((temp = realloc(m->str ? m->str : m->id,
884 length + strlen(ptr) + 1)) == NULL)
885 {
886 cupsFileClose(fp);
887 return (a);
888 }
889
890 if (m->str)
891 {
892 /*
893 * Copy the new portion to the end of the msgstr string - safe
894 * to use strcpy because the buffer is allocated to the correct
895 * size...
896 */
897
898 m->str = temp;
899
900 strcpy(m->str + length, ptr);
901 }
902 else
903 {
904 /*
905 * Copy the new portion to the end of the msgid string - safe
906 * to use strcpy because the buffer is allocated to the correct
907 * size...
908 */
909
910 m->id = temp;
911
912 strcpy(m->id + length, ptr);
913 }
914 }
915 else if (!strncmp(s, "msgstr", 6) && m)
916 {
917 /*
918 * Set the string...
919 */
920
921 m->str = strdup(ptr);
922 }
923 }
924
925 /*
926 * Add the last message string to the array as needed...
927 */
928
929 if (m)
930 cupsArrayAdd(a, m);
931
932 /*
933 * Close the message catalog file and return the new array...
934 */
935
936 cupsFileClose(fp);
937
938 return (a);
939 }
940
941
942 /*
943 * '_cupsMessageLookup()' - Lookup a message string.
944 */
945
946 const char * /* O - Localized message */
947 _cupsMessageLookup(cups_array_t *a, /* I - Message array */
948 const char *m) /* I - Message */
949 {
950 _cups_message_t key, /* Search key */
951 *match; /* Matching message */
952
953
954 /*
955 * Lookup the message string; if it doesn't exist in the catalog,
956 * then return the message that was passed to us...
957 */
958
959 key.id = (char *)m;
960 match = (_cups_message_t *)cupsArrayFind(a, &key);
961
962 if (match && match->str)
963 return (match->str);
964 else
965 return (m);
966 }
967
968
969 #ifdef __APPLE__
970 /*
971 * Code & data to translate OSX's language names to their ISO 639-1 locale.
972 *
973 * The first version uses the new CoreFoundation API added in 10.3 (Panther),
974 * the second is for 10.2 (Jaguar).
975 */
976
977 # ifdef HAVE_CF_LOCALE_ID
978
979 typedef struct
980 {
981 const char * const name; /* Language name */
982 const char * const locale; /* Locale name */
983 } _apple_name_locale_t;
984
985 static const _apple_name_locale_t apple_name_locale[] =
986 {
987 { "en" , "en_US" },
988 { "nb" , "no" },
989 { "zh-Hans" , "zh_CN" },
990 { "zh-Hant" , "zh_TW" }
991 };
992
993
994 /*
995 * 'appleLangDefault()' - Get the default locale string.
996 */
997
998 static const char * /* O - Locale string */
999 appleLangDefault(void)
1000 {
1001 int i; /* Looping var */
1002 CFBundleRef bundle; /* Main bundle (if any) */
1003 CFArrayRef bundleList; /* List of localizations in bundle */
1004 CFPropertyListRef localizationList;
1005 /* List of localization data */
1006 CFStringRef languageName; /* Current name */
1007 CFStringRef localeName; /* Canonical from of name */
1008 char *lang; /* LANG environment variable */
1009 _cups_globals_t *cg = _cupsGlobals();
1010 /* Pointer to library globals */
1011
1012
1013 /*
1014 * Only do the lookup and translation the first time.
1015 */
1016
1017 if (!cg->language[0])
1018 {
1019 if ((lang = getenv("LANG")))
1020 {
1021 strlcpy(cg->language, lang, sizeof(cg->language));
1022 return (cg->language);
1023 }
1024 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1025 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
1026 {
1027 localizationList =
1028 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
1029
1030 CFRelease(bundleList);
1031 }
1032 else
1033 localizationList =
1034 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1035 kCFPreferencesCurrentApplication);
1036
1037 if (localizationList)
1038 {
1039 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1040 CFArrayGetCount(localizationList) > 0)
1041 {
1042 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1043
1044 if (languageName &&
1045 CFGetTypeID(languageName) == CFStringGetTypeID())
1046 {
1047 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1048 kCFAllocatorDefault, languageName);
1049
1050 if (localeName)
1051 {
1052 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1053 kCFStringEncodingASCII);
1054 CFRelease(localeName);
1055
1056 DEBUG_printf(("appleLangDefault: cg->language=\"%s\"\n",
1057 cg->language));
1058
1059 /*
1060 * Map new language identifiers to locales...
1061 */
1062
1063 for (i = 0;
1064 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1065 i++)
1066 {
1067 if (!strcmp(cg->language, apple_name_locale[i].name))
1068 {
1069 DEBUG_printf(("appleLangDefault: mapping \"%s\" to \"%s\"...\n",
1070 cg->language, apple_name_locale[i].locale));
1071 strlcpy(cg->language, apple_name_locale[i].locale,
1072 sizeof(cg->language));
1073 break;
1074 }
1075 }
1076
1077 /*
1078 * Convert language subtag into region subtag...
1079 */
1080
1081 if (cg->language[2] == '-')
1082 cg->language[2] = '_';
1083
1084 if (!strchr(cg->language, '.'))
1085 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1086 }
1087 }
1088 }
1089
1090 CFRelease(localizationList);
1091 }
1092
1093 /*
1094 * If we didn't find the language, default to en_US...
1095 */
1096
1097 if (!cg->language[0])
1098 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1099 }
1100
1101 /*
1102 * Return the cached locale...
1103 */
1104
1105 return (cg->language);
1106 }
1107 # else
1108 /*
1109 * Code & data to translate OSX 10.2's language names to their ISO 639-1
1110 * locale.
1111 */
1112
1113 typedef struct
1114 {
1115 const char * const name; /* Language name */
1116 const char * const locale; /* Locale name */
1117 } _apple_name_locale_t;
1118
1119 static const _apple_name_locale_t apple_name_locale[] =
1120 {
1121 { "English" , "en_US.UTF-8" }, { "French" , "fr.UTF-8" },
1122 { "German" , "de.UTF-8" }, { "Italian" , "it.UTF-8" },
1123 { "Dutch" , "nl.UTF-8" }, { "Swedish" , "sv.UTF-8" },
1124 { "Spanish" , "es.UTF-8" }, { "Danish" , "da.UTF-8" },
1125 { "Portuguese" , "pt.UTF-8" }, { "Norwegian" , "no.UTF-8" },
1126 { "Hebrew" , "he.UTF-8" }, { "Japanese" , "ja.UTF-8" },
1127 { "Arabic" , "ar.UTF-8" }, { "Finnish" , "fi.UTF-8" },
1128 { "Greek" , "el.UTF-8" }, { "Icelandic" , "is.UTF-8" },
1129 { "Maltese" , "mt.UTF-8" }, { "Turkish" , "tr.UTF-8" },
1130 { "Croatian" , "hr.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1131 { "Urdu" , "ur.UTF-8" }, { "Hindi" , "hi.UTF-8" },
1132 { "Thai" , "th.UTF-8" }, { "Korean" , "ko.UTF-8" },
1133 { "Lithuanian" , "lt.UTF-8" }, { "Polish" , "pl.UTF-8" },
1134 { "Hungarian" , "hu.UTF-8" }, { "Estonian" , "et.UTF-8" },
1135 { "Latvian" , "lv.UTF-8" }, { "Sami" , "se.UTF-8" },
1136 { "Faroese" , "fo.UTF-8" }, { "Farsi" , "fa.UTF-8" },
1137 { "Russian" , "ru.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1138 { "Dutch" , "nl.UTF-8" }, { "Irish" , "ga.UTF-8" },
1139 { "Albanian" , "sq.UTF-8" }, { "Romanian" , "ro.UTF-8" },
1140 { "Czech" , "cs.UTF-8" }, { "Slovak" , "sk.UTF-8" },
1141 { "Slovenian" , "sl.UTF-8" }, { "Yiddish" , "yi.UTF-8" },
1142 { "Serbian" , "sr.UTF-8" }, { "Macedonian" , "mk.UTF-8" },
1143 { "Bulgarian" , "bg.UTF-8" }, { "Ukrainian" , "uk.UTF-8" },
1144 { "Byelorussian", "be.UTF-8" }, { "Uzbek" , "uz.UTF-8" },
1145 { "Kazakh" , "kk.UTF-8" }, { "Azerbaijani", "az.UTF-8" },
1146 { "Azerbaijani" , "az.UTF-8" }, { "Armenian" , "hy.UTF-8" },
1147 { "Georgian" , "ka.UTF-8" }, { "Moldavian" , "mo.UTF-8" },
1148 { "Kirghiz" , "ky.UTF-8" }, { "Tajiki" , "tg.UTF-8" },
1149 { "Turkmen" , "tk.UTF-8" }, { "Mongolian" , "mn.UTF-8" },
1150 { "Mongolian" , "mn.UTF-8" }, { "Pashto" , "ps.UTF-8" },
1151 { "Kurdish" , "ku.UTF-8" }, { "Kashmiri" , "ks.UTF-8" },
1152 { "Sindhi" , "sd.UTF-8" }, { "Tibetan" , "bo.UTF-8" },
1153 { "Nepali" , "ne.UTF-8" }, { "Sanskrit" , "sa.UTF-8" },
1154 { "Marathi" , "mr.UTF-8" }, { "Bengali" , "bn.UTF-8" },
1155 { "Assamese" , "as.UTF-8" }, { "Gujarati" , "gu.UTF-8" },
1156 { "Punjabi" , "pa.UTF-8" }, { "Oriya" , "or.UTF-8" },
1157 { "Malayalam" , "ml.UTF-8" }, { "Kannada" , "kn.UTF-8" },
1158 { "Tamil" , "ta.UTF-8" }, { "Telugu" , "te.UTF-8" },
1159 { "Sinhalese" , "si.UTF-8" }, { "Burmese" , "my.UTF-8" },
1160 { "Khmer" , "km.UTF-8" }, { "Lao" , "lo.UTF-8" },
1161 { "Vietnamese" , "vi.UTF-8" }, { "Indonesian" , "id.UTF-8" },
1162 { "Tagalog" , "tl.UTF-8" }, { "Malay" , "ms.UTF-8" },
1163 { "Malay" , "ms.UTF-8" }, { "Amharic" , "am.UTF-8" },
1164 { "Tigrinya" , "ti.UTF-8" }, { "Oromo" , "om.UTF-8" },
1165 { "Somali" , "so.UTF-8" }, { "Swahili" , "sw.UTF-8" },
1166 { "Kinyarwanda" , "rw.UTF-8" }, { "Rundi" , "rn.UTF-8" },
1167 { "Nyanja" , "" }, { "Malagasy" , "mg.UTF-8" },
1168 { "Esperanto" , "eo.UTF-8" }, { "Welsh" , "cy.UTF-8" },
1169 { "Basque" , "eu.UTF-8" }, { "Catalan" , "ca.UTF-8" },
1170 { "Latin" , "la.UTF-8" }, { "Quechua" , "qu.UTF-8" },
1171 { "Guarani" , "gn.UTF-8" }, { "Aymara" , "ay.UTF-8" },
1172 { "Tatar" , "tt.UTF-8" }, { "Uighur" , "ug.UTF-8" },
1173 { "Dzongkha" , "dz.UTF-8" }, { "Javanese" , "jv.UTF-8" },
1174 { "Sundanese" , "su.UTF-8" }, { "Galician" , "gl.UTF-8" },
1175 { "Afrikaans" , "af.UTF-8" }, { "Breton" , "br.UTF-8" },
1176 { "Inuktitut" , "iu.UTF-8" }, { "Scottish" , "gd.UTF-8" },
1177 { "Manx" , "gv.UTF-8" }, { "Irish" , "ga.UTF-8" },
1178 { "Tongan" , "to.UTF-8" }, { "Greek" , "el.UTF-8" },
1179 { "Greenlandic" , "kl.UTF-8" }, { "Azerbaijani", "az.UTF-8" }
1180 };
1181
1182
1183 /*
1184 * 'appleLangDefault()' - Get the default locale string.
1185 */
1186
1187 static const char * /* O - Locale string */
1188 appleLangDefault(void)
1189 {
1190 int i; /* Looping var */
1191 CFPropertyListRef localizationList;
1192 /* List of localization data */
1193 CFStringRef localizationName;
1194 /* Current name */
1195 char buff[256]; /* Temporary buffer */
1196 _cups_globals_t *cg = _cupsGlobals();
1197 /* Pointer to library globals */
1198 char *lang; /* LANG environment variable */
1199
1200
1201 /*
1202 * Only do the lookup and translation the first time.
1203 */
1204
1205 if (!cg->language[0])
1206 {
1207 if ((lang = getenv("LANG")))
1208 strlcpy(cg->language, lang, sizeof(cg->language));
1209 else
1210 {
1211 localizationList =
1212 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1213 kCFPreferencesCurrentApplication);
1214
1215 if (localizationList != NULL)
1216 {
1217 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1218 CFArrayGetCount(localizationList) > 0)
1219 {
1220 localizationName = CFArrayGetValueAtIndex(localizationList, 0);
1221
1222 if (localizationName != NULL &&
1223 CFGetTypeID(localizationName) == CFStringGetTypeID())
1224 {
1225 CFIndex length = CFStringGetLength(localizationName);
1226
1227 if (length <= sizeof(buff) &&
1228 CFStringGetCString(localizationName, buff, sizeof(buff),
1229 kCFStringEncodingASCII))
1230 {
1231 buff[sizeof(buff) - 1] = '\0';
1232
1233 for (i = 0;
1234 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1235 i++)
1236 {
1237 if (!strcasecmp(buff, apple_name_locale[i].name))
1238 {
1239 strlcpy(cg->language, apple_name_locale[i].locale,
1240 sizeof(cg->language));
1241 break;
1242 }
1243 }
1244 }
1245 }
1246 }
1247
1248 CFRelease(localizationList);
1249 }
1250 }
1251
1252 /*
1253 * If we didn't find the language, default to en_US...
1254 */
1255
1256 if (!cg->language[0])
1257 strlcpy(cg->language, apple_name_locale[0].locale, sizeof(cg->language));
1258 }
1259
1260 /*
1261 * Return the cached locale...
1262 */
1263
1264 return (cg->language);
1265 }
1266 # endif /* HAVE_CF_LOCALE_ID */
1267 #endif /* __APPLE__ */
1268
1269
1270 /*
1271 * 'cups_cache_lookup()' - Lookup a language in the cache...
1272 */
1273
1274 static cups_lang_t * /* O - Language data or NULL */
1275 cups_cache_lookup(const char *name,/* I - Name of locale */
1276 cups_encoding_t encoding)
1277 /* I - Encoding of locale */
1278 {
1279 cups_lang_t *lang; /* Current language */
1280
1281
1282 DEBUG_printf(("cups_cache_lookup(name=\"%s\", encoding=%d(%s))\n", name,
1283 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1284 lang_encodings[encoding]));
1285
1286 /*
1287 * Loop through the cache and return a match if found...
1288 */
1289
1290 for (lang = lang_cache; lang != NULL; lang = lang->next)
1291 {
1292 DEBUG_printf(("cups_cache_lookup: lang=%p, language=\"%s\", encoding=%d(%s)\n",
1293 lang, lang->language, lang->encoding,
1294 lang_encodings[lang->encoding]));
1295
1296 if (!strcmp(lang->language, name) &&
1297 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1298 {
1299 lang->used ++;
1300
1301 DEBUG_puts("cups_cache_lookup: returning match!");
1302
1303 return (lang);
1304 }
1305 }
1306
1307 DEBUG_puts("cups_cache_lookup: returning NULL!");
1308
1309 return (NULL);
1310 }
1311
1312
1313 /*
1314 * 'cups_message_compare()' - Compare two messages.
1315 */
1316
1317 static int /* O - Result of comparison */
1318 cups_message_compare(
1319 _cups_message_t *m1, /* I - First message */
1320 _cups_message_t *m2) /* I - Second message */
1321 {
1322 return (strcmp(m1->id, m2->id));
1323 }
1324
1325
1326 /*
1327 * 'cups_unquote()' - Unquote characters in strings...
1328 */
1329
1330 static void
1331 cups_unquote(char *d, /* O - Unquoted string */
1332 const char *s) /* I - Original string */
1333 {
1334 while (*s)
1335 {
1336 if (*s == '\\')
1337 {
1338 s ++;
1339 if (isdigit(*s))
1340 {
1341 *d = 0;
1342
1343 while (isdigit(*s))
1344 {
1345 *d = *d * 8 + *s - '0';
1346 s ++;
1347 }
1348
1349 d ++;
1350 }
1351 else
1352 {
1353 if (*s == 'n')
1354 *d ++ = '\n';
1355 else if (*s == 'r')
1356 *d ++ = '\r';
1357 else if (*s == 't')
1358 *d ++ = '\t';
1359 else
1360 *d++ = *s;
1361
1362 s ++;
1363 }
1364 }
1365 else
1366 *d++ = *s++;
1367 }
1368
1369 *d = '\0';
1370 }
1371
1372
1373 /*
1374 * End of "$Id: language.c 6917 2007-09-05 21:14:17Z mike $".
1375 */