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