|
|
/*
|
|
|
* PPD file routines for CUPS.
|
|
|
*
|
|
|
* Copyright © 2007-2019 by Apple Inc.
|
|
|
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
* information.
|
|
|
*
|
|
|
* PostScript is a trademark of Adobe Systems, Inc.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers.
|
|
|
*/
|
|
|
|
|
|
#include "cups-private.h"
|
|
|
#include "ppd-private.h"
|
|
|
#include "debug-internal.h"
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Definitions...
|
|
|
*/
|
|
|
|
|
|
#define PPD_KEYWORD 1 /* Line contained a keyword */
|
|
|
#define PPD_OPTION 2 /* Line contained an option name */
|
|
|
#define PPD_TEXT 4 /* Line contained human-readable text */
|
|
|
#define PPD_STRING 8 /* Line contained a string or code */
|
|
|
|
|
|
#define PPD_HASHSIZE 512 /* Size of hash */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Line buffer structure...
|
|
|
*/
|
|
|
|
|
|
typedef struct _ppd_line_s
|
|
|
{
|
|
|
char *buffer; /* Pointer to buffer */
|
|
|
size_t bufsize; /* Size of the buffer */
|
|
|
} _ppd_line_t;
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local globals...
|
|
|
*/
|
|
|
|
|
|
static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER;
|
|
|
/* Thread local storage key */
|
|
|
#ifdef HAVE_PTHREAD_H
|
|
|
static pthread_once_t ppd_globals_key_once = PTHREAD_ONCE_INIT;
|
|
|
/* One-time initialization object */
|
|
|
#endif /* HAVE_PTHREAD_H */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local functions...
|
|
|
*/
|
|
|
|
|
|
static ppd_attr_t *ppd_add_attr(ppd_file_t *ppd, const char *name,
|
|
|
const char *spec, const char *text,
|
|
|
const char *value);
|
|
|
static ppd_choice_t *ppd_add_choice(ppd_option_t *option, const char *name);
|
|
|
static ppd_size_t *ppd_add_size(ppd_file_t *ppd, const char *name);
|
|
|
static int ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
|
|
|
static int ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
|
|
|
static int ppd_compare_coptions(ppd_coption_t *a,
|
|
|
ppd_coption_t *b);
|
|
|
static int ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
|
|
|
static int ppd_decode(char *string);
|
|
|
static void ppd_free_filters(ppd_file_t *ppd);
|
|
|
static void ppd_free_group(ppd_group_t *group);
|
|
|
static void ppd_free_option(ppd_option_t *option);
|
|
|
static ppd_coption_t *ppd_get_coption(ppd_file_t *ppd, const char *name);
|
|
|
static ppd_cparam_t *ppd_get_cparam(ppd_coption_t *opt,
|
|
|
const char *param,
|
|
|
const char *text);
|
|
|
static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name,
|
|
|
const char *text, _ppd_globals_t *pg,
|
|
|
cups_encoding_t encoding);
|
|
|
static ppd_option_t *ppd_get_option(ppd_group_t *group, const char *name);
|
|
|
static _ppd_globals_t *ppd_globals_alloc(void);
|
|
|
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
|
|
|
static void ppd_globals_free(_ppd_globals_t *g);
|
|
|
#endif /* HAVE_PTHREAD_H || _WIN32 */
|
|
|
#ifdef HAVE_PTHREAD_H
|
|
|
static void ppd_globals_init(void);
|
|
|
#endif /* HAVE_PTHREAD_H */
|
|
|
static int ppd_hash_option(ppd_option_t *option);
|
|
|
static int ppd_read(cups_file_t *fp, _ppd_line_t *line,
|
|
|
char *keyword, char *option, char *text,
|
|
|
char **string, int ignoreblank,
|
|
|
_ppd_globals_t *pg);
|
|
|
static int ppd_update_filters(ppd_file_t *ppd,
|
|
|
_ppd_globals_t *pg);
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdClose()' - Free all memory used by the PPD file.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
ppdClose(ppd_file_t *ppd) /* I - PPD file record */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
ppd_group_t *group; /* Current group */
|
|
|
char **font; /* Current font */
|
|
|
ppd_attr_t **attr; /* Current attribute */
|
|
|
ppd_coption_t *coption; /* Current custom option */
|
|
|
ppd_cparam_t *cparam; /* Current custom parameter */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Range check arguments...
|
|
|
*/
|
|
|
|
|
|
if (!ppd)
|
|
|
return;
|
|
|
|
|
|
/*
|
|
|
* Free all strings at the top level...
|
|
|
*/
|
|
|
|
|
|
free(ppd->lang_encoding);
|
|
|
free(ppd->nickname);
|
|
|
free(ppd->patches);
|
|
|
free(ppd->jcl_begin);
|
|
|
free(ppd->jcl_end);
|
|
|
free(ppd->jcl_ps);
|
|
|
|
|
|
/*
|
|
|
* Free any UI groups, subgroups, and options...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_groups > 0)
|
|
|
{
|
|
|
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
|
|
|
ppd_free_group(group);
|
|
|
|
|
|
free(ppd->groups);
|
|
|
}
|
|
|
|
|
|
cupsArrayDelete(ppd->options);
|
|
|
cupsArrayDelete(ppd->marked);
|
|
|
|
|
|
/*
|
|
|
* Free any page sizes...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_sizes > 0)
|
|
|
free(ppd->sizes);
|
|
|
|
|
|
/*
|
|
|
* Free any constraints...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_consts > 0)
|
|
|
free(ppd->consts);
|
|
|
|
|
|
/*
|
|
|
* Free any filters...
|
|
|
*/
|
|
|
|
|
|
ppd_free_filters(ppd);
|
|
|
|
|
|
/*
|
|
|
* Free any fonts...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_fonts > 0)
|
|
|
{
|
|
|
for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
|
|
|
free(*font);
|
|
|
|
|
|
free(ppd->fonts);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Free any profiles...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_profiles > 0)
|
|
|
free(ppd->profiles);
|
|
|
|
|
|
/*
|
|
|
* Free any attributes...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_attrs > 0)
|
|
|
{
|
|
|
for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
|
|
|
{
|
|
|
free((*attr)->value);
|
|
|
free(*attr);
|
|
|
}
|
|
|
|
|
|
free(ppd->attrs);
|
|
|
}
|
|
|
|
|
|
cupsArrayDelete(ppd->sorted_attrs);
|
|
|
|
|
|
/*
|
|
|
* Free custom options...
|
|
|
*/
|
|
|
|
|
|
for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
|
|
|
coption;
|
|
|
coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
|
|
|
{
|
|
|
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
|
|
|
cparam;
|
|
|
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
|
|
|
{
|
|
|
switch (cparam->type)
|
|
|
{
|
|
|
case PPD_CUSTOM_PASSCODE :
|
|
|
case PPD_CUSTOM_PASSWORD :
|
|
|
case PPD_CUSTOM_STRING :
|
|
|
free(cparam->current.custom_string);
|
|
|
break;
|
|
|
|
|
|
default :
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
free(cparam);
|
|
|
}
|
|
|
|
|
|
cupsArrayDelete(coption->params);
|
|
|
|
|
|
free(coption);
|
|
|
}
|
|
|
|
|
|
cupsArrayDelete(ppd->coptions);
|
|
|
|
|
|
/*
|
|
|
* Free constraints...
|
|
|
*/
|
|
|
|
|
|
if (ppd->cups_uiconstraints)
|
|
|
{
|
|
|
_ppd_cups_uiconsts_t *consts; /* Current constraints */
|
|
|
|
|
|
|
|
|
for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
|
|
|
consts;
|
|
|
consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
|
|
|
{
|
|
|
free(consts->constraints);
|
|
|
free(consts);
|
|
|
}
|
|
|
|
|
|
cupsArrayDelete(ppd->cups_uiconstraints);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Free any PPD cache/mapping data...
|
|
|
*/
|
|
|
|
|
|
if (ppd->cache)
|
|
|
_ppdCacheDestroy(ppd->cache);
|
|
|
|
|
|
/*
|
|
|
* Free the whole record...
|
|
|
*/
|
|
|
|
|
|
free(ppd);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdErrorString()' - Returns the text associated with a status.
|
|
|
*
|
|
|
* @since CUPS 1.1.19/macOS 10.3@
|
|
|
*/
|
|
|
|
|
|
const char * /* O - Status string */
|
|
|
ppdErrorString(ppd_status_t status) /* I - PPD status */
|
|
|
{
|
|
|
static const char * const messages[] =/* Status messages */
|
|
|
{
|
|
|
_("OK"),
|
|
|
_("Unable to open PPD file"),
|
|
|
_("NULL PPD file pointer"),
|
|
|
_("Memory allocation error"),
|
|
|
_("Missing PPD-Adobe-4.x header"),
|
|
|
_("Missing value string"),
|
|
|
_("Internal error"),
|
|
|
_("Bad OpenGroup"),
|
|
|
_("OpenGroup without a CloseGroup first"),
|
|
|
_("Bad OpenUI/JCLOpenUI"),
|
|
|
_("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
|
|
|
_("Bad OrderDependency"),
|
|
|
_("Bad UIConstraints"),
|
|
|
_("Missing asterisk in column 1"),
|
|
|
_("Line longer than the maximum allowed (255 characters)"),
|
|
|
_("Illegal control character"),
|
|
|
_("Illegal main keyword string"),
|
|
|
_("Illegal option keyword string"),
|
|
|
_("Illegal translation string"),
|
|
|
_("Illegal whitespace character"),
|
|
|
_("Bad custom parameter"),
|
|
|
_("Missing option keyword"),
|
|
|
_("Bad value string"),
|
|
|
_("Missing CloseGroup"),
|
|
|
_("Bad CloseUI/JCLCloseUI"),
|
|
|
_("Missing CloseUI/JCLCloseUI")
|
|
|
};
|
|
|
|
|
|
|
|
|
if (status < PPD_OK || status >= PPD_MAX_STATUS)
|
|
|
return (_cupsLangString(cupsLangDefault(), _("Unknown")));
|
|
|
else
|
|
|
return (_cupsLangString(cupsLangDefault(), messages[status]));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '_ppdGetEncoding()' - Get the CUPS encoding value for the given
|
|
|
* LanguageEncoding.
|
|
|
*/
|
|
|
|
|
|
cups_encoding_t /* O - CUPS encoding value */
|
|
|
_ppdGetEncoding(const char *name) /* I - LanguageEncoding string */
|
|
|
{
|
|
|
if (!_cups_strcasecmp(name, "ISOLatin1"))
|
|
|
return (CUPS_ISO8859_1);
|
|
|
else if (!_cups_strcasecmp(name, "ISOLatin2"))
|
|
|
return (CUPS_ISO8859_2);
|
|
|
else if (!_cups_strcasecmp(name, "ISOLatin5"))
|
|
|
return (CUPS_ISO8859_5);
|
|
|
else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
|
|
|
return (CUPS_JIS_X0213);
|
|
|
else if (!_cups_strcasecmp(name, "MacStandard"))
|
|
|
return (CUPS_MAC_ROMAN);
|
|
|
else if (!_cups_strcasecmp(name, "WindowsANSI"))
|
|
|
return (CUPS_WINDOWS_1252);
|
|
|
else
|
|
|
return (CUPS_UTF8);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '_ppdGlobals()' - Return a pointer to thread local storage
|
|
|
*/
|
|
|
|
|
|
_ppd_globals_t * /* O - Pointer to global data */
|
|
|
_ppdGlobals(void)
|
|
|
{
|
|
|
_ppd_globals_t *pg; /* Pointer to global data */
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD_H
|
|
|
/*
|
|
|
* Initialize the global data exactly once...
|
|
|
*/
|
|
|
|
|
|
pthread_once(&ppd_globals_key_once, ppd_globals_init);
|
|
|
#endif /* HAVE_PTHREAD_H */
|
|
|
|
|
|
/*
|
|
|
* See if we have allocated the data yet...
|
|
|
*/
|
|
|
|
|
|
if ((pg = (_ppd_globals_t *)_cupsThreadGetData(ppd_globals_key)) == NULL)
|
|
|
{
|
|
|
/*
|
|
|
* No, allocate memory as set the pointer for the key...
|
|
|
*/
|
|
|
|
|
|
if ((pg = ppd_globals_alloc()) != NULL)
|
|
|
_cupsThreadSetData(ppd_globals_key, pg);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Return the pointer to the data...
|
|
|
*/
|
|
|
|
|
|
return (pg);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdLastError()' - Return the status from the last ppdOpen*().
|
|
|
*
|
|
|
* @since CUPS 1.1.19/macOS 10.3@
|
|
|
*/
|
|
|
|
|
|
ppd_status_t /* O - Status code */
|
|
|
ppdLastError(int *line) /* O - Line number */
|
|
|
{
|
|
|
_ppd_globals_t *pg = _ppdGlobals();
|
|
|
/* Global data */
|
|
|
|
|
|
|
|
|
if (line)
|
|
|
*line = pg->ppd_line;
|
|
|
|
|
|
return (pg->ppd_status);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '_ppdOpen()' - Read a PPD file into memory.
|
|
|
*
|
|
|
* @since CUPS 1.2/macOS 10.5@
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
|
|
|
_ppdOpen(
|
|
|
cups_file_t *fp, /* I - File to read from */
|
|
|
_ppd_localization_t localization) /* I - Localization to load */
|
|
|
{
|
|
|
int i, j, k; /* Looping vars */
|
|
|
_ppd_line_t line; /* Line buffer */
|
|
|
ppd_file_t *ppd; /* PPD file record */
|
|
|
ppd_group_t *group, /* Current group */
|
|
|
*subgroup; /* Current sub-group */
|
|
|
ppd_option_t *option; /* Current option */
|
|
|
ppd_choice_t *choice; /* Current choice */
|
|
|
ppd_const_t *constraint; /* Current constraint */
|
|
|
ppd_size_t *size; /* Current page size */
|
|
|
int mask; /* Line data mask */
|
|
|
char keyword[PPD_MAX_NAME],
|
|
|
/* Keyword from file */
|
|
|
name[PPD_MAX_NAME],
|
|
|
/* Option from file */
|
|
|
text[PPD_MAX_LINE],
|
|
|
/* Human-readable text from file */
|
|
|
*string, /* Code/text from file */
|
|
|
*sptr, /* Pointer into string */
|
|
|
*temp, /* Temporary string pointer */
|
|
|
**tempfonts; /* Temporary fonts pointer */
|
|
|
float order; /* Order dependency number */
|
|
|
ppd_section_t section; /* Order dependency section */
|
|
|
ppd_profile_t *profile; /* Pointer to color profile */
|
|
|
char **filter; /* Pointer to filter */
|
|
|
struct lconv *loc; /* Locale data */
|
|
|
int ui_keyword; /* Is this line a UI keyword? */
|
|
|
cups_lang_t *lang; /* Language data */
|
|
|
cups_encoding_t encoding; /* Encoding of PPD file */
|
|
|
_ppd_globals_t *pg = _ppdGlobals();
|
|
|
/* Global data */
|
|
|
char custom_name[PPD_MAX_NAME];
|
|
|
/* CustomFoo attribute name */
|
|
|
ppd_attr_t *custom_attr; /* CustomFoo attribute */
|
|
|
char ll[7], /* Base language + '.' */
|
|
|
ll_CC[7]; /* Language w/country + '.' */
|
|
|
size_t ll_len = 0, /* Base language length */
|
|
|
ll_CC_len = 0; /* Language w/country length */
|
|
|
static const char * const ui_keywords[] =
|
|
|
{
|
|
|
#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
|
|
|
/*
|
|
|
* Adobe defines some 41 keywords as "UI", meaning that they are
|
|
|
* user interface elements and that they should be treated as such
|
|
|
* even if the PPD creator doesn't use Open/CloseUI around them.
|
|
|
*
|
|
|
* Since this can cause previously invisible options to appear and
|
|
|
* confuse users, the default is to only treat the PageSize and
|
|
|
* PageRegion keywords this way.
|
|
|
*/
|
|
|
/* Boolean keywords */
|
|
|
"BlackSubstitution",
|
|
|
"Booklet",
|
|
|
"Collate",
|
|
|
"ManualFeed",
|
|
|
"MirrorPrint",
|
|
|
"NegativePrint",
|
|
|
"Sorter",
|
|
|
"TraySwitch",
|
|
|
|
|
|
/* PickOne keywords */
|
|
|
"AdvanceMedia",
|
|
|
"BindColor",
|
|
|
"BindEdge",
|
|
|
"BindType",
|
|
|
"BindWhen",
|
|
|
"BitsPerPixel",
|
|
|
"ColorModel",
|
|
|
"CutMedia",
|
|
|
"Duplex",
|
|
|
"FoldType",
|
|
|
"FoldWhen",
|
|
|
"InputSlot",
|
|
|
"JCLFrameBufferSize",
|
|
|
"JCLResolution",
|
|
|
"Jog",
|
|
|
"MediaColor",
|
|
|
"MediaType",
|
|
|
"MediaWeight",
|
|
|
"OutputBin",
|
|
|
"OutputMode",
|
|
|
"OutputOrder",
|
|
|
"PageRegion",
|
|
|
"PageSize",
|
|
|
"Resolution",
|
|
|
"Separations",
|
|
|
"Signature",
|
|
|
"Slipsheet",
|
|
|
"Smoothing",
|
|
|
"StapleLocation",
|
|
|
"StapleOrientation",
|
|
|
"StapleWhen",
|
|
|
"StapleX",
|
|
|
"StapleY"
|
|
|
#else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
|
|
|
"PageRegion",
|
|
|
"PageSize"
|
|
|
#endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
|
|
|
};
|
|
|
static const char * const color_keywords[] = /* Keywords associated with color profiles */
|
|
|
{
|
|
|
".cupsICCProfile",
|
|
|
".ColorModel",
|
|
|
};
|
|
|
|
|
|
|
|
|
DEBUG_printf(("_ppdOpen(fp=%p)", fp));
|
|
|
|
|
|
/*
|
|
|
* Default to "OK" status...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_OK;
|
|
|
pg->ppd_line = 0;
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (fp == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NULL_FILE;
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* If only loading a single localization set up the strings to match...
|
|
|
*/
|
|
|
|
|
|
if (localization == _PPD_LOCALIZATION_DEFAULT)
|
|
|
{
|
|
|
if ((lang = cupsLangDefault()) == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
|
|
|
|
|
|
/*
|
|
|
* <rdar://problem/22130168>
|
|
|
* <rdar://problem/27245567>
|
|
|
*
|
|
|
* Need to use a different base language for some locales...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(lang->language, "zh_HK"))
|
|
|
{ /* Traditional Chinese + variants */
|
|
|
strlcpy(ll_CC, "zh_TW.", sizeof(ll_CC));
|
|
|
strlcpy(ll, "zh_", sizeof(ll));
|
|
|
}
|
|
|
else if (!strncmp(lang->language, "zh", 2))
|
|
|
strlcpy(ll, "zh_", sizeof(ll)); /* Any Chinese variant */
|
|
|
else if (!strncmp(lang->language, "jp", 2))
|
|
|
{ /* Any Japanese variant */
|
|
|
strlcpy(ll_CC, "ja", sizeof(ll_CC));
|
|
|
strlcpy(ll, "jp", sizeof(ll));
|
|
|
}
|
|
|
else if (!strncmp(lang->language, "nb", 2) || !strncmp(lang->language, "no", 2))
|
|
|
{ /* Any Norwegian variant */
|
|
|
strlcpy(ll_CC, "nb", sizeof(ll_CC));
|
|
|
strlcpy(ll, "no", sizeof(ll));
|
|
|
}
|
|
|
else
|
|
|
snprintf(ll, sizeof(ll), "%2.2s.", lang->language);
|
|
|
|
|
|
ll_CC_len = strlen(ll_CC);
|
|
|
ll_len = strlen(ll);
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
|
|
|
ll_CC, ll));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
|
|
|
*/
|
|
|
|
|
|
line.buffer = NULL;
|
|
|
line.bufsize = 0;
|
|
|
|
|
|
mask = ppd_read(fp, &line, keyword, name, text, &string, 0, pg);
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword));
|
|
|
|
|
|
if (mask == 0 ||
|
|
|
strcmp(keyword, "PPD-Adobe") ||
|
|
|
string == NULL || string[0] != '4')
|
|
|
{
|
|
|
/*
|
|
|
* Either this is not a PPD file, or it is not a 4.x PPD file.
|
|
|
*/
|
|
|
|
|
|
if (pg->ppd_status == PPD_OK)
|
|
|
pg->ppd_status = PPD_MISSING_PPDADOBE4;
|
|
|
|
|
|
free(string);
|
|
|
free(line.buffer);
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
|
|
|
|
|
|
/*
|
|
|
* Allocate memory for the PPD file record...
|
|
|
*/
|
|
|
|
|
|
if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
free(string);
|
|
|
free(line.buffer);
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
|
|
|
ppd->language_level = 2;
|
|
|
ppd->color_device = 0;
|
|
|
ppd->colorspace = PPD_CS_N;
|
|
|
ppd->landscape = -90;
|
|
|
ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions, NULL);
|
|
|
|
|
|
/*
|
|
|
* Read lines from the PPD file and add them to the file record...
|
|
|
*/
|
|
|
|
|
|
group = NULL;
|
|
|
subgroup = NULL;
|
|
|
option = NULL;
|
|
|
choice = NULL;
|
|
|
ui_keyword = 0;
|
|
|
encoding = CUPS_ISO8859_1;
|
|
|
loc = localeconv();
|
|
|
|
|
|
while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, pg)) != 0)
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
|
|
|
"text=\"%s\", string=%d chars...", mask, keyword, name, text,
|
|
|
string ? (int)strlen(string) : 0));
|
|
|
|
|
|
if (strncmp(keyword, "Default", 7) && !string &&
|
|
|
pg->ppd_conform != PPD_CONFORM_RELAXED)
|
|
|
{
|
|
|
/*
|
|
|
* Need a string value!
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_MISSING_VALUE;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
else if (!string)
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
|
* Certain main keywords (as defined by the PPD spec) may be used
|
|
|
* without the usual OpenUI/CloseUI stuff. Presumably this is just
|
|
|
* so that Adobe wouldn't completely break compatibility with PPD
|
|
|
* files prior to v4.0 of the spec, but it is hopelessly
|
|
|
* inconsistent... Catch these main keywords and automatically
|
|
|
* create the corresponding option, as needed...
|
|
|
*/
|
|
|
|
|
|
if (ui_keyword)
|
|
|
{
|
|
|
/*
|
|
|
* Previous line was a UI keyword...
|
|
|
*/
|
|
|
|
|
|
option = NULL;
|
|
|
ui_keyword = 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* If we are filtering out keyword localizations, see if this line needs to
|
|
|
* be used...
|
|
|
*/
|
|
|
|
|
|
if (localization != _PPD_LOCALIZATION_ALL &&
|
|
|
(temp = strchr(keyword, '.')) != NULL &&
|
|
|
((temp - keyword) == 2 || (temp - keyword) == 5) &&
|
|
|
_cups_isalpha(keyword[0]) &&
|
|
|
_cups_isalpha(keyword[1]) &&
|
|
|
(keyword[2] == '.' ||
|
|
|
(keyword[2] == '_' && _cups_isalpha(keyword[3]) &&
|
|
|
_cups_isalpha(keyword[4]) && keyword[5] == '.')))
|
|
|
{
|
|
|
if (localization == _PPD_LOCALIZATION_NONE ||
|
|
|
(localization == _PPD_LOCALIZATION_DEFAULT &&
|
|
|
strncmp(ll_CC, keyword, ll_CC_len) &&
|
|
|
strncmp(ll, keyword, ll_len)))
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
continue;
|
|
|
}
|
|
|
else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
|
|
|
{
|
|
|
/*
|
|
|
* Only load localizations for the color profile related keywords...
|
|
|
*/
|
|
|
|
|
|
for (i = 0;
|
|
|
i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
|
|
|
i ++)
|
|
|
{
|
|
|
if (!_cups_strcasecmp(temp, color_keywords[i]))
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (option == NULL &&
|
|
|
(mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
|
|
|
(PPD_KEYWORD | PPD_OPTION | PPD_STRING))
|
|
|
{
|
|
|
for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
|
|
|
if (!strcmp(keyword, ui_keywords[i]))
|
|
|
break;
|
|
|
|
|
|
if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
|
|
|
{
|
|
|
/*
|
|
|
* Create the option in the appropriate group...
|
|
|
*/
|
|
|
|
|
|
ui_keyword = 1;
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
|
|
|
keyword));
|
|
|
|
|
|
if (!group)
|
|
|
{
|
|
|
if ((group = ppd_get_group(ppd, "General", _("General"), pg,
|
|
|
encoding)) == NULL)
|
|
|
goto error;
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
|
|
|
option = ppd_get_option(group, keyword);
|
|
|
group = NULL;
|
|
|
}
|
|
|
else
|
|
|
option = ppd_get_option(group, keyword);
|
|
|
|
|
|
if (option == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now fill in the initial information for the option...
|
|
|
*/
|
|
|
|
|
|
if (!strncmp(keyword, "JCL", 3))
|
|
|
option->section = PPD_ORDER_JCL;
|
|
|
else
|
|
|
option->section = PPD_ORDER_ANY;
|
|
|
|
|
|
option->order = 10.0f;
|
|
|
|
|
|
if (i < 8)
|
|
|
option->ui = PPD_UI_BOOLEAN;
|
|
|
else
|
|
|
option->ui = PPD_UI_PICKONE;
|
|
|
|
|
|
for (j = 0; j < ppd->num_attrs; j ++)
|
|
|
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
|
|
|
!strcmp(ppd->attrs[j]->name + 7, keyword) &&
|
|
|
ppd->attrs[j]->value)
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
|
|
|
option->keyword, ppd->attrs[j]->value));
|
|
|
strlcpy(option->defchoice, ppd->attrs[j]->value,
|
|
|
sizeof(option->defchoice));
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (!strcmp(keyword, "PageSize"))
|
|
|
strlcpy(option->text, _("Media Size"), sizeof(option->text));
|
|
|
else if (!strcmp(keyword, "MediaType"))
|
|
|
strlcpy(option->text, _("Media Type"), sizeof(option->text));
|
|
|
else if (!strcmp(keyword, "InputSlot"))
|
|
|
strlcpy(option->text, _("Media Source"), sizeof(option->text));
|
|
|
else if (!strcmp(keyword, "ColorModel"))
|
|
|
strlcpy(option->text, _("Output Mode"), sizeof(option->text));
|
|
|
else if (!strcmp(keyword, "Resolution"))
|
|
|
strlcpy(option->text, _("Resolution"), sizeof(option->text));
|
|
|
else
|
|
|
strlcpy(option->text, keyword, sizeof(option->text));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!strcmp(keyword, "LanguageLevel"))
|
|
|
ppd->language_level = atoi(string);
|
|
|
else if (!strcmp(keyword, "LanguageEncoding"))
|
|
|
{
|
|
|
/*
|
|
|
* Say all PPD files are UTF-8, since we convert to UTF-8...
|
|
|
*/
|
|
|
|
|
|
ppd->lang_encoding = strdup("UTF-8");
|
|
|
encoding = _ppdGetEncoding(string);
|
|
|
}
|
|
|
else if (!strcmp(keyword, "LanguageVersion"))
|
|
|
ppd->lang_version = string;
|
|
|
else if (!strcmp(keyword, "Manufacturer"))
|
|
|
ppd->manufacturer = string;
|
|
|
else if (!strcmp(keyword, "ModelName"))
|
|
|
ppd->modelname = string;
|
|
|
else if (!strcmp(keyword, "Protocols"))
|
|
|
ppd->protocols = string;
|
|
|
else if (!strcmp(keyword, "PCFileName"))
|
|
|
ppd->pcfilename = string;
|
|
|
else if (!strcmp(keyword, "NickName"))
|
|
|
{
|
|
|
if (encoding != CUPS_UTF8)
|
|
|
{
|
|
|
cups_utf8_t utf8[256]; /* UTF-8 version of NickName */
|
|
|
|
|
|
|
|
|
cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
|
|
|
ppd->nickname = strdup((char *)utf8);
|
|
|
}
|
|
|
else
|
|
|
ppd->nickname = strdup(string);
|
|
|
}
|
|
|
else if (!strcmp(keyword, "Product"))
|
|
|
ppd->product = string;
|
|
|
else if (!strcmp(keyword, "ShortNickName"))
|
|
|
ppd->shortnickname = string;
|
|
|
else if (!strcmp(keyword, "TTRasterizer"))
|
|
|
ppd->ttrasterizer = string;
|
|
|
else if (!strcmp(keyword, "JCLBegin"))
|
|
|
{
|
|
|
ppd->jcl_begin = strdup(string);
|
|
|
ppd_decode(ppd->jcl_begin); /* Decode quoted string */
|
|
|
}
|
|
|
else if (!strcmp(keyword, "JCLEnd"))
|
|
|
{
|
|
|
ppd->jcl_end = strdup(string);
|
|
|
ppd_decode(ppd->jcl_end); /* Decode quoted string */
|
|
|
}
|
|
|
else if (!strcmp(keyword, "JCLToPSInterpreter"))
|
|
|
{
|
|
|
ppd->jcl_ps = strdup(string);
|
|
|
ppd_decode(ppd->jcl_ps); /* Decode quoted string */
|
|
|
}
|
|
|
else if (!strcmp(keyword, "AccurateScreensSupport"))
|
|
|
ppd->accurate_screens = !strcmp(string, "True");
|
|
|
else if (!strcmp(keyword, "ColorDevice"))
|
|
|
ppd->color_device = !strcmp(string, "True");
|
|
|
else if (!strcmp(keyword, "ContoneOnly"))
|
|
|
ppd->contone_only = !strcmp(string, "True");
|
|
|
else if (!strcmp(keyword, "cupsFlipDuplex"))
|
|
|
ppd->flip_duplex = !strcmp(string, "True");
|
|
|
else if (!strcmp(keyword, "cupsManualCopies"))
|
|
|
ppd->manual_copies = !strcmp(string, "True");
|
|
|
else if (!strcmp(keyword, "cupsModelNumber"))
|
|
|
ppd->model_number = atoi(string);
|
|
|
else if (!strcmp(keyword, "cupsColorProfile"))
|
|
|
{
|
|
|
if (ppd->num_profiles == 0)
|
|
|
profile = malloc(sizeof(ppd_profile_t));
|
|
|
else
|
|
|
profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * (size_t)(ppd->num_profiles + 1));
|
|
|
|
|
|
if (!profile)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
ppd->profiles = profile;
|
|
|
profile += ppd->num_profiles;
|
|
|
ppd->num_profiles ++;
|
|
|
|
|
|
memset(profile, 0, sizeof(ppd_profile_t));
|
|
|
strlcpy(profile->resolution, name, sizeof(profile->resolution));
|
|
|
strlcpy(profile->media_type, text, sizeof(profile->media_type));
|
|
|
|
|
|
profile->density = (float)_cupsStrScand(string, &sptr, loc);
|
|
|
profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
}
|
|
|
else if (!strcmp(keyword, "cupsFilter"))
|
|
|
{
|
|
|
if (ppd->num_filters == 0)
|
|
|
filter = malloc(sizeof(char *));
|
|
|
else
|
|
|
filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
|
|
|
|
|
|
if (filter == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
ppd->filters = filter;
|
|
|
filter += ppd->num_filters;
|
|
|
ppd->num_filters ++;
|
|
|
|
|
|
/*
|
|
|
* Make a copy of the filter string...
|
|
|
*/
|
|
|
|
|
|
*filter = strdup(string);
|
|
|
}
|
|
|
else if (!strcmp(keyword, "Throughput"))
|
|
|
ppd->throughput = atoi(string);
|
|
|
else if (!strcmp(keyword, "Font"))
|
|
|
{
|
|
|
/*
|
|
|
* Add this font to the list of available fonts...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_fonts == 0)
|
|
|
tempfonts = (char **)malloc(sizeof(char *));
|
|
|
else
|
|
|
tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * (size_t)(ppd->num_fonts + 1));
|
|
|
|
|
|
if (tempfonts == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
ppd->fonts = tempfonts;
|
|
|
ppd->fonts[ppd->num_fonts] = strdup(name);
|
|
|
ppd->num_fonts ++;
|
|
|
}
|
|
|
else if (!strncmp(keyword, "ParamCustom", 11))
|
|
|
{
|
|
|
ppd_coption_t *coption; /* Custom option */
|
|
|
ppd_cparam_t *cparam; /* Custom parameter */
|
|
|
int corder; /* Order number */
|
|
|
char ctype[33], /* Data type */
|
|
|
cminimum[65], /* Minimum value */
|
|
|
cmaximum[65]; /* Maximum value */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Get the custom option and parameter...
|
|
|
*/
|
|
|
|
|
|
if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (cparam->type != PPD_CUSTOM_UNKNOWN)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Get the parameter data...
|
|
|
*/
|
|
|
|
|
|
if (!string ||
|
|
|
sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
|
|
|
cmaximum) != 4)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
cparam->order = corder;
|
|
|
|
|
|
if (!strcmp(ctype, "curve"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_CURVE;
|
|
|
cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
|
|
|
cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "int"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_INT;
|
|
|
cparam->minimum.custom_int = atoi(cminimum);
|
|
|
cparam->maximum.custom_int = atoi(cmaximum);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "invcurve"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_INVCURVE;
|
|
|
cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
|
|
|
cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "passcode"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_PASSCODE;
|
|
|
cparam->minimum.custom_passcode = atoi(cminimum);
|
|
|
cparam->maximum.custom_passcode = atoi(cmaximum);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "password"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_PASSWORD;
|
|
|
cparam->minimum.custom_password = atoi(cminimum);
|
|
|
cparam->maximum.custom_password = atoi(cmaximum);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "points"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_POINTS;
|
|
|
cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
|
|
|
cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "real"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_REAL;
|
|
|
cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
|
|
|
cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
|
|
|
}
|
|
|
else if (!strcmp(ctype, "string"))
|
|
|
{
|
|
|
cparam->type = PPD_CUSTOM_STRING;
|
|
|
cparam->minimum.custom_string = atoi(cminimum);
|
|
|
cparam->maximum.custom_string = atoi(cmaximum);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now special-case for CustomPageSize...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(coption->keyword, "PageSize"))
|
|
|
{
|
|
|
if (!strcmp(name, "Width"))
|
|
|
{
|
|
|
ppd->custom_min[0] = cparam->minimum.custom_points;
|
|
|
ppd->custom_max[0] = cparam->maximum.custom_points;
|
|
|
}
|
|
|
else if (!strcmp(name, "Height"))
|
|
|
{
|
|
|
ppd->custom_min[1] = cparam->minimum.custom_points;
|
|
|
ppd->custom_max[1] = cparam->maximum.custom_points;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "HWMargins"))
|
|
|
{
|
|
|
for (i = 0, sptr = string; i < 4; i ++)
|
|
|
ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
}
|
|
|
else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
|
|
|
{
|
|
|
ppd_option_t *custom_option; /* Custom option */
|
|
|
|
|
|
DEBUG_puts("2_ppdOpen: Processing Custom option...");
|
|
|
|
|
|
/*
|
|
|
* Get the option and custom option...
|
|
|
*/
|
|
|
|
|
|
if (!ppd_get_coption(ppd, keyword + 6))
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
|
|
|
custom_option = option;
|
|
|
else
|
|
|
custom_option = ppdFindOption(ppd, keyword + 6);
|
|
|
|
|
|
if (custom_option)
|
|
|
{
|
|
|
/*
|
|
|
* Add the "custom" option...
|
|
|
*/
|
|
|
|
|
|
if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
|
|
|
if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
|
|
|
{
|
|
|
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
strlcpy(choice->text, text[0] ? text : _("Custom"),
|
|
|
sizeof(choice->text));
|
|
|
|
|
|
choice->code = strdup(string);
|
|
|
|
|
|
if (custom_option->section == PPD_ORDER_JCL)
|
|
|
ppd_decode(choice->code);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now process custom page sizes specially...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(keyword, "CustomPageSize"))
|
|
|
{
|
|
|
/*
|
|
|
* Add a "Custom" page size entry...
|
|
|
*/
|
|
|
|
|
|
ppd->variable_sizes = 1;
|
|
|
|
|
|
ppd_add_size(ppd, "Custom");
|
|
|
|
|
|
if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
|
|
|
custom_option = option;
|
|
|
else
|
|
|
custom_option = ppdFindOption(ppd, "PageRegion");
|
|
|
|
|
|
if (custom_option)
|
|
|
{
|
|
|
if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
|
|
|
if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
|
|
|
{
|
|
|
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
strlcpy(choice->text, text[0] ? text : _("Custom"),
|
|
|
sizeof(choice->text));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "LandscapeOrientation"))
|
|
|
{
|
|
|
if (!strcmp(string, "Minus90"))
|
|
|
ppd->landscape = -90;
|
|
|
else if (!strcmp(string, "Plus90"))
|
|
|
ppd->landscape = 90;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "Emulators") && string && ppd->num_emulations == 0)
|
|
|
{
|
|
|
/*
|
|
|
* Issue #5562: Samsung printer drivers incorrectly use Emulators keyword
|
|
|
* to configure themselves
|
|
|
*
|
|
|
* The Emulators keyword was loaded but never used by anything in CUPS,
|
|
|
* and has no valid purpose in CUPS. The old code was removed due to a
|
|
|
* memory leak (Issue #5475), so the following (new) code supports a single
|
|
|
* name for the Emulators keyword, allowing these drivers to work until we
|
|
|
* remove PPD and driver support entirely in a future version of CUPS.
|
|
|
*/
|
|
|
|
|
|
ppd->num_emulations = 1;
|
|
|
ppd->emulations = calloc(1, sizeof(ppd_emul_t));
|
|
|
|
|
|
strlcpy(ppd->emulations[0].name, string, sizeof(ppd->emulations[0].name));
|
|
|
}
|
|
|
else if (!strcmp(keyword, "JobPatchFile"))
|
|
|
{
|
|
|
/*
|
|
|
* CUPS STR #3421: Check for "*JobPatchFile: int: string"
|
|
|
*/
|
|
|
|
|
|
if (isdigit(*string & 255))
|
|
|
{
|
|
|
for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
|
|
|
|
|
|
if (*sptr == ':')
|
|
|
{
|
|
|
/*
|
|
|
* Found "*JobPatchFile: int: string"...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_BAD_VALUE;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
/*
|
|
|
* Found "*JobPatchFile: string"...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (ppd->patches == NULL)
|
|
|
ppd->patches = strdup(string);
|
|
|
else
|
|
|
{
|
|
|
temp = realloc(ppd->patches, strlen(ppd->patches) +
|
|
|
strlen(string) + 1);
|
|
|
if (temp == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
ppd->patches = temp;
|
|
|
|
|
|
memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "OpenUI"))
|
|
|
{
|
|
|
/*
|
|
|
* Don't allow nesting of options...
|
|
|
*/
|
|
|
|
|
|
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NESTED_OPEN_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Add an option record to the current sub-group, group, or file...
|
|
|
*/
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
|
|
|
|
|
|
if (name[0] == '*')
|
|
|
_cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
|
|
|
|
|
|
for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
|
|
|
name[i] = '\0'; /* Eliminate trailing spaces */
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
|
|
|
group ? group->text : "(null)"));
|
|
|
|
|
|
if (subgroup != NULL)
|
|
|
option = ppd_get_option(subgroup, name);
|
|
|
else if (group == NULL)
|
|
|
{
|
|
|
if ((group = ppd_get_group(ppd, "General", _("General"), pg,
|
|
|
encoding)) == NULL)
|
|
|
goto error;
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
|
|
|
option = ppd_get_option(group, name);
|
|
|
group = NULL;
|
|
|
}
|
|
|
else
|
|
|
option = ppd_get_option(group, name);
|
|
|
|
|
|
if (option == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now fill in the initial information for the option...
|
|
|
*/
|
|
|
|
|
|
if (string && !strcmp(string, "PickMany"))
|
|
|
option->ui = PPD_UI_PICKMANY;
|
|
|
else if (string && !strcmp(string, "Boolean"))
|
|
|
option->ui = PPD_UI_BOOLEAN;
|
|
|
else if (string && !strcmp(string, "PickOne"))
|
|
|
option->ui = PPD_UI_PICKONE;
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_OPEN_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
else
|
|
|
option->ui = PPD_UI_PICKONE;
|
|
|
|
|
|
for (j = 0; j < ppd->num_attrs; j ++)
|
|
|
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
|
|
|
!strcmp(ppd->attrs[j]->name + 7, name) &&
|
|
|
ppd->attrs[j]->value)
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
|
|
|
option->keyword, ppd->attrs[j]->value));
|
|
|
strlcpy(option->defchoice, ppd->attrs[j]->value,
|
|
|
sizeof(option->defchoice));
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (text[0])
|
|
|
cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
|
|
|
sizeof(option->text), encoding);
|
|
|
else
|
|
|
{
|
|
|
if (!strcmp(name, "PageSize"))
|
|
|
strlcpy(option->text, _("Media Size"), sizeof(option->text));
|
|
|
else if (!strcmp(name, "MediaType"))
|
|
|
strlcpy(option->text, _("Media Type"), sizeof(option->text));
|
|
|
else if (!strcmp(name, "InputSlot"))
|
|
|
strlcpy(option->text, _("Media Source"), sizeof(option->text));
|
|
|
else if (!strcmp(name, "ColorModel"))
|
|
|
strlcpy(option->text, _("Output Mode"), sizeof(option->text));
|
|
|
else if (!strcmp(name, "Resolution"))
|
|
|
strlcpy(option->text, _("Resolution"), sizeof(option->text));
|
|
|
else
|
|
|
strlcpy(option->text, name, sizeof(option->text));
|
|
|
}
|
|
|
|
|
|
option->section = PPD_ORDER_ANY;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
|
|
|
/*
|
|
|
* Add a custom option choice if we have already seen a CustomFoo
|
|
|
* attribute...
|
|
|
*/
|
|
|
|
|
|
if (!_cups_strcasecmp(name, "PageRegion"))
|
|
|
strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
|
|
|
else
|
|
|
snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
|
|
|
|
|
|
if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
|
|
|
{
|
|
|
if ((choice = ppdFindChoice(option, "Custom")) == NULL)
|
|
|
if ((choice = ppd_add_choice(option, "Custom")) == NULL)
|
|
|
{
|
|
|
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
strlcpy(choice->text,
|
|
|
custom_attr->text[0] ? custom_attr->text : _("Custom"),
|
|
|
sizeof(choice->text));
|
|
|
choice->code = strdup(custom_attr->value);
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "JCLOpenUI"))
|
|
|
{
|
|
|
/*
|
|
|
* Don't allow nesting of options...
|
|
|
*/
|
|
|
|
|
|
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NESTED_OPEN_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Find the JCL group, and add if needed...
|
|
|
*/
|
|
|
|
|
|
group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
|
|
|
|
|
|
if (group == NULL)
|
|
|
goto error;
|
|
|
|
|
|
/*
|
|
|
* Add an option record to the current JCLs...
|
|
|
*/
|
|
|
|
|
|
if (name[0] == '*')
|
|
|
_cups_strcpy(name, name + 1);
|
|
|
|
|
|
option = ppd_get_option(group, name);
|
|
|
|
|
|
if (option == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now fill in the initial information for the option...
|
|
|
*/
|
|
|
|
|
|
if (string && !strcmp(string, "PickMany"))
|
|
|
option->ui = PPD_UI_PICKMANY;
|
|
|
else if (string && !strcmp(string, "Boolean"))
|
|
|
option->ui = PPD_UI_BOOLEAN;
|
|
|
else if (string && !strcmp(string, "PickOne"))
|
|
|
option->ui = PPD_UI_PICKONE;
|
|
|
else
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_OPEN_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
for (j = 0; j < ppd->num_attrs; j ++)
|
|
|
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
|
|
|
!strcmp(ppd->attrs[j]->name + 7, name) &&
|
|
|
ppd->attrs[j]->value)
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
|
|
|
option->keyword, ppd->attrs[j]->value));
|
|
|
strlcpy(option->defchoice, ppd->attrs[j]->value,
|
|
|
sizeof(option->defchoice));
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (text[0])
|
|
|
cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
|
|
|
sizeof(option->text), encoding);
|
|
|
else
|
|
|
strlcpy(option->text, name, sizeof(option->text));
|
|
|
|
|
|
option->section = PPD_ORDER_JCL;
|
|
|
group = NULL;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
|
|
|
/*
|
|
|
* Add a custom option choice if we have already seen a CustomFoo
|
|
|
* attribute...
|
|
|
*/
|
|
|
|
|
|
snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
|
|
|
|
|
|
if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
|
|
|
{
|
|
|
if ((choice = ppd_add_choice(option, "Custom")) == NULL)
|
|
|
{
|
|
|
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
strlcpy(choice->text,
|
|
|
custom_attr->text[0] ? custom_attr->text : _("Custom"),
|
|
|
sizeof(choice->text));
|
|
|
choice->code = strdup(custom_attr->value);
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "CloseUI"))
|
|
|
{
|
|
|
if ((!option || option->section == PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_CLOSE_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
option = NULL;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "JCLCloseUI"))
|
|
|
{
|
|
|
if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_CLOSE_UI;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
option = NULL;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "OpenGroup"))
|
|
|
{
|
|
|
/*
|
|
|
* Open a new group...
|
|
|
*/
|
|
|
|
|
|
if (group != NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NESTED_OPEN_GROUP;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (!string)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_OPEN_GROUP;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Separate the group name from the text (name/text)...
|
|
|
*/
|
|
|
|
|
|
if ((sptr = strchr(string, '/')) != NULL)
|
|
|
*sptr++ = '\0';
|
|
|
else
|
|
|
sptr = string;
|
|
|
|
|
|
/*
|
|
|
* Fix up the text...
|
|
|
*/
|
|
|
|
|
|
ppd_decode(sptr);
|
|
|
|
|
|
/*
|
|
|
* Find/add the group...
|
|
|
*/
|
|
|
|
|
|
group = ppd_get_group(ppd, string, sptr, pg, encoding);
|
|
|
|
|
|
if (group == NULL)
|
|
|
goto error;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "CloseGroup"))
|
|
|
{
|
|
|
group = NULL;
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "OrderDependency"))
|
|
|
{
|
|
|
order = (float)_cupsStrScand(string, &sptr, loc);
|
|
|
|
|
|
if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (keyword[0] == '*')
|
|
|
_cups_strcpy(keyword, keyword + 1);
|
|
|
|
|
|
if (!strcmp(name, "ExitServer"))
|
|
|
section = PPD_ORDER_EXIT;
|
|
|
else if (!strcmp(name, "Prolog"))
|
|
|
section = PPD_ORDER_PROLOG;
|
|
|
else if (!strcmp(name, "DocumentSetup"))
|
|
|
section = PPD_ORDER_DOCUMENT;
|
|
|
else if (!strcmp(name, "PageSetup"))
|
|
|
section = PPD_ORDER_PAGE;
|
|
|
else if (!strcmp(name, "JCLSetup"))
|
|
|
section = PPD_ORDER_JCL;
|
|
|
else
|
|
|
section = PPD_ORDER_ANY;
|
|
|
|
|
|
if (option == NULL)
|
|
|
{
|
|
|
ppd_group_t *gtemp;
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Only valid for Non-UI options...
|
|
|
*/
|
|
|
|
|
|
for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
|
|
|
if (gtemp->text[0] == '\0')
|
|
|
break;
|
|
|
|
|
|
if (i > 0)
|
|
|
for (i = 0; i < gtemp->num_options; i ++)
|
|
|
if (!strcmp(keyword, gtemp->options[i].keyword))
|
|
|
{
|
|
|
gtemp->options[i].section = section;
|
|
|
gtemp->options[i].order = order;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
option->section = section;
|
|
|
option->order = order;
|
|
|
}
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strncmp(keyword, "Default", 7))
|
|
|
{
|
|
|
if (string == NULL)
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
|
* Drop UI text, if any, from value...
|
|
|
*/
|
|
|
|
|
|
if (strchr(string, '/') != NULL)
|
|
|
*strchr(string, '/') = '\0';
|
|
|
|
|
|
/*
|
|
|
* Assign the default value as appropriate...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(keyword, "DefaultColorSpace"))
|
|
|
{
|
|
|
/*
|
|
|
* Set default colorspace...
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(string, "CMY"))
|
|
|
ppd->colorspace = PPD_CS_CMY;
|
|
|
else if (!strcmp(string, "CMYK"))
|
|
|
ppd->colorspace = PPD_CS_CMYK;
|
|
|
else if (!strcmp(string, "RGB"))
|
|
|
ppd->colorspace = PPD_CS_RGB;
|
|
|
else if (!strcmp(string, "RGBK"))
|
|
|
ppd->colorspace = PPD_CS_RGBK;
|
|
|
else if (!strcmp(string, "N"))
|
|
|
ppd->colorspace = PPD_CS_N;
|
|
|
else
|
|
|
ppd->colorspace = PPD_CS_GRAY;
|
|
|
}
|
|
|
else if (option && !strcmp(keyword + 7, option->keyword))
|
|
|
{
|
|
|
/*
|
|
|
* Set the default as part of the current option...
|
|
|
*/
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
|
|
|
|
|
|
strlcpy(option->defchoice, string, sizeof(option->defchoice));
|
|
|
|
|
|
DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/*
|
|
|
* Lookup option and set if it has been defined...
|
|
|
*/
|
|
|
|
|
|
ppd_option_t *toption; /* Temporary option */
|
|
|
|
|
|
|
|
|
if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
|
|
|
strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (!strcmp(keyword, "UIConstraints") ||
|
|
|
!strcmp(keyword, "NonUIConstraints"))
|
|
|
{
|
|
|
if (!string)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (ppd->num_consts == 0)
|
|
|
constraint = calloc(2, sizeof(ppd_const_t));
|
|
|
else
|
|
|
constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
|
|
|
|
|
|
if (constraint == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
ppd->consts = constraint;
|
|
|
constraint += ppd->num_consts;
|
|
|
ppd->num_consts ++;
|
|
|
|
|
|
switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
|
|
|
constraint->choice1, constraint->option2,
|
|
|
constraint->choice2))
|
|
|
{
|
|
|
default : /* Error */
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
|
|
|
case 2 : /* Two options... */
|
|
|
/*
|
|
|
* Check for broken constraints like "* Option"...
|
|
|
*/
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
(!strcmp(constraint->option1, "*") ||
|
|
|
!strcmp(constraint->choice1, "*")))
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* The following strcpy's are safe, as optionN and
|
|
|
* choiceN are all the same size (size defined by PPD spec...)
|
|
|
*/
|
|
|
|
|
|
if (constraint->option1[0] == '*')
|
|
|
_cups_strcpy(constraint->option1, constraint->option1 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (constraint->choice1[0] == '*')
|
|
|
_cups_strcpy(constraint->option2, constraint->choice1 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
constraint->choice1[0] = '\0';
|
|
|
constraint->choice2[0] = '\0';
|
|
|
break;
|
|
|
|
|
|
case 3 : /* Two options, one choice... */
|
|
|
/*
|
|
|
* Check for broken constraints like "* Option"...
|
|
|
*/
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
(!strcmp(constraint->option1, "*") ||
|
|
|
!strcmp(constraint->choice1, "*") ||
|
|
|
!strcmp(constraint->option2, "*")))
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* The following _cups_strcpy's are safe, as optionN and
|
|
|
* choiceN are all the same size (size defined by PPD spec...)
|
|
|
*/
|
|
|
|
|
|
if (constraint->option1[0] == '*')
|
|
|
_cups_strcpy(constraint->option1, constraint->option1 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (constraint->choice1[0] == '*')
|
|
|
{
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
constraint->option2[0] == '*')
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
_cups_strcpy(constraint->choice2, constraint->option2);
|
|
|
_cups_strcpy(constraint->option2, constraint->choice1 + 1);
|
|
|
constraint->choice1[0] = '\0';
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (constraint->option2[0] == '*')
|
|
|
_cups_strcpy(constraint->option2, constraint->option2 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
constraint->choice2[0] = '\0';
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case 4 : /* Two options, two choices... */
|
|
|
/*
|
|
|
* Check for broken constraints like "* Option"...
|
|
|
*/
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
(!strcmp(constraint->option1, "*") ||
|
|
|
!strcmp(constraint->choice1, "*") ||
|
|
|
!strcmp(constraint->option2, "*") ||
|
|
|
!strcmp(constraint->choice2, "*")))
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (constraint->option1[0] == '*')
|
|
|
_cups_strcpy(constraint->option1, constraint->option1 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
constraint->choice1[0] == '*')
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (constraint->option2[0] == '*')
|
|
|
_cups_strcpy(constraint->option2, constraint->option2 + 1);
|
|
|
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
|
|
|
constraint->choice2[0] == '*')
|
|
|
{
|
|
|
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
|
|
|
goto error;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Don't add this one as an attribute...
|
|
|
*/
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "PaperDimension"))
|
|
|
{
|
|
|
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
|
|
|
{
|
|
|
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
|
|
|
snprintf(cname, sizeof(cname), "_%s", name);
|
|
|
strlcpy(name, cname, sizeof(name));
|
|
|
}
|
|
|
|
|
|
if ((size = ppdPageSize(ppd, name)) == NULL)
|
|
|
size = ppd_add_size(ppd, name);
|
|
|
|
|
|
if (size == NULL)
|
|
|
{
|
|
|
/*
|
|
|
* Unable to add or find size!
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
size->width = (float)_cupsStrScand(string, &sptr, loc);
|
|
|
size->length = (float)_cupsStrScand(sptr, NULL, loc);
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (!strcmp(keyword, "ImageableArea"))
|
|
|
{
|
|
|
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
|
|
|
{
|
|
|
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
|
|
|
snprintf(cname, sizeof(cname), "_%s", name);
|
|
|
strlcpy(name, cname, sizeof(name));
|
|
|
}
|
|
|
|
|
|
if ((size = ppdPageSize(ppd, name)) == NULL)
|
|
|
size = ppd_add_size(ppd, name);
|
|
|
|
|
|
if (size == NULL)
|
|
|
{
|
|
|
/*
|
|
|
* Unable to add or find size!
|
|
|
*/
|
|
|
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
size->left = (float)_cupsStrScand(string, &sptr, loc);
|
|
|
size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
size->right = (float)_cupsStrScand(sptr, &sptr, loc);
|
|
|
size->top = (float)_cupsStrScand(sptr, NULL, loc);
|
|
|
|
|
|
free(string);
|
|
|
string = NULL;
|
|
|
}
|
|
|
else if (option != NULL &&
|
|
|
(mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
|
|
|
(PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
|
|
|
!strcmp(keyword, option->keyword))
|
|
|
{
|
|
|
DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
|
|
|
|
|
|
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
|
|
|
{
|
|
|
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
|
|
|
snprintf(cname, sizeof(cname), "_%s", name);
|
|
|
strlcpy(name, cname, sizeof(name));
|
|
|
}
|
|
|
|
|
|
if (!strcmp(keyword, "PageSize"))
|
|
|
{
|
|
|
/*
|
|
|
* Add a page size...
|
|
|
*/
|
|
|
|
|
|
if (ppdPageSize(ppd, name) == NULL)
|
|
|
ppd_add_size(ppd, name);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Add the option choice...
|
|
|
*/
|
|
|
|
|
|
if ((choice = ppd_add_choice(option, name)) == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
if (text[0])
|
|
|
cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
|
|
|
sizeof(choice->text), encoding);
|
|
|
else if (!strcmp(name, "True"))
|
|
|
strlcpy(choice->text, _("Yes"), sizeof(choice->text));
|
|
|
else if (!strcmp(name, "False"))
|
|
|
strlcpy(choice->text, _("No"), sizeof(choice->text));
|
|
|
else
|
|
|
strlcpy(choice->text, name, sizeof(choice->text));
|
|
|
|
|
|
if (option->section == PPD_ORDER_JCL)
|
|
|
ppd_decode(string); /* Decode quoted string */
|
|
|
|
|
|
choice->code = string;
|
|
|
string = NULL; /* Don't add as an attribute below */
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Add remaining lines with keywords and string values as attributes...
|
|
|
*/
|
|
|
|
|
|
if (string &&
|
|
|
(mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
|
|
|
ppd_add_attr(ppd, keyword, name, text, string);
|
|
|
else
|
|
|
free(string);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Check for a missing CloseUI/JCLCloseUI...
|
|
|
*/
|
|
|
|
|
|
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_MISSING_CLOSE_UI;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Check for a missing CloseGroup...
|
|
|
*/
|
|
|
|
|
|
if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
free(line.buffer);
|
|
|
|
|
|
/*
|
|
|
* Reset language preferences...
|
|
|
*/
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
if (!cupsFileEOF(fp))
|
|
|
DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
|
|
|
(unsigned long)cupsFileTell(fp)));
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
if (pg->ppd_status != PPD_OK)
|
|
|
{
|
|
|
/*
|
|
|
* Had an error reading the PPD file, cannot continue!
|
|
|
*/
|
|
|
|
|
|
ppdClose(ppd);
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Update the filters array as needed...
|
|
|
*/
|
|
|
|
|
|
if (!ppd_update_filters(ppd, pg))
|
|
|
{
|
|
|
ppdClose(ppd);
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Create the sorted options array and set the option back-pointer for
|
|
|
* each choice and custom option...
|
|
|
*/
|
|
|
|
|
|
ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
|
|
|
(cups_ahash_func_t)ppd_hash_option,
|
|
|
PPD_HASHSIZE);
|
|
|
|
|
|
for (i = ppd->num_groups, group = ppd->groups;
|
|
|
i > 0;
|
|
|
i --, group ++)
|
|
|
{
|
|
|
for (j = group->num_options, option = group->options;
|
|
|
j > 0;
|
|
|
j --, option ++)
|
|
|
{
|
|
|
ppd_coption_t *coption; /* Custom option */
|
|
|
|
|
|
|
|
|
cupsArrayAdd(ppd->options, option);
|
|
|
|
|
|
for (k = 0; k < option->num_choices; k ++)
|
|
|
option->choices[k].option = option;
|
|
|
|
|
|
if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
|
|
|
coption->option = option;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Create an array to track the marked choices...
|
|
|
*/
|
|
|
|
|
|
ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
|
|
|
|
|
|
/*
|
|
|
* Return the PPD file structure...
|
|
|
*/
|
|
|
|
|
|
return (ppd);
|
|
|
|
|
|
/*
|
|
|
* Common exit point for errors to save code size...
|
|
|
*/
|
|
|
|
|
|
error:
|
|
|
|
|
|
free(string);
|
|
|
free(line.buffer);
|
|
|
|
|
|
ppdClose(ppd);
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdOpen()' - Read a PPD file into memory.
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record */
|
|
|
ppdOpen(FILE *fp) /* I - File to read from */
|
|
|
{
|
|
|
ppd_file_t *ppd; /* PPD file record */
|
|
|
cups_file_t *cf; /* CUPS file */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Reopen the stdio file as a CUPS file...
|
|
|
*/
|
|
|
|
|
|
if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
/*
|
|
|
* Load the PPD file using the newer API...
|
|
|
*/
|
|
|
|
|
|
ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
|
|
|
|
|
|
/*
|
|
|
* Close the CUPS file and return the PPD...
|
|
|
*/
|
|
|
|
|
|
cupsFileClose(cf);
|
|
|
|
|
|
return (ppd);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdOpen2()' - Read a PPD file into memory.
|
|
|
*
|
|
|
* @since CUPS 1.2/macOS 10.5@
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
|
|
|
ppdOpen2(cups_file_t *fp) /* I - File to read from */
|
|
|
{
|
|
|
return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdOpenFd()' - Read a PPD file into memory.
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
|
|
|
ppdOpenFd(int fd) /* I - File to read from */
|
|
|
{
|
|
|
cups_file_t *fp; /* CUPS file pointer */
|
|
|
ppd_file_t *ppd; /* PPD file record */
|
|
|
_ppd_globals_t *pg = _ppdGlobals();
|
|
|
/* Global data */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Set the line number to 0...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = 0;
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (fd < 0)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NULL_FILE;
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Try to open the file and parse it...
|
|
|
*/
|
|
|
|
|
|
if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
|
|
|
{
|
|
|
ppd = ppdOpen2(fp);
|
|
|
|
|
|
cupsFileClose(fp);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pg->ppd_status = PPD_FILE_OPEN_ERROR;
|
|
|
ppd = NULL;
|
|
|
}
|
|
|
|
|
|
return (ppd);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '_ppdOpenFile()' - Read a PPD file into memory.
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
|
|
|
_ppdOpenFile(const char *filename, /* I - File to read from */
|
|
|
_ppd_localization_t localization) /* I - Localization to load */
|
|
|
{
|
|
|
cups_file_t *fp; /* File pointer */
|
|
|
ppd_file_t *ppd; /* PPD file record */
|
|
|
_ppd_globals_t *pg = _ppdGlobals();
|
|
|
/* Global data */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Set the line number to 0...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = 0;
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (filename == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_NULL_FILE;
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Try to open the file and parse it...
|
|
|
*/
|
|
|
|
|
|
if ((fp = cupsFileOpen(filename, "r")) != NULL)
|
|
|
{
|
|
|
ppd = _ppdOpen(fp, localization);
|
|
|
|
|
|
cupsFileClose(fp);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pg->ppd_status = PPD_FILE_OPEN_ERROR;
|
|
|
ppd = NULL;
|
|
|
}
|
|
|
|
|
|
return (ppd);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdOpenFile()' - Read a PPD file into memory.
|
|
|
*/
|
|
|
|
|
|
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
|
|
|
ppdOpenFile(const char *filename) /* I - File to read from */
|
|
|
{
|
|
|
return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppdSetConformance()' - Set the conformance level for PPD files.
|
|
|
*
|
|
|
* @since CUPS 1.1.20/macOS 10.4@
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
|
|
|
{
|
|
|
_ppd_globals_t *pg = _ppdGlobals();
|
|
|
/* Global data */
|
|
|
|
|
|
|
|
|
pg->ppd_conform = c;
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_add_attr()' - Add an attribute to the PPD data.
|
|
|
*/
|
|
|
|
|
|
static ppd_attr_t * /* O - New attribute */
|
|
|
ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
|
|
|
const char *name, /* I - Attribute name */
|
|
|
const char *spec, /* I - Specifier string, if any */
|
|
|
const char *text, /* I - Text string, if any */
|
|
|
const char *value) /* I - Value of attribute */
|
|
|
{
|
|
|
ppd_attr_t **ptr, /* New array */
|
|
|
*temp; /* New attribute */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (ppd == NULL || name == NULL || spec == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
/*
|
|
|
* Create the array as needed...
|
|
|
*/
|
|
|
|
|
|
if (!ppd->sorted_attrs)
|
|
|
ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
|
|
|
NULL);
|
|
|
|
|
|
/*
|
|
|
* Allocate memory for the new attribute...
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_attrs == 0)
|
|
|
ptr = malloc(sizeof(ppd_attr_t *));
|
|
|
else
|
|
|
ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
|
|
|
|
|
|
if (ptr == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
ppd->attrs = ptr;
|
|
|
ptr += ppd->num_attrs;
|
|
|
|
|
|
if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
*ptr = temp;
|
|
|
|
|
|
ppd->num_attrs ++;
|
|
|
|
|
|
/*
|
|
|
* Copy data over...
|
|
|
*/
|
|
|
|
|
|
strlcpy(temp->name, name, sizeof(temp->name));
|
|
|
strlcpy(temp->spec, spec, sizeof(temp->spec));
|
|
|
strlcpy(temp->text, text, sizeof(temp->text));
|
|
|
temp->value = (char *)value;
|
|
|
|
|
|
/*
|
|
|
* Add the attribute to the sorted array...
|
|
|
*/
|
|
|
|
|
|
cupsArrayAdd(ppd->sorted_attrs, temp);
|
|
|
|
|
|
/*
|
|
|
* Return the attribute...
|
|
|
*/
|
|
|
|
|
|
return (temp);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_add_choice()' - Add a choice to an option.
|
|
|
*/
|
|
|
|
|
|
static ppd_choice_t * /* O - Named choice */
|
|
|
ppd_add_choice(ppd_option_t *option, /* I - Option */
|
|
|
const char *name) /* I - Name of choice */
|
|
|
{
|
|
|
ppd_choice_t *choice; /* Choice */
|
|
|
|
|
|
|
|
|
if (option->num_choices == 0)
|
|
|
choice = malloc(sizeof(ppd_choice_t));
|
|
|
else
|
|
|
choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
|
|
|
|
|
|
if (choice == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
option->choices = choice;
|
|
|
choice += option->num_choices;
|
|
|
option->num_choices ++;
|
|
|
|
|
|
memset(choice, 0, sizeof(ppd_choice_t));
|
|
|
strlcpy(choice->choice, name, sizeof(choice->choice));
|
|
|
|
|
|
return (choice);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_add_size()' - Add a page size.
|
|
|
*/
|
|
|
|
|
|
static ppd_size_t * /* O - Named size */
|
|
|
ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
|
|
|
const char *name) /* I - Name of size */
|
|
|
{
|
|
|
ppd_size_t *size; /* Size */
|
|
|
|
|
|
|
|
|
if (ppd->num_sizes == 0)
|
|
|
size = malloc(sizeof(ppd_size_t));
|
|
|
else
|
|
|
size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
|
|
|
|
|
|
if (size == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
ppd->sizes = size;
|
|
|
size += ppd->num_sizes;
|
|
|
ppd->num_sizes ++;
|
|
|
|
|
|
memset(size, 0, sizeof(ppd_size_t));
|
|
|
strlcpy(size->name, name, sizeof(size->name));
|
|
|
|
|
|
return (size);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_compare_attrs()' - Compare two attributes.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
|
|
|
ppd_attr_t *b) /* I - Second attribute */
|
|
|
{
|
|
|
return (_cups_strcasecmp(a->name, b->name));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_compare_choices()' - Compare two choices...
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
|
|
|
ppd_choice_t *b) /* I - Second choice */
|
|
|
{
|
|
|
return (strcmp(a->option->keyword, b->option->keyword));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_compare_coptions()' - Compare two custom options.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
|
|
|
ppd_coption_t *b) /* I - Second option */
|
|
|
{
|
|
|
return (_cups_strcasecmp(a->keyword, b->keyword));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_compare_options()' - Compare two options.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
ppd_compare_options(ppd_option_t *a, /* I - First option */
|
|
|
ppd_option_t *b) /* I - Second option */
|
|
|
{
|
|
|
return (_cups_strcasecmp(a->keyword, b->keyword));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_decode()' - Decode a string value...
|
|
|
*/
|
|
|
|
|
|
static int /* O - Length of decoded string */
|
|
|
ppd_decode(char *string) /* I - String to decode */
|
|
|
{
|
|
|
char *inptr, /* Input pointer */
|
|
|
*outptr; /* Output pointer */
|
|
|
|
|
|
|
|
|
inptr = string;
|
|
|
outptr = string;
|
|
|
|
|
|
while (*inptr != '\0')
|
|
|
if (*inptr == '<' && isxdigit(inptr[1] & 255))
|
|
|
{
|
|
|
/*
|
|
|
* Convert hex to 8-bit values...
|
|
|
*/
|
|
|
|
|
|
inptr ++;
|
|
|
while (isxdigit(*inptr & 255))
|
|
|
{
|
|
|
if (_cups_isalpha(*inptr))
|
|
|
*outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
|
|
|
else
|
|
|
*outptr = (char)((*inptr - '0') << 4);
|
|
|
|
|
|
inptr ++;
|
|
|
|
|
|
if (!isxdigit(*inptr & 255))
|
|
|
break;
|
|
|
|
|
|
if (_cups_isalpha(*inptr))
|
|
|
*outptr |= (char)(tolower(*inptr) - 'a' + 10);
|
|
|
else
|
|
|
*outptr |= (char)(*inptr - '0');
|
|
|
|
|
|
inptr ++;
|
|
|
outptr ++;
|
|
|
}
|
|
|
|
|
|
while (*inptr != '>' && *inptr != '\0')
|
|
|
inptr ++;
|
|
|
while (*inptr == '>')
|
|
|
inptr ++;
|
|
|
}
|
|
|
else
|
|
|
*outptr++ = *inptr++;
|
|
|
|
|
|
*outptr = '\0';
|
|
|
|
|
|
return ((int)(outptr - string));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_free_filters()' - Free the filters array.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
char **filter; /* Current filter */
|
|
|
|
|
|
|
|
|
if (ppd->num_filters > 0)
|
|
|
{
|
|
|
for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
|
|
|
free(*filter);
|
|
|
|
|
|
free(ppd->filters);
|
|
|
|
|
|
ppd->num_filters = 0;
|
|
|
ppd->filters = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_free_group()' - Free a single UI group.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
ppd_free_group(ppd_group_t *group) /* I - Group to free */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
ppd_option_t *option; /* Current option */
|
|
|
ppd_group_t *subgroup; /* Current sub-group */
|
|
|
|
|
|
|
|
|
if (group->num_options > 0)
|
|
|
{
|
|
|
for (i = group->num_options, option = group->options;
|
|
|
i > 0;
|
|
|
i --, option ++)
|
|
|
ppd_free_option(option);
|
|
|
|
|
|
free(group->options);
|
|
|
}
|
|
|
|
|
|
if (group->num_subgroups > 0)
|
|
|
{
|
|
|
for (i = group->num_subgroups, subgroup = group->subgroups;
|
|
|
i > 0;
|
|
|
i --, subgroup ++)
|
|
|
ppd_free_group(subgroup);
|
|
|
|
|
|
free(group->subgroups);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_free_option()' - Free a single option.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
ppd_free_option(ppd_option_t *option) /* I - Option to free */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
ppd_choice_t *choice; /* Current choice */
|
|
|
|
|
|
|
|
|
if (option->num_choices > 0)
|
|
|
{
|
|
|
for (i = option->num_choices, choice = option->choices;
|
|
|
i > 0;
|
|
|
i --, choice ++)
|
|
|
{
|
|
|
free(choice->code);
|
|
|
}
|
|
|
|
|
|
free(option->choices);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_get_coption()' - Get a custom option record.
|
|
|
*/
|
|
|
|
|
|
static ppd_coption_t * /* O - Custom option... */
|
|
|
ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
|
|
|
const char *name) /* I - Name of option */
|
|
|
{
|
|
|
ppd_coption_t *copt; /* New custom option */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* See if the option already exists...
|
|
|
*/
|
|
|
|
|
|
if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
|
|
|
return (copt);
|
|
|
|
|
|
/*
|
|
|
* Not found, so create the custom option record...
|
|
|
*/
|
|
|
|
|
|
if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
strlcpy(copt->keyword, name, sizeof(copt->keyword));
|
|
|
|
|
|
copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
|
|
|
|
|
|
cupsArrayAdd(ppd->coptions, copt);
|
|
|
|
|
|
/*
|
|
|
* Return the new record...
|
|
|
*/
|
|
|
|
|
|
return (copt);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_get_cparam()' - Get a custom parameter record.
|
|
|
*/
|
|
|
|
|
|
static ppd_cparam_t * /* O - Extended option... */
|
|
|
ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
|
|
|
const char *param, /* I - Name of parameter */
|
|
|
const char *text) /* I - Human-readable text */
|
|
|
{
|
|
|
ppd_cparam_t *cparam; /* New custom parameter */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* See if the parameter already exists...
|
|
|
*/
|
|
|
|
|
|
if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
|
|
|
return (cparam);
|
|
|
|
|
|
/*
|
|
|
* Not found, so create the custom parameter record...
|
|
|
*/
|
|
|
|
|
|
if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
cparam->type = PPD_CUSTOM_UNKNOWN;
|
|
|
strlcpy(cparam->name, param, sizeof(cparam->name));
|
|
|
strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
|
|
|
|
|
|
/*
|
|
|
* Add this record to the array...
|
|
|
*/
|
|
|
|
|
|
cupsArrayAdd(opt->params, cparam);
|
|
|
|
|
|
/*
|
|
|
* Return the new record...
|
|
|
*/
|
|
|
|
|
|
return (cparam);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_get_group()' - Find or create the named group as needed.
|
|
|
*/
|
|
|
|
|
|
static ppd_group_t * /* O - Named group */
|
|
|
ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
|
|
|
const char *name, /* I - Name of group */
|
|
|
const char *text, /* I - Text for group */
|
|
|
_ppd_globals_t *pg, /* I - Global data */
|
|
|
cups_encoding_t encoding) /* I - Encoding of text */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
ppd_group_t *group; /* Group */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
|
|
|
ppd, name, text, pg));
|
|
|
|
|
|
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
|
|
|
if (!strcmp(group->name, name))
|
|
|
break;
|
|
|
|
|
|
if (i == 0)
|
|
|
{
|
|
|
DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
|
|
|
|
|
|
if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
if (ppd->num_groups == 0)
|
|
|
group = malloc(sizeof(ppd_group_t));
|
|
|
else
|
|
|
group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
|
|
|
|
|
|
if (group == NULL)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
ppd->groups = group;
|
|
|
group += ppd->num_groups;
|
|
|
ppd->num_groups ++;
|
|
|
|
|
|
memset(group, 0, sizeof(ppd_group_t));
|
|
|
strlcpy(group->name, name, sizeof(group->name));
|
|
|
|
|
|
cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
|
|
|
sizeof(group->text), encoding);
|
|
|
}
|
|
|
|
|
|
return (group);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_get_option()' - Find or create the named option as needed.
|
|
|
*/
|
|
|
|
|
|
static ppd_option_t * /* O - Named option */
|
|
|
ppd_get_option(ppd_group_t *group, /* I - Group */
|
|
|
const char *name) /* I - Name of option */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
ppd_option_t *option; /* Option */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
|
|
|
group, group->name, name));
|
|
|
|
|
|
for (i = group->num_options, option = group->options; i > 0; i --, option ++)
|
|
|
if (!strcmp(option->keyword, name))
|
|
|
break;
|
|
|
|
|
|
if (i == 0)
|
|
|
{
|
|
|
if (group->num_options == 0)
|
|
|
option = malloc(sizeof(ppd_option_t));
|
|
|
else
|
|
|
option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
|
|
|
|
|
|
if (option == NULL)
|
|
|
return (NULL);
|
|
|
|
|
|
group->options = option;
|
|
|
option += group->num_options;
|
|
|
group->num_options ++;
|
|
|
|
|
|
memset(option, 0, sizeof(ppd_option_t));
|
|
|
strlcpy(option->keyword, name, sizeof(option->keyword));
|
|
|
}
|
|
|
|
|
|
return (option);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_globals_alloc()' - Allocate and initialize global data.
|
|
|
*/
|
|
|
|
|
|
static _ppd_globals_t * /* O - Pointer to global data */
|
|
|
ppd_globals_alloc(void)
|
|
|
{
|
|
|
return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_globals_free()' - Free global data.
|
|
|
*/
|
|
|
|
|
|
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
|
|
|
static void
|
|
|
ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */
|
|
|
{
|
|
|
free(pg);
|
|
|
}
|
|
|
#endif /* HAVE_PTHREAD_H || _WIN32 */
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD_H
|
|
|
/*
|
|
|
* 'ppd_globals_init()' - Initialize per-thread globals...
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
ppd_globals_init(void)
|
|
|
{
|
|
|
/*
|
|
|
* Register the global data for this thread...
|
|
|
*/
|
|
|
|
|
|
pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
|
|
|
}
|
|
|
#endif /* HAVE_PTHREAD_H */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_hash_option()' - Generate a hash of the option name...
|
|
|
*/
|
|
|
|
|
|
static int /* O - Hash index */
|
|
|
ppd_hash_option(ppd_option_t *option) /* I - Option */
|
|
|
{
|
|
|
int hash = 0; /* Hash index */
|
|
|
const char *k; /* Pointer into keyword */
|
|
|
|
|
|
|
|
|
for (hash = option->keyword[0], k = option->keyword + 1; *k;)
|
|
|
hash = 33 * hash + *k++;
|
|
|
|
|
|
return (hash & 511);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
|
|
|
* necessary.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Bitmask of fields read */
|
|
|
ppd_read(cups_file_t *fp, /* I - File to read from */
|
|
|
_ppd_line_t *line, /* I - Line buffer */
|
|
|
char *keyword, /* O - Keyword from line */
|
|
|
char *option, /* O - Option from line */
|
|
|
char *text, /* O - Human-readable text from line */
|
|
|
char **string, /* O - Code/string data */
|
|
|
int ignoreblank, /* I - Ignore blank lines? */
|
|
|
_ppd_globals_t *pg) /* I - Global data */
|
|
|
{
|
|
|
int ch, /* Character from file */
|
|
|
col, /* Column in line */
|
|
|
colon, /* Colon seen? */
|
|
|
endquote, /* Waiting for an end quote */
|
|
|
mask, /* Mask to be returned */
|
|
|
startline, /* Start line */
|
|
|
textlen; /* Length of text */
|
|
|
char *keyptr, /* Keyword pointer */
|
|
|
*optptr, /* Option pointer */
|
|
|
*textptr, /* Text pointer */
|
|
|
*strptr, /* Pointer into string */
|
|
|
*lineptr; /* Current position in line buffer */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Now loop until we have a valid line...
|
|
|
*/
|
|
|
|
|
|
*string = NULL;
|
|
|
col = 0;
|
|
|
startline = pg->ppd_line + 1;
|
|
|
|
|
|
if (!line->buffer)
|
|
|
{
|
|
|
line->bufsize = 1024;
|
|
|
line->buffer = malloc(1024);
|
|
|
|
|
|
if (!line->buffer)
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
do
|
|
|
{
|
|
|
/*
|
|
|
* Read the line...
|
|
|
*/
|
|
|
|
|
|
lineptr = line->buffer;
|
|
|
endquote = 0;
|
|
|
colon = 0;
|
|
|
|
|
|
while ((ch = cupsFileGetChar(fp)) != EOF)
|
|
|
{
|
|
|
if (lineptr >= (line->buffer + line->bufsize - 1))
|
|
|
{
|
|
|
/*
|
|
|
* Expand the line buffer...
|
|
|
*/
|
|
|
|
|
|
char *temp; /* Temporary line pointer */
|
|
|
|
|
|
|
|
|
line->bufsize += 1024;
|
|
|
if (line->bufsize > 262144)
|
|
|
{
|
|
|
/*
|
|
|
* Don't allow lines longer than 256k!
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_LINE_TOO_LONG;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
temp = realloc(line->buffer, line->bufsize);
|
|
|
if (!temp)
|
|
|
{
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_LINE_TOO_LONG;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
lineptr = temp + (lineptr - line->buffer);
|
|
|
line->buffer = temp;
|
|
|
}
|
|
|
|
|
|
if (ch == '\r' || ch == '\n')
|
|
|
{
|
|
|
/*
|
|
|
* Line feed or carriage return...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line ++;
|
|
|
col = 0;
|
|
|
|
|
|
if (ch == '\r')
|
|
|
{
|
|
|
/*
|
|
|
* Check for a trailing line feed...
|
|
|
*/
|
|
|
|
|
|
if ((ch = cupsFilePeekChar(fp)) == EOF)
|
|
|
{
|
|
|
ch = '\n';
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (ch == 0x0a)
|
|
|
cupsFileGetChar(fp);
|
|
|
}
|
|
|
|
|
|
if (lineptr == line->buffer && ignoreblank)
|
|
|
continue; /* Skip blank lines */
|
|
|
|
|
|
ch = '\n';
|
|
|
|
|
|
if (!endquote) /* Continue for multi-line text */
|
|
|
break;
|
|
|
|
|
|
*lineptr++ = '\n';
|
|
|
}
|
|
|
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
/*
|
|
|
* Other control characters...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
else if (ch != 0x1a)
|
|
|
{
|
|
|
/*
|
|
|
* Any other character...
|
|
|
*/
|
|
|
|
|
|
*lineptr++ = (char)ch;
|
|
|
col ++;
|
|
|
|
|
|
if (col > (PPD_MAX_LINE - 1))
|
|
|
{
|
|
|
/*
|
|
|
* Line is too long...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_LINE_TOO_LONG;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
|
|
|
colon = 1;
|
|
|
|
|
|
if (ch == '\"' && colon)
|
|
|
endquote = !endquote;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (endquote)
|
|
|
{
|
|
|
/*
|
|
|
* Didn't finish this quoted string...
|
|
|
*/
|
|
|
|
|
|
while ((ch = cupsFileGetChar(fp)) != EOF)
|
|
|
if (ch == '\"')
|
|
|
break;
|
|
|
else if (ch == '\r' || ch == '\n')
|
|
|
{
|
|
|
pg->ppd_line ++;
|
|
|
col = 0;
|
|
|
|
|
|
if (ch == '\r')
|
|
|
{
|
|
|
/*
|
|
|
* Check for a trailing line feed...
|
|
|
*/
|
|
|
|
|
|
if ((ch = cupsFilePeekChar(fp)) == EOF)
|
|
|
break;
|
|
|
if (ch == 0x0a)
|
|
|
cupsFileGetChar(fp);
|
|
|
}
|
|
|
}
|
|
|
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
/*
|
|
|
* Other control characters...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
else if (ch != 0x1a)
|
|
|
{
|
|
|
col ++;
|
|
|
|
|
|
if (col > (PPD_MAX_LINE - 1))
|
|
|
{
|
|
|
/*
|
|
|
* Line is too long...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_LINE_TOO_LONG;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (ch != '\n')
|
|
|
{
|
|
|
/*
|
|
|
* Didn't finish this line...
|
|
|
*/
|
|
|
|
|
|
while ((ch = cupsFileGetChar(fp)) != EOF)
|
|
|
if (ch == '\r' || ch == '\n')
|
|
|
{
|
|
|
/*
|
|
|
* Line feed or carriage return...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line ++;
|
|
|
col = 0;
|
|
|
|
|
|
if (ch == '\r')
|
|
|
{
|
|
|
/*
|
|
|
* Check for a trailing line feed...
|
|
|
*/
|
|
|
|
|
|
if ((ch = cupsFilePeekChar(fp)) == EOF)
|
|
|
break;
|
|
|
if (ch == 0x0a)
|
|
|
cupsFileGetChar(fp);
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
/*
|
|
|
* Other control characters...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
else if (ch != 0x1a)
|
|
|
{
|
|
|
col ++;
|
|
|
|
|
|
if (col > (PPD_MAX_LINE - 1))
|
|
|
{
|
|
|
/*
|
|
|
* Line is too long...
|
|
|
*/
|
|
|
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_LINE_TOO_LONG;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (lineptr > line->buffer && lineptr[-1] == '\n')
|
|
|
lineptr --;
|
|
|
|
|
|
*lineptr = '\0';
|
|
|
|
|
|
DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
|
|
|
|
|
|
/*
|
|
|
* The dynamically created PPDs for older style macOS
|
|
|
* drivers include a large blob of data inserted as comments
|
|
|
* at the end of the file. As an optimization we can stop
|
|
|
* reading the PPD when we get to the start of this data.
|
|
|
*/
|
|
|
|
|
|
if (!strcmp(line->buffer, "*%APLWORKSET START"))
|
|
|
return (0);
|
|
|
|
|
|
if (ch == EOF && lineptr == line->buffer)
|
|
|
return (0);
|
|
|
|
|
|
/*
|
|
|
* Now parse it...
|
|
|
*/
|
|
|
|
|
|
mask = 0;
|
|
|
lineptr = line->buffer + 1;
|
|
|
|
|
|
keyword[0] = '\0';
|
|
|
option[0] = '\0';
|
|
|
text[0] = '\0';
|
|
|
*string = NULL;
|
|
|
|
|
|
if ((!line->buffer[0] || /* Blank line */
|
|
|
!strncmp(line->buffer, "*%", 2) || /* Comment line */
|
|
|
!strcmp(line->buffer, "*End")) && /* End of multi-line string */
|
|
|
ignoreblank) /* Ignore these? */
|
|
|
{
|
|
|
startline = pg->ppd_line + 1;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
|
|
|
{
|
|
|
if (pg->ppd_conform == PPD_CONFORM_RELAXED)
|
|
|
{
|
|
|
startline = pg->ppd_line + 1;
|
|
|
continue;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pg->ppd_line = startline;
|
|
|
pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (line->buffer[0] != '*') /* All lines start with an asterisk */
|
|
|
{
|
|
|
/*
|
|
|
* Allow lines consisting of just whitespace...
|
|
|
*/
|
|
|
|
|
|
for (lineptr = line->buffer; *lineptr; lineptr ++)
|
|
|
if (*lineptr && !_cups_isspace(*lineptr))
|
|
|
break;
|
|
|
|
|
|
if (*lineptr)
|
|
|
{
|
|
|
pg->ppd_status = PPD_MISSING_ASTERISK;
|
|
|
return (0);
|
|
|
}
|
|
|
else if (ignoreblank)
|
|
|
continue;
|
|
|
else
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Get a keyword...
|
|
|
*/
|
|
|
|
|
|
keyptr = keyword;
|
|
|
|
|
|
while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
|
|
|
{
|
|
|
if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
|
|
|
(keyptr - keyword) >= (PPD_MAX_NAME - 1))
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
*keyptr++ = *lineptr++;
|
|
|
}
|
|
|
|
|
|
*keyptr = '\0';
|
|
|
|
|
|
if (!strcmp(keyword, "End"))
|
|
|
continue;
|
|
|
|
|
|
mask |= PPD_KEYWORD;
|
|
|
|
|
|
if (_cups_isspace(*lineptr))
|
|
|
{
|
|
|
/*
|
|
|
* Get an option name...
|
|
|
*/
|
|
|
|
|
|
while (_cups_isspace(*lineptr))
|
|
|
lineptr ++;
|
|
|
|
|
|
optptr = option;
|
|
|
|
|
|
while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
|
|
|
*lineptr != '/')
|
|
|
{
|
|
|
if (*lineptr <= ' ' || *lineptr > 126 ||
|
|
|
(optptr - option) >= (PPD_MAX_NAME - 1))
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
*optptr++ = *lineptr++;
|
|
|
}
|
|
|
|
|
|
*optptr = '\0';
|
|
|
|
|
|
if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
while (_cups_isspace(*lineptr))
|
|
|
lineptr ++;
|
|
|
|
|
|
mask |= PPD_OPTION;
|
|
|
|
|
|
if (*lineptr == '/')
|
|
|
{
|
|
|
/*
|
|
|
* Get human-readable text...
|
|
|
*/
|
|
|
|
|
|
lineptr ++;
|
|
|
|
|
|
textptr = text;
|
|
|
|
|
|
while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
|
|
|
{
|
|
|
if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
|
|
|
(textptr - text) >= (PPD_MAX_LINE - 1))
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
*textptr++ = *lineptr++;
|
|
|
}
|
|
|
|
|
|
*textptr = '\0';
|
|
|
textlen = ppd_decode(text);
|
|
|
|
|
|
if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
mask |= PPD_TEXT;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
|
|
|
{
|
|
|
pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
while (_cups_isspace(*lineptr))
|
|
|
lineptr ++;
|
|
|
|
|
|
if (*lineptr == ':')
|
|
|
{
|
|
|
/*
|
|
|
* Get string after triming leading and trailing whitespace...
|
|
|
*/
|
|
|
|
|
|
lineptr ++;
|
|
|
while (_cups_isspace(*lineptr))
|
|
|
lineptr ++;
|
|
|
|
|
|
strptr = lineptr + strlen(lineptr) - 1;
|
|
|
while (strptr >= lineptr && _cups_isspace(*strptr))
|
|
|
*strptr-- = '\0';
|
|
|
|
|
|
if (*strptr == '\"')
|
|
|
{
|
|
|
/*
|
|
|
* Quoted string by itself, remove quotes...
|
|
|
*/
|
|
|
|
|
|
*strptr = '\0';
|
|
|
lineptr ++;
|
|
|
}
|
|
|
|
|
|
*string = strdup(lineptr);
|
|
|
|
|
|
mask |= PPD_STRING;
|
|
|
}
|
|
|
}
|
|
|
while (mask == 0);
|
|
|
|
|
|
return (mask);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'ppd_update_filters()' - Update the filters array as needed.
|
|
|
*
|
|
|
* This function re-populates the filters array with cupsFilter2 entries that
|
|
|
* have been stripped of the destination MIME media types and any maxsize hints.
|
|
|
*
|
|
|
* (All for backwards-compatibility)
|
|
|
*/
|
|
|
|
|
|
static int /* O - 1 on success, 0 on failure */
|
|
|
ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */
|
|
|
_ppd_globals_t *pg) /* I - Global data */
|
|
|
{
|
|
|
ppd_attr_t *attr; /* Current cupsFilter2 value */
|
|
|
char srcsuper[16], /* Source MIME media type */
|
|
|
srctype[256],
|
|
|
dstsuper[16], /* Destination MIME media type */
|
|
|
dsttype[256],
|
|
|
program[1024], /* Command to run */
|
|
|
*ptr, /* Pointer into command to run */
|
|
|
buffer[1024], /* Re-written cupsFilter value */
|
|
|
**filter; /* Current filter */
|
|
|
int cost; /* Cost of filter */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
|
|
|
|
|
|
/*
|
|
|
* See if we have any cupsFilter2 lines...
|
|
|
*/
|
|
|
|
|
|
if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
|
|
|
{
|
|
|
DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Yes, free the cupsFilter-defined filters and re-build...
|
|
|
*/
|
|
|
|
|
|
ppd_free_filters(ppd);
|
|
|
|
|
|
do
|
|
|
{
|
|
|
/*
|
|
|
* Parse the cupsFilter2 string:
|
|
|
*
|
|
|
* src/type dst/type cost program
|
|
|
* src/type dst/type cost maxsize(n) program
|
|
|
*/
|
|
|
|
|
|
DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
|
|
|
|
|
|
if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
|
|
|
srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
|
|
|
{
|
|
|
DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
|
|
|
pg->ppd_status = PPD_BAD_VALUE;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
|
|
|
"dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
|
|
|
srcsuper, srctype, dstsuper, dsttype, cost, program));
|
|
|
|
|
|
if (!strncmp(program, "maxsize(", 8) &&
|
|
|
(ptr = strchr(program + 8, ')')) != NULL)
|
|
|
{
|
|
|
DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
|
|
|
|
|
|
ptr ++;
|
|
|
while (_cups_isspace(*ptr))
|
|
|
ptr ++;
|
|
|
|
|
|
_cups_strcpy(program, ptr);
|
|
|
DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Convert to cupsFilter format:
|
|
|
*
|
|
|
* src/type cost program
|
|
|
*/
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
|
|
|
program);
|
|
|
DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
|
|
|
|
|
|
/*
|
|
|
* Add a cupsFilter-compatible string to the filters array.
|
|
|
*/
|
|
|
|
|
|
if (ppd->num_filters == 0)
|
|
|
filter = malloc(sizeof(char *));
|
|
|
else
|
|
|
filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
|
|
|
|
|
|
if (filter == NULL)
|
|
|
{
|
|
|
DEBUG_puts("5ppd_update_filters: Out of memory.");
|
|
|
pg->ppd_status = PPD_ALLOC_ERROR;
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
ppd->filters = filter;
|
|
|
filter += ppd->num_filters;
|
|
|
ppd->num_filters ++;
|
|
|
|
|
|
*filter = strdup(buffer);
|
|
|
}
|
|
|
while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
|
|
|
|
|
|
DEBUG_puts("5ppd_update_filters: Completed OK.");
|
|
|
return (1);
|
|
|
}
|