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