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