Revert manifest to default one
[external/cups.git] / cups / mark.c
1 /*
2  * "$Id: mark.c 9895 2011-08-12 00:16:55Z mike $"
3  *
4  *   Option marking routines for CUPS.
5  *
6  *   Copyright 2007-2011 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  * Contents:
20  *
21  *   cupsMarkOptions()     - Mark command-line options in a PPD file.
22  *   ppdFindChoice()       - Return a pointer to an option choice.
23  *   ppdFindMarkedChoice() - Return the marked choice for the specified option.
24  *   ppdFindOption()       - Return a pointer to the specified option.
25  *   ppdIsMarked()         - Check to see if an option is marked.
26  *   ppdMarkDefaults()     - Mark all default options in the PPD file.
27  *   ppdMarkOption()       - Mark an option in a PPD file and return the number
28  *                           of conflicts.
29  *   ppdFirstOption()      - Return the first option in the PPD file.
30  *   ppdNextOption()       - Return the next option in the PPD file.
31  *   _ppdParseOptions()    - Parse options from a PPD file.
32  *   ppd_debug_marked()    - Output the marked array to stdout...
33  *   ppd_defaults()        - Set the defaults for this group and all sub-groups.
34  *   ppd_mark_choices()    - Mark one or more option choices from a string.
35  *   ppd_mark_option()     - Quickly mark an option without checking for
36  *                           conflicts.
37  */
38
39 /*
40  * Include necessary headers...
41  */
42
43 #include "cups-private.h"
44
45
46 /*
47  * Local functions...
48  */
49
50 #ifdef DEBUG
51 static void     ppd_debug_marked(ppd_file_t *ppd, const char *title);
52 #else
53 #  define       ppd_debug_marked(ppd,title)
54 #endif /* DEBUG */
55 static void     ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
56 static void     ppd_mark_choices(ppd_file_t *ppd, const char *s);
57 static void     ppd_mark_option(ppd_file_t *ppd, const char *option,
58                                 const char *choice);
59
60
61 /*
62  * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
63  *
64  * This function maps the IPP "finishings", "media", "mirror",
65  * "multiple-document-handling", "output-bin", "print-color-mode",
66  * "print-quality", "printer-resolution", and "sides" attributes to their
67  * corresponding PPD options and choices.
68  */
69
70 int                                     /* O - 1 if conflicts exist, 0 otherwise */
71 cupsMarkOptions(
72     ppd_file_t    *ppd,                 /* I - PPD file */
73     int           num_options,          /* I - Number of options */
74     cups_option_t *options)             /* I - Options */
75 {
76   int           i, j;                   /* Looping vars */
77   char          *ptr,                   /* Pointer into string */
78                 s[255];                 /* Temporary string */
79   const char    *val,                   /* Pointer into value */
80                 *media,                 /* media option */
81                 *output_bin,            /* output-bin option */
82                 *page_size,             /* PageSize option */
83                 *ppd_keyword,           /* PPD keyword */
84                 *print_color_mode,      /* print-color-mode option */
85                 *print_quality,         /* print-quality option */
86                 *sides;                 /* sides option */
87   cups_option_t *optptr;                /* Current option */
88   ppd_attr_t    *attr;                  /* PPD attribute */
89   _ppd_cache_t  *cache;                 /* PPD cache and mapping data */
90
91
92  /*
93   * Check arguments...
94   */
95
96   if (!ppd || num_options <= 0 || !options)
97     return (0);
98
99   ppd_debug_marked(ppd, "Before...");
100
101  /*
102   * Do special handling for finishings, media, output-bin, output-mode,
103   * print-color-mode, print-quality, and PageSize...
104   */
105
106   media         = cupsGetOption("media", num_options, options);
107   output_bin    = cupsGetOption("output-bin", num_options, options);
108   page_size     = cupsGetOption("PageSize", num_options, options);
109   print_quality = cupsGetOption("print-quality", num_options, options);
110   sides         = cupsGetOption("sides", num_options, options);
111
112   if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
113                                         options)) == NULL)
114     print_color_mode = cupsGetOption("output-mode", num_options, options);
115
116   if ((media || output_bin || print_color_mode || print_quality || sides) &&
117       !ppd->cache)
118   {
119    /*
120     * Load PPD cache and mapping data as needed...
121     */
122
123     ppd->cache = _ppdCacheCreateWithPPD(ppd);
124   }
125
126   cache = ppd->cache;
127
128   if (media)
129   {
130    /*
131     * Loop through the option string, separating it at commas and marking each
132     * individual option as long as the corresponding PPD option (PageSize,
133     * InputSlot, etc.) is not also set.
134     *
135     * For PageSize, we also check for an empty option value since some versions
136     * of MacOS X use it to specify auto-selection of the media based solely on
137     * the size.
138     */
139
140     for (val = media; *val;)
141     {
142      /*
143       * Extract the sub-option from the string...
144       */
145
146       for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
147         *ptr++ = *val++;
148       *ptr++ = '\0';
149
150       if (*val == ',')
151         val ++;
152
153      /*
154       * Mark it...
155       */
156
157       if (!page_size || !page_size[0])
158       {
159         if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
160           ppd_mark_option(ppd, "PageSize", s);
161         else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
162           ppd_mark_option(ppd, "PageSize", ppd_keyword);
163       }
164
165       if (cache && cache->source_option &&
166           !cupsGetOption(cache->source_option, num_options, options) &&
167           (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL)
168         ppd_mark_option(ppd, cache->source_option, ppd_keyword);
169
170       if (!cupsGetOption("MediaType", num_options, options) &&
171           (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
172         ppd_mark_option(ppd, "MediaType", ppd_keyword);
173     }
174   }
175
176   if (cache)
177   {
178     if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
179                        num_options, options) &&
180         !cupsGetOption("APPrinterPreset", num_options, options) &&
181         (print_color_mode || print_quality))
182     {
183      /*
184       * Map output-mode and print-quality to a preset...
185       */
186
187       _pwg_print_color_mode_t   pwg_pcm;/* print-color-mode index */
188       _pwg_print_quality_t      pwg_pq; /* print-quality index */
189       cups_option_t             *preset;/* Current preset option */
190
191       if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
192         pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
193       else
194         pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
195
196       if (print_quality)
197       {
198         pwg_pq = atoi(print_quality) - IPP_QUALITY_DRAFT;
199         if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT)
200           pwg_pq = _PWG_PRINT_QUALITY_DRAFT;
201         else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH)
202           pwg_pq = _PWG_PRINT_QUALITY_HIGH;
203       }
204       else
205         pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
206
207       if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
208       {
209        /*
210         * Try to find a preset that works so that we maximize the chances of us
211         * getting a good print using IPP attributes.
212         */
213
214         if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
215           pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
216         else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
217           pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
218         else
219         {
220           pwg_pq  = _PWG_PRINT_QUALITY_NORMAL;
221           pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
222         }
223       }
224
225       if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
226       {
227        /*
228         * Copy the preset options as long as the corresponding names are not
229         * already defined in the IPP request...
230         */
231
232         for (i = cache->num_presets[pwg_pcm][pwg_pq],
233                  preset = cache->presets[pwg_pcm][pwg_pq];
234              i > 0;
235              i --, preset ++)
236         {
237           if (!cupsGetOption(preset->name, num_options, options))
238             ppd_mark_option(ppd, preset->name, preset->value);
239         }
240       }
241     }
242
243     if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
244         (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
245     {
246      /*
247       * Map output-bin to OutputBin...
248       */
249
250       ppd_mark_option(ppd, "OutputBin", ppd_keyword);
251     }
252
253     if (sides && cache->sides_option &&
254         !cupsGetOption(cache->sides_option, num_options, options))
255     {
256      /*
257       * Map sides to duplex option...
258       */
259
260       if (!strcmp(sides, "one-sided") && cache->sides_1sided)
261         ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
262       else if (!strcmp(sides, "two-sided-long-edge") &&
263                cache->sides_2sided_long)
264         ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
265       else if (!strcmp(sides, "two-sided-short-edge") &&
266                cache->sides_2sided_short)
267         ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
268     }
269   }
270
271  /*
272   * Mark other options...
273   */
274
275   for (i = num_options, optptr = options; i > 0; i --, optptr ++)
276     if (!_cups_strcasecmp(optptr->name, "media") ||
277         !_cups_strcasecmp(optptr->name, "output-bin") ||
278         !_cups_strcasecmp(optptr->name, "output-mode") ||
279         !_cups_strcasecmp(optptr->name, "print-quality") ||
280         !_cups_strcasecmp(optptr->name, "sides"))
281       continue;
282     else if (!_cups_strcasecmp(optptr->name, "resolution") ||
283              !_cups_strcasecmp(optptr->name, "printer-resolution"))
284     {
285       ppd_mark_option(ppd, "Resolution", optptr->value);
286       ppd_mark_option(ppd, "SetResolution", optptr->value);
287         /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
288       ppd_mark_option(ppd, "JCLResolution", optptr->value);
289         /* HP */
290       ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
291         /* Canon */
292     }
293     else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
294     {
295       if (!cupsGetOption("Collate", num_options, options) &&
296           ppdFindOption(ppd, "Collate"))
297       {
298         if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
299           ppd_mark_option(ppd, "Collate", "True");
300         else
301           ppd_mark_option(ppd, "Collate", "False");
302       }
303     }
304     else if (!_cups_strcasecmp(optptr->name, "finishings"))
305     {
306      /*
307       * Lookup cupsIPPFinishings attributes for each value...
308       */
309
310       for (ptr = optptr->value; *ptr;)
311       {
312        /*
313         * Get the next finishings number...
314         */
315
316         if (!isdigit(*ptr & 255))
317           break;
318
319         if ((j = strtol(ptr, &ptr, 10)) < 3)
320           break;
321
322        /*
323         * Skip separator as needed...
324         */
325
326         if (*ptr == ',')
327           ptr ++;
328
329        /*
330         * Look it up in the PPD file...
331         */
332
333         sprintf(s, "%d", j);
334
335         if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
336           continue;
337
338        /*
339         * Apply "*Option Choice" settings from the attribute value...
340         */
341
342         ppd_mark_choices(ppd, attr->value);
343       }
344     }
345     else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
346     {
347      /*
348       * Lookup APPrinterPreset value...
349       */
350
351       if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
352       {
353        /*
354         * Apply "*Option Choice" settings from the attribute value...
355         */
356
357         ppd_mark_choices(ppd, attr->value);
358       }
359     }
360     else if (!_cups_strcasecmp(optptr->name, "mirror"))
361       ppd_mark_option(ppd, "MirrorPrint", optptr->value);
362     else
363       ppd_mark_option(ppd, optptr->name, optptr->value);
364
365   ppd_debug_marked(ppd, "After...");
366
367   return (ppdConflicts(ppd) > 0);
368 }
369
370
371 /*
372  * 'ppdFindChoice()' - Return a pointer to an option choice.
373  */
374
375 ppd_choice_t *                          /* O - Choice pointer or @code NULL@ */
376 ppdFindChoice(ppd_option_t *o,          /* I - Pointer to option */
377               const char   *choice)     /* I - Name of choice */
378 {
379   int           i;                      /* Looping var */
380   ppd_choice_t  *c;                     /* Current choice */
381
382
383   if (!o || !choice)
384     return (NULL);
385
386   if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
387     choice = "Custom";
388
389   for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
390     if (!_cups_strcasecmp(c->choice, choice))
391       return (c);
392
393   return (NULL);
394 }
395
396
397 /*
398  * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
399  */
400
401 ppd_choice_t *                          /* O - Pointer to choice or @code NULL@ */
402 ppdFindMarkedChoice(ppd_file_t *ppd,    /* I - PPD file */
403                     const char *option) /* I - Keyword/option name */
404 {
405   ppd_choice_t  key,                    /* Search key for choice */
406                 *marked;                /* Marked choice */
407
408
409   DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
410
411   if ((key.option = ppdFindOption(ppd, option)) == NULL)
412   {
413     DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
414     return (NULL);
415   }
416
417   marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
418
419   DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
420                 marked ? marked->choice : "NULL"));
421
422   return (marked);
423 }
424
425
426 /*
427  * 'ppdFindOption()' - Return a pointer to the specified option.
428  */
429
430 ppd_option_t *                          /* O - Pointer to option or @code NULL@ */
431 ppdFindOption(ppd_file_t *ppd,          /* I - PPD file data */
432               const char *option)       /* I - Option/Keyword name */
433 {
434  /*
435   * Range check input...
436   */
437
438   if (!ppd || !option)
439     return (NULL);
440
441   if (ppd->options)
442   {
443    /*
444     * Search in the array...
445     */
446
447     ppd_option_t        key;            /* Option search key */
448
449
450     strlcpy(key.keyword, option, sizeof(key.keyword));
451
452     return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
453   }
454   else
455   {
456    /*
457     * Search in each group...
458     */
459
460     int                 i, j;           /* Looping vars */
461     ppd_group_t         *group;         /* Current group */
462     ppd_option_t        *optptr;        /* Current option */
463
464
465     for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
466       for (j = group->num_options, optptr = group->options;
467            j > 0;
468            j --, optptr ++)
469         if (!_cups_strcasecmp(optptr->keyword, option))
470           return (optptr);
471
472     return (NULL);
473   }
474 }
475
476
477 /*
478  * 'ppdIsMarked()' - Check to see if an option is marked.
479  */
480
481 int                                     /* O - Non-zero if option is marked */
482 ppdIsMarked(ppd_file_t *ppd,            /* I - PPD file data */
483             const char *option,         /* I - Option/Keyword name */
484             const char *choice)         /* I - Choice name */
485 {
486   ppd_choice_t  key,                    /* Search key */
487                 *c;                     /* Choice pointer */
488
489
490   if (!ppd)
491     return (0);
492
493   if ((key.option = ppdFindOption(ppd, option)) == NULL)
494     return (0);
495
496   if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
497     return (0);
498
499   return (!strcmp(c->choice, choice));
500 }
501
502
503 /*
504  * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
505  */
506
507 void
508 ppdMarkDefaults(ppd_file_t *ppd)        /* I - PPD file record */
509 {
510   int           i;                      /* Looping variables */
511   ppd_group_t   *g;                     /* Current group */
512   ppd_choice_t  *c;                     /* Current choice */
513
514
515   if (!ppd)
516     return;
517
518  /*
519   * Clean out the marked array...
520   */
521
522   for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
523        c;
524        c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
525   {
526     cupsArrayRemove(ppd->marked, c);
527     c->marked = 0;
528   }
529
530  /*
531   * Then repopulate it with the defaults...
532   */
533
534   for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
535     ppd_defaults(ppd, g);
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 */
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/Mac OS X 10.5@
579  */
580
581 ppd_option_t *                          /* O - First option or @code NULL@ */
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/Mac OS X 10.5@
597  */
598
599 ppd_option_t *                          /* O - Next option or @code NULL@ */
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 */
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
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
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       ppdMarkOption(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
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
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 X, 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
1098
1099 /*
1100  * End of "$Id: mark.c 9895 2011-08-12 00:16:55Z mike $".
1101  */