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