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