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