/*
- * "$Id: localize.c 6686 2007-07-16 23:11:59Z mike $"
+ * "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $"
*
- * PPD custom option routines for the Common UNIX Printing System (CUPS).
+ * PPD localization routines for CUPS.
*
- * Copyright 2007 by Apple Inc.
+ * Copyright 2007-2010 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
*
* Contents:
*
- * ppdLocalize() - Localize the PPD file to the current locale.
- * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
- * attribute.
- * ppd_ll_CC() - Get the current locale names.
- * ppd_localized_attr() - Find a localized attribute.
+ * ppdLocalize() - Localize the PPD file to the current locale.
+ * ppdLocalizeAttr() - Localize an attribute.
+ * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
+ * attribute.
+ * ppdLocalizeMarkerName() - Get the localized version of a marker-names
+ * attribute value.
+ * _ppdFreeLanguages() - Free an array of languages from _ppdGetLanguages.
+ * _ppdGetLanguages() - Get an array of languages from a PPD file.
+ * _ppdHashName() - Generate a hash value for a device or profile
+ * name.
+ * _ppdLocalizedAttr() - Find a localized attribute.
+ * ppd_ll_CC() - Get the current locale names.
*/
/*
* Include necessary headers.
*/
-#include "globals.h"
-#include "debug.h"
+#include "cups-private.h"
+#include "ppd-private.h"
/*
* Local functions...
*/
-static void ppd_ll_CC(char *ll_CC, char *ll);
-static ppd_attr_t *ppd_localized_attr(ppd_file_t *ppd,
- const char *keyword,
- const char *spec, const char *ll_CC,
- const char *ll);
+static cups_lang_t *ppd_ll_CC(char *ll_CC, int ll_CC_size);
/*
* descriptions, printer presets, and custom option parameters. Each
* localized string uses the UTF-8 character encoding.
*
- * @since CUPS 1.2@
+ * @since CUPS 1.2/Mac OS X 10.5@
*/
int /* O - 0 on success, -1 on error */
ppd_attr_t *attr, /* Current attribute */
*locattr; /* Localized attribute */
char ckeyword[PPD_MAX_NAME], /* Custom keyword */
- ll_CC[6], /* Language + country locale */
- ll[3]; /* Language locale */
+ ll_CC[6]; /* Language + country locale */
/*
* Range check input...
*/
- DEBUG_printf(("ppdLocalize(ppd=%p)\n", ppd));
+ DEBUG_printf(("ppdLocalize(ppd=%p)", ppd));
if (!ppd)
return (-1);
* Get the default language...
*/
- ppd_ll_CC(ll_CC, ll);
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
/*
* Now lookup all of the groups, options, choices, etc.
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
{
- if ((locattr = ppd_localized_attr(ppd, "Translation", group->name,
- ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "Translation", group->name,
+ ll_CC)) != NULL)
strlcpy(group->text, locattr->text, sizeof(group->text));
for (j = group->num_options, option = group->options; j > 0; j --, option ++)
{
- if ((locattr = ppd_localized_attr(ppd, "Translation", option->keyword,
- ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "Translation", option->keyword,
+ ll_CC)) != NULL)
strlcpy(option->text, locattr->text, sizeof(option->text));
for (k = option->num_choices, choice = option->choices;
k > 0;
k --, choice ++)
{
- if (strcmp(choice->choice, "Custom"))
- locattr = ppd_localized_attr(ppd, option->keyword, choice->choice,
- ll_CC, ll);
+ if (strcmp(choice->choice, "Custom") ||
+ !ppdFindCustomOption(ppd, option->keyword))
+ locattr = _ppdLocalizedAttr(ppd, option->keyword, choice->choice,
+ ll_CC);
else
{
snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
- locattr = ppd_localized_attr(ppd, ckeyword, "True", ll_CC, ll);
+ locattr = _ppdLocalizedAttr(ppd, ckeyword, "True", ll_CC);
}
if (locattr)
{
snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%s", coption->keyword);
- if ((locattr = ppd_localized_attr(ppd, ckeyword, cparam->name,
- ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, ckeyword, cparam->name,
+ ll_CC)) != NULL)
strlcpy(cparam->text, locattr->text, sizeof(cparam->text));
}
}
if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL)
{
- if ((locattr = ppd_localized_attr(ppd, "APCustomColorMatchingName",
- attr->spec, ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "APCustomColorMatchingName",
+ attr->spec, ll_CC)) != NULL)
strlcpy(attr->text, locattr->text, sizeof(attr->text));
}
{
cupsArraySave(ppd->sorted_attrs);
- if ((locattr = ppd_localized_attr(ppd, "cupsICCProfile", attr->spec,
- ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsICCProfile", attr->spec,
+ ll_CC)) != NULL)
strlcpy(attr->text, locattr->text, sizeof(attr->text));
cupsArrayRestore(ppd->sorted_attrs);
{
cupsArraySave(ppd->sorted_attrs);
- if ((locattr = ppd_localized_attr(ppd, "APPrinterPreset", attr->spec,
- ll_CC, ll)) != NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "APPrinterPreset", attr->spec,
+ ll_CC)) != NULL)
strlcpy(attr->text, locattr->text, sizeof(attr->text));
cupsArrayRestore(ppd->sorted_attrs);
}
+/*
+ * 'ppdLocalizeAttr()' - Localize an attribute.
+ *
+ * This function uses the current locale to find the localized attribute for
+ * the given main and option keywords. If no localized version of the
+ * attribute exists for the current locale, the unlocalized version is returned.
+ */
+
+ppd_attr_t * /* O - Localized attribute or @code NULL@ if none exists */
+ppdLocalizeAttr(ppd_file_t *ppd, /* I - PPD file */
+ const char *keyword, /* I - Main keyword */
+ const char *spec) /* I - Option keyword or @code NULL@ for none */
+{
+ ppd_attr_t *locattr; /* Localized attribute */
+ char ll_CC[6]; /* Language + country locale */
+
+
+ /*
+ * Get the default language...
+ */
+
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
+
+ /*
+ * Find the localized attribute...
+ */
+
+ if (spec)
+ locattr = _ppdLocalizedAttr(ppd, keyword, spec, ll_CC);
+ else
+ locattr = _ppdLocalizedAttr(ppd, "Translation", keyword, ll_CC);
+
+ if (!locattr)
+ locattr = ppdFindAttr(ppd, keyword, spec);
+
+ return (locattr);
+}
+
+
/*
* 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
* attribute.
*
* If no value of the requested scheme can be found, NULL is returned.
*
- * @since CUPS 1.3@
+ * @since CUPS 1.3/Mac OS X 10.5@
*/
const char * /* O - Value or NULL if not found */
char *buffer, /* I - Value buffer */
size_t bufsize) /* I - Size of value buffer */
{
+ cups_lang_t *lang; /* Current language */
ppd_attr_t *locattr; /* Localized attribute */
char ll_CC[6], /* Language + country locale */
- ll[3], /* Language locale */
*bufptr, /* Pointer into buffer */
*bufend, /* Pointer to end of buffer */
*valptr; /* Pointer into value */
* Get the default language...
*/
- ppd_ll_CC(ll_CC, ll);
+ lang = ppd_ll_CC(ll_CC, sizeof(ll_CC));
/*
* Find the localized attribute...
*/
- if ((locattr = ppd_localized_attr(ppd, "cupsIPPReason", reason,
- ll_CC, ll)) == NULL)
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsIPPReason", reason,
+ ll_CC)) == NULL)
locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
if (!locattr)
+ {
+ if (lang && (!scheme || !strcmp(scheme, "text")))
+ {
+ /*
+ * Try to localize a standard printer-state-reason keyword...
+ */
+
+ const char *message = NULL; /* Localized message */
+
+ if (!strncmp(reason, "media-needed", 12))
+ message = _("The paper tray needs to be filled.");
+ else if (!strncmp(reason, "media-jam", 9))
+ message = _("There is a paper jam.");
+ else if (!strncmp(reason, "offline", 7) ||
+ !strncmp(reason, "shutdown", 8))
+ message = _("The printer is not connected.");
+ else if (!strncmp(reason, "toner-low", 9))
+ message = _("The printer is low on toner.");
+ else if (!strncmp(reason, "toner-empty", 11))
+ message = _("The printer is out of toner.");
+ else if (!strncmp(reason, "cover-open", 10))
+ message = _("The printer's cover is open.");
+ else if (!strncmp(reason, "interlock-open", 14))
+ message = _("The printer's interlock is open.");
+ else if (!strncmp(reason, "door-open", 9))
+ message = _("The printer's door is open.");
+ else if (!strncmp(reason, "input-tray-missing", 18))
+ message = _("The paper tray is missing.");
+ else if (!strncmp(reason, "media-low", 9))
+ message = _("The paper tray is almost empty.");
+ else if (!strncmp(reason, "media-empty", 11))
+ message = _("The paper tray is empty.");
+ else if (!strncmp(reason, "output-tray-missing", 19))
+ message = _("The output bin is missing.");
+ else if (!strncmp(reason, "output-area-almost-full", 23))
+ message = _("The output bin is almost full.");
+ else if (!strncmp(reason, "output-area-full", 16))
+ message = _("The output bin is full.");
+ else if (!strncmp(reason, "marker-supply-low", 17))
+ message = _("The printer is almost out of ink.");
+ else if (!strncmp(reason, "marker-supply-empty", 19))
+ message = _("The printer is out of ink.");
+ else if (!strncmp(reason, "marker-waste-almost-full", 24))
+ message = _("The printer's waste bin is almost full.");
+ else if (!strncmp(reason, "marker-waste-full", 17))
+ message = _("The printer's waste bin is full.");
+ else if (!strncmp(reason, "fuser-over-temp", 15))
+ message = _("The fuser's temperature is high.");
+ else if (!strncmp(reason, "fuser-under-temp", 16))
+ message = _("The fuser's temperature is low.");
+ else if (!strncmp(reason, "opc-near-eol", 12))
+ message = _("The optical photoconductor will need to be replaced soon.");
+ else if (!strncmp(reason, "opc-life-over", 13))
+ message = _("The optical photoconductor needs to be replaced.");
+ else if (!strncmp(reason, "developer-low", 13))
+ message = _("The developer unit will need to be replaced soon.");
+ else if (!strncmp(reason, "developer-empty", 15))
+ message = _("The developer unit needs to be replaced.");
+
+ if (message)
+ {
+ strlcpy(buffer, _cupsLangString(lang, message), bufsize);
+ return (buffer);
+ }
+ }
+
return (NULL);
+ }
/*
* Now find the value we need...
* Decode text: URI and add to the buffer...
*/
- if (bufptr > buffer)
- *bufptr++ = ' '; /* Add leading whitespace */
-
valptr += 5;
- while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
+ while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
{
if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
isxdigit(valptr[2] & 255))
* Skip this URI...
*/
- while (*valptr && !isspace(*valptr & 255))
+ while (*valptr && !_cups_isspace(*valptr))
valptr++;
}
* Skip whitespace...
*/
- while (isspace(*valptr & 255))
+ while (_cups_isspace(*valptr))
valptr ++;
}
* Copy URI...
*/
- while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
+ while (*valptr && !_cups_isspace(*valptr) && bufptr < bufend)
*bufptr++ = *valptr++;
*bufptr = '\0';
* Skip this URI...
*/
- while (*valptr && !isspace(*valptr & 255))
+ while (*valptr && !_cups_isspace(*valptr))
valptr++;
}
* Skip whitespace...
*/
- while (isspace(*valptr & 255))
+ while (_cups_isspace(*valptr))
valptr ++;
}
/*
- * 'ppd_ll_CC()' - Get the current locale names.
+ * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
+ * attribute value.
+ *
+ * This function uses the current locale to find the corresponding name
+ * text from the attribute value. If no localized text for the requested
+ * name can be found, @code NULL@ is returned.
+ *
+ * @since CUPS 1.4/Mac OS X 10.6@
*/
-static void
-ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
- char *ll) /* O - Generic locale name */
-
+const char * /* O - Value or @code NULL@ if not found */
+ppdLocalizeMarkerName(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Marker name to look up */
{
- cups_lang_t *lang; /* Current language */
+ ppd_attr_t *locattr; /* Localized attribute */
+ char ll_CC[6]; /* Language + country locale */
/*
- * Get the current locale...
+ * Range check input...
*/
- if ((lang = cupsLangDefault()) == NULL)
- {
- strcpy(ll_CC, "en_US");
- strcpy(ll, "en");
- return;
- }
+ if (!ppd || !name)
+ return (NULL);
/*
- * Copy the locale name...
+ * Get the default language...
*/
- strlcpy(ll_CC, lang->language, sizeof(ll_CC));
- strlcpy(ll, lang->language, sizeof(ll));
+ ppd_ll_CC(ll_CC, sizeof(ll_CC));
- if (strlen(ll_CC) == 2)
+ /*
+ * Find the localized attribute...
+ */
+
+ if ((locattr = _ppdLocalizedAttr(ppd, "cupsMarkerName", name,
+ ll_CC)) == NULL)
+ locattr = ppdFindAttr(ppd, "cupsMarkerName", name);
+
+ return (locattr ? locattr->text : NULL);
+}
+
+
+/*
+ * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
+ */
+
+void
+_ppdFreeLanguages(
+ cups_array_t *languages) /* I - Languages array */
+{
+ char *language; /* Current language */
+
+
+ for (language = (char *)cupsArrayFirst(languages);
+ language;
+ language = (char *)cupsArrayNext(languages))
+ free(language);
+
+ cupsArrayDelete(languages);
+}
+
+
+/*
+ * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
+ */
+
+cups_array_t * /* O - Languages array */
+_ppdGetLanguages(ppd_file_t *ppd) /* I - PPD file */
+{
+ cups_array_t *languages; /* Languages array */
+ ppd_attr_t *attr; /* cupsLanguages attribute */
+ char *value, /* Copy of attribute value */
+ *start, /* Start of current language */
+ *ptr; /* Pointer into languages */
+
+
+ /*
+ * See if we have a cupsLanguages attribute...
+ */
+
+ if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) == NULL || !attr->value)
+ return (NULL);
+
+ /*
+ * Yes, load the list...
+ */
+
+ if ((languages = cupsArrayNew((cups_array_func_t)strcmp, NULL)) == NULL)
+ return (NULL);
+
+ if ((value = strdup(attr->value)) == NULL)
+ {
+ cupsArrayDelete(languages);
+ return (NULL);
+ }
+
+ for (ptr = value; *ptr;)
{
/*
- * Map "ll" to primary/origin country locales to have the best
- * chance of finding a match...
+ * Skip leading whitespace...
*/
- if (!strcmp(ll_CC, "cs"))
- strcpy(ll_CC, "cs_CZ");
- else if (!strcmp(ll_CC, "en"))
- strcpy(ll_CC, "en_US");
- else if (!strcmp(ll_CC, "ja"))
- strcpy(ll_CC, "ja_JP");
- else if (!strcmp(ll_CC, "sv"))
- strcpy(ll_CC, "sv_SE");
- else if (!strcmp(ll_CC, "zh"))
- strcpy(ll_CC, "zh_CN"); /* Simplified Chinese */
- else
- {
- ll_CC[2] = '_';
- ll_CC[3] = toupper(ll_CC[0] & 255);
- ll_CC[4] = toupper(ll_CC[1] & 255);
- ll_CC[5] = '\0';
- }
+ while (_cups_isspace(*ptr))
+ ptr ++;
+
+ if (!*ptr)
+ break;
+
+ /*
+ * Find the end of this language name...
+ */
+
+ for (start = ptr; *ptr && !_cups_isspace(*ptr); ptr ++);
+
+ if (*ptr)
+ *ptr++ = '\0';
+
+ if (!strcmp(start, "en"))
+ continue;
+
+ cupsArrayAdd(languages, strdup(start));
+ }
+
+ /*
+ * Free the temporary string and return either an array with one or more
+ * values or a NULL pointer...
+ */
+
+ free(value);
+
+ if (cupsArrayCount(languages) == 0)
+ {
+ cupsArrayDelete(languages);
+ return (NULL);
}
+ else
+ return (languages);
+}
+
+
+/*
+ * '_ppdHashName()' - Generate a hash value for a device or profile name.
+ *
+ * This function is primarily used on Mac OS X, but is generally accessible
+ * since cupstestppd needs to check for profile name collisions in PPD files...
+ */
+
+unsigned /* O - Hash value */
+_ppdHashName(const char *name) /* I - Name to hash */
+{
+ int mult; /* Multiplier */
+ unsigned hash = 0; /* Hash value */
- DEBUG_printf(("ppd_ll_CC: lang->language=\"%s\", ll=\"%s\", ll_CC=\"%s\"...\n",
- lang->language, ll, ll_CC));
+
+ for (mult = 1; *name && mult <= 128; mult ++, name ++)
+ hash += (*name & 255) * mult;
+
+ return (hash);
}
/*
- * 'ppd_localized_attr()' - Find a localized attribute.
+ * '_ppdLocalizedAttr()' - Find a localized attribute.
*/
-static ppd_attr_t * /* O - Localized attribute or NULL */
-ppd_localized_attr(ppd_file_t *ppd, /* I - PPD file */
- const char *keyword, /* I - Main keyword */
- const char *spec, /* I - Option keyword */
- const char *ll_CC, /* I - Language + country locale */
- const char *ll) /* I - Language locale */
+ppd_attr_t * /* O - Localized attribute or NULL */
+_ppdLocalizedAttr(ppd_file_t *ppd, /* I - PPD file */
+ const char *keyword, /* I - Main keyword */
+ const char *spec, /* I - Option keyword */
+ const char *ll_CC) /* I - Language + country locale */
{
char lkeyword[PPD_MAX_NAME]; /* Localization keyword */
ppd_attr_t *attr; /* Current attribute */
- DEBUG_printf(("ppd_text(ppd=%p, keyword=\"%s\", spec=\"%s\", "
- "ll_CC=\"%s\", ll=\"%s\")\n",
- ppd, keyword, spec, ll_CC, ll));
+ DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
+ "ll_CC=\"%s\")", ppd, keyword, spec, ll_CC));
/*
* Look for Keyword.ll_CC, then Keyword.ll...
snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
{
- snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll, keyword);
+ snprintf(lkeyword, sizeof(lkeyword), "%2.2s.%s", ll_CC, keyword);
attr = ppdFindAttr(ppd, lkeyword, spec);
- if (!attr && !strcmp(ll, "ja"))
+ if (!attr)
{
- /*
- * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
- * PPD files were incorrectly assigned "jp" as the locale name
- * instead of "ja". Support both the old (incorrect) and new
- * locale names for Japanese...
- */
+ if (!strncmp(ll_CC, "ja", 2))
+ {
+ /*
+ * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
+ * PPD files were incorrectly assigned "jp" as the locale name
+ * instead of "ja". Support both the old (incorrect) and new
+ * locale names for Japanese...
+ */
+
+ snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
+ attr = ppdFindAttr(ppd, lkeyword, spec);
+ }
+ else if (!strncmp(ll_CC, "no", 2))
+ {
+ /*
+ * Norway has two languages, "Bokmal" (the primary one)
+ * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
+ * recommended by the locale folks...
+ */
- snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
- attr = ppdFindAttr(ppd, lkeyword, spec);
+ snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
+ attr = ppdFindAttr(ppd, lkeyword, spec);
+ }
}
}
#ifdef DEBUG
if (attr)
- printf(" *%s %s/%s: \"%s\"\n", attr->name, attr->spec, attr->text,
- attr->value ? attr->value : "");
+ DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr->name,
+ attr->spec, attr->text, attr->value ? attr->value : ""));
else
- puts(" NOT FOUND");
+ DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
#endif /* DEBUG */
return (attr);
/*
- * End of "$Id: localize.c 6686 2007-07-16 23:11:59Z mike $".
+ * 'ppd_ll_CC()' - Get the current locale names.
+ */
+
+static cups_lang_t * /* O - Current language */
+ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
+ int ll_CC_size) /* I - Size of country-specific name */
+{
+ cups_lang_t *lang; /* Current language */
+
+
+ /*
+ * Get the current locale...
+ */
+
+ if ((lang = cupsLangDefault()) == NULL)
+ {
+ strlcpy(ll_CC, "en_US", ll_CC_size);
+ return (NULL);
+ }
+
+ /*
+ * Copy the locale name...
+ */
+
+ strlcpy(ll_CC, lang->language, ll_CC_size);
+
+ if (strlen(ll_CC) == 2)
+ {
+ /*
+ * Map "ll" to primary/origin country locales to have the best
+ * chance of finding a match...
+ */
+
+ if (!strcmp(ll_CC, "cs"))
+ strlcpy(ll_CC, "cs_CZ", ll_CC_size);
+ else if (!strcmp(ll_CC, "en"))
+ strlcpy(ll_CC, "en_US", ll_CC_size);
+ else if (!strcmp(ll_CC, "ja"))
+ strlcpy(ll_CC, "ja_JP", ll_CC_size);
+ else if (!strcmp(ll_CC, "sv"))
+ strlcpy(ll_CC, "sv_SE", ll_CC_size);
+ else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
+ strlcpy(ll_CC, "zh_CN", ll_CC_size);
+ }
+
+ DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
+ lang->language, ll_CC));
+ return (lang);
+}
+
+
+/*
+ * End of "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $".
*/