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