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