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  * 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     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@ */
ppdFindChoice(ppd_option_t * o,const char * choice)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@ */
ppdFindMarkedChoice(ppd_file_t * ppd,const char * option)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@ */
ppdFindOption(ppd_file_t * ppd,const char * option)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 */
ppdIsMarked(ppd_file_t * ppd,const char * option,const char * choice)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
ppdMarkDefaults(ppd_file_t * ppd)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 */
ppdMarkOption(ppd_file_t * ppd,const char * option,const char * choice)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@ */
ppdFirstOption(ppd_file_t * ppd)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@ */
ppdNextOption(ppd_file_t * ppd)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 */
_ppdParseOptions(const char * s,int num_options,cups_option_t ** options,_ppd_parse_t which)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
ppd_debug_marked(ppd_file_t * ppd,const char * title)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
ppd_defaults(ppd_file_t * ppd,ppd_group_t * g)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
ppd_mark_choices(ppd_file_t * ppd,const char * s)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
ppd_mark_option(ppd_file_t * ppd,const char * option,const char * choice)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 macOS, 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 }
1083