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