Coverage Report

Created: 2025-12-31 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/language.c
Line
Count
Source
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 */
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 */
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 */
345
_cupsEncodingName(
346
    cups_encoding_t encoding)   /* I - Encoding value */
347
0
{
348
0
  if (encoding < CUPS_US_ASCII ||
349
0
      encoding >= (cups_encoding_t)(sizeof(lang_encodings) / sizeof(lang_encodings[0])))
350
0
  {
351
0
    DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
352
0
                  encoding, lang_encodings[0]));
353
0
    return (lang_encodings[0]);
354
0
  }
355
0
  else
356
0
  {
357
0
    DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
358
0
                  encoding, lang_encodings[encoding]));
359
0
    return (lang_encodings[encoding]);
360
0
  }
361
0
}
362
363
364
/*
365
 * 'cupsLangDefault()' - Return the default language.
366
 */
367
368
cups_lang_t *       /* O - Language data */
369
cupsLangDefault(void)
370
0
{
371
0
  return (cupsLangGet(NULL));
372
0
}
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 */
381
cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
382
0
{
383
0
  if (lang == NULL)
384
0
    return ((char*)lang_encodings[0]);
385
0
  else
386
0
    return ((char*)lang_encodings[lang->encoding]);
387
0
}
388
389
390
/*
391
 * 'cupsLangFlush()' - Flush all language data out of the cache.
392
 */
393
394
void
395
cupsLangFlush(void)
396
0
{
397
0
  cups_lang_t *lang,      /* Current language */
398
0
    *next;      /* Next language */
399
400
401
 /*
402
  * Free all languages in the cache...
403
  */
404
405
0
  _cupsMutexLock(&lang_mutex);
406
407
0
  for (lang = lang_cache; lang != NULL; lang = next)
408
0
  {
409
   /*
410
    * Free all messages...
411
    */
412
413
0
    _cupsMessageFree(lang->strings);
414
415
   /*
416
    * Then free the language structure itself...
417
    */
418
419
0
    next = lang->next;
420
0
    free(lang);
421
0
  }
422
423
0
  lang_cache = NULL;
424
425
0
  _cupsMutexUnlock(&lang_mutex);
426
0
}
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
436
cupsLangFree(cups_lang_t *lang)   /* I - Language to free */
437
0
{
438
0
  _cupsMutexLock(&lang_mutex);
439
440
0
  if (lang != NULL && lang->used > 0)
441
0
    lang->used --;
442
443
0
  _cupsMutexUnlock(&lang_mutex);
444
0
}
445
446
447
/*
448
 * 'cupsLangGet()' - Get a language.
449
 */
450
451
cups_lang_t *       /* O - Language data */
452
cupsLangGet(const char *language) /* I - Language or locale */
453
0
{
454
0
  int     i;    /* Looping var */
455
0
#ifndef __APPLE__
456
0
  char      locale[255];  /* Copy of locale name */
457
0
#endif /* !__APPLE__ */
458
0
  char      langname[16], /* Requested language name */
459
0
      country[16],  /* Country code */
460
0
      charset[16],  /* Character set */
461
0
      *csptr,   /* Pointer to CODESET string */
462
0
      *ptr,   /* Pointer into language/charset */
463
0
      real[48]; /* Real language name */
464
0
  cups_encoding_t encoding; /* Encoding to use */
465
0
  cups_lang_t   *lang;    /* Current language... */
466
0
  static const char * const locale_encodings[] =
467
0
    {     /* Locale charset names */
468
0
      "ASCII",  "ISO88591", "ISO88592", "ISO88593",
469
0
      "ISO88594", "ISO88595", "ISO88596", "ISO88597",
470
0
      "ISO88598", "ISO88599", "ISO885910",  "UTF8",
471
0
      "ISO885913",  "ISO885914",  "ISO885915",  "CP874",
472
0
      "CP1250", "CP1251", "CP1252", "CP1253",
473
0
      "CP1254", "CP1255", "CP1256", "CP1257",
474
0
      "CP1258", "KOI8R",  "KOI8U",  "ISO885911",
475
0
      "ISO885916",  "MACROMAN", "",   "",
476
477
0
      "",   "",   "",   "",
478
0
      "",   "",   "",   "",
479
0
      "",   "",   "",   "",
480
0
      "",   "",   "",   "",
481
0
      "",   "",   "",   "",
482
0
      "",   "",   "",   "",
483
0
      "",   "",   "",   "",
484
0
      "",   "",   "",   "",
485
486
0
      "CP932",  "CP936",  "CP949",  "CP950",
487
0
      "CP1361", "",   "",   "",
488
0
      "",   "",   "",   "",
489
0
      "",   "",   "",   "",
490
0
      "",   "",   "",   "",
491
0
      "",   "",   "",   "",
492
0
      "",   "",   "",   "",
493
0
      "",   "",   "",   "",
494
495
0
      "",   "",   "",   "",
496
0
      "",   "",   "",   "",
497
0
      "",   "",   "",   "",
498
0
      "",   "",   "",   "",
499
0
      "",   "",   "",   "",
500
0
      "",   "",   "",   "",
501
0
      "",   "",   "",   "",
502
0
      "",   "",   "",   "",
503
504
0
      "EUCCN",  "EUCJP",  "EUCKR",  "EUCTW",
505
0
      "SHIFT_JISX0213"
506
0
    };
507
508
509
0
  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
0
  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
0
  if (!language)
545
0
  {
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
0
#  ifdef LC_MESSAGES
552
0
    ptr = setlocale(LC_MESSAGES, NULL);
553
#  else
554
    ptr = setlocale(LC_ALL, NULL);
555
#  endif /* LC_MESSAGES */
556
557
0
    DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
558
559
0
    if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
560
0
    {
561
     /*
562
      * Get the character set from the LC_CTYPE locale setting...
563
      */
564
565
0
      if ((ptr = getenv("LC_CTYPE")) == NULL)
566
0
        if ((ptr = getenv("LC_ALL")) == NULL)
567
0
    if ((ptr = getenv("LANG")) == NULL)
568
0
      ptr = "en_US";
569
570
0
      if ((csptr = strchr(ptr, '.')) != NULL)
571
0
      {
572
       /*
573
        * Extract the character set from the environment...
574
  */
575
576
0
  for (ptr = charset, csptr ++; *csptr; csptr ++)
577
0
    if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr))
578
0
      *ptr++ = *csptr;
579
580
0
        *ptr = '\0';
581
0
      }
582
583
     /*
584
      * Get the locale for messages from the LC_MESSAGES locale setting...
585
      */
586
587
0
      if ((ptr = getenv("LC_MESSAGES")) == NULL)
588
0
        if ((ptr = getenv("LC_ALL")) == NULL)
589
0
    if ((ptr = getenv("LANG")) == NULL)
590
0
      ptr = "en_US";
591
0
    }
592
593
0
    if (ptr)
594
0
    {
595
0
      strlcpy(locale, ptr, sizeof(locale));
596
0
      language = locale;
597
598
     /*
599
      * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
600
      */
601
602
0
      if (!strncmp(locale, "nb", 2))
603
0
        locale[1] = 'o';
604
605
0
      DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
606
0
    }
607
0
  }
608
0
#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
0
  if (!language)
616
0
  {
617
   /*
618
    * Switch to the POSIX ("C") locale...
619
    */
620
621
0
    language = "C";
622
0
  }
623
624
0
#ifdef CODESET
625
 /*
626
  * On systems that support the nl_langinfo(CODESET) call, use
627
  * this value as the character set...
628
  */
629
630
0
  if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
631
0
  {
632
   /*
633
    * Copy all of the letters and numbers in the CODESET string...
634
    */
635
636
0
    for (ptr = charset; *csptr; csptr ++)
637
0
      if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1))
638
0
        *ptr++ = *csptr;
639
640
0
    *ptr = '\0';
641
642
0
    DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
643
0
                  "nl_langinfo(CODESET)...", charset));
644
0
  }
645
0
#endif /* CODESET */
646
647
 /*
648
  * If we don't have a character set by now, default to UTF-8...
649
  */
650
651
0
  if (!charset[0])
652
0
    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
0
  country[0] = '\0';
664
665
0
  if (language == NULL || !language[0] ||
666
0
      !strcmp(language, "POSIX"))
667
0
    strlcpy(langname, "C", sizeof(langname));
668
0
  else
669
0
  {
670
   /*
671
    * Copy the parts of the locale string over safely...
672
    */
673
674
0
    for (ptr = langname; *language; language ++)
675
0
      if (*language == '_' || *language == '-' || *language == '.')
676
0
  break;
677
0
      else if (ptr < (langname + sizeof(langname) - 1))
678
0
        *ptr++ = (char)tolower(*language & 255);
679
680
0
    *ptr = '\0';
681
682
0
    if (*language == '_' || *language == '-')
683
0
    {
684
     /*
685
      * Copy the country code...
686
      */
687
688
0
      for (language ++, ptr = country; *language; language ++)
689
0
  if (*language == '.')
690
0
    break;
691
0
  else if (ptr < (country + sizeof(country) - 1))
692
0
          *ptr++ = (char)toupper(*language & 255);
693
694
0
      *ptr = '\0';
695
696
     /*
697
      * Map Chinese region codes to legacy country codes.
698
      */
699
700
0
      if (!strcmp(language, "zh") && !strcmp(country, "HANS"))
701
0
        strlcpy(country, "CN", sizeof(country));
702
0
      if (!strcmp(language, "zh") && !strcmp(country, "HANT"))
703
0
        strlcpy(country, "TW", sizeof(country));
704
0
    }
705
706
0
    if (*language == '.' && !charset[0])
707
0
    {
708
     /*
709
      * Copy the encoding...
710
      */
711
712
0
      for (language ++, ptr = charset; *language; language ++)
713
0
        if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
714
0
          *ptr++ = (char)toupper(*language & 255);
715
716
0
      *ptr = '\0';
717
0
    }
718
719
   /*
720
    * Force a POSIX locale for an invalid language name...
721
    */
722
723
0
    if (strlen(langname) != 2 && strlen(langname) != 3)
724
0
    {
725
0
      strlcpy(langname, "C", sizeof(langname));
726
0
      country[0] = '\0';
727
0
      charset[0] = '\0';
728
0
    }
729
0
  }
730
731
0
  DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
732
0
                langname, country, charset));
733
734
 /*
735
  * Figure out the desired encoding...
736
  */
737
738
0
  encoding = CUPS_AUTO_ENCODING;
739
740
0
  if (charset[0])
741
0
  {
742
0
    for (i = 0;
743
0
         i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
744
0
   i ++)
745
0
      if (!_cups_strcasecmp(charset, locale_encodings[i]))
746
0
      {
747
0
  encoding = (cups_encoding_t)i;
748
0
  break;
749
0
      }
750
751
0
    if (encoding == CUPS_AUTO_ENCODING)
752
0
    {
753
     /*
754
      * Map alternate names for various character sets...
755
      */
756
757
0
      if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
758
0
          !_cups_strcasecmp(charset, "sjis"))
759
0
  encoding = CUPS_WINDOWS_932;
760
0
      else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
761
0
  encoding = CUPS_WINDOWS_936;
762
0
      else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
763
0
  encoding = CUPS_WINDOWS_949;
764
0
      else if (!_cups_strcasecmp(charset, "big5"))
765
0
  encoding = CUPS_WINDOWS_950;
766
0
    }
767
0
  }
768
769
0
  DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
770
0
                encoding == CUPS_AUTO_ENCODING ? "auto" :
771
0
        lang_encodings[encoding]));
772
773
 /*
774
  * See if we already have this language/country loaded...
775
  */
776
777
0
  if (country[0])
778
0
    snprintf(real, sizeof(real), "%s_%s", langname, country);
779
0
  else
780
0
    strlcpy(real, langname, sizeof(real));
781
782
0
  _cupsMutexLock(&lang_mutex);
783
784
0
  if ((lang = cups_cache_lookup(real, encoding)) != NULL)
785
0
  {
786
0
    _cupsMutexUnlock(&lang_mutex);
787
788
0
    DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
789
790
0
    return (lang);
791
0
  }
792
793
 /*
794
  * See if there is a free language available; if so, use that
795
  * record...
796
  */
797
798
0
  for (lang = lang_cache; lang != NULL; lang = lang->next)
799
0
    if (lang->used == 0)
800
0
      break;
801
802
0
  if (lang == NULL)
803
0
  {
804
   /*
805
    * Allocate memory for the language and add it to the cache.
806
    */
807
808
0
    if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
809
0
    {
810
0
      _cupsMutexUnlock(&lang_mutex);
811
812
0
      return (NULL);
813
0
    }
814
815
0
    lang->next = lang_cache;
816
0
    lang_cache = lang;
817
0
  }
818
0
  else
819
0
  {
820
   /*
821
    * Free all old strings as needed...
822
    */
823
824
0
    _cupsMessageFree(lang->strings);
825
0
    lang->strings = NULL;
826
0
  }
827
828
 /*
829
  * Then assign the language and encoding fields...
830
  */
831
832
0
  lang->used ++;
833
0
  strlcpy(lang->language, real, sizeof(lang->language));
834
835
0
  if (encoding != CUPS_AUTO_ENCODING)
836
0
    lang->encoding = encoding;
837
0
  else
838
0
    lang->encoding = CUPS_UTF8;
839
840
 /*
841
  * Return...
842
  */
843
844
0
  _cupsMutexUnlock(&lang_mutex);
845
846
0
  return (lang);
847
0
}
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 */
858
_cupsLangString(cups_lang_t *lang,  /* I - Language */
859
                const char  *message) /* I - Message */
860
0
{
861
0
  const char *s;      /* Localized message */
862
863
864
0
  DEBUG_printf(("_cupsLangString(lang=%p, message=\"%s\")", (void *)lang, message));
865
866
 /*
867
  * Range check input...
868
  */
869
870
0
  if (!lang || !message || !*message)
871
0
    return (message);
872
873
0
  _cupsMutexLock(&lang_mutex);
874
875
 /*
876
  * Load the message catalog if needed...
877
  */
878
879
0
  if (!lang->strings)
880
0
    cups_message_load(lang);
881
882
0
  s = _cupsMessageLookup(lang->strings, message);
883
884
0
  _cupsMutexUnlock(&lang_mutex);
885
886
0
  return (s);
887
0
}
888
889
890
/*
891
 * '_cupsMessageFree()' - Free a messages array.
892
 */
893
894
void
895
_cupsMessageFree(cups_array_t *a) /* I - Message array */
896
0
{
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
0
  cupsArrayDelete(a);
911
0
}
912
913
914
/*
915
 * '_cupsMessageLoad()' - Load a .po file into a messages array.
916
 */
917
918
cups_array_t *        /* O - New message array */
919
_cupsMessageLoad(const char *filename,  /* I - Message catalog to load */
920
                 int        unquote)  /* I - Unescape \foo in strings? */
921
0
{
922
0
  cups_file_t   *fp;    /* Message file */
923
0
  cups_array_t    *a;   /* Message array */
924
0
  _cups_message_t *m;   /* Current message */
925
0
  char      s[4096],  /* String buffer */
926
0
      *ptr,   /* Pointer into buffer */
927
0
      *temp;    /* New string */
928
0
  size_t    length,   /* Length of combined strings */
929
0
      ptrlen;   /* Length of string */
930
931
932
0
  DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
933
934
 /*
935
  * Create an array to hold the messages...
936
  */
937
938
0
  if ((a = _cupsMessageNew(NULL)) == NULL)
939
0
  {
940
0
    DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
941
0
    return (NULL);
942
0
  }
943
944
 /*
945
  * Open the message catalog file...
946
  */
947
948
0
  if ((fp = cupsFileOpen(filename, "r")) == NULL)
949
0
  {
950
0
    DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
951
0
                  strerror(errno)));
952
0
    return (a);
953
0
  }
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
0
  m = NULL;
973
974
0
  while (cupsFileGets(fp, s, sizeof(s)) != NULL)
975
0
  {
976
   /*
977
    * Skip blank and comment lines...
978
    */
979
980
0
    if (s[0] == '#' || !s[0])
981
0
      continue;
982
983
   /*
984
    * Strip the trailing quote...
985
    */
986
987
0
    if ((ptr = strrchr(s, '\"')) == NULL)
988
0
      continue;
989
990
0
    *ptr = '\0';
991
992
   /*
993
    * Find start of value...
994
    */
995
996
0
    if ((ptr = strchr(s, '\"')) == NULL)
997
0
      continue;
998
999
0
    ptr ++;
1000
1001
   /*
1002
    * Unquote the text...
1003
    */
1004
1005
0
    if (unquote)
1006
0
      cups_unquote(ptr, ptr);
1007
1008
   /*
1009
    * Create or add to a message...
1010
    */
1011
1012
0
    if (!strncmp(s, "msgid", 5))
1013
0
    {
1014
     /*
1015
      * Add previous message as needed...
1016
      */
1017
1018
0
      if (m)
1019
0
      {
1020
0
        if (m->str && m->str[0])
1021
0
        {
1022
0
          cupsArrayAdd(a, m);
1023
0
        }
1024
0
        else
1025
0
        {
1026
         /*
1027
          * Translation is empty, don't add it... (STR #4033)
1028
          */
1029
1030
0
          free(m->id);
1031
0
          if (m->str)
1032
0
            free(m->str);
1033
0
          free(m);
1034
0
        }
1035
0
      }
1036
1037
     /*
1038
      * Create a new message with the given msgid string...
1039
      */
1040
1041
0
      if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
1042
0
      {
1043
0
        cupsFileClose(fp);
1044
0
  return (a);
1045
0
      }
1046
1047
0
      if ((m->id = strdup(ptr)) == NULL)
1048
0
      {
1049
0
        free(m);
1050
0
        cupsFileClose(fp);
1051
0
  return (a);
1052
0
      }
1053
0
    }
1054
0
    else if (s[0] == '\"' && m)
1055
0
    {
1056
     /*
1057
      * Append to current string...
1058
      */
1059
1060
0
      length = strlen(m->str ? m->str : m->id);
1061
0
      ptrlen = strlen(ptr);
1062
1063
0
      if ((temp = realloc(m->str ? m->str : m->id, length + ptrlen + 1)) == NULL)
1064
0
      {
1065
0
        if (m->str)
1066
0
    free(m->str);
1067
0
  free(m->id);
1068
0
        free(m);
1069
1070
0
  cupsFileClose(fp);
1071
0
  return (a);
1072
0
      }
1073
1074
0
      if (m->str)
1075
0
      {
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
0
        m->str = temp;
1083
1084
0
  memcpy(m->str + length, ptr, ptrlen + 1);
1085
0
      }
1086
0
      else
1087
0
      {
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
0
        m->id = temp;
1095
1096
0
  memcpy(m->id + length, ptr, ptrlen + 1);
1097
0
      }
1098
0
    }
1099
0
    else if (!strncmp(s, "msgstr", 6) && m)
1100
0
    {
1101
     /*
1102
      * Set the string...
1103
      */
1104
1105
0
      if ((m->str = strdup(ptr)) == NULL)
1106
0
      {
1107
0
  free(m->id);
1108
0
        free(m);
1109
1110
0
        cupsFileClose(fp);
1111
0
  return (a);
1112
0
      }
1113
0
    }
1114
0
  }
1115
1116
 /*
1117
  * Add the last message string to the array as needed...
1118
  */
1119
1120
0
  if (m)
1121
0
  {
1122
0
    if (m->str && m->str[0])
1123
0
    {
1124
0
      cupsArrayAdd(a, m);
1125
0
    }
1126
0
    else
1127
0
    {
1128
     /*
1129
      * Translation is empty, don't add it... (STR #4033)
1130
      */
1131
1132
0
      free(m->id);
1133
0
      if (m->str)
1134
0
  free(m->str);
1135
0
      free(m);
1136
0
    }
1137
0
  }
1138
1139
 /*
1140
  * Close the message catalog file and return the new array...
1141
  */
1142
1143
0
  cupsFileClose(fp);
1144
1145
0
  DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1146
0
                cupsArrayCount(a)));
1147
1148
0
  return (a);
1149
0
}
1150
1151
1152
/*
1153
 * '_cupsMessageLookup()' - Lookup a message string.
1154
 */
1155
1156
const char *        /* O - Localized message */
1157
_cupsMessageLookup(cups_array_t *a, /* I - Message array */
1158
                   const char   *m) /* I - Message */
1159
0
{
1160
0
  _cups_message_t key,    /* Search key */
1161
0
      *match;   /* Matching message */
1162
1163
1164
0
  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
0
  key.id = (char *)m;
1172
0
  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
0
  if (match && match->str)
1217
0
    return (match->str);
1218
0
  else
1219
0
    return (m);
1220
0
}
1221
1222
1223
/*
1224
 * '_cupsMessageNew()' - Make a new message catalog array.
1225
 */
1226
1227
cups_array_t *        /* O - Array */
1228
_cupsMessageNew(void *context)    /* I - User data */
1229
0
{
1230
0
  return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
1231
0
                        (cups_ahash_func_t)NULL, 0,
1232
0
      (cups_acopy_func_t)NULL,
1233
0
      (cups_afree_func_t)cups_message_free));
1234
0
}
1235
1236
1237
#ifdef __APPLE__
1238
/*
1239
 * 'appleLangDefault()' - Get the default locale string.
1240
 */
1241
1242
static const char *     /* O - Locale string */
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 */
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 */
1563
cups_cache_lookup(
1564
    const char      *name,    /* I - Name of locale */
1565
    cups_encoding_t encoding)   /* I - Encoding of locale */
1566
0
{
1567
0
  cups_lang_t *lang;      /* Current language */
1568
1569
1570
0
  DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
1571
0
                encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1572
0
                  lang_encodings[encoding]));
1573
1574
 /*
1575
  * Loop through the cache and return a match if found...
1576
  */
1577
1578
0
  for (lang = lang_cache; lang != NULL; lang = lang->next)
1579
0
  {
1580
0
    DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1581
0
      "encoding=%d(%s)", (void *)lang, lang->language, lang->encoding,
1582
0
      lang_encodings[lang->encoding]));
1583
1584
0
    if (!strcmp(lang->language, name) &&
1585
0
        (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1586
0
    {
1587
0
      lang->used ++;
1588
1589
0
      DEBUG_puts("8cups_cache_lookup: returning match!");
1590
1591
0
      return (lang);
1592
0
    }
1593
0
  }
1594
1595
0
  DEBUG_puts("8cups_cache_lookup: returning NULL!");
1596
1597
0
  return (NULL);
1598
0
}
1599
1600
1601
/*
1602
 * 'cups_message_compare()' - Compare two messages.
1603
 */
1604
1605
static int        /* O - Result of comparison */
1606
cups_message_compare(
1607
    _cups_message_t *m1,    /* I - First message */
1608
    _cups_message_t *m2)    /* I - Second message */
1609
0
{
1610
0
  return (strcmp(m1->id, m2->id));
1611
0
}
1612
1613
1614
/*
1615
 * 'cups_message_free()' - Free a message.
1616
 */
1617
1618
static void
1619
cups_message_free(_cups_message_t *m) /* I - Message */
1620
0
{
1621
0
  if (m->id)
1622
0
    free(m->id);
1623
1624
0
  if (m->str)
1625
0
    free(m->str);
1626
1627
0
  free(m);
1628
0
}
1629
1630
1631
/*
1632
 * 'cups_message_load()' - Load the message catalog for a language.
1633
 */
1634
1635
static void
1636
cups_message_load(cups_lang_t *lang)  /* I - Language */
1637
0
{
1638
#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1639
  lang->strings = appleMessageLoad(lang->language);
1640
1641
#else
1642
0
  char      filename[1024]; /* Filename for language locale file */
1643
0
  _cups_globals_t *cg = _cupsGlobals();
1644
            /* Pointer to library globals */
1645
1646
1647
0
  snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
1648
0
     lang->language, lang->language);
1649
1650
0
  if (strchr(lang->language, '_') && access(filename, 0))
1651
0
  {
1652
   /*
1653
    * Country localization not available, look for generic localization...
1654
    */
1655
1656
0
    snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
1657
0
             lang->language, lang->language);
1658
1659
0
    if (access(filename, 0))
1660
0
    {
1661
     /*
1662
      * No generic localization, so use POSIX...
1663
      */
1664
1665
0
      DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
1666
0
                    strerror(errno)));
1667
1668
0
      snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
1669
0
    }
1670
0
  }
1671
1672
 /*
1673
  * Read the strings from the file...
1674
  */
1675
1676
0
  lang->strings = _cupsMessageLoad(filename, 1);
1677
0
#endif /* __APPLE__ && CUPS_BUNDLEDIR */
1678
0
}
1679
1680
1681
/*
1682
 * 'cups_unquote()' - Unquote characters in strings...
1683
 */
1684
1685
static void
1686
cups_unquote(char       *d,   /* O - Unquoted string */
1687
             const char *s)   /* I - Original string */
1688
0
{
1689
0
  while (*s)
1690
0
  {
1691
0
    if (*s == '\\')
1692
0
    {
1693
0
      s ++;
1694
0
      if (isdigit(*s))
1695
0
      {
1696
0
  *d = 0;
1697
1698
0
  while (isdigit(*s))
1699
0
  {
1700
0
    *d = *d * 8 + *s - '0';
1701
0
    s ++;
1702
0
  }
1703
1704
0
  d ++;
1705
0
      }
1706
0
      else
1707
0
      {
1708
0
  if (*s == 'n')
1709
0
    *d ++ = '\n';
1710
0
  else if (*s == 'r')
1711
0
    *d ++ = '\r';
1712
0
  else if (*s == 't')
1713
0
    *d ++ = '\t';
1714
0
  else
1715
0
    *d++ = *s;
1716
1717
0
  s ++;
1718
0
      }
1719
0
    }
1720
0
    else
1721
0
      *d++ = *s++;
1722
0
  }
1723
1724
0
  *d = '\0';
1725
0
}