Home | History | Annotate | Download | only in cups
      1 /*
      2  * PPD file routines for CUPS.
      3  *
      4  * Copyright 2007-2017 by Apple Inc.
      5  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * which should have been included with this file.  If this file is
     11  * missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * PostScript is a trademark of Adobe Systems, Inc.
     14  *
     15  * This code and any derivative of it may be used and distributed
     16  * freely under the terms of the GNU General Public License when
     17  * used with GNU Ghostscript or its derivatives.  Use of the code
     18  * (or any derivative of it) with software other than GNU
     19  * GhostScript (or its derivatives) is governed by the CUPS license
     20  * agreement.
     21  *
     22  * This file is subject to the Apple OS-Developed Software exception.
     23  */
     24 
     25 /*
     26  * Include necessary headers.
     27  */
     28 
     29 #include "cups-private.h"
     30 #include "ppd-private.h"
     31 
     32 
     33 /*
     34  * Definitions...
     35  */
     36 
     37 #define ppd_free(p)	if (p) free(p)	/* Safe free macro */
     38 
     39 #define PPD_KEYWORD	1		/* Line contained a keyword */
     40 #define PPD_OPTION	2		/* Line contained an option name */
     41 #define PPD_TEXT	4		/* Line contained human-readable text */
     42 #define PPD_STRING	8		/* Line contained a string or code */
     43 
     44 #define PPD_HASHSIZE	512		/* Size of hash */
     45 
     46 
     47 /*
     48  * Line buffer structure...
     49  */
     50 
     51 typedef struct _ppd_line_s
     52 {
     53   char		*buffer;		/* Pointer to buffer */
     54   size_t	bufsize;		/* Size of the buffer */
     55 } _ppd_line_t;
     56 
     57 
     58 /*
     59  * Local globals...
     60  */
     61 
     62 static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER;
     63 					/* Thread local storage key */
     64 #ifdef HAVE_PTHREAD_H
     65 static pthread_once_t	ppd_globals_key_once = PTHREAD_ONCE_INIT;
     66 					/* One-time initialization object */
     67 #endif /* HAVE_PTHREAD_H */
     68 
     69 
     70 /*
     71  * Local functions...
     72  */
     73 
     74 static ppd_attr_t	*ppd_add_attr(ppd_file_t *ppd, const char *name,
     75 			              const char *spec, const char *text,
     76 				      const char *value);
     77 static ppd_choice_t	*ppd_add_choice(ppd_option_t *option, const char *name);
     78 static ppd_size_t	*ppd_add_size(ppd_file_t *ppd, const char *name);
     79 static int		ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
     80 static int		ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
     81 static int		ppd_compare_coptions(ppd_coption_t *a,
     82 			                     ppd_coption_t *b);
     83 static int		ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
     84 static int		ppd_decode(char *string);
     85 static void		ppd_free_filters(ppd_file_t *ppd);
     86 static void		ppd_free_group(ppd_group_t *group);
     87 static void		ppd_free_option(ppd_option_t *option);
     88 static ppd_coption_t	*ppd_get_coption(ppd_file_t *ppd, const char *name);
     89 static ppd_cparam_t	*ppd_get_cparam(ppd_coption_t *opt,
     90 			                const char *param,
     91 					const char *text);
     92 static ppd_group_t	*ppd_get_group(ppd_file_t *ppd, const char *name,
     93 			               const char *text, _ppd_globals_t *pg,
     94 				       cups_encoding_t encoding);
     95 static ppd_option_t	*ppd_get_option(ppd_group_t *group, const char *name);
     96 static _ppd_globals_t	*ppd_globals_alloc(void);
     97 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
     98 static void		ppd_globals_free(_ppd_globals_t *g);
     99 #endif /* HAVE_PTHREAD_H || WIN32 */
    100 #ifdef HAVE_PTHREAD_H
    101 static void		ppd_globals_init(void);
    102 #endif /* HAVE_PTHREAD_H */
    103 static int		ppd_hash_option(ppd_option_t *option);
    104 static int		ppd_read(cups_file_t *fp, _ppd_line_t *line,
    105 			         char *keyword, char *option, char *text,
    106 				 char **string, int ignoreblank,
    107 				 _ppd_globals_t *pg);
    108 static int		ppd_update_filters(ppd_file_t *ppd,
    109 			                   _ppd_globals_t *pg);
    110 
    111 
    112 /*
    113  * 'ppdClose()' - Free all memory used by the PPD file.
    114  */
    115 
    116 void
    117 ppdClose(ppd_file_t *ppd)		/* I - PPD file record */
    118 {
    119   int			i;		/* Looping var */
    120   ppd_emul_t		*emul;		/* Current emulation */
    121   ppd_group_t		*group;		/* Current group */
    122   char			**font;		/* Current font */
    123   ppd_attr_t		**attr;		/* Current attribute */
    124   ppd_coption_t		*coption;	/* Current custom option */
    125   ppd_cparam_t		*cparam;	/* Current custom parameter */
    126 
    127 
    128  /*
    129   * Range check arguments...
    130   */
    131 
    132   if (!ppd)
    133     return;
    134 
    135  /*
    136   * Free all strings at the top level...
    137   */
    138 
    139   _cupsStrFree(ppd->lang_encoding);
    140   _cupsStrFree(ppd->nickname);
    141   if (ppd->patches)
    142     free(ppd->patches);
    143   _cupsStrFree(ppd->jcl_begin);
    144   _cupsStrFree(ppd->jcl_end);
    145   _cupsStrFree(ppd->jcl_ps);
    146 
    147  /*
    148   * Free any emulations...
    149   */
    150 
    151   if (ppd->num_emulations > 0)
    152   {
    153     for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++)
    154     {
    155       _cupsStrFree(emul->start);
    156       _cupsStrFree(emul->stop);
    157     }
    158 
    159     ppd_free(ppd->emulations);
    160   }
    161 
    162  /*
    163   * Free any UI groups, subgroups, and options...
    164   */
    165 
    166   if (ppd->num_groups > 0)
    167   {
    168     for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
    169       ppd_free_group(group);
    170 
    171     ppd_free(ppd->groups);
    172   }
    173 
    174   cupsArrayDelete(ppd->options);
    175   cupsArrayDelete(ppd->marked);
    176 
    177  /*
    178   * Free any page sizes...
    179   */
    180 
    181   if (ppd->num_sizes > 0)
    182     ppd_free(ppd->sizes);
    183 
    184  /*
    185   * Free any constraints...
    186   */
    187 
    188   if (ppd->num_consts > 0)
    189     ppd_free(ppd->consts);
    190 
    191  /*
    192   * Free any filters...
    193   */
    194 
    195   ppd_free_filters(ppd);
    196 
    197  /*
    198   * Free any fonts...
    199   */
    200 
    201   if (ppd->num_fonts > 0)
    202   {
    203     for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
    204       _cupsStrFree(*font);
    205 
    206     ppd_free(ppd->fonts);
    207   }
    208 
    209  /*
    210   * Free any profiles...
    211   */
    212 
    213   if (ppd->num_profiles > 0)
    214     ppd_free(ppd->profiles);
    215 
    216  /*
    217   * Free any attributes...
    218   */
    219 
    220   if (ppd->num_attrs > 0)
    221   {
    222     for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
    223     {
    224       _cupsStrFree((*attr)->value);
    225       ppd_free(*attr);
    226     }
    227 
    228     ppd_free(ppd->attrs);
    229   }
    230 
    231   cupsArrayDelete(ppd->sorted_attrs);
    232 
    233  /*
    234   * Free custom options...
    235   */
    236 
    237   for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
    238        coption;
    239        coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
    240   {
    241     for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
    242          cparam;
    243 	 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
    244     {
    245       switch (cparam->type)
    246       {
    247         case PPD_CUSTOM_PASSCODE :
    248         case PPD_CUSTOM_PASSWORD :
    249         case PPD_CUSTOM_STRING :
    250             _cupsStrFree(cparam->current.custom_string);
    251 	    break;
    252 
    253 	default :
    254 	    break;
    255       }
    256 
    257       free(cparam);
    258     }
    259 
    260     cupsArrayDelete(coption->params);
    261 
    262     free(coption);
    263   }
    264 
    265   cupsArrayDelete(ppd->coptions);
    266 
    267  /*
    268   * Free constraints...
    269   */
    270 
    271   if (ppd->cups_uiconstraints)
    272   {
    273     _ppd_cups_uiconsts_t *consts;	/* Current constraints */
    274 
    275 
    276     for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
    277          consts;
    278 	 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
    279     {
    280       free(consts->constraints);
    281       free(consts);
    282     }
    283 
    284     cupsArrayDelete(ppd->cups_uiconstraints);
    285   }
    286 
    287  /*
    288   * Free any PPD cache/mapping data...
    289   */
    290 
    291   if (ppd->cache)
    292     _ppdCacheDestroy(ppd->cache);
    293 
    294  /*
    295   * Free the whole record...
    296   */
    297 
    298   ppd_free(ppd);
    299 }
    300 
    301 
    302 /*
    303  * 'ppdErrorString()' - Returns the text associated with a status.
    304  *
    305  * @since CUPS 1.1.19/macOS 10.3@
    306  */
    307 
    308 const char *				/* O - Status string */
    309 ppdErrorString(ppd_status_t status)	/* I - PPD status */
    310 {
    311   static const char * const messages[] =/* Status messages */
    312 		{
    313 		  _("OK"),
    314 		  _("Unable to open PPD file"),
    315 		  _("NULL PPD file pointer"),
    316 		  _("Memory allocation error"),
    317 		  _("Missing PPD-Adobe-4.x header"),
    318 		  _("Missing value string"),
    319 		  _("Internal error"),
    320 		  _("Bad OpenGroup"),
    321 		  _("OpenGroup without a CloseGroup first"),
    322 		  _("Bad OpenUI/JCLOpenUI"),
    323 		  _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
    324 		  _("Bad OrderDependency"),
    325 		  _("Bad UIConstraints"),
    326 		  _("Missing asterisk in column 1"),
    327 		  _("Line longer than the maximum allowed (255 characters)"),
    328 		  _("Illegal control character"),
    329 		  _("Illegal main keyword string"),
    330 		  _("Illegal option keyword string"),
    331 		  _("Illegal translation string"),
    332 		  _("Illegal whitespace character"),
    333 		  _("Bad custom parameter"),
    334 		  _("Missing option keyword"),
    335 		  _("Bad value string"),
    336 		  _("Missing CloseGroup")
    337 		};
    338 
    339 
    340   if (status < PPD_OK || status >= PPD_MAX_STATUS)
    341     return (_cupsLangString(cupsLangDefault(), _("Unknown")));
    342   else
    343     return (_cupsLangString(cupsLangDefault(), messages[status]));
    344 }
    345 
    346 
    347 /*
    348  * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
    349  *                       LanguageEncoding.
    350  */
    351 
    352 cups_encoding_t				/* O - CUPS encoding value */
    353 _ppdGetEncoding(const char *name)	/* I - LanguageEncoding string */
    354 {
    355   if (!_cups_strcasecmp(name, "ISOLatin1"))
    356     return (CUPS_ISO8859_1);
    357   else if (!_cups_strcasecmp(name, "ISOLatin2"))
    358     return (CUPS_ISO8859_2);
    359   else if (!_cups_strcasecmp(name, "ISOLatin5"))
    360     return (CUPS_ISO8859_5);
    361   else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
    362     return (CUPS_JIS_X0213);
    363   else if (!_cups_strcasecmp(name, "MacStandard"))
    364     return (CUPS_MAC_ROMAN);
    365   else if (!_cups_strcasecmp(name, "WindowsANSI"))
    366     return (CUPS_WINDOWS_1252);
    367   else
    368     return (CUPS_UTF8);
    369 }
    370 
    371 
    372 /*
    373  * '_ppdGlobals()' - Return a pointer to thread local storage
    374  */
    375 
    376 _ppd_globals_t *			/* O - Pointer to global data */
    377 _ppdGlobals(void)
    378 {
    379   _ppd_globals_t *pg;			/* Pointer to global data */
    380 
    381 
    382 #ifdef HAVE_PTHREAD_H
    383  /*
    384   * Initialize the global data exactly once...
    385   */
    386 
    387   pthread_once(&ppd_globals_key_once, ppd_globals_init);
    388 #endif /* HAVE_PTHREAD_H */
    389 
    390  /*
    391   * See if we have allocated the data yet...
    392   */
    393 
    394   if ((pg = (_ppd_globals_t *)_cupsThreadGetData(ppd_globals_key)) == NULL)
    395   {
    396    /*
    397     * No, allocate memory as set the pointer for the key...
    398     */
    399 
    400     if ((pg = ppd_globals_alloc()) != NULL)
    401       _cupsThreadSetData(ppd_globals_key, pg);
    402   }
    403 
    404  /*
    405   * Return the pointer to the data...
    406   */
    407 
    408   return (pg);
    409 }
    410 
    411 
    412 /*
    413  * 'ppdLastError()' - Return the status from the last ppdOpen*().
    414  *
    415  * @since CUPS 1.1.19/macOS 10.3@
    416  */
    417 
    418 ppd_status_t				/* O - Status code */
    419 ppdLastError(int *line)			/* O - Line number */
    420 {
    421   _ppd_globals_t	*pg = _ppdGlobals();
    422 					/* Global data */
    423 
    424 
    425   if (line)
    426     *line = pg->ppd_line;
    427 
    428   return (pg->ppd_status);
    429 }
    430 
    431 
    432 /*
    433  * '_ppdOpen()' - Read a PPD file into memory.
    434  *
    435  * @since CUPS 1.2/macOS 10.5@
    436  */
    437 
    438 ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
    439 _ppdOpen(
    440     cups_file_t		*fp,		/* I - File to read from */
    441     _ppd_localization_t	localization)	/* I - Localization to load */
    442 {
    443   int			i, j, k;	/* Looping vars */
    444   int			count;		/* Temporary count */
    445   _ppd_line_t		line;		/* Line buffer */
    446   ppd_file_t		*ppd;		/* PPD file record */
    447   ppd_group_t		*group,		/* Current group */
    448 			*subgroup;	/* Current sub-group */
    449   ppd_option_t		*option;	/* Current option */
    450   ppd_choice_t		*choice;	/* Current choice */
    451   ppd_const_t		*constraint;	/* Current constraint */
    452   ppd_size_t		*size;		/* Current page size */
    453   int			mask;		/* Line data mask */
    454   char			keyword[PPD_MAX_NAME],
    455   					/* Keyword from file */
    456 			name[PPD_MAX_NAME],
    457 					/* Option from file */
    458 			text[PPD_MAX_LINE],
    459 					/* Human-readable text from file */
    460 			*string,	/* Code/text from file */
    461 			*sptr,		/* Pointer into string */
    462 			*nameptr,	/* Pointer into name */
    463 			*temp,		/* Temporary string pointer */
    464 			**tempfonts;	/* Temporary fonts pointer */
    465   float			order;		/* Order dependency number */
    466   ppd_section_t		section;	/* Order dependency section */
    467   ppd_profile_t		*profile;	/* Pointer to color profile */
    468   char			**filter;	/* Pointer to filter */
    469   struct lconv		*loc;		/* Locale data */
    470   int			ui_keyword;	/* Is this line a UI keyword? */
    471   cups_lang_t		*lang;		/* Language data */
    472   cups_encoding_t	encoding;	/* Encoding of PPD file */
    473   _ppd_globals_t	*pg = _ppdGlobals();
    474 					/* Global data */
    475   char			custom_name[PPD_MAX_NAME];
    476 					/* CustomFoo attribute name */
    477   ppd_attr_t		*custom_attr;	/* CustomFoo attribute */
    478   char			ll[7],		/* Base language + '.' */
    479 			ll_CC[7];	/* Language w/country + '.' */
    480   size_t		ll_len = 0,	/* Base language length */
    481 			ll_CC_len = 0;	/* Language w/country length */
    482   static const char * const ui_keywords[] =
    483 			{
    484 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
    485  /*
    486   * Adobe defines some 41 keywords as "UI", meaning that they are
    487   * user interface elements and that they should be treated as such
    488   * even if the PPD creator doesn't use Open/CloseUI around them.
    489   *
    490   * Since this can cause previously invisible options to appear and
    491   * confuse users, the default is to only treat the PageSize and
    492   * PageRegion keywords this way.
    493   */
    494 			  /* Boolean keywords */
    495 			  "BlackSubstitution",
    496 			  "Booklet",
    497 			  "Collate",
    498 			  "ManualFeed",
    499 			  "MirrorPrint",
    500 			  "NegativePrint",
    501 			  "Sorter",
    502 			  "TraySwitch",
    503 
    504 			  /* PickOne keywords */
    505 			  "AdvanceMedia",
    506 			  "BindColor",
    507 			  "BindEdge",
    508 			  "BindType",
    509 			  "BindWhen",
    510 			  "BitsPerPixel",
    511 			  "ColorModel",
    512 			  "CutMedia",
    513 			  "Duplex",
    514 			  "FoldType",
    515 			  "FoldWhen",
    516 			  "InputSlot",
    517 			  "JCLFrameBufferSize",
    518 			  "JCLResolution",
    519 			  "Jog",
    520 			  "MediaColor",
    521 			  "MediaType",
    522 			  "MediaWeight",
    523 			  "OutputBin",
    524 			  "OutputMode",
    525 			  "OutputOrder",
    526 			  "PageRegion",
    527 			  "PageSize",
    528 			  "Resolution",
    529 			  "Separations",
    530 			  "Signature",
    531 			  "Slipsheet",
    532 			  "Smoothing",
    533 			  "StapleLocation",
    534 			  "StapleOrientation",
    535 			  "StapleWhen",
    536 			  "StapleX",
    537 			  "StapleY"
    538 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
    539 			  "PageRegion",
    540 			  "PageSize"
    541 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
    542 			};
    543   static const char * const color_keywords[] =	/* Keywords associated with color profiles */
    544 			{
    545 			  ".cupsICCProfile",
    546 			  ".ColorModel",
    547 			};
    548 
    549 
    550   DEBUG_printf(("_ppdOpen(fp=%p)", fp));
    551 
    552  /*
    553   * Default to "OK" status...
    554   */
    555 
    556   pg->ppd_status = PPD_OK;
    557   pg->ppd_line   = 0;
    558 
    559  /*
    560   * Range check input...
    561   */
    562 
    563   if (fp == NULL)
    564   {
    565     pg->ppd_status = PPD_NULL_FILE;
    566     return (NULL);
    567   }
    568 
    569  /*
    570   * If only loading a single localization set up the strings to match...
    571   */
    572 
    573   if (localization == _PPD_LOCALIZATION_DEFAULT)
    574   {
    575     if ((lang = cupsLangDefault()) == NULL)
    576       return (NULL);
    577 
    578     snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
    579 
    580    /*
    581     * <rdar://problem/22130168>
    582     * <rdar://problem/27245567>
    583     *
    584     * Need to use a different base language for some locales...
    585     */
    586 
    587     if (!strcmp(lang->language, "zh_HK"))
    588     {					/* Traditional Chinese + variants */
    589       strlcpy(ll_CC, "zh_TW.", sizeof(ll_CC));
    590       strlcpy(ll, "zh_", sizeof(ll));
    591     }
    592     else if (!strncmp(lang->language, "zh", 2))
    593       strlcpy(ll, "zh_", sizeof(ll));	/* Any Chinese variant */
    594     else if (!strncmp(lang->language, "jp", 2))
    595     {					/* Any Japanese variant */
    596       strlcpy(ll_CC, "ja", sizeof(ll_CC));
    597       strlcpy(ll, "jp", sizeof(ll));
    598     }
    599     else if (!strncmp(lang->language, "nb", 2) || !strncmp(lang->language, "no", 2))
    600     {					/* Any Norwegian variant */
    601       strlcpy(ll_CC, "nb", sizeof(ll_CC));
    602       strlcpy(ll, "no", sizeof(ll));
    603     }
    604     else
    605       snprintf(ll, sizeof(ll), "%2.2s.", lang->language);
    606 
    607     ll_CC_len = strlen(ll_CC);
    608     ll_len    = strlen(ll);
    609 
    610     DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
    611                   ll_CC, ll));
    612   }
    613 
    614  /*
    615   * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
    616   */
    617 
    618   line.buffer  = NULL;
    619   line.bufsize = 0;
    620 
    621   mask = ppd_read(fp, &line, keyword, name, text, &string, 0, pg);
    622 
    623   DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword));
    624 
    625   if (mask == 0 ||
    626       strcmp(keyword, "PPD-Adobe") ||
    627       string == NULL || string[0] != '4')
    628   {
    629    /*
    630     * Either this is not a PPD file, or it is not a 4.x PPD file.
    631     */
    632 
    633     if (pg->ppd_status == PPD_OK)
    634       pg->ppd_status = PPD_MISSING_PPDADOBE4;
    635 
    636     _cupsStrFree(string);
    637     ppd_free(line.buffer);
    638 
    639     return (NULL);
    640   }
    641 
    642   DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
    643 
    644   _cupsStrFree(string);
    645 
    646  /*
    647   * Allocate memory for the PPD file record...
    648   */
    649 
    650   if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
    651   {
    652     pg->ppd_status = PPD_ALLOC_ERROR;
    653 
    654     _cupsStrFree(string);
    655     ppd_free(line.buffer);
    656 
    657     return (NULL);
    658   }
    659 
    660   ppd->language_level = 2;
    661   ppd->color_device   = 0;
    662   ppd->colorspace     = PPD_CS_N;
    663   ppd->landscape      = -90;
    664   ppd->coptions       = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
    665                                      NULL);
    666 
    667  /*
    668   * Read lines from the PPD file and add them to the file record...
    669   */
    670 
    671   group      = NULL;
    672   subgroup   = NULL;
    673   option     = NULL;
    674   choice     = NULL;
    675   ui_keyword = 0;
    676   encoding   = CUPS_ISO8859_1;
    677   loc        = localeconv();
    678 
    679   while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, pg)) != 0)
    680   {
    681     DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
    682                   "text=\"%s\", string=%d chars...", mask, keyword, name, text,
    683 		  string ? (int)strlen(string) : 0));
    684 
    685     if (strncmp(keyword, "Default", 7) && !string &&
    686         pg->ppd_conform != PPD_CONFORM_RELAXED)
    687     {
    688      /*
    689       * Need a string value!
    690       */
    691 
    692       pg->ppd_status = PPD_MISSING_VALUE;
    693 
    694       goto error;
    695     }
    696     else if (!string)
    697       continue;
    698 
    699    /*
    700     * Certain main keywords (as defined by the PPD spec) may be used
    701     * without the usual OpenUI/CloseUI stuff.  Presumably this is just
    702     * so that Adobe wouldn't completely break compatibility with PPD
    703     * files prior to v4.0 of the spec, but it is hopelessly
    704     * inconsistent...  Catch these main keywords and automatically
    705     * create the corresponding option, as needed...
    706     */
    707 
    708     if (ui_keyword)
    709     {
    710      /*
    711       * Previous line was a UI keyword...
    712       */
    713 
    714       option     = NULL;
    715       ui_keyword = 0;
    716     }
    717 
    718    /*
    719     * If we are filtering out keyword localizations, see if this line needs to
    720     * be used...
    721     */
    722 
    723     if (localization != _PPD_LOCALIZATION_ALL &&
    724         (temp = strchr(keyword, '.')) != NULL &&
    725         ((temp - keyword) == 2 || (temp - keyword) == 5) &&
    726         _cups_isalpha(keyword[0]) &&
    727         _cups_isalpha(keyword[1]) &&
    728         (keyword[2] == '.' ||
    729          (keyword[2] == '_' && _cups_isalpha(keyword[3]) &&
    730           _cups_isalpha(keyword[4]) && keyword[5] == '.')))
    731     {
    732       if (localization == _PPD_LOCALIZATION_NONE ||
    733 	  (localization == _PPD_LOCALIZATION_DEFAULT &&
    734 	   strncmp(ll_CC, keyword, ll_CC_len) &&
    735 	   strncmp(ll, keyword, ll_len)))
    736       {
    737 	DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
    738 	continue;
    739       }
    740       else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
    741       {
    742        /*
    743         * Only load localizations for the color profile related keywords...
    744         */
    745 
    746 	for (i = 0;
    747 	     i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
    748 	     i ++)
    749 	{
    750 	  if (!_cups_strcasecmp(temp, color_keywords[i]))
    751 	    break;
    752 	}
    753 
    754 	if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
    755 	{
    756 	  DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
    757 	  continue;
    758 	}
    759       }
    760     }
    761 
    762     if (option == NULL &&
    763         (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
    764 	    (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
    765     {
    766       for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
    767         if (!strcmp(keyword, ui_keywords[i]))
    768 	  break;
    769 
    770       if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
    771       {
    772        /*
    773         * Create the option in the appropriate group...
    774 	*/
    775 
    776         ui_keyword = 1;
    777 
    778         DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
    779 	              keyword));
    780 
    781         if (!group)
    782 	{
    783           if ((group = ppd_get_group(ppd, "General", _("General"), pg,
    784 	                             encoding)) == NULL)
    785 	    goto error;
    786 
    787           DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
    788           option = ppd_get_option(group, keyword);
    789 	  group  = NULL;
    790 	}
    791 	else
    792           option = ppd_get_option(group, keyword);
    793 
    794 	if (option == NULL)
    795 	{
    796           pg->ppd_status = PPD_ALLOC_ERROR;
    797 
    798           goto error;
    799 	}
    800 
    801        /*
    802 	* Now fill in the initial information for the option...
    803 	*/
    804 
    805 	if (!strncmp(keyword, "JCL", 3))
    806           option->section = PPD_ORDER_JCL;
    807 	else
    808           option->section = PPD_ORDER_ANY;
    809 
    810 	option->order = 10.0f;
    811 
    812 	if (i < 8)
    813           option->ui = PPD_UI_BOOLEAN;
    814 	else
    815           option->ui = PPD_UI_PICKONE;
    816 
    817         for (j = 0; j < ppd->num_attrs; j ++)
    818 	  if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
    819 	      !strcmp(ppd->attrs[j]->name + 7, keyword) &&
    820 	      ppd->attrs[j]->value)
    821 	  {
    822 	    DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
    823 	                  option->keyword, ppd->attrs[j]->value));
    824 	    strlcpy(option->defchoice, ppd->attrs[j]->value,
    825 	            sizeof(option->defchoice));
    826 	    break;
    827 	  }
    828 
    829         if (!strcmp(keyword, "PageSize"))
    830 	  strlcpy(option->text, _("Media Size"), sizeof(option->text));
    831 	else if (!strcmp(keyword, "MediaType"))
    832 	  strlcpy(option->text, _("Media Type"), sizeof(option->text));
    833 	else if (!strcmp(keyword, "InputSlot"))
    834 	  strlcpy(option->text, _("Media Source"), sizeof(option->text));
    835 	else if (!strcmp(keyword, "ColorModel"))
    836 	  strlcpy(option->text, _("Output Mode"), sizeof(option->text));
    837 	else if (!strcmp(keyword, "Resolution"))
    838 	  strlcpy(option->text, _("Resolution"), sizeof(option->text));
    839         else
    840 	  strlcpy(option->text, keyword, sizeof(option->text));
    841       }
    842     }
    843 
    844     if (!strcmp(keyword, "LanguageLevel"))
    845       ppd->language_level = atoi(string);
    846     else if (!strcmp(keyword, "LanguageEncoding"))
    847     {
    848      /*
    849       * Say all PPD files are UTF-8, since we convert to UTF-8...
    850       */
    851 
    852       ppd->lang_encoding = _cupsStrAlloc("UTF-8");
    853       encoding           = _ppdGetEncoding(string);
    854     }
    855     else if (!strcmp(keyword, "LanguageVersion"))
    856       ppd->lang_version = string;
    857     else if (!strcmp(keyword, "Manufacturer"))
    858       ppd->manufacturer = string;
    859     else if (!strcmp(keyword, "ModelName"))
    860       ppd->modelname = string;
    861     else if (!strcmp(keyword, "Protocols"))
    862       ppd->protocols = string;
    863     else if (!strcmp(keyword, "PCFileName"))
    864       ppd->pcfilename = string;
    865     else if (!strcmp(keyword, "NickName"))
    866     {
    867       if (encoding != CUPS_UTF8)
    868       {
    869         cups_utf8_t	utf8[256];	/* UTF-8 version of NickName */
    870 
    871 
    872         cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
    873 	ppd->nickname = _cupsStrAlloc((char *)utf8);
    874       }
    875       else
    876         ppd->nickname = _cupsStrAlloc(string);
    877     }
    878     else if (!strcmp(keyword, "Product"))
    879       ppd->product = string;
    880     else if (!strcmp(keyword, "ShortNickName"))
    881       ppd->shortnickname = string;
    882     else if (!strcmp(keyword, "TTRasterizer"))
    883       ppd->ttrasterizer = string;
    884     else if (!strcmp(keyword, "JCLBegin"))
    885     {
    886       ppd->jcl_begin = _cupsStrAlloc(string);
    887       ppd_decode(ppd->jcl_begin);	/* Decode quoted string */
    888     }
    889     else if (!strcmp(keyword, "JCLEnd"))
    890     {
    891       ppd->jcl_end = _cupsStrAlloc(string);
    892       ppd_decode(ppd->jcl_end);		/* Decode quoted string */
    893     }
    894     else if (!strcmp(keyword, "JCLToPSInterpreter"))
    895     {
    896       ppd->jcl_ps = _cupsStrAlloc(string);
    897       ppd_decode(ppd->jcl_ps);		/* Decode quoted string */
    898     }
    899     else if (!strcmp(keyword, "AccurateScreensSupport"))
    900       ppd->accurate_screens = !strcmp(string, "True");
    901     else if (!strcmp(keyword, "ColorDevice"))
    902       ppd->color_device = !strcmp(string, "True");
    903     else if (!strcmp(keyword, "ContoneOnly"))
    904       ppd->contone_only = !strcmp(string, "True");
    905     else if (!strcmp(keyword, "cupsFlipDuplex"))
    906       ppd->flip_duplex = !strcmp(string, "True");
    907     else if (!strcmp(keyword, "cupsManualCopies"))
    908       ppd->manual_copies = !strcmp(string, "True");
    909     else if (!strcmp(keyword, "cupsModelNumber"))
    910       ppd->model_number = atoi(string);
    911     else if (!strcmp(keyword, "cupsColorProfile"))
    912     {
    913       if (ppd->num_profiles == 0)
    914         profile = malloc(sizeof(ppd_profile_t));
    915       else
    916         profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * (size_t)(ppd->num_profiles + 1));
    917 
    918       if (!profile)
    919       {
    920         pg->ppd_status = PPD_ALLOC_ERROR;
    921 
    922 	goto error;
    923       }
    924 
    925       ppd->profiles     = profile;
    926       profile           += ppd->num_profiles;
    927       ppd->num_profiles ++;
    928 
    929       memset(profile, 0, sizeof(ppd_profile_t));
    930       strlcpy(profile->resolution, name, sizeof(profile->resolution));
    931       strlcpy(profile->media_type, text, sizeof(profile->media_type));
    932 
    933       profile->density      = (float)_cupsStrScand(string, &sptr, loc);
    934       profile->gamma        = (float)_cupsStrScand(sptr, &sptr, loc);
    935       profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
    936       profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
    937       profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
    938       profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
    939       profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
    940       profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
    941       profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
    942       profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
    943       profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
    944     }
    945     else if (!strcmp(keyword, "cupsFilter"))
    946     {
    947       if (ppd->num_filters == 0)
    948         filter = malloc(sizeof(char *));
    949       else
    950         filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
    951 
    952       if (filter == NULL)
    953       {
    954         pg->ppd_status = PPD_ALLOC_ERROR;
    955 
    956 	goto error;
    957       }
    958 
    959       ppd->filters     = filter;
    960       filter           += ppd->num_filters;
    961       ppd->num_filters ++;
    962 
    963      /*
    964       * Retain a copy of the filter string...
    965       */
    966 
    967       *filter = _cupsStrRetain(string);
    968     }
    969     else if (!strcmp(keyword, "Throughput"))
    970       ppd->throughput = atoi(string);
    971     else if (!strcmp(keyword, "Font"))
    972     {
    973      /*
    974       * Add this font to the list of available fonts...
    975       */
    976 
    977       if (ppd->num_fonts == 0)
    978         tempfonts = (char **)malloc(sizeof(char *));
    979       else
    980         tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * (size_t)(ppd->num_fonts + 1));
    981 
    982       if (tempfonts == NULL)
    983       {
    984         pg->ppd_status = PPD_ALLOC_ERROR;
    985 
    986 	goto error;
    987       }
    988 
    989       ppd->fonts                 = tempfonts;
    990       ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name);
    991       ppd->num_fonts ++;
    992     }
    993     else if (!strncmp(keyword, "ParamCustom", 11))
    994     {
    995       ppd_coption_t	*coption;	/* Custom option */
    996       ppd_cparam_t	*cparam;	/* Custom parameter */
    997       int		corder;		/* Order number */
    998       char		ctype[33],	/* Data type */
    999 			cminimum[65],	/* Minimum value */
   1000 			cmaximum[65];	/* Maximum value */
   1001 
   1002 
   1003      /*
   1004       * Get the custom option and parameter...
   1005       */
   1006 
   1007       if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
   1008       {
   1009         pg->ppd_status = PPD_ALLOC_ERROR;
   1010 
   1011 	goto error;
   1012       }
   1013 
   1014       if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
   1015       {
   1016         pg->ppd_status = PPD_ALLOC_ERROR;
   1017 
   1018 	goto error;
   1019       }
   1020 
   1021      /*
   1022       * Get the parameter data...
   1023       */
   1024 
   1025       if (!string ||
   1026           sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
   1027                  cmaximum) != 4)
   1028       {
   1029         pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
   1030 
   1031 	goto error;
   1032       }
   1033 
   1034       cparam->order = corder;
   1035 
   1036       if (!strcmp(ctype, "curve"))
   1037       {
   1038         cparam->type = PPD_CUSTOM_CURVE;
   1039 	cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
   1040 	cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
   1041       }
   1042       else if (!strcmp(ctype, "int"))
   1043       {
   1044         cparam->type = PPD_CUSTOM_INT;
   1045 	cparam->minimum.custom_int = atoi(cminimum);
   1046 	cparam->maximum.custom_int = atoi(cmaximum);
   1047       }
   1048       else if (!strcmp(ctype, "invcurve"))
   1049       {
   1050         cparam->type = PPD_CUSTOM_INVCURVE;
   1051 	cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
   1052 	cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
   1053       }
   1054       else if (!strcmp(ctype, "passcode"))
   1055       {
   1056         cparam->type = PPD_CUSTOM_PASSCODE;
   1057 	cparam->minimum.custom_passcode = atoi(cminimum);
   1058 	cparam->maximum.custom_passcode = atoi(cmaximum);
   1059       }
   1060       else if (!strcmp(ctype, "password"))
   1061       {
   1062         cparam->type = PPD_CUSTOM_PASSWORD;
   1063 	cparam->minimum.custom_password = atoi(cminimum);
   1064 	cparam->maximum.custom_password = atoi(cmaximum);
   1065       }
   1066       else if (!strcmp(ctype, "points"))
   1067       {
   1068         cparam->type = PPD_CUSTOM_POINTS;
   1069 	cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
   1070 	cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
   1071       }
   1072       else if (!strcmp(ctype, "real"))
   1073       {
   1074         cparam->type = PPD_CUSTOM_REAL;
   1075 	cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
   1076 	cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
   1077       }
   1078       else if (!strcmp(ctype, "string"))
   1079       {
   1080         cparam->type = PPD_CUSTOM_STRING;
   1081 	cparam->minimum.custom_string = atoi(cminimum);
   1082 	cparam->maximum.custom_string = atoi(cmaximum);
   1083       }
   1084       else
   1085       {
   1086         pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
   1087 
   1088 	goto error;
   1089       }
   1090 
   1091      /*
   1092       * Now special-case for CustomPageSize...
   1093       */
   1094 
   1095       if (!strcmp(coption->keyword, "PageSize"))
   1096       {
   1097 	if (!strcmp(name, "Width"))
   1098 	{
   1099 	  ppd->custom_min[0] = cparam->minimum.custom_points;
   1100 	  ppd->custom_max[0] = cparam->maximum.custom_points;
   1101 	}
   1102 	else if (!strcmp(name, "Height"))
   1103 	{
   1104 	  ppd->custom_min[1] = cparam->minimum.custom_points;
   1105 	  ppd->custom_max[1] = cparam->maximum.custom_points;
   1106 	}
   1107       }
   1108     }
   1109     else if (!strcmp(keyword, "HWMargins"))
   1110     {
   1111       for (i = 0, sptr = string; i < 4; i ++)
   1112         ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
   1113     }
   1114     else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
   1115     {
   1116       ppd_option_t	*custom_option;	/* Custom option */
   1117 
   1118       DEBUG_puts("2_ppdOpen: Processing Custom option...");
   1119 
   1120      /*
   1121       * Get the option and custom option...
   1122       */
   1123 
   1124       if (!ppd_get_coption(ppd, keyword + 6))
   1125       {
   1126         pg->ppd_status = PPD_ALLOC_ERROR;
   1127 
   1128 	goto error;
   1129       }
   1130 
   1131       if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
   1132         custom_option = option;
   1133       else
   1134         custom_option = ppdFindOption(ppd, keyword + 6);
   1135 
   1136       if (custom_option)
   1137       {
   1138        /*
   1139 	* Add the "custom" option...
   1140 	*/
   1141 
   1142         if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
   1143 	  if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
   1144 	  {
   1145 	    DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
   1146 
   1147 	    pg->ppd_status = PPD_ALLOC_ERROR;
   1148 
   1149 	    goto error;
   1150 	  }
   1151 
   1152 	strlcpy(choice->text, text[0] ? text : _("Custom"),
   1153 		sizeof(choice->text));
   1154 
   1155 	choice->code = _cupsStrAlloc(string);
   1156 
   1157 	if (custom_option->section == PPD_ORDER_JCL)
   1158 	  ppd_decode(choice->code);
   1159       }
   1160 
   1161      /*
   1162       * Now process custom page sizes specially...
   1163       */
   1164 
   1165       if (!strcmp(keyword, "CustomPageSize"))
   1166       {
   1167        /*
   1168 	* Add a "Custom" page size entry...
   1169 	*/
   1170 
   1171 	ppd->variable_sizes = 1;
   1172 
   1173 	ppd_add_size(ppd, "Custom");
   1174 
   1175 	if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
   1176 	  custom_option = option;
   1177 	else
   1178 	  custom_option = ppdFindOption(ppd, "PageRegion");
   1179 
   1180         if (custom_option)
   1181 	{
   1182 	  if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
   1183 	    if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
   1184 	    {
   1185 	      DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
   1186 
   1187 	      pg->ppd_status = PPD_ALLOC_ERROR;
   1188 
   1189 	      goto error;
   1190 	    }
   1191 
   1192 	  strlcpy(choice->text, text[0] ? text : _("Custom"),
   1193 		  sizeof(choice->text));
   1194         }
   1195       }
   1196     }
   1197     else if (!strcmp(keyword, "LandscapeOrientation"))
   1198     {
   1199       if (!strcmp(string, "Minus90"))
   1200         ppd->landscape = -90;
   1201       else if (!strcmp(string, "Plus90"))
   1202         ppd->landscape = 90;
   1203     }
   1204     else if (!strcmp(keyword, "Emulators") && string)
   1205     {
   1206       for (count = 1, sptr = string; sptr != NULL;)
   1207         if ((sptr = strchr(sptr, ' ')) != NULL)
   1208 	{
   1209 	  count ++;
   1210 	  while (*sptr == ' ')
   1211 	    sptr ++;
   1212 	}
   1213 
   1214       ppd->num_emulations = count;
   1215       if ((ppd->emulations = calloc((size_t)count, sizeof(ppd_emul_t))) == NULL)
   1216       {
   1217         pg->ppd_status = PPD_ALLOC_ERROR;
   1218 
   1219 	goto error;
   1220       }
   1221 
   1222       for (i = 0, sptr = string; i < count; i ++)
   1223       {
   1224         for (nameptr = ppd->emulations[i].name;
   1225 	     *sptr != '\0' && *sptr != ' ';
   1226 	     sptr ++)
   1227 	  if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1))
   1228 	    *nameptr++ = *sptr;
   1229 
   1230 	*nameptr = '\0';
   1231 
   1232 	while (*sptr == ' ')
   1233 	  sptr ++;
   1234       }
   1235     }
   1236     else if (!strncmp(keyword, "StartEmulator_", 14))
   1237     {
   1238       ppd_decode(string);
   1239 
   1240       for (i = 0; i < ppd->num_emulations; i ++)
   1241         if (!strcmp(keyword + 14, ppd->emulations[i].name))
   1242 	{
   1243 	  ppd->emulations[i].start = string;
   1244 	  string = NULL;
   1245 	}
   1246     }
   1247     else if (!strncmp(keyword, "StopEmulator_", 13))
   1248     {
   1249       ppd_decode(string);
   1250 
   1251       for (i = 0; i < ppd->num_emulations; i ++)
   1252         if (!strcmp(keyword + 13, ppd->emulations[i].name))
   1253 	{
   1254 	  ppd->emulations[i].stop = string;
   1255 	  string = NULL;
   1256 	}
   1257     }
   1258     else if (!strcmp(keyword, "JobPatchFile"))
   1259     {
   1260      /*
   1261       * CUPS STR #3421: Check for "*JobPatchFile: int: string"
   1262       */
   1263 
   1264       if (isdigit(*string & 255))
   1265       {
   1266         for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
   1267 
   1268         if (*sptr == ':')
   1269         {
   1270          /*
   1271           * Found "*JobPatchFile: int: string"...
   1272           */
   1273 
   1274           pg->ppd_status = PPD_BAD_VALUE;
   1275 
   1276 	  goto error;
   1277         }
   1278       }
   1279 
   1280       if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
   1281       {
   1282        /*
   1283         * Found "*JobPatchFile: string"...
   1284         */
   1285 
   1286         pg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
   1287 
   1288 	goto error;
   1289       }
   1290 
   1291       if (ppd->patches == NULL)
   1292         ppd->patches = strdup(string);
   1293       else
   1294       {
   1295         temp = realloc(ppd->patches, strlen(ppd->patches) +
   1296 	                             strlen(string) + 1);
   1297         if (temp == NULL)
   1298 	{
   1299           pg->ppd_status = PPD_ALLOC_ERROR;
   1300 
   1301 	  goto error;
   1302 	}
   1303 
   1304         ppd->patches = temp;
   1305 
   1306         memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
   1307       }
   1308     }
   1309     else if (!strcmp(keyword, "OpenUI"))
   1310     {
   1311      /*
   1312       * Don't allow nesting of options...
   1313       */
   1314 
   1315       if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
   1316       {
   1317         pg->ppd_status = PPD_NESTED_OPEN_UI;
   1318 
   1319 	goto error;
   1320       }
   1321 
   1322      /*
   1323       * Add an option record to the current sub-group, group, or file...
   1324       */
   1325 
   1326       DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
   1327 
   1328       if (name[0] == '*')
   1329         _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
   1330 
   1331       for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
   1332         name[i] = '\0'; /* Eliminate trailing spaces */
   1333 
   1334       DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
   1335                     group ? group->text : "(null)"));
   1336 
   1337       if (subgroup != NULL)
   1338         option = ppd_get_option(subgroup, name);
   1339       else if (group == NULL)
   1340       {
   1341 	if ((group = ppd_get_group(ppd, "General", _("General"), pg,
   1342 	                           encoding)) == NULL)
   1343 	  goto error;
   1344 
   1345         DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
   1346         option = ppd_get_option(group, name);
   1347 	group  = NULL;
   1348       }
   1349       else
   1350         option = ppd_get_option(group, name);
   1351 
   1352       if (option == NULL)
   1353       {
   1354         pg->ppd_status = PPD_ALLOC_ERROR;
   1355 
   1356 	goto error;
   1357       }
   1358 
   1359      /*
   1360       * Now fill in the initial information for the option...
   1361       */
   1362 
   1363       if (string && !strcmp(string, "PickMany"))
   1364         option->ui = PPD_UI_PICKMANY;
   1365       else if (string && !strcmp(string, "Boolean"))
   1366         option->ui = PPD_UI_BOOLEAN;
   1367       else if (string && !strcmp(string, "PickOne"))
   1368         option->ui = PPD_UI_PICKONE;
   1369       else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1370       {
   1371         pg->ppd_status = PPD_BAD_OPEN_UI;
   1372 
   1373 	goto error;
   1374       }
   1375       else
   1376         option->ui = PPD_UI_PICKONE;
   1377 
   1378       for (j = 0; j < ppd->num_attrs; j ++)
   1379 	if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
   1380 	    !strcmp(ppd->attrs[j]->name + 7, name) &&
   1381 	    ppd->attrs[j]->value)
   1382 	{
   1383 	  DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
   1384 	                option->keyword, ppd->attrs[j]->value));
   1385 	  strlcpy(option->defchoice, ppd->attrs[j]->value,
   1386 	          sizeof(option->defchoice));
   1387 	  break;
   1388 	}
   1389 
   1390       if (text[0])
   1391         cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
   1392 	                   sizeof(option->text), encoding);
   1393       else
   1394       {
   1395         if (!strcmp(name, "PageSize"))
   1396 	  strlcpy(option->text, _("Media Size"), sizeof(option->text));
   1397 	else if (!strcmp(name, "MediaType"))
   1398 	  strlcpy(option->text, _("Media Type"), sizeof(option->text));
   1399 	else if (!strcmp(name, "InputSlot"))
   1400 	  strlcpy(option->text, _("Media Source"), sizeof(option->text));
   1401 	else if (!strcmp(name, "ColorModel"))
   1402 	  strlcpy(option->text, _("Output Mode"), sizeof(option->text));
   1403 	else if (!strcmp(name, "Resolution"))
   1404 	  strlcpy(option->text, _("Resolution"), sizeof(option->text));
   1405         else
   1406 	  strlcpy(option->text, name, sizeof(option->text));
   1407       }
   1408 
   1409       option->section = PPD_ORDER_ANY;
   1410 
   1411       _cupsStrFree(string);
   1412       string = NULL;
   1413 
   1414      /*
   1415       * Add a custom option choice if we have already seen a CustomFoo
   1416       * attribute...
   1417       */
   1418 
   1419       if (!_cups_strcasecmp(name, "PageRegion"))
   1420         strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
   1421       else
   1422         snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
   1423 
   1424       if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
   1425       {
   1426         if ((choice = ppdFindChoice(option, "Custom")) == NULL)
   1427 	  if ((choice = ppd_add_choice(option, "Custom")) == NULL)
   1428 	  {
   1429 	    DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
   1430 
   1431 	    pg->ppd_status = PPD_ALLOC_ERROR;
   1432 
   1433 	    goto error;
   1434 	  }
   1435 
   1436 	strlcpy(choice->text,
   1437 	        custom_attr->text[0] ? custom_attr->text : _("Custom"),
   1438 		sizeof(choice->text));
   1439         choice->code = _cupsStrRetain(custom_attr->value);
   1440       }
   1441     }
   1442     else if (!strcmp(keyword, "JCLOpenUI"))
   1443     {
   1444      /*
   1445       * Don't allow nesting of options...
   1446       */
   1447 
   1448       if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
   1449       {
   1450         pg->ppd_status = PPD_NESTED_OPEN_UI;
   1451 
   1452 	goto error;
   1453       }
   1454 
   1455      /*
   1456       * Find the JCL group, and add if needed...
   1457       */
   1458 
   1459       group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
   1460 
   1461       if (group == NULL)
   1462 	goto error;
   1463 
   1464      /*
   1465       * Add an option record to the current JCLs...
   1466       */
   1467 
   1468       if (name[0] == '*')
   1469         _cups_strcpy(name, name + 1);
   1470 
   1471       option = ppd_get_option(group, name);
   1472 
   1473       if (option == NULL)
   1474       {
   1475         pg->ppd_status = PPD_ALLOC_ERROR;
   1476 
   1477 	goto error;
   1478       }
   1479 
   1480      /*
   1481       * Now fill in the initial information for the option...
   1482       */
   1483 
   1484       if (string && !strcmp(string, "PickMany"))
   1485         option->ui = PPD_UI_PICKMANY;
   1486       else if (string && !strcmp(string, "Boolean"))
   1487         option->ui = PPD_UI_BOOLEAN;
   1488       else if (string && !strcmp(string, "PickOne"))
   1489         option->ui = PPD_UI_PICKONE;
   1490       else
   1491       {
   1492         pg->ppd_status = PPD_BAD_OPEN_UI;
   1493 
   1494 	goto error;
   1495       }
   1496 
   1497       for (j = 0; j < ppd->num_attrs; j ++)
   1498 	if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
   1499 	    !strcmp(ppd->attrs[j]->name + 7, name) &&
   1500 	    ppd->attrs[j]->value)
   1501 	{
   1502 	  DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
   1503 	                option->keyword, ppd->attrs[j]->value));
   1504 	  strlcpy(option->defchoice, ppd->attrs[j]->value,
   1505 	          sizeof(option->defchoice));
   1506 	  break;
   1507 	}
   1508 
   1509       if (text[0])
   1510         cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
   1511 	                   sizeof(option->text), encoding);
   1512       else
   1513         strlcpy(option->text, name, sizeof(option->text));
   1514 
   1515       option->section = PPD_ORDER_JCL;
   1516       group = NULL;
   1517 
   1518       _cupsStrFree(string);
   1519       string = NULL;
   1520 
   1521      /*
   1522       * Add a custom option choice if we have already seen a CustomFoo
   1523       * attribute...
   1524       */
   1525 
   1526       snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
   1527 
   1528       if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
   1529       {
   1530 	if ((choice = ppd_add_choice(option, "Custom")) == NULL)
   1531 	{
   1532 	  DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
   1533 
   1534 	  pg->ppd_status = PPD_ALLOC_ERROR;
   1535 
   1536 	  goto error;
   1537 	}
   1538 
   1539 	strlcpy(choice->text,
   1540 	        custom_attr->text[0] ? custom_attr->text : _("Custom"),
   1541 		sizeof(choice->text));
   1542         choice->code = _cupsStrRetain(custom_attr->value);
   1543       }
   1544     }
   1545     else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
   1546     {
   1547       option = NULL;
   1548 
   1549       _cupsStrFree(string);
   1550       string = NULL;
   1551     }
   1552     else if (!strcmp(keyword, "OpenGroup"))
   1553     {
   1554      /*
   1555       * Open a new group...
   1556       */
   1557 
   1558       if (group != NULL)
   1559       {
   1560         pg->ppd_status = PPD_NESTED_OPEN_GROUP;
   1561 
   1562 	goto error;
   1563       }
   1564 
   1565       if (!string)
   1566       {
   1567         pg->ppd_status = PPD_BAD_OPEN_GROUP;
   1568 
   1569 	goto error;
   1570       }
   1571 
   1572      /*
   1573       * Separate the group name from the text (name/text)...
   1574       */
   1575 
   1576       if ((sptr = strchr(string, '/')) != NULL)
   1577         *sptr++ = '\0';
   1578       else
   1579         sptr = string;
   1580 
   1581      /*
   1582       * Fix up the text...
   1583       */
   1584 
   1585       ppd_decode(sptr);
   1586 
   1587      /*
   1588       * Find/add the group...
   1589       */
   1590 
   1591       group = ppd_get_group(ppd, string, sptr, pg, encoding);
   1592 
   1593       if (group == NULL)
   1594 	goto error;
   1595 
   1596       _cupsStrFree(string);
   1597       string = NULL;
   1598     }
   1599     else if (!strcmp(keyword, "CloseGroup"))
   1600     {
   1601       group = NULL;
   1602 
   1603       _cupsStrFree(string);
   1604       string = NULL;
   1605     }
   1606     else if (!strcmp(keyword, "OrderDependency"))
   1607     {
   1608       order = (float)_cupsStrScand(string, &sptr, loc);
   1609 
   1610       if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
   1611       {
   1612         pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
   1613 
   1614 	goto error;
   1615       }
   1616 
   1617       if (keyword[0] == '*')
   1618         _cups_strcpy(keyword, keyword + 1);
   1619 
   1620       if (!strcmp(name, "ExitServer"))
   1621         section = PPD_ORDER_EXIT;
   1622       else if (!strcmp(name, "Prolog"))
   1623         section = PPD_ORDER_PROLOG;
   1624       else if (!strcmp(name, "DocumentSetup"))
   1625         section = PPD_ORDER_DOCUMENT;
   1626       else if (!strcmp(name, "PageSetup"))
   1627         section = PPD_ORDER_PAGE;
   1628       else if (!strcmp(name, "JCLSetup"))
   1629         section = PPD_ORDER_JCL;
   1630       else
   1631         section = PPD_ORDER_ANY;
   1632 
   1633       if (option == NULL)
   1634       {
   1635         ppd_group_t	*gtemp;
   1636 
   1637 
   1638        /*
   1639         * Only valid for Non-UI options...
   1640 	*/
   1641 
   1642         for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
   1643           if (gtemp->text[0] == '\0')
   1644 	    break;
   1645 
   1646         if (i > 0)
   1647           for (i = 0; i < gtemp->num_options; i ++)
   1648 	    if (!strcmp(keyword, gtemp->options[i].keyword))
   1649 	    {
   1650 	      gtemp->options[i].section = section;
   1651 	      gtemp->options[i].order   = order;
   1652 	      break;
   1653 	    }
   1654       }
   1655       else
   1656       {
   1657         option->section = section;
   1658 	option->order   = order;
   1659       }
   1660 
   1661       _cupsStrFree(string);
   1662       string = NULL;
   1663     }
   1664     else if (!strncmp(keyword, "Default", 7))
   1665     {
   1666       if (string == NULL)
   1667         continue;
   1668 
   1669      /*
   1670       * Drop UI text, if any, from value...
   1671       */
   1672 
   1673       if (strchr(string, '/') != NULL)
   1674         *strchr(string, '/') = '\0';
   1675 
   1676      /*
   1677       * Assign the default value as appropriate...
   1678       */
   1679 
   1680       if (!strcmp(keyword, "DefaultColorSpace"))
   1681       {
   1682        /*
   1683         * Set default colorspace...
   1684 	*/
   1685 
   1686 	if (!strcmp(string, "CMY"))
   1687           ppd->colorspace = PPD_CS_CMY;
   1688 	else if (!strcmp(string, "CMYK"))
   1689           ppd->colorspace = PPD_CS_CMYK;
   1690 	else if (!strcmp(string, "RGB"))
   1691           ppd->colorspace = PPD_CS_RGB;
   1692 	else if (!strcmp(string, "RGBK"))
   1693           ppd->colorspace = PPD_CS_RGBK;
   1694 	else if (!strcmp(string, "N"))
   1695           ppd->colorspace = PPD_CS_N;
   1696 	else
   1697           ppd->colorspace = PPD_CS_GRAY;
   1698       }
   1699       else if (option && !strcmp(keyword + 7, option->keyword))
   1700       {
   1701        /*
   1702         * Set the default as part of the current option...
   1703 	*/
   1704 
   1705         DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
   1706 
   1707         strlcpy(option->defchoice, string, sizeof(option->defchoice));
   1708 
   1709         DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
   1710       }
   1711       else
   1712       {
   1713        /*
   1714         * Lookup option and set if it has been defined...
   1715 	*/
   1716 
   1717         ppd_option_t	*toption;	/* Temporary option */
   1718 
   1719 
   1720         if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
   1721 	{
   1722 	  DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
   1723 	  strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
   1724 	}
   1725       }
   1726     }
   1727     else if (!strcmp(keyword, "UIConstraints") ||
   1728              !strcmp(keyword, "NonUIConstraints"))
   1729     {
   1730       if (!string)
   1731       {
   1732 	pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1733 	goto error;
   1734       }
   1735 
   1736       if (ppd->num_consts == 0)
   1737 	constraint = calloc(2, sizeof(ppd_const_t));
   1738       else
   1739 	constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
   1740 
   1741       if (constraint == NULL)
   1742       {
   1743         pg->ppd_status = PPD_ALLOC_ERROR;
   1744 
   1745 	goto error;
   1746       }
   1747 
   1748       ppd->consts = constraint;
   1749       constraint += ppd->num_consts;
   1750       ppd->num_consts ++;
   1751 
   1752       switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
   1753                      constraint->choice1, constraint->option2,
   1754 		     constraint->choice2))
   1755       {
   1756         case 0 : /* Error */
   1757 	case 1 : /* Error */
   1758 	    pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1759 	    goto error;
   1760 
   1761 	case 2 : /* Two options... */
   1762 	   /*
   1763 	    * Check for broken constraints like "* Option"...
   1764 	    */
   1765 
   1766 	    if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1767 	        (!strcmp(constraint->option1, "*") ||
   1768 	         !strcmp(constraint->choice1, "*")))
   1769 	    {
   1770 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1771 	      goto error;
   1772 	    }
   1773 
   1774 	   /*
   1775 	    * The following strcpy's are safe, as optionN and
   1776 	    * choiceN are all the same size (size defined by PPD spec...)
   1777 	    */
   1778 
   1779 	    if (constraint->option1[0] == '*')
   1780 	      _cups_strcpy(constraint->option1, constraint->option1 + 1);
   1781 	    else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1782 	    {
   1783 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1784 	      goto error;
   1785 	    }
   1786 
   1787 	    if (constraint->choice1[0] == '*')
   1788 	      _cups_strcpy(constraint->option2, constraint->choice1 + 1);
   1789 	    else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1790 	    {
   1791 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1792 	      goto error;
   1793 	    }
   1794 
   1795             constraint->choice1[0] = '\0';
   1796             constraint->choice2[0] = '\0';
   1797 	    break;
   1798 
   1799 	case 3 : /* Two options, one choice... */
   1800 	   /*
   1801 	    * Check for broken constraints like "* Option"...
   1802 	    */
   1803 
   1804 	    if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1805 	        (!strcmp(constraint->option1, "*") ||
   1806 	         !strcmp(constraint->choice1, "*") ||
   1807 	         !strcmp(constraint->option2, "*")))
   1808 	    {
   1809 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1810 	      goto error;
   1811 	    }
   1812 
   1813 	   /*
   1814 	    * The following _cups_strcpy's are safe, as optionN and
   1815 	    * choiceN are all the same size (size defined by PPD spec...)
   1816 	    */
   1817 
   1818 	    if (constraint->option1[0] == '*')
   1819 	      _cups_strcpy(constraint->option1, constraint->option1 + 1);
   1820 	    else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1821 	    {
   1822 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1823 	      goto error;
   1824 	    }
   1825 
   1826 	    if (constraint->choice1[0] == '*')
   1827 	    {
   1828 	      if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1829 	          constraint->option2[0] == '*')
   1830 	      {
   1831 		pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1832 		goto error;
   1833 	      }
   1834 
   1835 	      _cups_strcpy(constraint->choice2, constraint->option2);
   1836 	      _cups_strcpy(constraint->option2, constraint->choice1 + 1);
   1837               constraint->choice1[0] = '\0';
   1838 	    }
   1839 	    else
   1840 	    {
   1841 	      if (constraint->option2[0] == '*')
   1842   	        _cups_strcpy(constraint->option2, constraint->option2 + 1);
   1843 	      else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1844 	      {
   1845 		pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1846 		goto error;
   1847 	      }
   1848 
   1849               constraint->choice2[0] = '\0';
   1850 	    }
   1851 	    break;
   1852 
   1853 	case 4 : /* Two options, two choices... */
   1854 	   /*
   1855 	    * Check for broken constraints like "* Option"...
   1856 	    */
   1857 
   1858 	    if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1859 	        (!strcmp(constraint->option1, "*") ||
   1860 	         !strcmp(constraint->choice1, "*") ||
   1861 	         !strcmp(constraint->option2, "*") ||
   1862 	         !strcmp(constraint->choice2, "*")))
   1863 	    {
   1864 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1865 	      goto error;
   1866 	    }
   1867 
   1868 	    if (constraint->option1[0] == '*')
   1869 	      _cups_strcpy(constraint->option1, constraint->option1 + 1);
   1870 	    else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1871 	    {
   1872 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1873 	      goto error;
   1874 	    }
   1875 
   1876             if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1877 	        constraint->choice1[0] == '*')
   1878 	    {
   1879 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1880 	      goto error;
   1881 	    }
   1882 
   1883 	    if (constraint->option2[0] == '*')
   1884   	      _cups_strcpy(constraint->option2, constraint->option2 + 1);
   1885 	    else if (pg->ppd_conform == PPD_CONFORM_STRICT)
   1886 	    {
   1887 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1888 	      goto error;
   1889 	    }
   1890 
   1891             if (pg->ppd_conform == PPD_CONFORM_STRICT &&
   1892 	        constraint->choice2[0] == '*')
   1893 	    {
   1894 	      pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
   1895 	      goto error;
   1896 	    }
   1897 	    break;
   1898       }
   1899 
   1900      /*
   1901       * Don't add this one as an attribute...
   1902       */
   1903 
   1904       _cupsStrFree(string);
   1905       string = NULL;
   1906     }
   1907     else if (!strcmp(keyword, "PaperDimension"))
   1908     {
   1909       if ((size = ppdPageSize(ppd, name)) == NULL)
   1910 	size = ppd_add_size(ppd, name);
   1911 
   1912       if (size == NULL)
   1913       {
   1914        /*
   1915         * Unable to add or find size!
   1916 	*/
   1917 
   1918         pg->ppd_status = PPD_ALLOC_ERROR;
   1919 
   1920 	goto error;
   1921       }
   1922 
   1923       size->width  = (float)_cupsStrScand(string, &sptr, loc);
   1924       size->length = (float)_cupsStrScand(sptr, NULL, loc);
   1925 
   1926       _cupsStrFree(string);
   1927       string = NULL;
   1928     }
   1929     else if (!strcmp(keyword, "ImageableArea"))
   1930     {
   1931       if ((size = ppdPageSize(ppd, name)) == NULL)
   1932 	size = ppd_add_size(ppd, name);
   1933 
   1934       if (size == NULL)
   1935       {
   1936        /*
   1937         * Unable to add or find size!
   1938 	*/
   1939 
   1940         pg->ppd_status = PPD_ALLOC_ERROR;
   1941 
   1942 	goto error;
   1943       }
   1944 
   1945       size->left   = (float)_cupsStrScand(string, &sptr, loc);
   1946       size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
   1947       size->right  = (float)_cupsStrScand(sptr, &sptr, loc);
   1948       size->top    = (float)_cupsStrScand(sptr, NULL, loc);
   1949 
   1950       _cupsStrFree(string);
   1951       string = NULL;
   1952     }
   1953     else if (option != NULL &&
   1954              (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
   1955 	         (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
   1956 	     !strcmp(keyword, option->keyword))
   1957     {
   1958       DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
   1959 
   1960       if (!strcmp(keyword, "PageSize"))
   1961       {
   1962        /*
   1963         * Add a page size...
   1964 	*/
   1965 
   1966         if (ppdPageSize(ppd, name) == NULL)
   1967 	  ppd_add_size(ppd, name);
   1968       }
   1969 
   1970      /*
   1971       * Add the option choice...
   1972       */
   1973 
   1974       if ((choice = ppd_add_choice(option, name)) == NULL)
   1975       {
   1976         pg->ppd_status = PPD_ALLOC_ERROR;
   1977 
   1978 	goto error;
   1979       }
   1980 
   1981       if (text[0])
   1982         cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
   1983 	                   sizeof(choice->text), encoding);
   1984       else if (!strcmp(name, "True"))
   1985         strlcpy(choice->text, _("Yes"), sizeof(choice->text));
   1986       else if (!strcmp(name, "False"))
   1987         strlcpy(choice->text, _("No"), sizeof(choice->text));
   1988       else
   1989         strlcpy(choice->text, name, sizeof(choice->text));
   1990 
   1991       if (option->section == PPD_ORDER_JCL)
   1992         ppd_decode(string);		/* Decode quoted string */
   1993 
   1994       choice->code = string;
   1995       string       = NULL;		/* Don't add as an attribute below */
   1996     }
   1997 
   1998    /*
   1999     * Add remaining lines with keywords and string values as attributes...
   2000     */
   2001 
   2002     if (string &&
   2003         (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
   2004       ppd_add_attr(ppd, keyword, name, text, string);
   2005     else
   2006       _cupsStrFree(string);
   2007   }
   2008 
   2009  /*
   2010   * Check for a missing CloseGroup...
   2011   */
   2012 
   2013   if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
   2014   {
   2015     pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
   2016     goto error;
   2017   }
   2018 
   2019   ppd_free(line.buffer);
   2020 
   2021  /*
   2022   * Reset language preferences...
   2023   */
   2024 
   2025 #ifdef DEBUG
   2026   if (!cupsFileEOF(fp))
   2027     DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
   2028                   (unsigned long)cupsFileTell(fp)));
   2029 #endif /* DEBUG */
   2030 
   2031   if (pg->ppd_status != PPD_OK)
   2032   {
   2033    /*
   2034     * Had an error reading the PPD file, cannot continue!
   2035     */
   2036 
   2037     ppdClose(ppd);
   2038 
   2039     return (NULL);
   2040   }
   2041 
   2042  /*
   2043   * Update the filters array as needed...
   2044   */
   2045 
   2046   if (!ppd_update_filters(ppd, pg))
   2047   {
   2048     ppdClose(ppd);
   2049 
   2050     return (NULL);
   2051   }
   2052 
   2053  /*
   2054   * Create the sorted options array and set the option back-pointer for
   2055   * each choice and custom option...
   2056   */
   2057 
   2058   ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
   2059                                (cups_ahash_func_t)ppd_hash_option,
   2060 			       PPD_HASHSIZE);
   2061 
   2062   for (i = ppd->num_groups, group = ppd->groups;
   2063        i > 0;
   2064        i --, group ++)
   2065   {
   2066     for (j = group->num_options, option = group->options;
   2067          j > 0;
   2068 	 j --, option ++)
   2069     {
   2070       ppd_coption_t	*coption;	/* Custom option */
   2071 
   2072 
   2073       cupsArrayAdd(ppd->options, option);
   2074 
   2075       for (k = 0; k < option->num_choices; k ++)
   2076         option->choices[k].option = option;
   2077 
   2078       if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
   2079         coption->option = option;
   2080     }
   2081   }
   2082 
   2083  /*
   2084   * Create an array to track the marked choices...
   2085   */
   2086 
   2087   ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
   2088 
   2089  /*
   2090   * Return the PPD file structure...
   2091   */
   2092 
   2093   return (ppd);
   2094 
   2095  /*
   2096   * Common exit point for errors to save code size...
   2097   */
   2098 
   2099   error:
   2100 
   2101   _cupsStrFree(string);
   2102   ppd_free(line.buffer);
   2103 
   2104   ppdClose(ppd);
   2105 
   2106   return (NULL);
   2107 }
   2108 
   2109 
   2110 /*
   2111  * 'ppdOpen()' - Read a PPD file into memory.
   2112  */
   2113 
   2114 ppd_file_t *				/* O - PPD file record */
   2115 ppdOpen(FILE *fp)			/* I - File to read from */
   2116 {
   2117   ppd_file_t	*ppd;			/* PPD file record */
   2118   cups_file_t	*cf;			/* CUPS file */
   2119 
   2120 
   2121  /*
   2122   * Reopen the stdio file as a CUPS file...
   2123   */
   2124 
   2125   if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
   2126     return (NULL);
   2127 
   2128  /*
   2129   * Load the PPD file using the newer API...
   2130   */
   2131 
   2132   ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
   2133 
   2134  /*
   2135   * Close the CUPS file and return the PPD...
   2136   */
   2137 
   2138   cupsFileClose(cf);
   2139 
   2140   return (ppd);
   2141 }
   2142 
   2143 
   2144 /*
   2145  * 'ppdOpen2()' - Read a PPD file into memory.
   2146  *
   2147  * @since CUPS 1.2/macOS 10.5@
   2148  */
   2149 
   2150 ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
   2151 ppdOpen2(cups_file_t *fp)		/* I - File to read from */
   2152 {
   2153   return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
   2154 }
   2155 
   2156 
   2157 /*
   2158  * 'ppdOpenFd()' - Read a PPD file into memory.
   2159  */
   2160 
   2161 ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
   2162 ppdOpenFd(int fd)			/* I - File to read from */
   2163 {
   2164   cups_file_t		*fp;		/* CUPS file pointer */
   2165   ppd_file_t		*ppd;		/* PPD file record */
   2166   _ppd_globals_t	*pg = _ppdGlobals();
   2167 					/* Global data */
   2168 
   2169 
   2170  /*
   2171   * Set the line number to 0...
   2172   */
   2173 
   2174   pg->ppd_line = 0;
   2175 
   2176  /*
   2177   * Range check input...
   2178   */
   2179 
   2180   if (fd < 0)
   2181   {
   2182     pg->ppd_status = PPD_NULL_FILE;
   2183 
   2184     return (NULL);
   2185   }
   2186 
   2187  /*
   2188   * Try to open the file and parse it...
   2189   */
   2190 
   2191   if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
   2192   {
   2193     ppd = ppdOpen2(fp);
   2194 
   2195     cupsFileClose(fp);
   2196   }
   2197   else
   2198   {
   2199     pg->ppd_status = PPD_FILE_OPEN_ERROR;
   2200     ppd            = NULL;
   2201   }
   2202 
   2203   return (ppd);
   2204 }
   2205 
   2206 
   2207 /*
   2208  * '_ppdOpenFile()' - Read a PPD file into memory.
   2209  */
   2210 
   2211 ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
   2212 _ppdOpenFile(const char		  *filename,	/* I - File to read from */
   2213 	     _ppd_localization_t  localization)	/* I - Localization to load */
   2214 {
   2215   cups_file_t		*fp;		/* File pointer */
   2216   ppd_file_t		*ppd;		/* PPD file record */
   2217   _ppd_globals_t	*pg = _ppdGlobals();
   2218 					/* Global data */
   2219 
   2220 
   2221  /*
   2222   * Set the line number to 0...
   2223   */
   2224 
   2225   pg->ppd_line = 0;
   2226 
   2227  /*
   2228   * Range check input...
   2229   */
   2230 
   2231   if (filename == NULL)
   2232   {
   2233     pg->ppd_status = PPD_NULL_FILE;
   2234 
   2235     return (NULL);
   2236   }
   2237 
   2238  /*
   2239   * Try to open the file and parse it...
   2240   */
   2241 
   2242   if ((fp = cupsFileOpen(filename, "r")) != NULL)
   2243   {
   2244     ppd = _ppdOpen(fp, localization);
   2245 
   2246     cupsFileClose(fp);
   2247   }
   2248   else
   2249   {
   2250     pg->ppd_status = PPD_FILE_OPEN_ERROR;
   2251     ppd            = NULL;
   2252   }
   2253 
   2254   return (ppd);
   2255 }
   2256 
   2257 
   2258 /*
   2259  * 'ppdOpenFile()' - Read a PPD file into memory.
   2260  */
   2261 
   2262 ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
   2263 ppdOpenFile(const char *filename)	/* I - File to read from */
   2264 {
   2265   return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
   2266 }
   2267 
   2268 
   2269 /*
   2270  * 'ppdSetConformance()' - Set the conformance level for PPD files.
   2271  *
   2272  * @since CUPS 1.1.20/macOS 10.4@
   2273  */
   2274 
   2275 void
   2276 ppdSetConformance(ppd_conform_t c)	/* I - Conformance level */
   2277 {
   2278   _ppd_globals_t	*pg = _ppdGlobals();
   2279 					/* Global data */
   2280 
   2281 
   2282   pg->ppd_conform = c;
   2283 }
   2284 
   2285 
   2286 /*
   2287  * 'ppd_add_attr()' - Add an attribute to the PPD data.
   2288  */
   2289 
   2290 static ppd_attr_t *			/* O - New attribute */
   2291 ppd_add_attr(ppd_file_t *ppd,		/* I - PPD file data */
   2292              const char *name,		/* I - Attribute name */
   2293              const char *spec,		/* I - Specifier string, if any */
   2294 	     const char *text,		/* I - Text string, if any */
   2295 	     const char *value)		/* I - Value of attribute */
   2296 {
   2297   ppd_attr_t	**ptr,			/* New array */
   2298 		*temp;			/* New attribute */
   2299 
   2300 
   2301  /*
   2302   * Range check input...
   2303   */
   2304 
   2305   if (ppd == NULL || name == NULL || spec == NULL)
   2306     return (NULL);
   2307 
   2308  /*
   2309   * Create the array as needed...
   2310   */
   2311 
   2312   if (!ppd->sorted_attrs)
   2313     ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
   2314                                      NULL);
   2315 
   2316  /*
   2317   * Allocate memory for the new attribute...
   2318   */
   2319 
   2320   if (ppd->num_attrs == 0)
   2321     ptr = malloc(sizeof(ppd_attr_t *));
   2322   else
   2323     ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
   2324 
   2325   if (ptr == NULL)
   2326     return (NULL);
   2327 
   2328   ppd->attrs = ptr;
   2329   ptr += ppd->num_attrs;
   2330 
   2331   if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
   2332     return (NULL);
   2333 
   2334   *ptr = temp;
   2335 
   2336   ppd->num_attrs ++;
   2337 
   2338  /*
   2339   * Copy data over...
   2340   */
   2341 
   2342   strlcpy(temp->name, name, sizeof(temp->name));
   2343   strlcpy(temp->spec, spec, sizeof(temp->spec));
   2344   strlcpy(temp->text, text, sizeof(temp->text));
   2345   temp->value = (char *)value;
   2346 
   2347  /*
   2348   * Add the attribute to the sorted array...
   2349   */
   2350 
   2351   cupsArrayAdd(ppd->sorted_attrs, temp);
   2352 
   2353  /*
   2354   * Return the attribute...
   2355   */
   2356 
   2357   return (temp);
   2358 }
   2359 
   2360 
   2361 /*
   2362  * 'ppd_add_choice()' - Add a choice to an option.
   2363  */
   2364 
   2365 static ppd_choice_t *			/* O - Named choice */
   2366 ppd_add_choice(ppd_option_t *option,	/* I - Option */
   2367                const char   *name)	/* I - Name of choice */
   2368 {
   2369   ppd_choice_t	*choice;		/* Choice */
   2370 
   2371 
   2372   if (option->num_choices == 0)
   2373     choice = malloc(sizeof(ppd_choice_t));
   2374   else
   2375     choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
   2376 
   2377   if (choice == NULL)
   2378     return (NULL);
   2379 
   2380   option->choices = choice;
   2381   choice += option->num_choices;
   2382   option->num_choices ++;
   2383 
   2384   memset(choice, 0, sizeof(ppd_choice_t));
   2385   strlcpy(choice->choice, name, sizeof(choice->choice));
   2386 
   2387   return (choice);
   2388 }
   2389 
   2390 
   2391 /*
   2392  * 'ppd_add_size()' - Add a page size.
   2393  */
   2394 
   2395 static ppd_size_t *			/* O - Named size */
   2396 ppd_add_size(ppd_file_t *ppd,		/* I - PPD file */
   2397              const char *name)		/* I - Name of size */
   2398 {
   2399   ppd_size_t	*size;			/* Size */
   2400 
   2401 
   2402   if (ppd->num_sizes == 0)
   2403     size = malloc(sizeof(ppd_size_t));
   2404   else
   2405     size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
   2406 
   2407   if (size == NULL)
   2408     return (NULL);
   2409 
   2410   ppd->sizes = size;
   2411   size += ppd->num_sizes;
   2412   ppd->num_sizes ++;
   2413 
   2414   memset(size, 0, sizeof(ppd_size_t));
   2415   strlcpy(size->name, name, sizeof(size->name));
   2416 
   2417   return (size);
   2418 }
   2419 
   2420 
   2421 /*
   2422  * 'ppd_compare_attrs()' - Compare two attributes.
   2423  */
   2424 
   2425 static int				/* O - Result of comparison */
   2426 ppd_compare_attrs(ppd_attr_t *a,	/* I - First attribute */
   2427                   ppd_attr_t *b)	/* I - Second attribute */
   2428 {
   2429   return (_cups_strcasecmp(a->name, b->name));
   2430 }
   2431 
   2432 
   2433 /*
   2434  * 'ppd_compare_choices()' - Compare two choices...
   2435  */
   2436 
   2437 static int				/* O - Result of comparison */
   2438 ppd_compare_choices(ppd_choice_t *a,	/* I - First choice */
   2439                     ppd_choice_t *b)	/* I - Second choice */
   2440 {
   2441   return (strcmp(a->option->keyword, b->option->keyword));
   2442 }
   2443 
   2444 
   2445 /*
   2446  * 'ppd_compare_coptions()' - Compare two custom options.
   2447  */
   2448 
   2449 static int				/* O - Result of comparison */
   2450 ppd_compare_coptions(ppd_coption_t *a,	/* I - First option */
   2451                      ppd_coption_t *b)	/* I - Second option */
   2452 {
   2453   return (_cups_strcasecmp(a->keyword, b->keyword));
   2454 }
   2455 
   2456 
   2457 /*
   2458  * 'ppd_compare_options()' - Compare two options.
   2459  */
   2460 
   2461 static int				/* O - Result of comparison */
   2462 ppd_compare_options(ppd_option_t *a,	/* I - First option */
   2463                     ppd_option_t *b)	/* I - Second option */
   2464 {
   2465   return (_cups_strcasecmp(a->keyword, b->keyword));
   2466 }
   2467 
   2468 
   2469 /*
   2470  * 'ppd_decode()' - Decode a string value...
   2471  */
   2472 
   2473 static int				/* O - Length of decoded string */
   2474 ppd_decode(char *string)		/* I - String to decode */
   2475 {
   2476   char	*inptr,				/* Input pointer */
   2477 	*outptr;			/* Output pointer */
   2478 
   2479 
   2480   inptr  = string;
   2481   outptr = string;
   2482 
   2483   while (*inptr != '\0')
   2484     if (*inptr == '<' && isxdigit(inptr[1] & 255))
   2485     {
   2486      /*
   2487       * Convert hex to 8-bit values...
   2488       */
   2489 
   2490       inptr ++;
   2491       while (isxdigit(*inptr & 255))
   2492       {
   2493 	if (_cups_isalpha(*inptr))
   2494 	  *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
   2495 	else
   2496 	  *outptr = (char)((*inptr - '0') << 4);
   2497 
   2498 	inptr ++;
   2499 
   2500         if (!isxdigit(*inptr & 255))
   2501 	  break;
   2502 
   2503 	if (_cups_isalpha(*inptr))
   2504 	  *outptr |= (char)(tolower(*inptr) - 'a' + 10);
   2505 	else
   2506 	  *outptr |= (char)(*inptr - '0');
   2507 
   2508 	inptr ++;
   2509 	outptr ++;
   2510       }
   2511 
   2512       while (*inptr != '>' && *inptr != '\0')
   2513 	inptr ++;
   2514       while (*inptr == '>')
   2515 	inptr ++;
   2516     }
   2517     else
   2518       *outptr++ = *inptr++;
   2519 
   2520   *outptr = '\0';
   2521 
   2522   return ((int)(outptr - string));
   2523 }
   2524 
   2525 
   2526 /*
   2527  * 'ppd_free_filters()' - Free the filters array.
   2528  */
   2529 
   2530 static void
   2531 ppd_free_filters(ppd_file_t *ppd)	/* I - PPD file */
   2532 {
   2533   int	i;				/* Looping var */
   2534   char	**filter;			/* Current filter */
   2535 
   2536 
   2537   if (ppd->num_filters > 0)
   2538   {
   2539     for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
   2540       _cupsStrFree(*filter);
   2541 
   2542     ppd_free(ppd->filters);
   2543 
   2544     ppd->num_filters = 0;
   2545     ppd->filters     = NULL;
   2546   }
   2547 }
   2548 
   2549 
   2550 /*
   2551  * 'ppd_free_group()' - Free a single UI group.
   2552  */
   2553 
   2554 static void
   2555 ppd_free_group(ppd_group_t *group)	/* I - Group to free */
   2556 {
   2557   int		i;			/* Looping var */
   2558   ppd_option_t	*option;		/* Current option */
   2559   ppd_group_t	*subgroup;		/* Current sub-group */
   2560 
   2561 
   2562   if (group->num_options > 0)
   2563   {
   2564     for (i = group->num_options, option = group->options;
   2565          i > 0;
   2566 	 i --, option ++)
   2567       ppd_free_option(option);
   2568 
   2569     ppd_free(group->options);
   2570   }
   2571 
   2572   if (group->num_subgroups > 0)
   2573   {
   2574     for (i = group->num_subgroups, subgroup = group->subgroups;
   2575          i > 0;
   2576 	 i --, subgroup ++)
   2577       ppd_free_group(subgroup);
   2578 
   2579     ppd_free(group->subgroups);
   2580   }
   2581 }
   2582 
   2583 
   2584 /*
   2585  * 'ppd_free_option()' - Free a single option.
   2586  */
   2587 
   2588 static void
   2589 ppd_free_option(ppd_option_t *option)	/* I - Option to free */
   2590 {
   2591   int		i;			/* Looping var */
   2592   ppd_choice_t	*choice;		/* Current choice */
   2593 
   2594 
   2595   if (option->num_choices > 0)
   2596   {
   2597     for (i = option->num_choices, choice = option->choices;
   2598          i > 0;
   2599          i --, choice ++)
   2600     {
   2601       _cupsStrFree(choice->code);
   2602     }
   2603 
   2604     ppd_free(option->choices);
   2605   }
   2606 }
   2607 
   2608 
   2609 /*
   2610  * 'ppd_get_coption()' - Get a custom option record.
   2611  */
   2612 
   2613 static ppd_coption_t	*		/* O - Custom option... */
   2614 ppd_get_coption(ppd_file_t *ppd,	/* I - PPD file */
   2615                 const char *name)	/* I - Name of option */
   2616 {
   2617   ppd_coption_t	*copt;			/* New custom option */
   2618 
   2619 
   2620  /*
   2621   * See if the option already exists...
   2622   */
   2623 
   2624   if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
   2625     return (copt);
   2626 
   2627  /*
   2628   * Not found, so create the custom option record...
   2629   */
   2630 
   2631   if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
   2632     return (NULL);
   2633 
   2634   strlcpy(copt->keyword, name, sizeof(copt->keyword));
   2635 
   2636   copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
   2637 
   2638   cupsArrayAdd(ppd->coptions, copt);
   2639 
   2640  /*
   2641   * Return the new record...
   2642   */
   2643 
   2644   return (copt);
   2645 }
   2646 
   2647 
   2648 /*
   2649  * 'ppd_get_cparam()' - Get a custom parameter record.
   2650  */
   2651 
   2652 static ppd_cparam_t *			/* O - Extended option... */
   2653 ppd_get_cparam(ppd_coption_t *opt,	/* I - PPD file */
   2654                const char    *param,	/* I - Name of parameter */
   2655 	       const char    *text)	/* I - Human-readable text */
   2656 {
   2657   ppd_cparam_t	*cparam;		/* New custom parameter */
   2658 
   2659 
   2660  /*
   2661   * See if the parameter already exists...
   2662   */
   2663 
   2664   if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
   2665     return (cparam);
   2666 
   2667  /*
   2668   * Not found, so create the custom parameter record...
   2669   */
   2670 
   2671   if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
   2672     return (NULL);
   2673 
   2674   strlcpy(cparam->name, param, sizeof(cparam->name));
   2675   strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
   2676 
   2677  /*
   2678   * Add this record to the array...
   2679   */
   2680 
   2681   cupsArrayAdd(opt->params, cparam);
   2682 
   2683  /*
   2684   * Return the new record...
   2685   */
   2686 
   2687   return (cparam);
   2688 }
   2689 
   2690 
   2691 /*
   2692  * 'ppd_get_group()' - Find or create the named group as needed.
   2693  */
   2694 
   2695 static ppd_group_t *			/* O - Named group */
   2696 ppd_get_group(ppd_file_t      *ppd,	/* I - PPD file */
   2697               const char      *name,	/* I - Name of group */
   2698 	      const char      *text,	/* I - Text for group */
   2699               _ppd_globals_t  *pg,	/* I - Global data */
   2700 	      cups_encoding_t encoding)	/* I - Encoding of text */
   2701 {
   2702   int		i;			/* Looping var */
   2703   ppd_group_t	*group;			/* Group */
   2704 
   2705 
   2706   DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
   2707                 ppd, name, text, pg));
   2708 
   2709   for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
   2710     if (!strcmp(group->name, name))
   2711       break;
   2712 
   2713   if (i == 0)
   2714   {
   2715     DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
   2716 
   2717     if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
   2718     {
   2719       pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
   2720 
   2721       return (NULL);
   2722     }
   2723 
   2724     if (ppd->num_groups == 0)
   2725       group = malloc(sizeof(ppd_group_t));
   2726     else
   2727       group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
   2728 
   2729     if (group == NULL)
   2730     {
   2731       pg->ppd_status = PPD_ALLOC_ERROR;
   2732 
   2733       return (NULL);
   2734     }
   2735 
   2736     ppd->groups = group;
   2737     group += ppd->num_groups;
   2738     ppd->num_groups ++;
   2739 
   2740     memset(group, 0, sizeof(ppd_group_t));
   2741     strlcpy(group->name, name, sizeof(group->name));
   2742 
   2743     cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
   2744 	               sizeof(group->text), encoding);
   2745   }
   2746 
   2747   return (group);
   2748 }
   2749 
   2750 
   2751 /*
   2752  * 'ppd_get_option()' - Find or create the named option as needed.
   2753  */
   2754 
   2755 static ppd_option_t *			/* O - Named option */
   2756 ppd_get_option(ppd_group_t *group,	/* I - Group */
   2757                const char  *name)	/* I - Name of option */
   2758 {
   2759   int		i;			/* Looping var */
   2760   ppd_option_t	*option;		/* Option */
   2761 
   2762 
   2763   DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
   2764                 group, group->name, name));
   2765 
   2766   for (i = group->num_options, option = group->options; i > 0; i --, option ++)
   2767     if (!strcmp(option->keyword, name))
   2768       break;
   2769 
   2770   if (i == 0)
   2771   {
   2772     if (group->num_options == 0)
   2773       option = malloc(sizeof(ppd_option_t));
   2774     else
   2775       option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
   2776 
   2777     if (option == NULL)
   2778       return (NULL);
   2779 
   2780     group->options = option;
   2781     option += group->num_options;
   2782     group->num_options ++;
   2783 
   2784     memset(option, 0, sizeof(ppd_option_t));
   2785     strlcpy(option->keyword, name, sizeof(option->keyword));
   2786   }
   2787 
   2788   return (option);
   2789 }
   2790 
   2791 
   2792 /*
   2793  * 'ppd_globals_alloc()' - Allocate and initialize global data.
   2794  */
   2795 
   2796 static _ppd_globals_t *		/* O - Pointer to global data */
   2797 ppd_globals_alloc(void)
   2798 {
   2799   return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
   2800 }
   2801 
   2802 
   2803 /*
   2804  * 'ppd_globals_free()' - Free global data.
   2805  */
   2806 
   2807 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
   2808 static void
   2809 ppd_globals_free(_ppd_globals_t *pg)	/* I - Pointer to global data */
   2810 {
   2811   free(pg);
   2812 }
   2813 #endif /* HAVE_PTHREAD_H || WIN32 */
   2814 
   2815 
   2816 #ifdef HAVE_PTHREAD_H
   2817 /*
   2818  * 'ppd_globals_init()' - Initialize per-thread globals...
   2819  */
   2820 
   2821 static void
   2822 ppd_globals_init(void)
   2823 {
   2824  /*
   2825   * Register the global data for this thread...
   2826   */
   2827 
   2828   pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
   2829 }
   2830 #endif /* HAVE_PTHREAD_H */
   2831 
   2832 
   2833 /*
   2834  * 'ppd_hash_option()' - Generate a hash of the option name...
   2835  */
   2836 
   2837 static int				/* O - Hash index */
   2838 ppd_hash_option(ppd_option_t *option)	/* I - Option */
   2839 {
   2840   int		hash = 0;		/* Hash index */
   2841   const char	*k;			/* Pointer into keyword */
   2842 
   2843 
   2844   for (hash = option->keyword[0], k = option->keyword + 1; *k;)
   2845     hash = 33 * hash + *k++;
   2846 
   2847   return (hash & 511);
   2848 }
   2849 
   2850 
   2851 /*
   2852  * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
   2853  *                necessary.
   2854  */
   2855 
   2856 static int				/* O - Bitmask of fields read */
   2857 ppd_read(cups_file_t    *fp,		/* I - File to read from */
   2858          _ppd_line_t    *line,		/* I - Line buffer */
   2859          char           *keyword,	/* O - Keyword from line */
   2860 	 char           *option,	/* O - Option from line */
   2861          char           *text,		/* O - Human-readable text from line */
   2862 	 char           **string,	/* O - Code/string data */
   2863          int            ignoreblank,	/* I - Ignore blank lines? */
   2864 	 _ppd_globals_t *pg)		/* I - Global data */
   2865 {
   2866   int		ch,			/* Character from file */
   2867 		col,			/* Column in line */
   2868 		colon,			/* Colon seen? */
   2869 		endquote,		/* Waiting for an end quote */
   2870 		mask,			/* Mask to be returned */
   2871 		startline,		/* Start line */
   2872 		textlen;		/* Length of text */
   2873   char		*keyptr,		/* Keyword pointer */
   2874 		*optptr,		/* Option pointer */
   2875 		*textptr,		/* Text pointer */
   2876 		*strptr,		/* Pointer into string */
   2877 		*lineptr;		/* Current position in line buffer */
   2878 
   2879 
   2880  /*
   2881   * Now loop until we have a valid line...
   2882   */
   2883 
   2884   *string   = NULL;
   2885   col       = 0;
   2886   startline = pg->ppd_line + 1;
   2887 
   2888   if (!line->buffer)
   2889   {
   2890     line->bufsize = 1024;
   2891     line->buffer  = malloc(1024);
   2892 
   2893     if (!line->buffer)
   2894       return (0);
   2895   }
   2896 
   2897   do
   2898   {
   2899    /*
   2900     * Read the line...
   2901     */
   2902 
   2903     lineptr  = line->buffer;
   2904     endquote = 0;
   2905     colon    = 0;
   2906 
   2907     while ((ch = cupsFileGetChar(fp)) != EOF)
   2908     {
   2909       if (lineptr >= (line->buffer + line->bufsize - 1))
   2910       {
   2911        /*
   2912         * Expand the line buffer...
   2913 	*/
   2914 
   2915         char *temp;			/* Temporary line pointer */
   2916 
   2917 
   2918         line->bufsize += 1024;
   2919 	if (line->bufsize > 262144)
   2920 	{
   2921 	 /*
   2922 	  * Don't allow lines longer than 256k!
   2923 	  */
   2924 
   2925           pg->ppd_line   = startline;
   2926           pg->ppd_status = PPD_LINE_TOO_LONG;
   2927 
   2928 	  return (0);
   2929 	}
   2930 
   2931         temp = realloc(line->buffer, line->bufsize);
   2932 	if (!temp)
   2933 	{
   2934           pg->ppd_line   = startline;
   2935           pg->ppd_status = PPD_LINE_TOO_LONG;
   2936 
   2937 	  return (0);
   2938 	}
   2939 
   2940         lineptr      = temp + (lineptr - line->buffer);
   2941 	line->buffer = temp;
   2942       }
   2943 
   2944       if (ch == '\r' || ch == '\n')
   2945       {
   2946        /*
   2947 	* Line feed or carriage return...
   2948 	*/
   2949 
   2950         pg->ppd_line ++;
   2951 	col = 0;
   2952 
   2953 	if (ch == '\r')
   2954 	{
   2955 	 /*
   2956           * Check for a trailing line feed...
   2957 	  */
   2958 
   2959 	  if ((ch = cupsFilePeekChar(fp)) == EOF)
   2960 	  {
   2961 	    ch = '\n';
   2962 	    break;
   2963 	  }
   2964 
   2965 	  if (ch == 0x0a)
   2966 	    cupsFileGetChar(fp);
   2967 	}
   2968 
   2969 	if (lineptr == line->buffer && ignoreblank)
   2970           continue;			/* Skip blank lines */
   2971 
   2972 	ch = '\n';
   2973 
   2974 	if (!endquote)			/* Continue for multi-line text */
   2975           break;
   2976 
   2977 	*lineptr++ = '\n';
   2978       }
   2979       else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
   2980       {
   2981        /*
   2982         * Other control characters...
   2983 	*/
   2984 
   2985         pg->ppd_line   = startline;
   2986         pg->ppd_status = PPD_ILLEGAL_CHARACTER;
   2987 
   2988         return (0);
   2989       }
   2990       else if (ch != 0x1a)
   2991       {
   2992        /*
   2993 	* Any other character...
   2994 	*/
   2995 
   2996 	*lineptr++ = (char)ch;
   2997 	col ++;
   2998 
   2999 	if (col > (PPD_MAX_LINE - 1))
   3000 	{
   3001 	 /*
   3002           * Line is too long...
   3003 	  */
   3004 
   3005           pg->ppd_line   = startline;
   3006           pg->ppd_status = PPD_LINE_TOO_LONG;
   3007 
   3008           return (0);
   3009 	}
   3010 
   3011 	if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
   3012 	  colon = 1;
   3013 
   3014 	if (ch == '\"' && colon)
   3015 	  endquote = !endquote;
   3016       }
   3017     }
   3018 
   3019     if (endquote)
   3020     {
   3021      /*
   3022       * Didn't finish this quoted string...
   3023       */
   3024 
   3025       while ((ch = cupsFileGetChar(fp)) != EOF)
   3026         if (ch == '\"')
   3027 	  break;
   3028 	else if (ch == '\r' || ch == '\n')
   3029 	{
   3030 	  pg->ppd_line ++;
   3031 	  col = 0;
   3032 
   3033 	  if (ch == '\r')
   3034 	  {
   3035 	   /*
   3036             * Check for a trailing line feed...
   3037 	    */
   3038 
   3039 	    if ((ch = cupsFilePeekChar(fp)) == EOF)
   3040 	      break;
   3041 	    if (ch == 0x0a)
   3042 	      cupsFileGetChar(fp);
   3043 	  }
   3044 	}
   3045 	else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
   3046 	{
   3047 	 /*
   3048           * Other control characters...
   3049 	  */
   3050 
   3051           pg->ppd_line   = startline;
   3052           pg->ppd_status = PPD_ILLEGAL_CHARACTER;
   3053 
   3054           return (0);
   3055 	}
   3056 	else if (ch != 0x1a)
   3057 	{
   3058 	  col ++;
   3059 
   3060 	  if (col > (PPD_MAX_LINE - 1))
   3061 	  {
   3062 	   /*
   3063             * Line is too long...
   3064 	    */
   3065 
   3066             pg->ppd_line   = startline;
   3067             pg->ppd_status = PPD_LINE_TOO_LONG;
   3068 
   3069             return (0);
   3070 	  }
   3071 	}
   3072     }
   3073 
   3074     if (ch != '\n')
   3075     {
   3076      /*
   3077       * Didn't finish this line...
   3078       */
   3079 
   3080       while ((ch = cupsFileGetChar(fp)) != EOF)
   3081 	if (ch == '\r' || ch == '\n')
   3082 	{
   3083 	 /*
   3084 	  * Line feed or carriage return...
   3085 	  */
   3086 
   3087           pg->ppd_line ++;
   3088 	  col = 0;
   3089 
   3090 	  if (ch == '\r')
   3091 	  {
   3092 	   /*
   3093             * Check for a trailing line feed...
   3094 	    */
   3095 
   3096 	    if ((ch = cupsFilePeekChar(fp)) == EOF)
   3097 	      break;
   3098 	    if (ch == 0x0a)
   3099 	      cupsFileGetChar(fp);
   3100 	  }
   3101 
   3102 	  break;
   3103 	}
   3104 	else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
   3105 	{
   3106 	 /*
   3107           * Other control characters...
   3108 	  */
   3109 
   3110           pg->ppd_line   = startline;
   3111           pg->ppd_status = PPD_ILLEGAL_CHARACTER;
   3112 
   3113           return (0);
   3114 	}
   3115 	else if (ch != 0x1a)
   3116 	{
   3117 	  col ++;
   3118 
   3119 	  if (col > (PPD_MAX_LINE - 1))
   3120 	  {
   3121 	   /*
   3122             * Line is too long...
   3123 	    */
   3124 
   3125             pg->ppd_line   = startline;
   3126             pg->ppd_status = PPD_LINE_TOO_LONG;
   3127 
   3128             return (0);
   3129 	  }
   3130 	}
   3131     }
   3132 
   3133     if (lineptr > line->buffer && lineptr[-1] == '\n')
   3134       lineptr --;
   3135 
   3136     *lineptr = '\0';
   3137 
   3138     DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
   3139 
   3140    /*
   3141     * The dynamically created PPDs for older style macOS
   3142     * drivers include a large blob of data inserted as comments
   3143     * at the end of the file.  As an optimization we can stop
   3144     * reading the PPD when we get to the start of this data.
   3145     */
   3146 
   3147     if (!strcmp(line->buffer, "*%APLWORKSET START"))
   3148       return (0);
   3149 
   3150     if (ch == EOF && lineptr == line->buffer)
   3151       return (0);
   3152 
   3153    /*
   3154     * Now parse it...
   3155     */
   3156 
   3157     mask    = 0;
   3158     lineptr = line->buffer + 1;
   3159 
   3160     keyword[0] = '\0';
   3161     option[0]  = '\0';
   3162     text[0]    = '\0';
   3163     *string    = NULL;
   3164 
   3165     if ((!line->buffer[0] ||		/* Blank line */
   3166          !strncmp(line->buffer, "*%", 2) || /* Comment line */
   3167          !strcmp(line->buffer, "*End")) && /* End of multi-line string */
   3168         ignoreblank)			/* Ignore these? */
   3169     {
   3170       startline = pg->ppd_line + 1;
   3171       continue;
   3172     }
   3173 
   3174     if (!strcmp(line->buffer, "*"))	/* (Bad) comment line */
   3175     {
   3176       if (pg->ppd_conform == PPD_CONFORM_RELAXED)
   3177       {
   3178 	startline = pg->ppd_line + 1;
   3179 	continue;
   3180       }
   3181       else
   3182       {
   3183         pg->ppd_line   = startline;
   3184         pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
   3185 
   3186         return (0);
   3187       }
   3188     }
   3189 
   3190     if (line->buffer[0] != '*')		/* All lines start with an asterisk */
   3191     {
   3192      /*
   3193       * Allow lines consisting of just whitespace...
   3194       */
   3195 
   3196       for (lineptr = line->buffer; *lineptr; lineptr ++)
   3197         if (*lineptr && !_cups_isspace(*lineptr))
   3198 	  break;
   3199 
   3200       if (*lineptr)
   3201       {
   3202         pg->ppd_status = PPD_MISSING_ASTERISK;
   3203         return (0);
   3204       }
   3205       else if (ignoreblank)
   3206         continue;
   3207       else
   3208         return (0);
   3209     }
   3210 
   3211    /*
   3212     * Get a keyword...
   3213     */
   3214 
   3215     keyptr = keyword;
   3216 
   3217     while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
   3218     {
   3219       if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
   3220           (keyptr - keyword) >= (PPD_MAX_NAME - 1))
   3221       {
   3222         pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
   3223 	return (0);
   3224       }
   3225 
   3226       *keyptr++ = *lineptr++;
   3227     }
   3228 
   3229     *keyptr = '\0';
   3230 
   3231     if (!strcmp(keyword, "End"))
   3232       continue;
   3233 
   3234     mask |= PPD_KEYWORD;
   3235 
   3236     if (_cups_isspace(*lineptr))
   3237     {
   3238      /*
   3239       * Get an option name...
   3240       */
   3241 
   3242       while (_cups_isspace(*lineptr))
   3243         lineptr ++;
   3244 
   3245       optptr = option;
   3246 
   3247       while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
   3248              *lineptr != '/')
   3249       {
   3250 	if (*lineptr <= ' ' || *lineptr > 126 ||
   3251 	    (optptr - option) >= (PPD_MAX_NAME - 1))
   3252         {
   3253           pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
   3254 	  return (0);
   3255 	}
   3256 
   3257         *optptr++ = *lineptr++;
   3258       }
   3259 
   3260       *optptr = '\0';
   3261 
   3262       if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
   3263       {
   3264         pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
   3265 	return (0);
   3266       }
   3267 
   3268       while (_cups_isspace(*lineptr))
   3269 	lineptr ++;
   3270 
   3271       mask |= PPD_OPTION;
   3272 
   3273       if (*lineptr == '/')
   3274       {
   3275        /*
   3276         * Get human-readable text...
   3277 	*/
   3278 
   3279         lineptr ++;
   3280 
   3281 	textptr = text;
   3282 
   3283 	while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
   3284 	{
   3285 	  if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
   3286 	      (textptr - text) >= (PPD_MAX_LINE - 1))
   3287 	  {
   3288 	    pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
   3289 	    return (0);
   3290 	  }
   3291 
   3292 	  *textptr++ = *lineptr++;
   3293         }
   3294 
   3295 	*textptr = '\0';
   3296 	textlen  = ppd_decode(text);
   3297 
   3298 	if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
   3299 	{
   3300 	  pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
   3301 	  return (0);
   3302 	}
   3303 
   3304 	mask |= PPD_TEXT;
   3305       }
   3306     }
   3307 
   3308     if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
   3309     {
   3310       pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
   3311       return (0);
   3312     }
   3313 
   3314     while (_cups_isspace(*lineptr))
   3315       lineptr ++;
   3316 
   3317     if (*lineptr == ':')
   3318     {
   3319      /*
   3320       * Get string after triming leading and trailing whitespace...
   3321       */
   3322 
   3323       lineptr ++;
   3324       while (_cups_isspace(*lineptr))
   3325         lineptr ++;
   3326 
   3327       strptr = lineptr + strlen(lineptr) - 1;
   3328       while (strptr >= lineptr && _cups_isspace(*strptr))
   3329         *strptr-- = '\0';
   3330 
   3331       if (*strptr == '\"')
   3332       {
   3333        /*
   3334         * Quoted string by itself, remove quotes...
   3335 	*/
   3336 
   3337         *strptr = '\0';
   3338 	lineptr ++;
   3339       }
   3340 
   3341       *string = _cupsStrAlloc(lineptr);
   3342 
   3343       mask |= PPD_STRING;
   3344     }
   3345   }
   3346   while (mask == 0);
   3347 
   3348   return (mask);
   3349 }
   3350 
   3351 
   3352 /*
   3353  * 'ppd_update_filters()' - Update the filters array as needed.
   3354  *
   3355  * This function re-populates the filters array with cupsFilter2 entries that
   3356  * have been stripped of the destination MIME media types and any maxsize hints.
   3357  *
   3358  * (All for backwards-compatibility)
   3359  */
   3360 
   3361 static int				/* O - 1 on success, 0 on failure */
   3362 ppd_update_filters(ppd_file_t     *ppd,	/* I - PPD file */
   3363                    _ppd_globals_t *pg)	/* I - Global data */
   3364 {
   3365   ppd_attr_t	*attr;			/* Current cupsFilter2 value */
   3366   char		srcsuper[16],		/* Source MIME media type */
   3367 		srctype[256],
   3368 		dstsuper[16],		/* Destination MIME media type */
   3369 		dsttype[256],
   3370 		program[1024],		/* Command to run */
   3371 		*ptr,			/* Pointer into command to run */
   3372 		buffer[1024],		/* Re-written cupsFilter value */
   3373 		**filter;		/* Current filter */
   3374   int		cost;			/* Cost of filter */
   3375 
   3376 
   3377   DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
   3378 
   3379  /*
   3380   * See if we have any cupsFilter2 lines...
   3381   */
   3382 
   3383   if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
   3384   {
   3385     DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
   3386     return (1);
   3387   }
   3388 
   3389  /*
   3390   * Yes, free the cupsFilter-defined filters and re-build...
   3391   */
   3392 
   3393   ppd_free_filters(ppd);
   3394 
   3395   do
   3396   {
   3397    /*
   3398     * Parse the cupsFilter2 string:
   3399     *
   3400     *   src/type dst/type cost program
   3401     *   src/type dst/type cost maxsize(n) program
   3402     */
   3403 
   3404     DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
   3405 
   3406     if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
   3407 	       srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
   3408     {
   3409       DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
   3410       pg->ppd_status = PPD_BAD_VALUE;
   3411 
   3412       return (0);
   3413     }
   3414 
   3415     DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
   3416                   "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
   3417 		  srcsuper, srctype, dstsuper, dsttype, cost, program));
   3418 
   3419     if (!strncmp(program, "maxsize(", 8) &&
   3420         (ptr = strchr(program + 8, ')')) != NULL)
   3421     {
   3422       DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
   3423 
   3424       ptr ++;
   3425       while (_cups_isspace(*ptr))
   3426 	ptr ++;
   3427 
   3428       _cups_strcpy(program, ptr);
   3429       DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
   3430     }
   3431 
   3432    /*
   3433     * Convert to cupsFilter format:
   3434     *
   3435     *   src/type cost program
   3436     */
   3437 
   3438     snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
   3439              program);
   3440     DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
   3441 
   3442    /*
   3443     * Add a cupsFilter-compatible string to the filters array.
   3444     */
   3445 
   3446     if (ppd->num_filters == 0)
   3447       filter = malloc(sizeof(char *));
   3448     else
   3449       filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
   3450 
   3451     if (filter == NULL)
   3452     {
   3453       DEBUG_puts("5ppd_update_filters: Out of memory.");
   3454       pg->ppd_status = PPD_ALLOC_ERROR;
   3455 
   3456       return (0);
   3457     }
   3458 
   3459     ppd->filters     = filter;
   3460     filter           += ppd->num_filters;
   3461     ppd->num_filters ++;
   3462 
   3463     *filter = _cupsStrAlloc(buffer);
   3464   }
   3465   while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
   3466 
   3467   DEBUG_puts("5ppd_update_filters: Completed OK.");
   3468   return (1);
   3469 }
   3470