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