]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ppd.c
Merge pull request #5621 from zdohnal/cgigetarray-sigsegv
[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
ef416fc2 614 /*
615 * Allocate memory for the PPD file record...
616 */
617
618 if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
619 {
f787e1e3 620 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 621
5a00cf37
MS
622 free(string);
623 free(line.buffer);
9f5eb9be 624
ef416fc2 625 return (NULL);
626 }
627
60d8f884
MS
628 free(string);
629 string = NULL;
630
355e94dc 631 ppd->language_level = 2;
ef416fc2 632 ppd->color_device = 0;
839a51c8 633 ppd->colorspace = PPD_CS_N;
ef416fc2 634 ppd->landscape = -90;
60d8f884 635 ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions, 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));
db1c94f3
MS
708 free(string);
709 string = NULL;
9c80ffa2
MS
710 continue;
711 }
712 else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
713 {
714 /*
715 * Only load localizations for the color profile related keywords...
716 */
717
718 for (i = 0;
719 i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
720 i ++)
721 {
722 if (!_cups_strcasecmp(temp, color_keywords[i]))
723 break;
724 }
725
726 if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
727 {
728 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
db1c94f3
MS
729 free(string);
730 string = NULL;
9c80ffa2
MS
731 continue;
732 }
733 }
734 }
735
ef416fc2 736 if (option == NULL &&
737 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
738 (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
739 {
740 for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
741 if (!strcmp(keyword, ui_keywords[i]))
742 break;
743
744 if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
745 {
746 /*
747 * Create the option in the appropriate group...
748 */
749
750 ui_keyword = 1;
751
9c80ffa2 752 DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
ef416fc2 753 keyword));
754
755 if (!group)
756 {
f787e1e3 757 if ((group = ppd_get_group(ppd, "General", _("General"), pg,
e1d6a774 758 encoding)) == NULL)
ef416fc2 759 goto error;
760
9c80ffa2 761 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
ef416fc2 762 option = ppd_get_option(group, keyword);
763 group = NULL;
764 }
765 else
766 option = ppd_get_option(group, keyword);
767
768 if (option == NULL)
769 {
f787e1e3 770 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 771
772 goto error;
773 }
774
775 /*
776 * Now fill in the initial information for the option...
777 */
778
779 if (!strncmp(keyword, "JCL", 3))
780 option->section = PPD_ORDER_JCL;
781 else
782 option->section = PPD_ORDER_ANY;
783
784 option->order = 10.0f;
785
786 if (i < 8)
787 option->ui = PPD_UI_BOOLEAN;
788 else
789 option->ui = PPD_UI_PICKONE;
790
791 for (j = 0; j < ppd->num_attrs; j ++)
792 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
793 !strcmp(ppd->attrs[j]->name + 7, keyword) &&
794 ppd->attrs[j]->value)
795 {
9c80ffa2 796 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
ef416fc2 797 option->keyword, ppd->attrs[j]->value));
798 strlcpy(option->defchoice, ppd->attrs[j]->value,
799 sizeof(option->defchoice));
800 break;
801 }
802
803 if (!strcmp(keyword, "PageSize"))
804 strlcpy(option->text, _("Media Size"), sizeof(option->text));
805 else if (!strcmp(keyword, "MediaType"))
806 strlcpy(option->text, _("Media Type"), sizeof(option->text));
807 else if (!strcmp(keyword, "InputSlot"))
808 strlcpy(option->text, _("Media Source"), sizeof(option->text));
809 else if (!strcmp(keyword, "ColorModel"))
810 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
811 else if (!strcmp(keyword, "Resolution"))
812 strlcpy(option->text, _("Resolution"), sizeof(option->text));
813 else
814 strlcpy(option->text, keyword, sizeof(option->text));
815 }
816 }
817
818 if (!strcmp(keyword, "LanguageLevel"))
819 ppd->language_level = atoi(string);
820 else if (!strcmp(keyword, "LanguageEncoding"))
e1d6a774 821 {
822 /*
823 * Say all PPD files are UTF-8, since we convert to UTF-8...
824 */
825
5a00cf37 826 ppd->lang_encoding = strdup("UTF-8");
80ca4592 827 encoding = _ppdGetEncoding(string);
e1d6a774 828 }
ef416fc2 829 else if (!strcmp(keyword, "LanguageVersion"))
830 ppd->lang_version = string;
831 else if (!strcmp(keyword, "Manufacturer"))
832 ppd->manufacturer = string;
833 else if (!strcmp(keyword, "ModelName"))
834 ppd->modelname = string;
835 else if (!strcmp(keyword, "Protocols"))
836 ppd->protocols = string;
837 else if (!strcmp(keyword, "PCFileName"))
838 ppd->pcfilename = string;
839 else if (!strcmp(keyword, "NickName"))
e1d6a774 840 {
841 if (encoding != CUPS_UTF8)
842 {
843 cups_utf8_t utf8[256]; /* UTF-8 version of NickName */
844
845
846 cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
5a00cf37 847 ppd->nickname = strdup((char *)utf8);
e1d6a774 848 }
849 else
5a00cf37 850 ppd->nickname = strdup(string);
e1d6a774 851 }
ef416fc2 852 else if (!strcmp(keyword, "Product"))
853 ppd->product = string;
854 else if (!strcmp(keyword, "ShortNickName"))
855 ppd->shortnickname = string;
856 else if (!strcmp(keyword, "TTRasterizer"))
857 ppd->ttrasterizer = string;
858 else if (!strcmp(keyword, "JCLBegin"))
859 {
5a00cf37 860 ppd->jcl_begin = strdup(string);
ef416fc2 861 ppd_decode(ppd->jcl_begin); /* Decode quoted string */
862 }
863 else if (!strcmp(keyword, "JCLEnd"))
864 {
5a00cf37 865 ppd->jcl_end = strdup(string);
ef416fc2 866 ppd_decode(ppd->jcl_end); /* Decode quoted string */
867 }
868 else if (!strcmp(keyword, "JCLToPSInterpreter"))
869 {
5a00cf37 870 ppd->jcl_ps = strdup(string);
ef416fc2 871 ppd_decode(ppd->jcl_ps); /* Decode quoted string */
872 }
873 else if (!strcmp(keyword, "AccurateScreensSupport"))
874 ppd->accurate_screens = !strcmp(string, "True");
875 else if (!strcmp(keyword, "ColorDevice"))
876 ppd->color_device = !strcmp(string, "True");
877 else if (!strcmp(keyword, "ContoneOnly"))
878 ppd->contone_only = !strcmp(string, "True");
879 else if (!strcmp(keyword, "cupsFlipDuplex"))
880 ppd->flip_duplex = !strcmp(string, "True");
881 else if (!strcmp(keyword, "cupsManualCopies"))
882 ppd->manual_copies = !strcmp(string, "True");
883 else if (!strcmp(keyword, "cupsModelNumber"))
884 ppd->model_number = atoi(string);
885 else if (!strcmp(keyword, "cupsColorProfile"))
886 {
887 if (ppd->num_profiles == 0)
888 profile = malloc(sizeof(ppd_profile_t));
889 else
7e86f2f6 890 profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * (size_t)(ppd->num_profiles + 1));
ef416fc2 891
91c84a35
MS
892 if (!profile)
893 {
f787e1e3 894 pg->ppd_status = PPD_ALLOC_ERROR;
91c84a35
MS
895
896 goto error;
897 }
898
ef416fc2 899 ppd->profiles = profile;
900 profile += ppd->num_profiles;
901 ppd->num_profiles ++;
902
903 memset(profile, 0, sizeof(ppd_profile_t));
904 strlcpy(profile->resolution, name, sizeof(profile->resolution));
905 strlcpy(profile->media_type, text, sizeof(profile->media_type));
757d2cad 906
b86bc4cf 907 profile->density = (float)_cupsStrScand(string, &sptr, loc);
908 profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc);
909 profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
910 profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
911 profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
912 profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
913 profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
914 profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
915 profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
916 profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
917 profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
ef416fc2 918 }
919 else if (!strcmp(keyword, "cupsFilter"))
920 {
921 if (ppd->num_filters == 0)
922 filter = malloc(sizeof(char *));
923 else
7e86f2f6 924 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
ef416fc2 925
926 if (filter == NULL)
927 {
f787e1e3 928 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 929
930 goto error;
931 }
932
933 ppd->filters = filter;
934 filter += ppd->num_filters;
935 ppd->num_filters ++;
936
937 /*
5a00cf37 938 * Make a copy of the filter string...
ef416fc2 939 */
940
5a00cf37 941 *filter = strdup(string);
ef416fc2 942 }
943 else if (!strcmp(keyword, "Throughput"))
944 ppd->throughput = atoi(string);
945 else if (!strcmp(keyword, "Font"))
946 {
947 /*
948 * Add this font to the list of available fonts...
949 */
950
951 if (ppd->num_fonts == 0)
952 tempfonts = (char **)malloc(sizeof(char *));
953 else
7e86f2f6 954 tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * (size_t)(ppd->num_fonts + 1));
ef416fc2 955
956 if (tempfonts == NULL)
957 {
f787e1e3 958 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 959
960 goto error;
961 }
88f9aafc 962
ef416fc2 963 ppd->fonts = tempfonts;
5a00cf37 964 ppd->fonts[ppd->num_fonts] = strdup(name);
ef416fc2 965 ppd->num_fonts ++;
966 }
fa73b229 967 else if (!strncmp(keyword, "ParamCustom", 11))
ef416fc2 968 {
fa73b229 969 ppd_coption_t *coption; /* Custom option */
970 ppd_cparam_t *cparam; /* Custom parameter */
971 int corder; /* Order number */
972 char ctype[33], /* Data type */
973 cminimum[65], /* Minimum value */
974 cmaximum[65]; /* Maximum value */
975
976
977 /*
978 * Get the custom option and parameter...
979 */
ef416fc2 980
fa73b229 981 if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
ef416fc2 982 {
f787e1e3 983 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 984
fa73b229 985 goto error;
986 }
ef416fc2 987
fa73b229 988 if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
989 {
f787e1e3 990 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 991
fa73b229 992 goto error;
993 }
994
dc00a7c3
MS
995 if (cparam->type != PPD_CUSTOM_UNKNOWN)
996 {
997 pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
998
999 goto error;
1000 }
1001
fa73b229 1002 /*
1003 * Get the parameter data...
1004 */
1005
b0f6947b
MS
1006 if (!string ||
1007 sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
fa73b229 1008 cmaximum) != 4)
1009 {
f787e1e3 1010 pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
fa73b229 1011
1012 goto error;
1013 }
1014
1015 cparam->order = corder;
ef416fc2 1016
fa73b229 1017 if (!strcmp(ctype, "curve"))
1018 {
1019 cparam->type = PPD_CUSTOM_CURVE;
b86bc4cf 1020 cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
1021 cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
fa73b229 1022 }
1023 else if (!strcmp(ctype, "int"))
1024 {
1025 cparam->type = PPD_CUSTOM_INT;
1026 cparam->minimum.custom_int = atoi(cminimum);
1027 cparam->maximum.custom_int = atoi(cmaximum);
1028 }
1029 else if (!strcmp(ctype, "invcurve"))
1030 {
1031 cparam->type = PPD_CUSTOM_INVCURVE;
b86bc4cf 1032 cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
1033 cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
fa73b229 1034 }
1035 else if (!strcmp(ctype, "passcode"))
1036 {
1037 cparam->type = PPD_CUSTOM_PASSCODE;
b423cd4c 1038 cparam->minimum.custom_passcode = atoi(cminimum);
1039 cparam->maximum.custom_passcode = atoi(cmaximum);
fa73b229 1040 }
1041 else if (!strcmp(ctype, "password"))
1042 {
1043 cparam->type = PPD_CUSTOM_PASSWORD;
b423cd4c 1044 cparam->minimum.custom_password = atoi(cminimum);
1045 cparam->maximum.custom_password = atoi(cmaximum);
fa73b229 1046 }
1047 else if (!strcmp(ctype, "points"))
1048 {
1049 cparam->type = PPD_CUSTOM_POINTS;
b86bc4cf 1050 cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
1051 cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
fa73b229 1052 }
1053 else if (!strcmp(ctype, "real"))
1054 {
1055 cparam->type = PPD_CUSTOM_REAL;
b86bc4cf 1056 cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
1057 cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
fa73b229 1058 }
1059 else if (!strcmp(ctype, "string"))
1060 {
1061 cparam->type = PPD_CUSTOM_STRING;
b423cd4c 1062 cparam->minimum.custom_string = atoi(cminimum);
1063 cparam->maximum.custom_string = atoi(cmaximum);
fa73b229 1064 }
1065 else
1066 {
f787e1e3 1067 pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
fa73b229 1068
1069 goto error;
1070 }
1071
1072 /*
1073 * Now special-case for CustomPageSize...
1074 */
1075
1076 if (!strcmp(coption->keyword, "PageSize"))
1077 {
1078 if (!strcmp(name, "Width"))
1079 {
1080 ppd->custom_min[0] = cparam->minimum.custom_points;
1081 ppd->custom_max[0] = cparam->maximum.custom_points;
1082 }
1083 else if (!strcmp(name, "Height"))
ef416fc2 1084 {
fa73b229 1085 ppd->custom_min[1] = cparam->minimum.custom_points;
1086 ppd->custom_max[1] = cparam->maximum.custom_points;
1087 }
1088 }
1089 }
1090 else if (!strcmp(keyword, "HWMargins"))
757d2cad 1091 {
1092 for (i = 0, sptr = string; i < 4; i ++)
b86bc4cf 1093 ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
757d2cad 1094 }
323c5de1 1095 else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
fa73b229 1096 {
0a682745
MS
1097 ppd_option_t *custom_option; /* Custom option */
1098
9c80ffa2 1099 DEBUG_puts("2_ppdOpen: Processing Custom option...");
ef416fc2 1100
fa73b229 1101 /*
1102 * Get the option and custom option...
1103 */
ef416fc2 1104
e1d6a774 1105 if (!ppd_get_coption(ppd, keyword + 6))
ef416fc2 1106 {
f787e1e3 1107 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1108
1109 goto error;
1110 }
1111
88f9aafc 1112 if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
0a682745
MS
1113 custom_option = option;
1114 else
1115 custom_option = ppdFindOption(ppd, keyword + 6);
fa73b229 1116
0a682745 1117 if (custom_option)
ef416fc2 1118 {
0a682745
MS
1119 /*
1120 * Add the "custom" option...
1121 */
ef416fc2 1122
749b1e90
MS
1123 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
1124 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
1125 {
9c80ffa2 1126 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
ef416fc2 1127
f787e1e3 1128 pg->ppd_status = PPD_ALLOC_ERROR;
0a682745 1129
749b1e90
MS
1130 goto error;
1131 }
ef416fc2 1132
0a682745
MS
1133 strlcpy(choice->text, text[0] ? text : _("Custom"),
1134 sizeof(choice->text));
fa73b229 1135
5a00cf37 1136 choice->code = strdup(string);
005dd1eb
MS
1137
1138 if (custom_option->section == PPD_ORDER_JCL)
1139 ppd_decode(choice->code);
0a682745 1140 }
fa73b229 1141
1142 /*
1143 * Now process custom page sizes specially...
1144 */
1145
1146 if (!strcmp(keyword, "CustomPageSize"))
1147 {
fa73b229 1148 /*
1149 * Add a "Custom" page size entry...
1150 */
1151
0a682745 1152 ppd->variable_sizes = 1;
323c5de1 1153
0a682745 1154 ppd_add_size(ppd, "Custom");
323c5de1 1155
88f9aafc 1156 if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
0a682745
MS
1157 custom_option = option;
1158 else
1159 custom_option = ppdFindOption(ppd, "PageRegion");
09a101d6 1160
0a682745
MS
1161 if (custom_option)
1162 {
749b1e90
MS
1163 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
1164 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
1165 {
9c80ffa2 1166 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
0a682745 1167
f787e1e3 1168 pg->ppd_status = PPD_ALLOC_ERROR;
323c5de1 1169
749b1e90
MS
1170 goto error;
1171 }
323c5de1 1172
0a682745
MS
1173 strlcpy(choice->text, text[0] ? text : _("Custom"),
1174 sizeof(choice->text));
323c5de1 1175 }
fa73b229 1176 }
ef416fc2 1177 }
1178 else if (!strcmp(keyword, "LandscapeOrientation"))
1179 {
1180 if (!strcmp(string, "Minus90"))
1181 ppd->landscape = -90;
1182 else if (!strcmp(string, "Plus90"))
1183 ppd->landscape = 90;
1184 }
4c00fa53
MS
1185 else if (!strcmp(keyword, "Emulators") && string && ppd->num_emulations == 0)
1186 {
1187 /*
1188 * Issue #5562: Samsung printer drivers incorrectly use Emulators keyword
1189 * to configure themselves
1190 *
1191 * The Emulators keyword was loaded but never used by anything in CUPS,
1192 * and has no valid purpose in CUPS. The old code was removed due to a
1193 * memory leak (Issue #5475), so the following (new) code supports a single
1194 * name for the Emulators keyword, allowing these drivers to work until we
1195 * remove PPD and driver support entirely in a future version of CUPS.
1196 */
1197
1198 ppd->num_emulations = 1;
1199 ppd->emulations = calloc(1, sizeof(ppd_emul_t));
1200
1201 strlcpy(ppd->emulations[0].name, string, sizeof(ppd->emulations[0].name));
1202 }
ef416fc2 1203 else if (!strcmp(keyword, "JobPatchFile"))
1204 {
ef55b745
MS
1205 /*
1206 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1207 */
1208
1209 if (isdigit(*string & 255))
1210 {
1211 for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
1212
1213 if (*sptr == ':')
1214 {
1215 /*
1216 * Found "*JobPatchFile: int: string"...
1217 */
1218
f787e1e3 1219 pg->ppd_status = PPD_BAD_VALUE;
ef55b745
MS
1220
1221 goto error;
1222 }
1223 }
1224
f787e1e3 1225 if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
ef55b745
MS
1226 {
1227 /*
1228 * Found "*JobPatchFile: string"...
1229 */
1230
f787e1e3 1231 pg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
ef55b745
MS
1232
1233 goto error;
1234 }
1235
ef416fc2 1236 if (ppd->patches == NULL)
745129be 1237 ppd->patches = strdup(string);
ef416fc2 1238 else
1239 {
1240 temp = realloc(ppd->patches, strlen(ppd->patches) +
1241 strlen(string) + 1);
1242 if (temp == NULL)
1243 {
f787e1e3 1244 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1245
1246 goto error;
1247 }
1248
1249 ppd->patches = temp;
1250
5a9febac 1251 memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
ef416fc2 1252 }
1253 }
1254 else if (!strcmp(keyword, "OpenUI"))
1255 {
1256 /*
1257 * Don't allow nesting of options...
1258 */
1259
f787e1e3 1260 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 1261 {
f787e1e3 1262 pg->ppd_status = PPD_NESTED_OPEN_UI;
ef416fc2 1263
1264 goto error;
1265 }
1266
1267 /*
1268 * Add an option record to the current sub-group, group, or file...
1269 */
1270
9c80ffa2 1271 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
bc44d920 1272
ef416fc2 1273 if (name[0] == '*')
1274 _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
1275
7cf5915e 1276 for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
ef416fc2 1277 name[i] = '\0'; /* Eliminate trailing spaces */
1278
9c80ffa2 1279 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
ef416fc2 1280 group ? group->text : "(null)"));
1281
1282 if (subgroup != NULL)
1283 option = ppd_get_option(subgroup, name);
1284 else if (group == NULL)
1285 {
f787e1e3 1286 if ((group = ppd_get_group(ppd, "General", _("General"), pg,
e1d6a774 1287 encoding)) == NULL)
ef416fc2 1288 goto error;
1289
9c80ffa2 1290 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
ef416fc2 1291 option = ppd_get_option(group, name);
1292 group = NULL;
1293 }
1294 else
1295 option = ppd_get_option(group, name);
1296
1297 if (option == NULL)
1298 {
f787e1e3 1299 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1300
1301 goto error;
1302 }
1303
1304 /*
1305 * Now fill in the initial information for the option...
1306 */
1307
1308 if (string && !strcmp(string, "PickMany"))
1309 option->ui = PPD_UI_PICKMANY;
1310 else if (string && !strcmp(string, "Boolean"))
1311 option->ui = PPD_UI_BOOLEAN;
1312 else if (string && !strcmp(string, "PickOne"))
1313 option->ui = PPD_UI_PICKONE;
f787e1e3 1314 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 1315 {
f787e1e3 1316 pg->ppd_status = PPD_BAD_OPEN_UI;
ef416fc2 1317
1318 goto error;
1319 }
1320 else
1321 option->ui = PPD_UI_PICKONE;
1322
1323 for (j = 0; j < ppd->num_attrs; j ++)
1324 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1325 !strcmp(ppd->attrs[j]->name + 7, name) &&
1326 ppd->attrs[j]->value)
1327 {
9c80ffa2 1328 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
ef416fc2 1329 option->keyword, ppd->attrs[j]->value));
1330 strlcpy(option->defchoice, ppd->attrs[j]->value,
1331 sizeof(option->defchoice));
1332 break;
1333 }
1334
1335 if (text[0])
e1d6a774 1336 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1337 sizeof(option->text), encoding);
ef416fc2 1338 else
1339 {
1340 if (!strcmp(name, "PageSize"))
1341 strlcpy(option->text, _("Media Size"), sizeof(option->text));
1342 else if (!strcmp(name, "MediaType"))
1343 strlcpy(option->text, _("Media Type"), sizeof(option->text));
1344 else if (!strcmp(name, "InputSlot"))
1345 strlcpy(option->text, _("Media Source"), sizeof(option->text));
1346 else if (!strcmp(name, "ColorModel"))
1347 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
1348 else if (!strcmp(name, "Resolution"))
1349 strlcpy(option->text, _("Resolution"), sizeof(option->text));
1350 else
1351 strlcpy(option->text, name, sizeof(option->text));
1352 }
1353
1354 option->section = PPD_ORDER_ANY;
1355
5a00cf37 1356 free(string);
ef416fc2 1357 string = NULL;
0a682745
MS
1358
1359 /*
1360 * Add a custom option choice if we have already seen a CustomFoo
1361 * attribute...
1362 */
1363
88f9aafc 1364 if (!_cups_strcasecmp(name, "PageRegion"))
5a9febac 1365 strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
0a682745
MS
1366 else
1367 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1368
1369 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1370 {
749b1e90
MS
1371 if ((choice = ppdFindChoice(option, "Custom")) == NULL)
1372 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1373 {
9c80ffa2 1374 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
0a682745 1375
f787e1e3 1376 pg->ppd_status = PPD_ALLOC_ERROR;
0a682745 1377
749b1e90
MS
1378 goto error;
1379 }
0a682745
MS
1380
1381 strlcpy(choice->text,
1382 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1383 sizeof(choice->text));
5a00cf37 1384 choice->code = strdup(custom_attr->value);
0a682745 1385 }
ef416fc2 1386 }
1387 else if (!strcmp(keyword, "JCLOpenUI"))
1388 {
1389 /*
1390 * Don't allow nesting of options...
1391 */
1392
f787e1e3 1393 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 1394 {
f787e1e3 1395 pg->ppd_status = PPD_NESTED_OPEN_UI;
ef416fc2 1396
1397 goto error;
1398 }
1399
1400 /*
1401 * Find the JCL group, and add if needed...
1402 */
1403
f787e1e3 1404 group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
ef416fc2 1405
1406 if (group == NULL)
1407 goto error;
1408
1409 /*
1410 * Add an option record to the current JCLs...
1411 */
1412
1413 if (name[0] == '*')
1414 _cups_strcpy(name, name + 1);
1415
1416 option = ppd_get_option(group, name);
1417
1418 if (option == NULL)
1419 {
f787e1e3 1420 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1421
1422 goto error;
1423 }
1424
1425 /*
1426 * Now fill in the initial information for the option...
1427 */
1428
1429 if (string && !strcmp(string, "PickMany"))
1430 option->ui = PPD_UI_PICKMANY;
1431 else if (string && !strcmp(string, "Boolean"))
1432 option->ui = PPD_UI_BOOLEAN;
1433 else if (string && !strcmp(string, "PickOne"))
1434 option->ui = PPD_UI_PICKONE;
1435 else
1436 {
f787e1e3 1437 pg->ppd_status = PPD_BAD_OPEN_UI;
ef416fc2 1438
1439 goto error;
1440 }
1441
1442 for (j = 0; j < ppd->num_attrs; j ++)
1443 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1444 !strcmp(ppd->attrs[j]->name + 7, name) &&
1445 ppd->attrs[j]->value)
1446 {
9c80ffa2 1447 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
ef416fc2 1448 option->keyword, ppd->attrs[j]->value));
1449 strlcpy(option->defchoice, ppd->attrs[j]->value,
1450 sizeof(option->defchoice));
1451 break;
1452 }
1453
e1d6a774 1454 if (text[0])
1455 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1456 sizeof(option->text), encoding);
1457 else
1458 strlcpy(option->text, name, sizeof(option->text));
ef416fc2 1459
1460 option->section = PPD_ORDER_JCL;
1461 group = NULL;
1462
5a00cf37 1463 free(string);
ef416fc2 1464 string = NULL;
0a682745
MS
1465
1466 /*
1467 * Add a custom option choice if we have already seen a CustomFoo
1468 * attribute...
1469 */
1470
1471 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1472
1473 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1474 {
1475 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1476 {
9c80ffa2 1477 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
0a682745 1478
f787e1e3 1479 pg->ppd_status = PPD_ALLOC_ERROR;
0a682745
MS
1480
1481 goto error;
1482 }
1483
1484 strlcpy(choice->text,
1485 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1486 sizeof(choice->text));
5a00cf37 1487 choice->code = strdup(custom_attr->value);
0a682745 1488 }
ef416fc2 1489 }
bac967ae 1490 else if (!strcmp(keyword, "CloseUI"))
ef416fc2 1491 {
bac967ae
MS
1492 if ((!option || option->section == PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
1493 {
1494 pg->ppd_status = PPD_BAD_CLOSE_UI;
1495
1496 goto error;
1497 }
1498
1499 option = NULL;
1500
5a00cf37 1501 free(string);
bac967ae
MS
1502 string = NULL;
1503 }
1504 else if (!strcmp(keyword, "JCLCloseUI"))
1505 {
1506 if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
1507 {
1508 pg->ppd_status = PPD_BAD_CLOSE_UI;
1509
1510 goto error;
1511 }
1512
ef416fc2 1513 option = NULL;
1514
5a00cf37 1515 free(string);
ef416fc2 1516 string = NULL;
1517 }
1518 else if (!strcmp(keyword, "OpenGroup"))
1519 {
1520 /*
1521 * Open a new group...
1522 */
1523
1524 if (group != NULL)
1525 {
f787e1e3 1526 pg->ppd_status = PPD_NESTED_OPEN_GROUP;
ef416fc2 1527
1528 goto error;
1529 }
1530
1531 if (!string)
1532 {
f787e1e3 1533 pg->ppd_status = PPD_BAD_OPEN_GROUP;
ef416fc2 1534
1535 goto error;
1536 }
1537
1538 /*
1539 * Separate the group name from the text (name/text)...
1540 */
1541
1542 if ((sptr = strchr(string, '/')) != NULL)
1543 *sptr++ = '\0';
1544 else
1545 sptr = string;
1546
1547 /*
1548 * Fix up the text...
1549 */
1550
1551 ppd_decode(sptr);
1552
1553 /*
1554 * Find/add the group...
1555 */
1556
f787e1e3 1557 group = ppd_get_group(ppd, string, sptr, pg, encoding);
ef416fc2 1558
1559 if (group == NULL)
1560 goto error;
1561
5a00cf37 1562 free(string);
ef416fc2 1563 string = NULL;
1564 }
1565 else if (!strcmp(keyword, "CloseGroup"))
1566 {
1567 group = NULL;
1568
5a00cf37 1569 free(string);
ef416fc2 1570 string = NULL;
1571 }
0a682745 1572 else if (!strcmp(keyword, "OrderDependency"))
ef416fc2 1573 {
b86bc4cf 1574 order = (float)_cupsStrScand(string, &sptr, loc);
757d2cad 1575
1576 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
ef416fc2 1577 {
f787e1e3 1578 pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
ef416fc2 1579
1580 goto error;
1581 }
1582
1583 if (keyword[0] == '*')
1584 _cups_strcpy(keyword, keyword + 1);
1585
1586 if (!strcmp(name, "ExitServer"))
1587 section = PPD_ORDER_EXIT;
1588 else if (!strcmp(name, "Prolog"))
1589 section = PPD_ORDER_PROLOG;
1590 else if (!strcmp(name, "DocumentSetup"))
1591 section = PPD_ORDER_DOCUMENT;
1592 else if (!strcmp(name, "PageSetup"))
1593 section = PPD_ORDER_PAGE;
1594 else if (!strcmp(name, "JCLSetup"))
1595 section = PPD_ORDER_JCL;
1596 else
1597 section = PPD_ORDER_ANY;
1598
1599 if (option == NULL)
1600 {
1601 ppd_group_t *gtemp;
1602
1603
1604 /*
1605 * Only valid for Non-UI options...
1606 */
1607
1608 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
1609 if (gtemp->text[0] == '\0')
1610 break;
1611
1612 if (i > 0)
1613 for (i = 0; i < gtemp->num_options; i ++)
1614 if (!strcmp(keyword, gtemp->options[i].keyword))
1615 {
1616 gtemp->options[i].section = section;
1617 gtemp->options[i].order = order;
1618 break;
1619 }
1620 }
1621 else
1622 {
1623 option->section = section;
1624 option->order = order;
1625 }
1626
5a00cf37 1627 free(string);
ef416fc2 1628 string = NULL;
1629 }
1630 else if (!strncmp(keyword, "Default", 7))
1631 {
1632 if (string == NULL)
1633 continue;
1634
1635 /*
1636 * Drop UI text, if any, from value...
1637 */
1638
1639 if (strchr(string, '/') != NULL)
1640 *strchr(string, '/') = '\0';
1641
1642 /*
1643 * Assign the default value as appropriate...
1644 */
1645
1646 if (!strcmp(keyword, "DefaultColorSpace"))
1647 {
1648 /*
1649 * Set default colorspace...
1650 */
1651
1652 if (!strcmp(string, "CMY"))
1653 ppd->colorspace = PPD_CS_CMY;
1654 else if (!strcmp(string, "CMYK"))
1655 ppd->colorspace = PPD_CS_CMYK;
1656 else if (!strcmp(string, "RGB"))
1657 ppd->colorspace = PPD_CS_RGB;
1658 else if (!strcmp(string, "RGBK"))
1659 ppd->colorspace = PPD_CS_RGBK;
1660 else if (!strcmp(string, "N"))
1661 ppd->colorspace = PPD_CS_N;
1662 else
1663 ppd->colorspace = PPD_CS_GRAY;
1664 }
1665 else if (option && !strcmp(keyword + 7, option->keyword))
1666 {
1667 /*
1668 * Set the default as part of the current option...
1669 */
1670
9c80ffa2 1671 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
ef416fc2 1672
1673 strlcpy(option->defchoice, string, sizeof(option->defchoice));
1674
9c80ffa2 1675 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
ef416fc2 1676 }
1677 else
1678 {
1679 /*
1680 * Lookup option and set if it has been defined...
1681 */
1682
1683 ppd_option_t *toption; /* Temporary option */
1684
1685
1686 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
1687 {
9c80ffa2 1688 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
ef416fc2 1689 strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
1690 }
1691 }
1692 }
1693 else if (!strcmp(keyword, "UIConstraints") ||
1694 !strcmp(keyword, "NonUIConstraints"))
1695 {
b0f6947b
MS
1696 if (!string)
1697 {
f787e1e3 1698 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
b0f6947b
MS
1699 goto error;
1700 }
1701
ef416fc2 1702 if (ppd->num_consts == 0)
323c5de1 1703 constraint = calloc(2, sizeof(ppd_const_t));
ef416fc2 1704 else
7e86f2f6 1705 constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
ef416fc2 1706
1707 if (constraint == NULL)
1708 {
f787e1e3 1709 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1710
1711 goto error;
1712 }
1713
1714 ppd->consts = constraint;
1715 constraint += ppd->num_consts;
1716 ppd->num_consts ++;
1717
1718 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
1719 constraint->choice1, constraint->option2,
1720 constraint->choice2))
1721 {
1722 case 0 : /* Error */
1723 case 1 : /* Error */
f787e1e3 1724 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
ef416fc2 1725 goto error;
1726
1727 case 2 : /* Two options... */
2abf387c 1728 /*
1729 * Check for broken constraints like "* Option"...
1730 */
1731
f787e1e3 1732 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1733 (!strcmp(constraint->option1, "*") ||
1734 !strcmp(constraint->choice1, "*")))
1735 {
f787e1e3 1736 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1737 goto error;
1738 }
1739
ef416fc2 1740 /*
1741 * The following strcpy's are safe, as optionN and
1742 * choiceN are all the same size (size defined by PPD spec...)
1743 */
1744
1745 if (constraint->option1[0] == '*')
1746 _cups_strcpy(constraint->option1, constraint->option1 + 1);
f787e1e3 1747 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1748 {
f787e1e3 1749 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1750 goto error;
1751 }
ef416fc2 1752
1753 if (constraint->choice1[0] == '*')
1754 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
f787e1e3 1755 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1756 {
f787e1e3 1757 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1758 goto error;
1759 }
ef416fc2 1760
1761 constraint->choice1[0] = '\0';
1762 constraint->choice2[0] = '\0';
1763 break;
88f9aafc 1764
ef416fc2 1765 case 3 : /* Two options, one choice... */
2abf387c 1766 /*
1767 * Check for broken constraints like "* Option"...
1768 */
1769
f787e1e3 1770 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1771 (!strcmp(constraint->option1, "*") ||
1772 !strcmp(constraint->choice1, "*") ||
1773 !strcmp(constraint->option2, "*")))
1774 {
f787e1e3 1775 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1776 goto error;
1777 }
1778
ef416fc2 1779 /*
1780 * The following _cups_strcpy's are safe, as optionN and
1781 * choiceN are all the same size (size defined by PPD spec...)
1782 */
1783
1784 if (constraint->option1[0] == '*')
1785 _cups_strcpy(constraint->option1, constraint->option1 + 1);
f787e1e3 1786 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1787 {
f787e1e3 1788 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1789 goto error;
1790 }
ef416fc2 1791
1792 if (constraint->choice1[0] == '*')
1793 {
f787e1e3 1794 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1795 constraint->option2[0] == '*')
1796 {
f787e1e3 1797 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1798 goto error;
1799 }
1800
ef416fc2 1801 _cups_strcpy(constraint->choice2, constraint->option2);
1802 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1803 constraint->choice1[0] = '\0';
1804 }
1805 else
1806 {
1807 if (constraint->option2[0] == '*')
1808 _cups_strcpy(constraint->option2, constraint->option2 + 1);
f787e1e3 1809 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1810 {
f787e1e3 1811 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1812 goto error;
1813 }
ef416fc2 1814
1815 constraint->choice2[0] = '\0';
1816 }
1817 break;
88f9aafc 1818
ef416fc2 1819 case 4 : /* Two options, two choices... */
2abf387c 1820 /*
1821 * Check for broken constraints like "* Option"...
1822 */
1823
f787e1e3 1824 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1825 (!strcmp(constraint->option1, "*") ||
1826 !strcmp(constraint->choice1, "*") ||
1827 !strcmp(constraint->option2, "*") ||
1828 !strcmp(constraint->choice2, "*")))
1829 {
f787e1e3 1830 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1831 goto error;
1832 }
1833
ef416fc2 1834 if (constraint->option1[0] == '*')
1835 _cups_strcpy(constraint->option1, constraint->option1 + 1);
f787e1e3 1836 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1837 {
f787e1e3 1838 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1839 goto error;
1840 }
1841
f787e1e3 1842 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1843 constraint->choice1[0] == '*')
1844 {
f787e1e3 1845 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1846 goto error;
1847 }
ef416fc2 1848
1849 if (constraint->option2[0] == '*')
1850 _cups_strcpy(constraint->option2, constraint->option2 + 1);
f787e1e3 1851 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
2abf387c 1852 {
f787e1e3 1853 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1854 goto error;
1855 }
1856
f787e1e3 1857 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
2abf387c 1858 constraint->choice2[0] == '*')
1859 {
f787e1e3 1860 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
2abf387c 1861 goto error;
1862 }
ef416fc2 1863 break;
1864 }
1865
2abf387c 1866 /*
1867 * Don't add this one as an attribute...
1868 */
1869
5a00cf37 1870 free(string);
ef416fc2 1871 string = NULL;
1872 }
1873 else if (!strcmp(keyword, "PaperDimension"))
1874 {
dc00a7c3
MS
1875 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1876 {
2a06655e
MS
1877 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1878 snprintf(cname, sizeof(cname), "_%s", name);
1879 strlcpy(name, cname, sizeof(name));
dc00a7c3
MS
1880 }
1881
ef416fc2 1882 if ((size = ppdPageSize(ppd, name)) == NULL)
1883 size = ppd_add_size(ppd, name);
1884
1885 if (size == NULL)
1886 {
1887 /*
1888 * Unable to add or find size!
1889 */
1890
f787e1e3 1891 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1892
1893 goto error;
1894 }
1895
b86bc4cf 1896 size->width = (float)_cupsStrScand(string, &sptr, loc);
1897 size->length = (float)_cupsStrScand(sptr, NULL, loc);
ef416fc2 1898
5a00cf37 1899 free(string);
ef416fc2 1900 string = NULL;
1901 }
1902 else if (!strcmp(keyword, "ImageableArea"))
1903 {
dc00a7c3
MS
1904 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1905 {
2a06655e
MS
1906 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1907 snprintf(cname, sizeof(cname), "_%s", name);
1908 strlcpy(name, cname, sizeof(name));
dc00a7c3
MS
1909 }
1910
ef416fc2 1911 if ((size = ppdPageSize(ppd, name)) == NULL)
1912 size = ppd_add_size(ppd, name);
1913
1914 if (size == NULL)
1915 {
1916 /*
1917 * Unable to add or find size!
1918 */
1919
f787e1e3 1920 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 1921
1922 goto error;
1923 }
1924
b86bc4cf 1925 size->left = (float)_cupsStrScand(string, &sptr, loc);
1926 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
1927 size->right = (float)_cupsStrScand(sptr, &sptr, loc);
1928 size->top = (float)_cupsStrScand(sptr, NULL, loc);
ef416fc2 1929
5a00cf37 1930 free(string);
ef416fc2 1931 string = NULL;
1932 }
1933 else if (option != NULL &&
1934 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
1935 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
1936 !strcmp(keyword, option->keyword))
1937 {
9c80ffa2 1938 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
ef416fc2 1939
dc00a7c3
MS
1940 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1941 {
2a06655e
MS
1942 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1943 snprintf(cname, sizeof(cname), "_%s", name);
1944 strlcpy(name, cname, sizeof(name));
dc00a7c3
MS
1945 }
1946
ef416fc2 1947 if (!strcmp(keyword, "PageSize"))
1948 {
1949 /*
1950 * Add a page size...
1951 */
1952
1953 if (ppdPageSize(ppd, name) == NULL)
1954 ppd_add_size(ppd, name);
1955 }
1956
1957 /*
1958 * Add the option choice...
1959 */
1960
91c84a35
MS
1961 if ((choice = ppd_add_choice(option, name)) == NULL)
1962 {
f787e1e3 1963 pg->ppd_status = PPD_ALLOC_ERROR;
91c84a35
MS
1964
1965 goto error;
1966 }
ef416fc2 1967
e1d6a774 1968 if (text[0])
1969 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
1970 sizeof(choice->text), encoding);
ef416fc2 1971 else if (!strcmp(name, "True"))
5a9febac 1972 strlcpy(choice->text, _("Yes"), sizeof(choice->text));
ef416fc2 1973 else if (!strcmp(name, "False"))
5a9febac 1974 strlcpy(choice->text, _("No"), sizeof(choice->text));
ef416fc2 1975 else
1976 strlcpy(choice->text, name, sizeof(choice->text));
1977
1978 if (option->section == PPD_ORDER_JCL)
1979 ppd_decode(string); /* Decode quoted string */
1980
1981 choice->code = string;
1982 string = NULL; /* Don't add as an attribute below */
1983 }
ef416fc2 1984
1985 /*
1986 * Add remaining lines with keywords and string values as attributes...
1987 */
1988
1989 if (string &&
1990 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
1991 ppd_add_attr(ppd, keyword, name, text, string);
1992 else
5a00cf37 1993 free(string);
ef416fc2 1994 }
1995
bac967ae
MS
1996 /*
1997 * Check for a missing CloseUI/JCLCloseUI...
1998 */
1999
2000 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
2001 {
2002 pg->ppd_status = PPD_MISSING_CLOSE_UI;
2003 goto error;
2004 }
2005
0268488e
MS
2006 /*
2007 * Check for a missing CloseGroup...
2008 */
2009
f787e1e3 2010 if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
0268488e 2011 {
f787e1e3 2012 pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
0268488e
MS
2013 goto error;
2014 }
2015
5a00cf37 2016 free(line.buffer);
2e4ff8af 2017
ef416fc2 2018 /*
2019 * Reset language preferences...
2020 */
2021
ef416fc2 2022#ifdef DEBUG
bc44d920 2023 if (!cupsFileEOF(fp))
9c80ffa2 2024 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
ae71f5de 2025 (unsigned long)cupsFileTell(fp)));
ef416fc2 2026#endif /* DEBUG */
2027
f787e1e3 2028 if (pg->ppd_status != PPD_OK)
ef416fc2 2029 {
2030 /*
2031 * Had an error reading the PPD file, cannot continue!
2032 */
2033
2034 ppdClose(ppd);
2035
2036 return (NULL);
2037 }
2038
a4845881
MS
2039 /*
2040 * Update the filters array as needed...
2041 */
2042
f787e1e3 2043 if (!ppd_update_filters(ppd, pg))
a4845881
MS
2044 {
2045 ppdClose(ppd);
2046
2047 return (NULL);
2048 }
2049
ef416fc2 2050 /*
fa73b229 2051 * Create the sorted options array and set the option back-pointer for
2052 * each choice and custom option...
ef416fc2 2053 */
2054
b94498cf 2055 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
2056 (cups_ahash_func_t)ppd_hash_option,
2057 PPD_HASHSIZE);
ef416fc2 2058
2059 for (i = ppd->num_groups, group = ppd->groups;
2060 i > 0;
2061 i --, group ++)
2062 {
ef416fc2 2063 for (j = group->num_options, option = group->options;
2064 j > 0;
2065 j --, option ++)
2066 {
fa73b229 2067 ppd_coption_t *coption; /* Custom option */
ef416fc2 2068
ef416fc2 2069
fa73b229 2070 cupsArrayAdd(ppd->options, option);
ef416fc2 2071
fa73b229 2072 for (k = 0; k < option->num_choices; k ++)
2073 option->choices[k].option = option;
ef416fc2 2074
fa73b229 2075 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
2076 coption->option = option;
2077 }
2078 }
ef416fc2 2079
b94498cf 2080 /*
2081 * Create an array to track the marked choices...
2082 */
2083
2084 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
2085
ef416fc2 2086 /*
2087 * Return the PPD file structure...
2088 */
2089
2090 return (ppd);
2091
2092 /*
2093 * Common exit point for errors to save code size...
2094 */
2095
2096 error:
2097
5a00cf37
MS
2098 free(string);
2099 free(line.buffer);
ef416fc2 2100
2101 ppdClose(ppd);
2102
ef416fc2 2103 return (NULL);
2104}
2105
2106
9c80ffa2
MS
2107/*
2108 * 'ppdOpen()' - Read a PPD file into memory.
2109 */
2110
2111ppd_file_t * /* O - PPD file record */
2112ppdOpen(FILE *fp) /* I - File to read from */
2113{
2114 ppd_file_t *ppd; /* PPD file record */
2115 cups_file_t *cf; /* CUPS file */
2116
2117
2118 /*
2119 * Reopen the stdio file as a CUPS file...
2120 */
2121
2122 if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
2123 return (NULL);
2124
2125 /*
2126 * Load the PPD file using the newer API...
2127 */
2128
2129 ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
2130
2131 /*
2132 * Close the CUPS file and return the PPD...
2133 */
2134
2135 cupsFileClose(cf);
2136
2137 return (ppd);
2138}
2139
2140
2141/*
2142 * 'ppdOpen2()' - Read a PPD file into memory.
2143 *
8072030b 2144 * @since CUPS 1.2/macOS 10.5@
9c80ffa2
MS
2145 */
2146
2147ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2148ppdOpen2(cups_file_t *fp) /* I - File to read from */
2149{
2150 return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
2151}
2152
2153
ef416fc2 2154/*
2155 * 'ppdOpenFd()' - Read a PPD file into memory.
2156 */
2157
5a738aea 2158ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
ef416fc2 2159ppdOpenFd(int fd) /* I - File to read from */
2160{
fa73b229 2161 cups_file_t *fp; /* CUPS file pointer */
ef416fc2 2162 ppd_file_t *ppd; /* PPD file record */
f787e1e3 2163 _ppd_globals_t *pg = _ppdGlobals();
ef416fc2 2164 /* Global data */
2165
2166
2167 /*
2168 * Set the line number to 0...
2169 */
2170
f787e1e3 2171 pg->ppd_line = 0;
ef416fc2 2172
2173 /*
2174 * Range check input...
2175 */
2176
2177 if (fd < 0)
2178 {
f787e1e3 2179 pg->ppd_status = PPD_NULL_FILE;
ef416fc2 2180
2181 return (NULL);
2182 }
2183
2184 /*
2185 * Try to open the file and parse it...
2186 */
2187
fa73b229 2188 if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
ef416fc2 2189 {
fa73b229 2190 ppd = ppdOpen2(fp);
ef416fc2 2191
fa73b229 2192 cupsFileClose(fp);
ef416fc2 2193 }
2194 else
2195 {
f787e1e3 2196 pg->ppd_status = PPD_FILE_OPEN_ERROR;
fa73b229 2197 ppd = NULL;
ef416fc2 2198 }
2199
2200 return (ppd);
2201}
2202
2203
2204/*
9c80ffa2 2205 * '_ppdOpenFile()' - Read a PPD file into memory.
ef416fc2 2206 */
2207
5a738aea 2208ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
9c80ffa2
MS
2209_ppdOpenFile(const char *filename, /* I - File to read from */
2210 _ppd_localization_t localization) /* I - Localization to load */
ef416fc2 2211{
2212 cups_file_t *fp; /* File pointer */
2213 ppd_file_t *ppd; /* PPD file record */
f787e1e3 2214 _ppd_globals_t *pg = _ppdGlobals();
ef416fc2 2215 /* Global data */
2216
2217
2218 /*
2219 * Set the line number to 0...
2220 */
2221
f787e1e3 2222 pg->ppd_line = 0;
ef416fc2 2223
2224 /*
2225 * Range check input...
2226 */
2227
2228 if (filename == NULL)
2229 {
f787e1e3 2230 pg->ppd_status = PPD_NULL_FILE;
ef416fc2 2231
2232 return (NULL);
2233 }
2234
2235 /*
2236 * Try to open the file and parse it...
2237 */
2238
2239 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2240 {
9c80ffa2 2241 ppd = _ppdOpen(fp, localization);
ef416fc2 2242
2243 cupsFileClose(fp);
2244 }
2245 else
2246 {
f787e1e3 2247 pg->ppd_status = PPD_FILE_OPEN_ERROR;
ef416fc2 2248 ppd = NULL;
2249 }
2250
2251 return (ppd);
2252}
2253
2254
9c80ffa2
MS
2255/*
2256 * 'ppdOpenFile()' - Read a PPD file into memory.
2257 */
2258
2259ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2260ppdOpenFile(const char *filename) /* I - File to read from */
2261{
2262 return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
2263}
2264
2265
ef416fc2 2266/*
2267 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2268 *
8072030b 2269 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 2270 */
2271
2272void
2273ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
2274{
f787e1e3 2275 _ppd_globals_t *pg = _ppdGlobals();
ef416fc2 2276 /* Global data */
2277
2278
f787e1e3 2279 pg->ppd_conform = c;
ef416fc2 2280}
2281
2282
2283/*
2284 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2285 */
2286
2287static ppd_attr_t * /* O - New attribute */
2288ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
2289 const char *name, /* I - Attribute name */
2290 const char *spec, /* I - Specifier string, if any */
2291 const char *text, /* I - Text string, if any */
2292 const char *value) /* I - Value of attribute */
2293{
2294 ppd_attr_t **ptr, /* New array */
2295 *temp; /* New attribute */
2296
2297
2298 /*
2299 * Range check input...
2300 */
2301
2302 if (ppd == NULL || name == NULL || spec == NULL)
2303 return (NULL);
2304
bd7854cb 2305 /*
2306 * Create the array as needed...
2307 */
2308
2309 if (!ppd->sorted_attrs)
2310 ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
2311 NULL);
2312
ef416fc2 2313 /*
2314 * Allocate memory for the new attribute...
2315 */
2316
2317 if (ppd->num_attrs == 0)
2318 ptr = malloc(sizeof(ppd_attr_t *));
2319 else
7e86f2f6 2320 ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
ef416fc2 2321
2322 if (ptr == NULL)
2323 return (NULL);
2324
2325 ppd->attrs = ptr;
2326 ptr += ppd->num_attrs;
2327
2328 if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
2329 return (NULL);
2330
2331 *ptr = temp;
2332
2333 ppd->num_attrs ++;
2334
2335 /*
2336 * Copy data over...
2337 */
2338
2339 strlcpy(temp->name, name, sizeof(temp->name));
2340 strlcpy(temp->spec, spec, sizeof(temp->spec));
2341 strlcpy(temp->text, text, sizeof(temp->text));
2342 temp->value = (char *)value;
2343
bd7854cb 2344 /*
2345 * Add the attribute to the sorted array...
2346 */
2347
2348 cupsArrayAdd(ppd->sorted_attrs, temp);
2349
ef416fc2 2350 /*
2351 * Return the attribute...
2352 */
2353
2354 return (temp);
2355}
2356
2357
2358/*
2359 * 'ppd_add_choice()' - Add a choice to an option.
2360 */
2361
2362static ppd_choice_t * /* O - Named choice */
2363ppd_add_choice(ppd_option_t *option, /* I - Option */
2364 const char *name) /* I - Name of choice */
2365{
2366 ppd_choice_t *choice; /* Choice */
2367
2368
2369 if (option->num_choices == 0)
2370 choice = malloc(sizeof(ppd_choice_t));
2371 else
7e86f2f6 2372 choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
ef416fc2 2373
2374 if (choice == NULL)
2375 return (NULL);
2376
2377 option->choices = choice;
2378 choice += option->num_choices;
2379 option->num_choices ++;
2380
2381 memset(choice, 0, sizeof(ppd_choice_t));
2382 strlcpy(choice->choice, name, sizeof(choice->choice));
2383
2384 return (choice);
2385}
2386
2387
2388/*
2389 * 'ppd_add_size()' - Add a page size.
2390 */
2391
2392static ppd_size_t * /* O - Named size */
2393ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
2394 const char *name) /* I - Name of size */
2395{
2396 ppd_size_t *size; /* Size */
2397
2398
2399 if (ppd->num_sizes == 0)
2400 size = malloc(sizeof(ppd_size_t));
2401 else
7e86f2f6 2402 size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
ef416fc2 2403
2404 if (size == NULL)
2405 return (NULL);
2406
2407 ppd->sizes = size;
2408 size += ppd->num_sizes;
2409 ppd->num_sizes ++;
2410
2411 memset(size, 0, sizeof(ppd_size_t));
2412 strlcpy(size->name, name, sizeof(size->name));
2413
2414 return (size);
2415}
2416
2417
bd7854cb 2418/*
2419 * 'ppd_compare_attrs()' - Compare two attributes.
2420 */
2421
2422static int /* O - Result of comparison */
2423ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
2424 ppd_attr_t *b) /* I - Second attribute */
2425{
88f9aafc 2426 return (_cups_strcasecmp(a->name, b->name));
bd7854cb 2427}
2428
2429
b94498cf 2430/*
2431 * 'ppd_compare_choices()' - Compare two choices...
2432 */
2433
2434static int /* O - Result of comparison */
2435ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
2436 ppd_choice_t *b) /* I - Second choice */
2437{
355e94dc 2438 return (strcmp(a->option->keyword, b->option->keyword));
b94498cf 2439}
2440
2441
ef416fc2 2442/*
fa73b229 2443 * 'ppd_compare_coptions()' - Compare two custom options.
ef416fc2 2444 */
2445
2446static int /* O - Result of comparison */
fa73b229 2447ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
2448 ppd_coption_t *b) /* I - Second option */
ef416fc2 2449{
88f9aafc 2450 return (_cups_strcasecmp(a->keyword, b->keyword));
fa73b229 2451}
2452
2453
ef416fc2 2454/*
2455 * 'ppd_compare_options()' - Compare two options.
2456 */
2457
2458static int /* O - Result of comparison */
fa73b229 2459ppd_compare_options(ppd_option_t *a, /* I - First option */
2460 ppd_option_t *b) /* I - Second option */
ef416fc2 2461{
88f9aafc 2462 return (_cups_strcasecmp(a->keyword, b->keyword));
ef416fc2 2463}
ef416fc2 2464
2465
2466/*
2467 * 'ppd_decode()' - Decode a string value...
2468 */
2469
2470static int /* O - Length of decoded string */
2471ppd_decode(char *string) /* I - String to decode */
2472{
2473 char *inptr, /* Input pointer */
2474 *outptr; /* Output pointer */
2475
2476
2477 inptr = string;
2478 outptr = string;
2479
2480 while (*inptr != '\0')
2481 if (*inptr == '<' && isxdigit(inptr[1] & 255))
2482 {
2483 /*
2484 * Convert hex to 8-bit values...
2485 */
2486
2487 inptr ++;
2488 while (isxdigit(*inptr & 255))
2489 {
7cf5915e 2490 if (_cups_isalpha(*inptr))
7e86f2f6 2491 *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
ef416fc2 2492 else
7e86f2f6 2493 *outptr = (char)((*inptr - '0') << 4);
ef416fc2 2494
2495 inptr ++;
2496
2497 if (!isxdigit(*inptr & 255))
2498 break;
2499
7cf5915e 2500 if (_cups_isalpha(*inptr))
7e86f2f6 2501 *outptr |= (char)(tolower(*inptr) - 'a' + 10);
ef416fc2 2502 else
7e86f2f6 2503 *outptr |= (char)(*inptr - '0');
ef416fc2 2504
2505 inptr ++;
2506 outptr ++;
2507 }
2508
2509 while (*inptr != '>' && *inptr != '\0')
2510 inptr ++;
2511 while (*inptr == '>')
2512 inptr ++;
2513 }
2514 else
2515 *outptr++ = *inptr++;
2516
2517 *outptr = '\0';
2518
2519 return ((int)(outptr - string));
2520}
2521
2522
a4845881
MS
2523/*
2524 * 'ppd_free_filters()' - Free the filters array.
2525 */
2526
2527static void
2528ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
2529{
2530 int i; /* Looping var */
2531 char **filter; /* Current filter */
2532
2533
2534 if (ppd->num_filters > 0)
2535 {
2536 for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
5a00cf37 2537 free(*filter);
a4845881 2538
5a00cf37 2539 free(ppd->filters);
a4845881
MS
2540
2541 ppd->num_filters = 0;
2542 ppd->filters = NULL;
2543 }
2544}
2545
2546
ef416fc2 2547/*
2548 * 'ppd_free_group()' - Free a single UI group.
2549 */
2550
2551static void
2552ppd_free_group(ppd_group_t *group) /* I - Group to free */
2553{
2554 int i; /* Looping var */
2555 ppd_option_t *option; /* Current option */
2556 ppd_group_t *subgroup; /* Current sub-group */
2557
2558
2559 if (group->num_options > 0)
2560 {
2561 for (i = group->num_options, option = group->options;
2562 i > 0;
2563 i --, option ++)
2564 ppd_free_option(option);
2565
5a00cf37 2566 free(group->options);
ef416fc2 2567 }
2568
2569 if (group->num_subgroups > 0)
2570 {
2571 for (i = group->num_subgroups, subgroup = group->subgroups;
2572 i > 0;
2573 i --, subgroup ++)
2574 ppd_free_group(subgroup);
2575
5a00cf37 2576 free(group->subgroups);
ef416fc2 2577 }
2578}
2579
2580
2581/*
2582 * 'ppd_free_option()' - Free a single option.
2583 */
2584
2585static void
2586ppd_free_option(ppd_option_t *option) /* I - Option to free */
2587{
2588 int i; /* Looping var */
2589 ppd_choice_t *choice; /* Current choice */
2590
2591
2592 if (option->num_choices > 0)
2593 {
2594 for (i = option->num_choices, choice = option->choices;
2595 i > 0;
2596 i --, choice ++)
2597 {
5a00cf37 2598 free(choice->code);
ef416fc2 2599 }
2600
5a00cf37 2601 free(option->choices);
ef416fc2 2602 }
2603}
2604
2605
ef416fc2 2606/*
fa73b229 2607 * 'ppd_get_coption()' - Get a custom option record.
ef416fc2 2608 */
2609
fa73b229 2610static ppd_coption_t * /* O - Custom option... */
2611ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
2612 const char *name) /* I - Name of option */
ef416fc2 2613{
fa73b229 2614 ppd_coption_t *copt; /* New custom option */
ef416fc2 2615
2616
2617 /*
2618 * See if the option already exists...
2619 */
2620
fa73b229 2621 if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
2622 return (copt);
ef416fc2 2623
2624 /*
fa73b229 2625 * Not found, so create the custom option record...
ef416fc2 2626 */
2627
fa73b229 2628 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
ef416fc2 2629 return (NULL);
2630
fa73b229 2631 strlcpy(copt->keyword, name, sizeof(copt->keyword));
ef416fc2 2632
0268488e 2633 copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
ef416fc2 2634
fa73b229 2635 cupsArrayAdd(ppd->coptions, copt);
ef416fc2 2636
2637 /*
2638 * Return the new record...
2639 */
2640
fa73b229 2641 return (copt);
ef416fc2 2642}
2643
2644
2645/*
fa73b229 2646 * 'ppd_get_cparam()' - Get a custom parameter record.
ef416fc2 2647 */
2648
fa73b229 2649static ppd_cparam_t * /* O - Extended option... */
2650ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
2651 const char *param, /* I - Name of parameter */
2652 const char *text) /* I - Human-readable text */
ef416fc2 2653{
fa73b229 2654 ppd_cparam_t *cparam; /* New custom parameter */
ef416fc2 2655
2656
2657 /*
2658 * See if the parameter already exists...
2659 */
2660
fa73b229 2661 if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
2662 return (cparam);
ef416fc2 2663
2664 /*
fa73b229 2665 * Not found, so create the custom parameter record...
ef416fc2 2666 */
2667
fa73b229 2668 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
ef416fc2 2669 return (NULL);
2670
dc00a7c3 2671 cparam->type = PPD_CUSTOM_UNKNOWN;
fa73b229 2672 strlcpy(cparam->name, param, sizeof(cparam->name));
b86bc4cf 2673 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
ef416fc2 2674
2675 /*
fa73b229 2676 * Add this record to the array...
ef416fc2 2677 */
2678
fa73b229 2679 cupsArrayAdd(opt->params, cparam);
ef416fc2 2680
2681 /*
2682 * Return the new record...
2683 */
2684
fa73b229 2685 return (cparam);
ef416fc2 2686}
ef416fc2 2687
2688
2689/*
2690 * 'ppd_get_group()' - Find or create the named group as needed.
2691 */
2692
2693static ppd_group_t * /* O - Named group */
e1d6a774 2694ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
2695 const char *name, /* I - Name of group */
2696 const char *text, /* I - Text for group */
f787e1e3 2697 _ppd_globals_t *pg, /* I - Global data */
e1d6a774 2698 cups_encoding_t encoding) /* I - Encoding of text */
ef416fc2 2699{
2700 int i; /* Looping var */
2701 ppd_group_t *group; /* Group */
2702
2703
e07d4801 2704 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
f787e1e3 2705 ppd, name, text, pg));
ef416fc2 2706
2707 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
2708 if (!strcmp(group->name, name))
2709 break;
2710
2711 if (i == 0)
2712 {
e07d4801 2713 DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
ef416fc2 2714
f787e1e3 2715 if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
ef416fc2 2716 {
f787e1e3 2717 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
ef416fc2 2718
2719 return (NULL);
2720 }
88f9aafc 2721
ef416fc2 2722 if (ppd->num_groups == 0)
2723 group = malloc(sizeof(ppd_group_t));
2724 else
7e86f2f6 2725 group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
ef416fc2 2726
2727 if (group == NULL)
2728 {
f787e1e3 2729 pg->ppd_status = PPD_ALLOC_ERROR;
ef416fc2 2730
2731 return (NULL);
2732 }
2733
2734 ppd->groups = group;
2735 group += ppd->num_groups;
2736 ppd->num_groups ++;
2737
2738 memset(group, 0, sizeof(ppd_group_t));
2739 strlcpy(group->name, name, sizeof(group->name));
e1d6a774 2740
2741 cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
2742 sizeof(group->text), encoding);
ef416fc2 2743 }
2744
2745 return (group);
2746}
2747
2748
2749/*
2750 * 'ppd_get_option()' - Find or create the named option as needed.
2751 */
2752
2753static ppd_option_t * /* O - Named option */
2754ppd_get_option(ppd_group_t *group, /* I - Group */
2755 const char *name) /* I - Name of option */
2756{
2757 int i; /* Looping var */
2758 ppd_option_t *option; /* Option */
2759
2760
e07d4801 2761 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
ef416fc2 2762 group, group->name, name));
2763
2764 for (i = group->num_options, option = group->options; i > 0; i --, option ++)
2765 if (!strcmp(option->keyword, name))
2766 break;
2767
2768 if (i == 0)
2769 {
2770 if (group->num_options == 0)
2771 option = malloc(sizeof(ppd_option_t));
2772 else
7e86f2f6 2773 option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
ef416fc2 2774
2775 if (option == NULL)
2776 return (NULL);
2777
2778 group->options = option;
2779 option += group->num_options;
2780 group->num_options ++;
2781
2782 memset(option, 0, sizeof(ppd_option_t));
2783 strlcpy(option->keyword, name, sizeof(option->keyword));
2784 }
2785
2786 return (option);
2787}
2788
2789
f787e1e3
MS
2790/*
2791 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2792 */
2793
2794static _ppd_globals_t * /* O - Pointer to global data */
2795ppd_globals_alloc(void)
2796{
2797 return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
2798}
2799
2800
2801/*
2802 * 'ppd_globals_free()' - Free global data.
2803 */
2804
19dc16f7 2805#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
f787e1e3
MS
2806static void
2807ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */
2808{
2809 free(pg);
2810}
19dc16f7 2811#endif /* HAVE_PTHREAD_H || _WIN32 */
f787e1e3
MS
2812
2813
2814#ifdef HAVE_PTHREAD_H
2815/*
2816 * 'ppd_globals_init()' - Initialize per-thread globals...
2817 */
2818
2819static void
2820ppd_globals_init(void)
2821{
2822 /*
2823 * Register the global data for this thread...
2824 */
2825
2826 pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
2827}
2828#endif /* HAVE_PTHREAD_H */
2829
2830
b94498cf 2831/*
2832 * 'ppd_hash_option()' - Generate a hash of the option name...
2833 */
2834
2835static int /* O - Hash index */
2836ppd_hash_option(ppd_option_t *option) /* I - Option */
2837{
2838 int hash = 0; /* Hash index */
2839 const char *k; /* Pointer into keyword */
2840
2841
2842 for (hash = option->keyword[0], k = option->keyword + 1; *k;)
2843 hash = 33 * hash + *k++;
2844
2845 return (hash & 511);
2846}
2847
2848
ef416fc2 2849/*
2850 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2851 * necessary.
2852 */
2853
2854static int /* O - Bitmask of fields read */
2855ppd_read(cups_file_t *fp, /* I - File to read from */
2e4ff8af 2856 _ppd_line_t *line, /* I - Line buffer */
ef416fc2 2857 char *keyword, /* O - Keyword from line */
2858 char *option, /* O - Option from line */
2859 char *text, /* O - Human-readable text from line */
2860 char **string, /* O - Code/string data */
2861 int ignoreblank, /* I - Ignore blank lines? */
f787e1e3 2862 _ppd_globals_t *pg) /* I - Global data */
ef416fc2 2863{
2864 int ch, /* Character from file */
2865 col, /* Column in line */
2866 colon, /* Colon seen? */
2867 endquote, /* Waiting for an end quote */
2868 mask, /* Mask to be returned */
2869 startline, /* Start line */
2870 textlen; /* Length of text */
2871 char *keyptr, /* Keyword pointer */
2872 *optptr, /* Option pointer */
2873 *textptr, /* Text pointer */
2874 *strptr, /* Pointer into string */
2e4ff8af 2875 *lineptr; /* Current position in line buffer */
ef416fc2 2876
ef416fc2 2877
2878 /*
2879 * Now loop until we have a valid line...
2880 */
2881
2882 *string = NULL;
2883 col = 0;
f787e1e3 2884 startline = pg->ppd_line + 1;
ef416fc2 2885
2e4ff8af
MS
2886 if (!line->buffer)
2887 {
2888 line->bufsize = 1024;
2889 line->buffer = malloc(1024);
2890
2891 if (!line->buffer)
2892 return (0);
2893 }
ef416fc2 2894
2895 do
2896 {
2897 /*
2898 * Read the line...
2899 */
2900
2e4ff8af 2901 lineptr = line->buffer;
ef416fc2 2902 endquote = 0;
2903 colon = 0;
2904
2905 while ((ch = cupsFileGetChar(fp)) != EOF)
2906 {
2e4ff8af 2907 if (lineptr >= (line->buffer + line->bufsize - 1))
ef416fc2 2908 {
2909 /*
2910 * Expand the line buffer...
2911 */
2912
2913 char *temp; /* Temporary line pointer */
2914
2915
2e4ff8af
MS
2916 line->bufsize += 1024;
2917 if (line->bufsize > 262144)
ef416fc2 2918 {
2919 /*
2920 * Don't allow lines longer than 256k!
2921 */
2922
f787e1e3
MS
2923 pg->ppd_line = startline;
2924 pg->ppd_status = PPD_LINE_TOO_LONG;
ef416fc2 2925
ef416fc2 2926 return (0);
2927 }
2928
2e4ff8af 2929 temp = realloc(line->buffer, line->bufsize);
ef416fc2 2930 if (!temp)
2931 {
f787e1e3
MS
2932 pg->ppd_line = startline;
2933 pg->ppd_status = PPD_LINE_TOO_LONG;
ef416fc2 2934
ef416fc2 2935 return (0);
2936 }
2937
2e4ff8af
MS
2938 lineptr = temp + (lineptr - line->buffer);
2939 line->buffer = temp;
ef416fc2 2940 }
2941
2942 if (ch == '\r' || ch == '\n')
2943 {
2944 /*
2945 * Line feed or carriage return...
2946 */
2947
f787e1e3 2948 pg->ppd_line ++;
ef416fc2 2949 col = 0;
2950
2951 if (ch == '\r')
2952 {
2953 /*
2954 * Check for a trailing line feed...
2955 */
2956
2957 if ((ch = cupsFilePeekChar(fp)) == EOF)
fa73b229 2958 {
2959 ch = '\n';
ef416fc2 2960 break;
fa73b229 2961 }
2962
ef416fc2 2963 if (ch == 0x0a)
2964 cupsFileGetChar(fp);
2965 }
2966
2e4ff8af 2967 if (lineptr == line->buffer && ignoreblank)
ef416fc2 2968 continue; /* Skip blank lines */
2969
2970 ch = '\n';
2971
2972 if (!endquote) /* Continue for multi-line text */
2973 break;
2974
2975 *lineptr++ = '\n';
2976 }
f787e1e3 2977 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 2978 {
2979 /*
2980 * Other control characters...
2981 */
2982
f787e1e3
MS
2983 pg->ppd_line = startline;
2984 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
ef416fc2 2985
ef416fc2 2986 return (0);
2987 }
2988 else if (ch != 0x1a)
2989 {
2990 /*
2991 * Any other character...
2992 */
2993
7e86f2f6 2994 *lineptr++ = (char)ch;
ef416fc2 2995 col ++;
2996
2997 if (col > (PPD_MAX_LINE - 1))
2998 {
2999 /*
3000 * Line is too long...
3001 */
3002
f787e1e3
MS
3003 pg->ppd_line = startline;
3004 pg->ppd_status = PPD_LINE_TOO_LONG;
ef416fc2 3005
ef416fc2 3006 return (0);
3007 }
3008
2e4ff8af 3009 if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
ef416fc2 3010 colon = 1;
3011
3012 if (ch == '\"' && colon)
3013 endquote = !endquote;
3014 }
3015 }
3016
3017 if (endquote)
3018 {
3019 /*
3020 * Didn't finish this quoted string...
3021 */
3022
3023 while ((ch = cupsFileGetChar(fp)) != EOF)
3024 if (ch == '\"')
3025 break;
3026 else if (ch == '\r' || ch == '\n')
3027 {
f787e1e3 3028 pg->ppd_line ++;
ef416fc2 3029 col = 0;
3030
3031 if (ch == '\r')
3032 {
3033 /*
3034 * Check for a trailing line feed...
3035 */
3036
3037 if ((ch = cupsFilePeekChar(fp)) == EOF)
3038 break;
3039 if (ch == 0x0a)
3040 cupsFileGetChar(fp);
3041 }
ef416fc2 3042 }
f787e1e3 3043 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 3044 {
3045 /*
3046 * Other control characters...
3047 */
3048
f787e1e3
MS
3049 pg->ppd_line = startline;
3050 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
ef416fc2 3051
ef416fc2 3052 return (0);
3053 }
3054 else if (ch != 0x1a)
3055 {
3056 col ++;
3057
3058 if (col > (PPD_MAX_LINE - 1))
3059 {
3060 /*
3061 * Line is too long...
3062 */
3063
f787e1e3
MS
3064 pg->ppd_line = startline;
3065 pg->ppd_status = PPD_LINE_TOO_LONG;
ef416fc2 3066
ef416fc2 3067 return (0);
3068 }
3069 }
3070 }
3071
3072 if (ch != '\n')
3073 {
3074 /*
3075 * Didn't finish this line...
3076 */
3077
3078 while ((ch = cupsFileGetChar(fp)) != EOF)
3079 if (ch == '\r' || ch == '\n')
3080 {
3081 /*
3082 * Line feed or carriage return...
3083 */
3084
f787e1e3 3085 pg->ppd_line ++;
ef416fc2 3086 col = 0;
3087
3088 if (ch == '\r')
3089 {
3090 /*
3091 * Check for a trailing line feed...
3092 */
3093
3094 if ((ch = cupsFilePeekChar(fp)) == EOF)
3095 break;
3096 if (ch == 0x0a)
3097 cupsFileGetChar(fp);
3098 }
3099
3100 break;
3101 }
f787e1e3 3102 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 3103 {
3104 /*
3105 * Other control characters...
3106 */
3107
f787e1e3
MS
3108 pg->ppd_line = startline;
3109 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
ef416fc2 3110
ef416fc2 3111 return (0);
3112 }
3113 else if (ch != 0x1a)
3114 {
3115 col ++;
3116
3117 if (col > (PPD_MAX_LINE - 1))
3118 {
3119 /*
3120 * Line is too long...
3121 */
3122
f787e1e3
MS
3123 pg->ppd_line = startline;
3124 pg->ppd_status = PPD_LINE_TOO_LONG;
ef416fc2 3125
ef416fc2 3126 return (0);
3127 }
3128 }
3129 }
3130
2e4ff8af 3131 if (lineptr > line->buffer && lineptr[-1] == '\n')
ef416fc2 3132 lineptr --;
3133
3134 *lineptr = '\0';
3135
e07d4801 3136 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
ef416fc2 3137
bd7854cb 3138 /*
8072030b 3139 * The dynamically created PPDs for older style macOS
bd7854cb 3140 * drivers include a large blob of data inserted as comments
3141 * at the end of the file. As an optimization we can stop
3142 * reading the PPD when we get to the start of this data.
3143 */
3144
2e4ff8af 3145 if (!strcmp(line->buffer, "*%APLWORKSET START"))
bd7854cb 3146 return (0);
bd7854cb 3147
2e4ff8af 3148 if (ch == EOF && lineptr == line->buffer)
ef416fc2 3149 return (0);
ef416fc2 3150
3151 /*
3152 * Now parse it...
3153 */
3154
3155 mask = 0;
2e4ff8af 3156 lineptr = line->buffer + 1;
ef416fc2 3157
3158 keyword[0] = '\0';
3159 option[0] = '\0';
3160 text[0] = '\0';
3161 *string = NULL;
3162
2e4ff8af
MS
3163 if ((!line->buffer[0] || /* Blank line */
3164 !strncmp(line->buffer, "*%", 2) || /* Comment line */
3165 !strcmp(line->buffer, "*End")) && /* End of multi-line string */
ef416fc2 3166 ignoreblank) /* Ignore these? */
3167 {
f787e1e3 3168 startline = pg->ppd_line + 1;
ef416fc2 3169 continue;
3170 }
3171
2e4ff8af 3172 if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
ef416fc2 3173 {
f787e1e3 3174 if (pg->ppd_conform == PPD_CONFORM_RELAXED)
ef416fc2 3175 {
f787e1e3 3176 startline = pg->ppd_line + 1;
ef416fc2 3177 continue;
3178 }
3179 else
3180 {
f787e1e3
MS
3181 pg->ppd_line = startline;
3182 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
ef416fc2 3183
ef416fc2 3184 return (0);
3185 }
3186 }
3187
2e4ff8af 3188 if (line->buffer[0] != '*') /* All lines start with an asterisk */
ef416fc2 3189 {
ef416fc2 3190 /*
3191 * Allow lines consisting of just whitespace...
3192 */
3193
2e4ff8af 3194 for (lineptr = line->buffer; *lineptr; lineptr ++)
7cf5915e 3195 if (*lineptr && !_cups_isspace(*lineptr))
ef416fc2 3196 break;
3197
3198 if (*lineptr)
3199 {
f787e1e3 3200 pg->ppd_status = PPD_MISSING_ASTERISK;
ef416fc2 3201 return (0);
3202 }
3203 else if (ignoreblank)
3204 continue;
3205 else
ef416fc2 3206 return (0);
ef416fc2 3207 }
3208
3209 /*
3210 * Get a keyword...
3211 */
3212
3213 keyptr = keyword;
3214
7cf5915e 3215 while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
ef416fc2 3216 {
3217 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
3218 (keyptr - keyword) >= (PPD_MAX_NAME - 1))
3219 {
f787e1e3 3220 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
ef416fc2 3221 return (0);
3222 }
3223
3224 *keyptr++ = *lineptr++;
3225 }
3226
3227 *keyptr = '\0';
3228
3229 if (!strcmp(keyword, "End"))
3230 continue;
3231
3232 mask |= PPD_KEYWORD;
3233
7cf5915e 3234 if (_cups_isspace(*lineptr))
ef416fc2 3235 {
3236 /*
3237 * Get an option name...
3238 */
3239
7cf5915e 3240 while (_cups_isspace(*lineptr))
ef416fc2 3241 lineptr ++;
3242
3243 optptr = option;
3244
7cf5915e 3245 while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
ef416fc2 3246 *lineptr != '/')
3247 {
3248 if (*lineptr <= ' ' || *lineptr > 126 ||
3249 (optptr - option) >= (PPD_MAX_NAME - 1))
3250 {
f787e1e3 3251 pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
ef416fc2 3252 return (0);
3253 }
3254
3255 *optptr++ = *lineptr++;
3256 }
3257
3258 *optptr = '\0';
3259
f787e1e3 3260 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 3261 {
f787e1e3 3262 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
ef416fc2 3263 return (0);
3264 }
3265
7cf5915e 3266 while (_cups_isspace(*lineptr))
ef416fc2 3267 lineptr ++;
3268
3269 mask |= PPD_OPTION;
3270
ef416fc2 3271 if (*lineptr == '/')
3272 {
3273 /*
3274 * Get human-readable text...
3275 */
3276
3277 lineptr ++;
88f9aafc 3278
ef416fc2 3279 textptr = text;
3280
3281 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
3282 {
3283 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
3284 (textptr - text) >= (PPD_MAX_LINE - 1))
3285 {
f787e1e3 3286 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
ef416fc2 3287 return (0);
3288 }
3289
3290 *textptr++ = *lineptr++;
3291 }
3292
3293 *textptr = '\0';
3294 textlen = ppd_decode(text);
3295
f787e1e3 3296 if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 3297 {
f787e1e3 3298 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
ef416fc2 3299 return (0);
3300 }
88f9aafc 3301
ef416fc2 3302 mask |= PPD_TEXT;
3303 }
ef416fc2 3304 }
3305
f787e1e3 3306 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
ef416fc2 3307 {
f787e1e3 3308 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
ef416fc2 3309 return (0);
3310 }
3311
7cf5915e 3312 while (_cups_isspace(*lineptr))
ef416fc2 3313 lineptr ++;
3314
3315 if (*lineptr == ':')
3316 {
3317 /*
3318 * Get string after triming leading and trailing whitespace...
3319 */
3320
3321 lineptr ++;
7cf5915e 3322 while (_cups_isspace(*lineptr))
ef416fc2 3323 lineptr ++;
3324
3325 strptr = lineptr + strlen(lineptr) - 1;
7cf5915e 3326 while (strptr >= lineptr && _cups_isspace(*strptr))
ef416fc2 3327 *strptr-- = '\0';
3328
3329 if (*strptr == '\"')
3330 {
3331 /*
2e4ff8af 3332 * Quoted string by itself, remove quotes...
ef416fc2 3333 */
3334
2e4ff8af
MS
3335 *strptr = '\0';
3336 lineptr ++;
ef416fc2 3337 }
2e4ff8af 3338
5a00cf37 3339 *string = strdup(lineptr);
ef416fc2 3340
ef416fc2 3341 mask |= PPD_STRING;
3342 }
3343 }
3344 while (mask == 0);
3345
ef416fc2 3346 return (mask);
3347}
3348
3349
3350/*
a4845881
MS
3351 * 'ppd_update_filters()' - Update the filters array as needed.
3352 *
3353 * This function re-populates the filters array with cupsFilter2 entries that
3354 * have been stripped of the destination MIME media types and any maxsize hints.
3355 *
3356 * (All for backwards-compatibility)
3357 */
3358
3359static int /* O - 1 on success, 0 on failure */
f787e1e3
MS
3360ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */
3361 _ppd_globals_t *pg) /* I - Global data */
a4845881
MS
3362{
3363 ppd_attr_t *attr; /* Current cupsFilter2 value */
3364 char srcsuper[16], /* Source MIME media type */
3365 srctype[256],
3366 dstsuper[16], /* Destination MIME media type */
3367 dsttype[256],
3368 program[1024], /* Command to run */
3369 *ptr, /* Pointer into command to run */
3370 buffer[1024], /* Re-written cupsFilter value */
3371 **filter; /* Current filter */
3372 int cost; /* Cost of filter */
3373
3374
f787e1e3 3375 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
a4845881
MS
3376
3377 /*
3378 * See if we have any cupsFilter2 lines...
3379 */
3380
3381 if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
3382 {
3383 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3384 return (1);
3385 }
3386
3387 /*
3388 * Yes, free the cupsFilter-defined filters and re-build...
3389 */
3390
3391 ppd_free_filters(ppd);
3392
3393 do
3394 {
3395 /*
3396 * Parse the cupsFilter2 string:
3397 *
3398 * src/type dst/type cost program
3399 * src/type dst/type cost maxsize(n) program
3400 */
3401
3402 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
3403
3404 if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3405 srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
3406 {
3407 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
f787e1e3 3408 pg->ppd_status = PPD_BAD_VALUE;
a4845881
MS
3409
3410 return (0);
3411 }
3412
3413 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3414 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3415 srcsuper, srctype, dstsuper, dsttype, cost, program));
3416
3417 if (!strncmp(program, "maxsize(", 8) &&
3418 (ptr = strchr(program + 8, ')')) != NULL)
3419 {
3420 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3421
3422 ptr ++;
3423 while (_cups_isspace(*ptr))
3424 ptr ++;
3425
3426 _cups_strcpy(program, ptr);
3427 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
3428 }
3429
3430 /*
3431 * Convert to cupsFilter format:
3432 *
3433 * src/type cost program
3434 */
3435
3436 snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
3437 program);
3438 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
3439
3440 /*
3441 * Add a cupsFilter-compatible string to the filters array.
3442 */
3443
3444 if (ppd->num_filters == 0)
3445 filter = malloc(sizeof(char *));
3446 else
7e86f2f6 3447 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
a4845881
MS
3448
3449 if (filter == NULL)
3450 {
3451 DEBUG_puts("5ppd_update_filters: Out of memory.");
f787e1e3 3452 pg->ppd_status = PPD_ALLOC_ERROR;
a4845881
MS
3453
3454 return (0);
3455 }
3456
3457 ppd->filters = filter;
3458 filter += ppd->num_filters;
3459 ppd->num_filters ++;
3460
5a00cf37 3461 *filter = strdup(buffer);
a4845881
MS
3462 }
3463 while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
3464
3465 DEBUG_puts("5ppd_update_filters: Completed OK.");
3466 return (1);
3467}