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