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