]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Merge changes from CUPS 1.3.1.
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
db1f069b 2 * "$Id: language.c 6917 2007-09-05 21:14:17Z mike $"
ef416fc2 3 *
4 * I18N/language support for the Common UNIX Printing System (CUPS).
5 *
bc44d920 6 * Copyright 2007 by Apple Inc.
b86bc4cf 7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 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.
ef416fc2 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>
bd7854cb 44#include <errno.h>
ef416fc2 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 */
fa73b229 53#ifdef HAVE_COREFOUNDATION_H
54# include <CoreFoundation/CoreFoundation.h>
55#endif /* HAVE_COREFOUNDATION_H */
ef416fc2 56
57
d6ae789d 58/*
59 * Local globals...
60 */
61
62#ifdef HAVE_PTHREAD_H
63static pthread_mutex_t lang_mutex = PTHREAD_MUTEX_INITIALIZER;
64 /* Mutex to control access to cache */
65#endif /* HAVE_PTHREAD_H */
66static cups_lang_t *lang_cache = NULL;
67 /* Language string cache */
68
69
ef416fc2 70/*
71 * Local functions...
72 */
73
74#ifdef __APPLE__
ef416fc2 75static const char *appleLangDefault(void);
76#endif /* __APPLE__ */
77static cups_lang_t *cups_cache_lookup(const char *name,
78 cups_encoding_t encoding);
79static int cups_message_compare(_cups_message_t *m1,
80 _cups_message_t *m2);
81static void cups_unquote(char *d, const char *s);
82
83
84/*
85 * Local globals...
86 */
87
88static 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",
bd7854cb 104 "iso-8859-16", "mac-roman",
ef416fc2 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
164const 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
180cups_lang_t * /* O - Language data */
181cupsLangDefault(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
192const char * /* O - Character encoding */
193cupsLangEncoding(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
206void
207cupsLangFlush(void)
208{
209 cups_lang_t *lang, /* Current language */
210 *next; /* Next language */
ef416fc2 211
212
213 /*
214 * Free all languages in the cache...
215 */
216
d6ae789d 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)
ef416fc2 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
d6ae789d 237 lang_cache = NULL;
238
239#ifdef HAVE_PTHREAD_H
240 pthread_mutex_unlock(&lang_mutex);
241#endif /* HAVE_PTHREAD_H */
ef416fc2 242}
243
244
245/*
246 * 'cupsLangFree()' - Free language data.
247 *
248 * This does not actually free anything; use cupsLangFlush() for that.
249 */
250
251void
252cupsLangFree(cups_lang_t *lang) /* I - Language to free */
253{
d6ae789d 254#ifdef HAVE_PTHREAD_H
255 pthread_mutex_lock(&lang_mutex);
256#endif /* HAVE_PTHREAD_H */
257
ef416fc2 258 if (lang != NULL && lang->used > 0)
259 lang->used --;
d6ae789d 260
261#ifdef HAVE_PTHREAD_H
262 pthread_mutex_unlock(&lang_mutex);
263#endif /* HAVE_PTHREAD_H */
ef416fc2 264}
265
266
267/*
268 * 'cupsLangGet()' - Get a language.
269 */
270
271cups_lang_t * /* O - Language data */
272cupsLangGet(const char *language) /* I - Language or locale */
273{
274 int i; /* Looping var */
480ef0fe 275#ifndef __APPLE__
276 char locale[255]; /* Copy of locale name */
277#endif /* !__APPLE__ */
278 char langname[16], /* Requested language name */
ef416fc2 279 country[16], /* Country code */
280 charset[16], /* Character set */
ef416fc2 281 *csptr, /* Pointer to CODESET string */
ef416fc2 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... */
ef416fc2 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",
bd7854cb 298 "ISO885916", "MACROMAN", "", "",
ef416fc2 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__
757d2cad 334 /*
335 * Set the character set to UTF-8...
336 */
337
338 strcpy(charset, "UTF8");
339
ef416fc2 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
3d8365b8 345 if (!language && (language = getenv("LANG")) == NULL)
ef416fc2 346 language = appleLangDefault();
757d2cad 347
ef416fc2 348#else
757d2cad 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)
ef416fc2 362 {
363 /*
364 * First see if the locale has been set; if it is still "C" or
757d2cad 365 * "POSIX", use the environment to get the default...
ef416fc2 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"))
ef416fc2 378 {
757d2cad 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 }
757d2cad 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";
ef416fc2 409 }
ef416fc2 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 /*
757d2cad 430 * Switch to the POSIX ("C") locale...
ef416fc2 431 */
432
757d2cad 433 language = "C";
ef416fc2 434 }
435
ef416fc2 436#ifdef CODESET
437 /*
438 * On systems that support the nl_langinfo(CODESET) call, use
439 * this value as the character set...
440 */
441
757d2cad 442 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
ef416fc2 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
07725fee 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
ef416fc2 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
ef416fc2 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 {
bd7854cb 545 for (i = 0;
546 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
547 i ++)
ef416fc2 548 if (!strcasecmp(charset, locale_encodings[i]))
549 {
550 encoding = (cups_encoding_t)i;
551 break;
552 }
8ca02f3c 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 }
ef416fc2 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
bd7854cb 580 if (country[0])
581 {
582 snprintf(real, sizeof(real), "%s_%s", langname, country);
ef416fc2 583
bd7854cb 584 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
585 real, real);
586 }
587 else
d6ae789d 588 {
589 strcpy(real, langname);
bd7854cb 590 filename[0] = '\0'; /* anti-compiler-warning-code */
d6ae789d 591 }
592
593#ifdef HAVE_PTHREAD_H
594 pthread_mutex_lock(&lang_mutex);
595#endif /* HAVE_PTHREAD_H */
596
c0e1af83 597 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
d6ae789d 598 {
599#ifdef HAVE_PTHREAD_H
600 pthread_mutex_unlock(&lang_mutex);
601#endif /* HAVE_PTHREAD_H */
602
603 return (lang);
604 }
ef416fc2 605
606 if (!country[0] || access(filename, 0))
607 {
608 /*
609 * Country localization not available, look for generic localization...
610 */
611
bd7854cb 612 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
ef416fc2 613 langname, langname);
614
615 if (access(filename, 0))
616 {
617 /*
618 * No generic localization, so use POSIX...
619 */
620
bd7854cb 621 DEBUG_printf(("access(\"%s\", 0): %s\n", filename, strerror(errno)));
622
bd7854cb 623 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
ef416fc2 624 }
ef416fc2 625 }
626
627 /*
628 * See if there is a free language available; if so, use that
629 * record...
630 */
631
d6ae789d 632 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 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)
d6ae789d 643 {
644#ifdef HAVE_PTHREAD_H
645 pthread_mutex_unlock(&lang_mutex);
646#endif /* HAVE_PTHREAD_H */
647
ef416fc2 648 return (NULL);
d6ae789d 649 }
ef416fc2 650
d6ae789d 651 lang->next = lang_cache;
652 lang_cache = lang;
ef416fc2 653 }
bd7854cb 654 else
655 {
656 /*
657 * Free all old strings as needed...
658 */
ef416fc2 659
bd7854cb 660 _cupsMessageFree(lang->strings);
661 }
ef416fc2 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
d6ae789d 685#ifdef HAVE_PTHREAD_H
686 pthread_mutex_unlock(&lang_mutex);
687#endif /* HAVE_PTHREAD_H */
688
ef416fc2 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
700const 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
d6ae789d 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
ef416fc2 724 return (_cupsMessageLookup(lang->strings, message));
d6ae789d 725#endif /* HAVE_PTHREAD_H */
ef416fc2 726}
727
728
729/*
730 * '_cupsMessageFree()' - Free a messages array.
731 */
732
733void
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
770cups_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
bd7854cb 782 DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
783
ef416fc2 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 *
bd7854cb 806 * The ID and localized text can span multiple lines using the form:
ef416fc2 807 *
bd7854cb 808 * msgid ""
809 * "some long text"
810 * msgstr ""
811 * "localized text spanning "
ef416fc2 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 {
bd7854cb 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
ef416fc2 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);
ef416fc2 874 }
bd7854cb 875 else if (s[0] == '\"' && m)
ef416fc2 876 {
bd7854cb 877 /*
878 * Append to current string...
879 */
ef416fc2 880
b86bc4cf 881 length = (int)strlen(m->str ? m->str : m->id);
ef416fc2 882
bd7854cb 883 if ((temp = realloc(m->str ? m->str : m->id,
884 length + strlen(ptr) + 1)) == NULL)
885 {
886 cupsFileClose(fp);
887 return (a);
888 }
ef416fc2 889
bd7854cb 890 if (m->str)
891 {
ef416fc2 892 /*
bd7854cb 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...
ef416fc2 896 */
897
bd7854cb 898 m->str = temp;
899
ef416fc2 900 strcpy(m->str + length, ptr);
901 }
902 else
903 {
904 /*
bd7854cb 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...
ef416fc2 908 */
909
bd7854cb 910 m->id = temp;
911
912 strcpy(m->id + length, ptr);
ef416fc2 913 }
914 }
bd7854cb 915 else if (!strncmp(s, "msgstr", 6) && m)
916 {
917 /*
918 * Set the string...
919 */
920
921 m->str = strdup(ptr);
922 }
ef416fc2 923 }
924
bd7854cb 925 /*
926 * Add the last message string to the array as needed...
927 */
928
929 if (m)
930 cupsArrayAdd(a, m);
931
ef416fc2 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
946const 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
ef416fc2 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
b94498cf 978
979typedef struct
980{
981 const char * const name; /* Language name */
982 const char * const locale; /* Locale name */
983} _apple_name_locale_t;
984
985static const _apple_name_locale_t apple_name_locale[] =
986{
987 { "en" , "en_US" },
db1f069b 988 { "nb" , "no" },
b94498cf 989 { "zh-Hans" , "zh_CN" },
990 { "zh-Hant" , "zh_TW" }
991};
992
db1f069b 993
ef416fc2 994/*
995 * 'appleLangDefault()' - Get the default locale string.
996 */
997
998static const char * /* O - Locale string */
999appleLangDefault(void)
1000{
b94498cf 1001 int i; /* Looping var */
db1f069b
MS
1002 CFBundleRef bundle; /* Main bundle (if any) */
1003 CFArrayRef bundleList; /* List of localizations in bundle */
ef416fc2 1004 CFPropertyListRef localizationList;
1005 /* List of localization data */
1006 CFStringRef languageName; /* Current name */
1007 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1008 char *lang; /* LANG environment variable */
ef416fc2 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 {
26d47ec6 1019 if ((lang = getenv("LANG")))
db1f069b 1020 {
26d47ec6 1021 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1022 return (cg->language);
1023 }
1024 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1025 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1026 {
26d47ec6 1027 localizationList =
db1f069b 1028 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
26d47ec6 1029
db1f069b
MS
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)
ef416fc2 1041 {
db1f069b
MS
1042 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1043
1044 if (languageName &&
1045 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1046 {
db1f069b
MS
1047 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1048 kCFAllocatorDefault, languageName);
ef416fc2 1049
db1f069b
MS
1050 if (localeName)
1051 {
1052 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1053 kCFStringEncodingASCII);
1054 CFRelease(localeName);
ef416fc2 1055
db1f069b
MS
1056 DEBUG_printf(("appleLangDefault: cg->language=\"%s\"\n",
1057 cg->language));
26d47ec6 1058
db1f069b
MS
1059 /*
1060 * Map new language identifiers to locales...
1061 */
b94498cf 1062
db1f069b
MS
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))
b94498cf 1068 {
db1f069b
MS
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;
b94498cf 1074 }
db1f069b 1075 }
b94498cf 1076
db1f069b
MS
1077 /*
1078 * Convert language subtag into region subtag...
1079 */
b94498cf 1080
db1f069b
MS
1081 if (cg->language[2] == '-')
1082 cg->language[2] = '_';
b94498cf 1083
db1f069b
MS
1084 if (!strchr(cg->language, '.'))
1085 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1086 }
26d47ec6 1087 }
26d47ec6 1088 }
db1f069b
MS
1089
1090 CFRelease(localizationList);
ef416fc2 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
1113typedef struct
1114{
1115 const char * const name; /* Language name */
1116 const char * const locale; /* Locale name */
1117} _apple_name_locale_t;
1118
1119static 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
1187static const char * /* O - Locale string */
1188appleLangDefault(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 */
26d47ec6 1198 char *lang; /* LANG environment variable */
ef416fc2 1199
1200
1201 /*
1202 * Only do the lookup and translation the first time.
1203 */
1204
26d47ec6 1205 if (!cg->language[0])
ef416fc2 1206 {
26d47ec6 1207 if ((lang = getenv("LANG")))
1208 strlcpy(cg->language, lang, sizeof(cg->language));
1209 else
ef416fc2 1210 {
26d47ec6 1211 localizationList =
1212 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1213 kCFPreferencesCurrentApplication);
ef416fc2 1214
26d47ec6 1215 if (localizationList != NULL)
1216 {
1217 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1218 CFArrayGetCount(localizationList) > 0)
ef416fc2 1219 {
26d47ec6 1220 localizationName = CFArrayGetValueAtIndex(localizationList, 0);
ef416fc2 1221
26d47ec6 1222 if (localizationName != NULL &&
1223 CFGetTypeID(localizationName) == CFStringGetTypeID())
ef416fc2 1224 {
26d47ec6 1225 CFIndex length = CFStringGetLength(localizationName);
ef416fc2 1226
26d47ec6 1227 if (length <= sizeof(buff) &&
1228 CFStringGetCString(localizationName, buff, sizeof(buff),
1229 kCFStringEncodingASCII))
ef416fc2 1230 {
26d47ec6 1231 buff[sizeof(buff) - 1] = '\0';
1232
1233 for (i = 0;
1234 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1235 i++)
ef416fc2 1236 {
26d47ec6 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 }
ef416fc2 1243 }
1244 }
1245 }
1246 }
ef416fc2 1247
26d47ec6 1248 CFRelease(localizationList);
1249 }
ef416fc2 1250 }
1251
1252 /*
1253 * If we didn't find the language, default to en_US...
1254 */
1255
26d47ec6 1256 if (!cg->language[0])
1257 strlcpy(cg->language, apple_name_locale[0].locale, sizeof(cg->language));
ef416fc2 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
1274static cups_lang_t * /* O - Language data or NULL */
1275cups_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
d6ae789d 1290 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 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
1317static int /* O - Result of comparison */
1318cups_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
1330static void
1331cups_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 }
d09495fa 1348
1349 d ++;
ef416fc2 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/*
db1f069b 1374 * End of "$Id: language.c 6917 2007-09-05 21:14:17Z mike $".
ef416fc2 1375 */