]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ppd-mark.c
Merge pull request #5621 from zdohnal/cgigetarray-sigsegv
[thirdparty/cups.git] / cups / ppd-mark.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Option marking routines for CUPS.
ef416fc2 3 *
5a00cf37
MS
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
4b042bf6
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 *
7e86f2f6 10 * PostScript is a trademark of Adobe Systems, Inc.
ef416fc2 11 */
12
13/*
14 * Include necessary headers...
15 */
16
71e16022 17#include "cups-private.h"
f787e1e3 18#include "ppd-private.h"
fb863569 19#include "debug-internal.h"
ef416fc2 20
21
22/*
23 * Local functions...
24 */
25
5a738aea 26#ifdef DEBUG
ba55dc12 27static void ppd_debug_marked(ppd_file_t *ppd, const char *title);
5a738aea 28#else
ba55dc12 29# define ppd_debug_marked(ppd,title)
5a738aea 30#endif /* DEBUG */
ef416fc2 31static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
66ab9486
MS
32static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
33static void ppd_mark_option(ppd_file_t *ppd, const char *option,
34 const char *choice);
ef416fc2 35
36
37/*
5a738aea
MS
38 * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
39 *
40 * This function maps the IPP "finishings", "media", "mirror",
f14324a7
MS
41 * "multiple-document-handling", "output-bin", "print-color-mode",
42 * "print-quality", "printer-resolution", and "sides" attributes to their
43 * corresponding PPD options and choices.
5a738aea
MS
44 */
45
66ab9486 46int /* O - 1 if conflicts exist, 0 otherwise */
5a738aea
MS
47cupsMarkOptions(
48 ppd_file_t *ppd, /* I - PPD file */
49 int num_options, /* I - Number of options */
50 cups_option_t *options) /* I - Options */
51{
c7017ecc 52 int i, j; /* Looping vars */
d2354e63 53 char *ptr, /* Pointer into string */
5a738aea 54 s[255]; /* Temporary string */
d2354e63
MS
55 const char *val, /* Pointer into value */
56 *media, /* media option */
0268488e 57 *output_bin, /* output-bin option */
54afec33 58 *page_size, /* PageSize option */
0268488e 59 *ppd_keyword, /* PPD keyword */
f14324a7 60 *print_color_mode, /* print-color-mode option */
0268488e
MS
61 *print_quality, /* print-quality option */
62 *sides; /* sides option */
5a738aea 63 cups_option_t *optptr; /* Current option */
5a738aea 64 ppd_attr_t *attr; /* PPD attribute */
f14324a7 65 _ppd_cache_t *cache; /* PPD cache and mapping data */
5a738aea
MS
66
67
68 /*
69 * Check arguments...
70 */
71
72 if (!ppd || num_options <= 0 || !options)
73 return (0);
74
ba55dc12 75 ppd_debug_marked(ppd, "Before...");
5a738aea
MS
76
77 /*
0268488e
MS
78 * Do special handling for finishings, media, output-bin, output-mode,
79 * print-color-mode, print-quality, and PageSize...
5a738aea
MS
80 */
81
0268488e
MS
82 media = cupsGetOption("media", num_options, options);
83 output_bin = cupsGetOption("output-bin", num_options, options);
0268488e
MS
84 page_size = cupsGetOption("PageSize", num_options, options);
85 print_quality = cupsGetOption("print-quality", num_options, options);
86 sides = cupsGetOption("sides", num_options, options);
d2354e63 87
f14324a7
MS
88 if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
89 options)) == NULL)
90 print_color_mode = cupsGetOption("output-mode", num_options, options);
91
92 if ((media || output_bin || print_color_mode || print_quality || sides) &&
93 !ppd->cache)
d2354e63
MS
94 {
95 /*
f14324a7 96 * Load PPD cache and mapping data as needed...
d2354e63
MS
97 */
98
f14324a7 99 ppd->cache = _ppdCacheCreateWithPPD(ppd);
0268488e
MS
100 }
101
f14324a7 102 cache = ppd->cache;
5a738aea 103
0268488e
MS
104 if (media)
105 {
d2354e63 106 /*
0268488e
MS
107 * Loop through the option string, separating it at commas and marking each
108 * individual option as long as the corresponding PPD option (PageSize,
109 * InputSlot, etc.) is not also set.
d2354e63 110 *
0268488e 111 * For PageSize, we also check for an empty option value since some versions
d4874933 112 * of macOS use it to specify auto-selection of the media based solely on
0268488e 113 * the size.
d2354e63 114 */
5a738aea 115
d2354e63
MS
116 for (val = media; *val;)
117 {
118 /*
119 * Extract the sub-option from the string...
120 */
5a738aea 121
7e86f2f6 122 for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);)
d2354e63
MS
123 *ptr++ = *val++;
124 *ptr++ = '\0';
125
126 if (*val == ',')
127 val ++;
128
129 /*
130 * Mark it...
131 */
132
cc754834
MS
133 if (!page_size || !page_size[0])
134 {
88f9aafc 135 if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
cc754834 136 ppd_mark_option(ppd, "PageSize", s);
f14324a7 137 else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
cc754834
MS
138 ppd_mark_option(ppd, "PageSize", ppd_keyword);
139 }
d2354e63 140
f14324a7
MS
141 if (cache && cache->source_option &&
142 !cupsGetOption(cache->source_option, num_options, options) &&
143 (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL)
144 ppd_mark_option(ppd, cache->source_option, ppd_keyword);
d2354e63 145
54afec33 146 if (!cupsGetOption("MediaType", num_options, options) &&
f14324a7 147 (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
54afec33 148 ppd_mark_option(ppd, "MediaType", ppd_keyword);
5a738aea 149 }
d2354e63
MS
150 }
151
f14324a7 152 if (cache)
0268488e
MS
153 {
154 if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
155 num_options, options) &&
156 !cupsGetOption("APPrinterPreset", num_options, options) &&
f14324a7 157 (print_color_mode || print_quality))
0268488e
MS
158 {
159 /*
160 * Map output-mode and print-quality to a preset...
161 */
162
f14324a7 163 _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */
0268488e
MS
164 _pwg_print_quality_t pwg_pq; /* print-quality index */
165 cups_option_t *preset;/* Current preset option */
166
f14324a7
MS
167 if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
168 pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
0268488e 169 else
f14324a7 170 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
171
172 if (print_quality)
173 {
7e86f2f6 174 pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT);
0268488e
MS
175 if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT)
176 pwg_pq = _PWG_PRINT_QUALITY_DRAFT;
177 else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH)
178 pwg_pq = _PWG_PRINT_QUALITY_HIGH;
179 }
180 else
181 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
182
f14324a7 183 if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
0268488e
MS
184 {
185 /*
186 * Try to find a preset that works so that we maximize the chances of us
187 * getting a good print using IPP attributes.
188 */
189
f14324a7 190 if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
0268488e 191 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
f14324a7
MS
192 else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
193 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
194 else
195 {
f14324a7
MS
196 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
197 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
198 }
199 }
200
f14324a7 201 if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
0268488e
MS
202 {
203 /*
204 * Copy the preset options as long as the corresponding names are not
205 * already defined in the IPP request...
206 */
207
f14324a7
MS
208 for (i = cache->num_presets[pwg_pcm][pwg_pq],
209 preset = cache->presets[pwg_pcm][pwg_pq];
0268488e
MS
210 i > 0;
211 i --, preset ++)
212 {
213 if (!cupsGetOption(preset->name, num_options, options))
214 ppd_mark_option(ppd, preset->name, preset->value);
215 }
216 }
217 }
218
219 if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
88f9aafc 220 (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
0268488e
MS
221 {
222 /*
223 * Map output-bin to OutputBin...
224 */
225
226 ppd_mark_option(ppd, "OutputBin", ppd_keyword);
227 }
228
f14324a7
MS
229 if (sides && cache->sides_option &&
230 !cupsGetOption(cache->sides_option, num_options, options))
0268488e
MS
231 {
232 /*
233 * Map sides to duplex option...
234 */
235
a4845881 236 if (!strcmp(sides, "one-sided") && cache->sides_1sided)
f14324a7 237 ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
a4845881
MS
238 else if (!strcmp(sides, "two-sided-long-edge") &&
239 cache->sides_2sided_long)
f14324a7 240 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
a4845881
MS
241 else if (!strcmp(sides, "two-sided-short-edge") &&
242 cache->sides_2sided_short)
f14324a7 243 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
0268488e
MS
244 }
245 }
246
d2354e63
MS
247 /*
248 * Mark other options...
249 */
250
251 for (i = num_options, optptr = options; i > 0; i --, optptr ++)
40cc612a 252 {
88f9aafc
MS
253 if (!_cups_strcasecmp(optptr->name, "media") ||
254 !_cups_strcasecmp(optptr->name, "output-bin") ||
255 !_cups_strcasecmp(optptr->name, "output-mode") ||
256 !_cups_strcasecmp(optptr->name, "print-quality") ||
257 !_cups_strcasecmp(optptr->name, "sides"))
d2354e63 258 continue;
88f9aafc
MS
259 else if (!_cups_strcasecmp(optptr->name, "resolution") ||
260 !_cups_strcasecmp(optptr->name, "printer-resolution"))
5a738aea 261 {
66ab9486
MS
262 ppd_mark_option(ppd, "Resolution", optptr->value);
263 ppd_mark_option(ppd, "SetResolution", optptr->value);
5a738aea 264 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
66ab9486
MS
265 ppd_mark_option(ppd, "JCLResolution", optptr->value);
266 /* HP */
267 ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
268 /* Canon */
5a738aea 269 }
88f9aafc 270 else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
5a738aea
MS
271 {
272 if (!cupsGetOption("Collate", num_options, options) &&
273 ppdFindOption(ppd, "Collate"))
274 {
88f9aafc 275 if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
66ab9486 276 ppd_mark_option(ppd, "Collate", "True");
5a738aea 277 else
66ab9486 278 ppd_mark_option(ppd, "Collate", "False");
5a738aea
MS
279 }
280 }
88f9aafc 281 else if (!_cups_strcasecmp(optptr->name, "finishings"))
5a738aea
MS
282 {
283 /*
284 * Lookup cupsIPPFinishings attributes for each value...
285 */
286
287 for (ptr = optptr->value; *ptr;)
288 {
289 /*
290 * Get the next finishings number...
291 */
292
293 if (!isdigit(*ptr & 255))
294 break;
295
7e86f2f6 296 if ((j = (int)strtol(ptr, &ptr, 10)) < 3)
5a738aea
MS
297 break;
298
299 /*
300 * Skip separator as needed...
301 */
302
303 if (*ptr == ',')
304 ptr ++;
305
306 /*
307 * Look it up in the PPD file...
308 */
309
310 sprintf(s, "%d", j);
311
312 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
313 continue;
314
315 /*
316 * Apply "*Option Choice" settings from the attribute value...
317 */
318
66ab9486 319 ppd_mark_choices(ppd, attr->value);
5a738aea
MS
320 }
321 }
88f9aafc 322 else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
ef416fc2 323 {
324 /*
66ab9486 325 * Lookup APPrinterPreset value...
ef416fc2 326 */
327
66ab9486
MS
328 if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
329 {
330 /*
331 * Apply "*Option Choice" settings from the attribute value...
332 */
ef416fc2 333
66ab9486
MS
334 ppd_mark_choices(ppd, attr->value);
335 }
ef416fc2 336 }
88f9aafc 337 else if (!_cups_strcasecmp(optptr->name, "mirror"))
66ab9486
MS
338 ppd_mark_option(ppd, "MirrorPrint", optptr->value);
339 else
340 ppd_mark_option(ppd, optptr->name, optptr->value);
40cc612a
MS
341 }
342
343 if (print_quality)
344 {
345 int pq = atoi(print_quality); /* print-quaity value */
346
347 if (pq == IPP_QUALITY_DRAFT)
348 ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
349 else if (pq == IPP_QUALITY_HIGH)
350 ppd_mark_option(ppd, "cupsPrintQuality", "High");
351 else
352 ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
353 }
355e94dc 354
ba55dc12 355 ppd_debug_marked(ppd, "After...");
ef416fc2 356
66ab9486 357 return (ppdConflicts(ppd) > 0);
ef416fc2 358}
359
360
361/*
362 * 'ppdFindChoice()' - Return a pointer to an option choice.
363 */
364
5a738aea 365ppd_choice_t * /* O - Choice pointer or @code NULL@ */
ef416fc2 366ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */
367 const char *choice) /* I - Name of choice */
368{
b94498cf 369 int i; /* Looping var */
370 ppd_choice_t *c; /* Current choice */
ef416fc2 371
372
bdd6c45b 373 if (!o || !choice)
ef416fc2 374 return (NULL);
375
88f9aafc 376 if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
bdd6c45b
MS
377 choice = "Custom";
378
ef416fc2 379 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
88f9aafc 380 if (!_cups_strcasecmp(c->choice, choice))
ef416fc2 381 return (c);
382
383 return (NULL);
384}
385
386
387/*
388 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
389 */
390
5a738aea 391ppd_choice_t * /* O - Pointer to choice or @code NULL@ */
ef416fc2 392ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */
393 const char *option) /* I - Keyword/option name */
394{
d1c13e16
MS
395 ppd_choice_t key, /* Search key for choice */
396 *marked; /* Marked choice */
ef416fc2 397
398
e07d4801 399 DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
d1c13e16 400
b94498cf 401 if ((key.option = ppdFindOption(ppd, option)) == NULL)
d1c13e16 402 {
e07d4801 403 DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
ef416fc2 404 return (NULL);
d1c13e16
MS
405 }
406
407 marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
408
e07d4801 409 DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
d1c13e16 410 marked ? marked->choice : "NULL"));
ef416fc2 411
d1c13e16 412 return (marked);
ef416fc2 413}
414
415
416/*
417 * 'ppdFindOption()' - Return a pointer to the specified option.
418 */
419
5a738aea 420ppd_option_t * /* O - Pointer to option or @code NULL@ */
ef416fc2 421ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */
422 const char *option) /* I - Option/Keyword name */
423{
fa73b229 424 /*
425 * Range check input...
426 */
ef416fc2 427
fa73b229 428 if (!ppd || !option)
ef416fc2 429 return (NULL);
430
f301802f 431 if (ppd->options)
432 {
433 /*
434 * Search in the array...
435 */
436
437 ppd_option_t key; /* Option search key */
438
439
440 strlcpy(key.keyword, option, sizeof(key.keyword));
ef416fc2 441
f301802f 442 return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
443 }
444 else
445 {
446 /*
447 * Search in each group...
448 */
ef416fc2 449
f301802f 450 int i, j; /* Looping vars */
451 ppd_group_t *group; /* Current group */
452 ppd_option_t *optptr; /* Current option */
453
454
455 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
456 for (j = group->num_options, optptr = group->options;
457 j > 0;
458 j --, optptr ++)
88f9aafc 459 if (!_cups_strcasecmp(optptr->keyword, option))
f301802f 460 return (optptr);
461
462 return (NULL);
463 }
ef416fc2 464}
465
466
467/*
5a738aea 468 * 'ppdIsMarked()' - Check to see if an option is marked.
ef416fc2 469 */
470
b94498cf 471int /* O - Non-zero if option is marked */
472ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */
473 const char *option, /* I - Option/Keyword name */
474 const char *choice) /* I - Choice name */
ef416fc2 475{
b94498cf 476 ppd_choice_t key, /* Search key */
477 *c; /* Choice pointer */
ef416fc2 478
479
b94498cf 480 if (!ppd)
ef416fc2 481 return (0);
482
b94498cf 483 if ((key.option = ppdFindOption(ppd, option)) == NULL)
ef416fc2 484 return (0);
485
b94498cf 486 if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
ef416fc2 487 return (0);
488
b94498cf 489 return (!strcmp(c->choice, choice));
ef416fc2 490}
491
492
493/*
494 * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
495 */
496
497void
b94498cf 498ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */
ef416fc2 499{
b94498cf 500 int i; /* Looping variables */
501 ppd_group_t *g; /* Current group */
502 ppd_choice_t *c; /* Current choice */
ef416fc2 503
504
b94498cf 505 if (!ppd)
ef416fc2 506 return;
507
b94498cf 508 /*
509 * Clean out the marked array...
510 */
511
512 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
513 c;
514 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
7cf5915e 515 {
b94498cf 516 cupsArrayRemove(ppd->marked, c);
7cf5915e
MS
517 c->marked = 0;
518 }
b94498cf 519
520 /*
521 * Then repopulate it with the defaults...
522 */
523
ef416fc2 524 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
525 ppd_defaults(ppd, g);
93a5da07
MS
526
527 /*
528 * Finally, tag any conflicts (API compatibility) once at the end.
529 */
530
531 ppdConflicts(ppd);
ef416fc2 532}
533
534
535/*
66ab9486
MS
536 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
537 * conflicts.
ef416fc2 538 */
539
540int /* O - Number of conflicts */
541ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */
542 const char *option, /* I - Keyword */
543 const char *choice) /* I - Option name */
544{
e07d4801 545 DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
bd7854cb 546 ppd, option, choice));
547
fa73b229 548 /*
549 * Range check input...
550 */
551
552 if (!ppd || !option || !choice)
ef416fc2 553 return (0);
554
66ab9486
MS
555 /*
556 * Mark the option...
557 */
558
559 ppd_mark_option(ppd, option, choice);
560
561 /*
562 * Return the number of conflicts...
563 */
564
565 return (ppdConflicts(ppd));
566}
567
568
569/*
570 * 'ppdFirstOption()' - Return the first option in the PPD file.
571 *
572 * Options are returned from all groups in ascending alphanumeric order.
573 *
8072030b 574 * @since CUPS 1.2/macOS 10.5@
66ab9486
MS
575 */
576
577ppd_option_t * /* O - First option or @code NULL@ */
578ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */
579{
580 if (!ppd)
581 return (NULL);
582 else
583 return ((ppd_option_t *)cupsArrayFirst(ppd->options));
584}
585
586
587/*
588 * 'ppdNextOption()' - Return the next option in the PPD file.
589 *
590 * Options are returned from all groups in ascending alphanumeric order.
591 *
8072030b 592 * @since CUPS 1.2/macOS 10.5@
66ab9486
MS
593 */
594
595ppd_option_t * /* O - Next option or @code NULL@ */
596ppdNextOption(ppd_file_t *ppd) /* I - PPD file */
597{
598 if (!ppd)
599 return (NULL);
600 else
601 return ((ppd_option_t *)cupsArrayNext(ppd->options));
602}
603
604
605/*
606 * '_ppdParseOptions()' - Parse options from a PPD file.
607 *
608 * This function looks for strings of the form:
609 *
610 * *option choice ... *optionN choiceN
c7017ecc 611 * property value ... propertyN valueN
66ab9486
MS
612 *
613 * It stops when it finds a string that doesn't match this format.
614 */
615
616int /* O - Number of options */
617_ppdParseOptions(
618 const char *s, /* I - String to parse */
619 int num_options, /* I - Number of options */
c7017ecc
MS
620 cups_option_t **options, /* IO - Options */
621 _ppd_parse_t which) /* I - What to parse */
66ab9486 622{
7cf5915e 623 char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */
c7017ecc 624 choice[PPD_MAX_NAME], /* Current choice/value */
66ab9486
MS
625 *ptr; /* Pointer into option or choice */
626
627
628 if (!s)
629 return (num_options);
630
631 /*
c7017ecc
MS
632 * Read all of the "*Option Choice" and "property value" pairs from the
633 * string, add them to an options array as we go...
66ab9486
MS
634 */
635
636 while (*s)
637 {
638 /*
639 * Skip leading whitespace...
640 */
641
7cf5915e 642 while (_cups_isspace(*s))
66ab9486
MS
643 s ++;
644
66ab9486 645 /*
c7017ecc 646 * Get the option/property name...
66ab9486
MS
647 */
648
66ab9486 649 ptr = option;
7cf5915e 650 while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
66ab9486
MS
651 *ptr++ = *s++;
652
7cf5915e 653 if (ptr == s || !_cups_isspace(*s))
66ab9486
MS
654 break;
655
656 *ptr = '\0';
657
658 /*
659 * Get the choice...
660 */
661
7cf5915e 662 while (_cups_isspace(*s))
66ab9486
MS
663 s ++;
664
665 if (!*s)
666 break;
667
668 ptr = choice;
7cf5915e 669 while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
66ab9486
MS
670 *ptr++ = *s++;
671
7cf5915e 672 if (*s && !_cups_isspace(*s))
c7017ecc
MS
673 break;
674
66ab9486
MS
675 *ptr = '\0';
676
677 /*
678 * Add it to the options array...
679 */
680
c7017ecc
MS
681 if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES)
682 num_options = cupsAddOption(option + 1, choice, num_options, options);
683 else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS)
684 num_options = cupsAddOption(option, choice, num_options, options);
66ab9486
MS
685 }
686
687 return (num_options);
688}
689
690
691#ifdef DEBUG
692/*
ba55dc12 693 * 'ppd_debug_marked()' - Output the marked array to stdout...
66ab9486
MS
694 */
695
696static void
ba55dc12 697ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */
66ab9486
MS
698 const char *title) /* I - Title for list */
699{
700 ppd_choice_t *c; /* Current choice */
701
702
e07d4801 703 DEBUG_printf(("2cupsMarkOptions: %s", title));
66ab9486
MS
704
705 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
706 c;
707 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
e07d4801 708 DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
66ab9486
MS
709}
710#endif /* DEBUG */
711
712
713/*
714 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
715 */
716
717static void
718ppd_defaults(ppd_file_t *ppd, /* I - PPD file */
719 ppd_group_t *g) /* I - Group to default */
720{
721 int i; /* Looping var */
722 ppd_option_t *o; /* Current option */
723 ppd_group_t *sg; /* Current sub-group */
724
725
726 for (i = g->num_options, o = g->options; i > 0; i --, o ++)
88f9aafc 727 if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
93a5da07 728 ppd_mark_option(ppd, o->keyword, o->defchoice);
66ab9486
MS
729
730 for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
731 ppd_defaults(ppd, sg);
732}
733
734
735/*
736 * 'ppd_mark_choices()' - Mark one or more option choices from a string.
737 */
738
739static void
740ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
741 const char *s) /* I - "*Option Choice ..." string */
742{
743 int i, /* Looping var */
744 num_options; /* Number of options */
745 cups_option_t *options, /* Options */
746 *option; /* Current option */
747
748
5d6412a9 749 if (!s)
66ab9486
MS
750 return;
751
752 options = NULL;
c7017ecc 753 num_options = _ppdParseOptions(s, 0, &options, 0);
66ab9486
MS
754
755 for (i = num_options, option = options; i > 0; i --, option ++)
756 ppd_mark_option(ppd, option->name, option->value);
757
758 cupsFreeOptions(num_options, options);
759}
760
761
762/*
763 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
764 */
765
766static void
767ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */
768 const char *option, /* I - Option name */
769 const char *choice) /* I - Choice name */
770{
771 int i, j; /* Looping vars */
772 ppd_option_t *o; /* Option pointer */
773 ppd_choice_t *c, /* Choice pointer */
774 *oldc, /* Old choice pointer */
775 key; /* Search key for choice */
776 struct lconv *loc; /* Locale data */
777
778
e07d4801 779 DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
66ab9486
MS
780 ppd, option, choice));
781
fa73b229 782 /*
d4874933 783 * AP_D_InputSlot is the "default input slot" on macOS, and setting
fa73b229 784 * it clears the regular InputSlot choices...
785 */
ef416fc2 786
88f9aafc 787 if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
fa73b229 788 {
ef55b745
MS
789 cupsArraySave(ppd->options);
790
fa73b229 791 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
b94498cf 792 {
793 key.option = o;
794 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
795 {
796 oldc->marked = 0;
797 cupsArrayRemove(ppd->marked, oldc);
798 }
799 }
ef55b745
MS
800
801 cupsArrayRestore(ppd->options);
ef416fc2 802 }
803
fa73b229 804 /*
805 * Check for custom options...
806 */
807
ef55b745
MS
808 cupsArraySave(ppd->options);
809
810 o = ppdFindOption(ppd, option);
811
812 cupsArrayRestore(ppd->options);
813
814 if (!o)
66ab9486 815 return;
ef416fc2 816
757d2cad 817 loc = localeconv();
ef416fc2 818
88f9aafc 819 if (!_cups_strncasecmp(choice, "Custom.", 7))
ef416fc2 820 {
821 /*
fa73b229 822 * Handle a custom option...
ef416fc2 823 */
824
fa73b229 825 if ((c = ppdFindChoice(o, "Custom")) == NULL)
66ab9486 826 return;
ef416fc2 827
88f9aafc 828 if (!_cups_strcasecmp(option, "PageSize"))
fa73b229 829 {
830 /*
831 * Handle custom page sizes...
832 */
ef416fc2 833
fa73b229 834 ppdPageSize(ppd, choice);
835 }
836 else
ef416fc2 837 {
838 /*
fa73b229 839 * Handle other custom options...
ef416fc2 840 */
841
fa73b229 842 ppd_coption_t *coption; /* Custom option */
843 ppd_cparam_t *cparam; /* Custom parameter */
757d2cad 844 char *units; /* Custom points units */
fa73b229 845
8ca02f3c 846
fa73b229 847 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
ef416fc2 848 {
fa73b229 849 if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
66ab9486 850 return;
fa73b229 851
852 switch (cparam->type)
853 {
dc00a7c3
MS
854 case PPD_CUSTOM_UNKNOWN :
855 break;
856
fa73b229 857 case PPD_CUSTOM_CURVE :
858 case PPD_CUSTOM_INVCURVE :
859 case PPD_CUSTOM_REAL :
b86bc4cf 860 cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
861 NULL, loc);
fa73b229 862 break;
863
864 case PPD_CUSTOM_POINTS :
b86bc4cf 865 cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
866 &units,
867 loc);
757d2cad 868
869 if (units)
870 {
88f9aafc 871 if (!_cups_strcasecmp(units, "cm"))
ef55b745 872 cparam->current.custom_points *= 72.0f / 2.54f;
88f9aafc 873 else if (!_cups_strcasecmp(units, "mm"))
ef55b745 874 cparam->current.custom_points *= 72.0f / 25.4f;
88f9aafc 875 else if (!_cups_strcasecmp(units, "m"))
ef55b745 876 cparam->current.custom_points *= 72.0f / 0.0254f;
88f9aafc 877 else if (!_cups_strcasecmp(units, "in"))
ef55b745 878 cparam->current.custom_points *= 72.0f;
88f9aafc 879 else if (!_cups_strcasecmp(units, "ft"))
ef55b745 880 cparam->current.custom_points *= 12.0f * 72.0f;
757d2cad 881 }
fa73b229 882 break;
883
884 case PPD_CUSTOM_INT :
885 cparam->current.custom_int = atoi(choice + 7);
886 break;
887
888 case PPD_CUSTOM_PASSCODE :
889 case PPD_CUSTOM_PASSWORD :
890 case PPD_CUSTOM_STRING :
891 if (cparam->current.custom_string)
5a00cf37 892 free(cparam->current.custom_string);
fa73b229 893
5a00cf37 894 cparam->current.custom_string = strdup(choice + 7);
fa73b229 895 break;
896 }
ef416fc2 897 }
898 }
8ca02f3c 899
900 /*
901 * Make sure that we keep the option marked below...
902 */
903
904 choice = "Custom";
fa73b229 905 }
b423cd4c 906 else if (choice[0] == '{')
907 {
908 /*
909 * Handle multi-value custom options...
910 */
911
912 ppd_coption_t *coption; /* Custom option */
913 ppd_cparam_t *cparam; /* Custom parameter */
757d2cad 914 char *units; /* Custom points units */
b423cd4c 915 int num_vals; /* Number of values */
916 cups_option_t *vals, /* Values */
917 *val; /* Value */
918
919
920 if ((c = ppdFindChoice(o, "Custom")) == NULL)
66ab9486 921 return;
b423cd4c 922
923 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
924 {
ee571f26 925 num_vals = cupsParseOptions(choice, 0, &vals);
b423cd4c 926
927 for (i = 0, val = vals; i < num_vals; i ++, val ++)
928 {
929 if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
930 continue;
931
932 switch (cparam->type)
933 {
dc00a7c3
MS
934 case PPD_CUSTOM_UNKNOWN :
935 break;
936
b423cd4c 937 case PPD_CUSTOM_CURVE :
938 case PPD_CUSTOM_INVCURVE :
939 case PPD_CUSTOM_REAL :
b86bc4cf 940 cparam->current.custom_real = (float)_cupsStrScand(val->value,
941 NULL, loc);
b423cd4c 942 break;
943
944 case PPD_CUSTOM_POINTS :
b86bc4cf 945 cparam->current.custom_points = (float)_cupsStrScand(val->value,
946 &units,
947 loc);
757d2cad 948
949 if (units)
950 {
88f9aafc 951 if (!_cups_strcasecmp(units, "cm"))
b86bc4cf 952 cparam->current.custom_points *= 72.0f / 2.54f;
88f9aafc 953 else if (!_cups_strcasecmp(units, "mm"))
b86bc4cf 954 cparam->current.custom_points *= 72.0f / 25.4f;
88f9aafc 955 else if (!_cups_strcasecmp(units, "m"))
b86bc4cf 956 cparam->current.custom_points *= 72.0f / 0.0254f;
88f9aafc 957 else if (!_cups_strcasecmp(units, "in"))
b86bc4cf 958 cparam->current.custom_points *= 72.0f;
88f9aafc 959 else if (!_cups_strcasecmp(units, "ft"))
b86bc4cf 960 cparam->current.custom_points *= 12.0f * 72.0f;
757d2cad 961 }
b423cd4c 962 break;
963
964 case PPD_CUSTOM_INT :
965 cparam->current.custom_int = atoi(val->value);
966 break;
967
968 case PPD_CUSTOM_PASSCODE :
969 case PPD_CUSTOM_PASSWORD :
970 case PPD_CUSTOM_STRING :
971 if (cparam->current.custom_string)
5a00cf37 972 free(cparam->current.custom_string);
b423cd4c 973
5a00cf37 974 cparam->current.custom_string = strdup(val->value);
b423cd4c 975 break;
976 }
977 }
978
979 cupsFreeOptions(num_vals, vals);
980 }
981 }
fa73b229 982 else
983 {
984 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
88f9aafc 985 if (!_cups_strcasecmp(c->choice, choice))
fa73b229 986 break;
ef416fc2 987
fa73b229 988 if (!i)
66ab9486 989 return;
fa73b229 990 }
ef416fc2 991
fa73b229 992 /*
993 * Option found; mark it and then handle unmarking any other options.
994 */
995
fa73b229 996 if (o->ui != PPD_UI_PICKMANY)
997 {
998 /*
999 * Unmark all other choices...
1000 */
1001
b94498cf 1002 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
1003 {
1004 oldc->marked = 0;
1005 cupsArrayRemove(ppd->marked, oldc);
1006 }
1007
88f9aafc 1008 if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
b94498cf 1009 {
1010 /*
1011 * Mark current page size...
1012 */
1013
1014 for (j = 0; j < ppd->num_sizes; j ++)
88f9aafc 1015 ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
b94498cf 1016 choice);
1017
1018 /*
1019 * Unmark the current PageSize or PageRegion setting, as
1020 * appropriate...
1021 */
1022
ef55b745
MS
1023 cupsArraySave(ppd->options);
1024
88f9aafc 1025 if (!_cups_strcasecmp(option, "PageSize"))
fa73b229 1026 {
b94498cf 1027 if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
1028 {
1029 key.option = o;
1030 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1031 {
1032 oldc->marked = 0;
1033 cupsArrayRemove(ppd->marked, oldc);
1034 }
1035 }
1036 }
1037 else
1038 {
1039 if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
1040 {
1041 key.option = o;
1042 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1043 {
1044 oldc->marked = 0;
1045 cupsArrayRemove(ppd->marked, oldc);
1046 }
1047 }
1048 }
ef55b745
MS
1049
1050 cupsArrayRestore(ppd->options);
b94498cf 1051 }
88f9aafc 1052 else if (!_cups_strcasecmp(option, "InputSlot"))
b94498cf 1053 {
1054 /*
355e94dc 1055 * Unmark ManualFeed option...
b94498cf 1056 */
fa73b229 1057
ef55b745
MS
1058 cupsArraySave(ppd->options);
1059
b94498cf 1060 if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
1061 {
1062 key.option = o;
1063 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1064 {
1065 oldc->marked = 0;
1066 cupsArrayRemove(ppd->marked, oldc);
1067 }
1068 }
ef55b745
MS
1069
1070 cupsArrayRestore(ppd->options);
b94498cf 1071 }
88f9aafc
MS
1072 else if (!_cups_strcasecmp(option, "ManualFeed") &&
1073 !_cups_strcasecmp(choice, "True"))
b94498cf 1074 {
1075 /*
1076 * Unmark InputSlot option...
1077 */
fa73b229 1078
ef55b745
MS
1079 cupsArraySave(ppd->options);
1080
b94498cf 1081 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
1082 {
1083 key.option = o;
1084 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1085 {
1086 oldc->marked = 0;
1087 cupsArrayRemove(ppd->marked, oldc);
1088 }
fa73b229 1089 }
ef55b745
MS
1090
1091 cupsArrayRestore(ppd->options);
b94498cf 1092 }
ef416fc2 1093 }
1094
b94498cf 1095 c->marked = 1;
1096
1097 cupsArrayAdd(ppd->marked, c);
5a738aea 1098}