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
51typedef 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
62static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER;
63					/* Thread local storage key */
64#ifdef HAVE_PTHREAD_H
65static 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
74static ppd_attr_t	*ppd_add_attr(ppd_file_t *ppd, const char *name,
75			              const char *spec, const char *text,
76				      const char *value);
77static ppd_choice_t	*ppd_add_choice(ppd_option_t *option, const char *name);
78static ppd_size_t	*ppd_add_size(ppd_file_t *ppd, const char *name);
79static int		ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
80static int		ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
81static int		ppd_compare_coptions(ppd_coption_t *a,
82			                     ppd_coption_t *b);
83static int		ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
84static int		ppd_decode(char *string);
85static void		ppd_free_filters(ppd_file_t *ppd);
86static void		ppd_free_group(ppd_group_t *group);
87static void		ppd_free_option(ppd_option_t *option);
88static ppd_coption_t	*ppd_get_coption(ppd_file_t *ppd, const char *name);
89static ppd_cparam_t	*ppd_get_cparam(ppd_coption_t *opt,
90			                const char *param,
91					const char *text);
92static 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);
95static ppd_option_t	*ppd_get_option(ppd_group_t *group, const char *name);
96static _ppd_globals_t	*ppd_globals_alloc(void);
97#if defined(HAVE_PTHREAD_H) || defined(WIN32)
98static void		ppd_globals_free(_ppd_globals_t *g);
99#endif /* HAVE_PTHREAD_H || WIN32 */
100#ifdef HAVE_PTHREAD_H
101static void		ppd_globals_init(void);
102#endif /* HAVE_PTHREAD_H */
103static int		ppd_hash_option(ppd_option_t *option);
104static 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);
108static 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
116void
117ppdClose(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
308const char *				/* O - Status string */
309ppdErrorString(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
352cups_encoding_t				/* O - CUPS encoding value */
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 */
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
418ppd_status_t				/* O - Status code */
419ppdLastError(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
438ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
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
2114ppd_file_t *				/* O - PPD file record */
2115ppdOpen(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
2150ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2151ppdOpen2(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
2161ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2162ppdOpenFd(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
2211ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
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
2262ppd_file_t *				/* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2263ppdOpenFile(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
2275void
2276ppdSetConformance(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
2290static ppd_attr_t *			/* O - New attribute */
2291ppd_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
2365static ppd_choice_t *			/* O - Named choice */
2366ppd_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
2395static ppd_size_t *			/* O - Named size */
2396ppd_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
2425static int				/* O - Result of comparison */
2426ppd_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
2437static int				/* O - Result of comparison */
2438ppd_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
2449static int				/* O - Result of comparison */
2450ppd_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
2461static int				/* O - Result of comparison */
2462ppd_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
2473static int				/* O - Length of decoded string */
2474ppd_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
2530static void
2531ppd_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
2554static void
2555ppd_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
2588static void
2589ppd_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
2613static ppd_coption_t	*		/* O - Custom option... */
2614ppd_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
2652static ppd_cparam_t *			/* O - Extended option... */
2653ppd_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
2695static ppd_group_t *			/* O - Named group */
2696ppd_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
2755static ppd_option_t *			/* O - Named option */
2756ppd_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
2796static _ppd_globals_t *		/* O - Pointer to global data */
2797ppd_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)
2808static void
2809ppd_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
2821static void
2822ppd_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
2837static int				/* O - Hash index */
2838ppd_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
2856static int				/* O - Bitmask of fields read */
2857ppd_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
3361static int				/* O - 1 on success, 0 on failure */
3362ppd_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