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