]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Import CUPS v2.0b1
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
1a18c85c 2 * "$Id: language.c 11558 2014-02-06 18:33:34Z msweet $"
ef416fc2 3 *
1a18c85c 4 * I18N/language support for CUPS.
ef416fc2 5 *
1a18c85c
MS
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
1a18c85c
MS
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
1a18c85c 15 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 16 */
17
18/*
19 * Include necessary headers...
20 */
21
71e16022 22#include "cups-private.h"
ef416fc2 23#ifdef HAVE_LANGINFO_H
24# include <langinfo.h>
25#endif /* HAVE_LANGINFO_H */
26#ifdef WIN32
27# include <io.h>
28#else
29# include <unistd.h>
30#endif /* WIN32 */
fa73b229 31#ifdef HAVE_COREFOUNDATION_H
32# include <CoreFoundation/CoreFoundation.h>
33#endif /* HAVE_COREFOUNDATION_H */
ef416fc2 34
35
d6ae789d 36/*
37 * Local globals...
38 */
39
6d2f911b 40static _cups_mutex_t lang_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 41 /* Mutex to control access to cache */
d6ae789d 42static cups_lang_t *lang_cache = NULL;
43 /* Language string cache */
ef416fc2 44static const char * const lang_encodings[] =
45 { /* Encoding strings */
46 "us-ascii", "iso-8859-1",
47 "iso-8859-2", "iso-8859-3",
48 "iso-8859-4", "iso-8859-5",
49 "iso-8859-6", "iso-8859-7",
50 "iso-8859-8", "iso-8859-9",
51 "iso-8859-10", "utf-8",
52 "iso-8859-13", "iso-8859-14",
cc754834
MS
53 "iso-8859-15", "cp874",
54 "cp1250", "cp1251",
55 "cp1252", "cp1253",
56 "cp1254", "cp1255",
57 "cp1256", "cp1257",
58 "cp1258", "koi8-r",
ef416fc2 59 "koi8-u", "iso-8859-11",
a2326b5b 60 "iso-8859-16", "mac",
ef416fc2 61 "unknown", "unknown",
62 "unknown", "unknown",
63 "unknown", "unknown",
64 "unknown", "unknown",
65 "unknown", "unknown",
66 "unknown", "unknown",
67 "unknown", "unknown",
68 "unknown", "unknown",
69 "unknown", "unknown",
70 "unknown", "unknown",
71 "unknown", "unknown",
72 "unknown", "unknown",
73 "unknown", "unknown",
74 "unknown", "unknown",
75 "unknown", "unknown",
76 "unknown", "unknown",
77 "unknown", "unknown",
cc754834
MS
78 "cp932", "cp936",
79 "cp949", "cp950",
80 "cp1361", "unknown",
ef416fc2 81 "unknown", "unknown",
82 "unknown", "unknown",
83 "unknown", "unknown",
84 "unknown", "unknown",
85 "unknown", "unknown",
86 "unknown", "unknown",
87 "unknown", "unknown",
88 "unknown", "unknown",
89 "unknown", "unknown",
90 "unknown", "unknown",
91 "unknown", "unknown",
92 "unknown", "unknown",
93 "unknown", "unknown",
94 "unknown", "unknown",
95 "unknown", "unknown",
96 "unknown", "unknown",
97 "unknown", "unknown",
98 "unknown", "unknown",
99 "unknown", "unknown",
100 "unknown", "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 "euc-cn", "euc-jp",
4b3f67ff 111 "euc-kr", "euc-tw",
f9d9f126 112 "shift_jisx0213"
ef416fc2 113 };
114
0a682745
MS
115#ifdef __APPLE__
116typedef struct
117{
118 const char * const language; /* Language ID */
119 const char * const locale; /* Locale ID */
120} _apple_language_locale_t;
121
122static const _apple_language_locale_t apple_language_locale[] =
123{ /* Locale to language ID LUT */
0837b7e8
MS
124 { "en", "en_US" },
125 { "nb", "no" },
126 { "zh-Hans", "zh_CN" },
127 { "zh-Hant", "zh_TW" }
0a682745
MS
128};
129#endif /* __APPLE__ */
130
131
132/*
133 * Local functions...
134 */
135
82f97232 136
0a682745
MS
137#ifdef __APPLE__
138static const char *appleLangDefault(void);
0837b7e8 139# ifdef CUPS_BUNDLEDIR
82f97232
MS
140# ifndef CF_RETURNS_RETAINED
141# if __has_feature(attribute_cf_returns_retained)
142# define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
143# else
144# define CF_RETURNS_RETAINED
145# endif /* __has_feature(attribute_cf_returns_retained) */
146# endif /* !CF_RETURNED_RETAINED */
147static cups_array_t *appleMessageLoad(const char *locale)
148 CF_RETURNS_RETAINED;
0837b7e8 149# endif /* CUPS_BUNDLEDIR */
0a682745
MS
150#endif /* __APPLE__ */
151static cups_lang_t *cups_cache_lookup(const char *name,
152 cups_encoding_t encoding);
153static int cups_message_compare(_cups_message_t *m1,
154 _cups_message_t *m2);
0837b7e8 155static void cups_message_free(_cups_message_t *m);
9c80ffa2 156static void cups_message_load(cups_lang_t *lang);
0a682745
MS
157static void cups_unquote(char *d, const char *s);
158
159
160#ifdef __APPLE__
161/*
0837b7e8
MS
162 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
163 * locale ID.
0a682745
MS
164 */
165
166const char * /* O - Language ID */
167_cupsAppleLanguage(const char *locale, /* I - Locale ID */
168 char *language,/* I - Language ID buffer */
169 size_t langsize) /* I - Size of language ID buffer */
170{
171 int i; /* Looping var */
172 CFStringRef localeid, /* CF locale identifier */
173 langid; /* CF language identifier */
174
175
176 /*
177 * Copy the locale name and convert, as needed, to the Apple-specific
178 * locale identifier...
179 */
180
181 switch (strlen(locale))
182 {
183 default :
184 /*
185 * Invalid locale...
186 */
187
188 strlcpy(language, "en", langsize);
189 break;
190
191 case 2 :
192 strlcpy(language, locale, langsize);
193 break;
194
195 case 5 :
196 strlcpy(language, locale, langsize);
197
198 if (language[2] == '-')
199 {
200 /*
201 * Convert ll-cc to ll_CC...
202 */
203
204 language[2] = '_';
1a18c85c
MS
205 language[3] = (char)toupper(language[3] & 255);
206 language[4] = (char)toupper(language[4] & 255);
0a682745
MS
207 }
208 break;
209 }
210
211 for (i = 0;
212 i < (int)(sizeof(apple_language_locale) /
213 sizeof(apple_language_locale[0]));
214 i ++)
215 if (!strcmp(locale, apple_language_locale[i].locale))
216 {
217 strlcpy(language, apple_language_locale[i].language, sizeof(language));
218 break;
219 }
220
221 /*
222 * Attempt to map the locale ID to a language ID...
223 */
224
225 if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
226 kCFStringEncodingASCII)) != NULL)
227 {
228 if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
229 kCFAllocatorDefault, localeid)) != NULL)
230 {
1a18c85c 231 CFStringGetCString(langid, language, (CFIndex)langsize, kCFStringEncodingASCII);
0a682745
MS
232 CFRelease(langid);
233 }
234
235 CFRelease(localeid);
236 }
237
238 /*
239 * Return what we got...
240 */
241
242 return (language);
243}
244#endif /* __APPLE__ */
245
ef416fc2 246
247/*
248 * '_cupsEncodingName()' - Return the character encoding name string
249 * for the given encoding enumeration.
250 */
251
252const char * /* O - Character encoding */
253_cupsEncodingName(
254 cups_encoding_t encoding) /* I - Encoding value */
255{
1a18c85c
MS
256 if (encoding < CUPS_US_ASCII ||
257 encoding >= (cups_encoding_t)(sizeof(lang_encodings) / sizeof(lang_encodings[0])))
c7017ecc
MS
258 {
259 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
260 encoding, lang_encodings[0]));
ef416fc2 261 return (lang_encodings[0]);
c7017ecc 262 }
ef416fc2 263 else
c7017ecc
MS
264 {
265 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
266 encoding, lang_encodings[encoding]));
ef416fc2 267 return (lang_encodings[encoding]);
c7017ecc 268 }
ef416fc2 269}
270
271
272/*
273 * 'cupsLangDefault()' - Return the default language.
274 */
275
276cups_lang_t * /* O - Language data */
277cupsLangDefault(void)
278{
279 return (cupsLangGet(NULL));
280}
281
282
283/*
284 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
285 * for the given language.
286 */
287
288const char * /* O - Character encoding */
289cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
290{
291 if (lang == NULL)
292 return ((char*)lang_encodings[0]);
293 else
294 return ((char*)lang_encodings[lang->encoding]);
295}
296
297
298/*
299 * 'cupsLangFlush()' - Flush all language data out of the cache.
300 */
301
302void
303cupsLangFlush(void)
304{
305 cups_lang_t *lang, /* Current language */
306 *next; /* Next language */
ef416fc2 307
308
309 /*
310 * Free all languages in the cache...
311 */
312
6d2f911b 313 _cupsMutexLock(&lang_mutex);
d6ae789d 314
315 for (lang = lang_cache; lang != NULL; lang = next)
ef416fc2 316 {
317 /*
318 * Free all messages...
319 */
320
321 _cupsMessageFree(lang->strings);
322
323 /*
324 * Then free the language structure itself...
325 */
326
327 next = lang->next;
328 free(lang);
329 }
330
d6ae789d 331 lang_cache = NULL;
332
6d2f911b 333 _cupsMutexUnlock(&lang_mutex);
ef416fc2 334}
335
336
337/*
338 * 'cupsLangFree()' - Free language data.
339 *
5a738aea 340 * This does not actually free anything; use @link cupsLangFlush@ for that.
ef416fc2 341 */
342
343void
344cupsLangFree(cups_lang_t *lang) /* I - Language to free */
345{
6d2f911b 346 _cupsMutexLock(&lang_mutex);
d6ae789d 347
ef416fc2 348 if (lang != NULL && lang->used > 0)
349 lang->used --;
d6ae789d 350
6d2f911b 351 _cupsMutexUnlock(&lang_mutex);
ef416fc2 352}
353
354
355/*
356 * 'cupsLangGet()' - Get a language.
357 */
358
359cups_lang_t * /* O - Language data */
360cupsLangGet(const char *language) /* I - Language or locale */
361{
362 int i; /* Looping var */
480ef0fe 363#ifndef __APPLE__
364 char locale[255]; /* Copy of locale name */
365#endif /* !__APPLE__ */
366 char langname[16], /* Requested language name */
ef416fc2 367 country[16], /* Country code */
368 charset[16], /* Character set */
ef416fc2 369 *csptr, /* Pointer to CODESET string */
ef416fc2 370 *ptr, /* Pointer into language/charset */
0837b7e8 371 real[48]; /* Real language name */
ef416fc2 372 cups_encoding_t encoding; /* Encoding to use */
373 cups_lang_t *lang; /* Current language... */
ef416fc2 374 static const char * const locale_encodings[] =
375 { /* Locale charset names */
376 "ASCII", "ISO88591", "ISO88592", "ISO88593",
377 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
378 "ISO88598", "ISO88599", "ISO885910", "UTF8",
379 "ISO885913", "ISO885914", "ISO885915", "CP874",
380 "CP1250", "CP1251", "CP1252", "CP1253",
381 "CP1254", "CP1255", "CP1256", "CP1257",
382 "CP1258", "KOI8R", "KOI8U", "ISO885911",
bd7854cb 383 "ISO885916", "MACROMAN", "", "",
ef416fc2 384
385 "", "", "", "",
386 "", "", "", "",
387 "", "", "", "",
388 "", "", "", "",
389 "", "", "", "",
390 "", "", "", "",
391 "", "", "", "",
392 "", "", "", "",
393
394 "CP932", "CP936", "CP949", "CP950",
395 "CP1361", "", "", "",
396 "", "", "", "",
397 "", "", "", "",
398 "", "", "", "",
399 "", "", "", "",
400 "", "", "", "",
401 "", "", "", "",
402
403 "", "", "", "",
404 "", "", "", "",
405 "", "", "", "",
406 "", "", "", "",
407 "", "", "", "",
408 "", "", "", "",
409 "", "", "", "",
410 "", "", "", "",
411
4b3f67ff
MS
412 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
413 "SHIFT_JISX0213"
ef416fc2 414 };
415
416
f11a948a 417 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
ef416fc2 418
419#ifdef __APPLE__
757d2cad 420 /*
421 * Set the character set to UTF-8...
422 */
423
5a9febac 424 strlcpy(charset, "UTF8", sizeof(charset));
757d2cad 425
ef416fc2 426 /*
c8fef167 427 * Apple's setlocale doesn't give us the user's localization
ef416fc2 428 * preference so we have to look it up this way...
429 */
430
c9fc04c6
MS
431 if (!language)
432 {
b0f6947b 433 if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
c9fc04c6
MS
434 language = appleLangDefault();
435
f11a948a 436 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
c9fc04c6 437 }
757d2cad 438
ef416fc2 439#else
757d2cad 440 /*
441 * Set the charset to "unknown"...
442 */
443
444 charset[0] = '\0';
445
446 /*
447 * Use setlocale() to determine the currently set locale, and then
448 * fallback to environment variables to avoid setting the locale,
449 * since setlocale() is not thread-safe!
450 */
451
452 if (!language)
ef416fc2 453 {
454 /*
455 * First see if the locale has been set; if it is still "C" or
757d2cad 456 * "POSIX", use the environment to get the default...
ef416fc2 457 */
458
459# ifdef LC_MESSAGES
460 ptr = setlocale(LC_MESSAGES, NULL);
461# else
462 ptr = setlocale(LC_ALL, NULL);
463# endif /* LC_MESSAGES */
464
f11a948a 465 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
ef416fc2 466
467 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
ef416fc2 468 {
757d2cad 469 /*
470 * Get the character set from the LC_CTYPE locale setting...
471 */
472
473 if ((ptr = getenv("LC_CTYPE")) == NULL)
474 if ((ptr = getenv("LC_ALL")) == NULL)
475 if ((ptr = getenv("LANG")) == NULL)
476 ptr = "en_US";
477
478 if ((csptr = strchr(ptr, '.')) != NULL)
479 {
480 /*
481 * Extract the character set from the environment...
482 */
483
484 for (ptr = charset, csptr ++; *csptr; csptr ++)
7cf5915e 485 if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr))
757d2cad 486 *ptr++ = *csptr;
487
488 *ptr = '\0';
489 }
757d2cad 490
491 /*
492 * Get the locale for messages from the LC_MESSAGES locale setting...
493 */
494
495 if ((ptr = getenv("LC_MESSAGES")) == NULL)
496 if ((ptr = getenv("LC_ALL")) == NULL)
497 if ((ptr = getenv("LANG")) == NULL)
498 ptr = "en_US";
ef416fc2 499 }
ef416fc2 500
501 if (ptr)
502 {
503 strlcpy(locale, ptr, sizeof(locale));
504 language = locale;
505
a41f09e2
MS
506 /*
507 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
508 */
509
510 if (!strncmp(locale, "nb", 2))
511 locale[1] = 'o';
512
f11a948a 513 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
ef416fc2 514 }
515 }
516#endif /* __APPLE__ */
517
518 /*
519 * If "language" is NULL at this point, then chances are we are using
520 * a language that is not installed for the base OS.
521 */
522
523 if (!language)
524 {
525 /*
757d2cad 526 * Switch to the POSIX ("C") locale...
ef416fc2 527 */
528
757d2cad 529 language = "C";
ef416fc2 530 }
531
ef416fc2 532#ifdef CODESET
533 /*
534 * On systems that support the nl_langinfo(CODESET) call, use
535 * this value as the character set...
536 */
537
757d2cad 538 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
ef416fc2 539 {
540 /*
541 * Copy all of the letters and numbers in the CODESET string...
542 */
543
544 for (ptr = charset; *csptr; csptr ++)
7cf5915e 545 if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1))
ef416fc2 546 *ptr++ = *csptr;
547
548 *ptr = '\0';
549
f11a948a 550 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
e07d4801 551 "nl_langinfo(CODESET)...", charset));
ef416fc2 552 }
553#endif /* CODESET */
554
07725fee 555 /*
556 * If we don't have a character set by now, default to UTF-8...
557 */
558
559 if (!charset[0])
5a9febac 560 strlcpy(charset, "UTF8", sizeof(charset));
07725fee 561
ef416fc2 562 /*
563 * Parse the language string passed in to a locale string. "C" is the
564 * standard POSIX locale and is copied unchanged. Otherwise the
565 * language string is converted from ll-cc[.charset] (language-country)
566 * to ll_CC[.CHARSET] to match the file naming convention used by all
567 * POSIX-compliant operating systems. Invalid language names are mapped
568 * to the POSIX locale.
569 */
570
571 country[0] = '\0';
572
573 if (language == NULL || !language[0] ||
574 !strcmp(language, "POSIX"))
5a9febac 575 strlcpy(langname, "C", sizeof(langname));
ef416fc2 576 else
577 {
578 /*
579 * Copy the parts of the locale string over safely...
580 */
581
582 for (ptr = langname; *language; language ++)
583 if (*language == '_' || *language == '-' || *language == '.')
584 break;
585 else if (ptr < (langname + sizeof(langname) - 1))
1a18c85c 586 *ptr++ = (char)tolower(*language & 255);
ef416fc2 587
588 *ptr = '\0';
589
590 if (*language == '_' || *language == '-')
591 {
592 /*
593 * Copy the country code...
594 */
595
596 for (language ++, ptr = country; *language; language ++)
597 if (*language == '.')
598 break;
599 else if (ptr < (country + sizeof(country) - 1))
1a18c85c 600 *ptr++ = (char)toupper(*language & 255);
ef416fc2 601
602 *ptr = '\0';
603 }
604
605 if (*language == '.' && !charset[0])
606 {
607 /*
608 * Copy the encoding...
609 */
610
611 for (language ++, ptr = charset; *language; language ++)
7cf5915e 612 if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
1a18c85c 613 *ptr++ = (char)toupper(*language & 255);
ef416fc2 614
615 *ptr = '\0';
616 }
617
618 /*
619 * Force a POSIX locale for an invalid language name...
620 */
621
622 if (strlen(langname) != 2)
623 {
5a9febac 624 strlcpy(langname, "C", sizeof(langname));
ef416fc2 625 country[0] = '\0';
626 charset[0] = '\0';
627 }
628 }
629
f11a948a 630 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
ef416fc2 631 langname, country, charset));
632
633 /*
634 * Figure out the desired encoding...
635 */
636
637 encoding = CUPS_AUTO_ENCODING;
638
639 if (charset[0])
640 {
bd7854cb 641 for (i = 0;
642 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
643 i ++)
88f9aafc 644 if (!_cups_strcasecmp(charset, locale_encodings[i]))
ef416fc2 645 {
646 encoding = (cups_encoding_t)i;
647 break;
648 }
8ca02f3c 649
650 if (encoding == CUPS_AUTO_ENCODING)
651 {
652 /*
653 * Map alternate names for various character sets...
654 */
655
88f9aafc
MS
656 if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
657 !_cups_strcasecmp(charset, "sjis"))
8ca02f3c 658 encoding = CUPS_WINDOWS_932;
88f9aafc 659 else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
8ca02f3c 660 encoding = CUPS_WINDOWS_936;
88f9aafc 661 else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
8ca02f3c 662 encoding = CUPS_WINDOWS_949;
88f9aafc 663 else if (!_cups_strcasecmp(charset, "big5"))
8ca02f3c 664 encoding = CUPS_WINDOWS_950;
665 }
ef416fc2 666 }
667
f11a948a 668 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
ef416fc2 669 encoding == CUPS_AUTO_ENCODING ? "auto" :
670 lang_encodings[encoding]));
671
672 /*
673 * See if we already have this language/country loaded...
674 */
675
bd7854cb 676 if (country[0])
bd7854cb 677 snprintf(real, sizeof(real), "%s_%s", langname, country);
bd7854cb 678 else
5a9febac 679 strlcpy(real, langname, sizeof(real));
d6ae789d 680
6d2f911b 681 _cupsMutexLock(&lang_mutex);
d6ae789d 682
c0e1af83 683 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
d6ae789d 684 {
6d2f911b 685 _cupsMutexUnlock(&lang_mutex);
d6ae789d 686
f11a948a 687 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
c9fc04c6 688
d6ae789d 689 return (lang);
690 }
ef416fc2 691
ef416fc2 692 /*
693 * See if there is a free language available; if so, use that
694 * record...
695 */
696
d6ae789d 697 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 698 if (lang->used == 0)
699 break;
700
701 if (lang == NULL)
702 {
703 /*
704 * Allocate memory for the language and add it to the cache.
705 */
706
707 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
d6ae789d 708 {
6d2f911b 709 _cupsMutexUnlock(&lang_mutex);
d6ae789d 710
ef416fc2 711 return (NULL);
d6ae789d 712 }
ef416fc2 713
d6ae789d 714 lang->next = lang_cache;
715 lang_cache = lang;
ef416fc2 716 }
bd7854cb 717 else
718 {
719 /*
720 * Free all old strings as needed...
721 */
ef416fc2 722
bd7854cb 723 _cupsMessageFree(lang->strings);
9c80ffa2 724 lang->strings = NULL;
bd7854cb 725 }
ef416fc2 726
727 /*
728 * Then assign the language and encoding fields...
729 */
730
731 lang->used ++;
732 strlcpy(lang->language, real, sizeof(lang->language));
733
734 if (encoding != CUPS_AUTO_ENCODING)
735 lang->encoding = encoding;
736 else
737 lang->encoding = CUPS_UTF8;
738
ef416fc2 739 /*
740 * Return...
741 */
742
6d2f911b 743 _cupsMutexUnlock(&lang_mutex);
d6ae789d 744
ef416fc2 745 return (lang);
746}
747
748
749/*
750 * '_cupsLangString()' - Get a message string.
751 *
752 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
753 * convert the string to the language encoding.
754 */
755
756const char * /* O - Localized message */
757_cupsLangString(cups_lang_t *lang, /* I - Language */
758 const char *message) /* I - Message */
759{
6d2f911b
MS
760 const char *s; /* Localized message */
761
ef416fc2 762 /*
763 * Range check input...
764 */
765
37e7e6e0 766 if (!lang || !message || !*message)
ef416fc2 767 return (message);
768
6d2f911b 769 _cupsMutexLock(&lang_mutex);
d6ae789d 770
9c80ffa2
MS
771 /*
772 * Load the message catalog if needed...
773 */
774
775 if (!lang->strings)
776 cups_message_load(lang);
777
6d2f911b 778 s = _cupsMessageLookup(lang->strings, message);
d6ae789d 779
6d2f911b 780 _cupsMutexUnlock(&lang_mutex);
d6ae789d 781
6d2f911b 782 return (s);
ef416fc2 783}
784
785
786/*
787 * '_cupsMessageFree()' - Free a messages array.
788 */
789
790void
791_cupsMessageFree(cups_array_t *a) /* I - Message array */
792{
0837b7e8
MS
793#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
794 /*
795 * Release the cups.strings dictionary as needed...
796 */
ef416fc2 797
0837b7e8
MS
798 if (cupsArrayUserData(a))
799 CFRelease((CFDictionaryRef)cupsArrayUserData(a));
800#endif /* __APPLE__ && CUPS_BUNDLEDIR */
ef416fc2 801
802 /*
803 * Free the array...
804 */
805
806 cupsArrayDelete(a);
807}
808
809
810/*
811 * '_cupsMessageLoad()' - Load a .po file into a messages array.
812 */
813
814cups_array_t * /* O - New message array */
8b116e60 815_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
0837b7e8 816 int unquote) /* I - Unescape \foo in strings? */
ef416fc2 817{
818 cups_file_t *fp; /* Message file */
819 cups_array_t *a; /* Message array */
820 _cups_message_t *m; /* Current message */
821 char s[4096], /* String buffer */
822 *ptr, /* Pointer into buffer */
823 *temp; /* New string */
1a18c85c
MS
824 size_t length, /* Length of combined strings */
825 ptrlen; /* Length of string */
ef416fc2 826
827
e07d4801 828 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
bd7854cb 829
ef416fc2 830 /*
831 * Create an array to hold the messages...
832 */
833
a29fd7dd 834 if ((a = _cupsMessageNew(NULL)) == NULL)
c9fc04c6 835 {
e07d4801 836 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
ef416fc2 837 return (NULL);
c9fc04c6 838 }
ef416fc2 839
840 /*
841 * Open the message catalog file...
842 */
843
844 if ((fp = cupsFileOpen(filename, "r")) == NULL)
c9fc04c6 845 {
e07d4801 846 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
c9fc04c6 847 strerror(errno)));
ef416fc2 848 return (a);
c9fc04c6 849 }
ef416fc2 850
851 /*
852 * Read messages from the catalog file until EOF...
853 *
854 * The format is the GNU gettext .po format, which is fairly simple:
855 *
856 * msgid "some text"
857 * msgstr "localized text"
858 *
bd7854cb 859 * The ID and localized text can span multiple lines using the form:
ef416fc2 860 *
bd7854cb 861 * msgid ""
862 * "some long text"
863 * msgstr ""
864 * "localized text spanning "
ef416fc2 865 * "multiple lines"
866 */
867
868 m = NULL;
869
870 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
871 {
872 /*
873 * Skip blank and comment lines...
874 */
875
876 if (s[0] == '#' || !s[0])
877 continue;
878
879 /*
880 * Strip the trailing quote...
881 */
882
883 if ((ptr = strrchr(s, '\"')) == NULL)
884 continue;
885
886 *ptr = '\0';
887
888 /*
889 * Find start of value...
890 */
c8fef167 891
ef416fc2 892 if ((ptr = strchr(s, '\"')) == NULL)
893 continue;
894
895 ptr ++;
896
897 /*
898 * Unquote the text...
899 */
900
8b116e60
MS
901 if (unquote)
902 cups_unquote(ptr, ptr);
ef416fc2 903
904 /*
905 * Create or add to a message...
906 */
907
908 if (!strncmp(s, "msgid", 5))
909 {
bd7854cb 910 /*
911 * Add previous message as needed...
912 */
913
914 if (m)
82cc1f9a
MS
915 {
916 if (m->str && m->str[0])
917 {
918 cupsArrayAdd(a, m);
919 }
920 else
921 {
922 /*
923 * Translation is empty, don't add it... (STR #4033)
924 */
925
926 free(m->id);
927 if (m->str)
928 free(m->str);
929 free(m);
930 }
931 }
bd7854cb 932
933 /*
934 * Create a new message with the given msgid string...
935 */
936
ef416fc2 937 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
938 {
939 cupsFileClose(fp);
940 return (a);
941 }
942
91c84a35
MS
943 if ((m->id = strdup(ptr)) == NULL)
944 {
945 free(m);
946 cupsFileClose(fp);
947 return (a);
948 }
ef416fc2 949 }
bd7854cb 950 else if (s[0] == '\"' && m)
ef416fc2 951 {
bd7854cb 952 /*
953 * Append to current string...
954 */
ef416fc2 955
1a18c85c 956 length = strlen(m->str ? m->str : m->id);
5a9febac 957 ptrlen = strlen(ptr);
ef416fc2 958
1a18c85c 959 if ((temp = realloc(m->str ? m->str : m->id, length + ptrlen + 1)) == NULL)
bd7854cb 960 {
82cc1f9a
MS
961 if (m->str)
962 free(m->str);
963 free(m->id);
964 free(m);
965
bd7854cb 966 cupsFileClose(fp);
967 return (a);
968 }
ef416fc2 969
bd7854cb 970 if (m->str)
971 {
ef416fc2 972 /*
bd7854cb 973 * Copy the new portion to the end of the msgstr string - safe
5a9febac 974 * to use memcpy because the buffer is allocated to the correct
bd7854cb 975 * size...
ef416fc2 976 */
977
bd7854cb 978 m->str = temp;
979
5a9febac 980 memcpy(m->str + length, ptr, ptrlen + 1);
ef416fc2 981 }
982 else
983 {
984 /*
bd7854cb 985 * Copy the new portion to the end of the msgid string - safe
5a9febac 986 * to use memcpy because the buffer is allocated to the correct
bd7854cb 987 * size...
ef416fc2 988 */
989
bd7854cb 990 m->id = temp;
991
5a9febac 992 memcpy(m->id + length, ptr, ptrlen + 1);
ef416fc2 993 }
994 }
bd7854cb 995 else if (!strncmp(s, "msgstr", 6) && m)
996 {
997 /*
998 * Set the string...
999 */
1000
91c84a35
MS
1001 if ((m->str = strdup(ptr)) == NULL)
1002 {
82cc1f9a
MS
1003 free(m->id);
1004 free(m);
1005
91c84a35
MS
1006 cupsFileClose(fp);
1007 return (a);
1008 }
bd7854cb 1009 }
ef416fc2 1010 }
1011
bd7854cb 1012 /*
1013 * Add the last message string to the array as needed...
1014 */
1015
1016 if (m)
82cc1f9a
MS
1017 {
1018 if (m->str && m->str[0])
1019 {
1020 cupsArrayAdd(a, m);
1021 }
1022 else
1023 {
1024 /*
1025 * Translation is empty, don't add it... (STR #4033)
1026 */
1027
1028 free(m->id);
1029 if (m->str)
1030 free(m->str);
1031 free(m);
1032 }
1033 }
bd7854cb 1034
ef416fc2 1035 /*
1036 * Close the message catalog file and return the new array...
1037 */
1038
1039 cupsFileClose(fp);
1040
e07d4801 1041 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
c9fc04c6
MS
1042 cupsArrayCount(a)));
1043
ef416fc2 1044 return (a);
1045}
1046
1047
1048/*
1049 * '_cupsMessageLookup()' - Lookup a message string.
1050 */
1051
1052const char * /* O - Localized message */
1053_cupsMessageLookup(cups_array_t *a, /* I - Message array */
1054 const char *m) /* I - Message */
1055{
1056 _cups_message_t key, /* Search key */
1057 *match; /* Matching message */
1058
1059
1060 /*
1061 * Lookup the message string; if it doesn't exist in the catalog,
1062 * then return the message that was passed to us...
1063 */
1064
1065 key.id = (char *)m;
1066 match = (_cups_message_t *)cupsArrayFind(a, &key);
1067
0837b7e8
MS
1068#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1069 if (!match && cupsArrayUserData(a))
1070 {
1071 /*
1072 * Try looking the string up in the cups.strings dictionary...
1073 */
1074
1075 CFDictionaryRef dict; /* cups.strings dictionary */
1076 CFStringRef cfm, /* Message as a CF string */
1077 cfstr; /* Localized text as a CF string */
1078
1079 dict = (CFDictionaryRef)cupsArrayUserData(a);
1080 cfm = CFStringCreateWithCString(kCFAllocatorDefault, m,
1081 kCFStringEncodingUTF8);
1082 match = calloc(1, sizeof(_cups_message_t));
1083 match->id = strdup(m);
1084 cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
1085
1086 if (cfstr)
1087 {
1088 char buffer[1024]; /* Message buffer */
1089
1090 CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
1091 match->str = strdup(buffer);
c8fef167
MS
1092
1093 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1094 m, buffer));
0837b7e8
MS
1095 }
1096 else
c8fef167 1097 {
0837b7e8
MS
1098 match->str = strdup(m);
1099
c8fef167
MS
1100 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m));
1101 }
1102
0837b7e8 1103 cupsArrayAdd(a, match);
e60ec91f
MS
1104
1105 if (cfm)
1106 CFRelease(cfm);
0837b7e8
MS
1107 }
1108#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1109
ef416fc2 1110 if (match && match->str)
1111 return (match->str);
1112 else
1113 return (m);
1114}
1115
1116
a29fd7dd
MS
1117/*
1118 * '_cupsMessageNew()' - Make a new message catalog array.
1119 */
1120
1121cups_array_t * /* O - Array */
1122_cupsMessageNew(void *context) /* I - User data */
1123{
1124 return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
1125 (cups_ahash_func_t)NULL, 0,
1126 (cups_acopy_func_t)NULL,
1127 (cups_afree_func_t)cups_message_free));
1128}
1129
1130
ef416fc2 1131#ifdef __APPLE__
ef416fc2 1132/*
1133 * 'appleLangDefault()' - Get the default locale string.
1134 */
1135
1136static const char * /* O - Locale string */
1137appleLangDefault(void)
1138{
b94498cf 1139 int i; /* Looping var */
db1f069b
MS
1140 CFBundleRef bundle; /* Main bundle (if any) */
1141 CFArrayRef bundleList; /* List of localizations in bundle */
ef416fc2 1142 CFPropertyListRef localizationList;
1143 /* List of localization data */
1144 CFStringRef languageName; /* Current name */
1145 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1146 char *lang; /* LANG environment variable */
ef416fc2 1147 _cups_globals_t *cg = _cupsGlobals();
1148 /* Pointer to library globals */
1149
1150
b0f6947b
MS
1151 DEBUG_puts("2appleLangDefault()");
1152
ef416fc2 1153 /*
1154 * Only do the lookup and translation the first time.
1155 */
1156
1157 if (!cg->language[0])
1158 {
b0f6947b 1159 if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
db1f069b 1160 {
94436c5a 1161 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang));
26d47ec6 1162 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1163 return (cg->language);
1164 }
1165 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1166 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1167 {
94436c5a
MS
1168 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1169
26d47ec6 1170 localizationList =
db1f069b 1171 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
26d47ec6 1172
db1f069b
MS
1173 CFRelease(bundleList);
1174 }
1175 else
94436c5a
MS
1176 {
1177 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1178
db1f069b
MS
1179 localizationList =
1180 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1181 kCFPreferencesCurrentApplication);
94436c5a 1182 }
db1f069b
MS
1183
1184 if (localizationList)
1185 {
94436c5a
MS
1186
1187#ifdef DEBUG
1188 if (CFGetTypeID(localizationList) == CFArrayGetTypeID())
1189 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1190 (int)CFArrayGetCount(localizationList)));
1191 else
1192 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1193#endif /* DEBUG */
1194
db1f069b
MS
1195 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1196 CFArrayGetCount(localizationList) > 0)
ef416fc2 1197 {
db1f069b
MS
1198 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1199
1200 if (languageName &&
1201 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1202 {
db1f069b
MS
1203 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1204 kCFAllocatorDefault, languageName);
ef416fc2 1205
db1f069b
MS
1206 if (localeName)
1207 {
1208 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1209 kCFStringEncodingASCII);
1210 CFRelease(localeName);
ef416fc2 1211
94436c5a 1212 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
db1f069b 1213 cg->language));
26d47ec6 1214
db1f069b
MS
1215 /*
1216 * Map new language identifiers to locales...
1217 */
b94498cf 1218
db1f069b 1219 for (i = 0;
0a682745
MS
1220 i < (int)(sizeof(apple_language_locale) /
1221 sizeof(apple_language_locale[0]));
1222 i ++)
db1f069b 1223 {
0a682745 1224 if (!strcmp(cg->language, apple_language_locale[i].language))
b94498cf 1225 {
94436c5a 1226 DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
0a682745 1227 cg->language, apple_language_locale[i].locale));
c8fef167 1228 strlcpy(cg->language, apple_language_locale[i].locale,
db1f069b
MS
1229 sizeof(cg->language));
1230 break;
b94498cf 1231 }
db1f069b 1232 }
b94498cf 1233
db1f069b
MS
1234 /*
1235 * Convert language subtag into region subtag...
1236 */
b94498cf 1237
db1f069b
MS
1238 if (cg->language[2] == '-')
1239 cg->language[2] = '_';
b94498cf 1240
db1f069b
MS
1241 if (!strchr(cg->language, '.'))
1242 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1243 }
94436c5a
MS
1244 else
1245 DEBUG_puts("3appleLangDefault: Unable to get localeName.");
26d47ec6 1246 }
26d47ec6 1247 }
db1f069b
MS
1248
1249 CFRelease(localizationList);
ef416fc2 1250 }
c8fef167 1251
ef416fc2 1252 /*
1253 * If we didn't find the language, default to en_US...
1254 */
1255
1256 if (!cg->language[0])
94436c5a
MS
1257 {
1258 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
ef416fc2 1259 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
94436c5a 1260 }
ef416fc2 1261 }
1262
1263 /*
1264 * Return the cached locale...
1265 */
1266
1267 return (cg->language);
1268}
0837b7e8
MS
1269
1270
1271# ifdef CUPS_BUNDLEDIR
1272/*
1273 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1274 */
1275
1276static cups_array_t * /* O - Message catalog */
1277appleMessageLoad(const char *locale) /* I - Locale ID */
1278{
1279 char filename[1024], /* Path to cups.strings file */
1280 applelang[256]; /* Apple language ID */
1281 CFURLRef url; /* URL to cups.strings file */
c8fef167
MS
1282 CFReadStreamRef stream = NULL; /* File stream */
1283 CFPropertyListRef plist = NULL; /* Localization file */
1284#ifdef DEBUG
1285 CFErrorRef error = NULL; /* Error when opening file */
1286#endif /* DEBUG */
1287
1288
1289 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
0837b7e8
MS
1290
1291 /*
1292 * Load the cups.strings file...
1293 */
1294
1295 snprintf(filename, sizeof(filename),
1296 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
1297 _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
c8fef167
MS
1298 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
1299
1300 if (access(filename, 0))
1301 {
1302 /*
1303 * Try alternate lproj directory names...
1304 */
1305
1306 if (!strncmp(locale, "en", 2))
1307 locale = "English";
1308 else if (!strncmp(locale, "nb", 2) || !strncmp(locale, "nl", 2))
1309 locale = "Dutch";
1310 else if (!strncmp(locale, "fr", 2))
1311 locale = "French";
1312 else if (!strncmp(locale, "de", 2))
1313 locale = "German";
1314 else if (!strncmp(locale, "it", 2))
1315 locale = "Italian";
1316 else if (!strncmp(locale, "ja", 2))
1317 locale = "Japanese";
1318 else if (!strncmp(locale, "es", 2))
1319 locale = "Spanish";
1320
1321 snprintf(filename, sizeof(filename),
1322 CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
1323 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
1324 }
1325
1326 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1327 (UInt8 *)filename,
1a18c85c 1328 (CFIndex)strlen(filename), false);
c8fef167
MS
1329 if (url)
1330 {
1331 stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
1332 if (stream)
1333 {
82f97232
MS
1334 /*
1335 * Read the property list containing the localization data.
1336 *
1337 * NOTE: This code currently generates a clang "potential leak"
1338 * warning, but the object is released in _cupsMessageFree().
1339 */
1340
c8fef167
MS
1341 CFReadStreamOpen(stream);
1342
1343#ifdef DEBUG
1344 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1345 kCFPropertyListImmutable, NULL,
1346 &error);
1347 if (error)
1348 {
1349 CFStringRef msg = CFErrorCopyDescription(error);
1350 /* Error message */
1351
1352 CFStringGetCString(msg, filename, sizeof(filename),
1353 kCFStringEncodingUTF8);
1354 DEBUG_printf(("1appleMessageLoad: %s", filename));
1355
82f97232 1356 CFRelease(msg);
c8fef167
MS
1357 CFRelease(error);
1358 }
1359
1360#else
1361 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
1362 kCFPropertyListImmutable, NULL,
1363 NULL);
1364#endif /* DEBUG */
1365
1366 if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
1367 {
1368 CFRelease(plist);
1369 plist = NULL;
1370 }
1371
1372 CFRelease(stream);
1373 }
1374
1375 CFRelease(url);
1376 }
0837b7e8 1377
c8fef167
MS
1378 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
1379 plist));
0837b7e8
MS
1380
1381 /*
1382 * Create and return an empty array to act as a cache for messages, passing the
c8fef167 1383 * plist as the user data.
0837b7e8
MS
1384 */
1385
a29fd7dd 1386 return (_cupsMessageNew((void *)plist));
0837b7e8
MS
1387}
1388# endif /* CUPS_BUNDLEDIR */
ef416fc2 1389#endif /* __APPLE__ */
1390
1391
1392/*
1393 * 'cups_cache_lookup()' - Lookup a language in the cache...
1394 */
1395
1396static cups_lang_t * /* O - Language data or NULL */
0837b7e8
MS
1397cups_cache_lookup(
1398 const char *name, /* I - Name of locale */
1399 cups_encoding_t encoding) /* I - Encoding of locale */
ef416fc2 1400{
1401 cups_lang_t *lang; /* Current language */
1402
1403
e07d4801 1404 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
ef416fc2 1405 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1406 lang_encodings[encoding]));
1407
1408 /*
1409 * Loop through the cache and return a match if found...
1410 */
1411
d6ae789d 1412 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1413 {
e07d4801
MS
1414 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1415 "encoding=%d(%s)", lang, lang->language, lang->encoding,
ef416fc2 1416 lang_encodings[lang->encoding]));
1417
1418 if (!strcmp(lang->language, name) &&
1419 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1420 {
1421 lang->used ++;
1422
e07d4801 1423 DEBUG_puts("8cups_cache_lookup: returning match!");
ef416fc2 1424
1425 return (lang);
1426 }
1427 }
1428
e07d4801 1429 DEBUG_puts("8cups_cache_lookup: returning NULL!");
ef416fc2 1430
1431 return (NULL);
1432}
1433
1434
1435/*
1436 * 'cups_message_compare()' - Compare two messages.
1437 */
1438
1439static int /* O - Result of comparison */
1440cups_message_compare(
1441 _cups_message_t *m1, /* I - First message */
1442 _cups_message_t *m2) /* I - Second message */
1443{
1444 return (strcmp(m1->id, m2->id));
1445}
1446
1447
0837b7e8
MS
1448/*
1449 * 'cups_message_free()' - Free a message.
1450 */
1451
1452static void
1453cups_message_free(_cups_message_t *m) /* I - Message */
1454{
1455 if (m->id)
1456 free(m->id);
1457
1458 if (m->str)
1459 free(m->str);
1460
1461 free(m);
1462}
1463
1464
9c80ffa2
MS
1465/*
1466 * 'cups_message_load()' - Load the message catalog for a language.
1467 */
1468
1469static void
1470cups_message_load(cups_lang_t *lang) /* I - Language */
1471{
1472#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1473 lang->strings = appleMessageLoad(lang->language);
1474
1475#else
1476 char filename[1024]; /* Filename for language locale file */
1477 _cups_globals_t *cg = _cupsGlobals();
1478 /* Pointer to library globals */
1479
1480
1481 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
1482 lang->language, lang->language);
1483
1484 if (strchr(lang->language, '_') && access(filename, 0))
1485 {
1486 /*
1487 * Country localization not available, look for generic localization...
1488 */
1489
1490 snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
1491 lang->language, lang->language);
1492
1493 if (access(filename, 0))
1494 {
1495 /*
1496 * No generic localization, so use POSIX...
1497 */
1498
1499 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
1500 strerror(errno)));
1501
1502 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
1503 }
1504 }
1505
1506 /*
1507 * Read the strings from the file...
1508 */
1509
1510 lang->strings = _cupsMessageLoad(filename, 1);
1511#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1512}
1513
1514
ef416fc2 1515/*
1516 * 'cups_unquote()' - Unquote characters in strings...
1517 */
1518
1519static void
1520cups_unquote(char *d, /* O - Unquoted string */
1521 const char *s) /* I - Original string */
1522{
1523 while (*s)
1524 {
1525 if (*s == '\\')
1526 {
1527 s ++;
1528 if (isdigit(*s))
1529 {
1530 *d = 0;
1531
1532 while (isdigit(*s))
1533 {
1534 *d = *d * 8 + *s - '0';
1535 s ++;
1536 }
d09495fa 1537
1538 d ++;
ef416fc2 1539 }
1540 else
1541 {
1542 if (*s == 'n')
1543 *d ++ = '\n';
1544 else if (*s == 'r')
1545 *d ++ = '\r';
1546 else if (*s == 't')
1547 *d ++ = '\t';
1548 else
1549 *d++ = *s;
1550
1551 s ++;
1552 }
1553 }
1554 else
1555 *d++ = *s++;
1556 }
1557
1558 *d = '\0';
1559}
1560
1561
1562/*
1a18c85c 1563 * End of "$Id: language.c 11558 2014-02-06 18:33:34Z msweet $".
ef416fc2 1564 */