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