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