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