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