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