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