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