]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
bc44d920 2 * "$Id: language.c 6649 2007-07-11 21:46:42Z 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" },
988 { "no" , "nb" },
989 { "zh-Hans" , "zh_CN" },
990 { "zh-Hant" , "zh_TW" }
991};
992
ef416fc2 993/*
994 * 'appleLangDefault()' - Get the default locale string.
995 */
996
997static const char * /* O - Locale string */
998appleLangDefault(void)
999{
b94498cf 1000 int i; /* Looping var */
ef416fc2 1001 CFPropertyListRef localizationList;
1002 /* List of localization data */
1003 CFStringRef languageName; /* Current name */
1004 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1005 char *lang; /* LANG environment variable */
ef416fc2 1006 _cups_globals_t *cg = _cupsGlobals();
1007 /* Pointer to library globals */
1008
1009
1010 /*
1011 * Only do the lookup and translation the first time.
1012 */
1013
1014 if (!cg->language[0])
1015 {
26d47ec6 1016 if ((lang = getenv("LANG")))
1017 strlcpy(cg->language, lang, sizeof(cg->language));
1018 else
ef416fc2 1019 {
26d47ec6 1020 localizationList =
1021 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1022 kCFPreferencesCurrentApplication);
1023
1024 if (localizationList != NULL)
ef416fc2 1025 {
26d47ec6 1026 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1027 CFArrayGetCount(localizationList) > 0)
1028 {
1029 languageName = CFArrayGetValueAtIndex(localizationList, 0);
ef416fc2 1030
26d47ec6 1031 if (languageName != NULL &&
1032 CFGetTypeID(languageName) == CFStringGetTypeID())
1033 {
1034 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1035 kCFAllocatorDefault, languageName);
ef416fc2 1036
26d47ec6 1037 if (localeName != NULL)
1038 {
1039 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1040 kCFStringEncodingASCII);
1041 CFRelease(localeName);
1042
b94498cf 1043 /*
1044 * Map new language identifiers to locales...
1045 */
1046
1047 for (i = 0;
1048 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1049 i++)
1050 {
1051 if (!strcmp(cg->language, apple_name_locale[i].name))
1052 {
1053 strlcpy(cg->language, apple_name_locale[i].locale,
1054 sizeof(cg->language));
1055 break;
1056 }
1057 }
1058
1059 /*
1060 * Convert language subtag into region subtag...
1061 */
1062
1063 if (cg->language[2] == '-')
1064 cg->language[2] = '_';
1065
1066 if (strchr(cg->language, '.') == NULL)
26d47ec6 1067 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1068 }
1069 }
1070 }
ef416fc2 1071
26d47ec6 1072 CFRelease(localizationList);
1073 }
ef416fc2 1074 }
1075
1076 /*
1077 * If we didn't find the language, default to en_US...
1078 */
1079
1080 if (!cg->language[0])
1081 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1082 }
1083
1084 /*
1085 * Return the cached locale...
1086 */
1087
1088 return (cg->language);
1089}
1090# else
1091/*
1092 * Code & data to translate OSX 10.2's language names to their ISO 639-1
1093 * locale.
1094 */
1095
1096typedef struct
1097{
1098 const char * const name; /* Language name */
1099 const char * const locale; /* Locale name */
1100} _apple_name_locale_t;
1101
1102static const _apple_name_locale_t apple_name_locale[] =
1103{
1104 { "English" , "en_US.UTF-8" }, { "French" , "fr.UTF-8" },
1105 { "German" , "de.UTF-8" }, { "Italian" , "it.UTF-8" },
1106 { "Dutch" , "nl.UTF-8" }, { "Swedish" , "sv.UTF-8" },
1107 { "Spanish" , "es.UTF-8" }, { "Danish" , "da.UTF-8" },
1108 { "Portuguese" , "pt.UTF-8" }, { "Norwegian" , "no.UTF-8" },
1109 { "Hebrew" , "he.UTF-8" }, { "Japanese" , "ja.UTF-8" },
1110 { "Arabic" , "ar.UTF-8" }, { "Finnish" , "fi.UTF-8" },
1111 { "Greek" , "el.UTF-8" }, { "Icelandic" , "is.UTF-8" },
1112 { "Maltese" , "mt.UTF-8" }, { "Turkish" , "tr.UTF-8" },
1113 { "Croatian" , "hr.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1114 { "Urdu" , "ur.UTF-8" }, { "Hindi" , "hi.UTF-8" },
1115 { "Thai" , "th.UTF-8" }, { "Korean" , "ko.UTF-8" },
1116 { "Lithuanian" , "lt.UTF-8" }, { "Polish" , "pl.UTF-8" },
1117 { "Hungarian" , "hu.UTF-8" }, { "Estonian" , "et.UTF-8" },
1118 { "Latvian" , "lv.UTF-8" }, { "Sami" , "se.UTF-8" },
1119 { "Faroese" , "fo.UTF-8" }, { "Farsi" , "fa.UTF-8" },
1120 { "Russian" , "ru.UTF-8" }, { "Chinese" , "zh.UTF-8" },
1121 { "Dutch" , "nl.UTF-8" }, { "Irish" , "ga.UTF-8" },
1122 { "Albanian" , "sq.UTF-8" }, { "Romanian" , "ro.UTF-8" },
1123 { "Czech" , "cs.UTF-8" }, { "Slovak" , "sk.UTF-8" },
1124 { "Slovenian" , "sl.UTF-8" }, { "Yiddish" , "yi.UTF-8" },
1125 { "Serbian" , "sr.UTF-8" }, { "Macedonian" , "mk.UTF-8" },
1126 { "Bulgarian" , "bg.UTF-8" }, { "Ukrainian" , "uk.UTF-8" },
1127 { "Byelorussian", "be.UTF-8" }, { "Uzbek" , "uz.UTF-8" },
1128 { "Kazakh" , "kk.UTF-8" }, { "Azerbaijani", "az.UTF-8" },
1129 { "Azerbaijani" , "az.UTF-8" }, { "Armenian" , "hy.UTF-8" },
1130 { "Georgian" , "ka.UTF-8" }, { "Moldavian" , "mo.UTF-8" },
1131 { "Kirghiz" , "ky.UTF-8" }, { "Tajiki" , "tg.UTF-8" },
1132 { "Turkmen" , "tk.UTF-8" }, { "Mongolian" , "mn.UTF-8" },
1133 { "Mongolian" , "mn.UTF-8" }, { "Pashto" , "ps.UTF-8" },
1134 { "Kurdish" , "ku.UTF-8" }, { "Kashmiri" , "ks.UTF-8" },
1135 { "Sindhi" , "sd.UTF-8" }, { "Tibetan" , "bo.UTF-8" },
1136 { "Nepali" , "ne.UTF-8" }, { "Sanskrit" , "sa.UTF-8" },
1137 { "Marathi" , "mr.UTF-8" }, { "Bengali" , "bn.UTF-8" },
1138 { "Assamese" , "as.UTF-8" }, { "Gujarati" , "gu.UTF-8" },
1139 { "Punjabi" , "pa.UTF-8" }, { "Oriya" , "or.UTF-8" },
1140 { "Malayalam" , "ml.UTF-8" }, { "Kannada" , "kn.UTF-8" },
1141 { "Tamil" , "ta.UTF-8" }, { "Telugu" , "te.UTF-8" },
1142 { "Sinhalese" , "si.UTF-8" }, { "Burmese" , "my.UTF-8" },
1143 { "Khmer" , "km.UTF-8" }, { "Lao" , "lo.UTF-8" },
1144 { "Vietnamese" , "vi.UTF-8" }, { "Indonesian" , "id.UTF-8" },
1145 { "Tagalog" , "tl.UTF-8" }, { "Malay" , "ms.UTF-8" },
1146 { "Malay" , "ms.UTF-8" }, { "Amharic" , "am.UTF-8" },
1147 { "Tigrinya" , "ti.UTF-8" }, { "Oromo" , "om.UTF-8" },
1148 { "Somali" , "so.UTF-8" }, { "Swahili" , "sw.UTF-8" },
1149 { "Kinyarwanda" , "rw.UTF-8" }, { "Rundi" , "rn.UTF-8" },
1150 { "Nyanja" , "" }, { "Malagasy" , "mg.UTF-8" },
1151 { "Esperanto" , "eo.UTF-8" }, { "Welsh" , "cy.UTF-8" },
1152 { "Basque" , "eu.UTF-8" }, { "Catalan" , "ca.UTF-8" },
1153 { "Latin" , "la.UTF-8" }, { "Quechua" , "qu.UTF-8" },
1154 { "Guarani" , "gn.UTF-8" }, { "Aymara" , "ay.UTF-8" },
1155 { "Tatar" , "tt.UTF-8" }, { "Uighur" , "ug.UTF-8" },
1156 { "Dzongkha" , "dz.UTF-8" }, { "Javanese" , "jv.UTF-8" },
1157 { "Sundanese" , "su.UTF-8" }, { "Galician" , "gl.UTF-8" },
1158 { "Afrikaans" , "af.UTF-8" }, { "Breton" , "br.UTF-8" },
1159 { "Inuktitut" , "iu.UTF-8" }, { "Scottish" , "gd.UTF-8" },
1160 { "Manx" , "gv.UTF-8" }, { "Irish" , "ga.UTF-8" },
1161 { "Tongan" , "to.UTF-8" }, { "Greek" , "el.UTF-8" },
1162 { "Greenlandic" , "kl.UTF-8" }, { "Azerbaijani", "az.UTF-8" }
1163};
1164
1165
1166/*
1167 * 'appleLangDefault()' - Get the default locale string.
1168 */
1169
1170static const char * /* O - Locale string */
1171appleLangDefault(void)
1172{
1173 int i; /* Looping var */
1174 CFPropertyListRef localizationList;
1175 /* List of localization data */
1176 CFStringRef localizationName;
1177 /* Current name */
1178 char buff[256]; /* Temporary buffer */
1179 _cups_globals_t *cg = _cupsGlobals();
1180 /* Pointer to library globals */
26d47ec6 1181 char *lang; /* LANG environment variable */
ef416fc2 1182
1183
1184 /*
1185 * Only do the lookup and translation the first time.
1186 */
1187
26d47ec6 1188 if (!cg->language[0])
ef416fc2 1189 {
26d47ec6 1190 if ((lang = getenv("LANG")))
1191 strlcpy(cg->language, lang, sizeof(cg->language));
1192 else
ef416fc2 1193 {
26d47ec6 1194 localizationList =
1195 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1196 kCFPreferencesCurrentApplication);
ef416fc2 1197
26d47ec6 1198 if (localizationList != NULL)
1199 {
1200 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1201 CFArrayGetCount(localizationList) > 0)
ef416fc2 1202 {
26d47ec6 1203 localizationName = CFArrayGetValueAtIndex(localizationList, 0);
ef416fc2 1204
26d47ec6 1205 if (localizationName != NULL &&
1206 CFGetTypeID(localizationName) == CFStringGetTypeID())
ef416fc2 1207 {
26d47ec6 1208 CFIndex length = CFStringGetLength(localizationName);
ef416fc2 1209
26d47ec6 1210 if (length <= sizeof(buff) &&
1211 CFStringGetCString(localizationName, buff, sizeof(buff),
1212 kCFStringEncodingASCII))
ef416fc2 1213 {
26d47ec6 1214 buff[sizeof(buff) - 1] = '\0';
1215
1216 for (i = 0;
1217 i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
1218 i++)
ef416fc2 1219 {
26d47ec6 1220 if (!strcasecmp(buff, apple_name_locale[i].name))
1221 {
1222 strlcpy(cg->language, apple_name_locale[i].locale,
1223 sizeof(cg->language));
1224 break;
1225 }
ef416fc2 1226 }
1227 }
1228 }
1229 }
ef416fc2 1230
26d47ec6 1231 CFRelease(localizationList);
1232 }
ef416fc2 1233 }
1234
1235 /*
1236 * If we didn't find the language, default to en_US...
1237 */
1238
26d47ec6 1239 if (!cg->language[0])
1240 strlcpy(cg->language, apple_name_locale[0].locale, sizeof(cg->language));
ef416fc2 1241 }
1242
1243 /*
1244 * Return the cached locale...
1245 */
1246
1247 return (cg->language);
1248}
1249# endif /* HAVE_CF_LOCALE_ID */
1250#endif /* __APPLE__ */
1251
1252
1253/*
1254 * 'cups_cache_lookup()' - Lookup a language in the cache...
1255 */
1256
1257static cups_lang_t * /* O - Language data or NULL */
1258cups_cache_lookup(const char *name,/* I - Name of locale */
1259 cups_encoding_t encoding)
1260 /* I - Encoding of locale */
1261{
1262 cups_lang_t *lang; /* Current language */
1263
1264
1265 DEBUG_printf(("cups_cache_lookup(name=\"%s\", encoding=%d(%s))\n", name,
1266 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1267 lang_encodings[encoding]));
1268
1269 /*
1270 * Loop through the cache and return a match if found...
1271 */
1272
d6ae789d 1273 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1274 {
1275 DEBUG_printf(("cups_cache_lookup: lang=%p, language=\"%s\", encoding=%d(%s)\n",
1276 lang, lang->language, lang->encoding,
1277 lang_encodings[lang->encoding]));
1278
1279 if (!strcmp(lang->language, name) &&
1280 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1281 {
1282 lang->used ++;
1283
1284 DEBUG_puts("cups_cache_lookup: returning match!");
1285
1286 return (lang);
1287 }
1288 }
1289
1290 DEBUG_puts("cups_cache_lookup: returning NULL!");
1291
1292 return (NULL);
1293}
1294
1295
1296/*
1297 * 'cups_message_compare()' - Compare two messages.
1298 */
1299
1300static int /* O - Result of comparison */
1301cups_message_compare(
1302 _cups_message_t *m1, /* I - First message */
1303 _cups_message_t *m2) /* I - Second message */
1304{
1305 return (strcmp(m1->id, m2->id));
1306}
1307
1308
1309/*
1310 * 'cups_unquote()' - Unquote characters in strings...
1311 */
1312
1313static void
1314cups_unquote(char *d, /* O - Unquoted string */
1315 const char *s) /* I - Original string */
1316{
1317 while (*s)
1318 {
1319 if (*s == '\\')
1320 {
1321 s ++;
1322 if (isdigit(*s))
1323 {
1324 *d = 0;
1325
1326 while (isdigit(*s))
1327 {
1328 *d = *d * 8 + *s - '0';
1329 s ++;
1330 }
d09495fa 1331
1332 d ++;
ef416fc2 1333 }
1334 else
1335 {
1336 if (*s == 'n')
1337 *d ++ = '\n';
1338 else if (*s == 'r')
1339 *d ++ = '\r';
1340 else if (*s == 't')
1341 *d ++ = '\t';
1342 else
1343 *d++ = *s;
1344
1345 s ++;
1346 }
1347 }
1348 else
1349 *d++ = *s++;
1350 }
1351
1352 *d = '\0';
1353}
1354
1355
1356/*
bc44d920 1357 * End of "$Id: language.c 6649 2007-07-11 21:46:42Z mike $".
ef416fc2 1358 */