2 * Option marking routines for CUPS.
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
10 * PostScript is a trademark of Adobe Systems, Inc.
14 * Include necessary headers...
17 #include "cups-private.h"
18 #include "ppd-private.h"
19 #include "debug-internal.h"
27 static void ppd_debug_marked(ppd_file_t *ppd, const char *title);
29 # define ppd_debug_marked(ppd,title)
31 static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
32 static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
33 static void ppd_mark_option(ppd_file_t *ppd, const char *option,
38 * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
40 * This function maps the IPP "finishings", "media", "mirror",
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.
46 int /* O - 1 if conflicts exist, 0 otherwise */
48 ppd_file_t *ppd, /* I - PPD file */
49 int num_options, /* I - Number of options */
50 cups_option_t *options) /* I - Options */
52 int i, j; /* Looping vars */
53 char *ptr, /* Pointer into string */
54 s[255]; /* Temporary string */
55 const char *val, /* Pointer into value */
56 *media, /* media option */
57 *output_bin, /* output-bin option */
58 *page_size, /* PageSize option */
59 *ppd_keyword, /* PPD keyword */
60 *print_color_mode, /* print-color-mode option */
61 *print_quality, /* print-quality option */
62 *sides; /* sides option */
63 cups_option_t *optptr; /* Current option */
64 ppd_attr_t *attr; /* PPD attribute */
65 _ppd_cache_t *cache; /* PPD cache and mapping data */
72 if (!ppd || num_options <= 0 || !options)
75 ppd_debug_marked(ppd, "Before...");
78 * Do special handling for finishings, media, output-bin, output-mode,
79 * print-color-mode, print-quality, and PageSize...
82 media = cupsGetOption("media", num_options, options);
83 output_bin = cupsGetOption("output-bin", num_options, options);
84 page_size = cupsGetOption("PageSize", num_options, options);
85 print_quality = cupsGetOption("print-quality", num_options, options);
86 sides = cupsGetOption("sides", num_options, options);
88 if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
90 print_color_mode = cupsGetOption("output-mode", num_options, options);
92 if ((media || output_bin || print_color_mode || print_quality || sides) &&
96 * Load PPD cache and mapping data as needed...
99 ppd->cache = _ppdCacheCreateWithPPD(ppd);
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.
111 * For PageSize, we also check for an empty option value since some versions
112 * of macOS use it to specify auto-selection of the media based solely on
116 for (val = media; *val;)
119 * Extract the sub-option from the string...
122 for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);)
133 if (!page_size || !page_size[0])
135 if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
136 ppd_mark_option(ppd, "PageSize", s);
137 else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
138 ppd_mark_option(ppd, "PageSize", ppd_keyword);
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);
146 if (!cupsGetOption("MediaType", num_options, options) &&
147 (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
148 ppd_mark_option(ppd, "MediaType", ppd_keyword);
154 if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
155 num_options, options) &&
156 !cupsGetOption("APPrinterPreset", num_options, options) &&
157 (print_color_mode || print_quality))
160 * Map output-mode and print-quality to a preset...
163 _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */
164 _pwg_print_quality_t pwg_pq; /* print-quality index */
165 cups_option_t *preset;/* Current preset option */
167 if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
168 pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
170 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
174 pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT);
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;
181 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
183 if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
186 * Try to find a preset that works so that we maximize the chances of us
187 * getting a good print using IPP attributes.
190 if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
191 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
192 else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
193 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
196 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
197 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
201 if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
204 * Copy the preset options as long as the corresponding names are not
205 * already defined in the IPP request...
208 for (i = cache->num_presets[pwg_pcm][pwg_pq],
209 preset = cache->presets[pwg_pcm][pwg_pq];
213 if (!cupsGetOption(preset->name, num_options, options))
214 ppd_mark_option(ppd, preset->name, preset->value);
219 if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
220 (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
223 * Map output-bin to OutputBin...
226 ppd_mark_option(ppd, "OutputBin", ppd_keyword);
229 if (sides && cache->sides_option &&
230 !cupsGetOption(cache->sides_option, num_options, options))
233 * Map sides to duplex option...
236 if (!strcmp(sides, "one-sided") && cache->sides_1sided)
237 ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
238 else if (!strcmp(sides, "two-sided-long-edge") &&
239 cache->sides_2sided_long)
240 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
241 else if (!strcmp(sides, "two-sided-short-edge") &&
242 cache->sides_2sided_short)
243 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
248 * Mark other options...
251 for (i = num_options, optptr = options; i > 0; i --, optptr ++)
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"))
259 else if (!_cups_strcasecmp(optptr->name, "resolution") ||
260 !_cups_strcasecmp(optptr->name, "printer-resolution"))
262 ppd_mark_option(ppd, "Resolution", optptr->value);
263 ppd_mark_option(ppd, "SetResolution", optptr->value);
264 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
265 ppd_mark_option(ppd, "JCLResolution", optptr->value);
267 ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
270 else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
272 if (!cupsGetOption("Collate", num_options, options) &&
273 ppdFindOption(ppd, "Collate"))
275 if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
276 ppd_mark_option(ppd, "Collate", "True");
278 ppd_mark_option(ppd, "Collate", "False");
281 else if (!_cups_strcasecmp(optptr->name, "finishings"))
284 * Lookup cupsIPPFinishings attributes for each value...
287 for (ptr = optptr->value; *ptr;)
290 * Get the next finishings number...
293 if (!isdigit(*ptr & 255))
296 if ((j = (int)strtol(ptr, &ptr, 10)) < 3)
300 * Skip separator as needed...
307 * Look it up in the PPD file...
312 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
316 * Apply "*Option Choice" settings from the attribute value...
319 ppd_mark_choices(ppd, attr->value);
322 else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
325 * Lookup APPrinterPreset value...
328 if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
331 * Apply "*Option Choice" settings from the attribute value...
334 ppd_mark_choices(ppd, attr->value);
337 else if (!_cups_strcasecmp(optptr->name, "mirror"))
338 ppd_mark_option(ppd, "MirrorPrint", optptr->value);
340 ppd_mark_option(ppd, optptr->name, optptr->value);
345 int pq = atoi(print_quality); /* print-quaity value */
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");
352 ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
355 ppd_debug_marked(ppd, "After...");
357 return (ppdConflicts(ppd) > 0);
362 * 'ppdFindChoice()' - Return a pointer to an option choice.
365 ppd_choice_t * /* O - Choice pointer or @code NULL@ */
366 ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */
367 const char *choice) /* I - Name of choice */
369 int i; /* Looping var */
370 ppd_choice_t *c; /* Current choice */
376 if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
379 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
380 if (!_cups_strcasecmp(c->choice, choice))
388 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
391 ppd_choice_t * /* O - Pointer to choice or @code NULL@ */
392 ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */
393 const char *option) /* I - Keyword/option name */
395 ppd_choice_t key, /* Search key for choice */
396 *marked; /* Marked choice */
399 DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
401 if ((key.option = ppdFindOption(ppd, option)) == NULL)
403 DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
407 marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
409 DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
410 marked ? marked->choice : "NULL"));
417 * 'ppdFindOption()' - Return a pointer to the specified option.
420 ppd_option_t * /* O - Pointer to option or @code NULL@ */
421 ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */
422 const char *option) /* I - Option/Keyword name */
425 * Range check input...
434 * Search in the array...
437 ppd_option_t key; /* Option search key */
440 strlcpy(key.keyword, option, sizeof(key.keyword));
442 return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
447 * Search in each group...
450 int i, j; /* Looping vars */
451 ppd_group_t *group; /* Current group */
452 ppd_option_t *optptr; /* Current option */
455 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
456 for (j = group->num_options, optptr = group->options;
459 if (!_cups_strcasecmp(optptr->keyword, option))
468 * 'ppdIsMarked()' - Check to see if an option is marked.
471 int /* O - Non-zero if option is marked */
472 ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */
473 const char *option, /* I - Option/Keyword name */
474 const char *choice) /* I - Choice name */
476 ppd_choice_t key, /* Search key */
477 *c; /* Choice pointer */
483 if ((key.option = ppdFindOption(ppd, option)) == NULL)
486 if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
489 return (!strcmp(c->choice, choice));
494 * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
498 ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */
500 int i; /* Looping variables */
501 ppd_group_t *g; /* Current group */
502 ppd_choice_t *c; /* Current choice */
509 * Clean out the marked array...
512 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
514 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
516 cupsArrayRemove(ppd->marked, c);
521 * Then repopulate it with the defaults...
524 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
525 ppd_defaults(ppd, g);
528 * Finally, tag any conflicts (API compatibility) once at the end.
536 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
540 int /* O - Number of conflicts */
541 ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */
542 const char *option, /* I - Keyword */
543 const char *choice) /* I - Option name */
545 DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
546 ppd, option, choice));
549 * Range check input...
552 if (!ppd || !option || !choice)
559 ppd_mark_option(ppd, option, choice);
562 * Return the number of conflicts...
565 return (ppdConflicts(ppd));
570 * 'ppdFirstOption()' - Return the first option in the PPD file.
572 * Options are returned from all groups in ascending alphanumeric order.
574 * @since CUPS 1.2/macOS 10.5@
577 ppd_option_t * /* O - First option or @code NULL@ */
578 ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */
583 return ((ppd_option_t *)cupsArrayFirst(ppd->options));
588 * 'ppdNextOption()' - Return the next option in the PPD file.
590 * Options are returned from all groups in ascending alphanumeric order.
592 * @since CUPS 1.2/macOS 10.5@
595 ppd_option_t * /* O - Next option or @code NULL@ */
596 ppdNextOption(ppd_file_t *ppd) /* I - PPD file */
601 return ((ppd_option_t *)cupsArrayNext(ppd->options));
606 * '_ppdParseOptions()' - Parse options from a PPD file.
608 * This function looks for strings of the form:
610 * *option choice ... *optionN choiceN
611 * property value ... propertyN valueN
613 * It stops when it finds a string that doesn't match this format.
616 int /* O - Number of options */
618 const char *s, /* I - String to parse */
619 int num_options, /* I - Number of options */
620 cups_option_t **options, /* IO - Options */
621 _ppd_parse_t which) /* I - What to parse */
623 char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */
624 choice[PPD_MAX_NAME], /* Current choice/value */
625 *ptr; /* Pointer into option or choice */
629 return (num_options);
632 * Read all of the "*Option Choice" and "property value" pairs from the
633 * string, add them to an options array as we go...
639 * Skip leading whitespace...
642 while (_cups_isspace(*s))
646 * Get the option/property name...
650 while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
653 if (ptr == s || !_cups_isspace(*s))
662 while (_cups_isspace(*s))
669 while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
672 if (*s && !_cups_isspace(*s))
678 * Add it to the options array...
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);
687 return (num_options);
693 * 'ppd_debug_marked()' - Output the marked array to stdout...
697 ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */
698 const char *title) /* I - Title for list */
700 ppd_choice_t *c; /* Current choice */
703 DEBUG_printf(("2cupsMarkOptions: %s", title));
705 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
707 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
708 DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
714 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
718 ppd_defaults(ppd_file_t *ppd, /* I - PPD file */
719 ppd_group_t *g) /* I - Group to default */
721 int i; /* Looping var */
722 ppd_option_t *o; /* Current option */
723 ppd_group_t *sg; /* Current sub-group */
726 for (i = g->num_options, o = g->options; i > 0; i --, o ++)
727 if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
728 ppd_mark_option(ppd, o->keyword, o->defchoice);
730 for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
731 ppd_defaults(ppd, sg);
736 * 'ppd_mark_choices()' - Mark one or more option choices from a string.
740 ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
741 const char *s) /* I - "*Option Choice ..." string */
743 int i, /* Looping var */
744 num_options; /* Number of options */
745 cups_option_t *options, /* Options */
746 *option; /* Current option */
753 num_options = _ppdParseOptions(s, 0, &options, 0);
755 for (i = num_options, option = options; i > 0; i --, option ++)
756 ppd_mark_option(ppd, option->name, option->value);
758 cupsFreeOptions(num_options, options);
763 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
767 ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */
768 const char *option, /* I - Option name */
769 const char *choice) /* I - Choice name */
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 */
779 DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
780 ppd, option, choice));
783 * AP_D_InputSlot is the "default input slot" on macOS, and setting
784 * it clears the regular InputSlot choices...
787 if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
789 cupsArraySave(ppd->options);
791 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
794 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
797 cupsArrayRemove(ppd->marked, oldc);
801 cupsArrayRestore(ppd->options);
805 * Check for custom options...
808 cupsArraySave(ppd->options);
810 o = ppdFindOption(ppd, option);
812 cupsArrayRestore(ppd->options);
819 if (!_cups_strncasecmp(choice, "Custom.", 7))
822 * Handle a custom option...
825 if ((c = ppdFindChoice(o, "Custom")) == NULL)
828 if (!_cups_strcasecmp(option, "PageSize"))
831 * Handle custom page sizes...
834 ppdPageSize(ppd, choice);
839 * Handle other custom options...
842 ppd_coption_t *coption; /* Custom option */
843 ppd_cparam_t *cparam; /* Custom parameter */
844 char *units; /* Custom points units */
847 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
849 if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
852 switch (cparam->type)
854 case PPD_CUSTOM_UNKNOWN :
857 case PPD_CUSTOM_CURVE :
858 case PPD_CUSTOM_INVCURVE :
859 case PPD_CUSTOM_REAL :
860 cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
864 case PPD_CUSTOM_POINTS :
865 cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
871 if (!_cups_strcasecmp(units, "cm"))
872 cparam->current.custom_points *= 72.0f / 2.54f;
873 else if (!_cups_strcasecmp(units, "mm"))
874 cparam->current.custom_points *= 72.0f / 25.4f;
875 else if (!_cups_strcasecmp(units, "m"))
876 cparam->current.custom_points *= 72.0f / 0.0254f;
877 else if (!_cups_strcasecmp(units, "in"))
878 cparam->current.custom_points *= 72.0f;
879 else if (!_cups_strcasecmp(units, "ft"))
880 cparam->current.custom_points *= 12.0f * 72.0f;
884 case PPD_CUSTOM_INT :
885 cparam->current.custom_int = atoi(choice + 7);
888 case PPD_CUSTOM_PASSCODE :
889 case PPD_CUSTOM_PASSWORD :
890 case PPD_CUSTOM_STRING :
891 if (cparam->current.custom_string)
892 free(cparam->current.custom_string);
894 cparam->current.custom_string = strdup(choice + 7);
901 * Make sure that we keep the option marked below...
906 else if (choice[0] == '{')
909 * Handle multi-value custom options...
912 ppd_coption_t *coption; /* Custom option */
913 ppd_cparam_t *cparam; /* Custom parameter */
914 char *units; /* Custom points units */
915 int num_vals; /* Number of values */
916 cups_option_t *vals, /* Values */
920 if ((c = ppdFindChoice(o, "Custom")) == NULL)
923 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
925 num_vals = cupsParseOptions(choice, 0, &vals);
927 for (i = 0, val = vals; i < num_vals; i ++, val ++)
929 if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
932 switch (cparam->type)
934 case PPD_CUSTOM_UNKNOWN :
937 case PPD_CUSTOM_CURVE :
938 case PPD_CUSTOM_INVCURVE :
939 case PPD_CUSTOM_REAL :
940 cparam->current.custom_real = (float)_cupsStrScand(val->value,
944 case PPD_CUSTOM_POINTS :
945 cparam->current.custom_points = (float)_cupsStrScand(val->value,
951 if (!_cups_strcasecmp(units, "cm"))
952 cparam->current.custom_points *= 72.0f / 2.54f;
953 else if (!_cups_strcasecmp(units, "mm"))
954 cparam->current.custom_points *= 72.0f / 25.4f;
955 else if (!_cups_strcasecmp(units, "m"))
956 cparam->current.custom_points *= 72.0f / 0.0254f;
957 else if (!_cups_strcasecmp(units, "in"))
958 cparam->current.custom_points *= 72.0f;
959 else if (!_cups_strcasecmp(units, "ft"))
960 cparam->current.custom_points *= 12.0f * 72.0f;
964 case PPD_CUSTOM_INT :
965 cparam->current.custom_int = atoi(val->value);
968 case PPD_CUSTOM_PASSCODE :
969 case PPD_CUSTOM_PASSWORD :
970 case PPD_CUSTOM_STRING :
971 if (cparam->current.custom_string)
972 free(cparam->current.custom_string);
974 cparam->current.custom_string = strdup(val->value);
979 cupsFreeOptions(num_vals, vals);
984 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
985 if (!_cups_strcasecmp(c->choice, choice))
993 * Option found; mark it and then handle unmarking any other options.
996 if (o->ui != PPD_UI_PICKMANY)
999 * Unmark all other choices...
1002 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
1005 cupsArrayRemove(ppd->marked, oldc);
1008 if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
1011 * Mark current page size...
1014 for (j = 0; j < ppd->num_sizes; j ++)
1015 ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
1019 * Unmark the current PageSize or PageRegion setting, as
1023 cupsArraySave(ppd->options);
1025 if (!_cups_strcasecmp(option, "PageSize"))
1027 if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
1030 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1033 cupsArrayRemove(ppd->marked, oldc);
1039 if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
1042 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1045 cupsArrayRemove(ppd->marked, oldc);
1050 cupsArrayRestore(ppd->options);
1052 else if (!_cups_strcasecmp(option, "InputSlot"))
1055 * Unmark ManualFeed option...
1058 cupsArraySave(ppd->options);
1060 if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
1063 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1066 cupsArrayRemove(ppd->marked, oldc);
1070 cupsArrayRestore(ppd->options);
1072 else if (!_cups_strcasecmp(option, "ManualFeed") &&
1073 !_cups_strcasecmp(choice, "True"))
1076 * Unmark InputSlot option...
1079 cupsArraySave(ppd->options);
1081 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
1084 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1087 cupsArrayRemove(ppd->marked, oldc);
1091 cupsArrayRestore(ppd->options);
1097 cupsArrayAdd(ppd->marked, c);