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