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