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