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