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