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