/* * Option marking routines for CUPS. * * Copyright © 2007-2019 by Apple Inc. * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * * Licensed under Apache License v2.0. See the file "LICENSE" for more * information. * * PostScript is a trademark of Adobe Systems, Inc. */ /* * Include necessary headers... */ #include "cups-private.h" #include "ppd-private.h" #include "debug-internal.h" /* * Local functions... */ #ifdef DEBUG static void ppd_debug_marked(ppd_file_t *ppd, const char *title); #else # define ppd_debug_marked(ppd,title) #endif /* DEBUG */ static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g); static void ppd_mark_choices(ppd_file_t *ppd, const char *s); static void ppd_mark_option(ppd_file_t *ppd, const char *option, const char *choice); /* * 'cupsMarkOptions()' - Mark command-line options in a PPD file. * * This function maps the IPP "finishings", "media", "mirror", * "multiple-document-handling", "output-bin", "print-color-mode", * "print-quality", "printer-resolution", and "sides" attributes to their * corresponding PPD options and choices. */ int /* O - 1 if conflicts exist, 0 otherwise */ cupsMarkOptions( ppd_file_t *ppd, /* I - PPD file */ int num_options, /* I - Number of options */ cups_option_t *options) /* I - Options */ { int i, j; /* Looping vars */ char *ptr, /* Pointer into string */ s[255]; /* Temporary string */ const char *val, /* Pointer into value */ *media, /* media option */ *output_bin, /* output-bin option */ *page_size, /* PageSize option */ *ppd_keyword, /* PPD keyword */ *print_color_mode, /* print-color-mode option */ *print_quality, /* print-quality option */ *sides; /* sides option */ cups_option_t *optptr; /* Current option */ ppd_attr_t *attr; /* PPD attribute */ _ppd_cache_t *cache; /* PPD cache and mapping data */ /* * Check arguments... */ if (!ppd || num_options <= 0 || !options) return (0); ppd_debug_marked(ppd, "Before..."); /* * Do special handling for finishings, media, output-bin, output-mode, * print-color-mode, print-quality, and PageSize... */ media = cupsGetOption("media", num_options, options); output_bin = cupsGetOption("output-bin", num_options, options); page_size = cupsGetOption("PageSize", num_options, options); print_quality = cupsGetOption("print-quality", num_options, options); sides = cupsGetOption("sides", num_options, options); if ((print_color_mode = cupsGetOption("print-color-mode", num_options, options)) == NULL) print_color_mode = cupsGetOption("output-mode", num_options, options); if ((media || output_bin || print_color_mode || print_quality || sides) && !ppd->cache) { /* * Load PPD cache and mapping data as needed... */ ppd->cache = _ppdCacheCreateWithPPD(ppd); } cache = ppd->cache; if (media) { /* * Loop through the option string, separating it at commas and marking each * individual option as long as the corresponding PPD option (PageSize, * InputSlot, etc.) is not also set. * * For PageSize, we also check for an empty option value since some versions * of macOS use it to specify auto-selection of the media based solely on * the size. */ for (val = media; *val;) { /* * Extract the sub-option from the string... */ for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);) *ptr++ = *val++; *ptr++ = '\0'; if (*val == ',') val ++; /* * Mark it... */ if (!page_size || !page_size[0]) { if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s)) ppd_mark_option(ppd, "PageSize", s); else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL) ppd_mark_option(ppd, "PageSize", ppd_keyword); } if (cache && cache->source_option && !cupsGetOption(cache->source_option, num_options, options) && (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL) ppd_mark_option(ppd, cache->source_option, ppd_keyword); if (!cupsGetOption("MediaType", num_options, options) && (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL) ppd_mark_option(ppd, "MediaType", ppd_keyword); } } if (cache) { if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat", num_options, options) && !cupsGetOption("APPrinterPreset", num_options, options) && (print_color_mode || print_quality)) { /* * Map output-mode and print-quality to a preset... */ _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */ _pwg_print_quality_t pwg_pq; /* print-quality index */ cups_option_t *preset;/* Current preset option */ if (print_color_mode && !strcmp(print_color_mode, "monochrome")) pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME; else pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; if (print_quality) { pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT); if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT) pwg_pq = _PWG_PRINT_QUALITY_DRAFT; else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH) pwg_pq = _PWG_PRINT_QUALITY_HIGH; } else pwg_pq = _PWG_PRINT_QUALITY_NORMAL; if (cache->num_presets[pwg_pcm][pwg_pq] == 0) { /* * Try to find a preset that works so that we maximize the chances of us * getting a good print using IPP attributes. */ if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0) pwg_pq = _PWG_PRINT_QUALITY_NORMAL; else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0) pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; else { pwg_pq = _PWG_PRINT_QUALITY_NORMAL; pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; } } if (cache->num_presets[pwg_pcm][pwg_pq] > 0) { /* * Copy the preset options as long as the corresponding names are not * already defined in the IPP request... */ for (i = cache->num_presets[pwg_pcm][pwg_pq], preset = cache->presets[pwg_pcm][pwg_pq]; i > 0; i --, preset ++) { if (!cupsGetOption(preset->name, num_options, options)) ppd_mark_option(ppd, preset->name, preset->value); } } } if (output_bin && !cupsGetOption("OutputBin", num_options, options) && (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL) { /* * Map output-bin to OutputBin... */ ppd_mark_option(ppd, "OutputBin", ppd_keyword); } if (sides && cache->sides_option && !cupsGetOption(cache->sides_option, num_options, options)) { /* * Map sides to duplex option... */ if (!strcmp(sides, "one-sided") && cache->sides_1sided) ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided); else if (!strcmp(sides, "two-sided-long-edge") && cache->sides_2sided_long) ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long); else if (!strcmp(sides, "two-sided-short-edge") && cache->sides_2sided_short) ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short); } } /* * Mark other options... */ for (i = num_options, optptr = options; i > 0; i --, optptr ++) { if (!_cups_strcasecmp(optptr->name, "media") || !_cups_strcasecmp(optptr->name, "output-bin") || !_cups_strcasecmp(optptr->name, "output-mode") || !_cups_strcasecmp(optptr->name, "print-quality") || !_cups_strcasecmp(optptr->name, "sides")) continue; else if (!_cups_strcasecmp(optptr->name, "resolution") || !_cups_strcasecmp(optptr->name, "printer-resolution")) { ppd_mark_option(ppd, "Resolution", optptr->value); ppd_mark_option(ppd, "SetResolution", optptr->value); /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */ ppd_mark_option(ppd, "JCLResolution", optptr->value); /* HP */ ppd_mark_option(ppd, "CNRes_PGP", optptr->value); /* Canon */ } else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling")) { if (!cupsGetOption("Collate", num_options, options) && ppdFindOption(ppd, "Collate")) { if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies")) ppd_mark_option(ppd, "Collate", "True"); else ppd_mark_option(ppd, "Collate", "False"); } } else if (!_cups_strcasecmp(optptr->name, "finishings")) { /* * Lookup cupsIPPFinishings attributes for each value... */ for (ptr = optptr->value; *ptr;) { /* * Get the next finishings number... */ if (!isdigit(*ptr & 255)) break; if ((j = (int)strtol(ptr, &ptr, 10)) < 3) break; /* * Skip separator as needed... */ if (*ptr == ',') ptr ++; /* * Look it up in the PPD file... */ snprintf(s, sizeof(s), "%d", j); if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL) continue; /* * Apply "*Option Choice" settings from the attribute value... */ ppd_mark_choices(ppd, attr->value); } } else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset")) { /* * Lookup APPrinterPreset value... */ if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL) { /* * Apply "*Option Choice" settings from the attribute value... */ ppd_mark_choices(ppd, attr->value); } } else if (!_cups_strcasecmp(optptr->name, "mirror")) ppd_mark_option(ppd, "MirrorPrint", optptr->value); else ppd_mark_option(ppd, optptr->name, optptr->value); } if (print_quality) { int pq = atoi(print_quality); /* print-quaity value */ if (pq == IPP_QUALITY_DRAFT) ppd_mark_option(ppd, "cupsPrintQuality", "Draft"); else if (pq == IPP_QUALITY_HIGH) ppd_mark_option(ppd, "cupsPrintQuality", "High"); else ppd_mark_option(ppd, "cupsPrintQuality", "Normal"); } ppd_debug_marked(ppd, "After..."); return (ppdConflicts(ppd) > 0); } /* * 'ppdFindChoice()' - Return a pointer to an option choice. */ ppd_choice_t * /* O - Choice pointer or @code NULL@ */ ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */ const char *choice) /* I - Name of choice */ { int i; /* Looping var */ ppd_choice_t *c; /* Current choice */ if (!o || !choice) return (NULL); if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7)) choice = "Custom"; for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) if (!_cups_strcasecmp(c->choice, choice)) return (c); return (NULL); } /* * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option. */ ppd_choice_t * /* O - Pointer to choice or @code NULL@ */ ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */ const char *option) /* I - Keyword/option name */ { ppd_choice_t key, /* Search key for choice */ *marked; /* Marked choice */ DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option)); if ((key.option = ppdFindOption(ppd, option)) == NULL) { DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL"); return (NULL); } marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key); DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked, marked ? marked->choice : "NULL")); return (marked); } /* * 'ppdFindOption()' - Return a pointer to the specified option. */ ppd_option_t * /* O - Pointer to option or @code NULL@ */ ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */ const char *option) /* I - Option/Keyword name */ { /* * Range check input... */ if (!ppd || !option) return (NULL); if (ppd->options) { /* * Search in the array... */ ppd_option_t key; /* Option search key */ strlcpy(key.keyword, option, sizeof(key.keyword)); return ((ppd_option_t *)cupsArrayFind(ppd->options, &key)); } else { /* * Search in each group... */ int i, j; /* Looping vars */ ppd_group_t *group; /* Current group */ ppd_option_t *optptr; /* Current option */ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) for (j = group->num_options, optptr = group->options; j > 0; j --, optptr ++) if (!_cups_strcasecmp(optptr->keyword, option)) return (optptr); return (NULL); } } /* * 'ppdIsMarked()' - Check to see if an option is marked. */ int /* O - Non-zero if option is marked */ ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */ const char *option, /* I - Option/Keyword name */ const char *choice) /* I - Choice name */ { ppd_choice_t key, /* Search key */ *c; /* Choice pointer */ if (!ppd) return (0); if ((key.option = ppdFindOption(ppd, option)) == NULL) return (0); if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL) return (0); return (!strcmp(c->choice, choice)); } /* * 'ppdMarkDefaults()' - Mark all default options in the PPD file. */ void ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */ { int i; /* Looping variables */ ppd_group_t *g; /* Current group */ ppd_choice_t *c; /* Current choice */ if (!ppd) return; /* * Clean out the marked array... */ for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); c; c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) { cupsArrayRemove(ppd->marked, c); c->marked = 0; } /* * Then repopulate it with the defaults... */ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++) ppd_defaults(ppd, g); /* * Finally, tag any conflicts (API compatibility) once at the end. */ ppdConflicts(ppd); } /* * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of * conflicts. */ int /* O - Number of conflicts */ ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */ const char *option, /* I - Keyword */ const char *choice) /* I - Option name */ { DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")", ppd, option, choice)); /* * Range check input... */ if (!ppd || !option || !choice) return (0); /* * Mark the option... */ ppd_mark_option(ppd, option, choice); /* * Return the number of conflicts... */ return (ppdConflicts(ppd)); } /* * 'ppdFirstOption()' - Return the first option in the PPD file. * * Options are returned from all groups in ascending alphanumeric order. * * @since CUPS 1.2/macOS 10.5@ */ ppd_option_t * /* O - First option or @code NULL@ */ ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */ { if (!ppd) return (NULL); else return ((ppd_option_t *)cupsArrayFirst(ppd->options)); } /* * 'ppdNextOption()' - Return the next option in the PPD file. * * Options are returned from all groups in ascending alphanumeric order. * * @since CUPS 1.2/macOS 10.5@ */ ppd_option_t * /* O - Next option or @code NULL@ */ ppdNextOption(ppd_file_t *ppd) /* I - PPD file */ { if (!ppd) return (NULL); else return ((ppd_option_t *)cupsArrayNext(ppd->options)); } /* * '_ppdParseOptions()' - Parse options from a PPD file. * * This function looks for strings of the form: * * *option choice ... *optionN choiceN * property value ... propertyN valueN * * It stops when it finds a string that doesn't match this format. */ int /* O - Number of options */ _ppdParseOptions( const char *s, /* I - String to parse */ int num_options, /* I - Number of options */ cups_option_t **options, /* IO - Options */ _ppd_parse_t which) /* I - What to parse */ { char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */ choice[PPD_MAX_NAME], /* Current choice/value */ *ptr; /* Pointer into option or choice */ if (!s) return (num_options); /* * Read all of the "*Option Choice" and "property value" pairs from the * string, add them to an options array as we go... */ while (*s) { /* * Skip leading whitespace... */ while (_cups_isspace(*s)) s ++; /* * Get the option/property name... */ ptr = option; while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1)) *ptr++ = *s++; if (ptr == s || !_cups_isspace(*s)) break; *ptr = '\0'; /* * Get the choice... */ while (_cups_isspace(*s)) s ++; if (!*s) break; ptr = choice; while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1)) *ptr++ = *s++; if (*s && !_cups_isspace(*s)) break; *ptr = '\0'; /* * Add it to the options array... */ if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES) num_options = cupsAddOption(option + 1, choice, num_options, options); else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS) num_options = cupsAddOption(option, choice, num_options, options); } return (num_options); } #ifdef DEBUG /* * 'ppd_debug_marked()' - Output the marked array to stdout... */ static void ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */ const char *title) /* I - Title for list */ { ppd_choice_t *c; /* Current choice */ DEBUG_printf(("2cupsMarkOptions: %s", title)); for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); c; c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice)); } #endif /* DEBUG */ /* * 'ppd_defaults()' - Set the defaults for this group and all sub-groups. */ static void ppd_defaults(ppd_file_t *ppd, /* I - PPD file */ ppd_group_t *g) /* I - Group to default */ { int i; /* Looping var */ ppd_option_t *o; /* Current option */ ppd_group_t *sg; /* Current sub-group */ for (i = g->num_options, o = g->options; i > 0; i --, o ++) if (_cups_strcasecmp(o->keyword, "PageRegion") != 0) ppd_mark_option(ppd, o->keyword, o->defchoice); for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++) ppd_defaults(ppd, sg); } /* * 'ppd_mark_choices()' - Mark one or more option choices from a string. */ static void ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */ const char *s) /* I - "*Option Choice ..." string */ { int i, /* Looping var */ num_options; /* Number of options */ cups_option_t *options, /* Options */ *option; /* Current option */ if (!s) return; options = NULL; num_options = _ppdParseOptions(s, 0, &options, 0); for (i = num_options, option = options; i > 0; i --, option ++) ppd_mark_option(ppd, option->name, option->value); cupsFreeOptions(num_options, options); } /* * 'ppd_mark_option()' - Quick mark an option without checking for conflicts. */ static void ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */ const char *option, /* I - Option name */ const char *choice) /* I - Choice name */ { int i, j; /* Looping vars */ ppd_option_t *o; /* Option pointer */ ppd_choice_t *c, /* Choice pointer */ *oldc, /* Old choice pointer */ key; /* Search key for choice */ struct lconv *loc; /* Locale data */ DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")", ppd, option, choice)); /* * AP_D_InputSlot is the "default input slot" on macOS, and setting * it clears the regular InputSlot choices... */ if (!_cups_strcasecmp(option, "AP_D_InputSlot")) { cupsArraySave(ppd->options); if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) { key.option = o; if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } } cupsArrayRestore(ppd->options); } /* * Check for custom options... */ cupsArraySave(ppd->options); o = ppdFindOption(ppd, option); cupsArrayRestore(ppd->options); if (!o) return; loc = localeconv(); if (!_cups_strncasecmp(choice, "Custom.", 7)) { /* * Handle a custom option... */ if ((c = ppdFindChoice(o, "Custom")) == NULL) return; if (!_cups_strcasecmp(option, "PageSize")) { /* * Handle custom page sizes... */ ppdPageSize(ppd, choice); } else { /* * Handle other custom options... */ ppd_coption_t *coption; /* Custom option */ ppd_cparam_t *cparam; /* Custom parameter */ char *units; /* Custom points units */ if ((coption = ppdFindCustomOption(ppd, option)) != NULL) { if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL) return; switch (cparam->type) { case PPD_CUSTOM_UNKNOWN : break; case PPD_CUSTOM_CURVE : case PPD_CUSTOM_INVCURVE : case PPD_CUSTOM_REAL : cparam->current.custom_real = (float)_cupsStrScand(choice + 7, NULL, loc); break; case PPD_CUSTOM_POINTS : cparam->current.custom_points = (float)_cupsStrScand(choice + 7, &units, loc); if (units) { if (!_cups_strcasecmp(units, "cm")) cparam->current.custom_points *= 72.0f / 2.54f; else if (!_cups_strcasecmp(units, "mm")) cparam->current.custom_points *= 72.0f / 25.4f; else if (!_cups_strcasecmp(units, "m")) cparam->current.custom_points *= 72.0f / 0.0254f; else if (!_cups_strcasecmp(units, "in")) cparam->current.custom_points *= 72.0f; else if (!_cups_strcasecmp(units, "ft")) cparam->current.custom_points *= 12.0f * 72.0f; } break; case PPD_CUSTOM_INT : cparam->current.custom_int = atoi(choice + 7); break; case PPD_CUSTOM_PASSCODE : case PPD_CUSTOM_PASSWORD : case PPD_CUSTOM_STRING : if (cparam->current.custom_string) free(cparam->current.custom_string); cparam->current.custom_string = strdup(choice + 7); break; } } } /* * Make sure that we keep the option marked below... */ choice = "Custom"; } else if (choice[0] == '{') { /* * Handle multi-value custom options... */ ppd_coption_t *coption; /* Custom option */ ppd_cparam_t *cparam; /* Custom parameter */ char *units; /* Custom points units */ int num_vals; /* Number of values */ cups_option_t *vals, /* Values */ *val; /* Value */ if ((c = ppdFindChoice(o, "Custom")) == NULL) return; if ((coption = ppdFindCustomOption(ppd, option)) != NULL) { num_vals = cupsParseOptions(choice, 0, &vals); for (i = 0, val = vals; i < num_vals; i ++, val ++) { if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL) continue; switch (cparam->type) { case PPD_CUSTOM_UNKNOWN : break; case PPD_CUSTOM_CURVE : case PPD_CUSTOM_INVCURVE : case PPD_CUSTOM_REAL : cparam->current.custom_real = (float)_cupsStrScand(val->value, NULL, loc); break; case PPD_CUSTOM_POINTS : cparam->current.custom_points = (float)_cupsStrScand(val->value, &units, loc); if (units) { if (!_cups_strcasecmp(units, "cm")) cparam->current.custom_points *= 72.0f / 2.54f; else if (!_cups_strcasecmp(units, "mm")) cparam->current.custom_points *= 72.0f / 25.4f; else if (!_cups_strcasecmp(units, "m")) cparam->current.custom_points *= 72.0f / 0.0254f; else if (!_cups_strcasecmp(units, "in")) cparam->current.custom_points *= 72.0f; else if (!_cups_strcasecmp(units, "ft")) cparam->current.custom_points *= 12.0f * 72.0f; } break; case PPD_CUSTOM_INT : cparam->current.custom_int = atoi(val->value); break; case PPD_CUSTOM_PASSCODE : case PPD_CUSTOM_PASSWORD : case PPD_CUSTOM_STRING : if (cparam->current.custom_string) free(cparam->current.custom_string); cparam->current.custom_string = strdup(val->value); break; } } cupsFreeOptions(num_vals, vals); } } else { for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) if (!_cups_strcasecmp(c->choice, choice)) break; if (!i) return; } /* * Option found; mark it and then handle unmarking any other options. */ if (o->ui != PPD_UI_PICKMANY) { /* * Unmark all other choices... */ if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion")) { /* * Mark current page size... */ for (j = 0; j < ppd->num_sizes; j ++) ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name, choice); /* * Unmark the current PageSize or PageRegion setting, as * appropriate... */ cupsArraySave(ppd->options); if (!_cups_strcasecmp(option, "PageSize")) { if ((o = ppdFindOption(ppd, "PageRegion")) != NULL) { key.option = o; if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } } } else { if ((o = ppdFindOption(ppd, "PageSize")) != NULL) { key.option = o; if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } } } cupsArrayRestore(ppd->options); } else if (!_cups_strcasecmp(option, "InputSlot")) { /* * Unmark ManualFeed option... */ cupsArraySave(ppd->options); if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL) { key.option = o; if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } } cupsArrayRestore(ppd->options); } else if (!_cups_strcasecmp(option, "ManualFeed") && !_cups_strcasecmp(choice, "True")) { /* * Unmark InputSlot option... */ cupsArraySave(ppd->options); if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) { key.option = o; if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) { oldc->marked = 0; cupsArrayRemove(ppd->marked, oldc); } } cupsArrayRestore(ppd->options); } } c->marked = 1; cupsArrayAdd(ppd->marked, c); }