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