Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/ppd-localize.c
Line
Count
Source
1
/*
2
 * PPD localization routines for CUPS.
3
 *
4
 * Copyright 2007-2018 by Apple Inc.
5
 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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
 * PostScript is a trademark of Adobe Systems, Inc.
14
 *
15
 * This code and any derivative of it may be used and distributed
16
 * freely under the terms of the GNU General Public License when
17
 * used with GNU Ghostscript or its derivatives.  Use of the code
18
 * (or any derivative of it) with software other than GNU
19
 * GhostScript (or its derivatives) is governed by the CUPS license
20
 * agreement.
21
 *
22
 * This file is subject to the Apple OS-Developed Software exception.
23
 */
24
25
/*
26
 * Include necessary headers.
27
 */
28
29
#include "cups-private.h"
30
#include "ppd-private.h"
31
32
33
/*
34
 * Local functions...
35
 */
36
37
static cups_lang_t  *ppd_ll_CC(char *ll_CC, size_t ll_CC_size);
38
39
40
/*
41
 * 'ppdLocalize()' - Localize the PPD file to the current locale.
42
 *
43
 * All groups, options, and choices are localized, as are ICC profile
44
 * descriptions, printer presets, and custom option parameters.  Each
45
 * localized string uses the UTF-8 character encoding.
46
 *
47
 * @since CUPS 1.2/macOS 10.5@
48
 */
49
50
int         /* O - 0 on success, -1 on error */
51
ppdLocalize(ppd_file_t *ppd)    /* I - PPD file */
52
0
{
53
0
  int   i, j, k;    /* Looping vars */
54
0
  ppd_group_t *group;     /* Current group */
55
0
  ppd_option_t  *option;    /* Current option */
56
0
  ppd_choice_t  *choice;    /* Current choice */
57
0
  ppd_coption_t *coption;   /* Current custom option */
58
0
  ppd_cparam_t  *cparam;    /* Current custom parameter */
59
0
  ppd_attr_t  *attr,      /* Current attribute */
60
0
    *locattr;   /* Localized attribute */
61
0
  char    ckeyword[PPD_MAX_NAME], /* Custom keyword */
62
0
    ll_CC[6];   /* Language + country locale */
63
64
65
 /*
66
  * Range check input...
67
  */
68
69
0
  DEBUG_printf(("ppdLocalize(ppd=%p)", ppd));
70
71
0
  if (!ppd)
72
0
    return (-1);
73
74
 /*
75
  * Get the default language...
76
  */
77
78
0
  ppd_ll_CC(ll_CC, sizeof(ll_CC));
79
80
 /*
81
  * Now lookup all of the groups, options, choices, etc.
82
  */
83
84
0
  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
85
0
  {
86
0
    if ((locattr = _ppdLocalizedAttr(ppd, "Translation", group->name,
87
0
                                     ll_CC)) != NULL)
88
0
      strlcpy(group->text, locattr->text, sizeof(group->text));
89
90
0
    for (j = group->num_options, option = group->options; j > 0; j --, option ++)
91
0
    {
92
0
      if ((locattr = _ppdLocalizedAttr(ppd, "Translation", option->keyword,
93
0
                                       ll_CC)) != NULL)
94
0
  strlcpy(option->text, locattr->text, sizeof(option->text));
95
96
0
      for (k = option->num_choices, choice = option->choices;
97
0
           k > 0;
98
0
     k --, choice ++)
99
0
      {
100
0
        if (strcmp(choice->choice, "Custom") ||
101
0
      !ppdFindCustomOption(ppd, option->keyword))
102
0
    locattr = _ppdLocalizedAttr(ppd, option->keyword, choice->choice,
103
0
                                ll_CC);
104
0
  else
105
0
  {
106
0
    snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
107
108
0
    locattr = _ppdLocalizedAttr(ppd, ckeyword, "True", ll_CC);
109
0
  }
110
111
0
        if (locattr)
112
0
    strlcpy(choice->text, locattr->text, sizeof(choice->text));
113
0
      }
114
0
    }
115
0
  }
116
117
 /*
118
  * Translate any custom parameters...
119
  */
120
121
0
  for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
122
0
       coption;
123
0
       coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
124
0
  {
125
0
    for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
126
0
   cparam;
127
0
   cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
128
0
    {
129
0
      snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%s", coption->keyword);
130
131
0
      if ((locattr = _ppdLocalizedAttr(ppd, ckeyword, cparam->name,
132
0
                                       ll_CC)) != NULL)
133
0
        strlcpy(cparam->text, locattr->text, sizeof(cparam->text));
134
0
    }
135
0
  }
136
137
 /*
138
  * Translate ICC profile names...
139
  */
140
141
0
  if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL)
142
0
  {
143
0
    if ((locattr = _ppdLocalizedAttr(ppd, "APCustomColorMatchingName",
144
0
                                     attr->spec, ll_CC)) != NULL)
145
0
      strlcpy(attr->text, locattr->text, sizeof(attr->text));
146
0
  }
147
148
0
  for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
149
0
       attr;
150
0
       attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
151
0
  {
152
0
    cupsArraySave(ppd->sorted_attrs);
153
154
0
    if ((locattr = _ppdLocalizedAttr(ppd, "cupsICCProfile", attr->spec,
155
0
                                     ll_CC)) != NULL)
156
0
      strlcpy(attr->text, locattr->text, sizeof(attr->text));
157
158
0
    cupsArrayRestore(ppd->sorted_attrs);
159
0
  }
160
161
 /*
162
  * Translate printer presets...
163
  */
164
165
0
  for (attr = ppdFindAttr(ppd, "APPrinterPreset", NULL);
166
0
       attr;
167
0
       attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL))
168
0
  {
169
0
    cupsArraySave(ppd->sorted_attrs);
170
171
0
    if ((locattr = _ppdLocalizedAttr(ppd, "APPrinterPreset", attr->spec,
172
0
                                     ll_CC)) != NULL)
173
0
      strlcpy(attr->text, locattr->text, sizeof(attr->text));
174
175
0
    cupsArrayRestore(ppd->sorted_attrs);
176
0
  }
177
178
0
  return (0);
179
0
}
180
181
182
/*
183
 * 'ppdLocalizeAttr()' - Localize an attribute.
184
 *
185
 * This function uses the current locale to find the localized attribute for
186
 * the given main and option keywords.  If no localized version of the
187
 * attribute exists for the current locale, the unlocalized version is returned.
188
 */
189
190
ppd_attr_t *        /* O - Localized attribute or @code NULL@ if none exists */
191
ppdLocalizeAttr(ppd_file_t *ppd,  /* I - PPD file */
192
    const char *keyword,  /* I - Main keyword */
193
    const char *spec) /* I - Option keyword or @code NULL@ for none */
194
0
{
195
0
  ppd_attr_t  *locattr;   /* Localized attribute */
196
0
  char    ll_CC[6];   /* Language + country locale */
197
198
199
 /*
200
  * Get the default language...
201
  */
202
203
0
  ppd_ll_CC(ll_CC, sizeof(ll_CC));
204
205
 /*
206
  * Find the localized attribute...
207
  */
208
209
0
  if (spec)
210
0
    locattr = _ppdLocalizedAttr(ppd, keyword, spec, ll_CC);
211
0
  else
212
0
    locattr = _ppdLocalizedAttr(ppd, "Translation", keyword, ll_CC);
213
214
0
  if (!locattr)
215
0
    locattr = ppdFindAttr(ppd, keyword, spec);
216
217
0
  return (locattr);
218
0
}
219
220
221
/*
222
 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
223
 *                            attribute.
224
 *
225
 * This function uses the current locale to find the corresponding reason
226
 * text or URI from the attribute value. If "scheme" is NULL or "text",
227
 * the returned value contains human-readable (UTF-8) text from the translation
228
 * string or attribute value. Otherwise the corresponding URI is returned.
229
 *
230
 * If no value of the requested scheme can be found, NULL is returned.
231
 *
232
 * @since CUPS 1.3/macOS 10.5@
233
 */
234
235
const char *        /* O - Value or NULL if not found */
236
ppdLocalizeIPPReason(
237
    ppd_file_t *ppd,      /* I - PPD file */
238
    const char *reason,     /* I - IPP reason keyword to look up */
239
    const char *scheme,     /* I - URI scheme or NULL for text */
240
    char       *buffer,     /* I - Value buffer */
241
    size_t     bufsize)     /* I - Size of value buffer */
242
0
{
243
0
  cups_lang_t *lang;      /* Current language */
244
0
  ppd_attr_t  *locattr;   /* Localized attribute */
245
0
  char    ll_CC[6],   /* Language + country locale */
246
0
    *bufptr,    /* Pointer into buffer */
247
0
    *bufend,    /* Pointer to end of buffer */
248
0
    *valptr;    /* Pointer into value */
249
0
  int   ch;     /* Hex-encoded character */
250
0
  size_t  schemelen;    /* Length of scheme name */
251
252
253
 /*
254
  * Range check input...
255
  */
256
257
0
  if (buffer)
258
0
    *buffer = '\0';
259
260
0
  if (!ppd || !reason || (scheme && !*scheme) ||
261
0
      !buffer || bufsize < PPD_MAX_TEXT)
262
0
    return (NULL);
263
264
 /*
265
  * Get the default language...
266
  */
267
268
0
  lang = ppd_ll_CC(ll_CC, sizeof(ll_CC));
269
270
 /*
271
  * Find the localized attribute...
272
  */
273
274
0
  if ((locattr = _ppdLocalizedAttr(ppd, "cupsIPPReason", reason,
275
0
                                   ll_CC)) == NULL)
276
0
    locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
277
278
0
  if (!locattr)
279
0
  {
280
0
    if (lang && (!scheme || !strcmp(scheme, "text")) && strcmp(reason, "none"))
281
0
    {
282
     /*
283
      * Try to localize a standard printer-state-reason keyword...
284
      */
285
286
0
      const char *message = NULL; /* Localized message */
287
288
0
      if (!strncmp(reason, "media-needed", 12))
289
0
  message = _("Load paper.");
290
0
      else if (!strncmp(reason, "media-jam", 9))
291
0
  message = _("Paper jam.");
292
0
      else if (!strncmp(reason, "offline", 7) ||
293
0
           !strncmp(reason, "shutdown", 8))
294
0
  message = _("The printer is not connected.");
295
0
      else if (!strncmp(reason, "toner-low", 9))
296
0
  message = _("The printer is low on toner.");
297
0
      else if (!strncmp(reason, "toner-empty", 11))
298
0
  message = _("The printer may be out of toner.");
299
0
      else if (!strncmp(reason, "cover-open", 10))
300
0
  message = _("The printer's cover is open.");
301
0
      else if (!strncmp(reason, "interlock-open", 14))
302
0
  message = _("The printer's interlock is open.");
303
0
      else if (!strncmp(reason, "door-open", 9))
304
0
  message = _("The printer's door is open.");
305
0
      else if (!strncmp(reason, "input-tray-missing", 18))
306
0
  message = _("Paper tray is missing.");
307
0
      else if (!strncmp(reason, "media-low", 9))
308
0
  message = _("Paper tray is almost empty.");
309
0
      else if (!strncmp(reason, "media-empty", 11))
310
0
  message = _("Paper tray is empty.");
311
0
      else if (!strncmp(reason, "output-tray-missing", 19))
312
0
  message = _("Output bin is missing.");
313
0
      else if (!strncmp(reason, "output-area-almost-full", 23))
314
0
  message = _("Output bin is almost full.");
315
0
      else if (!strncmp(reason, "output-area-full", 16))
316
0
  message = _("Output bin is full.");
317
0
      else if (!strncmp(reason, "marker-supply-low", 17))
318
0
  message = _("The printer is low on ink.");
319
0
      else if (!strncmp(reason, "marker-supply-empty", 19))
320
0
  message = _("The printer may be out of ink.");
321
0
      else if (!strncmp(reason, "marker-waste-almost-full", 24))
322
0
  message = _("The printer's waste bin is almost full.");
323
0
      else if (!strncmp(reason, "marker-waste-full", 17))
324
0
  message = _("The printer's waste bin is full.");
325
0
      else if (!strncmp(reason, "fuser-over-temp", 15))
326
0
  message = _("The fuser's temperature is high.");
327
0
      else if (!strncmp(reason, "fuser-under-temp", 16))
328
0
  message = _("The fuser's temperature is low.");
329
0
      else if (!strncmp(reason, "opc-near-eol", 12))
330
0
  message = _("The optical photoconductor will need to be replaced soon.");
331
0
      else if (!strncmp(reason, "opc-life-over", 13))
332
0
  message = _("The optical photoconductor needs to be replaced.");
333
0
      else if (!strncmp(reason, "developer-low", 13))
334
0
  message = _("The developer unit will need to be replaced soon.");
335
0
      else if (!strncmp(reason, "developer-empty", 15))
336
0
  message = _("The developer unit needs to be replaced.");
337
338
0
      if (message)
339
0
      {
340
0
        strlcpy(buffer, _cupsLangString(lang, message), bufsize);
341
0
  return (buffer);
342
0
      }
343
0
    }
344
345
0
    return (NULL);
346
0
  }
347
348
 /*
349
  * Now find the value we need...
350
  */
351
352
0
  bufend = buffer + bufsize - 1;
353
354
0
  if (!scheme || !strcmp(scheme, "text"))
355
0
  {
356
   /*
357
    * Copy a text value (either the translation text or text:... URIs from
358
    * the value...
359
    */
360
361
0
    strlcpy(buffer, locattr->text, bufsize);
362
363
0
    for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
364
0
    {
365
0
      if (!strncmp(valptr, "text:", 5))
366
0
      {
367
       /*
368
        * Decode text: URI and add to the buffer...
369
  */
370
371
0
  valptr += 5;
372
373
0
        while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
374
0
  {
375
0
    if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
376
0
        isxdigit(valptr[2] & 255))
377
0
    {
378
     /*
379
      * Pull a hex-encoded character from the URI...
380
      */
381
382
0
            valptr ++;
383
384
0
      if (isdigit(*valptr & 255))
385
0
        ch = (*valptr - '0') << 4;
386
0
      else
387
0
        ch = (tolower(*valptr) - 'a' + 10) << 4;
388
0
      valptr ++;
389
390
0
      if (isdigit(*valptr & 255))
391
0
        *bufptr++ = (char)(ch | (*valptr - '0'));
392
0
      else
393
0
        *bufptr++ = (char)(ch | (tolower(*valptr) - 'a' + 10));
394
0
      valptr ++;
395
0
    }
396
0
    else if (*valptr == '+')
397
0
    {
398
0
      *bufptr++ = ' ';
399
0
      valptr ++;
400
0
    }
401
0
    else
402
0
      *bufptr++ = *valptr++;
403
0
        }
404
0
      }
405
0
      else
406
0
      {
407
       /*
408
        * Skip this URI...
409
  */
410
411
0
        while (*valptr && !_cups_isspace(*valptr))
412
0
          valptr++;
413
0
      }
414
415
     /*
416
      * Skip whitespace...
417
      */
418
419
0
      while (_cups_isspace(*valptr))
420
0
  valptr ++;
421
0
    }
422
423
0
    if (bufptr > buffer)
424
0
      *bufptr = '\0';
425
426
0
    return (buffer);
427
0
  }
428
0
  else
429
0
  {
430
   /*
431
    * Copy a URI...
432
    */
433
434
0
    schemelen = strlen(scheme);
435
0
    if (scheme[schemelen - 1] == ':') /* Force scheme to be just the name */
436
0
      schemelen --;
437
438
0
    for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
439
0
    {
440
0
      if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') ||
441
0
          (*valptr == '/' && !strcmp(scheme, "file")))
442
0
      {
443
       /*
444
        * Copy URI...
445
  */
446
447
0
        while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
448
0
    *bufptr++ = *valptr++;
449
450
0
  *bufptr = '\0';
451
452
0
  return (buffer);
453
0
      }
454
0
      else
455
0
      {
456
       /*
457
        * Skip this URI...
458
  */
459
460
0
  while (*valptr && !_cups_isspace(*valptr))
461
0
    valptr++;
462
0
      }
463
464
     /*
465
      * Skip whitespace...
466
      */
467
468
0
      while (_cups_isspace(*valptr))
469
0
  valptr ++;
470
0
    }
471
472
0
    return (NULL);
473
0
  }
474
0
}
475
476
477
/*
478
 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
479
 *                             attribute value.
480
 *
481
 * This function uses the current locale to find the corresponding name
482
 * text from the attribute value. If no localized text for the requested
483
 * name can be found, @code NULL@ is returned.
484
 *
485
 * @since CUPS 1.4/macOS 10.6@
486
 */
487
488
const char *        /* O - Value or @code NULL@ if not found */
489
ppdLocalizeMarkerName(
490
    ppd_file_t *ppd,      /* I - PPD file */
491
    const char *name)     /* I - Marker name to look up */
492
0
{
493
0
  ppd_attr_t  *locattr;   /* Localized attribute */
494
0
  char    ll_CC[6];   /* Language + country locale */
495
496
497
 /*
498
  * Range check input...
499
  */
500
501
0
  if (!ppd || !name)
502
0
    return (NULL);
503
504
 /*
505
  * Get the default language...
506
  */
507
508
0
  ppd_ll_CC(ll_CC, sizeof(ll_CC));
509
510
 /*
511
  * Find the localized attribute...
512
  */
513
514
0
  if ((locattr = _ppdLocalizedAttr(ppd, "cupsMarkerName", name,
515
0
                                   ll_CC)) == NULL)
516
0
    locattr = ppdFindAttr(ppd, "cupsMarkerName", name);
517
518
0
  return (locattr ? locattr->text : NULL);
519
0
}
520
521
522
/*
523
 * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
524
 */
525
526
void
527
_ppdFreeLanguages(
528
    cups_array_t *languages)    /* I - Languages array */
529
0
{
530
0
  char  *language;      /* Current language */
531
532
533
0
  for (language = (char *)cupsArrayFirst(languages);
534
0
       language;
535
0
       language = (char *)cupsArrayNext(languages))
536
0
    free(language);
537
538
0
  cupsArrayDelete(languages);
539
0
}
540
541
542
/*
543
 * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
544
 */
545
546
cups_array_t *        /* O - Languages array */
547
_ppdGetLanguages(ppd_file_t *ppd) /* I - PPD file */
548
0
{
549
0
  cups_array_t  *languages;   /* Languages array */
550
0
  ppd_attr_t  *attr;      /* cupsLanguages attribute */
551
0
  char    *value,     /* Copy of attribute value */
552
0
    *start,     /* Start of current language */
553
0
    *ptr;     /* Pointer into languages */
554
555
556
 /*
557
  * See if we have a cupsLanguages attribute...
558
  */
559
560
0
  if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) == NULL || !attr->value)
561
0
    return (NULL);
562
563
 /*
564
  * Yes, load the list...
565
  */
566
567
0
  if ((languages = cupsArrayNew((cups_array_func_t)strcmp, NULL)) == NULL)
568
0
    return (NULL);
569
570
0
  if ((value = strdup(attr->value)) == NULL)
571
0
  {
572
0
    cupsArrayDelete(languages);
573
0
    return (NULL);
574
0
  }
575
576
0
  for (ptr = value; *ptr;)
577
0
  {
578
   /*
579
    * Skip leading whitespace...
580
    */
581
582
0
    while (_cups_isspace(*ptr))
583
0
      ptr ++;
584
585
0
    if (!*ptr)
586
0
      break;
587
588
   /*
589
    * Find the end of this language name...
590
    */
591
592
0
    for (start = ptr; *ptr && !_cups_isspace(*ptr); ptr ++);
593
594
0
    if (*ptr)
595
0
      *ptr++ = '\0';
596
597
0
    if (!strcmp(start, "en"))
598
0
      continue;
599
600
0
    cupsArrayAdd(languages, strdup(start));
601
0
  }
602
603
 /*
604
  * Free the temporary string and return either an array with one or more
605
  * values or a NULL pointer...
606
  */
607
608
0
  free(value);
609
610
0
  if (cupsArrayCount(languages) == 0)
611
0
  {
612
0
    cupsArrayDelete(languages);
613
0
    return (NULL);
614
0
  }
615
0
  else
616
0
    return (languages);
617
0
}
618
619
620
/*
621
 * '_ppdHashName()' - Generate a hash value for a device or profile name.
622
 *
623
 * This function is primarily used on macOS, but is generally accessible
624
 * since cupstestppd needs to check for profile name collisions in PPD files...
625
 */
626
627
unsigned        /* O - Hash value */
628
_ppdHashName(const char *name)    /* I - Name to hash */
629
0
{
630
0
  unsigned  mult,     /* Multiplier */
631
0
    hash = 0;   /* Hash value */
632
633
634
0
  for (mult = 1; *name && mult <= 128; mult ++, name ++)
635
0
    hash += (*name & 255) * mult;
636
637
0
  return (hash);
638
0
}
639
640
641
/*
642
 * '_ppdLocalizedAttr()' - Find a localized attribute.
643
 */
644
645
ppd_attr_t *        /* O - Localized attribute or NULL */
646
_ppdLocalizedAttr(ppd_file_t *ppd,  /* I - PPD file */
647
      const char *keyword,  /* I - Main keyword */
648
      const char *spec, /* I - Option keyword */
649
      const char *ll_CC)  /* I - Language + country locale */
650
0
{
651
0
  char    lkeyword[PPD_MAX_NAME]; /* Localization keyword */
652
0
  ppd_attr_t  *attr;      /* Current attribute */
653
654
655
0
  DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
656
0
                "ll_CC=\"%s\")", ppd, keyword, spec, ll_CC));
657
658
 /*
659
  * Look for Keyword.ll_CC, then Keyword.ll...
660
  */
661
662
0
  snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
663
0
  if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
664
0
  {
665
   /*
666
    * <rdar://problem/22130168>
667
    *
668
    * Multiple locales need special handling...  Sigh...
669
    */
670
671
0
    if (!strcmp(ll_CC, "zh_HK"))
672
0
    {
673
0
      snprintf(lkeyword, sizeof(lkeyword), "zh_TW.%s", keyword);
674
0
      attr = ppdFindAttr(ppd, lkeyword, spec);
675
0
    }
676
677
0
    if (!attr)
678
0
    {
679
0
      snprintf(lkeyword, sizeof(lkeyword), "%2.2s.%s", ll_CC, keyword);
680
0
      attr = ppdFindAttr(ppd, lkeyword, spec);
681
0
    }
682
683
0
    if (!attr)
684
0
    {
685
0
      if (!strncmp(ll_CC, "ja", 2))
686
0
      {
687
       /*
688
  * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
689
  * PPD files were incorrectly assigned "jp" as the locale name
690
  * instead of "ja".  Support both the old (incorrect) and new
691
  * locale names for Japanese...
692
  */
693
694
0
  snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
695
0
  attr = ppdFindAttr(ppd, lkeyword, spec);
696
0
      }
697
0
      else if (!strncmp(ll_CC, "nb", 2))
698
0
      {
699
       /*
700
  * Norway has two languages, "Bokmal" (the primary one)
701
  * and "Nynorsk" (new Norwegian); this code maps from the (currently)
702
  * recommended "nb" to the previously recommended "no"...
703
  */
704
705
0
  snprintf(lkeyword, sizeof(lkeyword), "no.%s", keyword);
706
0
  attr = ppdFindAttr(ppd, lkeyword, spec);
707
0
      }
708
0
      else if (!strncmp(ll_CC, "no", 2))
709
0
      {
710
       /*
711
  * Norway has two languages, "Bokmal" (the primary one)
712
  * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
713
  * recommended by the locale folks...
714
  */
715
716
0
  snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
717
0
  attr = ppdFindAttr(ppd, lkeyword, spec);
718
0
      }
719
0
    }
720
0
  }
721
722
#ifdef DEBUG
723
  if (attr)
724
    DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr->name,
725
                  attr->spec, attr->text, attr->value ? attr->value : ""));
726
  else
727
    DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
728
#endif /* DEBUG */
729
730
0
  return (attr);
731
0
}
732
733
734
/*
735
 * 'ppd_ll_CC()' - Get the current locale names.
736
 */
737
738
static cups_lang_t *      /* O - Current language */
739
ppd_ll_CC(char   *ll_CC,    /* O - Country-specific locale name */
740
          size_t ll_CC_size)    /* I - Size of country-specific name */
741
0
{
742
0
  cups_lang_t *lang;      /* Current language */
743
744
745
 /*
746
  * Get the current locale...
747
  */
748
749
0
  if ((lang = cupsLangDefault()) == NULL)
750
0
  {
751
0
    strlcpy(ll_CC, "en_US", ll_CC_size);
752
0
    return (NULL);
753
0
  }
754
755
 /*
756
  * Copy the locale name...
757
  */
758
759
0
  strlcpy(ll_CC, lang->language, ll_CC_size);
760
761
0
  if (strlen(ll_CC) == 2)
762
0
  {
763
   /*
764
    * Map "ll" to primary/origin country locales to have the best
765
    * chance of finding a match...
766
    */
767
768
0
    if (!strcmp(ll_CC, "cs"))
769
0
      strlcpy(ll_CC, "cs_CZ", ll_CC_size);
770
0
    else if (!strcmp(ll_CC, "en"))
771
0
      strlcpy(ll_CC, "en_US", ll_CC_size);
772
0
    else if (!strcmp(ll_CC, "ja"))
773
0
      strlcpy(ll_CC, "ja_JP", ll_CC_size);
774
0
    else if (!strcmp(ll_CC, "sv"))
775
0
      strlcpy(ll_CC, "sv_SE", ll_CC_size);
776
0
    else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
777
0
      strlcpy(ll_CC, "zh_CN", ll_CC_size);
778
0
  }
779
780
0
  DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
781
0
                lang->language, ll_CC));
782
0
  return (lang);
783
0
}