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