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