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