• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * 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  * 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 */
cupsMarkOptions(ppd_file_t * ppd,int num_options,cups_option_t * options)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 macOS 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   {
257     if (!_cups_strcasecmp(optptr->name, "media") ||
258         !_cups_strcasecmp(optptr->name, "output-bin") ||
259 	!_cups_strcasecmp(optptr->name, "output-mode") ||
260 	!_cups_strcasecmp(optptr->name, "print-quality") ||
261 	!_cups_strcasecmp(optptr->name, "sides"))
262       continue;
263     else if (!_cups_strcasecmp(optptr->name, "resolution") ||
264              !_cups_strcasecmp(optptr->name, "printer-resolution"))
265     {
266       ppd_mark_option(ppd, "Resolution", optptr->value);
267       ppd_mark_option(ppd, "SetResolution", optptr->value);
268       	/* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
269       ppd_mark_option(ppd, "JCLResolution", optptr->value);
270       	/* HP */
271       ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
272       	/* Canon */
273     }
274     else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
275     {
276       if (!cupsGetOption("Collate", num_options, options) &&
277           ppdFindOption(ppd, "Collate"))
278       {
279         if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
280 	  ppd_mark_option(ppd, "Collate", "True");
281 	else
282 	  ppd_mark_option(ppd, "Collate", "False");
283       }
284     }
285     else if (!_cups_strcasecmp(optptr->name, "finishings"))
286     {
287      /*
288       * Lookup cupsIPPFinishings attributes for each value...
289       */
290 
291       for (ptr = optptr->value; *ptr;)
292       {
293        /*
294         * Get the next finishings number...
295 	*/
296 
297         if (!isdigit(*ptr & 255))
298 	  break;
299 
300         if ((j = (int)strtol(ptr, &ptr, 10)) < 3)
301 	  break;
302 
303        /*
304         * Skip separator as needed...
305 	*/
306 
307         if (*ptr == ',')
308 	  ptr ++;
309 
310        /*
311         * Look it up in the PPD file...
312 	*/
313 
314 	sprintf(s, "%d", j);
315 
316         if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
317 	  continue;
318 
319        /*
320         * Apply "*Option Choice" settings from the attribute value...
321 	*/
322 
323         ppd_mark_choices(ppd, attr->value);
324       }
325     }
326     else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
327     {
328      /*
329       * Lookup APPrinterPreset value...
330       */
331 
332       if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
333       {
334        /*
335         * Apply "*Option Choice" settings from the attribute value...
336 	*/
337 
338         ppd_mark_choices(ppd, attr->value);
339       }
340     }
341     else if (!_cups_strcasecmp(optptr->name, "mirror"))
342       ppd_mark_option(ppd, "MirrorPrint", optptr->value);
343     else
344       ppd_mark_option(ppd, optptr->name, optptr->value);
345   }
346 
347   if (print_quality)
348   {
349     int pq = atoi(print_quality);       /* print-quaity value */
350 
351     if (pq == IPP_QUALITY_DRAFT)
352       ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
353     else if (pq == IPP_QUALITY_HIGH)
354       ppd_mark_option(ppd, "cupsPrintQuality", "High");
355     else
356       ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
357   }
358 
359   ppd_debug_marked(ppd, "After...");
360 
361   return (ppdConflicts(ppd) > 0);
362 }
363 
364 
365 /*
366  * 'ppdFindChoice()' - Return a pointer to an option choice.
367  */
368 
369 ppd_choice_t *				/* O - Choice pointer or @code NULL@ */
ppdFindChoice(ppd_option_t * o,const char * choice)370 ppdFindChoice(ppd_option_t *o,		/* I - Pointer to option */
371               const char   *choice)	/* I - Name of choice */
372 {
373   int		i;			/* Looping var */
374   ppd_choice_t	*c;			/* Current choice */
375 
376 
377   if (!o || !choice)
378     return (NULL);
379 
380   if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
381     choice = "Custom";
382 
383   for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
384     if (!_cups_strcasecmp(c->choice, choice))
385       return (c);
386 
387   return (NULL);
388 }
389 
390 
391 /*
392  * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
393  */
394 
395 ppd_choice_t *				/* O - Pointer to choice or @code NULL@ */
ppdFindMarkedChoice(ppd_file_t * ppd,const char * option)396 ppdFindMarkedChoice(ppd_file_t *ppd,	/* I - PPD file */
397                     const char *option)	/* I - Keyword/option name */
398 {
399   ppd_choice_t	key,			/* Search key for choice */
400 		*marked;		/* Marked choice */
401 
402 
403   DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
404 
405   if ((key.option = ppdFindOption(ppd, option)) == NULL)
406   {
407     DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
408     return (NULL);
409   }
410 
411   marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
412 
413   DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
414                 marked ? marked->choice : "NULL"));
415 
416   return (marked);
417 }
418 
419 
420 /*
421  * 'ppdFindOption()' - Return a pointer to the specified option.
422  */
423 
424 ppd_option_t *				/* O - Pointer to option or @code NULL@ */
ppdFindOption(ppd_file_t * ppd,const char * option)425 ppdFindOption(ppd_file_t *ppd,		/* I - PPD file data */
426               const char *option)	/* I - Option/Keyword name */
427 {
428  /*
429   * Range check input...
430   */
431 
432   if (!ppd || !option)
433     return (NULL);
434 
435   if (ppd->options)
436   {
437    /*
438     * Search in the array...
439     */
440 
441     ppd_option_t	key;		/* Option search key */
442 
443 
444     strlcpy(key.keyword, option, sizeof(key.keyword));
445 
446     return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
447   }
448   else
449   {
450    /*
451     * Search in each group...
452     */
453 
454     int			i, j;		/* Looping vars */
455     ppd_group_t		*group;		/* Current group */
456     ppd_option_t	*optptr;	/* Current option */
457 
458 
459     for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
460       for (j = group->num_options, optptr = group->options;
461            j > 0;
462 	   j --, optptr ++)
463         if (!_cups_strcasecmp(optptr->keyword, option))
464 	  return (optptr);
465 
466     return (NULL);
467   }
468 }
469 
470 
471 /*
472  * 'ppdIsMarked()' - Check to see if an option is marked.
473  */
474 
475 int					/* O - Non-zero if option is marked */
ppdIsMarked(ppd_file_t * ppd,const char * option,const char * choice)476 ppdIsMarked(ppd_file_t *ppd,		/* I - PPD file data */
477             const char *option,		/* I - Option/Keyword name */
478             const char *choice)		/* I - Choice name */
479 {
480   ppd_choice_t	key,			/* Search key */
481 		*c;			/* Choice pointer */
482 
483 
484   if (!ppd)
485     return (0);
486 
487   if ((key.option = ppdFindOption(ppd, option)) == NULL)
488     return (0);
489 
490   if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
491     return (0);
492 
493   return (!strcmp(c->choice, choice));
494 }
495 
496 
497 /*
498  * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
499  */
500 
501 void
ppdMarkDefaults(ppd_file_t * ppd)502 ppdMarkDefaults(ppd_file_t *ppd)	/* I - PPD file record */
503 {
504   int		i;			/* Looping variables */
505   ppd_group_t	*g;			/* Current group */
506   ppd_choice_t	*c;			/* Current choice */
507 
508 
509   if (!ppd)
510     return;
511 
512  /*
513   * Clean out the marked array...
514   */
515 
516   for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
517        c;
518        c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
519   {
520     cupsArrayRemove(ppd->marked, c);
521     c->marked = 0;
522   }
523 
524  /*
525   * Then repopulate it with the defaults...
526   */
527 
528   for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
529     ppd_defaults(ppd, g);
530 
531  /*
532   * Finally, tag any conflicts (API compatibility) once at the end.
533   */
534 
535   ppdConflicts(ppd);
536 }
537 
538 
539 /*
540  * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
541  *                     conflicts.
542  */
543 
544 int					/* O - Number of conflicts */
ppdMarkOption(ppd_file_t * ppd,const char * option,const char * choice)545 ppdMarkOption(ppd_file_t *ppd,		/* I - PPD file record */
546               const char *option,	/* I - Keyword */
547               const char *choice)	/* I - Option name */
548 {
549   DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
550         	ppd, option, choice));
551 
552  /*
553   * Range check input...
554   */
555 
556   if (!ppd || !option || !choice)
557     return (0);
558 
559  /*
560   * Mark the option...
561   */
562 
563   ppd_mark_option(ppd, option, choice);
564 
565  /*
566   * Return the number of conflicts...
567   */
568 
569   return (ppdConflicts(ppd));
570 }
571 
572 
573 /*
574  * 'ppdFirstOption()' - Return the first option in the PPD file.
575  *
576  * Options are returned from all groups in ascending alphanumeric order.
577  *
578  * @since CUPS 1.2/macOS 10.5@
579  */
580 
581 ppd_option_t *				/* O - First option or @code NULL@ */
ppdFirstOption(ppd_file_t * ppd)582 ppdFirstOption(ppd_file_t *ppd)		/* I - PPD file */
583 {
584   if (!ppd)
585     return (NULL);
586   else
587     return ((ppd_option_t *)cupsArrayFirst(ppd->options));
588 }
589 
590 
591 /*
592  * 'ppdNextOption()' - Return the next option in the PPD file.
593  *
594  * Options are returned from all groups in ascending alphanumeric order.
595  *
596  * @since CUPS 1.2/macOS 10.5@
597  */
598 
599 ppd_option_t *				/* O - Next option or @code NULL@ */
ppdNextOption(ppd_file_t * ppd)600 ppdNextOption(ppd_file_t *ppd)		/* I - PPD file */
601 {
602   if (!ppd)
603     return (NULL);
604   else
605     return ((ppd_option_t *)cupsArrayNext(ppd->options));
606 }
607 
608 
609 /*
610  * '_ppdParseOptions()' - Parse options from a PPD file.
611  *
612  * This function looks for strings of the form:
613  *
614  *     *option choice ... *optionN choiceN
615  *     property value ... propertyN valueN
616  *
617  * It stops when it finds a string that doesn't match this format.
618  */
619 
620 int					/* O  - Number of options */
_ppdParseOptions(const char * s,int num_options,cups_option_t ** options,_ppd_parse_t which)621 _ppdParseOptions(
622     const char    *s,			/* I  - String to parse */
623     int           num_options,		/* I  - Number of options */
624     cups_option_t **options,		/* IO - Options */
625     _ppd_parse_t  which)		/* I  - What to parse */
626 {
627   char	option[PPD_MAX_NAME * 2 + 1],	/* Current option/property */
628 	choice[PPD_MAX_NAME],		/* Current choice/value */
629 	*ptr;				/* Pointer into option or choice */
630 
631 
632   if (!s)
633     return (num_options);
634 
635  /*
636   * Read all of the "*Option Choice" and "property value" pairs from the
637   * string, add them to an options array as we go...
638   */
639 
640   while (*s)
641   {
642    /*
643     * Skip leading whitespace...
644     */
645 
646     while (_cups_isspace(*s))
647       s ++;
648 
649    /*
650     * Get the option/property name...
651     */
652 
653     ptr = option;
654     while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
655       *ptr++ = *s++;
656 
657     if (ptr == s || !_cups_isspace(*s))
658       break;
659 
660     *ptr = '\0';
661 
662    /*
663     * Get the choice...
664     */
665 
666     while (_cups_isspace(*s))
667       s ++;
668 
669     if (!*s)
670       break;
671 
672     ptr = choice;
673     while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
674       *ptr++ = *s++;
675 
676     if (*s && !_cups_isspace(*s))
677       break;
678 
679     *ptr = '\0';
680 
681    /*
682     * Add it to the options array...
683     */
684 
685     if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES)
686       num_options = cupsAddOption(option + 1, choice, num_options, options);
687     else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS)
688       num_options = cupsAddOption(option, choice, num_options, options);
689   }
690 
691   return (num_options);
692 }
693 
694 
695 #ifdef DEBUG
696 /*
697  * 'ppd_debug_marked()' - Output the marked array to stdout...
698  */
699 
700 static void
ppd_debug_marked(ppd_file_t * ppd,const char * title)701 ppd_debug_marked(ppd_file_t *ppd,		/* I - PPD file data */
702              const char *title)		/* I - Title for list */
703 {
704   ppd_choice_t	*c;			/* Current choice */
705 
706 
707   DEBUG_printf(("2cupsMarkOptions: %s", title));
708 
709   for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
710        c;
711        c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
712     DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
713 }
714 #endif /* DEBUG */
715 
716 
717 /*
718  * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
719  */
720 
721 static void
ppd_defaults(ppd_file_t * ppd,ppd_group_t * g)722 ppd_defaults(ppd_file_t  *ppd,		/* I - PPD file */
723              ppd_group_t *g)		/* I - Group to default */
724 {
725   int		i;			/* Looping var */
726   ppd_option_t	*o;			/* Current option */
727   ppd_group_t	*sg;			/* Current sub-group */
728 
729 
730   for (i = g->num_options, o = g->options; i > 0; i --, o ++)
731     if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
732       ppd_mark_option(ppd, o->keyword, o->defchoice);
733 
734   for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
735     ppd_defaults(ppd, sg);
736 }
737 
738 
739 /*
740  * 'ppd_mark_choices()' - Mark one or more option choices from a string.
741  */
742 
743 static void
ppd_mark_choices(ppd_file_t * ppd,const char * s)744 ppd_mark_choices(ppd_file_t *ppd,	/* I - PPD file */
745                  const char *s)		/* I - "*Option Choice ..." string */
746 {
747   int		i,			/* Looping var */
748 		num_options;		/* Number of options */
749   cups_option_t	*options,		/* Options */
750 		*option;		/* Current option */
751 
752 
753   if (!s)
754     return;
755 
756   options     = NULL;
757   num_options = _ppdParseOptions(s, 0, &options, 0);
758 
759   for (i = num_options, option = options; i > 0; i --, option ++)
760     ppd_mark_option(ppd, option->name, option->value);
761 
762   cupsFreeOptions(num_options, options);
763 }
764 
765 
766 /*
767  * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
768  */
769 
770 static void
ppd_mark_option(ppd_file_t * ppd,const char * option,const char * choice)771 ppd_mark_option(ppd_file_t *ppd,	/* I - PPD file */
772                 const char *option,	/* I - Option name */
773                 const char *choice)	/* I - Choice name */
774 {
775   int		i, j;			/* Looping vars */
776   ppd_option_t	*o;			/* Option pointer */
777   ppd_choice_t	*c,			/* Choice pointer */
778 		*oldc,			/* Old choice pointer */
779 		key;			/* Search key for choice */
780   struct lconv	*loc;			/* Locale data */
781 
782 
783   DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
784         	ppd, option, choice));
785 
786  /*
787   * AP_D_InputSlot is the "default input slot" on macOS, and setting
788   * it clears the regular InputSlot choices...
789   */
790 
791   if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
792   {
793     cupsArraySave(ppd->options);
794 
795     if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
796     {
797       key.option = o;
798       if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
799       {
800         oldc->marked = 0;
801         cupsArrayRemove(ppd->marked, oldc);
802       }
803     }
804 
805     cupsArrayRestore(ppd->options);
806   }
807 
808  /*
809   * Check for custom options...
810   */
811 
812   cupsArraySave(ppd->options);
813 
814   o = ppdFindOption(ppd, option);
815 
816   cupsArrayRestore(ppd->options);
817 
818   if (!o)
819     return;
820 
821   loc = localeconv();
822 
823   if (!_cups_strncasecmp(choice, "Custom.", 7))
824   {
825    /*
826     * Handle a custom option...
827     */
828 
829     if ((c = ppdFindChoice(o, "Custom")) == NULL)
830       return;
831 
832     if (!_cups_strcasecmp(option, "PageSize"))
833     {
834      /*
835       * Handle custom page sizes...
836       */
837 
838       ppdPageSize(ppd, choice);
839     }
840     else
841     {
842      /*
843       * Handle other custom options...
844       */
845 
846       ppd_coption_t	*coption;	/* Custom option */
847       ppd_cparam_t	*cparam;	/* Custom parameter */
848       char		*units;		/* Custom points units */
849 
850 
851       if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
852       {
853         if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
854 	  return;
855 
856         switch (cparam->type)
857 	{
858 	  case PPD_CUSTOM_CURVE :
859 	  case PPD_CUSTOM_INVCURVE :
860 	  case PPD_CUSTOM_REAL :
861 	      cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
862 	                                                         NULL, loc);
863 	      break;
864 
865 	  case PPD_CUSTOM_POINTS :
866 	      cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
867 	                                                           &units,
868 	                                                           loc);
869 
870               if (units)
871 	      {
872         	if (!_cups_strcasecmp(units, "cm"))
873 	          cparam->current.custom_points *= 72.0f / 2.54f;
874         	else if (!_cups_strcasecmp(units, "mm"))
875 	          cparam->current.custom_points *= 72.0f / 25.4f;
876         	else if (!_cups_strcasecmp(units, "m"))
877 	          cparam->current.custom_points *= 72.0f / 0.0254f;
878         	else if (!_cups_strcasecmp(units, "in"))
879 	          cparam->current.custom_points *= 72.0f;
880         	else if (!_cups_strcasecmp(units, "ft"))
881 	          cparam->current.custom_points *= 12.0f * 72.0f;
882               }
883 	      break;
884 
885 	  case PPD_CUSTOM_INT :
886 	      cparam->current.custom_int = atoi(choice + 7);
887 	      break;
888 
889 	  case PPD_CUSTOM_PASSCODE :
890 	  case PPD_CUSTOM_PASSWORD :
891 	  case PPD_CUSTOM_STRING :
892 	      if (cparam->current.custom_string)
893 	        _cupsStrFree(cparam->current.custom_string);
894 
895 	      cparam->current.custom_string = _cupsStrAlloc(choice + 7);
896 	      break;
897 	}
898       }
899     }
900 
901    /*
902     * Make sure that we keep the option marked below...
903     */
904 
905     choice = "Custom";
906   }
907   else if (choice[0] == '{')
908   {
909    /*
910     * Handle multi-value custom options...
911     */
912 
913     ppd_coption_t	*coption;	/* Custom option */
914     ppd_cparam_t	*cparam;	/* Custom parameter */
915     char		*units;		/* Custom points units */
916     int			num_vals;	/* Number of values */
917     cups_option_t	*vals,		/* Values */
918 			*val;		/* Value */
919 
920 
921     if ((c = ppdFindChoice(o, "Custom")) == NULL)
922       return;
923 
924     if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
925     {
926       num_vals = cupsParseOptions(choice, 0, &vals);
927 
928       for (i = 0, val = vals; i < num_vals; i ++, val ++)
929       {
930         if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
931 	  continue;
932 
933 	switch (cparam->type)
934 	{
935 	  case PPD_CUSTOM_CURVE :
936 	  case PPD_CUSTOM_INVCURVE :
937 	  case PPD_CUSTOM_REAL :
938 	      cparam->current.custom_real = (float)_cupsStrScand(val->value,
939 	                                                         NULL, loc);
940 	      break;
941 
942 	  case PPD_CUSTOM_POINTS :
943 	      cparam->current.custom_points = (float)_cupsStrScand(val->value,
944 	                                                           &units,
945 	                                                           loc);
946 
947 	      if (units)
948 	      {
949         	if (!_cups_strcasecmp(units, "cm"))
950 		  cparam->current.custom_points *= 72.0f / 2.54f;
951         	else if (!_cups_strcasecmp(units, "mm"))
952 		  cparam->current.custom_points *= 72.0f / 25.4f;
953         	else if (!_cups_strcasecmp(units, "m"))
954 		  cparam->current.custom_points *= 72.0f / 0.0254f;
955         	else if (!_cups_strcasecmp(units, "in"))
956 		  cparam->current.custom_points *= 72.0f;
957         	else if (!_cups_strcasecmp(units, "ft"))
958 		  cparam->current.custom_points *= 12.0f * 72.0f;
959 	      }
960 	      break;
961 
962 	  case PPD_CUSTOM_INT :
963 	      cparam->current.custom_int = atoi(val->value);
964 	      break;
965 
966 	  case PPD_CUSTOM_PASSCODE :
967 	  case PPD_CUSTOM_PASSWORD :
968 	  case PPD_CUSTOM_STRING :
969 	      if (cparam->current.custom_string)
970 		_cupsStrFree(cparam->current.custom_string);
971 
972 	      cparam->current.custom_string = _cupsStrRetain(val->value);
973 	      break;
974 	}
975       }
976 
977       cupsFreeOptions(num_vals, vals);
978     }
979   }
980   else
981   {
982     for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
983       if (!_cups_strcasecmp(c->choice, choice))
984         break;
985 
986     if (!i)
987       return;
988   }
989 
990  /*
991   * Option found; mark it and then handle unmarking any other options.
992   */
993 
994   if (o->ui != PPD_UI_PICKMANY)
995   {
996    /*
997     * Unmark all other choices...
998     */
999 
1000     if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
1001     {
1002       oldc->marked = 0;
1003       cupsArrayRemove(ppd->marked, oldc);
1004     }
1005 
1006     if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
1007     {
1008      /*
1009       * Mark current page size...
1010       */
1011 
1012       for (j = 0; j < ppd->num_sizes; j ++)
1013 	ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
1014 		                           choice);
1015 
1016      /*
1017       * Unmark the current PageSize or PageRegion setting, as
1018       * appropriate...
1019       */
1020 
1021       cupsArraySave(ppd->options);
1022 
1023       if (!_cups_strcasecmp(option, "PageSize"))
1024       {
1025 	if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
1026         {
1027           key.option = o;
1028           if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1029           {
1030             oldc->marked = 0;
1031             cupsArrayRemove(ppd->marked, oldc);
1032           }
1033         }
1034       }
1035       else
1036       {
1037 	if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
1038         {
1039           key.option = o;
1040           if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1041           {
1042             oldc->marked = 0;
1043             cupsArrayRemove(ppd->marked, oldc);
1044           }
1045         }
1046       }
1047 
1048       cupsArrayRestore(ppd->options);
1049     }
1050     else if (!_cups_strcasecmp(option, "InputSlot"))
1051     {
1052      /*
1053       * Unmark ManualFeed option...
1054       */
1055 
1056       cupsArraySave(ppd->options);
1057 
1058       if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
1059       {
1060         key.option = o;
1061         if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1062         {
1063           oldc->marked = 0;
1064           cupsArrayRemove(ppd->marked, oldc);
1065         }
1066       }
1067 
1068       cupsArrayRestore(ppd->options);
1069     }
1070     else if (!_cups_strcasecmp(option, "ManualFeed") &&
1071 	     !_cups_strcasecmp(choice, "True"))
1072     {
1073      /*
1074       * Unmark InputSlot option...
1075       */
1076 
1077       cupsArraySave(ppd->options);
1078 
1079       if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
1080       {
1081         key.option = o;
1082         if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1083         {
1084           oldc->marked = 0;
1085           cupsArrayRemove(ppd->marked, oldc);
1086         }
1087       }
1088 
1089       cupsArrayRestore(ppd->options);
1090     }
1091   }
1092 
1093   c->marked = 1;
1094 
1095   cupsArrayAdd(ppd->marked, c);
1096 }
1097